mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 02:39:48 +00:00
Merge branches 'ib-mfd-base-acpi-dma-4.3', 'ib-mfd-clocksource-rtc-watchdog-4.3' and 'ib-mfd-i2c-x86-watchdog-4.3' into ibs-for-mfd-merged
This commit is contained in:
commit
d548ca43d0
16 changed files with 410 additions and 60 deletions
|
@ -1,20 +1,23 @@
|
|||
STMicroelectronics Low Power Controller (LPC) - RTC
|
||||
===================================================
|
||||
|
||||
LPC currently supports Watchdog OR Real Time Clock functionality.
|
||||
LPC currently supports Watchdog OR Real Time Clock OR Clocksource
|
||||
functionality.
|
||||
|
||||
[See: ../watchdog/st_lpc_wdt.txt for Watchdog options]
|
||||
[See: ../timer/st,stih407-lpc for Clocksource options]
|
||||
|
||||
Required properties
|
||||
|
||||
- compatible : Must be one of: "st,stih407-lpc" "st,stih416-lpc"
|
||||
"st,stih415-lpc" "st,stid127-lpc"
|
||||
- compatible : Must be: "st,stih407-lpc"
|
||||
- reg : LPC registers base address + size
|
||||
- interrupts : LPC interrupt line number and associated flags
|
||||
- clocks : Clock used by LPC device (See: ../clock/clock-bindings.txt)
|
||||
- st,lpc-mode : The LPC can run either one of two modes ST_LPC_MODE_RTC [0] or
|
||||
ST_LPC_MODE_WDT [1]. One (and only one) mode must be
|
||||
selected.
|
||||
- st,lpc-mode : The LPC can run either one of three modes:
|
||||
ST_LPC_MODE_RTC [0]
|
||||
ST_LPC_MODE_WDT [1]
|
||||
ST_LPC_MODE_CLKSRC [2]
|
||||
One (and only one) mode must be selected.
|
||||
|
||||
Example:
|
||||
lpc@fde05000 {
|
||||
|
|
28
Documentation/devicetree/bindings/timer/st,stih407-lpc
Normal file
28
Documentation/devicetree/bindings/timer/st,stih407-lpc
Normal file
|
@ -0,0 +1,28 @@
|
|||
STMicroelectronics Low Power Controller (LPC) - Clocksource
|
||||
===========================================================
|
||||
|
||||
LPC currently supports Watchdog OR Real Time Clock OR Clocksource
|
||||
functionality.
|
||||
|
||||
[See: ../watchdog/st_lpc_wdt.txt for Watchdog options]
|
||||
[See: ../rtc/rtc-st-lpc.txt for RTC options]
|
||||
|
||||
Required properties
|
||||
|
||||
- compatible : Must be: "st,stih407-lpc"
|
||||
- reg : LPC registers base address + size
|
||||
- interrupts : LPC interrupt line number and associated flags
|
||||
- clocks : Clock used by LPC device (See: ../clock/clock-bindings.txt)
|
||||
- st,lpc-mode : The LPC can run either one of three modes:
|
||||
ST_LPC_MODE_RTC [0]
|
||||
ST_LPC_MODE_WDT [1]
|
||||
ST_LPC_MODE_CLKSRC [2]
|
||||
One (and only one) mode must be selected.
|
||||
|
||||
Example:
|
||||
lpc@fde05000 {
|
||||
compatible = "st,stih407-lpc";
|
||||
reg = <0xfde05000 0x1000>;
|
||||
clocks = <&clk_s_d3_flexgen CLK_LPC_0>;
|
||||
st,lpc-mode = <ST_LPC_MODE_CLKSRC>;
|
||||
};
|
|
@ -1,9 +1,11 @@
|
|||
STMicroelectronics Low Power Controller (LPC) - Watchdog
|
||||
========================================================
|
||||
|
||||
LPC currently supports Watchdog OR Real Time Clock functionality.
|
||||
LPC currently supports Watchdog OR Real Time Clock OR Clocksource
|
||||
functionality.
|
||||
|
||||
[See: ../rtc/rtc-st-lpc.txt for RTC options]
|
||||
[See: ../timer/st,stih407-lpc for Clocksource options]
|
||||
|
||||
Required properties
|
||||
|
||||
|
@ -12,9 +14,11 @@ Required properties
|
|||
- reg : LPC registers base address + size
|
||||
- interrupts : LPC interrupt line number and associated flags
|
||||
- clocks : Clock used by LPC device (See: ../clock/clock-bindings.txt)
|
||||
- st,lpc-mode : The LPC can run either one of two modes ST_LPC_MODE_RTC [0] or
|
||||
ST_LPC_MODE_WDT [1]. One (and only one) mode must be
|
||||
selected.
|
||||
- st,lpc-mode : The LPC can run either one of three modes:
|
||||
ST_LPC_MODE_RTC [0]
|
||||
ST_LPC_MODE_WDT [1]
|
||||
ST_LPC_MODE_CLKSRC [2]
|
||||
One (and only one) mode must be selected.
|
||||
|
||||
Required properties [watchdog mode]
|
||||
|
||||
|
|
|
@ -1503,6 +1503,7 @@ S: Maintained
|
|||
F: arch/arm/mach-sti/
|
||||
F: arch/arm/boot/dts/sti*
|
||||
F: drivers/clocksource/arm_global_timer.c
|
||||
F: drivers/clocksource/clksrc_st_lpc.c
|
||||
F: drivers/i2c/busses/i2c-st.c
|
||||
F: drivers/media/rc/st_rc.c
|
||||
F: drivers/mmc/host/sdhci-st.c
|
||||
|
|
|
@ -293,4 +293,12 @@ config CLKSRC_IMX_GPT
|
|||
depends on ARM && CLKDEV_LOOKUP
|
||||
select CLKSRC_MMIO
|
||||
|
||||
config CLKSRC_ST_LPC
|
||||
bool
|
||||
depends on ARCH_STI
|
||||
select CLKSRC_OF if OF
|
||||
help
|
||||
Enable this option to use the Low Power controller timer
|
||||
as clocksource.
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -60,3 +60,4 @@ obj-$(CONFIG_ASM9260_TIMER) += asm9260_timer.o
|
|||
obj-$(CONFIG_H8300) += h8300_timer8.o
|
||||
obj-$(CONFIG_H8300_TMR16) += h8300_timer16.o
|
||||
obj-$(CONFIG_H8300_TPU) += h8300_tpu.o
|
||||
obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o
|
||||
|
|
131
drivers/clocksource/clksrc_st_lpc.c
Normal file
131
drivers/clocksource/clksrc_st_lpc.c
Normal file
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Clocksource using the Low Power Timer found in the Low Power Controller (LPC)
|
||||
*
|
||||
* Copyright (C) 2015 STMicroelectronics – All Rights Reserved
|
||||
*
|
||||
* Author(s): Francesco Virlinzi <francesco.virlinzi@st.com>
|
||||
* Ajit Pal Singh <ajitpal.singh@st.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/sched_clock.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <dt-bindings/mfd/st-lpc.h>
|
||||
|
||||
/* Low Power Timer */
|
||||
#define LPC_LPT_LSB_OFF 0x400
|
||||
#define LPC_LPT_MSB_OFF 0x404
|
||||
#define LPC_LPT_START_OFF 0x408
|
||||
|
||||
static struct st_clksrc_ddata {
|
||||
struct clk *clk;
|
||||
void __iomem *base;
|
||||
} ddata;
|
||||
|
||||
static void __init st_clksrc_reset(void)
|
||||
{
|
||||
writel_relaxed(0, ddata.base + LPC_LPT_START_OFF);
|
||||
writel_relaxed(0, ddata.base + LPC_LPT_MSB_OFF);
|
||||
writel_relaxed(0, ddata.base + LPC_LPT_LSB_OFF);
|
||||
writel_relaxed(1, ddata.base + LPC_LPT_START_OFF);
|
||||
}
|
||||
|
||||
static u64 notrace st_clksrc_sched_clock_read(void)
|
||||
{
|
||||
return (u64)readl_relaxed(ddata.base + LPC_LPT_LSB_OFF);
|
||||
}
|
||||
|
||||
static int __init st_clksrc_init(void)
|
||||
{
|
||||
unsigned long rate;
|
||||
int ret;
|
||||
|
||||
st_clksrc_reset();
|
||||
|
||||
rate = clk_get_rate(ddata.clk);
|
||||
|
||||
sched_clock_register(st_clksrc_sched_clock_read, 32, rate);
|
||||
|
||||
ret = clocksource_mmio_init(ddata.base + LPC_LPT_LSB_OFF,
|
||||
"clksrc-st-lpc", rate, 300, 32,
|
||||
clocksource_mmio_readl_up);
|
||||
if (ret) {
|
||||
pr_err("clksrc-st-lpc: Failed to register clocksource\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init st_clksrc_setup_clk(struct device_node *np)
|
||||
{
|
||||
struct clk *clk;
|
||||
|
||||
clk = of_clk_get(np, 0);
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("clksrc-st-lpc: Failed to get LPC clock\n");
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
if (clk_prepare_enable(clk)) {
|
||||
pr_err("clksrc-st-lpc: Failed to enable LPC clock\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!clk_get_rate(clk)) {
|
||||
pr_err("clksrc-st-lpc: Failed to get LPC clock rate\n");
|
||||
clk_disable_unprepare(clk);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ddata.clk = clk;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __init st_clksrc_of_register(struct device_node *np)
|
||||
{
|
||||
int ret;
|
||||
uint32_t mode;
|
||||
|
||||
ret = of_property_read_u32(np, "st,lpc-mode", &mode);
|
||||
if (ret) {
|
||||
pr_err("clksrc-st-lpc: An LPC mode must be provided\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* LPC can either run as a Clocksource or in RTC or WDT mode */
|
||||
if (mode != ST_LPC_MODE_CLKSRC)
|
||||
return;
|
||||
|
||||
ddata.base = of_iomap(np, 0);
|
||||
if (!ddata.base) {
|
||||
pr_err("clksrc-st-lpc: Unable to map iomem\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (st_clksrc_setup_clk(np)) {
|
||||
iounmap(ddata.base);
|
||||
return;
|
||||
}
|
||||
|
||||
if (st_clksrc_init()) {
|
||||
clk_disable_unprepare(ddata.clk);
|
||||
clk_put(ddata.clk);
|
||||
iounmap(ddata.base);
|
||||
return;
|
||||
}
|
||||
|
||||
pr_info("clksrc-st-lpc: clocksource initialised - running @ %luHz\n",
|
||||
clk_get_rate(ddata.clk));
|
||||
}
|
||||
CLOCKSOURCE_OF_DECLARE(ddata, "st,stih407-lpc", st_clksrc_of_register);
|
|
@ -88,12 +88,13 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/itco_wdt.h>
|
||||
|
||||
#if (defined CONFIG_I2C_MUX_GPIO || defined CONFIG_I2C_MUX_GPIO_MODULE) && \
|
||||
defined CONFIG_DMI
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c-mux-gpio.h>
|
||||
#include <linux/platform_device.h>
|
||||
#endif
|
||||
|
||||
/* I801 SMBus address offsets */
|
||||
|
@ -113,6 +114,16 @@
|
|||
#define SMBPCICTL 0x004
|
||||
#define SMBPCISTS 0x006
|
||||
#define SMBHSTCFG 0x040
|
||||
#define TCOBASE 0x050
|
||||
#define TCOCTL 0x054
|
||||
|
||||
#define ACPIBASE 0x040
|
||||
#define ACPIBASE_SMI_OFF 0x030
|
||||
#define ACPICTRL 0x044
|
||||
#define ACPICTRL_EN 0x080
|
||||
|
||||
#define SBREG_BAR 0x10
|
||||
#define SBREG_SMBCTRL 0xc6000c
|
||||
|
||||
/* Host status bits for SMBPCISTS */
|
||||
#define SMBPCISTS_INTS 0x08
|
||||
|
@ -125,6 +136,9 @@
|
|||
#define SMBHSTCFG_SMB_SMI_EN 2
|
||||
#define SMBHSTCFG_I2C_EN 4
|
||||
|
||||
/* TCO configuration bits for TCOCTL */
|
||||
#define TCOCTL_EN 0x0100
|
||||
|
||||
/* Auxiliary control register bits, ICH4+ only */
|
||||
#define SMBAUXCTL_CRC 1
|
||||
#define SMBAUXCTL_E32B 2
|
||||
|
@ -221,6 +235,7 @@ struct i801_priv {
|
|||
const struct i801_mux_config *mux_drvdata;
|
||||
struct platform_device *mux_pdev;
|
||||
#endif
|
||||
struct platform_device *tco_pdev;
|
||||
};
|
||||
|
||||
#define FEATURE_SMBUS_PEC (1 << 0)
|
||||
|
@ -230,6 +245,7 @@ struct i801_priv {
|
|||
#define FEATURE_IRQ (1 << 4)
|
||||
/* Not really a feature, but it's convenient to handle it as such */
|
||||
#define FEATURE_IDF (1 << 15)
|
||||
#define FEATURE_TCO (1 << 16)
|
||||
|
||||
static const char *i801_feature_names[] = {
|
||||
"SMBus PEC",
|
||||
|
@ -1132,6 +1148,95 @@ static inline unsigned int i801_get_adapter_class(struct i801_priv *priv)
|
|||
}
|
||||
#endif
|
||||
|
||||
static const struct itco_wdt_platform_data tco_platform_data = {
|
||||
.name = "Intel PCH",
|
||||
.version = 4,
|
||||
};
|
||||
|
||||
static DEFINE_SPINLOCK(p2sb_spinlock);
|
||||
|
||||
static void i801_add_tco(struct i801_priv *priv)
|
||||
{
|
||||
struct pci_dev *pci_dev = priv->pci_dev;
|
||||
struct resource tco_res[3], *res;
|
||||
struct platform_device *pdev;
|
||||
unsigned int devfn;
|
||||
u32 tco_base, tco_ctl;
|
||||
u32 base_addr, ctrl_val;
|
||||
u64 base64_addr;
|
||||
|
||||
if (!(priv->features & FEATURE_TCO))
|
||||
return;
|
||||
|
||||
pci_read_config_dword(pci_dev, TCOBASE, &tco_base);
|
||||
pci_read_config_dword(pci_dev, TCOCTL, &tco_ctl);
|
||||
if (!(tco_ctl & TCOCTL_EN))
|
||||
return;
|
||||
|
||||
memset(tco_res, 0, sizeof(tco_res));
|
||||
|
||||
res = &tco_res[ICH_RES_IO_TCO];
|
||||
res->start = tco_base & ~1;
|
||||
res->end = res->start + 32 - 1;
|
||||
res->flags = IORESOURCE_IO;
|
||||
|
||||
/*
|
||||
* Power Management registers.
|
||||
*/
|
||||
devfn = PCI_DEVFN(PCI_SLOT(pci_dev->devfn), 2);
|
||||
pci_bus_read_config_dword(pci_dev->bus, devfn, ACPIBASE, &base_addr);
|
||||
|
||||
res = &tco_res[ICH_RES_IO_SMI];
|
||||
res->start = (base_addr & ~1) + ACPIBASE_SMI_OFF;
|
||||
res->end = res->start + 3;
|
||||
res->flags = IORESOURCE_IO;
|
||||
|
||||
/*
|
||||
* Enable the ACPI I/O space.
|
||||
*/
|
||||
pci_bus_read_config_dword(pci_dev->bus, devfn, ACPICTRL, &ctrl_val);
|
||||
ctrl_val |= ACPICTRL_EN;
|
||||
pci_bus_write_config_dword(pci_dev->bus, devfn, ACPICTRL, ctrl_val);
|
||||
|
||||
/*
|
||||
* We must access the NO_REBOOT bit over the Primary to Sideband
|
||||
* bridge (P2SB). The BIOS prevents the P2SB device from being
|
||||
* enumerated by the PCI subsystem, so we need to unhide/hide it
|
||||
* to lookup the P2SB BAR.
|
||||
*/
|
||||
spin_lock(&p2sb_spinlock);
|
||||
|
||||
devfn = PCI_DEVFN(PCI_SLOT(pci_dev->devfn), 1);
|
||||
|
||||
/* Unhide the P2SB device */
|
||||
pci_bus_write_config_byte(pci_dev->bus, devfn, 0xe1, 0x0);
|
||||
|
||||
pci_bus_read_config_dword(pci_dev->bus, devfn, SBREG_BAR, &base_addr);
|
||||
base64_addr = base_addr & 0xfffffff0;
|
||||
|
||||
pci_bus_read_config_dword(pci_dev->bus, devfn, SBREG_BAR + 0x4, &base_addr);
|
||||
base64_addr |= (u64)base_addr << 32;
|
||||
|
||||
/* Hide the P2SB device */
|
||||
pci_bus_write_config_byte(pci_dev->bus, devfn, 0xe1, 0x1);
|
||||
spin_unlock(&p2sb_spinlock);
|
||||
|
||||
res = &tco_res[ICH_RES_MEM_OFF];
|
||||
res->start = (resource_size_t)base64_addr + SBREG_SMBCTRL;
|
||||
res->end = res->start + 3;
|
||||
res->flags = IORESOURCE_MEM;
|
||||
|
||||
pdev = platform_device_register_resndata(&pci_dev->dev, "iTCO_wdt", -1,
|
||||
tco_res, 3, &tco_platform_data,
|
||||
sizeof(tco_platform_data));
|
||||
if (IS_ERR(pdev)) {
|
||||
dev_warn(&pci_dev->dev, "failed to create iTCO device\n");
|
||||
return;
|
||||
}
|
||||
|
||||
priv->tco_pdev = pdev;
|
||||
}
|
||||
|
||||
static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
unsigned char temp;
|
||||
|
@ -1149,6 +1254,15 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|||
|
||||
priv->pci_dev = dev;
|
||||
switch (dev->device) {
|
||||
case PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_SMBUS:
|
||||
priv->features |= FEATURE_I2C_BLOCK_READ;
|
||||
priv->features |= FEATURE_IRQ;
|
||||
priv->features |= FEATURE_SMBUS_PEC;
|
||||
priv->features |= FEATURE_BLOCK_BUFFER;
|
||||
priv->features |= FEATURE_TCO;
|
||||
break;
|
||||
|
||||
case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF0:
|
||||
case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF1:
|
||||
case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2:
|
||||
|
@ -1265,6 +1379,8 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|||
dev_info(&dev->dev, "SMBus using %s\n",
|
||||
priv->features & FEATURE_IRQ ? "PCI interrupt" : "polling");
|
||||
|
||||
i801_add_tco(priv);
|
||||
|
||||
/* set up the sysfs linkage to our parent device */
|
||||
priv->adapter.dev.parent = &dev->dev;
|
||||
|
||||
|
@ -1296,6 +1412,8 @@ static void i801_remove(struct pci_dev *dev)
|
|||
i2c_del_adapter(&priv->adapter);
|
||||
pci_write_config_byte(dev, SMBHSTCFG, priv->original_hstcfg);
|
||||
|
||||
platform_device_unregister(priv->tco_pdev);
|
||||
|
||||
/*
|
||||
* do not call pci_disable_device(dev) since it can cause hard hangs on
|
||||
* some systems during power-off (eg. Fujitsu-Siemens Lifebook E8010)
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
#include <linux/pci.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/lpc_ich.h>
|
||||
#include <linux/platform_data/itco_wdt.h>
|
||||
|
||||
#define ACPIBASE 0x40
|
||||
#define ACPIBASE_GPE_OFF 0x28
|
||||
|
@ -835,9 +836,31 @@ static void lpc_ich_enable_pmc_space(struct pci_dev *dev)
|
|||
priv->actrl_pbase_save = reg_save;
|
||||
}
|
||||
|
||||
static void lpc_ich_finalize_cell(struct pci_dev *dev, struct mfd_cell *cell)
|
||||
static int lpc_ich_finalize_wdt_cell(struct pci_dev *dev)
|
||||
{
|
||||
struct itco_wdt_platform_data *pdata;
|
||||
struct lpc_ich_priv *priv = pci_get_drvdata(dev);
|
||||
struct lpc_ich_info *info;
|
||||
struct mfd_cell *cell = &lpc_ich_cells[LPC_WDT];
|
||||
|
||||
pdata = devm_kzalloc(&dev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return -ENOMEM;
|
||||
|
||||
info = &lpc_chipset_info[priv->chipset];
|
||||
|
||||
pdata->version = info->iTCO_version;
|
||||
strlcpy(pdata->name, info->name, sizeof(pdata->name));
|
||||
|
||||
cell->platform_data = pdata;
|
||||
cell->pdata_size = sizeof(*pdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lpc_ich_finalize_gpio_cell(struct pci_dev *dev)
|
||||
{
|
||||
struct lpc_ich_priv *priv = pci_get_drvdata(dev);
|
||||
struct mfd_cell *cell = &lpc_ich_cells[LPC_GPIO];
|
||||
|
||||
cell->platform_data = &lpc_chipset_info[priv->chipset];
|
||||
cell->pdata_size = sizeof(struct lpc_ich_info);
|
||||
|
@ -933,7 +956,7 @@ static int lpc_ich_init_gpio(struct pci_dev *dev)
|
|||
lpc_chipset_info[priv->chipset].use_gpio = ret;
|
||||
lpc_ich_enable_gpio_space(dev);
|
||||
|
||||
lpc_ich_finalize_cell(dev, &lpc_ich_cells[LPC_GPIO]);
|
||||
lpc_ich_finalize_gpio_cell(dev);
|
||||
ret = mfd_add_devices(&dev->dev, PLATFORM_DEVID_AUTO,
|
||||
&lpc_ich_cells[LPC_GPIO], 1, NULL, 0, NULL);
|
||||
|
||||
|
@ -1007,7 +1030,10 @@ static int lpc_ich_init_wdt(struct pci_dev *dev)
|
|||
res->end = base_addr + ACPIBASE_PMC_END;
|
||||
}
|
||||
|
||||
lpc_ich_finalize_cell(dev, &lpc_ich_cells[LPC_WDT]);
|
||||
ret = lpc_ich_finalize_wdt_cell(dev);
|
||||
if (ret)
|
||||
goto wdt_done;
|
||||
|
||||
ret = mfd_add_devices(&dev->dev, PLATFORM_DEVID_AUTO,
|
||||
&lpc_ich_cells[LPC_WDT], 1, NULL, 0, NULL);
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
#include <linux/suspend.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <asm/intel_pmc_ipc.h>
|
||||
#include <linux/mfd/lpc_ich.h>
|
||||
#include <linux/platform_data/itco_wdt.h>
|
||||
|
||||
/*
|
||||
* IPC registers
|
||||
|
@ -460,9 +460,9 @@ static struct resource tco_res[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static struct lpc_ich_info tco_info = {
|
||||
static struct itco_wdt_platform_data tco_info = {
|
||||
.name = "Apollo Lake SoC",
|
||||
.iTCO_version = 3,
|
||||
.version = 3,
|
||||
};
|
||||
|
||||
static int ipc_create_punit_device(void)
|
||||
|
@ -539,8 +539,7 @@ static int ipc_create_tco_device(void)
|
|||
goto err;
|
||||
}
|
||||
|
||||
ret = platform_device_add_data(pdev, &tco_info,
|
||||
sizeof(struct lpc_ich_info));
|
||||
ret = platform_device_add_data(pdev, &tco_info, sizeof(tco_info));
|
||||
if (ret) {
|
||||
dev_err(ipcdev.dev, "Failed to add tco platform data\n");
|
||||
goto err;
|
||||
|
|
|
@ -208,7 +208,7 @@ static int st_rtc_probe(struct platform_device *pdev)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* LPC can either run in RTC or WDT mode */
|
||||
/* LPC can either run as a Clocksource or in RTC or WDT mode */
|
||||
if (mode != ST_LPC_MODE_RTC)
|
||||
return -ENODEV;
|
||||
|
||||
|
|
|
@ -797,7 +797,8 @@ config ITCO_WDT
|
|||
tristate "Intel TCO Timer/Watchdog"
|
||||
depends on (X86 || IA64) && PCI
|
||||
select WATCHDOG_CORE
|
||||
select LPC_ICH
|
||||
select LPC_ICH if !EXPERT
|
||||
select I2C_I801 if !EXPERT
|
||||
---help---
|
||||
Hardware driver for the intel TCO timer based watchdog devices.
|
||||
These drivers are included in the Intel 82801 I/O Controller
|
||||
|
|
|
@ -66,8 +66,7 @@
|
|||
#include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */
|
||||
#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
|
||||
#include <linux/io.h> /* For inb/outb/... */
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/lpc_ich.h>
|
||||
#include <linux/platform_data/itco_wdt.h>
|
||||
|
||||
#include "iTCO_vendor.h"
|
||||
|
||||
|
@ -146,59 +145,67 @@ static inline unsigned int ticks_to_seconds(int ticks)
|
|||
return iTCO_wdt_private.iTCO_version == 3 ? ticks : (ticks * 6) / 10;
|
||||
}
|
||||
|
||||
static inline u32 no_reboot_bit(void)
|
||||
{
|
||||
u32 enable_bit;
|
||||
|
||||
switch (iTCO_wdt_private.iTCO_version) {
|
||||
case 3:
|
||||
enable_bit = 0x00000010;
|
||||
break;
|
||||
case 2:
|
||||
enable_bit = 0x00000020;
|
||||
break;
|
||||
case 4:
|
||||
case 1:
|
||||
default:
|
||||
enable_bit = 0x00000002;
|
||||
break;
|
||||
}
|
||||
|
||||
return enable_bit;
|
||||
}
|
||||
|
||||
static void iTCO_wdt_set_NO_REBOOT_bit(void)
|
||||
{
|
||||
u32 val32;
|
||||
|
||||
/* Set the NO_REBOOT bit: this disables reboots */
|
||||
if (iTCO_wdt_private.iTCO_version == 3) {
|
||||
if (iTCO_wdt_private.iTCO_version >= 2) {
|
||||
val32 = readl(iTCO_wdt_private.gcs_pmc);
|
||||
val32 |= 0x00000010;
|
||||
writel(val32, iTCO_wdt_private.gcs_pmc);
|
||||
} else if (iTCO_wdt_private.iTCO_version == 2) {
|
||||
val32 = readl(iTCO_wdt_private.gcs_pmc);
|
||||
val32 |= 0x00000020;
|
||||
val32 |= no_reboot_bit();
|
||||
writel(val32, iTCO_wdt_private.gcs_pmc);
|
||||
} else if (iTCO_wdt_private.iTCO_version == 1) {
|
||||
pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32);
|
||||
val32 |= 0x00000002;
|
||||
val32 |= no_reboot_bit();
|
||||
pci_write_config_dword(iTCO_wdt_private.pdev, 0xd4, val32);
|
||||
}
|
||||
}
|
||||
|
||||
static int iTCO_wdt_unset_NO_REBOOT_bit(void)
|
||||
{
|
||||
int ret = 0;
|
||||
u32 val32;
|
||||
u32 enable_bit = no_reboot_bit();
|
||||
u32 val32 = 0;
|
||||
|
||||
/* Unset the NO_REBOOT bit: this enables reboots */
|
||||
if (iTCO_wdt_private.iTCO_version == 3) {
|
||||
if (iTCO_wdt_private.iTCO_version >= 2) {
|
||||
val32 = readl(iTCO_wdt_private.gcs_pmc);
|
||||
val32 &= 0xffffffef;
|
||||
val32 &= ~enable_bit;
|
||||
writel(val32, iTCO_wdt_private.gcs_pmc);
|
||||
|
||||
val32 = readl(iTCO_wdt_private.gcs_pmc);
|
||||
if (val32 & 0x00000010)
|
||||
ret = -EIO;
|
||||
} else if (iTCO_wdt_private.iTCO_version == 2) {
|
||||
val32 = readl(iTCO_wdt_private.gcs_pmc);
|
||||
val32 &= 0xffffffdf;
|
||||
writel(val32, iTCO_wdt_private.gcs_pmc);
|
||||
|
||||
val32 = readl(iTCO_wdt_private.gcs_pmc);
|
||||
if (val32 & 0x00000020)
|
||||
ret = -EIO;
|
||||
} else if (iTCO_wdt_private.iTCO_version == 1) {
|
||||
pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32);
|
||||
val32 &= 0xfffffffd;
|
||||
val32 &= ~enable_bit;
|
||||
pci_write_config_dword(iTCO_wdt_private.pdev, 0xd4, val32);
|
||||
|
||||
pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32);
|
||||
if (val32 & 0x00000002)
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
return ret; /* returns: 0 = OK, -EIO = Error */
|
||||
if (val32 & enable_bit)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iTCO_wdt_start(struct watchdog_device *wd_dev)
|
||||
|
@ -418,9 +425,9 @@ static int iTCO_wdt_probe(struct platform_device *dev)
|
|||
{
|
||||
int ret = -ENODEV;
|
||||
unsigned long val32;
|
||||
struct lpc_ich_info *ich_info = dev_get_platdata(&dev->dev);
|
||||
struct itco_wdt_platform_data *pdata = dev_get_platdata(&dev->dev);
|
||||
|
||||
if (!ich_info)
|
||||
if (!pdata)
|
||||
goto out;
|
||||
|
||||
spin_lock_init(&iTCO_wdt_private.io_lock);
|
||||
|
@ -435,7 +442,7 @@ static int iTCO_wdt_probe(struct platform_device *dev)
|
|||
if (!iTCO_wdt_private.smi_res)
|
||||
goto out;
|
||||
|
||||
iTCO_wdt_private.iTCO_version = ich_info->iTCO_version;
|
||||
iTCO_wdt_private.iTCO_version = pdata->version;
|
||||
iTCO_wdt_private.dev = dev;
|
||||
iTCO_wdt_private.pdev = to_pci_dev(dev->dev.parent);
|
||||
|
||||
|
@ -501,15 +508,24 @@ static int iTCO_wdt_probe(struct platform_device *dev)
|
|||
}
|
||||
|
||||
pr_info("Found a %s TCO device (Version=%d, TCOBASE=0x%04llx)\n",
|
||||
ich_info->name, ich_info->iTCO_version, (u64)TCOBASE);
|
||||
pdata->name, pdata->version, (u64)TCOBASE);
|
||||
|
||||
/* Clear out the (probably old) status */
|
||||
if (iTCO_wdt_private.iTCO_version == 3) {
|
||||
switch (iTCO_wdt_private.iTCO_version) {
|
||||
case 4:
|
||||
outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */
|
||||
outw(0x0002, TCO2_STS); /* Clear SECOND_TO_STS bit */
|
||||
break;
|
||||
case 3:
|
||||
outl(0x20008, TCO1_STS);
|
||||
} else {
|
||||
break;
|
||||
case 2:
|
||||
case 1:
|
||||
default:
|
||||
outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */
|
||||
outw(0x0002, TCO2_STS); /* Clear SECOND_TO_STS bit */
|
||||
outw(0x0004, TCO2_STS); /* Clear BOOT_STS bit */
|
||||
break;
|
||||
}
|
||||
|
||||
iTCO_wdt_watchdog_dev.bootstatus = 0;
|
||||
|
|
|
@ -11,5 +11,6 @@
|
|||
|
||||
#define ST_LPC_MODE_RTC 0
|
||||
#define ST_LPC_MODE_WDT 1
|
||||
#define ST_LPC_MODE_CLKSRC 2
|
||||
|
||||
#endif /* __DT_BINDINGS_ST_LPC_H__ */
|
||||
|
|
|
@ -20,12 +20,6 @@
|
|||
#ifndef LPC_ICH_H
|
||||
#define LPC_ICH_H
|
||||
|
||||
/* Watchdog resources */
|
||||
#define ICH_RES_IO_TCO 0
|
||||
#define ICH_RES_IO_SMI 1
|
||||
#define ICH_RES_MEM_OFF 2
|
||||
#define ICH_RES_MEM_GCS_PMC 0
|
||||
|
||||
/* GPIO resources */
|
||||
#define ICH_RES_GPIO 0
|
||||
#define ICH_RES_GPE0 1
|
||||
|
|
19
include/linux/platform_data/itco_wdt.h
Normal file
19
include/linux/platform_data/itco_wdt.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Platform data for the Intel TCO Watchdog
|
||||
*/
|
||||
|
||||
#ifndef _ITCO_WDT_H_
|
||||
#define _ITCO_WDT_H_
|
||||
|
||||
/* Watchdog resources */
|
||||
#define ICH_RES_IO_TCO 0
|
||||
#define ICH_RES_IO_SMI 1
|
||||
#define ICH_RES_MEM_OFF 2
|
||||
#define ICH_RES_MEM_GCS_PMC 0
|
||||
|
||||
struct itco_wdt_platform_data {
|
||||
char name[32];
|
||||
unsigned int version;
|
||||
};
|
||||
|
||||
#endif /* _ITCO_WDT_H_ */
|
Loading…
Reference in a new issue