diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-03-31 14:14:03 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-03-31 14:14:03 -0700 |
commit | 354b8bf222ee15bf9aac3d870ba8e0880dd9bc8d (patch) | |
tree | 4eac3e66f60f01fb4c6475999dafdbd01e419dd6 | |
parent | d4f1db771599852713fc2bbb7d3cc6cf9ace3b54 (diff) | |
parent | 826270373f17fd8ebd10753ca0a5fd2ceb1dc38e (diff) |
Merge tag 'linux-watchdog-5.18-rc1' of git://www.linux-watchdog.org/linux-watchdog
Pull watchdog updates from Wim Van Sebroeck:
- add support for BCM4908
- renesas_wdt: add R-Car Gen4 support
- improve watchdog_dev function documentation
- sp5100_tco: replace the cd6h/cd7h port I/O with MMIO accesses during
initialization
- several other small improvements and fixes
* tag 'linux-watchdog-5.18-rc1' of git://www.linux-watchdog.org/linux-watchdog:
Watchdog: sp5100_tco: Enable Family 17h+ CPUs
Watchdog: sp5100_tco: Add initialization using EFCH MMIO
Watchdog: sp5100_tco: Refactor MMIO base address initialization
Watchdog: sp5100_tco: Move timer initialization into function
watchdog: ixp4xx: Implement restart
watchdog: orion_wdt: support pretimeout on Armada-XP
watchdog: allow building BCM7038_WDT for BCM4908
watchdog: renesas_wdt: Add R-Car Gen4 support
dt-bindings: watchdog: renesas-wdt: Document r8a779f0 support
watchdog: Improve watchdog_dev function documentation
watchdog: aspeed: add nowayout support
watchdog: rti-wdt: Add missing pm_runtime_disable() in probe function
watchdog: imx2_wdg: Alow ping on suspend
-rw-r--r-- | Documentation/devicetree/bindings/watchdog/renesas,wdt.yaml | 5 | ||||
-rw-r--r-- | drivers/watchdog/Kconfig | 2 | ||||
-rw-r--r-- | drivers/watchdog/aspeed_wdt.c | 7 | ||||
-rw-r--r-- | drivers/watchdog/imx2_wdt.c | 27 | ||||
-rw-r--r-- | drivers/watchdog/ixp4xx_wdt.c | 14 | ||||
-rw-r--r-- | drivers/watchdog/orion_wdt.c | 13 | ||||
-rw-r--r-- | drivers/watchdog/renesas_wdt.c | 1 | ||||
-rw-r--r-- | drivers/watchdog/rti_wdt.c | 1 | ||||
-rw-r--r-- | drivers/watchdog/sp5100_tco.c | 334 | ||||
-rw-r--r-- | drivers/watchdog/sp5100_tco.h | 7 | ||||
-rw-r--r-- | drivers/watchdog/watchdog_dev.c | 244 |
11 files changed, 409 insertions, 246 deletions
diff --git a/Documentation/devicetree/bindings/watchdog/renesas,wdt.yaml b/Documentation/devicetree/bindings/watchdog/renesas,wdt.yaml index 91a98ccd4226..d060438e1402 100644 --- a/Documentation/devicetree/bindings/watchdog/renesas,wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/renesas,wdt.yaml @@ -55,6 +55,11 @@ properties: - renesas,r8a779a0-wdt # R-Car V3U - const: renesas,rcar-gen3-wdt # R-Car Gen3 and RZ/G2 + - items: + - enum: + - renesas,r8a779f0-wdt # R-Car S4-8 + - const: renesas,rcar-gen4-wdt # R-Car Gen4 + reg: maxItems: 1 diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 085f5a4ad06c..c4e82a8d863f 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1779,7 +1779,7 @@ config BCM7038_WDT tristate "BCM63xx/BCM7038 Watchdog" select WATCHDOG_CORE depends on HAS_IOMEM - depends on ARCH_BRCMSTB || BMIPS_GENERIC || BCM63XX || COMPILE_TEST + depends on ARCH_BCM4908 || ARCH_BRCMSTB || BMIPS_GENERIC || BCM63XX || COMPILE_TEST help Watchdog driver for the built-in hardware in Broadcom 7038 and later SoCs used in set-top boxes. BCM7038 was made public diff --git a/drivers/watchdog/aspeed_wdt.c b/drivers/watchdog/aspeed_wdt.c index 436571b6fc79..bd06622813eb 100644 --- a/drivers/watchdog/aspeed_wdt.c +++ b/drivers/watchdog/aspeed_wdt.c @@ -13,6 +13,11 @@ #include <linux/platform_device.h> #include <linux/watchdog.h> +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + struct aspeed_wdt { struct watchdog_device wdd; void __iomem *base; @@ -266,6 +271,8 @@ static int aspeed_wdt_probe(struct platform_device *pdev) wdt->wdd.timeout = WDT_DEFAULT_TIMEOUT; watchdog_init_timeout(&wdt->wdd, 0, dev); + watchdog_set_nowayout(&wdt->wdd, nowayout); + np = dev->of_node; ofdid = of_match_node(aspeed_wdt_of_table, np); diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index 51bfb796898b..d0c5d47ddede 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -66,6 +66,7 @@ struct imx2_wdt_device { struct watchdog_device wdog; bool ext_reset; bool clk_is_on; + bool no_ping; }; static bool nowayout = WATCHDOG_NOWAYOUT; @@ -312,12 +313,18 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) wdev->ext_reset = of_property_read_bool(dev->of_node, "fsl,ext-reset-output"); + /* + * The i.MX7D doesn't support low power mode, so we need to ping the watchdog + * during suspend. + */ + wdev->no_ping = !of_device_is_compatible(dev->of_node, "fsl,imx7d-wdt"); platform_set_drvdata(pdev, wdog); watchdog_set_drvdata(wdog, wdev); watchdog_set_nowayout(wdog, nowayout); watchdog_set_restart_priority(wdog, 128); watchdog_init_timeout(wdog, timeout, dev); - watchdog_stop_ping_on_suspend(wdog); + if (wdev->no_ping) + watchdog_stop_ping_on_suspend(wdog); if (imx2_wdt_is_running(wdev)) { imx2_wdt_set_timeout(wdog, wdog->timeout); @@ -366,9 +373,11 @@ static int __maybe_unused imx2_wdt_suspend(struct device *dev) imx2_wdt_ping(wdog); } - clk_disable_unprepare(wdev->clk); + if (wdev->no_ping) { + clk_disable_unprepare(wdev->clk); - wdev->clk_is_on = false; + wdev->clk_is_on = false; + } return 0; } @@ -380,11 +389,14 @@ static int __maybe_unused imx2_wdt_resume(struct device *dev) struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); int ret; - ret = clk_prepare_enable(wdev->clk); - if (ret) - return ret; + if (wdev->no_ping) { + ret = clk_prepare_enable(wdev->clk); - wdev->clk_is_on = true; + if (ret) + return ret; + + wdev->clk_is_on = true; + } if (watchdog_active(wdog) && !imx2_wdt_is_running(wdev)) { /* @@ -407,6 +419,7 @@ static SIMPLE_DEV_PM_OPS(imx2_wdt_pm_ops, imx2_wdt_suspend, static const struct of_device_id imx2_wdt_dt_ids[] = { { .compatible = "fsl,imx21-wdt", }, + { .compatible = "fsl,imx7d-wdt", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, imx2_wdt_dt_ids); diff --git a/drivers/watchdog/ixp4xx_wdt.c b/drivers/watchdog/ixp4xx_wdt.c index 31b03fa71341..281a48d9889f 100644 --- a/drivers/watchdog/ixp4xx_wdt.c +++ b/drivers/watchdog/ixp4xx_wdt.c @@ -84,10 +84,24 @@ static int ixp4xx_wdt_set_timeout(struct watchdog_device *wdd, return 0; } +static int ixp4xx_wdt_restart(struct watchdog_device *wdd, + unsigned long action, void *data) +{ + struct ixp4xx_wdt *iwdt = to_ixp4xx_wdt(wdd); + + __raw_writel(IXP4XX_WDT_KEY, iwdt->base + IXP4XX_OSWK_OFFSET); + __raw_writel(0, iwdt->base + IXP4XX_OSWT_OFFSET); + __raw_writel(IXP4XX_WDT_COUNT_ENABLE | IXP4XX_WDT_RESET_ENABLE, + iwdt->base + IXP4XX_OSWE_OFFSET); + + return 0; +} + static const struct watchdog_ops ixp4xx_wdt_ops = { .start = ixp4xx_wdt_start, .stop = ixp4xx_wdt_stop, .set_timeout = ixp4xx_wdt_set_timeout, + .restart = ixp4xx_wdt_restart, .owner = THIS_MODULE, }; diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c index 127eefc9161d..e25e6bf4647f 100644 --- a/drivers/watchdog/orion_wdt.c +++ b/drivers/watchdog/orion_wdt.c @@ -238,8 +238,10 @@ static int armada370_start(struct watchdog_device *wdt_dev) atomic_io_modify(dev->reg + TIMER_A370_STATUS, WDT_A370_EXPIRED, 0); /* Enable watchdog timer */ - atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, - dev->data->wdt_enable_bit); + reg = dev->data->wdt_enable_bit; + if (dev->wdt.info->options & WDIOF_PRETIMEOUT) + reg |= TIMER1_ENABLE_BIT; + atomic_io_modify(dev->reg + TIMER_CTRL, reg, reg); /* Enable reset on watchdog */ reg = readl(dev->rstout); @@ -312,7 +314,7 @@ static int armada375_stop(struct watchdog_device *wdt_dev) static int armada370_stop(struct watchdog_device *wdt_dev) { struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); - u32 reg; + u32 reg, mask; /* Disable reset on watchdog */ reg = readl(dev->rstout); @@ -320,7 +322,10 @@ static int armada370_stop(struct watchdog_device *wdt_dev) writel(reg, dev->rstout); /* Disable watchdog timer */ - atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0); + mask = dev->data->wdt_enable_bit; + if (wdt_dev->info->options & WDIOF_PRETIMEOUT) + mask |= TIMER1_ENABLE_BIT; + atomic_io_modify(dev->reg + TIMER_CTRL, mask, 0); return 0; } diff --git a/drivers/watchdog/renesas_wdt.c b/drivers/watchdog/renesas_wdt.c index 5791198960e6..41d58ea5eb2f 100644 --- a/drivers/watchdog/renesas_wdt.c +++ b/drivers/watchdog/renesas_wdt.c @@ -327,6 +327,7 @@ static SIMPLE_DEV_PM_OPS(rwdt_pm_ops, rwdt_suspend, rwdt_resume); static const struct of_device_id rwdt_ids[] = { { .compatible = "renesas,rcar-gen2-wdt", }, { .compatible = "renesas,rcar-gen3-wdt", }, + { .compatible = "renesas,rcar-gen4-wdt", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, rwdt_ids); diff --git a/drivers/watchdog/rti_wdt.c b/drivers/watchdog/rti_wdt.c index 117bc2a8eb0a..db843f825860 100644 --- a/drivers/watchdog/rti_wdt.c +++ b/drivers/watchdog/rti_wdt.c @@ -228,6 +228,7 @@ static int rti_wdt_probe(struct platform_device *pdev) ret = pm_runtime_get_sync(dev); if (ret) { pm_runtime_put_noidle(dev); + pm_runtime_disable(&pdev->dev); return dev_err_probe(dev, ret, "runtime pm failed\n"); } diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c index dd9a744f82f8..86ffb58fbc85 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c @@ -49,7 +49,7 @@ /* internal variables */ enum tco_reg_layout { - sp5100, sb800, efch + sp5100, sb800, efch, efch_mmio }; struct sp5100_tco { @@ -87,6 +87,10 @@ static enum tco_reg_layout tco_reg_layout(struct pci_dev *dev) dev->revision < 0x40) { return sp5100; } else if (dev->vendor == PCI_VENDOR_ID_AMD && + sp5100_tco_pci->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS && + sp5100_tco_pci->revision >= AMD_ZEN_SMBUS_PCI_REV) { + return efch_mmio; + } else if (dev->vendor == PCI_VENDOR_ID_AMD && ((dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS && dev->revision >= 0x41) || (dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS && @@ -209,6 +213,8 @@ static void tco_timer_enable(struct sp5100_tco *tco) ~EFCH_PM_WATCHDOG_DISABLE, EFCH_PM_DECODEEN_SECOND_RES); break; + default: + break; } } @@ -223,14 +229,195 @@ static u32 sp5100_tco_read_pm_reg32(u8 index) return val; } +static u32 sp5100_tco_request_region(struct device *dev, + u32 mmio_addr, + const char *dev_name) +{ + if (!devm_request_mem_region(dev, mmio_addr, SP5100_WDT_MEM_MAP_SIZE, + dev_name)) { + dev_dbg(dev, "MMIO address 0x%08x already in use\n", mmio_addr); + return 0; + } + + return mmio_addr; +} + +static u32 sp5100_tco_prepare_base(struct sp5100_tco *tco, + u32 mmio_addr, + u32 alt_mmio_addr, + const char *dev_name) +{ + struct device *dev = tco->wdd.parent; + + dev_dbg(dev, "Got 0x%08x from SBResource_MMIO register\n", mmio_addr); + + if (!mmio_addr && !alt_mmio_addr) + return -ENODEV; + + /* Check for MMIO address and alternate MMIO address conflicts */ + if (mmio_addr) + mmio_addr = sp5100_tco_request_region(dev, mmio_addr, dev_name); + + if (!mmio_addr && alt_mmio_addr) + mmio_addr = sp5100_tco_request_region(dev, alt_mmio_addr, dev_name); + + if (!mmio_addr) { + dev_err(dev, "Failed to reserve MMIO or alternate MMIO region\n"); + return -EBUSY; + } + + tco->tcobase = devm_ioremap(dev, mmio_addr, SP5100_WDT_MEM_MAP_SIZE); + if (!tco->tcobase) { + dev_err(dev, "MMIO address 0x%08x failed mapping\n", mmio_addr); + devm_release_mem_region(dev, mmio_addr, SP5100_WDT_MEM_MAP_SIZE); + return -ENOMEM; + } + + dev_info(dev, "Using 0x%08x for watchdog MMIO address\n", mmio_addr); + + return 0; +} + +static int sp5100_tco_timer_init(struct sp5100_tco *tco) +{ + struct watchdog_device *wdd = &tco->wdd; + struct device *dev = wdd->parent; + u32 val; + + val = readl(SP5100_WDT_CONTROL(tco->tcobase)); + if (val & SP5100_WDT_DISABLED) { + dev_err(dev, "Watchdog hardware is disabled\n"); + return -ENODEV; + } + + /* + * Save WatchDogFired status, because WatchDogFired flag is + * cleared here. + */ + if (val & SP5100_WDT_FIRED) + wdd->bootstatus = WDIOF_CARDRESET; + + /* Set watchdog action to reset the system */ + val &= ~SP5100_WDT_ACTION_RESET; + writel(val, SP5100_WDT_CONTROL(tco->tcobase)); + + /* Set a reasonable heartbeat before we stop the timer */ + tco_timer_set_timeout(wdd, wdd->timeout); + + /* + * Stop the TCO before we change anything so we don't race with + * a zeroed timer. + */ + tco_timer_stop(wdd); + + return 0; +} + +static u8 efch_read_pm_reg8(void __iomem *addr, u8 index) +{ + return readb(addr + index); +} + +static void efch_update_pm_reg8(void __iomem *addr, u8 index, u8 reset, u8 set) +{ + u8 val; + + val = readb(addr + index); + val &= reset; + val |= set; + writeb(val, addr + index); +} + +static void tco_timer_enable_mmio(void __iomem *addr) +{ + efch_update_pm_reg8(addr, EFCH_PM_DECODEEN3, + ~EFCH_PM_WATCHDOG_DISABLE, + EFCH_PM_DECODEEN_SECOND_RES); +} + +static int sp5100_tco_setupdevice_mmio(struct device *dev, + struct watchdog_device *wdd) +{ + struct sp5100_tco *tco = watchdog_get_drvdata(wdd); + const char *dev_name = SB800_DEVNAME; + u32 mmio_addr = 0, alt_mmio_addr = 0; + struct resource *res; + void __iomem *addr; + int ret; + u32 val; + + res = request_mem_region_muxed(EFCH_PM_ACPI_MMIO_PM_ADDR, + EFCH_PM_ACPI_MMIO_PM_SIZE, + "sp5100_tco"); + + if (!res) { + dev_err(dev, + "Memory region 0x%08x already in use\n", + EFCH_PM_ACPI_MMIO_PM_ADDR); + return -EBUSY; + } + + addr = ioremap(EFCH_PM_ACPI_MMIO_PM_ADDR, EFCH_PM_ACPI_MMIO_PM_SIZE); + if (!addr) { + dev_err(dev, "Address mapping failed\n"); + ret = -ENOMEM; + goto out; + } + + /* + * EFCH_PM_DECODEEN_WDT_TMREN is dual purpose. This bitfield + * enables sp5100_tco register MMIO space decoding. The bitfield + * also starts the timer operation. Enable if not already enabled. + */ + val = efch_read_pm_reg8(addr, EFCH_PM_DECODEEN); + if (!(val & EFCH_PM_DECODEEN_WDT_TMREN)) { + efch_update_pm_reg8(addr, EFCH_PM_DECODEEN, 0xff, + EFCH_PM_DECODEEN_WDT_TMREN); + } + + /* Error if the timer could not be enabled */ + val = efch_read_pm_reg8(addr, EFCH_PM_DECODEEN); + if (!(val & EFCH_PM_DECODEEN_WDT_TMREN)) { + dev_err(dev, "Failed to enable the timer\n"); + ret = -EFAULT; + goto out; + } + + mmio_addr = EFCH_PM_WDT_ADDR; + + /* Determine alternate MMIO base address */ + val = efch_read_pm_reg8(addr, EFCH_PM_ISACONTROL); + if (val & EFCH_PM_ISACONTROL_MMIOEN) + alt_mmio_addr = EFCH_PM_ACPI_MMIO_ADDR + + EFCH_PM_ACPI_MMIO_WDT_OFFSET; + + ret = sp5100_tco_prepare_base(tco, mmio_addr, alt_mmio_addr, dev_name); + if (!ret) { + tco_timer_enable_mmio(addr); + ret = sp5100_tco_timer_init(tco); + } + +out: + if (addr) + iounmap(addr); + + release_resource(res); + + return ret; +} + static int sp5100_tco_setupdevice(struct device *dev, struct watchdog_device *wdd) { struct sp5100_tco *tco = watchdog_get_drvdata(wdd); const char *dev_name; u32 mmio_addr = 0, val; + u32 alt_mmio_addr = 0; int ret; + if (tco->tco_reg_layout == efch_mmio) + return sp5100_tco_setupdevice_mmio(dev, wdd); + /* Request the IO ports used by this driver */ if (!request_muxed_region(SP5100_IO_PM_INDEX_REG, SP5100_PM_IOPORTS_SIZE, "sp5100_tco")) { @@ -247,138 +434,55 @@ static int sp5100_tco_setupdevice(struct device *dev, dev_name = SP5100_DEVNAME; mmio_addr = sp5100_tco_read_pm_reg32(SP5100_PM_WATCHDOG_BASE) & 0xfffffff8; + + /* + * Secondly, find the watchdog timer MMIO address + * from SBResource_MMIO register. + */ + + /* Read SBResource_MMIO from PCI config(PCI_Reg: 9Ch) */ + pci_read_config_dword(sp5100_tco_pci, + SP5100_SB_RESOURCE_MMIO_BASE, + &val); + + /* Verify MMIO is enabled and using bar0 */ + if ((val & SB800_ACPI_MMIO_MASK) == SB800_ACPI_MMIO_DECODE_EN) + alt_mmio_addr = (val & ~0xfff) + SB800_PM_WDT_MMIO_OFFSET; break; case sb800: dev_name = SB800_DEVNAME; mmio_addr = sp5100_tco_read_pm_reg32(SB800_PM_WATCHDOG_BASE) & 0xfffffff8; + + /* Read SBResource_MMIO from AcpiMmioEn(PM_Reg: 24h) */ + val = sp5100_tco_read_pm_reg32(SB800_PM_ACPI_MMIO_EN); + + /* Verify MMIO is enabled and using bar0 */ + if ((val & SB800_ACPI_MMIO_MASK) == SB800_ACPI_MMIO_DECODE_EN) + alt_mmio_addr = (val & ~0xfff) + SB800_PM_WDT_MMIO_OFFSET; break; case efch: dev_name = SB800_DEVNAME; - /* - * On Family 17h devices, the EFCH_PM_DECODEEN_WDT_TMREN bit of - * EFCH_PM_DECODEEN not only enables the EFCH_PM_WDT_ADDR memory - * region, it also enables the watchdog itself. - */ - if (boot_cpu_data.x86 == 0x17) { - val = sp5100_tco_read_pm_reg8(EFCH_PM_DECODEEN); - if (!(val & EFCH_PM_DECODEEN_WDT_TMREN)) { - sp5100_tco_update_pm_reg8(EFCH_PM_DECODEEN, 0xff, - EFCH_PM_DECODEEN_WDT_TMREN); - } - } val = sp5100_tco_read_pm_reg8(EFCH_PM_DECODEEN); if (val & EFCH_PM_DECODEEN_WDT_TMREN) mmio_addr = EFCH_PM_WDT_ADDR; + + val = sp5100_tco_read_pm_reg8(EFCH_PM_ISACONTROL); + if (val & EFCH_PM_ISACONTROL_MMIOEN) + alt_mmio_addr = EFCH_PM_ACPI_MMIO_ADDR + + EFCH_PM_ACPI_MMIO_WDT_OFFSET; break; default: return -ENODEV; } - /* Check MMIO address conflict */ - if (!mmio_addr || - !devm_request_mem_region(dev, mmio_addr, SP5100_WDT_MEM_MAP_SIZE, - dev_name)) { - if (mmio_addr) - dev_dbg(dev, "MMIO address 0x%08x already in use\n", - mmio_addr); - switch (tco->tco_reg_layout) { - case sp5100: - /* - * Secondly, Find the watchdog timer MMIO address - * from SBResource_MMIO register. - */ - /* Read SBResource_MMIO from PCI config(PCI_Reg: 9Ch) */ - pci_read_config_dword(sp5100_tco_pci, - SP5100_SB_RESOURCE_MMIO_BASE, - &mmio_addr); - if ((mmio_addr & (SB800_ACPI_MMIO_DECODE_EN | - SB800_ACPI_MMIO_SEL)) != - SB800_ACPI_MMIO_DECODE_EN) { - ret = -ENODEV; - goto unreg_region; - } - mmio_addr &= ~0xFFF; - mmio_addr += SB800_PM_WDT_MMIO_OFFSET; - break; - case sb800: - /* Read SBResource_MMIO from AcpiMmioEn(PM_Reg: 24h) */ - mmio_addr = - sp5100_tco_read_pm_reg32(SB800_PM_ACPI_MMIO_EN); - if ((mmio_addr & (SB800_ACPI_MMIO_DECODE_EN | - SB800_ACPI_MMIO_SEL)) != - SB800_ACPI_MMIO_DECODE_EN) { - ret = -ENODEV; - goto unreg_region; - } - mmio_addr &= ~0xFFF; - mmio_addr += SB800_PM_WDT_MMIO_OFFSET; - break; - case efch: - val = sp5100_tco_read_pm_reg8(EFCH_PM_ISACONTROL); - if (!(val & EFCH_PM_ISACONTROL_MMIOEN)) { - ret = -ENODEV; - goto unreg_region; - } - mmio_addr = EFCH_PM_ACPI_MMIO_ADDR + - EFCH_PM_ACPI_MMIO_WDT_OFFSET; - break; - } - dev_dbg(dev, "Got 0x%08x from SBResource_MMIO register\n", - mmio_addr); - if (!devm_request_mem_region(dev, mmio_addr, - SP5100_WDT_MEM_MAP_SIZE, - dev_name)) { - dev_dbg(dev, "MMIO address 0x%08x already in use\n", - mmio_addr); - ret = -EBUSY; - goto unreg_region; - } - } - - tco->tcobase = devm_ioremap(dev, mmio_addr, SP5100_WDT_MEM_MAP_SIZE); - if (!tco->tcobase) { - dev_err(dev, "failed to get tcobase address\n"); - ret = -ENOMEM; - goto unreg_region; - } - - dev_info(dev, "Using 0x%08x for watchdog MMIO address\n", mmio_addr); - - /* Setup the watchdog timer */ - tco_timer_enable(tco); - - val = readl(SP5100_WDT_CONTROL(tco->tcobase)); - if (val & SP5100_WDT_DISABLED) { - dev_err(dev, "Watchdog hardware is disabled\n"); - ret = -ENODEV; - goto unreg_region; + ret = sp5100_tco_prepare_base(tco, mmio_addr, alt_mmio_addr, dev_name); + if (!ret) { + /* Setup the watchdog timer */ + tco_timer_enable(tco); + ret = sp5100_tco_timer_init(tco); } - /* - * Save WatchDogFired status, because WatchDogFired flag is - * cleared here. - */ - if (val & SP5100_WDT_FIRED) - wdd->bootstatus = WDIOF_CARDRESET; - /* Set watchdog action to reset the system */ - val &= ~SP5100_WDT_ACTION_RESET; - writel(val, SP5100_WDT_CONTROL(tco->tcobase)); - - /* Set a reasonable heartbeat before we stop the timer */ - tco_timer_set_timeout(wdd, wdd->timeout); - - /* - * Stop the TCO before we change anything so we don't race with - * a zeroed timer. - */ - tco_timer_stop(wdd); - - release_region(SP5100_IO_PM_INDEX_REG, SP5100_PM_IOPORTS_SIZE); - - return 0; - -unreg_region: release_region(SP5100_IO_PM_INDEX_REG, SP5100_PM_IOPORTS_SIZE); return ret; } diff --git a/drivers/watchdog/sp5100_tco.h b/drivers/watchdog/sp5100_tco.h index adf015aa4126..6a0986d2c94b 100644 --- a/drivers/watchdog/sp5100_tco.h +++ b/drivers/watchdog/sp5100_tco.h @@ -58,6 +58,7 @@ #define SB800_PM_WATCHDOG_SECOND_RES GENMASK(1, 0) #define SB800_ACPI_MMIO_DECODE_EN BIT(0) #define SB800_ACPI_MMIO_SEL BIT(1) +#define SB800_ACPI_MMIO_MASK GENMASK(1, 0) #define SB800_PM_WDT_MMIO_OFFSET 0xB00 @@ -82,4 +83,10 @@ #define EFCH_PM_ISACONTROL_MMIOEN BIT(1) #define EFCH_PM_ACPI_MMIO_ADDR 0xfed80000 +#define EFCH_PM_ACPI_MMIO_PM_OFFSET 0x00000300 #define EFCH_PM_ACPI_MMIO_WDT_OFFSET 0x00000b00 + +#define EFCH_PM_ACPI_MMIO_PM_ADDR (EFCH_PM_ACPI_MMIO_ADDR + \ + EFCH_PM_ACPI_MMIO_PM_OFFSET) +#define EFCH_PM_ACPI_MMIO_PM_SIZE 8 +#define AMD_ZEN_SMBUS_PCI_REV 0x51 diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 3a3d8b5c7ad5..54903f3c851e 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -171,17 +171,17 @@ static int __watchdog_ping(struct watchdog_device *wdd) } /* - * watchdog_ping: ping the watchdog. - * @wdd: the watchdog device to ping + * watchdog_ping - ping the watchdog + * @wdd: The watchdog device to ping * - * The caller must hold wd_data->lock. + * If the watchdog has no own ping operation then it needs to be + * restarted via the start operation. This wrapper function does + * exactly that. + * We only ping when the watchdog device is running. + * The caller must hold wd_data->lock. * - * If the watchdog has no own ping operation then it needs to be - * restarted via the start operation. This wrapper function does - * exactly that. - * We only ping when the watchdog device is running. + * Return: 0 on success, error otherwise. */ - static int watchdog_ping(struct watchdog_device *wdd) { struct watchdog_core_data *wd_data = wdd->wd_data; @@ -231,16 +231,14 @@ static enum hrtimer_restart watchdog_timer_expired(struct hrtimer *timer) } /* - * watchdog_start: wrapper to start the watchdog. - * @wdd: the watchdog device to start + * watchdog_start - wrapper to start the watchdog + * @wdd: The watchdog device to start * - * The caller must hold wd_data->lock. + * Start the watchdog if it is not active and mark it active. + * The caller must hold wd_data->lock. * - * Start the watchdog if it is not active and mark it active. - * This function returns zero on success or a negative errno code for - * failure. + * Return: 0 on success or a negative errno code for failure. */ - static int watchdog_start(struct watchdog_device *wdd) { struct watchdog_core_data *wd_data = wdd->wd_data; @@ -274,17 +272,15 @@ static int watchdog_start(struct watchdog_device *wdd) } /* - * watchdog_stop: wrapper to stop the watchdog. - * @wdd: the watchdog device to stop + * watchdog_stop - wrapper to stop the watchdog + * @wdd: The watchdog device to stop * - * The caller must hold wd_data->lock. + * Stop the watchdog if it is still active and unmark it active. + * If the 'nowayout' feature was set, the watchdog cannot be stopped. + * The caller must hold wd_data->lock. * - * Stop the watchdog if it is still active and unmark it active. - * This function returns zero on success or a negative errno code for - * failure. - * If the 'nowayout' feature was set, the watchdog cannot be stopped. + * Return: 0 on success or a negative errno code for failure. */ - static int watchdog_stop(struct watchdog_device *wdd) { int err = 0; @@ -315,14 +311,14 @@ static int watchdog_stop(struct watchdog_device *wdd) } /* - * watchdog_get_status: wrapper to get the watchdog status - * @wdd: the watchdog device to get the status from + * watchdog_get_status - wrapper to get the watchdog status + * @wdd: The watchdog device to get the status from * - * The caller must hold wd_data->lock. + * Get the watchdog's status flags. + * The caller must hold wd_data->lock. * - * Get the watchdog's status flags. + * Return: watchdog's status flags. */ - static unsigned int watchdog_get_status(struct watchdog_device *wdd) { struct watchdog_core_data *wd_data = wdd->wd_data; @@ -352,13 +348,14 @@ static unsigned int watchdog_get_status(struct watchdog_device *wdd) } /* - * watchdog_set_timeout: set the watchdog timer timeout - * @wdd: the watchdog device to set the timeout for - * @timeout: timeout to set in seconds + * watchdog_set_timeout - set the watchdog timer timeout + * @wdd: The watchdog device to set the timeout for + * @timeout: Timeout to set in seconds + * + * The caller must hold wd_data->lock. * - * The caller must hold wd_data->lock. + * Return: 0 if successful, error otherwise. */ - static int watchdog_set_timeout(struct watchdog_device *wdd, unsigned int timeout) { @@ -385,11 +382,12 @@ static int watchdog_set_timeout(struct watchdog_device *wdd, } /* - * watchdog_set_pretimeout: set the watchdog timer pretimeout - * @wdd: the watchdog device to set the timeout for - * @timeout: pretimeout to set in seconds + * watchdog_set_pretimeout - set the watchdog timer pretimeout + * @wdd: The watchdog device to set the timeout for + * @timeout: pretimeout to set in seconds + * + * Return: 0 if successful, error otherwise. */ - static int watchdog_set_pretimeout(struct watchdog_device *wdd, unsigned int timeout) { @@ -410,15 +408,15 @@ static int watchdog_set_pretimeout(struct watchdog_device *wdd, } /* - * watchdog_get_timeleft: wrapper to get the time left before a reboot - * @wdd: the watchdog device to get the remaining time from - * @timeleft: the time that's left + * watchdog_get_timeleft - wrapper to get the time left before a reboot + * @wdd: The watchdog device to get the remaining time from + * @timeleft: The time that's left * - * The caller must hold wd_data->lock. + * Get the time before a watchdog will reboot (if not pinged). + * The caller must hold wd_data->lock. * - * Get the time before a watchdog will reboot (if not pinged). + * Return: 0 if successful, error otherwise. */ - static int watchdog_get_timeleft(struct watchdog_device *wdd, unsigned int *timeleft) { @@ -635,14 +633,15 @@ __ATTRIBUTE_GROUPS(wdt); #endif /* - * watchdog_ioctl_op: call the watchdog drivers ioctl op if defined - * @wdd: the watchdog device to do the ioctl on - * @cmd: watchdog command - * @arg: argument pointer + * watchdog_ioctl_op - call the watchdog drivers ioctl op if defined + * @wdd: The watchdog device to do the ioctl on + * @cmd: Watchdog command + * @arg: Argument pointer * - * The caller must hold wd_data->lock. + * The caller must hold wd_data->lock. + * + * Return: 0 if successful, error otherwise. */ - static int watchdog_ioctl_op(struct watchdog_device *wdd, unsigned int cmd, unsigned long arg) { @@ -653,17 +652,18 @@ static int watchdog_ioctl_op(struct watchdog_device *wdd, unsigned int cmd, } /* - * watchdog_write: writes to the watchdog. - * @file: file from VFS - * @data: user address of data - * @len: length of data - * @ppos: pointer to the file offset + * watchdog_write - writes to the watchdog + * @file: File from VFS + * @data: User address of data + * @len: Length of data + * @ppos: Pointer to the file offset * - * A write to a watchdog device is defined as a keepalive ping. - * Writing the magic 'V' sequence allows the next close to turn - * off the watchdog (if 'nowayout' is not set). + * A write to a watchdog device is defined as a keepalive ping. + * Writing the magic 'V' sequence allows the next close to turn + * off the watchdog (if 'nowayout' is not set). + * + * Return: @len if successful, error otherwise. */ - static ssize_t watchdog_write(struct file *file, const char __user *data, size_t len, loff_t *ppos) { @@ -706,13 +706,15 @@ static ssize_t watchdog_write(struct file *file, const char __user *data, } /* - * watchdog_ioctl: handle the different ioctl's for the watchdog device. - * @file: file handle to the device - * @cmd: watchdog command - * @arg: argument pointer + * watchdog_ioctl - handle the different ioctl's for the watchdog device + * @file: File handle to the device + * @cmd: Watchdog command + * @arg: Argument pointer * - * The watchdog API defines a common set of functions for all watchdogs - * according to their available features. + * The watchdog API defines a common set of functions for all watchdogs + * according to their available features. + * + * Return: 0 if successful, error otherwise. */ static long watchdog_ioctl(struct file *file, unsigned int cmd, @@ -819,15 +821,16 @@ out_ioctl: } /* - * watchdog_open: open the /dev/watchdog* devices. - * @inode: inode of device - * @file: file handle to device + * watchdog_open - open the /dev/watchdog* devices + * @inode: Inode of device + * @file: File handle to device + * + * When the /dev/watchdog* device gets opened, we start the watchdog. + * Watch out: the /dev/watchdog device is single open, so we make sure + * it can only be opened once. * - * When the /dev/watchdog* device gets opened, we start the watchdog. - * Watch out: the /dev/watchdog device is single open, so we make sure - * it can only be opened once. + * Return: 0 if successful, error otherwise. */ - static int watchdog_open(struct inode *inode, struct file *file) { struct watchdog_core_data *wd_data; @@ -896,15 +899,16 @@ static void watchdog_core_data_release(struct device *dev) } /* - * watchdog_release: release the watchdog device. - * @inode: inode of device - * @file: file handle to device + * watchdog_release - release the watchdog device + * @inode: Inode of device + * @file: File handle to device + * + * This is the code for when /dev/watchdog gets closed. We will only + * stop the watchdog when we have received the magic char (and nowayout + * was not set), else the watchdog will keep running. * - * This is the code for when /dev/watchdog gets closed. We will only - * stop the watchdog when we have received the magic char (and nowayout - * was not set), else the watchdog will keep running. + * Always returns 0. */ - static int watchdog_release(struct inode *inode, struct file *file) { struct watchdog_core_data *wd_data = file->private_data; @@ -977,14 +981,15 @@ static struct class watchdog_class = { }; /* - * watchdog_cdev_register: register watchdog character device - * @wdd: watchdog device + * watchdog_cdev_register - register watchdog character device + * @wdd: Watchdog device + * + * Register a watchdog character device including handling the legacy + * /dev/watchdog node. /dev/watchdog is actually a miscdevice and + * thus we set it up like that. * - * Register a watchdog character device including handling the legacy - * /dev/watchdog node. /dev/watchdog is actually a miscdevice and - * thus we set it up like that. + * Return: 0 if successful, error otherwise. */ - static int watchdog_cdev_register(struct watchdog_device *wdd) { struct watchdog_core_data *wd_data; @@ -1074,13 +1079,12 @@ static int watchdog_cdev_register(struct watchdog_device *wdd) } /* - * watchdog_cdev_unregister: unregister watchdog character device - * @watchdog: watchdog device + * watchdog_cdev_unregister - unregister watchdog character device + * @wdd: Watchdog device * - * Unregister watchdog character device and if needed the legacy - * /dev/watchdog device. + * Unregister watchdog character device and if needed the legacy + * /dev/watchdog device. */ - static void watchdog_cdev_unregister(struct watchdog_device *wdd) { struct watchdog_core_data *wd_data = wdd->wd_data; @@ -1109,15 +1113,16 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd) put_device(&wd_data->dev); } -/* - * watchdog_dev_register: register a watchdog device - * @wdd: watchdog device +/** + * watchdog_dev_register - register a watchdog device + * @wdd: Watchdog device + * + * Register a watchdog device including handling the legacy + * /dev/watchdog node. /dev/watchdog is actually a miscdevice and + * thus we set it up like that. * - * Register a watchdog device including handling the legacy - * /dev/watchdog node. /dev/watchdog is actually a miscdevice and - * thus we set it up like that. + * Return: 0 if successful, error otherwise. */ - int watchdog_dev_register(struct watchdog_device *wdd) { int ret; @@ -1133,30 +1138,31 @@ int watchdog_dev_register(struct watchdog_device *wdd) return ret; } -/* - * watchdog_dev_unregister: unregister a watchdog device - * @watchdog: watchdog device +/** + * watchdog_dev_unregister - unregister a watchdog device + * @wdd: watchdog device * - * Unregister watchdog device and if needed the legacy - * /dev/watchdog device. + * Unregister watchdog device and if needed the legacy + * /dev/watchdog device. */ - void watchdog_dev_unregister(struct watchdog_device *wdd) { watchdog_unregister_pretimeout(wdd); watchdog_cdev_unregister(wdd); } -/* - * watchdog_set_last_hw_keepalive: set last HW keepalive time for watchdog - * @wdd: watchdog device - * @last_ping_ms: time since last HW heartbeat +/** + * watchdog_set_last_hw_keepalive - set last HW keepalive time for watchdog + * @wdd: Watchdog device + * @last_ping_ms: Time since last HW heartbeat * - * Adjusts the last known HW keepalive time for a watchdog timer. - * This is needed if the watchdog is already running when the probe - * function is called, and it can't be pinged immediately. This - * function must be called immediately after watchdog registration, - * and min_hw_heartbeat_ms must be set for this to be useful. + * Adjusts the last known HW keepalive time for a watchdog timer. + * This is needed if the watchdog is already running when the probe + * function is called, and it can't be pinged immediately. This + * function must be called immediately after watchdog registration, + * and min_hw_heartbeat_ms must be set for this to be useful. + * + * Return: 0 if successful, error otherwise. */ int watchdog_set_last_hw_keepalive(struct watchdog_device *wdd, unsigned int last_ping_ms) @@ -1180,12 +1186,13 @@ int watchdog_set_last_hw_keepalive(struct watchdog_device *wdd, } EXPORT_SYMBOL_GPL(watchdog_set_last_hw_keepalive); -/* - * watchdog_dev_init: init dev part of watchdog core +/** + * watchdog_dev_init - init dev part of watchdog core * - * Allocate a range of chardev nodes to use for watchdog devices + * Allocate a range of chardev nodes to use for watchdog devices. + * + * Return: 0 if successful, error otherwise. */ - int __init watchdog_dev_init(void) { int err; @@ -1218,12 +1225,11 @@ err_register: return err; } -/* - * watchdog_dev_exit: exit dev part of watchdog core +/** + * watchdog_dev_exit - exit dev part of watchdog core * - * Release the range of chardev nodes used for watchdog devices + * Release the range of chardev nodes used for watchdog devices. */ - void __exit watchdog_dev_exit(void) { unregister_chrdev_region(watchdog_devt, MAX_DOGS); |