diff options
Diffstat (limited to 'drivers/watchdog')
26 files changed, 1267 insertions, 188 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 080bc91ff7..762e37c9c2 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config WATCHDOG_IMX_RESET_SOURCE bool @@ -30,7 +31,7 @@ config WATCHDOG_AT91SAM9 config WATCHDOG_EFI bool "Generic EFI Watchdog Driver" - depends on EFI_BOOTUP + depends on EFI_PAYLOAD help Add support for the EFI watchdog. @@ -58,6 +59,12 @@ config WATCHDOG_IMX help Add support for watchdog found on Freescale i.MX SoCs. +config WATCHDOG_IMXULP + bool "i.MX ULP watchdog" + depends on ARCH_IMX || COMPILE_TEST + help + Add support for watchdog found on Freescale i.MX SoCs. + config WATCHDOG_JZ4740 bool "Ingenic jz4740 SoC hardware watchdog" depends on MACH_MIPS_XBURST || COMPILE_TEST @@ -96,7 +103,7 @@ config RAVE_SP_WATCHDOG config STM32_IWDG_WATCHDOG bool "STM32 IWDG" - depends on ARCH_STM32MP || COMPILE_TEST + depends on ARCH_STM32 || COMPILE_TEST select MFD_SYSCON help Enable to support configuration of the STM32's on-SoC IWDG watchdog. @@ -108,6 +115,12 @@ config STPMIC1_WATCHDOG help Enable to support configuration of the stpmic1's built-in watchdog. +config RN568_WATCHDOG + bool "Ricoh RN5t568 PMIC based Watchdog" + depends on MFD_RN568PMIC + help + Enable to support system control via the PMIC based watchdog. + config F71808E_WDT bool "Fintek F718xx, F818xx Super I/O Watchdog" depends on X86 @@ -148,4 +161,20 @@ config STARFIVE_WDT If you say yes here you get support for the watchdog device on StarFive SoCs. +config WDAT_WDT + bool "ACPI Watchdog Action Table (WDAT)" + depends on X86 + depends on ACPI + help + This driver adds support for systems with ACPI Watchdog Action + Table (WDAT) table. Servers typically have this but it can be + found on some desktop machines as well. This driver will take + over the native iTCO watchdog driver found on many Intel CPUs. + +config CADENCE_WATCHDOG + tristate "Cadence Watchdog Timer" + help + Say Y here if you want to include support for the watchdog + timer in the Xilinx Zynq. + endif diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 4e784b5aaa..2b0da7cea9 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_WATCHDOG) += wd_core.o obj-$(CONFIG_WATCHDOG_AR9344) += ar9344_wdt.o obj-$(CONFIG_WATCHDOG_AT91SAM9) += at91sam9_wdt.o @@ -9,13 +10,17 @@ obj-$(CONFIG_WATCHDOG_DW) += dw_wdt.o obj-$(CONFIG_WATCHDOG_JZ4740) += jz4740.o obj-$(CONFIG_WATCHDOG_IMX_RESET_SOURCE) += imxwd.o obj-$(CONFIG_WATCHDOG_IMX) += imxwd.o +obj-$(CONFIG_WATCHDOG_IMXULP) += imxulp-wdt.o obj-$(CONFIG_WATCHDOG_KVX) += kvx_wdt.o obj-$(CONFIG_WATCHDOG_ORION) += orion_wdt.o obj-$(CONFIG_ARCH_BCM283X) += bcm2835_wdt.o obj-$(CONFIG_RAVE_SP_WATCHDOG) += rave-sp-wdt.o obj-$(CONFIG_STM32_IWDG_WATCHDOG) += stm32_iwdg.o obj-$(CONFIG_STPMIC1_WATCHDOG) += stpmic1_wdt.o +obj-$(CONFIG_RN568_WATCHDOG) += rn5t568_wdt.o obj-$(CONFIG_F71808E_WDT) += f71808e_wdt.o obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o obj-$(CONFIG_ITCO_WDT) += itco_wdt.o obj-$(CONFIG_STARFIVE_WDT) += starfive_wdt.o +obj-$(CONFIG_WDAT_WDT) += wdat_wdt.o +obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o diff --git a/drivers/watchdog/ar9344_wdt.c b/drivers/watchdog/ar9344_wdt.c index c7cd552dc7..50e83fa685 100644 --- a/drivers/watchdog/ar9344_wdt.c +++ b/drivers/watchdog/ar9344_wdt.c @@ -1,13 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * AR9344 Watchdog driver * * Copyright (C) 2017 Oleksij Rempel <linux@rempel-privat.de> - * - * 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 <common.h> @@ -34,7 +29,7 @@ struct ar9344_wd { struct watchdog wd; void __iomem *base; - struct device_d *dev; + struct device *dev; unsigned int rate; }; @@ -70,7 +65,7 @@ static void ar9344_watchdog_detect_reset_source(struct ar9344_wd *priv) /* else keep the default 'unknown' state */ } -static int ar9344_wdt_probe(struct device_d *dev) +static int ar9344_wdt_probe(struct device *dev) { struct resource *iores; struct ar9344_wd *priv; @@ -129,8 +124,9 @@ static __maybe_unused struct of_device_id ar9344_wdt_dt_ids[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, ar9344_wdt_dt_ids); -static struct driver_d ar9344_wdt_driver = { +static struct driver ar9344_wdt_driver = { .name = "ar9344-wdt", .probe = ar9344_wdt_probe, .of_compatible = DRV_OF_COMPAT(ar9344_wdt_dt_ids), diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c index fe6f2e0408..46bb986229 100644 --- a/drivers/watchdog/at91sam9_wdt.c +++ b/drivers/watchdog/at91sam9_wdt.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2019 Pengutronix, Ahmad Fatoum <a.fatoum@pengutronix.de> */ @@ -8,7 +8,7 @@ #include <io.h> #include <watchdog.h> #include <linux/clk.h> -#include <mach/at91_wdt.h> +#include <mach/at91/at91_wdt.h> #define MIN_WDT_TIMEOUT 1 #define MAX_WDT_TIMEOUT 16 @@ -54,7 +54,7 @@ static inline bool at91sam9x_wdt_is_disabled(struct at91sam9x_wdt *wdt) return readl(wdt->base + AT91_WDT_MR) & AT91_WDT_WDDIS; } -static int at91sam9x_wdt_probe(struct device_d *dev) +static int at91sam9x_wdt_probe(struct device *dev) { struct at91sam9x_wdt *wdt; struct resource *iores; @@ -95,8 +95,9 @@ static const __maybe_unused struct of_device_id at91sam9x_wdt_dt_ids[] = { { .compatible = "atmel,sama5d4-wdt", }, { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, at91sam9x_wdt_dt_ids); -static struct driver_d at91sam9x_wdt_driver = { +static struct driver at91sam9x_wdt_driver = { .name = "at91sam9x-wdt", .of_compatible = DRV_OF_COMPAT(at91sam9x_wdt_dt_ids), .probe = at91sam9x_wdt_probe, diff --git a/drivers/watchdog/bcm2835_wdt.c b/drivers/watchdog/bcm2835_wdt.c index 233eaa85c1..874315d502 100644 --- a/drivers/watchdog/bcm2835_wdt.c +++ b/drivers/watchdog/bcm2835_wdt.c @@ -1,18 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2017 Pengutronix, Lucas Stach <l.stach@pengutronix.de> * * Based on code from Carlo Caione <carlo@carlocaione.org> - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <common.h> #include <init.h> @@ -20,28 +10,7 @@ #include <restart.h> #include <watchdog.h> -#define PM_RSTC 0x1c -#define PM_RSTS 0x20 -#define PM_WDOG 0x24 - -#define PM_WDOG_RESET 0000000000 -#define PM_PASSWORD 0x5a000000 -#define PM_WDOG_TIME_SET 0x000fffff -#define PM_RSTC_WRCFG_CLR 0xffffffcf -#define PM_RSTC_WRCFG_SET 0x00000030 -#define PM_RSTC_WRCFG_FULL_RESET 0x00000020 -#define PM_RSTC_RESET 0x00000102 - -#define PM_RSTS_HADPOR_SET 0x00001000 -#define PM_RSTS_HADSRH_SET 0x00000400 -#define PM_RSTS_HADSRF_SET 0x00000200 -#define PM_RSTS_HADSRQ_SET 0x00000100 -#define PM_RSTS_HADWRH_SET 0x00000040 -#define PM_RSTS_HADWRF_SET 0x00000020 -#define PM_RSTS_HADWRQ_SET 0x00000010 -#define PM_RSTS_HADDRH_SET 0x00000004 -#define PM_RSTS_HADDRF_SET 0x00000002 -#define PM_RSTS_HADDRQ_SET 0x00000001 +#include <soc/bcm283x/wdt.h> #define SECS_TO_WDOG_TICKS(x) ((x) << 16) @@ -52,7 +21,7 @@ struct bcm2835_wd { struct watchdog wd; void __iomem *base; - struct device_d *dev; + struct device *dev; struct restart_handler restart; }; @@ -91,7 +60,7 @@ static int bcm2835_wd_set_timeout(struct watchdog *wd, unsigned timeout) return 0; } -static int bcm2835_wd_probe(struct device_d *dev) +static int bcm2835_wd_probe(struct device *dev) { struct resource *iores; struct bcm2835_wd *priv; @@ -132,8 +101,9 @@ static __maybe_unused struct of_device_id bcm2835_wd_dt_ids[] = { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, bcm2835_wd_dt_ids); -static struct driver_d bcm2835_wd_driver = { +static struct driver bcm2835_wd_driver = { .name = "bcm2835_wd", .of_compatible = DRV_OF_COMPAT(bcm2835_wd_dt_ids), .probe = bcm2835_wd_probe, diff --git a/drivers/watchdog/cadence_wdt.c b/drivers/watchdog/cadence_wdt.c new file mode 100644 index 0000000000..17655a188c --- /dev/null +++ b/drivers/watchdog/cadence_wdt.c @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Cadence WDT driver - Used by Xilinx Zynq + * + * Copyright (C) 2010 - 2014 Xilinx, Inc. + * + */ + +#include <common.h> +#include <init.h> +#include <io.h> +#include <of.h> +#include <restart.h> +#include <watchdog.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/reset.h> + +/* Supports 1 - 516 sec */ +#define CDNS_WDT_MAX_TIMEOUT 516 + +/* Restart key */ +#define CDNS_WDT_RESTART_KEY 0x00001999 + +/* Counter register access key */ +#define CDNS_WDT_REGISTER_ACCESS_KEY 0x00920000 + +/* Counter value divisor */ +#define CDNS_WDT_COUNTER_VALUE_DIVISOR 0x1000 + +/* Clock prescaler value and selection */ +#define CDNS_WDT_PRESCALE_64 64 +#define CDNS_WDT_PRESCALE_512 512 +#define CDNS_WDT_PRESCALE_4096 4096 +#define CDNS_WDT_PRESCALE_SELECT_64 1 +#define CDNS_WDT_PRESCALE_SELECT_512 2 +#define CDNS_WDT_PRESCALE_SELECT_4096 3 + +/* Input clock frequency */ +#define CDNS_WDT_CLK_10MHZ 10000000 +#define CDNS_WDT_CLK_75MHZ 75000000 + +/* Counter maximum value */ +#define CDNS_WDT_COUNTER_MAX 0xFFF + +/** + * struct cdns_wdt - Watchdog device structure + * @regs: baseaddress of device + * @clk: struct clk * of a clock source + * @prescaler: for saving prescaler value + * @ctrl_clksel: counter clock prescaler selection + * @cdns_wdt_device: watchdog device structure + * + * Structure containing parameters specific to cadence watchdog. + */ +struct cdns_wdt { + void __iomem *regs; + struct clk *clk; + u32 prescaler; + u32 ctrl_clksel; + struct watchdog cdns_wdt_device; + unsigned timeout; +}; + +static inline struct cdns_wdt *to_cdns_wdt(struct watchdog *wdd) +{ + return container_of(wdd, struct cdns_wdt, cdns_wdt_device); +} + +/* Write access to Registers */ +static inline void cdns_wdt_writereg(struct cdns_wdt *wdt, u32 offset, u32 val) +{ + writel_relaxed(val, wdt->regs + offset); +} + +/*************************Register Map**************************************/ + +/* Register Offsets for the WDT */ +#define CDNS_WDT_ZMR_OFFSET 0x0 /* Zero Mode Register */ +#define CDNS_WDT_CCR_OFFSET 0x4 /* Counter Control Register */ +#define CDNS_WDT_RESTART_OFFSET 0x8 /* Restart Register */ +#define CDNS_WDT_SR_OFFSET 0xC /* Status Register */ + +/* + * Zero Mode Register - This register controls how the time out is indicated + * and also contains the access code to allow writes to the register (0xABC). + */ +#define CDNS_WDT_ZMR_WDEN_MASK 0x00000001 /* Enable the WDT */ +#define CDNS_WDT_ZMR_RSTEN_MASK 0x00000002 /* Enable the reset output */ +#define CDNS_WDT_ZMR_IRQEN_MASK 0x00000004 /* Enable IRQ output */ +#define CDNS_WDT_ZMR_RSTLEN_16 0x00000030 /* Reset pulse of 16 pclk cycles */ +#define CDNS_WDT_ZMR_ZKEY_VAL 0x00ABC000 /* Access key, 0xABC << 12 */ +/* + * Counter Control register - This register controls how fast the timer runs + * and the reset value and also contains the access code to allow writes to + * the register. + */ +#define CDNS_WDT_CCR_CRV_MASK 0x00003FFC /* Counter reset value */ + +/** + * cdns_wdt_stop - Stop the watchdog. + * + * @wdt: cadence watchdog device + * + * Read the contents of the ZMR register, clear the WDEN bit + * in the register and set the access key for successful write. + */ +static void cdns_wdt_stop(struct cdns_wdt *wdt) +{ + cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET, + CDNS_WDT_ZMR_ZKEY_VAL & (~CDNS_WDT_ZMR_WDEN_MASK)); +} + +/** + * cdns_wdt_reload - Reload the watchdog timer (i.e. pat the watchdog). + * + * @wdt: cadence watchdog device + * + * Write the restart key value (0x00001999) to the restart register. + */ +static void cdns_wdt_reload(struct cdns_wdt *wdt) +{ + cdns_wdt_writereg(wdt, CDNS_WDT_RESTART_OFFSET, + CDNS_WDT_RESTART_KEY); +} + +/** + * cdns_wdt_start - Enable and start the watchdog. + * + * @wdt: cadence watchdog device + * @timeout: new timeout + * + * The counter value is calculated according to the formula: + * calculated count = (timeout * clock) / prescaler + 1. + * The calculated count is divided by 0x1000 to obtain the field value + * to write to counter control register. + * Clears the contents of prescaler and counter reset value. Sets the + * prescaler to 4096 and the calculated count and access key + * to write to CCR Register. + * Sets the WDT (WDEN bit) and either the Reset signal(RSTEN bit) + * or Interrupt signal(IRQEN) with a specified cycles and the access + * key to write to ZMR Register. + */ +static void cdns_wdt_start(struct cdns_wdt *wdt, unsigned timeout) +{ + unsigned int data = 0; + unsigned short count; + unsigned long clock_f = clk_get_rate(wdt->clk); + + /* + * Counter value divisor to obtain the value of + * counter reset to be written to control register. + */ + count = (timeout * (clock_f / wdt->prescaler)) / + CDNS_WDT_COUNTER_VALUE_DIVISOR + 1; + + if (count > CDNS_WDT_COUNTER_MAX) + count = CDNS_WDT_COUNTER_MAX; + + cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET, + CDNS_WDT_ZMR_ZKEY_VAL); + + count = (count << 2) & CDNS_WDT_CCR_CRV_MASK; + + /* Write counter access key first to be able write to register */ + data = count | CDNS_WDT_REGISTER_ACCESS_KEY | wdt->ctrl_clksel; + cdns_wdt_writereg(wdt, CDNS_WDT_CCR_OFFSET, data); + data = CDNS_WDT_ZMR_WDEN_MASK | CDNS_WDT_ZMR_RSTLEN_16 | + CDNS_WDT_ZMR_ZKEY_VAL; + + /* Reset on timeout regardless of what's specified in device tree. */ + data |= CDNS_WDT_ZMR_RSTEN_MASK; + data &= ~CDNS_WDT_ZMR_IRQEN_MASK; + + cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET, data); + cdns_wdt_writereg(wdt, CDNS_WDT_RESTART_OFFSET, + CDNS_WDT_RESTART_KEY); +} + +/** + * cdns_wdt_settimeout - Set a new timeout value for the watchdog device. + * + * @wdd: watchdog device + * @new_time: new timeout value that needs to be set + * Return: 0 on success + * + * Update the watchdog device timeout with new value which is used when + * cdns_wdt_start is called. + */ +static int cdns_wdt_settimeout(struct watchdog *wdd, + unsigned int new_time) +{ + struct cdns_wdt *wdt = to_cdns_wdt(wdd); + + if (new_time > wdd->timeout_max) + return -EINVAL; + + if (new_time == 0) { + cdns_wdt_stop(wdt); + } else if (wdt->timeout != new_time) { + cdns_wdt_start(wdt, new_time); + wdt->timeout = new_time; + } else { + cdns_wdt_reload(wdt); + } + + return 0; +} + +/************************Platform Operations*****************************/ +/** + * cdns_wdt_probe - Probe call for the device. + * + * @pdev: handle to the platform device structure. + * Return: 0 on success, negative error otherwise. + * + * It does all the memory allocation and registration for the device. + */ +static int cdns_wdt_probe(struct device *dev) +{ + unsigned long clock_f; + struct cdns_wdt *wdt; + struct resource *res; + struct watchdog *cdns_wdt_device; + + wdt = xzalloc(sizeof(*wdt)); + + cdns_wdt_device = &wdt->cdns_wdt_device; + cdns_wdt_device->name = "cdns_wdt"; + cdns_wdt_device->hwdev = dev; + cdns_wdt_device->set_timeout = cdns_wdt_settimeout; + cdns_wdt_device->timeout_max = CDNS_WDT_MAX_TIMEOUT; + + res = dev_request_mem_resource(dev, 0); + if (IS_ERR(res)) + return PTR_ERR(res); + + wdt->regs = IOMEM(res->start); + + /* We don't service interrupts in barebox, so a watchdog that doesn't + * reset the system isn't a useful thing to register + */ + if (!of_property_read_bool(dev->of_node, "reset-on-timeout")) + dev_notice(dev, "proceeding as if reset-on-timeout was set\n"); + + wdt->clk = clk_get_enabled(dev, NULL); + if (IS_ERR(wdt->clk)) + return dev_err_probe(dev, PTR_ERR(wdt->clk), + "input clock not found\n"); + + clock_f = clk_get_rate(wdt->clk); + if (clock_f <= CDNS_WDT_CLK_75MHZ) { + wdt->prescaler = CDNS_WDT_PRESCALE_512; + wdt->ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_512; + } else { + wdt->prescaler = CDNS_WDT_PRESCALE_4096; + wdt->ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_4096; + } + + return watchdog_register(cdns_wdt_device); +} + +static const struct of_device_id cdns_wdt_of_match[] = { + { .compatible = "cdns,wdt-r1p2", }, + { /* end of table */ } +}; +MODULE_DEVICE_TABLE(of, cdns_wdt_of_match); + +static struct driver cdns_wdt_driver = { + .name = "cdns-wdt", + .probe = cdns_wdt_probe, + .of_match_table = cdns_wdt_of_match, +}; +device_platform_driver(cdns_wdt_driver); + +MODULE_AUTHOR("Xilinx, Inc."); +MODULE_DESCRIPTION("Watchdog driver for Cadence WDT"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/davinci_wdt.c b/drivers/watchdog/davinci_wdt.c index 2ac5f8b38d..0b2df50c48 100644 --- a/drivers/watchdog/davinci_wdt.c +++ b/drivers/watchdog/davinci_wdt.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * drivers/char/watchdog/davinci_wdt.c * @@ -6,10 +7,7 @@ * Copyright (C) 2006-2013 Texas Instruments. * Copyright (C) 2015 Jan Luebbe <jluebbe@debian.org> * - * 2007 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. + * 2007 (c) MontaVista Software, Inc. */ #include <common.h> @@ -129,7 +127,7 @@ static int davinci_wdt_set_timeout(struct watchdog *wd, unsigned timeout) return 0; } -static int davinci_wdt_probe(struct device_d *dev) +static int davinci_wdt_probe(struct device *dev) { struct resource *iores; int ret = 0; @@ -162,8 +160,9 @@ static __maybe_unused struct of_device_id davinci_wdt_of_match[] = { { .compatible = "ti,davinci-wdt", }, {}, }; +MODULE_DEVICE_TABLE(of, davinci_wdt_of_match); -static struct driver_d platform_wdt_driver = { +static struct driver platform_wdt_driver = { .name = "davinci-wdt", .of_compatible = DRV_OF_COMPAT(davinci_wdt_of_match), .probe = davinci_wdt_probe, diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index 774d211a5f..178e0a29f1 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -1,12 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright 2010-2011 Picochip Ltd., Jamie Iles * http://www.picochip.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. - * * This file implements a driver for the Synopsys DesignWare watchdog device * in the many subsystems. The watchdog has 16 different timeout periods * and these are a function of the input clock frequency. @@ -132,7 +128,7 @@ static void __noreturn dw_wdt_restart_handle(struct restart_handler *rst) hang(); } -static int dw_wdt_drv_probe(struct device_d *dev) +static int dw_wdt_drv_probe(struct device *dev) { struct watchdog *wdd; struct dw_wdt *dw_wdt; @@ -200,8 +196,9 @@ static struct of_device_id dw_wdt_of_match[] = { { .compatible = "snps,dw-wdt", }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, dw_wdt_of_match); -static struct driver_d dw_wdt_driver = { +static struct driver dw_wdt_driver = { .name = "dw-wdt", .probe = dw_wdt_drv_probe, .of_compatible = DRV_OF_COMPAT(dw_wdt_of_match), diff --git a/drivers/watchdog/efi_wdt.c b/drivers/watchdog/efi_wdt.c index 1512b1d99a..072240fcaf 100644 --- a/drivers/watchdog/efi_wdt.c +++ b/drivers/watchdog/efi_wdt.c @@ -7,12 +7,12 @@ #include <init.h> #include <driver.h> #include <efi.h> -#include <efi/efi.h> +#include <efi/efi-payload.h> #include <watchdog.h> struct efi_wdt_priv { struct watchdog wd; - struct device_d *dev; + struct device *dev; }; #define to_efi_wdt(h) container_of(h, struct efi_wdt_priv, wd) @@ -31,7 +31,7 @@ static int efi_wdt_set_timeout(struct watchdog *wd, unsigned timeout) return 0; } -static int efi_wdt_probe(struct device_d *dev) +static int efi_wdt_probe(struct device *dev) { struct efi_wdt_priv *priv; int ret; @@ -58,7 +58,7 @@ on_error: return ret; } -static struct driver_d efi_wdt_driver = { +static struct driver efi_wdt_driver = { .name = "efi-wdt", .probe = efi_wdt_probe, }; diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c index 925c2f809d..5bee066366 100644 --- a/drivers/watchdog/f71808e_wdt.c +++ b/drivers/watchdog/f71808e_wdt.c @@ -13,7 +13,7 @@ #include <asm/io.h> #include <driver.h> #include <watchdog.h> -#include <printk.h> +#include <linux/printk.h> #include <reset_source.h> #include <superio.h> #include <common.h> @@ -239,7 +239,7 @@ static int f71808e_wdt_set_timeout(struct watchdog *wdd, unsigned int new_timeou return 0; } -static int f71808e_wdt_init(struct f71808e_wdt *wd, struct device_d *dev) +static int f71808e_wdt_init(struct f71808e_wdt *wd, struct device *dev) { struct watchdog *wdd = &wd->wdd; const char * const *names = pulse_width_names; @@ -376,7 +376,7 @@ static struct platform_device_id f71808e_wdt_ids[] = { { /* sentinel */ }, }; -static int f71808e_probe(struct device_d *dev) +static int f71808e_probe(struct device *dev) { struct f71808e_wdt *wd; struct resource *res; @@ -396,7 +396,7 @@ static int f71808e_probe(struct device_d *dev) return f71808e_wdt_init(wd, dev); } -static struct driver_d f71808e_wdt_driver = { +static struct driver f71808e_wdt_driver = { .probe = f71808e_probe, .name = "f71808e_wdt", .id_table = f71808e_wdt_ids, diff --git a/drivers/watchdog/gpio_wdt.c b/drivers/watchdog/gpio_wdt.c index 4de49dcee3..5fa98f87c6 100644 --- a/drivers/watchdog/gpio_wdt.c +++ b/drivers/watchdog/gpio_wdt.c @@ -9,7 +9,7 @@ #include <driver.h> #include <watchdog.h> #include <superio.h> -#include <gpiod.h> +#include <linux/gpio/consumer.h> enum { HW_ALGO_TOGGLE, @@ -17,7 +17,7 @@ enum { }; struct gpio_wdt_priv { - int gpio; + struct gpio_desc *gpiod; bool state; bool started; unsigned int hw_algo; @@ -32,11 +32,11 @@ static inline struct gpio_wdt_priv *to_gpio_wdt_priv(struct watchdog *wdd) static void gpio_wdt_disable(struct gpio_wdt_priv *priv) { /* Eternal ping */ - gpio_set_active(priv->gpio, 1); + gpiod_set_value(priv->gpiod, 1); /* Put GPIO back to tristate */ if (priv->hw_algo == HW_ALGO_TOGGLE) - gpio_direction_input(priv->gpio); + gpiod_direction_input(priv->gpiod); priv->started = false; } @@ -47,13 +47,13 @@ static void gpio_wdt_ping(struct gpio_wdt_priv *priv) case HW_ALGO_TOGGLE: /* Toggle output pin */ priv->state = !priv->state; - gpio_set_active(priv->gpio, priv->state); + gpiod_set_value(priv->gpiod, priv->state); break; case HW_ALGO_LEVEL: /* Pulse */ - gpio_set_active(priv->gpio, true); + gpiod_set_value(priv->gpiod, true); udelay(1); - gpio_set_active(priv->gpio, false); + gpiod_set_value(priv->gpiod, false); break; } } @@ -61,7 +61,7 @@ static void gpio_wdt_ping(struct gpio_wdt_priv *priv) static void gpio_wdt_start(struct gpio_wdt_priv *priv) { priv->state = false; - gpio_direction_active(priv->gpio, priv->state); + gpiod_direction_output(priv->gpiod, priv->state); priv->started = true; } @@ -81,9 +81,9 @@ static int gpio_wdt_set_timeout(struct watchdog *wdd, unsigned int new_timeout) return 0; } -static int gpio_wdt_probe(struct device_d *dev) +static int gpio_wdt_probe(struct device *dev) { - struct device_node *np = dev->device_node; + struct device_node *np = dev->of_node; struct gpio_wdt_priv *priv; enum gpiod_flags gflags; unsigned int hw_margin; @@ -113,9 +113,9 @@ static int gpio_wdt_probe(struct device_d *dev) return -EINVAL; } - priv->gpio = gpiod_get(dev, NULL, gflags); - if (priv->gpio < 0) - return priv->gpio; + priv->gpiod = gpiod_get(dev, NULL, gflags); + if (IS_ERR(priv->gpiod)) + return PTR_ERR(priv->gpiod); priv->wdd.hwdev = dev; priv->wdd.timeout_max = hw_margin / 1000; @@ -129,8 +129,9 @@ static const struct of_device_id gpio_wdt_dt_ids[] = { { .compatible = "linux,wdt-gpio", }, { } }; +MODULE_DEVICE_TABLE(of, gpio_wdt_dt_ids); -static struct driver_d gpio_wdt_driver = { +static struct driver gpio_wdt_driver = { .name = "gpio-wdt", .of_compatible = gpio_wdt_dt_ids, .probe = gpio_wdt_probe, diff --git a/drivers/watchdog/im28wd.c b/drivers/watchdog/im28wd.c index 6ebd97e5f5..b52e585175 100644 --- a/drivers/watchdog/im28wd.c +++ b/drivers/watchdog/im28wd.c @@ -1,16 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * (c) 2012 Juergen Beisert <kernel@pengutronix.de> * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * * Note: this driver works for the i.MX28 SoC. It might work for the * i.MX23 Soc as well, but is not tested yet. */ @@ -184,7 +175,7 @@ static void __maybe_unused imx28_detect_reset_source(const struct imx28_wd *p) reset_source_set(RESET_RST); } -static int imx28_wd_probe(struct device_d *dev) +static int imx28_wd_probe(struct device *dev) { struct resource *iores; struct imx28_wd *priv; @@ -196,7 +187,7 @@ static int imx28_wd_probe(struct device_d *dev) return PTR_ERR(iores); priv->regs = IOMEM(iores->start); priv->wd.set_timeout = imx28_watchdog_set_timeout; - priv->wd.timeout_max = ULONG_MAX / WDOG_TICK_RATE; + priv->wd.timeout_max = U32_MAX / WDOG_TICK_RATE; priv->wd.hwdev = dev; if (!(readl(priv->regs + MXS_RTC_STAT) & MXS_RTC_STAT_WD_PRESENT)) { @@ -222,7 +213,7 @@ on_error: return rc; } -static void imx28_wd_remove(struct device_d *dev) +static void imx28_wd_remove(struct device *dev) { struct imx28_wd *priv= dev->priv; watchdog_deregister(&priv->wd); @@ -236,8 +227,9 @@ static __maybe_unused struct of_device_id imx28_wdt_dt_ids[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, imx28_wdt_dt_ids); -static struct driver_d imx28_wd_driver = { +static struct driver imx28_wd_driver = { .name = "im28wd", .probe = imx28_wd_probe, .remove = imx28_wd_remove, diff --git a/drivers/watchdog/imxulp-wdt.c b/drivers/watchdog/imxulp-wdt.c new file mode 100644 index 0000000000..5a89876175 --- /dev/null +++ b/drivers/watchdog/imxulp-wdt.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + */ + +#include <common.h> +#include <init.h> +#include <io.h> +#include <of.h> +#include <errno.h> +#include <malloc.h> +#include <restart.h> +#include <watchdog.h> +#include <reset_source.h> +#include <linux/clk.h> +#include <asm/system.h> + +struct imxulp_socdata { + bool prescaler_enable; + unsigned int rate; +}; + +struct imxulp_wd { + struct watchdog wd; + void __iomem *base; + bool prescaler_enable; + struct device *dev; + const struct imxulp_socdata *socdata; +}; + +#define REFRESH_WORD0 0xA602 /* 1st refresh word */ +#define REFRESH_WORD1 0xB480 /* 2nd refresh word */ + +#define UNLOCK_WORD0 0xC520 /* 1st unlock word */ +#define UNLOCK_WORD1 0xD928 /* 2nd unlock word */ + +#define UNLOCK_WORD 0xD928C520 /* unlock word */ +#define REFRESH_WORD 0xB480A602 /* refresh word */ + +#define WDOG_CS 0x0 +#define WDOG_CS_UPDATE BIT(5) +#define WDOG_CS_EN BIT(7) +#define WDOG_CS_RCS BIT(10) +#define WDOG_CS_ULK BIT(11) +#define WDOG_CS_PRES BIT(12) +#define WDOG_CS_CMD32EN BIT(13) +#define WDOG_CS_FLG BIT(14) +#define WDOG_CS_INT BIT(6) +#define WDOG_CS_LPO_CLK (0x1 << 8) + +#define WDOG_CNT 0x4 +#define WDOG_TOVAL 0x8 + +#define CLK_RATE_1KHZ 1000 +#define CLK_RATE_32KHZ 128 + +static int imxulp_watchdog_set_timeout(struct watchdog *wd, unsigned int timeout) +{ + struct imxulp_wd *imxwd = container_of(wd, struct imxulp_wd, wd); + u32 cmd32 = 0; + + if (timeout == 0) + return -ENOSYS; + + if (readl(imxwd->base + WDOG_CS) & WDOG_CS_CMD32EN) { + writel(UNLOCK_WORD, imxwd->base + WDOG_CNT); + cmd32 = WDOG_CS_CMD32EN; + } else { + writel(UNLOCK_WORD0, imxwd->base + WDOG_CNT); + writel(UNLOCK_WORD1, imxwd->base + WDOG_CNT); + } + + /* Wait WDOG Unlock */ + while (!(readl(imxwd->base + WDOG_CS) & WDOG_CS_ULK)) + ; + + writel(timeout * imxwd->socdata->rate, imxwd->base + WDOG_TOVAL); + + if (imxwd->socdata->prescaler_enable) + cmd32 |= WDOG_CS_PRES; + + writel(cmd32 | WDOG_CS_EN | WDOG_CS_UPDATE | WDOG_CS_LPO_CLK | + WDOG_CS_FLG | WDOG_CS_INT, imxwd->base + WDOG_CS); + + /* Wait WDOG reconfiguration */ + while (!(readl(imxwd->base + WDOG_CS) & WDOG_CS_RCS)) + ; + + if (readl(imxwd->base + WDOG_CS) & WDOG_CS_CMD32EN) { + writel(REFRESH_WORD, imxwd->base + WDOG_CNT); + } else { + writel(REFRESH_WORD0, imxwd->base + WDOG_CNT); + writel(REFRESH_WORD1, imxwd->base + WDOG_CNT); + } + + return 0; +} + +static enum wdog_hw_runnning imxulp_wd_running(struct imxulp_wd *imxwd) +{ + if (readl(imxwd->base + WDOG_CS) & WDOG_CS_EN) + return WDOG_HW_RUNNING; + else + return WDOG_HW_NOT_RUNNING; +} + +static int imxulp_wd_probe(struct device *dev) +{ + struct imxulp_wd *imxwd; + struct resource *iores; + struct imxulp_socdata *socdata; + int ret; + + ret = dev_get_drvdata(dev, (const void **)&socdata); + if (ret) + return ret; + + imxwd = xzalloc(sizeof(*imxwd)); + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return dev_err_probe(dev, PTR_ERR(iores), "could not get memory region\n"); + + imxwd->socdata = socdata; + imxwd->base = IOMEM(iores->start); + imxwd->wd.set_timeout = imxulp_watchdog_set_timeout; + imxwd->wd.timeout_max = 0xffff / imxwd->socdata->rate; + imxwd->wd.hwdev = dev; + imxwd->wd.running = imxulp_wd_running(imxwd); + + ret = watchdog_register(&imxwd->wd); + if (ret) + return dev_err_probe(dev, ret, "Failed to register watchdog device\n"); + + return 0; +} + +static struct imxulp_socdata imx7ulp_wd_socdata = { + .prescaler_enable = false, + .rate = CLK_RATE_1KHZ, +}; + +static struct imxulp_socdata imx93_wd_socdata = { + .prescaler_enable = true, + .rate = CLK_RATE_32KHZ, +}; + +static __maybe_unused struct of_device_id imxulp_wdt_dt_ids[] = { + { + .compatible = "fsl,imx7ulp-wdt", + .data = &imx7ulp_wd_socdata, + }, { + .compatible = "fsl,imx8ulp-wdt", + .data = &imx7ulp_wd_socdata, + }, { + .compatible = "fsl,imx93-wdt", + .data = &imx93_wd_socdata, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, imx_wdt_dt_ids); + +static struct driver imxulp_wd_driver = { + .name = "imxulp-watchdog", + .probe = imxulp_wd_probe, + .of_compatible = DRV_OF_COMPAT(imxulp_wdt_dt_ids), +}; +device_platform_driver(imxulp_wd_driver); diff --git a/drivers/watchdog/imxwd.c b/drivers/watchdog/imxwd.c index a109f6fee7..8f4de5a994 100644 --- a/drivers/watchdog/imxwd.c +++ b/drivers/watchdog/imxwd.c @@ -1,15 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * (c) 2012 Sascha Hauer <s.hauer@pengutronix.de> - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <common.h> @@ -36,7 +27,7 @@ struct imx_wd_ops { struct imx_wd { struct watchdog wd; void __iomem *base; - struct device_d *dev; + struct device *dev; const struct imx_wd_ops *ops; struct restart_handler restart; struct restart_handler restart_warm; @@ -56,6 +47,7 @@ struct imx_wd { #define IMX21_WDOG_WSR 0x02 /* Watchdog Service Register */ #define IMX21_WDOG_WSTR 0x04 /* Watchdog Status Register */ #define IMX21_WDOG_WMCR 0x08 /* Misc Register */ +#define IMX21_WDOG_WCR_WDZST (1 << 0) #define IMX21_WDOG_WCR_WDE (1 << 2) #define IMX21_WDOG_WCR_WDT (1 << 3) #define IMX21_WDOG_WCR_SRS (1 << 4) @@ -134,6 +126,9 @@ static int imx21_watchdog_set_timeout(struct imx_wd *priv, unsigned timeout) if (priv->ext_reset) val |= IMX21_WDOG_WCR_WDT; + /* Suspend timer in low power mode */ + val |= IMX21_WDOG_WCR_WDZST; + /* * set time and some write once bits first prior enabling the * watchdog according to the datasheet @@ -218,7 +213,7 @@ static void imx_watchdog_detect_reset_source(struct imx_wd *priv) /* else keep the default 'unknown' state */ } -static int imx21_wd_init(struct imx_wd *priv) +static int imx21_wd_init_no_warm_reset(struct imx_wd *priv) { imx_watchdog_detect_reset_source(priv); @@ -230,7 +225,19 @@ static int imx21_wd_init(struct imx_wd *priv) return 0; } -static int imx_wd_probe(struct device_d *dev) +static int imx21_wd_init(struct imx_wd *priv) +{ + priv->restart_warm.name = "imxwd-warm"; + priv->restart_warm.restart = imxwd_force_soc_reset_internal; + priv->restart_warm.priority = RESTART_DEFAULT_PRIORITY - 10; + priv->restart_warm.flags = RESTART_FLAG_WARM_BOOTROM; + + restart_handler_register(&priv->restart_warm); + + return imx21_wd_init_no_warm_reset(priv); +} + +static int imx_wd_probe(struct device *dev) { struct resource *iores; struct imx_wd *priv; @@ -244,18 +251,17 @@ static int imx_wd_probe(struct device_d *dev) priv = xzalloc(sizeof(struct imx_wd)); iores = dev_request_mem_resource(dev, 0); - if (IS_ERR(iores)) { - dev_err(dev, "could not get memory region\n"); - return PTR_ERR(iores); - } + if (IS_ERR(iores)) + return dev_err_probe(dev, PTR_ERR(iores), + "could not get memory region\n"); clk = clk_get(dev, NULL); if (IS_ERR(clk)) - return PTR_ERR(clk); + return dev_err_probe(dev, PTR_ERR(clk), "Failed to get clk\n"); ret = clk_enable(clk); if (ret) - return ret; + return dev_err_probe(dev, ret, "Failed to enable clk\n"); priv->base = IOMEM(iores->start); priv->ops = ops; @@ -263,9 +269,9 @@ static int imx_wd_probe(struct device_d *dev) priv->wd.timeout_max = priv->ops->timeout_max; priv->wd.hwdev = dev; priv->dev = dev; - priv->bigendian = of_device_is_big_endian(dev->device_node); + priv->bigendian = of_device_is_big_endian(dev->of_node); - priv->ext_reset = of_property_read_bool(dev->device_node, + priv->ext_reset = of_property_read_bool(dev->of_node, "fsl,ext-reset-output"); if (IS_ENABLED(CONFIG_WATCHDOG_IMX)) { @@ -277,14 +283,17 @@ static int imx_wd_probe(struct device_d *dev) } ret = watchdog_register(&priv->wd); - if (ret) + if (ret) { + dev_err_probe(dev, ret, "Failed to register watchdog device\n"); goto on_error; + } } if (priv->ops->init) { ret = priv->ops->init(priv); if (ret) { - dev_err(dev, "Failed to init watchdog device %d\n", ret); + dev_err_probe(dev, ret, + "Failed to init watchdog device\n"); goto error_unregister; } } @@ -297,12 +306,6 @@ static int imx_wd_probe(struct device_d *dev) restart_handler_register(&priv->restart); - priv->restart_warm.name = "imxwd-warm"; - priv->restart_warm.restart = imxwd_force_soc_reset_internal; - priv->restart_warm.priority = RESTART_DEFAULT_PRIORITY - 10; - - restart_handler_register(&priv->restart_warm); - return 0; error_unregister: @@ -313,6 +316,14 @@ on_error: return ret; } +static const struct imx_wd_ops imx7d_wd_ops = { + .set_timeout = imx21_watchdog_set_timeout, + .soc_reset = imx21_soc_reset, + .init = imx21_wd_init_no_warm_reset, + .is_running = imx21_watchdog_is_running, + .timeout_max = 128, +}; + static const struct imx_wd_ops imx21_wd_ops = { .set_timeout = imx21_watchdog_set_timeout, .soc_reset = imx21_soc_reset, @@ -335,9 +346,13 @@ static __maybe_unused struct of_device_id imx_wdt_dt_ids[] = { .compatible = "fsl,imx21-wdt", .data = &imx21_wd_ops, }, { + .compatible = "fsl,imx7d-wdt", + .data = &imx7d_wd_ops, + }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, imx_wdt_dt_ids); static struct platform_device_id imx_wdt_ids[] = { { @@ -351,7 +366,7 @@ static struct platform_device_id imx_wdt_ids[] = { }, }; -static struct driver_d imx_wd_driver = { +static struct driver imx_wd_driver = { .name = "imx-watchdog", .probe = imx_wd_probe, .of_compatible = DRV_OF_COMPAT(imx_wdt_dt_ids), diff --git a/drivers/watchdog/itco_wdt.c b/drivers/watchdog/itco_wdt.c index e7bd0fc99b..ca012c92dc 100644 --- a/drivers/watchdog/itco_wdt.c +++ b/drivers/watchdog/itco_wdt.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * EFI Boot Guard, iTCO support (Version 3 and later) * diff --git a/drivers/watchdog/jz4740.c b/drivers/watchdog/jz4740.c index f28bb9177a..8b4b985cd4 100644 --- a/drivers/watchdog/jz4740.c +++ b/drivers/watchdog/jz4740.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * JZ4740 Watchdog driver * @@ -6,12 +7,6 @@ * Based on jz4740_wdt.c from linux-3.15. * * Copyright (C) 2010, Paul Cercueil <paul@crapouillou.net> - * - * 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 <common.h> @@ -65,7 +60,7 @@ static void __noreturn jz4740_reset_soc(struct restart_handler *rst) hang(); } -static int jz4740_wdt_probe(struct device_d *dev) +static int jz4740_wdt_probe(struct device *dev) { struct resource *iores; struct jz4740_wdt_drvdata *priv; @@ -94,8 +89,9 @@ static __maybe_unused struct of_device_id jz4740_wdt_dt_ids[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, jz4740_wdt_dt_ids); -static struct driver_d jz4740_wdt_driver = { +static struct driver jz4740_wdt_driver = { .name = "jz4740-wdt", .probe = jz4740_wdt_probe, .of_compatible = DRV_OF_COMPAT(jz4740_wdt_dt_ids), diff --git a/drivers/watchdog/kvx_wdt.c b/drivers/watchdog/kvx_wdt.c index da19136fda..be6b08b71c 100644 --- a/drivers/watchdog/kvx_wdt.c +++ b/drivers/watchdog/kvx_wdt.c @@ -48,7 +48,7 @@ static int kvx_wdt_set_timeout(struct watchdog *wdd, unsigned int timeout) static int count; -static int kvx_wdt_drv_probe(struct device_d *dev) +static int kvx_wdt_drv_probe(struct device *dev) { struct watchdog *wdd; struct clk *clk; @@ -73,11 +73,9 @@ static int kvx_wdt_drv_probe(struct device_d *dev) wdd->hwdev = dev; wdd->set_timeout = kvx_wdt_set_timeout; - /* Be sure that interrupt are disable */ + /* Be sure that interrupt are disabled */ kvx_sfr_set_field(TCR, WIE, 0); - kvx_watchdog_disable(); - return watchdog_register(wdd); } @@ -85,8 +83,9 @@ static struct of_device_id kvx_wdt_of_match[] = { { .compatible = "kalray,kvx-core-watchdog", }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, kvx_wdt_of_match); -static struct driver_d kvx_wdt_driver = { +static struct driver kvx_wdt_driver = { .name = "kvx-wdt", .probe = kvx_wdt_drv_probe, .of_compatible = DRV_OF_COMPAT(kvx_wdt_of_match), diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c index c8a7ccf607..0ebc1172aa 100644 --- a/drivers/watchdog/omap_wdt.c +++ b/drivers/watchdog/omap_wdt.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * omap_wdt.c * @@ -11,10 +12,7 @@ * Author: MontaVista Software, Inc. * <gdavis@mvista.com> or <source@mvista.com> * - * 2003 (c) MontaVista Software, Inc. This file is licensed under the - * terms of the GNU General Public License version 2. This program is - * licensed "as is" without any warranty of any kind, whether express - * or implied. + * 2003 (c) MontaVista Software, Inc. * * History: * @@ -152,7 +150,7 @@ static int omap_wdt_set_timeout(struct watchdog *wdog, return 0; } -static int omap_wdt_probe(struct device_d *dev) +static int omap_wdt_probe(struct device *dev) { struct resource *iores; struct omap_wdt_dev *wdev; @@ -193,8 +191,9 @@ static const struct of_device_id omap_wdt_of_match[] = { { .compatible = "ti,omap3-wdt", }, {}, }; +MODULE_DEVICE_TABLE(of, omap_wdt_of_match); -static struct driver_d omap_wdt_driver = { +static struct driver omap_wdt_driver = { .probe = omap_wdt_probe, .name = "omap_wdt", .of_compatible = DRV_OF_COMPAT(omap_wdt_of_match), diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c index dd1fa3a04d..227f8b7bb1 100644 --- a/drivers/watchdog/orion_wdt.c +++ b/drivers/watchdog/orion_wdt.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Watchdog driver for Marvell Armada XP. * * Copyright (C) 2017 Pengutronix, Uwe Kleine-König <kernel@pengutronix.de> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <common.h> @@ -76,7 +68,7 @@ static int armada_xp_set_timeout(struct watchdog *wd, unsigned timeout) return 0; } -static int orion_wdt_probe(struct device_d *dev) +static int orion_wdt_probe(struct device *dev) { struct orion_wdt_ddata* ddata; struct resource *res_timer, *res_rstout; @@ -112,8 +104,9 @@ static const struct of_device_id orion_wdt_of_match[] = { .compatible = "marvell,armada-xp-wdt", }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, orion_wdt_of_match); -static struct driver_d orion_wdt_driver = { +static struct driver orion_wdt_driver = { .probe = orion_wdt_probe, .name = "orion_wdt", .of_compatible = DRV_OF_COMPAT(orion_wdt_of_match), diff --git a/drivers/watchdog/rave-sp-wdt.c b/drivers/watchdog/rave-sp-wdt.c index cad63e22f9..b4fc18cb8b 100644 --- a/drivers/watchdog/rave-sp-wdt.c +++ b/drivers/watchdog/rave-sp-wdt.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Driver for watchdog aspect of for Zodiac Inflight Innovations RAVE @@ -252,6 +252,7 @@ static const struct of_device_id rave_sp_wdt_of_match[] = { }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, rave_sp_wdt_of_match); static int rave_sp_wdt_set_boot_source(struct param_d *param, void *priv) { @@ -286,7 +287,7 @@ static int rave_sp_wdt_get_boot_source(struct param_d *param, void *priv) static int rave_sp_wdt_add_params(struct rave_sp_wdt *sp_wd) { struct watchdog *wdd = &sp_wd->wdd; - struct device_node *np = wdd->hwdev->device_node; + struct device_node *np = wdd->hwdev->of_node; struct param_d *p; sp_wd->boot_source_cell = of_nvmem_cell_get(np, "boot-source"); @@ -302,7 +303,7 @@ static int rave_sp_wdt_add_params(struct rave_sp_wdt *sp_wd) return PTR_ERR_OR_ZERO(p); } -static int rave_sp_wdt_probe(struct device_d *dev) +static int rave_sp_wdt_probe(struct device *dev) { struct rave_sp_wdt *sp_wd; const char *reset_reason; @@ -415,7 +416,7 @@ static int rave_sp_wdt_probe(struct device_d *dev) return 0; } -static struct driver_d rave_sp_wdt_driver = { +static struct driver rave_sp_wdt_driver = { .name = "rave-sp-wdt", .probe = rave_sp_wdt_probe, .of_compatible = DRV_OF_COMPAT(rave_sp_wdt_of_match), diff --git a/drivers/watchdog/rn5t568_wdt.c b/drivers/watchdog/rn5t568_wdt.c new file mode 100644 index 0000000000..2011e3e01c --- /dev/null +++ b/drivers/watchdog/rn5t568_wdt.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Watchdog driver for Ricoh RN5T618 PMIC + * + * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com> + */ + +#include <common.h> +#include <init.h> +#include <watchdog.h> +#include <linux/regmap.h> +#include <of.h> + +#define RN5T568_WATCHDOG 0x0b +# define RN5T568_WATCHDOG_WDPWROFFEN BIT(2) +# define RN5T568_WATCHDOG_WDOGTIM_M (BIT(0) | BIT(1)) +#define RN5T568_PWRIREN 0x12 +# define RN5T568_PWRIREN_EN_WDOG BIT(6) +#define RN5T568_PWRIRQ 0x13 +# define RN5T568_PWRIRQ_IR_WDOG BIT(6) + +struct rn5t568_wdt { + struct watchdog wdd; + struct regmap *regmap; + unsigned int timeout; +}; + +struct rn5t568_wdt_tim { + u8 reg_val; + u8 time; +}; + +static const struct rn5t568_wdt_tim rn5t568_wdt_timeout[] = { + { .reg_val = 0, .time = 1, }, + { .reg_val = 1, .time = 8, }, + { .reg_val = 2, .time = 32, }, + { .reg_val = 3, .time = 128, }, +}; + +#define PMIC_WDT_MAX_TIMEOUT 128 + +static int rn5t568_wdt_start(struct regmap *regmap, int idx) +{ + int ret; + + ret = regmap_update_bits(regmap, RN5T568_WATCHDOG, RN5T568_WATCHDOG_WDOGTIM_M, + rn5t568_wdt_timeout[idx].reg_val); + if (ret) + return ret; + + regmap_clear_bits(regmap, RN5T568_PWRIRQ, RN5T568_PWRIRQ_IR_WDOG); + regmap_set_bits(regmap, RN5T568_PWRIREN, RN5T568_PWRIREN_EN_WDOG); + + pr_debug("RN5t: Starting the watchdog with %u seconds\n", rn5t568_wdt_timeout[idx].time); + + return regmap_set_bits(regmap, RN5T568_WATCHDOG, RN5T568_WATCHDOG_WDPWROFFEN); +} + +static int rn5t568_wdt_stop(struct regmap *regmap) +{ + int ret; + + ret = regmap_clear_bits(regmap, RN5T568_PWRIREN, RN5T568_PWRIREN_EN_WDOG); + if (ret) + return ret; + + return regmap_clear_bits(regmap, RN5T568_WATCHDOG, RN5T568_WATCHDOG_WDPWROFFEN); +} + +static int rn5t568_wdt_ping(struct regmap *regmap) +{ + unsigned int val; + int ret; + + ret = regmap_read(regmap, RN5T568_WATCHDOG, &val); + if (ret) + return ret; + + return regmap_write(regmap, RN5T568_WATCHDOG, val); +} + +static int rn5t568_wdt_set_timeout(struct watchdog *wdd, unsigned int timeout) +{ + struct rn5t568_wdt *wdt = container_of(wdd, struct rn5t568_wdt, wdd); + int ret, i; + + if (!timeout) + return rn5t568_wdt_stop(wdt->regmap); + + for (i = 0; i < ARRAY_SIZE(rn5t568_wdt_timeout); i++) { + if (timeout < rn5t568_wdt_timeout[i].time) + break; + } + + if (i == ARRAY_SIZE(rn5t568_wdt_timeout)) + return -EINVAL; + + if (wdt->timeout == timeout) + return rn5t568_wdt_ping(wdt->regmap); + + ret = rn5t568_wdt_start(wdt->regmap, i); + if (ret) + return ret; + + wdt->timeout = rn5t568_wdt_timeout[i].time; + + return ret; +} + +static int rn5t568_wdt_probe(struct device *dev) +{ + struct rn5t568_wdt *wdt; + struct watchdog *wdd; + unsigned int val; + int ret; + + wdt = xzalloc(sizeof(*wdt)); + + wdt->regmap = dev_get_regmap(dev->parent, NULL); + if (IS_ERR(wdt->regmap)) + return PTR_ERR(wdt->regmap); + + wdd = &wdt->wdd; + wdd->hwdev = dev; + wdd->set_timeout = rn5t568_wdt_set_timeout; + wdd->timeout_max = PMIC_WDT_MAX_TIMEOUT; + + ret = regmap_read(wdt->regmap, RN5T568_WATCHDOG, &val); + if (ret == 0) + wdd->running = val & RN5T568_WATCHDOG_WDPWROFFEN ? + WDOG_HW_RUNNING : WDOG_HW_NOT_RUNNING; + + return watchdog_register(wdd); +} + +static __maybe_unused const struct of_device_id rn5t568_wdt_of_match[] = { + { .compatible = "ricoh,rn5t568-wdt" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rn5t568_wdt_of_match); + +static struct driver rn5t568_wdt_driver = { + .name = "rn5t568-wdt", + .probe = rn5t568_wdt_probe, + .of_compatible = DRV_OF_COMPAT(rn5t568_wdt_of_match), +}; +device_platform_driver(rn5t568_wdt_driver); diff --git a/drivers/watchdog/starfive_wdt.c b/drivers/watchdog/starfive_wdt.c index 6779566fd6..90f1e0ae6b 100644 --- a/drivers/watchdog/starfive_wdt.c +++ b/drivers/watchdog/starfive_wdt.c @@ -44,7 +44,7 @@ static int starfive_wdt_set_timeout(struct watchdog *wdd, unsigned int timeout) return 0; } -static int starfive_wdt_drv_probe(struct device_d *dev) +static int starfive_wdt_drv_probe(struct device *dev) { struct starfive_wdt *wd; struct resource *iores; @@ -97,8 +97,9 @@ static struct of_device_id starfive_wdt_of_match[] = { { .compatible = "starfive,wdt", }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, starfive_wdt_of_match); -static struct driver_d starfive_wdt_driver = { +static struct driver starfive_wdt_driver = { .name = "starfive-wdt", .probe = starfive_wdt_drv_probe, .of_compatible = starfive_wdt_of_match, diff --git a/drivers/watchdog/stm32_iwdg.c b/drivers/watchdog/stm32_iwdg.c index 4d7a263b7e..6ac9e7d56e 100644 --- a/drivers/watchdog/stm32_iwdg.c +++ b/drivers/watchdog/stm32_iwdg.c @@ -125,8 +125,9 @@ static const struct of_device_id stm32_iwdg_of_match[] = { { .compatible = "st,stm32mp1-iwdg", .data = &stm32mp1_iwdg_data }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, stm32_iwdg_of_match); -static int stm32_iwdg_probe(struct device_d *dev) +static int stm32_iwdg_probe(struct device *dev) { struct stm32_iwdg_data *data; struct stm32_iwdg *wd; @@ -187,7 +188,7 @@ static int stm32_iwdg_probe(struct device_d *dev) return 0; } -static struct driver_d stm32_iwdg_driver = { +static struct driver stm32_iwdg_driver = { .name = "stm32-iwdg", .probe = stm32_iwdg_probe, .of_compatible = DRV_OF_COMPAT(stm32_iwdg_of_match), diff --git a/drivers/watchdog/stpmic1_wdt.c b/drivers/watchdog/stpmic1_wdt.c index 105ba39fb7..4a0519690a 100644 --- a/drivers/watchdog/stpmic1_wdt.c +++ b/drivers/watchdog/stpmic1_wdt.c @@ -11,6 +11,7 @@ #include <linux/iopoll.h> #include <poweroff.h> #include <mfd/syscon.h> +#include <linux/regmap.h> #include <restart.h> #include <reset_source.h> @@ -157,7 +158,7 @@ static int stpmic1_set_reset_reason(struct regmap *map) return 0; } -static int stpmic1_wdt_probe(struct device_d *dev) +static int stpmic1_wdt_probe(struct device *dev) { struct stpmic1_wdt *wdt; struct watchdog *wdd; @@ -208,8 +209,9 @@ static __maybe_unused const struct of_device_id stpmic1_wdt_of_match[] = { { .compatible = "st,stpmic1-wdt" }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, stpmic1_wdt_of_match); -static struct driver_d stpmic1_wdt_driver = { +static struct driver stpmic1_wdt_driver = { .name = "stpmic1-wdt", .probe = stpmic1_wdt_probe, .of_compatible = DRV_OF_COMPAT(stpmic1_wdt_of_match), diff --git a/drivers/watchdog/wd_core.c b/drivers/watchdog/wd_core.c index 2e2814a8f2..f39a8f4522 100644 --- a/drivers/watchdog/wd_core.c +++ b/drivers/watchdog/wd_core.c @@ -1,15 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * (c) 2012 Juergen Beisert <kernel@pengutronix.de> - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #define pr_fmt(fmt) "watchdog: " fmt @@ -148,12 +139,12 @@ static int watchdog_register_dev(struct watchdog *wd, const char *name, int id) * * return: The priority */ -static unsigned int dev_get_watchdog_priority(struct device_d *dev) +static unsigned int dev_get_watchdog_priority(struct device *dev) { unsigned int priority = WATCHDOG_DEFAULT_PRIORITY; if (dev) - of_property_read_u32(dev->device_node, "watchdog-priority", + of_property_read_u32(dev->of_node, "watchdog-priority", &priority); return priority; @@ -188,7 +179,9 @@ static void __noreturn watchdog_restart_handle(struct restart_handler *this) BUG_ON(ret); mdelay(2000); - __builtin_unreachable(); + + pr_emerg("Watchdog failed to reset the machine\n"); + hang(); } static struct restart_handler restart_handler = { @@ -203,7 +196,7 @@ int watchdog_register(struct watchdog *wd) int ret = 0; if (wd->hwdev) - alias = of_alias_get(wd->hwdev->device_node); + alias = of_alias_get(wd->hwdev->of_node); if (alias) ret = watchdog_register_dev(wd, alias, DEVICE_ID_SINGLE); @@ -318,7 +311,7 @@ EXPORT_SYMBOL(watchdog_get_default); int watchdog_get_alias_id_from(struct watchdog *wd, struct device_node *root) { - struct device_node *dstnp, *srcnp = wd->hwdev ? wd->hwdev->device_node : NULL; + struct device_node *dstnp, *srcnp = wd->hwdev ? wd->hwdev->of_node : NULL; char *name; if (!srcnp) @@ -331,14 +324,14 @@ int watchdog_get_alias_id_from(struct watchdog *wd, struct device_node *root) if (!dstnp) return -ENODEV; - return of_alias_get_id_from(root, wd->hwdev->device_node, "watchdog"); + return of_alias_get_id_from(root, wd->hwdev->of_node, "watchdog"); } EXPORT_SYMBOL(watchdog_get_alias_id_from); struct watchdog *watchdog_get_by_name(const char *name) { struct watchdog *tmp; - struct device_d *dev = get_device_by_name(name); + struct device *dev = get_device_by_name(name); if (!dev) return NULL; diff --git a/drivers/watchdog/wdat_wdt.c b/drivers/watchdog/wdat_wdt.c new file mode 100644 index 0000000000..522f1c25f1 --- /dev/null +++ b/drivers/watchdog/wdat_wdt.c @@ -0,0 +1,496 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ACPI Hardware Watchdog (WDAT) driver. + * + * Copyright (C) 2016, Intel Corporation + * Author: Mika Westerberg <mika.westerberg@linux.intel.com> + */ +#include <common.h> +#include <acpi.h> +#include <errno.h> +#include <init.h> +#include <io.h> +#include <malloc.h> +#include <of.h> +#include <watchdog.h> + +enum acpi_wdat_actions { + ACPI_WDAT_RESET = 1, + ACPI_WDAT_GET_CURRENT_COUNTDOWN = 4, + ACPI_WDAT_GET_COUNTDOWN = 5, + ACPI_WDAT_SET_COUNTDOWN = 6, + ACPI_WDAT_GET_RUNNING_STATE = 8, + ACPI_WDAT_SET_RUNNING_STATE = 9, + ACPI_WDAT_GET_STOPPED_STATE = 10, + ACPI_WDAT_SET_STOPPED_STATE = 11, + ACPI_WDAT_GET_REBOOT = 16, + ACPI_WDAT_SET_REBOOT = 17, + ACPI_WDAT_GET_SHUTDOWN = 18, + ACPI_WDAT_SET_SHUTDOWN = 19, + ACPI_WDAT_GET_STATUS = 32, + ACPI_WDAT_SET_STATUS = 33, + ACPI_WDAT_ACTION_RESERVED = 34 /* 34 and greater are reserved */ +}; + +enum acpi_wdat_instructions { + ACPI_WDAT_READ_VALUE = 0, + ACPI_WDAT_READ_COUNTDOWN = 1, + ACPI_WDAT_WRITE_VALUE = 2, + ACPI_WDAT_WRITE_COUNTDOWN = 3, + ACPI_WDAT_INSTRUCTION_RESERVED = 4, /* 4 and greater are reserved */ + ACPI_WDAT_PRESERVE_REGISTER = 0x80 /* Except for this value */ +}; + +#define MAX_WDAT_ACTIONS ACPI_WDAT_ACTION_RESERVED + +#define WDAT_DEFAULT_TIMEOUT 30 + +/* WDAT Instruction Entries (actions) */ + +struct __packed acpi_wdat_entry { + u8 action; + u8 instruction; + u16 reserved; + struct acpi_generic_address register_region; + u32 value; /* Value used with Read/Write register */ + u32 mask; /* Bitmask required for this register instruction */ +}; + +/** + * struct wdat_instruction - Single ACPI WDAT instruction + * @entry: Copy of the ACPI table instruction + * @reg: Register the instruction is accessing + * @node: Next instruction in action sequence + */ +struct wdat_instruction { + struct acpi_wdat_entry entry; + void __iomem *reg; + struct list_head node; +}; + +/** + * struct wdat_wdt - ACPI WDAT watchdog device + * @dev: Parent platform device + * @wdd: Watchdog core device + * @period: How long is one watchdog period in ms + * @stopped_in_sleep: Is this watchdog stopped by the firmware in S1-S5 + * @stopped: Was the watchdog stopped by the driver in suspend + * @instructions: An array of instruction lists indexed by an action number from + * the WDAT table. There can be %NULL entries for not implemented + * actions. + */ +struct wdat_wdt { + struct watchdog wdd; + unsigned int period; + bool stopped_in_sleep; + bool stopped; + struct list_head *instructions[MAX_WDAT_ACTIONS]; +}; + +struct __packed acpi_table_wdat { + struct acpi_table_header header; /* Common ACPI table header */ + u32 header_length; /* Watchdog Header Length */ + u16 pci_segment; /* PCI Segment number */ + u8 pci_bus; /* PCI Bus number */ + u8 pci_device; /* PCI Device number */ + u8 pci_function; /* PCI Function number */ + u8 reserved[3]; + u32 timer_period; /* Period of one timer count (msec) */ + u32 max_count; /* Maximum counter value supported */ + u32 min_count; /* Minimum counter value */ + u8 flags; + u8 reserved2[3]; + u32 nr_entries; /* Number of watchdog entries that follow */ + struct acpi_wdat_entry entries[]; +}; + +#define ACPI_WDAT_ENABLED (1) +#define ACPI_WDAT_STOPPED 0x80 + +#define IO_COND(instr, is_pio, is_mmio) do { \ + const struct acpi_generic_address *gas = &instr->entry.register_region; \ + unsigned long port = (unsigned long __force)instr->reg; \ + if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { \ + is_mmio; \ + } else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) { \ + is_pio; \ + } \ +} while (0) + +static unsigned int read8(const struct wdat_instruction *instr) +{ + IO_COND(instr, return inb(port), return readb(instr->reg)); + return 0xff; +} + +static unsigned int read16(const struct wdat_instruction *instr) +{ + IO_COND(instr, return inw(port), return readw(instr->reg)); + return 0xffff; +} + +static unsigned int read32(const struct wdat_instruction *instr) +{ + IO_COND(instr, return inl(port), return readl(instr->reg)); + return 0xffffffff; +} + +static void write8(u8 val, const struct wdat_instruction *instr) +{ + IO_COND(instr, outb(val,port), writeb(val, instr->reg)); +} + +static void write16(u16 val, const struct wdat_instruction *instr) +{ + IO_COND(instr, outw(val,port), writew(val, instr->reg)); +} + +static void write32(u32 val, const struct wdat_instruction *instr) +{ + IO_COND(instr, outl(val,port), writel(val, instr->reg)); +} + +static int wdat_wdt_read(struct wdat_wdt *wdat, + const struct wdat_instruction *instr, u32 *value) +{ + const struct acpi_generic_address *gas = &instr->entry.register_region; + + switch (gas->access_width) { + case 1: + *value = read8(instr); + break; + case 2: + *value = read16(instr); + break; + case 3: + *value = read32(instr); + break; + default: + return -EINVAL; + } + + dev_dbg(wdat->wdd.hwdev, "Read %#x from 0x%08llx\n", *value, + gas->address); + + return 0; +} + +static int wdat_wdt_write(struct wdat_wdt *wdat, + const struct wdat_instruction *instr, u32 value) +{ + const struct acpi_generic_address *gas = &instr->entry.register_region; + + switch (gas->access_width) { + case 1: + write8((u8)value, instr); + break; + case 2: + write16((u16)value, instr); + break; + case 3: + write32(value, instr); + break; + default: + return -EINVAL; + } + + dev_dbg(wdat->wdd.hwdev, "Wrote %#x to 0x%08llx\n", value, + gas->address); + + return 0; +} + +static int wdat_wdt_run_action(struct wdat_wdt *wdat, unsigned int action, + u32 param, u32 *retval) +{ + struct wdat_instruction *instr; + + if (action >= ARRAY_SIZE(wdat->instructions)) { + dev_dbg(wdat->wdd.hwdev, "Invalid action %#x\n", action); + return -EINVAL; + } + + if (!wdat->instructions[action]) { + dev_dbg(wdat->wdd.hwdev, "Unsupported action %#x\n", action); + return -EOPNOTSUPP; + } + + dev_dbg(wdat->wdd.hwdev, "Running action %#x\n", action); + + /* Run each instruction sequentially */ + list_for_each_entry(instr, wdat->instructions[action], node) { + const struct acpi_wdat_entry *entry = &instr->entry; + const struct acpi_generic_address *gas; + u32 flags, value, mask, x, y; + bool preserve; + int ret; + + gas = &entry->register_region; + + preserve = entry->instruction & ACPI_WDAT_PRESERVE_REGISTER; + flags = entry->instruction & ~ACPI_WDAT_PRESERVE_REGISTER; + value = entry->value; + mask = entry->mask; + + switch (flags) { + case ACPI_WDAT_READ_VALUE: + ret = wdat_wdt_read(wdat, instr, &x); + if (ret) + return ret; + x >>= gas->bit_offset; + x &= mask; + if (retval) + *retval = x == value; + break; + + case ACPI_WDAT_READ_COUNTDOWN: + ret = wdat_wdt_read(wdat, instr, &x); + if (ret) + return ret; + x >>= gas->bit_offset; + x &= mask; + if (retval) + *retval = x; + break; + + case ACPI_WDAT_WRITE_VALUE: + x = value & mask; + x <<= gas->bit_offset; + if (preserve) { + ret = wdat_wdt_read(wdat, instr, &y); + if (ret) + return ret; + y = y & ~(mask << gas->bit_offset); + x |= y; + } + ret = wdat_wdt_write(wdat, instr, x); + if (ret) + return ret; + break; + + case ACPI_WDAT_WRITE_COUNTDOWN: + x = param; + x &= mask; + x <<= gas->bit_offset; + if (preserve) { + ret = wdat_wdt_read(wdat, instr, &y); + if (ret) + return ret; + y = y & ~(mask << gas->bit_offset); + x |= y; + } + ret = wdat_wdt_write(wdat, instr, x); + if (ret) + return ret; + break; + + default: + dev_err(wdat->wdd.hwdev, "Unknown instruction: %u\n", + flags); + return -EINVAL; + } + } + + return 0; +} + +static void wdat_wdt_boot_status(struct wdat_wdt *wdat) +{ + u32 boot_status = 0; + int ret; + + ret = wdat_wdt_run_action(wdat, ACPI_WDAT_GET_STATUS, 0, &boot_status); + if (ret && ret != -EOPNOTSUPP) { + dev_err(wdat->wdd.hwdev, "Failed to read boot status\n"); + return; + } + + /* Clear the boot status in case BIOS did not do it */ + ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_STATUS, 0, NULL); + if (ret && ret != -EOPNOTSUPP) + dev_err(wdat->wdd.hwdev, "Failed to clear boot status\n"); +} + +static int wdat_wdt_start(struct watchdog *wdd) +{ + struct wdat_wdt *wdat = container_of(wdd, struct wdat_wdt, wdd); + + return wdat_wdt_run_action(wdat, ACPI_WDAT_SET_RUNNING_STATE, 0, NULL); +} + +static int wdat_wdt_stop(struct watchdog *wdd) +{ + struct wdat_wdt *wdat = container_of(wdd, struct wdat_wdt, wdd); + + return wdat_wdt_run_action(wdat, ACPI_WDAT_SET_STOPPED_STATE, 0, NULL); +} + +static void wdat_wdt_set_running(struct wdat_wdt *wdat) +{ + u32 running = 0; + int ret; + + ret = wdat_wdt_run_action(wdat, ACPI_WDAT_GET_RUNNING_STATE, 0, + &running); + if (ret && ret != -EOPNOTSUPP) + dev_err(wdat->wdd.hwdev, "Failed to read running state\n"); + + dev_dbg(wdat->wdd.hwdev, "Running state: %d\n", running); + + wdat->wdd.running = running ? WDOG_HW_RUNNING : WDOG_HW_NOT_RUNNING; +} + +static int wdat_wdt_set_timeout(struct watchdog *wdd, unsigned int timeout) +{ + struct wdat_wdt *wdat = container_of(wdd, struct wdat_wdt, wdd); + unsigned int periods; + int ret; + + if (timeout == 0) + return wdat_wdt_stop(wdd); + + periods = timeout * 1000 / wdat->period; + ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_COUNTDOWN, periods, NULL); + if (ret) + return ret; + + if (wdat->wdd.running == WDOG_HW_NOT_RUNNING) { + wdat_wdt_start(wdd); + wdat->wdd.running = WDOG_HW_RUNNING; + } + + return wdat_wdt_run_action(wdat, ACPI_WDAT_RESET, 0, NULL); +} + +static int wdat_wdt_enable_reboot(struct wdat_wdt *wdat) +{ + int ret; + + /* + * WDAT specification says that the watchdog is required to reboot + * the system when it fires. However, it also states that it is + * recommeded to make it configurable through hardware register. We + * enable reboot now if it is configrable, just in case. + */ + ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_REBOOT, 0, NULL); + if (ret && ret != -EOPNOTSUPP) { + dev_err(wdat->wdd.hwdev, + "Failed to enable reboot when watchdog triggers\n"); + return ret; + } + + return 0; +} + +static int wdat_wdt_probe(struct device *const dev) +{ + const struct acpi_wdat_entry *entries; + struct acpi_table_wdat *tbl; + struct wdat_wdt *wdat; + int i, ret; + + dev_dbg(dev, "driver initializing...\n"); + + tbl = (struct acpi_table_wdat __force *)dev_request_mem_region_by_name(dev, "SDT"); + if (IS_ERR(tbl)) { + dev_err(dev, "no SDT resource available: %pe\n", tbl); + return PTR_ERR(tbl); + } + + dev_dbg(dev, "SDT is at 0x%p\n", tbl); + + wdat = xzalloc(sizeof(*wdat)); + + /* WDAT specification wants to have >= 1ms period */ + if (tbl->timer_period < 1) { + dev_dbg(dev, "timer_period is less than 1: %d\n", tbl->timer_period); + return -EINVAL; + } + if (tbl->min_count > tbl->max_count) { + dev_dbg(dev, "min_count must be greater than max_count\n"); + return -EINVAL; + } + + wdat->period = tbl->timer_period; + wdat->stopped_in_sleep = tbl->flags & ACPI_WDAT_STOPPED; + wdat->wdd.set_timeout = wdat_wdt_set_timeout; + wdat->wdd.hwdev = dev; + wdat->wdd.timeout_max = U32_MAX; + + entries = tbl->entries; + + for (i = 0; i < tbl->nr_entries; i++) { + const struct acpi_generic_address *gas; + struct wdat_instruction *instr; + struct list_head *instructions; + struct resource *res; + unsigned int action; + struct resource r; + + action = entries[i].action; + if (action >= MAX_WDAT_ACTIONS) { + dev_dbg(dev, "Skipping unknown action: %u\n", action); + continue; + } + + instr = xzalloc(sizeof(*instr)); + + INIT_LIST_HEAD(&instr->node); + instr->entry = entries[i]; + + gas = &entries[i].register_region; + + memset(&r, 0, sizeof(r)); + r.start = gas->address; + r.end = r.start + ACPI_ACCESS_BYTE_WIDTH(gas->access_width) - 1; + if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { + res = request_iomem_region(dev_name(dev), r.start, r.end); + } else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) { + res = request_ioport_region(dev_name(dev), r.start, r.end); + } else { + dev_dbg(dev, "Unsupported address space: %d\n", + gas->space_id); + continue; + } + + /* + * Some entries have the same gas->address. + * We want the action but can't request the region multiple times. + */ + if (IS_ERR(res) && (PTR_ERR(res) != -EBUSY)) + return PTR_ERR(res); + + instr->reg = IOMEM(r.start); + + instructions = wdat->instructions[action]; + if (!instructions) { + instructions = xzalloc(sizeof(*instructions)); + if (!instructions) + return -ENOMEM; + + INIT_LIST_HEAD(instructions); + wdat->instructions[action] = instructions; + } + + list_add_tail(&instr->node, instructions); + } + + wdat_wdt_boot_status(wdat); + wdat_wdt_set_running(wdat); + + ret = wdat_wdt_enable_reboot(wdat); + if (ret) + return ret; + + return watchdog_register(&wdat->wdd); +} + + +static struct acpi_driver wdat_wdt_driver = { + .signature = "WDAT", + .driver = { + .name = "wdat-wdt", + .probe = wdat_wdt_probe, + } +}; +device_acpi_driver(wdat_wdt_driver); |