projects/linux·commit 696c45bc

Merge tag 'linux-watchdog-6.15-rc1' of git://www.linux-watchdog.org/linux-watchdog

696c45bc · Linus Torvalds <[email protected]> · 2025-04-01 16:33:36 -07:00

Pull watchdog updates from Wim Van Sebroeck:

 - Add watchdog driver for Lenovo SE30 platform

 - Add support for Allwinner A523

 - Add i.MX94 support

 - watchdog framework: Convert to use device property

 - renesas,wdt: Document RZ/G3E support

 - Various other fixes and improvemenents

* tag 'linux-watchdog-6.15-rc1' of git://www.linux-watchdog.org/linux-watchdog:
  watchdog: sunxi_wdt: Add support for Allwinner A523
  dt-bindings: watchdog: sunxi: add Allwinner A523 compatible string
  watchdog: aspeed: fix 64-bit division
  watchdog: npcm: Remove unnecessary NULL check before clk_prepare_enable/clk_disable_unprepare
  dt-bindings: watchdog: renesas,wdt: Document RZ/G3E support
  watchdog: Convert to use device property
  watchdog: lenovo_se30_wdt: include io.h for devm_ioremap()
  dt-bindings: watchdog: fsl-imx7ulp-wdt: Add i.MX94 support
  watchdog: nic7018_wdt: tidy up ACPI ID table
  watchdog: s3c2410_wdt: Fix PMU register bits for ExynosAutoV920 SoC
  watchdog: lenovo_se30_wdt: Watchdog driver for Lenovo SE30 platform
  watchdog: Enable RZV2HWDT driver depend on ARCH_RENESAS
  watchdog: cros-ec: Add newlines to printks
  watchdog: aspeed: Update bootstatus handling
Commit
696c45bcc3c35486578fd741d8551865aee42915
Parents
28a1b056 · 9bc64d33

Merge commit. Changes below are compared with the first parent: 28a1b056.

Changes

13 files changed · +535 −28

Only the first 600 diff lines are shown. Download the complete patch.

Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml

View file
diff --git a/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml b/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml
index 64c8f73..b35ac03 100644
--- a/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml
+++ b/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml
@@ -32,6 +32,7 @@ properties:
- items:
- const: allwinner,sun20i-d1-wdt-reset
- const: allwinner,sun20i-d1-wdt
+ - const: allwinner,sun55i-a523-wdt
reg:
maxItems: 1
@@ -60,6 +61,7 @@ if:
- allwinner,sun20i-d1-wdt-reset
- allwinner,sun50i-r329-wdt
- allwinner,sun50i-r329-wdt-reset
+ - allwinner,sun55i-a523-wdt
then:
properties:

Documentation/devicetree/bindings/watchdog/fsl-imx7ulp-wdt.yaml

View file
diff --git a/Documentation/devicetree/bindings/watchdog/fsl-imx7ulp-wdt.yaml b/Documentation/devicetree/bindings/watchdog/fsl-imx7ulp-wdt.yaml
index a09686b..6ec391b 100644
--- a/Documentation/devicetree/bindings/watchdog/fsl-imx7ulp-wdt.yaml
+++ b/Documentation/devicetree/bindings/watchdog/fsl-imx7ulp-wdt.yaml
@@ -22,6 +22,10 @@ properties:
- const: fsl,imx8ulp-wdt
- const: fsl,imx7ulp-wdt
- const: fsl,imx93-wdt
+ - items:
+ - enum:
+ - fsl,imx94-wdt
+ - const: fsl,imx93-wdt
reg:
maxItems: 1

Documentation/devicetree/bindings/watchdog/renesas,wdt.yaml

View file
diff --git a/Documentation/devicetree/bindings/watchdog/renesas,wdt.yaml b/Documentation/devicetree/bindings/watchdog/renesas,wdt.yaml
index 29ada89..3e0a874 100644
--- a/Documentation/devicetree/bindings/watchdog/renesas,wdt.yaml
+++ b/Documentation/devicetree/bindings/watchdog/renesas,wdt.yaml
@@ -75,6 +75,10 @@ properties:
- renesas,r8a779h0-wdt # R-Car V4M
- const: renesas,rcar-gen4-wdt # R-Car Gen4
+ - items:
+ - const: renesas,r9a09g047-wdt # RZ/G3E
+ - const: renesas,r9a09g057-wdt # RZ/V2H(P)
+
- const: renesas,r9a09g057-wdt # RZ/V2H(P)
reg:

drivers/watchdog/Kconfig

View file
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index f81705f..0d8d37f 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -279,6 +279,18 @@ config LENOVO_SE10_WDT
This driver can also be built as a module. If so, the module
will be called lenovo-se10-wdt.
+config LENOVO_SE30_WDT
+ tristate "Lenovo SE30 Watchdog"
+ depends on (X86 && DMI) || COMPILE_TEST
+ depends on HAS_IOPORT
+ select WATCHDOG_CORE
+ help
+ If you say yes here you get support for the watchdog
+ functionality for the Lenovo SE30 platform.
+
+ This driver can also be built as a module. If so, the module
+ will be called lenovo-se30-wdt.
+
config MENF21BMC_WATCHDOG
tristate "MEN 14F021P00 BMC Watchdog"
depends on MFD_MENF21BMC || COMPILE_TEST
@@ -963,13 +975,14 @@ config RENESAS_RZG2LWDT
Renesas RZ/G2L SoCs. These watchdogs can be used to reset a system.
config RENESAS_RZV2HWDT
- tristate "Renesas RZ/V2H(P) WDT Watchdog"
- depends on ARCH_R9A09G057 || COMPILE_TEST
+ tristate "Renesas RZ/{G3E,V2H(P)} WDT Watchdog"
+ depends on ARCH_RENESAS || COMPILE_TEST
depends on PM || COMPILE_TEST
select WATCHDOG_CORE
help
This driver adds watchdog support for the integrated watchdogs in the
- Renesas RZ/V2H(P) SoCs. These watchdogs can be used to reset a system.
+ Renesas RZ/{G3E,V2H(P)} SoCs. These watchdogs can be used to reset a
+ system.
config ASPEED_WATCHDOG
tristate "Aspeed BMC watchdog support"
@@ -1730,7 +1743,8 @@ config NI903X_WDT
config NIC7018_WDT
tristate "NIC7018 Watchdog"
- depends on X86 && ACPI
+ depends on HAS_IOPORT
+ depends on ACPI || COMPILE_TEST
select WATCHDOG_CORE
help
Support for National Instruments NIC7018 Watchdog.

drivers/watchdog/Makefile

View file
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 8411626..c948290 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -124,6 +124,7 @@ obj-$(CONFIG_I6300ESB_WDT) += i6300esb.o
obj-$(CONFIG_IE6XX_WDT) += ie6xx_wdt.o
obj-$(CONFIG_ITCO_WDT) += iTCO_wdt.o
obj-$(CONFIG_LENOVO_SE10_WDT) += lenovo_se10_wdt.o
+obj-$(CONFIG_LENOVO_SE30_WDT) += lenovo_se30_wdt.o
ifeq ($(CONFIG_ITCO_VENDOR_SUPPORT),y)
obj-$(CONFIG_ITCO_WDT) += iTCO_vendor_support.o
endif

drivers/watchdog/aspeed_wdt.c

View file
diff --git a/drivers/watchdog/aspeed_wdt.c b/drivers/watchdog/aspeed_wdt.c
index b4773a6..837e157 100644
--- a/drivers/watchdog/aspeed_wdt.c
+++ b/drivers/watchdog/aspeed_wdt.c
@@ -11,21 +11,30 @@
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/kstrtox.h>
+#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
+#include <linux/regmap.h>
#include <linux/watchdog.h>
static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+struct aspeed_wdt_scu {
+ const char *compatible;
+ u32 reset_status_reg;
+ u32 wdt_reset_mask;
+ u32 wdt_reset_mask_shift;
+};
struct aspeed_wdt_config {
u32 ext_pulse_width_mask;
u32 irq_shift;
u32 irq_mask;
+ struct aspeed_wdt_scu scu;
};
struct aspeed_wdt {
@@ -39,18 +48,36 @@ static const struct aspeed_wdt_config ast2400_config = {
.ext_pulse_width_mask = 0xff,
.irq_shift = 0,
.irq_mask = 0,
+ .scu = {
+ .compatible = "aspeed,ast2400-scu",
+ .reset_status_reg = 0x3c,
+ .wdt_reset_mask = 0x1,
+ .wdt_reset_mask_shift = 1,
+ },
};
static const struct aspeed_wdt_config ast2500_config = {
.ext_pulse_width_mask = 0xfffff,
.irq_shift = 12,
.irq_mask = GENMASK(31, 12),
+ .scu = {
+ .compatible = "aspeed,ast2500-scu",
+ .reset_status_reg = 0x3c,
+ .wdt_reset_mask = 0x1,
+ .wdt_reset_mask_shift = 2,
+ },
};
static const struct aspeed_wdt_config ast2600_config = {
.ext_pulse_width_mask = 0xfffff,
.irq_shift = 0,
.irq_mask = GENMASK(31, 10),
+ .scu = {
+ .compatible = "aspeed,ast2600-scu",
+ .reset_status_reg = 0x74,
+ .wdt_reset_mask = 0xf,
+ .wdt_reset_mask_shift = 16,
+ },
};
static const struct of_device_id aspeed_wdt_of_table[] = {
@@ -213,6 +240,56 @@ static int aspeed_wdt_restart(struct watchdog_device *wdd,
return 0;
}
+static void aspeed_wdt_update_bootstatus(struct platform_device *pdev,
+ struct aspeed_wdt *wdt)
+{
+ const struct resource *res;
+ struct aspeed_wdt_scu scu = wdt->cfg->scu;
+ struct regmap *scu_base;
+ u32 reset_mask_width;
+ u32 reset_mask_shift;
+ u32 idx = 0;
+ u32 status;
+ int ret;
+
+ if (!of_device_is_compatible(pdev->dev.of_node, "aspeed,ast2400-wdt")) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ idx = ((intptr_t)wdt->base & 0x00000fff) / (uintptr_t)resource_size(res);
+ }
+
+ scu_base = syscon_regmap_lookup_by_compatible(scu.compatible);
+ if (IS_ERR(scu_base)) {
+ wdt->wdd.bootstatus = WDIOS_UNKNOWN;
+ return;
+ }
+
+ ret = regmap_read(scu_base, scu.reset_status_reg, &status);
+ if (ret) {
+ wdt->wdd.bootstatus = WDIOS_UNKNOWN;
+ return;
+ }
+
+ reset_mask_width = hweight32(scu.wdt_reset_mask);
+ reset_mask_shift = scu.wdt_reset_mask_shift +
+ reset_mask_width * idx;
+
+ if (status & (scu.wdt_reset_mask << reset_mask_shift))
+ wdt->wdd.bootstatus = WDIOF_CARDRESET;
+
+ /* clear wdt reset event flag */
+ if (of_device_is_compatible(pdev->dev.of_node, "aspeed,ast2400-wdt") ||
+ of_device_is_compatible(pdev->dev.of_node, "aspeed,ast2500-wdt")) {
+ ret = regmap_read(scu_base, scu.reset_status_reg, &status);
+ if (!ret) {
+ status &= ~(scu.wdt_reset_mask << reset_mask_shift);
+ regmap_write(scu_base, scu.reset_status_reg, status);
+ }
+ } else {
+ regmap_write(scu_base, scu.reset_status_reg,
+ scu.wdt_reset_mask << reset_mask_shift);
+ }
+}
+
/* access_cs0 shows if cs0 is accessible, hence the reverted bit */
static ssize_t access_cs0_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -458,10 +535,10 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
writel(duration - 1, wdt->base + WDT_RESET_WIDTH);
}
+ aspeed_wdt_update_bootstatus(pdev, wdt);
+
status = readl(wdt->base + WDT_TIMEOUT_STATUS);
if (status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY) {
- wdt->wdd.bootstatus = WDIOF_CARDRESET;
-
if (of_device_is_compatible(np, "aspeed,ast2400-wdt") ||
of_device_is_compatible(np, "aspeed,ast2500-wdt"))
wdt->wdd.groups = bswitch_groups;

drivers/watchdog/cros_ec_wdt.c

View file
diff --git a/drivers/watchdog/cros_ec_wdt.c b/drivers/watchdog/cros_ec_wdt.c
index ba045e2..716c23f 100644
--- a/drivers/watchdog/cros_ec_wdt.c
+++ b/drivers/watchdog/cros_ec_wdt.c
@@ -58,7 +58,7 @@ static int cros_ec_wdt_ping(struct watchdog_device *wdd)
arg.req.command = EC_HANG_DETECT_CMD_RELOAD;
ret = cros_ec_wdt_send_cmd(cros_ec, &arg);
if (ret < 0)
- dev_dbg(wdd->parent, "Failed to ping watchdog (%d)", ret);
+ dev_dbg(wdd->parent, "Failed to ping watchdog (%d)\n", ret);
return ret;
}
@@ -74,7 +74,7 @@ static int cros_ec_wdt_start(struct watchdog_device *wdd)
arg.req.reboot_timeout_sec = wdd->timeout;
ret = cros_ec_wdt_send_cmd(cros_ec, &arg);
if (ret < 0)
- dev_dbg(wdd->parent, "Failed to start watchdog (%d)", ret);
+ dev_dbg(wdd->parent, "Failed to start watchdog (%d)\n", ret);
return ret;
}
@@ -88,7 +88,7 @@ static int cros_ec_wdt_stop(struct watchdog_device *wdd)
arg.req.command = EC_HANG_DETECT_CMD_CANCEL;
ret = cros_ec_wdt_send_cmd(cros_ec, &arg);
if (ret < 0)
- dev_dbg(wdd->parent, "Failed to stop watchdog (%d)", ret);
+ dev_dbg(wdd->parent, "Failed to stop watchdog (%d)\n", ret);
return ret;
}
@@ -136,7 +136,7 @@ static int cros_ec_wdt_probe(struct platform_device *pdev)
arg.req.command = EC_HANG_DETECT_CMD_GET_STATUS;
ret = cros_ec_wdt_send_cmd(cros_ec, &arg);
if (ret < 0)
- return dev_err_probe(dev, ret, "Failed to get watchdog bootstatus");
+ return dev_err_probe(dev, ret, "Failed to get watchdog bootstatus\n");
wdd->parent = &pdev->dev;
wdd->info = &cros_ec_wdt_ident;
@@ -150,7 +150,7 @@ static int cros_ec_wdt_probe(struct platform_device *pdev)
arg.req.command = EC_HANG_DETECT_CMD_CLEAR_STATUS;
ret = cros_ec_wdt_send_cmd(cros_ec, &arg);
if (ret < 0)
- return dev_err_probe(dev, ret, "Failed to clear watchdog bootstatus");
+ return dev_err_probe(dev, ret, "Failed to clear watchdog bootstatus\n");
watchdog_stop_on_reboot(wdd);
watchdog_stop_on_unregister(wdd);

drivers/watchdog/lenovo_se30_wdt.c

View file
diff --git a/drivers/watchdog/lenovo_se30_wdt.c b/drivers/watchdog/lenovo_se30_wdt.c
new file mode 100644
index 0000000..024b842
--- /dev/null
+++ b/drivers/watchdog/lenovo_se30_wdt.c
@@ -0,0 +1,394 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * WDT driver for Lenovo SE30 device
+ */
+
+#define dev_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/io.h>
+#include <linux/dmi.h>
+#include <linux/delay.h>
+#include <linux/iommu.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+
+#define IOREGION_OFFSET 4 /* Use EC port 1 */
+#define IOREGION_LENGTH 4
+
+#define WATCHDOG_TIMEOUT 60
+
+#define MIN_TIMEOUT 1
+#define MAX_TIMEOUT 255
+#define MAX_WAIT 10
+
+static int timeout; /* in seconds */
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. 1 <= timeout <= 255, default="
+ __MODULE_STRING(WATCHDOG_TIMEOUT) ".");
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+#define LNV_SE30_NAME "lenovo-se30-wdt"
+#define LNV_SE30_ID 0x0110
+#define CHIPID_MASK 0xFFF0
+
+#define CHIPID_REG 0x20
+#define SIO_REG 0x2e
+#define LDN_REG 0x07
+#define UNLOCK_KEY 0x87
+#define LOCK_KEY 0xAA
+#define LD_NUM_SHM 0x0F
+#define LD_BASE_ADDR 0xF8
+
+#define WDT_MODULE 0x10
+#define WDT_CFG_INDEX 0x15 /* WD configuration register */
+#define WDT_CNT_INDEX 0x16 /* WD timer count register */
+#define WDT_CFG_RESET 0x2
+
+/* Host Interface WIN2 offset definition */
+#define SHM_WIN_SIZE 0xFF
+#define SHM_WIN_MOD_OFFSET 0x01
+#define SHM_WIN_CMD_OFFSET 0x02
+#define SHM_WIN_SEL_OFFSET 0x03
+#define SHM_WIN_CTL_OFFSET 0x04
+#define VAL_SHM_WIN_CTRL_WR 0x40
+#define VAL_SHM_WIN_CTRL_RD 0x80
+#define SHM_WIN_ID_OFFSET 0x08
+#define SHM_WIN_DAT_OFFSET 0x10
+
+struct nct6692_reg {
+ unsigned char mod;
+ unsigned char cmd;
+ unsigned char sel;
+ unsigned int idx;
+};
+
+/* Watchdog is based on NCT6692 device */
+struct lenovo_se30_wdt {
+ unsigned char __iomem *shm_base_addr;
+ struct nct6692_reg wdt_cfg;
+ struct nct6692_reg wdt_cnt;
+ struct watchdog_device wdt;
+};
+
+static inline void superio_outb(int ioreg, int reg, int val)
+{
+ outb(reg, ioreg);
+ outb(val, ioreg + 1);
+}
+
+static inline int superio_inb(int ioreg, int reg)
+{
+ outb(reg, ioreg);
+ return inb(ioreg + 1);
+}
+
+static inline int superio_enter(int key, int addr, const char *name)
+{
+ if (!request_muxed_region(addr, 2, name)) {
+ pr_err("I/O address 0x%04x already in use\n", addr);
+ return -EBUSY;
+ }
+ outb(key, addr); /* Enter extended function mode */
+ outb(key, addr); /* Again according to manual */
+
+ return 0;
+}
+
+static inline void superio_exit(int key, int addr)
+{
+ outb(key, addr); /* Leave extended function mode */
+ release_region(addr, 2);
+}
+
+static int shm_get_ready(unsigned char __iomem *shm_base_addr,
+ const struct nct6692_reg *reg)
+{
+ unsigned char pre_id, new_id;
+ int loop = 0;
+
+ iowrite8(reg->mod, shm_base_addr + SHM_WIN_MOD_OFFSET);
+ iowrite8(reg->cmd, shm_base_addr + SHM_WIN_CMD_OFFSET);
+ iowrite8(reg->sel, shm_base_addr + SHM_WIN_SEL_OFFSET);
+
+ pre_id = ioread8(shm_base_addr + SHM_WIN_ID_OFFSET);
+ iowrite8(VAL_SHM_WIN_CTRL_RD, shm_base_addr + SHM_WIN_CTL_OFFSET);
+
+ /* Loop checking when interface is ready */
+ while (loop < MAX_WAIT) {
+ new_id = ioread8(shm_base_addr + SHM_WIN_ID_OFFSET);
+ if (new_id != pre_id)
+ return 0;
+ loop++;
+ usleep_range(10, 125);
+ }
+ return -ETIMEDOUT;
+}
+
+static int read_shm_win(unsigned char __iomem *shm_base_addr,
+ const struct nct6692_reg *reg,
+ unsigned char idx_offset,
+ unsigned char *data)
+{
+ int err = shm_get_ready(shm_base_addr, reg);
+
+ if (err)
+ return err;
+ *data = ioread8(shm_base_addr + SHM_WIN_DAT_OFFSET + reg->idx + idx_offset);
+ return 0;
+}
+
+static int write_shm_win(unsigned char __iomem *shm_base_addr,
+ const struct nct6692_reg *reg,
+ unsigned char idx_offset,
+ unsigned char val)
+{
+ int err = shm_get_ready(shm_base_addr, reg);
+
+ if (err)
+ return err;
+ iowrite8(val, shm_base_addr + SHM_WIN_DAT_OFFSET + reg->idx + idx_offset);
+ iowrite8(VAL_SHM_WIN_CTRL_WR, shm_base_addr + SHM_WIN_CTL_OFFSET);
+ err = shm_get_ready(shm_base_addr, reg);
+ return err;
+}
+
+static int lenovo_se30_wdt_enable(struct lenovo_se30_wdt *data, unsigned int timeout)
+{
+ if (timeout) {
+ int err = write_shm_win(data->shm_base_addr, &data->wdt_cfg, 0, WDT_CFG_RESET);
+
+ if (err)
+ return err;
+ }
+ return write_shm_win(data->shm_base_addr, &data->wdt_cnt, 0, timeout);
+}
+
+static int lenovo_se30_wdt_start(struct watchdog_device *wdog)
+{
+ struct lenovo_se30_wdt *data = watchdog_get_drvdata(wdog);
+
+ return lenovo_se30_wdt_enable(data, wdog->timeout);
+}
+
+static int lenovo_se30_wdt_stop(struct watchdog_device *wdog)
+{
+ struct lenovo_se30_wdt *data = watchdog_get_drvdata(wdog);
+
+ return lenovo_se30_wdt_enable(data, 0);
+}
+
+static unsigned int lenovo_se30_wdt_get_timeleft(struct watchdog_device *wdog)
+{
+ struct lenovo_se30_wdt *data = watchdog_get_drvdata(wdog);
+ unsigned char timeleft;
+ int err;
+
+ err = read_shm_win(data->shm_base_addr, &data->wdt_cnt, 0, &timeleft);
+ if (err)
+ return 0;
+ return timeleft;
+}
+
+static int lenovo_se30_wdt_ping(struct watchdog_device *wdt)
+{
+ struct lenovo_se30_wdt *data = watchdog_get_drvdata(wdt);
+ int err = 0;
+
+ /*
+ * Device does not support refreshing WDT_TIMER_REG register when
+ * the watchdog is active. Need to disable, feed and enable again
+ */
+ err = lenovo_se30_wdt_enable(data, 0);
+ if (err)
+ return err;
+
+ err = write_shm_win(data->shm_base_addr, &data->wdt_cnt, 0, wdt->timeout);
+ if (!err)
+ err = lenovo_se30_wdt_enable(data, wdt->timeout);
+
+ return err;
+}
+
+static const struct watchdog_info lenovo_se30_wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE,
+ .identity = "Lenovo SE30 watchdog",
+};
+
+static const struct watchdog_ops lenovo_se30_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = lenovo_se30_wdt_start,
+ .stop = lenovo_se30_wdt_stop,
+ .ping = lenovo_se30_wdt_ping,
+ .get_timeleft = lenovo_se30_wdt_get_timeleft,
+};
+
+static int lenovo_se30_wdt_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct lenovo_se30_wdt *priv;
+ unsigned long base_phys;
+ unsigned short val;
+ int err;
+
+ err = superio_enter(UNLOCK_KEY, SIO_REG, LNV_SE30_NAME);
+ if (err)
+ return err;
+
+ val = superio_inb(SIO_REG, CHIPID_REG) << 8;
+ val |= superio_inb(SIO_REG, CHIPID_REG + 1);
+
+ if ((val & CHIPID_MASK) != LNV_SE30_ID) {
+ superio_exit(LOCK_KEY, SIO_REG);
+ return -ENODEV;
+ }
+
+ superio_outb(SIO_REG, LDN_REG, LD_NUM_SHM);
+ base_phys = (superio_inb(SIO_REG, LD_BASE_ADDR) |
+ (superio_inb(SIO_REG, LD_BASE_ADDR + 1) << 8) |
+ (superio_inb(SIO_REG, LD_BASE_ADDR + 2) << 16) |
+ (superio_inb(SIO_REG, LD_BASE_ADDR + 3) << 24)) &
+ 0xFFFFFFFF;
+
+ superio_exit(LOCK_KEY, SIO_REG);
+ if (base_phys == 0xFFFFFFFF || base_phys == 0)
+ return -ENODEV;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ if (!devm_request_mem_region(dev, base_phys, SHM_WIN_SIZE, LNV_SE30_NAME))
+ return -EBUSY;
+
+ priv->shm_base_addr = devm_ioremap(dev, base_phys, SHM_WIN_SIZE);
+
+ priv->wdt_cfg.mod = WDT_MODULE;
+ priv->wdt_cfg.idx = WDT_CFG_INDEX;
+ priv->wdt_cnt.mod = WDT_MODULE;
+ priv->wdt_cnt.idx = WDT_CNT_INDEX;
+
+ priv->wdt.ops = &lenovo_se30_wdt_ops;
+ priv->wdt.info = &lenovo_se30_wdt_info;
+ priv->wdt.timeout = WATCHDOG_TIMEOUT; /* Set default timeout */
+ priv->wdt.min_timeout = MIN_TIMEOUT;
+ priv->wdt.max_timeout = MAX_TIMEOUT;
+ priv->wdt.parent = dev;
+
+ watchdog_init_timeout(&priv->wdt, timeout, dev);
+ watchdog_set_drvdata(&priv->wdt, priv);
+ watchdog_set_nowayout(&priv->wdt, nowayout);
+ watchdog_stop_on_reboot(&priv->wdt);

This diff is incomplete. Download the complete patch.

drivers/watchdog/nic7018_wdt.c

View file

This diff is incomplete. Download the complete patch.

drivers/watchdog/npcm_wdt.c

View file

This diff is incomplete. Download the complete patch.

drivers/watchdog/s3c2410_wdt.c

View file

This diff is incomplete. Download the complete patch.

drivers/watchdog/sunxi_wdt.c

View file

This diff is incomplete. Download the complete patch.

drivers/watchdog/watchdog_core.c

View file

This diff is incomplete. Download the complete patch.