diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/watchdog/Kconfig | 22 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 2 | ||||
-rw-r--r-- | drivers/watchdog/asm9260_wdt.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/bcm_kona_wdt.c | 16 | ||||
-rw-r--r-- | drivers/watchdog/cpwd.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/hpwdt.c | 15 | ||||
-rw-r--r-- | drivers/watchdog/ie6xx_wdt.c | 16 | ||||
-rw-r--r-- | drivers/watchdog/mena21_wdt.c | 73 | ||||
-rw-r--r-- | drivers/watchdog/mtx-1_wdt.c | 19 | ||||
-rw-r--r-- | drivers/watchdog/pm8916_wdt.c | 211 | ||||
-rw-r--r-- | drivers/watchdog/renesas_wdt.c | 24 | ||||
-rw-r--r-- | drivers/watchdog/tqmx86_wdt.c | 126 | ||||
-rw-r--r-- | drivers/watchdog/w83627hf_wdt.c | 38 |
13 files changed, 471 insertions, 95 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 2d64333f4782..57f017d74a97 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -538,7 +538,7 @@ config COH901327_WATCHDOG config NPCM7XX_WATCHDOG bool "Nuvoton NPCM750 watchdog" depends on ARCH_NPCM || COMPILE_TEST - default y if ARCH_NPCM750 + default y if ARCH_NPCM7XX select WATCHDOG_CORE help Say Y here to include Watchdog timer support for the @@ -847,6 +847,14 @@ config SPRD_WATCHDOG Say Y here to include watchdog timer supported by Spreadtrum system. +config PM8916_WATCHDOG + tristate "QCOM PM8916 pmic watchdog" + depends on OF && MFD_SPMI_PMIC + select WATCHDOG_CORE + help + Say Y here to include support watchdog timer embedded into the + pm8916 module. + # X86 (i386 + ia64 + x86_64) Architecture config ACQUIRE_WDT @@ -1308,6 +1316,18 @@ config SMSC37B787_WDT Most people will say N. +config TQMX86_WDT + tristate "TQ-Systems TQMX86 Watchdog Timer" + depends on X86 + help + This is the driver for the hardware watchdog timer in the TQMX86 IO + controller found on some of their ComExpress Modules. + + To compile this driver as a module, choose M here; the module + will be called tqmx86_wdt. + + Most people will say N. + config VIA_WDT tristate "VIA Watchdog Timer" depends on X86 && PCI diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index f69cdff5ad7f..a0917ef28e07 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -92,6 +92,7 @@ obj-$(CONFIG_STM32_WATCHDOG) += stm32_iwdg.o obj-$(CONFIG_UNIPHIER_WATCHDOG) += uniphier_wdt.o obj-$(CONFIG_RTD119X_WATCHDOG) += rtd119x_wdt.o obj-$(CONFIG_SPRD_WATCHDOG) += sprd_wdt.o +obj-$(CONFIG_PM8916_WATCHDOG) += pm8916_wdt.o # X86 (i386 + ia64 + x86_64) Architecture obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o @@ -129,6 +130,7 @@ obj-$(CONFIG_SBC7240_WDT) += sbc7240_wdt.o obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o obj-$(CONFIG_SMSC_SCH311X_WDT) += sch311x_wdt.o obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o +obj-$(CONFIG_TQMX86_WDT) += tqmx86_wdt.o obj-$(CONFIG_VIA_WDT) += via_wdt.o obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o diff --git a/drivers/watchdog/asm9260_wdt.c b/drivers/watchdog/asm9260_wdt.c index 2cf56b459d84..9768e44ffeb8 100644 --- a/drivers/watchdog/asm9260_wdt.c +++ b/drivers/watchdog/asm9260_wdt.c @@ -278,7 +278,7 @@ static int asm9260_wdt_probe(struct platform_device *pdev) struct watchdog_device *wdd; struct resource *res; int ret; - const char * const mode_name[] = { "hw", "sw", "debug", }; + static const char * const mode_name[] = { "hw", "sw", "debug", }; priv = devm_kzalloc(&pdev->dev, sizeof(struct asm9260_wdt_priv), GFP_KERNEL); diff --git a/drivers/watchdog/bcm_kona_wdt.c b/drivers/watchdog/bcm_kona_wdt.c index 1462be9e6fc5..4249b47902bd 100644 --- a/drivers/watchdog/bcm_kona_wdt.c +++ b/drivers/watchdog/bcm_kona_wdt.c @@ -90,7 +90,7 @@ static int secure_register_read(struct bcm_kona_wdt *wdt, uint32_t offset) #ifdef CONFIG_BCM_KONA_WDT_DEBUG -static int bcm_kona_wdt_dbg_show(struct seq_file *s, void *data) +static int bcm_kona_show(struct seq_file *s, void *data) { int ctl_val, cur_val; unsigned long flags; @@ -130,17 +130,7 @@ static int bcm_kona_wdt_dbg_show(struct seq_file *s, void *data) return 0; } -static int bcm_kona_dbg_open(struct inode *inode, struct file *file) -{ - return single_open(file, bcm_kona_wdt_dbg_show, inode->i_private); -} - -static const struct file_operations bcm_kona_dbg_operations = { - .open = bcm_kona_dbg_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(bcm_kona); static void bcm_kona_wdt_debug_init(struct platform_device *pdev) { @@ -157,7 +147,7 @@ static void bcm_kona_wdt_debug_init(struct platform_device *pdev) return; if (debugfs_create_file("info", S_IFREG | S_IRUGO, dir, wdt, - &bcm_kona_dbg_operations)) + &bcm_kona_fops)) wdt->debugfs = dir; else debugfs_remove_recursive(dir); diff --git a/drivers/watchdog/cpwd.c b/drivers/watchdog/cpwd.c index aee0b25cf10d..32156e199c51 100644 --- a/drivers/watchdog/cpwd.c +++ b/drivers/watchdog/cpwd.c @@ -570,6 +570,8 @@ static int cpwd_probe(struct platform_device *op) if (str_prop) p->timeout = simple_strtoul(str_prop, NULL, 10); + of_node_put(options); + /* CP1400s seem to have broken PLD implementations-- the * interrupt_mask register cannot be written, so no timer * interrupts can be masked within the PLD. diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index 93562304f7aa..ef30c7e9728d 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -26,7 +26,7 @@ #include <linux/watchdog.h> #include <asm/nmi.h> -#define HPWDT_VERSION "2.0.1" +#define HPWDT_VERSION "2.0.2" #define SECS_TO_TICKS(secs) ((secs) * 1000 / 128) #define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000) #define HPWDT_MAX_TIMER TICKS_TO_SECS(65535) @@ -50,6 +50,11 @@ static const struct pci_device_id hpwdt_devices[] = { }; MODULE_DEVICE_TABLE(pci, hpwdt_devices); +static const struct pci_device_id hpwdt_blacklist[] = { + { PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3306, PCI_VENDOR_ID_HP, 0x1979) }, /* auxilary iLO */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3306, PCI_VENDOR_ID_HP_3PAR, 0x0289) }, /* CL */ + {0}, /* terminate list */ +}; /* * Watchdog operations @@ -274,12 +279,10 @@ static int hpwdt_init_one(struct pci_dev *dev, return -ENODEV; } - /* - * Ignore all auxilary iLO devices with the following PCI ID - */ - if (dev->subsystem_vendor == PCI_VENDOR_ID_HP && - dev->subsystem_device == 0x1979) + if (pci_match_id(hpwdt_blacklist, dev)) { + dev_dbg(&dev->dev, "Not supported on this device\n"); return -ENODEV; + } if (pci_enable_device(dev)) { dev_warn(&dev->dev, diff --git a/drivers/watchdog/ie6xx_wdt.c b/drivers/watchdog/ie6xx_wdt.c index 78c2541f5d52..8de9fb1ed371 100644 --- a/drivers/watchdog/ie6xx_wdt.c +++ b/drivers/watchdog/ie6xx_wdt.c @@ -193,7 +193,7 @@ static struct watchdog_device ie6xx_wdt_dev = { #ifdef CONFIG_DEBUG_FS -static int ie6xx_wdt_dbg_show(struct seq_file *s, void *unused) +static int ie6xx_wdt_show(struct seq_file *s, void *unused) { seq_printf(s, "PV1 = 0x%08x\n", inl(ie6xx_wdt_data.sch_wdtba + PV1)); @@ -212,23 +212,13 @@ static int ie6xx_wdt_dbg_show(struct seq_file *s, void *unused) return 0; } -static int ie6xx_wdt_dbg_open(struct inode *inode, struct file *file) -{ - return single_open(file, ie6xx_wdt_dbg_show, NULL); -} - -static const struct file_operations ie6xx_wdt_dbg_operations = { - .open = ie6xx_wdt_dbg_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(ie6xx_wdt); static void ie6xx_wdt_debugfs_init(void) { /* /sys/kernel/debug/ie6xx_wdt */ ie6xx_wdt_data.debugfs = debugfs_create_file("ie6xx_wdt", - S_IFREG | S_IRUGO, NULL, NULL, &ie6xx_wdt_dbg_operations); + S_IFREG | S_IRUGO, NULL, NULL, &ie6xx_wdt_fops); } static void ie6xx_wdt_debugfs_exit(void) diff --git a/drivers/watchdog/mena21_wdt.c b/drivers/watchdog/mena21_wdt.c index 0be7f50e8ff9..6db69883ece6 100644 --- a/drivers/watchdog/mena21_wdt.c +++ b/drivers/watchdog/mena21_wdt.c @@ -13,10 +13,10 @@ #include <linux/platform_device.h> #include <linux/watchdog.h> #include <linux/uaccess.h> -#include <linux/gpio.h> -#include <linux/of_gpio.h> +#include <linux/gpio/consumer.h> #include <linux/delay.h> #include <linux/bitops.h> +#include <linux/of.h> #define NUM_GPIOS 6 @@ -31,7 +31,7 @@ enum a21_wdt_gpios { struct a21_wdt_drv { struct watchdog_device wdt; - unsigned gpios[NUM_GPIOS]; + struct gpio_desc *gpios[NUM_GPIOS]; }; static bool nowayout = WATCHDOG_NOWAYOUT; @@ -43,9 +43,9 @@ static unsigned int a21_wdt_get_bootstatus(struct a21_wdt_drv *drv) { int reset = 0; - reset |= gpio_get_value(drv->gpios[GPIO_WD_RST0]) ? (1 << 0) : 0; - reset |= gpio_get_value(drv->gpios[GPIO_WD_RST1]) ? (1 << 1) : 0; - reset |= gpio_get_value(drv->gpios[GPIO_WD_RST2]) ? (1 << 2) : 0; + reset |= gpiod_get_value(drv->gpios[GPIO_WD_RST0]) ? (1 << 0) : 0; + reset |= gpiod_get_value(drv->gpios[GPIO_WD_RST1]) ? (1 << 1) : 0; + reset |= gpiod_get_value(drv->gpios[GPIO_WD_RST2]) ? (1 << 2) : 0; return reset; } @@ -54,7 +54,7 @@ static int a21_wdt_start(struct watchdog_device *wdt) { struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt); - gpio_set_value(drv->gpios[GPIO_WD_ENAB], 1); + gpiod_set_value(drv->gpios[GPIO_WD_ENAB], 1); return 0; } @@ -63,7 +63,7 @@ static int a21_wdt_stop(struct watchdog_device *wdt) { struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt); - gpio_set_value(drv->gpios[GPIO_WD_ENAB], 0); + gpiod_set_value(drv->gpios[GPIO_WD_ENAB], 0); return 0; } @@ -72,9 +72,9 @@ static int a21_wdt_ping(struct watchdog_device *wdt) { struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt); - gpio_set_value(drv->gpios[GPIO_WD_TRIG], 0); + gpiod_set_value(drv->gpios[GPIO_WD_TRIG], 0); ndelay(10); - gpio_set_value(drv->gpios[GPIO_WD_TRIG], 1); + gpiod_set_value(drv->gpios[GPIO_WD_TRIG], 1); return 0; } @@ -96,9 +96,9 @@ static int a21_wdt_set_timeout(struct watchdog_device *wdt, } if (timeout == 1) - gpio_set_value(drv->gpios[GPIO_WD_FAST], 1); + gpiod_set_value(drv->gpios[GPIO_WD_FAST], 1); else - gpio_set_value(drv->gpios[GPIO_WD_FAST], 0); + gpiod_set_value(drv->gpios[GPIO_WD_FAST], 0); wdt->timeout = timeout; @@ -127,7 +127,6 @@ static struct watchdog_device a21_wdt = { static int a21_wdt_probe(struct platform_device *pdev) { - struct device_node *node; struct a21_wdt_drv *drv; unsigned int reset = 0; int num_gpios; @@ -138,40 +137,40 @@ static int a21_wdt_probe(struct platform_device *pdev) if (!drv) return -ENOMEM; - /* Fill GPIO pin array */ - node = pdev->dev.of_node; - - num_gpios = of_gpio_count(node); + num_gpios = gpiod_count(&pdev->dev, NULL); if (num_gpios != NUM_GPIOS) { dev_err(&pdev->dev, "gpios DT property wrong, got %d want %d", num_gpios, NUM_GPIOS); return -ENODEV; } - for (i = 0; i < num_gpios; i++) { - int val; - - val = of_get_gpio(node, i); - if (val < 0) - return val; - - drv->gpios[i] = val; - } - /* Request the used GPIOs */ for (i = 0; i < num_gpios; i++) { - ret = devm_gpio_request(&pdev->dev, drv->gpios[i], - "MEN A21 Watchdog"); - if (ret) - return ret; + enum gpiod_flags gflags; if (i < GPIO_WD_RST0) - ret = gpio_direction_output(drv->gpios[i], - gpio_get_value(drv->gpios[i])); - else /* GPIO_WD_RST[0..2] are inputs */ - ret = gpio_direction_input(drv->gpios[i]); - if (ret) + gflags = GPIOD_ASIS; + else + gflags = GPIOD_IN; + drv->gpios[i] = devm_gpiod_get_index(&pdev->dev, NULL, i, + gflags); + if (IS_ERR(drv->gpios[i])) { + ret = PTR_ERR(drv->gpios[i]); return ret; + } + + gpiod_set_consumer_name(drv->gpios[i], "MEN A21 Watchdog"); + + /* + * Retrieve the initial value from the GPIOs that should be + * output, then set up the line as output with that value. + */ + if (i < GPIO_WD_RST0) { + int val; + + val = gpiod_get_value(drv->gpios[i]); + gpiod_direction_output(drv->gpios[i], val); + } } watchdog_init_timeout(&a21_wdt, 30, &pdev->dev); @@ -207,7 +206,7 @@ static void a21_wdt_shutdown(struct platform_device *pdev) { struct a21_wdt_drv *drv = dev_get_drvdata(&pdev->dev); - gpio_set_value(drv->gpios[GPIO_WD_ENAB], 0); + gpiod_set_value(drv->gpios[GPIO_WD_ENAB], 0); } static const struct of_device_id a21_wdt_ids[] = { diff --git a/drivers/watchdog/mtx-1_wdt.c b/drivers/watchdog/mtx-1_wdt.c index 1fa7d2b32494..e028e0a2eca0 100644 --- a/drivers/watchdog/mtx-1_wdt.c +++ b/drivers/watchdog/mtx-1_wdt.c @@ -39,7 +39,7 @@ #include <linux/platform_device.h> #include <linux/io.h> #include <linux/uaccess.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <asm/mach-au1x00/au1000.h> @@ -55,7 +55,7 @@ static struct { int queue; int default_ticks; unsigned long inuse; - unsigned gpio; + struct gpio_desc *gpiod; unsigned int gstate; } mtx1_wdt_device; @@ -67,7 +67,7 @@ static void mtx1_wdt_trigger(struct timer_list *unused) /* toggle wdt gpio */ mtx1_wdt_device.gstate = !mtx1_wdt_device.gstate; - gpio_set_value(mtx1_wdt_device.gpio, mtx1_wdt_device.gstate); + gpiod_set_value(mtx1_wdt_device.gpiod, mtx1_wdt_device.gstate); if (mtx1_wdt_device.queue && ticks) mod_timer(&mtx1_wdt_device.timer, jiffies + MTX1_WDT_INTERVAL); @@ -90,7 +90,7 @@ static void mtx1_wdt_start(void) if (!mtx1_wdt_device.queue) { mtx1_wdt_device.queue = 1; mtx1_wdt_device.gstate = 1; - gpio_set_value(mtx1_wdt_device.gpio, 1); + gpiod_set_value(mtx1_wdt_device.gpiod, 1); mod_timer(&mtx1_wdt_device.timer, jiffies + MTX1_WDT_INTERVAL); } mtx1_wdt_device.running++; @@ -105,7 +105,7 @@ static int mtx1_wdt_stop(void) if (mtx1_wdt_device.queue) { mtx1_wdt_device.queue = 0; mtx1_wdt_device.gstate = 0; - gpio_set_value(mtx1_wdt_device.gpio, 0); + gpiod_set_value(mtx1_wdt_device.gpiod, 0); } ticks = mtx1_wdt_device.default_ticks; spin_unlock_irqrestore(&mtx1_wdt_device.lock, flags); @@ -198,12 +198,11 @@ static int mtx1_wdt_probe(struct platform_device *pdev) { int ret; - mtx1_wdt_device.gpio = pdev->resource[0].start; - ret = devm_gpio_request_one(&pdev->dev, mtx1_wdt_device.gpio, - GPIOF_OUT_INIT_HIGH, "mtx1-wdt"); - if (ret < 0) { + mtx1_wdt_device.gpiod = devm_gpiod_get(&pdev->dev, + NULL, GPIOD_OUT_HIGH); + if (IS_ERR(mtx1_wdt_device.gpiod)) { dev_err(&pdev->dev, "failed to request gpio"); - return ret; + return PTR_ERR(mtx1_wdt_device.gpiod); } spin_lock_init(&mtx1_wdt_device.lock); diff --git a/drivers/watchdog/pm8916_wdt.c b/drivers/watchdog/pm8916_wdt.c new file mode 100644 index 000000000000..7f10041fcf5b --- /dev/null +++ b/drivers/watchdog/pm8916_wdt.c @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/bitops.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/property.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/watchdog.h> + +#define PON_INT_RT_STS 0x10 +#define PMIC_WD_BARK_STS_BIT BIT(6) + +#define PON_PMIC_WD_RESET_S1_TIMER 0x54 +#define PON_PMIC_WD_RESET_S2_TIMER 0x55 + +#define PON_PMIC_WD_RESET_S2_CTL 0x56 +#define RESET_TYPE_WARM 0x01 +#define RESET_TYPE_SHUTDOWN 0x04 +#define RESET_TYPE_HARD 0x07 + +#define PON_PMIC_WD_RESET_S2_CTL2 0x57 +#define S2_RESET_EN_BIT BIT(7) + +#define PON_PMIC_WD_RESET_PET 0x58 +#define WATCHDOG_PET_BIT BIT(0) + +#define PM8916_WDT_DEFAULT_TIMEOUT 32 +#define PM8916_WDT_MIN_TIMEOUT 1 +#define PM8916_WDT_MAX_TIMEOUT 127 + +struct pm8916_wdt { + struct regmap *regmap; + struct watchdog_device wdev; + u32 baseaddr; +}; + +static int pm8916_wdt_start(struct watchdog_device *wdev) +{ + struct pm8916_wdt *wdt = watchdog_get_drvdata(wdev); + + return regmap_update_bits(wdt->regmap, + wdt->baseaddr + PON_PMIC_WD_RESET_S2_CTL2, + S2_RESET_EN_BIT, S2_RESET_EN_BIT); +} + +static int pm8916_wdt_stop(struct watchdog_device *wdev) +{ + struct pm8916_wdt *wdt = watchdog_get_drvdata(wdev); + + return regmap_update_bits(wdt->regmap, + wdt->baseaddr + PON_PMIC_WD_RESET_S2_CTL2, + S2_RESET_EN_BIT, 0); +} + +static int pm8916_wdt_ping(struct watchdog_device *wdev) +{ + struct pm8916_wdt *wdt = watchdog_get_drvdata(wdev); + + return regmap_update_bits(wdt->regmap, + wdt->baseaddr + PON_PMIC_WD_RESET_PET, + WATCHDOG_PET_BIT, WATCHDOG_PET_BIT); +} + +static int pm8916_wdt_configure_timers(struct watchdog_device *wdev) +{ + struct pm8916_wdt *wdt = watchdog_get_drvdata(wdev); + int err; + + err = regmap_write(wdt->regmap, + wdt->baseaddr + PON_PMIC_WD_RESET_S1_TIMER, + wdev->timeout - wdev->pretimeout); + if (err) + return err; + + return regmap_write(wdt->regmap, + wdt->baseaddr + PON_PMIC_WD_RESET_S2_TIMER, + wdev->pretimeout); +} + +static int pm8916_wdt_set_timeout(struct watchdog_device *wdev, + unsigned int timeout) +{ + wdev->timeout = timeout; + + return pm8916_wdt_configure_timers(wdev); +} + +static int pm8916_wdt_set_pretimeout(struct watchdog_device *wdev, + unsigned int pretimeout) +{ + wdev->pretimeout = pretimeout; + + return pm8916_wdt_configure_timers(wdev); +} + +static irqreturn_t pm8916_wdt_isr(int irq, void *arg) +{ + struct pm8916_wdt *wdt = arg; + int err, sts; + + err = regmap_read(wdt->regmap, wdt->baseaddr + PON_INT_RT_STS, &sts); + if (err) + return IRQ_HANDLED; + + if (sts & PMIC_WD_BARK_STS_BIT) + watchdog_notify_pretimeout(&wdt->wdev); + + return IRQ_HANDLED; +} + +static const struct watchdog_info pm8916_wdt_ident = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .identity = "QCOM PM8916 PON WDT", +}; + +static const struct watchdog_info pm8916_wdt_pt_ident = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | + WDIOF_PRETIMEOUT, + .identity = "QCOM PM8916 PON WDT", +}; + +static const struct watchdog_ops pm8916_wdt_ops = { + .owner = THIS_MODULE, + .start = pm8916_wdt_start, + .stop = pm8916_wdt_stop, + .ping = pm8916_wdt_ping, + .set_timeout = pm8916_wdt_set_timeout, + .set_pretimeout = pm8916_wdt_set_pretimeout, +}; + +static int pm8916_wdt_probe(struct platform_device *pdev) +{ + struct pm8916_wdt *wdt; + struct device *parent; + int err, irq; + + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + parent = pdev->dev.parent; + + /* + * The pm8916-pon-wdt is a child of the pon device, which is a child + * of the pm8916 mfd device. We want access to the pm8916 registers. + * Retrieve regmap from pm8916 (parent->parent) and base address + * from pm8916-pon (pon). + */ + wdt->regmap = dev_get_regmap(parent->parent, NULL); + if (!wdt->regmap) { + dev_err(&pdev->dev, "failed to locate regmap\n"); + return -ENODEV; + } + + err = device_property_read_u32(parent, "reg", &wdt->baseaddr); + if (err) { + dev_err(&pdev->dev, "failed to get pm8916-pon address\n"); + return err; + } + + irq = platform_get_irq(pdev, 0); + if (irq > 0) { + if (devm_request_irq(&pdev->dev, irq, pm8916_wdt_isr, 0, + "pm8916_wdt", wdt)) + irq = 0; + } + + /* Configure watchdog to hard-reset mode */ + err = regmap_write(wdt->regmap, + wdt->baseaddr + PON_PMIC_WD_RESET_S2_CTL, + RESET_TYPE_HARD); + if (err) { + dev_err(&pdev->dev, "failed configure watchdog\n"); + return err; + } + + wdt->wdev.info = (irq > 0) ? &pm8916_wdt_pt_ident : &pm8916_wdt_ident, + wdt->wdev.ops = &pm8916_wdt_ops, + wdt->wdev.parent = &pdev->dev; + wdt->wdev.min_timeout = PM8916_WDT_MIN_TIMEOUT; + wdt->wdev.max_timeout = PM8916_WDT_MAX_TIMEOUT; + wdt->wdev.timeout = PM8916_WDT_DEFAULT_TIMEOUT; + wdt->wdev.pretimeout = 0; + watchdog_set_drvdata(&wdt->wdev, wdt); + + watchdog_init_timeout(&wdt->wdev, 0, &pdev->dev); + pm8916_wdt_configure_timers(&wdt->wdev); + + return devm_watchdog_register_device(&pdev->dev, &wdt->wdev); +} + +static const struct of_device_id pm8916_wdt_id_table[] = { + { .compatible = "qcom,pm8916-wdt" }, + { } +}; +MODULE_DEVICE_TABLE(of, pm8916_wdt_id_table); + +static struct platform_driver pm8916_wdt_driver = { + .probe = pm8916_wdt_probe, + .driver = { + .name = "pm8916-wdt", + .of_match_table = of_match_ptr(pm8916_wdt_id_table), + }, +}; +module_platform_driver(pm8916_wdt_driver); + +MODULE_AUTHOR("Loic Poulain <loic.poulain@linaro.org>"); +MODULE_DESCRIPTION("Qualcomm pm8916 watchdog driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/watchdog/renesas_wdt.c b/drivers/watchdog/renesas_wdt.c index 0d74c3e48979..622ede529912 100644 --- a/drivers/watchdog/renesas_wdt.c +++ b/drivers/watchdog/renesas_wdt.c @@ -48,7 +48,6 @@ struct rwdt_priv { void __iomem *base; struct watchdog_device wdev; unsigned long clk_rate; - u16 time_left; u8 cks; }; @@ -74,12 +73,17 @@ static int rwdt_init_timeout(struct watchdog_device *wdev) static int rwdt_start(struct watchdog_device *wdev) { struct rwdt_priv *priv = watchdog_get_drvdata(wdev); + u8 val; pm_runtime_get_sync(wdev->parent); - rwdt_write(priv, 0, RWTCSRB); - rwdt_write(priv, priv->cks, RWTCSRA); + /* Stop the timer before we modify any register */ + val = readb_relaxed(priv->base + RWTCSRA) & ~RWTCSRA_TME; + rwdt_write(priv, val, RWTCSRA); + rwdt_init_timeout(wdev); + rwdt_write(priv, priv->cks, RWTCSRA); + rwdt_write(priv, 0, RWTCSRB); while (readb_relaxed(priv->base + RWTCSRA) & RWTCSRA_WRFLG) cpu_relax(); @@ -220,8 +224,8 @@ static int rwdt_probe(struct platform_device *pdev) goto out_pm_disable; } - priv->wdev.info = &rwdt_ident, - priv->wdev.ops = &rwdt_ops, + priv->wdev.info = &rwdt_ident; + priv->wdev.ops = &rwdt_ops; priv->wdev.parent = &pdev->dev; priv->wdev.min_timeout = 1; priv->wdev.max_timeout = DIV_BY_CLKS_PER_SEC(priv, 65536); @@ -263,10 +267,9 @@ static int __maybe_unused rwdt_suspend(struct device *dev) { struct rwdt_priv *priv = dev_get_drvdata(dev); - if (watchdog_active(&priv->wdev)) { - priv->time_left = readw(priv->base + RWTCNT); + if (watchdog_active(&priv->wdev)) rwdt_stop(&priv->wdev); - } + return 0; } @@ -274,10 +277,9 @@ static int __maybe_unused rwdt_resume(struct device *dev) { struct rwdt_priv *priv = dev_get_drvdata(dev); - if (watchdog_active(&priv->wdev)) { + if (watchdog_active(&priv->wdev)) rwdt_start(&priv->wdev); - rwdt_write(priv, priv->time_left, RWTCNT); - } + return 0; } diff --git a/drivers/watchdog/tqmx86_wdt.c b/drivers/watchdog/tqmx86_wdt.c new file mode 100644 index 000000000000..0d3a0fbbd7a5 --- /dev/null +++ b/drivers/watchdog/tqmx86_wdt.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Watchdog driver for TQMx86 PLD. + * + * The watchdog supports power of 2 timeouts from 1 to 4096sec. + * Once started, it cannot be stopped. + * + * Based on the vendor code written by Vadim V.Vlasov + * <vvlasov@dev.rtsoft.ru> + */ + +#include <linux/io.h> +#include <linux/log2.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/timer.h> +#include <linux/watchdog.h> + +/* default timeout (secs) */ +#define WDT_TIMEOUT 32 + +static unsigned int timeout; +module_param(timeout, uint, 0); +MODULE_PARM_DESC(timeout, + "Watchdog timeout in seconds. (1<=timeout<=4096, default=" + __MODULE_STRING(WDT_TIMEOUT) ")"); +struct tqmx86_wdt { + struct watchdog_device wdd; + void __iomem *io_base; +}; + +#define TQMX86_WDCFG 0x00 /* Watchdog Configuration Register */ +#define TQMX86_WDCS 0x01 /* Watchdog Config/Status Register */ + +static int tqmx86_wdt_start(struct watchdog_device *wdd) +{ + struct tqmx86_wdt *priv = watchdog_get_drvdata(wdd); + + iowrite8(0x81, priv->io_base + TQMX86_WDCS); + + return 0; +} + +static int tqmx86_wdt_set_timeout(struct watchdog_device *wdd, unsigned int t) +{ + struct tqmx86_wdt *priv = watchdog_get_drvdata(wdd); + u8 val; + + t = roundup_pow_of_two(t); + val = ilog2(t) | 0x90; + val += 3; /* values 0,1,2 correspond to 0.125,0.25,0.5s timeouts */ + iowrite8(val, priv->io_base + TQMX86_WDCFG); + + wdd->timeout = t; + + return 0; +} + +static const struct watchdog_info tqmx86_wdt_info = { + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING, + .identity = "TQMx86 Watchdog", +}; + +static struct watchdog_ops tqmx86_wdt_ops = { + .owner = THIS_MODULE, + .start = tqmx86_wdt_start, + .set_timeout = tqmx86_wdt_set_timeout, +}; + +static int tqmx86_wdt_probe(struct platform_device *pdev) +{ + struct tqmx86_wdt *priv; + struct resource *res; + int err; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (IS_ERR(res)) + return PTR_ERR(res); + + priv->io_base = devm_ioport_map(&pdev->dev, res->start, + resource_size(res)); + if (IS_ERR(priv->io_base)) + return PTR_ERR(priv->io_base); + + watchdog_set_drvdata(&priv->wdd, priv); + + priv->wdd.parent = &pdev->dev; + priv->wdd.info = &tqmx86_wdt_info; + priv->wdd.ops = &tqmx86_wdt_ops; + priv->wdd.min_timeout = 1; + priv->wdd.max_timeout = 4096; + priv->wdd.max_hw_heartbeat_ms = 4096*1000; + priv->wdd.timeout = WDT_TIMEOUT; + + watchdog_init_timeout(&priv->wdd, timeout, &pdev->dev); + watchdog_set_nowayout(&priv->wdd, WATCHDOG_NOWAYOUT); + + tqmx86_wdt_set_timeout(&priv->wdd, priv->wdd.timeout); + + err = devm_watchdog_register_device(&pdev->dev, &priv->wdd); + if (err) + return err; + + dev_info(&pdev->dev, "TQMx86 watchdog\n"); + + return 0; +} + +static struct platform_driver tqmx86_wdt_driver = { + .driver = { + .name = "tqmx86-wdt", + }, + .probe = tqmx86_wdt_probe, +}; + +module_platform_driver(tqmx86_wdt_driver); + +MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>"); +MODULE_DESCRIPTION("TQMx86 Watchdog"); +MODULE_ALIAS("platform:tqmx86-wdt"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c index 4b9365d4de7a..3a49ba9ea608 100644 --- a/drivers/watchdog/w83627hf_wdt.c +++ b/drivers/watchdog/w83627hf_wdt.c @@ -38,6 +38,7 @@ #include <linux/ioport.h> #include <linux/init.h> #include <linux/io.h> +#include <linux/dmi.h> #define WATCHDOG_NAME "w83627hf/thf/hg/dhg WDT" #define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */ @@ -46,6 +47,8 @@ static int wdt_io; static int cr_wdt_timeout; /* WDT timeout register */ static int cr_wdt_control; /* WDT control register */ static int cr_wdt_csr; /* WDT control & status register */ +static int wdt_cfg_enter = 0x87;/* key to unlock configuration space */ +static int wdt_cfg_leave = 0xAA;/* key to lock configuration space */ enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf, w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p, @@ -130,8 +133,8 @@ static int superio_enter(void) if (!request_muxed_region(wdt_io, 2, WATCHDOG_NAME)) return -EBUSY; - outb_p(0x87, WDT_EFER); /* Enter extended function mode */ - outb_p(0x87, WDT_EFER); /* Again according to manual */ + outb_p(wdt_cfg_enter, WDT_EFER); /* Enter extended function mode */ + outb_p(wdt_cfg_enter, WDT_EFER); /* Again according to manual */ return 0; } @@ -143,7 +146,7 @@ static void superio_select(int ld) static void superio_exit(void) { - outb_p(0xAA, WDT_EFER); /* Leave extended function mode */ + outb_p(wdt_cfg_leave, WDT_EFER); /* Leave extended function mode */ release_region(wdt_io, 2); } @@ -430,6 +433,32 @@ static int wdt_find(int addr) return ret; } +/* + * On some systems, the NCT6791D comes with a companion chip and the + * watchdog function is in this companion chip. We must use a different + * unlocking sequence to access the companion chip. + */ +static int __init wdt_use_alt_key(const struct dmi_system_id *d) +{ + wdt_cfg_enter = 0x88; + wdt_cfg_leave = 0xBB; + + return 0; +} + +static const struct dmi_system_id wdt_dmi_table[] __initconst = { + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "INVES"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "CTS"), + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "INVES"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "SHARKBAY"), + }, + .callback = wdt_use_alt_key, + }, + {} +}; + static int __init wdt_init(void) { int ret; @@ -459,6 +488,9 @@ static int __init wdt_init(void) "NCT6102", }; + /* Apply system-specific quirks */ + dmi_check_system(wdt_dmi_table); + wdt_io = 0x2e; chip = wdt_find(0x2e); if (chip < 0) { |