diff --git a/Documentation/devicetree/bindings/rtc/rtc-st-lpc.txt b/Documentation/devicetree/bindings/rtc/rtc-st-lpc.txt index 73407f502e4e93b8ec5146f8ed218e5b7cf8408e..daf88265df32391004a72238c5c39061d707eb84 100644 --- a/Documentation/devicetree/bindings/rtc/rtc-st-lpc.txt +++ b/Documentation/devicetree/bindings/rtc/rtc-st-lpc.txt @@ -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 { diff --git a/Documentation/devicetree/bindings/timer/st,stih407-lpc b/Documentation/devicetree/bindings/timer/st,stih407-lpc new file mode 100644 index 0000000000000000000000000000000000000000..72acb487b856dfc88983396e5ac17dd03f4b2db7 --- /dev/null +++ b/Documentation/devicetree/bindings/timer/st,stih407-lpc @@ -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 = ; + }; diff --git a/Documentation/devicetree/bindings/watchdog/st_lpc_wdt.txt b/Documentation/devicetree/bindings/watchdog/st_lpc_wdt.txt index 388c88a0122244b2adcabe6724f7fb8eae4a4c03..039c5ca45577c3c4a5f00c4279d9b9374d312c92 100644 --- a/Documentation/devicetree/bindings/watchdog/st_lpc_wdt.txt +++ b/Documentation/devicetree/bindings/watchdog/st_lpc_wdt.txt @@ -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] diff --git a/MAINTAINERS b/MAINTAINERS index fd60784430838fb99d5cb13ae8767298f6b9b35d..64cf79158390b2c7d52494bd37274424c3e75d0b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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 diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 4e57730e0be4e919203e21e070fb43666bb7b61d..ae3ec9da0d153525cade63a6b883a4b2f623fdb2 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -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 diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index f228354961ca6f853a193f39b0a5fa2a63e47cd4..1c2c9d2cd85997e206a25693cfcf0f02782a4d29 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -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 diff --git a/drivers/clocksource/clksrc_st_lpc.c b/drivers/clocksource/clksrc_st_lpc.c new file mode 100644 index 0000000000000000000000000000000000000000..65ec4674416d0e70567164059a71c01b0fa7813e --- /dev/null +++ b/drivers/clocksource/clksrc_st_lpc.c @@ -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 + * Ajit Pal Singh + * + * 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 +#include +#include +#include +#include +#include + +#include + +/* 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); diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 5ecbb3fdc27ee2c0c8c87e35cc4aa08d9621bb3f..eaef9bc9d88c469bfa1b4516b9ecb88c0211480d 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -88,12 +88,13 @@ #include #include #include +#include +#include #if (defined CONFIG_I2C_MUX_GPIO || defined CONFIG_I2C_MUX_GPIO_MODULE) && \ defined CONFIG_DMI #include #include -#include #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) diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index 8de34398abc085956831c40ed8cca6c85c3516d8..c5a9a08b5dfbf91ce609b22bb4fbcf331871237f 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -66,6 +66,7 @@ #include #include #include +#include #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); diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c index d734763dab6970fac3b9916a4596b1fe2d0fcd58..fbd1cc7d5de3bc1bf049a7ed6812172bac78c374 100644 --- a/drivers/platform/x86/intel_pmc_ipc.c +++ b/drivers/platform/x86/intel_pmc_ipc.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include /* * 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; diff --git a/drivers/rtc/rtc-st-lpc.c b/drivers/rtc/rtc-st-lpc.c index 3f9d0acb81c7a8023cdf978964777046b7f53c72..74c0a336ceea0650965701f93399814299275bd1 100644 --- a/drivers/rtc/rtc-st-lpc.c +++ b/drivers/rtc/rtc-st-lpc.c @@ -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; diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 241fafde42cb54ceb2e50918e4a02e1c64f4f923..55c4b5b0a3173f799c4df9b294c9f4b8dac886ba 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -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 diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 3c3fd417ddeb135db14f50a9de9a5d8411bab8e8..0acc6c5f729d6f107783e46c94a6f758716c5db1 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -66,8 +66,7 @@ #include /* For spin_lock/spin_unlock/... */ #include /* For copy_to_user/put_user/... */ #include /* For inb/outb/... */ -#include -#include +#include #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) { - val32 = readl(iTCO_wdt_private.gcs_pmc); - val32 |= 0x00000010; - writel(val32, iTCO_wdt_private.gcs_pmc); - } else if (iTCO_wdt_private.iTCO_version == 2) { + 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) { - val32 = readl(iTCO_wdt_private.gcs_pmc); - val32 &= 0xffffffef; - 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) { + if (iTCO_wdt_private.iTCO_version >= 2) { val32 = readl(iTCO_wdt_private.gcs_pmc); - val32 &= 0xffffffdf; + val32 &= ~enable_bit; 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; diff --git a/include/dt-bindings/mfd/st-lpc.h b/include/dt-bindings/mfd/st-lpc.h index e3e6c75d8822cf2d2d6485c872bccd645e97ddd0..d05894afa7e720310665e47d583d2c3cc35fbad0 100644 --- a/include/dt-bindings/mfd/st-lpc.h +++ b/include/dt-bindings/mfd/st-lpc.h @@ -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__ */ diff --git a/include/linux/mfd/lpc_ich.h b/include/linux/mfd/lpc_ich.h index 8feac782fa835e8a78c6b6b6ae65e5e4da6602eb..2b300b44f99440ad4e0be7c57bb7f07cc5d811d1 100644 --- a/include/linux/mfd/lpc_ich.h +++ b/include/linux/mfd/lpc_ich.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 diff --git a/include/linux/platform_data/itco_wdt.h b/include/linux/platform_data/itco_wdt.h new file mode 100644 index 0000000000000000000000000000000000000000..f16542c77ff794ad13a1e81a54a8d1d57b5d1c94 --- /dev/null +++ b/include/linux/platform_data/itco_wdt.h @@ -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_ */