From 6f2536141ce12e405d70f868451e706216e7e52d Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 12 May 2015 13:58:09 +0100 Subject: mfd: dt-bindings: Provide human readable define for Clocksource mode ST's Low Power Controller can now operate in three supported modes; Watchdog, Real Time Clock and most recently as a Clocksource. This new define will allow the LPC IP to be configured for Clocksource from DT. Signed-off-by: Lee Jones --- include/dt-bindings/mfd/st-lpc.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/dt-bindings/mfd/st-lpc.h b/include/dt-bindings/mfd/st-lpc.h index e3e6c75d8822..d05894afa7e7 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__ */ -- cgit From 70bef01c0f1c9a55b54b625be3f82ff7ee1e8c05 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 26 May 2015 13:39:43 +0100 Subject: clocksource: sti: Provide support for the ST LPC Clocksource IP This IP is shared with Watchdog and RTC functionality. All 3 of these devices are mutually exclusive from one another i.e. Only 1 IP can be used at any given time. We use the device-driver model combined with a DT 'mode' property to enforce this. The ST LPC Clocksource IP can be used as the system (tick) timer. Acked-by: Daniel Lezcano Signed-off-by: Lee Jones --- drivers/clocksource/Kconfig | 8 +++ drivers/clocksource/Makefile | 1 + drivers/clocksource/clksrc_st_lpc.c | 123 ++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 drivers/clocksource/clksrc_st_lpc.c diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 4e57730e0be4..ae3ec9da0d15 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 f228354961ca..1c2c9d2cd859 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 000000000000..f38cf33281a2 --- /dev/null +++ b/drivers/clocksource/clksrc_st_lpc.c @@ -0,0 +1,123 @@ +/* + * 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 + +/* 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 int __init st_clksrc_init(void) +{ + unsigned long rate; + int ret; + + st_clksrc_reset(); + + rate = clk_get_rate(ddata.clk); + + 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); -- cgit From ff45d8dd84dbb6e674e3757a07ef9471568c2e94 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 12 May 2015 13:58:11 +0100 Subject: clocksource: sti: Provide 'use timer as sched clock' capability Acked-by: Daniel Lezcano Signed-off-by: Lee Jones --- drivers/clocksource/clksrc_st_lpc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/clocksource/clksrc_st_lpc.c b/drivers/clocksource/clksrc_st_lpc.c index f38cf33281a2..65ec4674416d 100644 --- a/drivers/clocksource/clksrc_st_lpc.c +++ b/drivers/clocksource/clksrc_st_lpc.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -38,6 +39,11 @@ static void __init st_clksrc_reset(void) 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; @@ -47,6 +53,8 @@ static int __init st_clksrc_init(void) 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); -- cgit From bea6356c12600b683de55a70e6a4c6cc36fa640f Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 12 May 2015 13:58:12 +0100 Subject: clocksource: bindings: Provide bindings for ST's LPC Clocksource device On current ST platforms the LPC controls a number of functions including Watchdog and Real Time Clock. This patch provides the bindings used to configure LPC in Clocksource mode. Signed-off-by: Lee Jones --- .../devicetree/bindings/timer/st,stih407-lpc | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 Documentation/devicetree/bindings/timer/st,stih407-lpc diff --git a/Documentation/devicetree/bindings/timer/st,stih407-lpc b/Documentation/devicetree/bindings/timer/st,stih407-lpc new file mode 100644 index 000000000000..72acb487b856 --- /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 = ; + }; -- cgit From 82c32c0326dc758bdf202c82cb05594ba0dd0d2e Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 12 May 2015 13:58:14 +0100 Subject: watchdog: bindings: Supply knowledge of a third supported device - clocksource Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/watchdog/st_lpc_wdt.txt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/watchdog/st_lpc_wdt.txt b/Documentation/devicetree/bindings/watchdog/st_lpc_wdt.txt index 388c88a01222..039c5ca45577 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] -- cgit From 742e40978ff79cc92ba982a27a55b957226f9dbe Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 12 May 2015 13:58:15 +0100 Subject: rtc: st: Update IP layout information to include Clocksource Initial submission adding support for this IP only included Watchdog and the Real-Time Clock. Now the third (and final) device is enabled this trivial patch is required to update the comment in the RTC driver to encompass Clocksource. Acked-by: Alexandre Belloni Signed-off-by: Lee Jones --- drivers/rtc/rtc-st-lpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-st-lpc.c b/drivers/rtc/rtc-st-lpc.c index 3f9d0acb81c7..74c0a336ceea 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; -- cgit From c74a06dce5f82ea4e94a552d486293667c905267 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 12 May 2015 13:58:16 +0100 Subject: rtc: bindings: Supply knowledge of a third supported device - clocksource Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/rtc/rtc-st-lpc.txt | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/rtc/rtc-st-lpc.txt b/Documentation/devicetree/bindings/rtc/rtc-st-lpc.txt index 73407f502e4e..daf88265df32 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 { -- cgit From 82805d1b3ee0d93d08fe8957f253642a6f778f9a Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 12 May 2015 13:58:17 +0100 Subject: MAINTAINERS: Add the LPC Clocksource to STi maintained driver list Signed-off-by: Lee Jones --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index fd6078443083..64cf79158390 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 -- cgit From 420b54de25828c45f3fc1f12d52d9657f5e90a53 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Thu, 6 Aug 2015 13:46:24 +0100 Subject: mfd: watchdog: iTCO_wdt: Expose watchdog properties using platform data Intel Sunrisepoint (Skylake PCH) has the iTCO watchdog accessible across the SMBus, unlike previous generations of PCH/ICH where it was on the LPC bus. Because it's on the SMBus, it doesn't make sense to pass around a 'struct lpc_ich_info', and leaking the type of bus into the iTCO watchdog driver is kind of backwards anyway. This change introduces a new 'struct itco_wdt_platform_data' for use inside the iTCO watchdog driver and by the upcoming Intel Sunrisepoint code, which neatly avoids having to include lpc_ich headers in the i801 i2c driver. This change is overdue because lpc_ich_info has already found its way into other TCO watchdog users, notably the intel_pmc_ipc driver where the watchdog actually isn't on the LPC bus as far as I can see. A simple translation layer is provided for converting from the existing 'struct lpc_ich_info' inside the lpc_ich mfd driver. Signed-off-by: Matt Fleming Acked-by: Darren Hart [drivers/x86 refactoring] Reviewed-by: Guenter Roeck Signed-off-by: Lee Jones --- drivers/mfd/lpc_ich.c | 32 +++++++++++++++++++++++++++++--- drivers/platform/x86/intel_pmc_ipc.c | 9 ++++----- drivers/watchdog/iTCO_wdt.c | 11 +++++------ include/linux/mfd/lpc_ich.h | 6 ------ include/linux/platform_data/itco_wdt.h | 19 +++++++++++++++++++ 5 files changed, 57 insertions(+), 20 deletions(-) create mode 100644 include/linux/platform_data/itco_wdt.h diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index 8de34398abc0..c5a9a08b5dfb 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 @@ gpe0_done: 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 d734763dab69..fbd1cc7d5de3 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/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 3c3fd417ddeb..a94401b2deca 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" @@ -418,9 +417,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 +434,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,7 +500,7 @@ 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) { diff --git a/include/linux/mfd/lpc_ich.h b/include/linux/mfd/lpc_ich.h index 8feac782fa83..2b300b44f994 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 000000000000..f16542c77ff7 --- /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_ */ -- cgit From 9424693035a57961a8eb09e96aab315a7096535d Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 6 Aug 2015 13:46:25 +0100 Subject: i2c: i801: Create iTCO device on newer Intel PCHs Starting from Intel Sunrisepoint (Skylake PCH) the iTCO watchdog resources have been moved to reside under the i801 SMBus host controller whereas previously they were under the LPC device. In order to support the iTCO watchdog on newer PCHs we need to create the platform device here in the SMBus driver and pass all known resources using platform data. Signed-off-by: Mika Westerberg Signed-off-by: Matt Fleming Reviewed-by: Guenter Roeck Acked-by: Wolfram Sang Signed-off-by: Lee Jones --- drivers/i2c/busses/i2c-i801.c | 120 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 5ecbb3fdc27e..eaef9bc9d88c 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) -- cgit From 2a7a0e9bf7b32e838d873226808ab8a6c00148f7 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Thu, 6 Aug 2015 13:46:26 +0100 Subject: watchdog: iTCO_wdt: Add support for TCO on Intel Sunrisepoint The revision of the watchdog hardware in Sunrisepoint necessitates a new "version" inside the TCO watchdog driver because some of the register layouts have changed. Also update the Kconfig entry to select both the LPC and SMBus drivers since the TCO device is on the SMBus in Sunrisepoint. Signed-off-by: Matt Fleming Reviewed-by: Guenter Roeck Signed-off-by: Lee Jones --- drivers/watchdog/Kconfig | 3 +- drivers/watchdog/iTCO_wdt.c | 71 ++++++++++++++++++++++++++++----------------- 2 files changed, 46 insertions(+), 28 deletions(-) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 241fafde42cb..55c4b5b0a317 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 a94401b2deca..0acc6c5f729d 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -145,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) @@ -503,12 +511,21 @@ static int iTCO_wdt_probe(struct platform_device *dev) 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; -- cgit