summaryrefslogtreecommitdiff
path: root/drivers/watchdog
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/watchdog')
-rw-r--r--drivers/watchdog/Kconfig11
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/intel_oc_wdt.c8
-rw-r--r--drivers/watchdog/mpc8xxx_wdt.c2
-rw-r--r--drivers/watchdog/nct6694_wdt.c307
-rw-r--r--drivers/watchdog/rzg2l_wdt.c4
-rw-r--r--drivers/watchdog/rzv2h_wdt.c150
-rw-r--r--drivers/watchdog/s3c2410_wdt.c46
-rw-r--r--drivers/watchdog/visconti_wdt.c5
9 files changed, 491 insertions, 43 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 0c25b2ed44eb..05008d937e40 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -760,6 +760,17 @@ config MAX77620_WATCHDOG
MAX77620 chips. To compile this driver as a module,
choose M here: the module will be called max77620_wdt.
+config NCT6694_WATCHDOG
+ tristate "Nuvoton NCT6694 watchdog support"
+ depends on MFD_NCT6694
+ select WATCHDOG_CORE
+ help
+ Say Y here to support Nuvoton NCT6694 watchdog timer
+ functionality.
+
+ This driver can also be built as a module. If so, the module
+ will be called nct6694_wdt.
+
config IMX2_WDT
tristate "IMX2+ Watchdog"
depends on ARCH_MXC || ARCH_LAYERSCAPE || COMPILE_TEST
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index bbd4d62d2cc3..b680e4d3c1bc 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -235,6 +235,7 @@ obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o
obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o
obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o
obj-$(CONFIG_MAX77620_WATCHDOG) += max77620_wdt.o
+obj-$(CONFIG_NCT6694_WATCHDOG) += nct6694_wdt.o
obj-$(CONFIG_ZIIRAVE_WATCHDOG) += ziirave_wdt.o
obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o
diff --git a/drivers/watchdog/intel_oc_wdt.c b/drivers/watchdog/intel_oc_wdt.c
index 7c0551106981..a39892c10770 100644
--- a/drivers/watchdog/intel_oc_wdt.c
+++ b/drivers/watchdog/intel_oc_wdt.c
@@ -41,6 +41,7 @@
struct intel_oc_wdt {
struct watchdog_device wdd;
struct resource *ctrl_res;
+ struct watchdog_info info;
bool locked;
};
@@ -115,7 +116,6 @@ static const struct watchdog_ops intel_oc_wdt_ops = {
static int intel_oc_wdt_setup(struct intel_oc_wdt *oc_wdt)
{
- struct watchdog_info *info;
unsigned long val;
val = inl(INTEL_OC_WDT_CTRL_REG(oc_wdt));
@@ -134,7 +134,6 @@ static int intel_oc_wdt_setup(struct intel_oc_wdt *oc_wdt)
set_bit(WDOG_HW_RUNNING, &oc_wdt->wdd.status);
if (oc_wdt->locked) {
- info = (struct watchdog_info *)&intel_oc_wdt_info;
/*
* Set nowayout unconditionally as we cannot stop
* the watchdog.
@@ -145,7 +144,7 @@ static int intel_oc_wdt_setup(struct intel_oc_wdt *oc_wdt)
* and inform the core we can't change it.
*/
oc_wdt->wdd.timeout = (val & INTEL_OC_WDT_TOV) + 1;
- info->options &= ~WDIOF_SETTIMEOUT;
+ oc_wdt->info.options &= ~WDIOF_SETTIMEOUT;
dev_info(oc_wdt->wdd.parent,
"Register access locked, heartbeat fixed at: %u s\n",
@@ -193,7 +192,8 @@ static int intel_oc_wdt_probe(struct platform_device *pdev)
wdd->min_timeout = INTEL_OC_WDT_MIN_TOV;
wdd->max_timeout = INTEL_OC_WDT_MAX_TOV;
wdd->timeout = INTEL_OC_WDT_DEF_TOV;
- wdd->info = &intel_oc_wdt_info;
+ oc_wdt->info = intel_oc_wdt_info;
+ wdd->info = &oc_wdt->info;
wdd->ops = &intel_oc_wdt_ops;
wdd->parent = dev;
diff --git a/drivers/watchdog/mpc8xxx_wdt.c b/drivers/watchdog/mpc8xxx_wdt.c
index 867f9f311379..a4b497ecfa20 100644
--- a/drivers/watchdog/mpc8xxx_wdt.c
+++ b/drivers/watchdog/mpc8xxx_wdt.c
@@ -100,6 +100,8 @@ static int mpc8xxx_wdt_start(struct watchdog_device *w)
ddata->swtc = tmp >> 16;
set_bit(WDOG_HW_RUNNING, &ddata->wdd.status);
+ mpc8xxx_wdt_keepalive(ddata);
+
return 0;
}
diff --git a/drivers/watchdog/nct6694_wdt.c b/drivers/watchdog/nct6694_wdt.c
new file mode 100644
index 000000000000..bc3689bd4b6b
--- /dev/null
+++ b/drivers/watchdog/nct6694_wdt.c
@@ -0,0 +1,307 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Nuvoton NCT6694 WDT driver based on USB interface.
+ *
+ * Copyright (C) 2025 Nuvoton Technology Corp.
+ */
+
+#include <linux/idr.h>
+#include <linux/kernel.h>
+#include <linux/mfd/nct6694.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/watchdog.h>
+
+#define DEVICE_NAME "nct6694-wdt"
+
+#define NCT6694_DEFAULT_TIMEOUT 10
+#define NCT6694_DEFAULT_PRETIMEOUT 0
+
+#define NCT6694_WDT_MAX_DEVS 2
+
+/*
+ * USB command module type for NCT6694 WDT controller.
+ * This defines the module type used for communication with the NCT6694
+ * WDT controller over the USB interface.
+ */
+#define NCT6694_WDT_MOD 0x07
+
+/* Command 00h - WDT Setup */
+#define NCT6694_WDT_SETUP 0x00
+#define NCT6694_WDT_SETUP_SEL(idx) (idx ? 0x01 : 0x00)
+
+/* Command 01h - WDT Command */
+#define NCT6694_WDT_COMMAND 0x01
+#define NCT6694_WDT_COMMAND_SEL(idx) (idx ? 0x01 : 0x00)
+
+static unsigned int timeout[NCT6694_WDT_MAX_DEVS] = {
+ [0 ... (NCT6694_WDT_MAX_DEVS - 1)] = NCT6694_DEFAULT_TIMEOUT
+};
+module_param_array(timeout, int, NULL, 0644);
+MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds");
+
+static unsigned int pretimeout[NCT6694_WDT_MAX_DEVS] = {
+ [0 ... (NCT6694_WDT_MAX_DEVS - 1)] = NCT6694_DEFAULT_PRETIMEOUT
+};
+module_param_array(pretimeout, int, NULL, 0644);
+MODULE_PARM_DESC(pretimeout, "Watchdog pre-timeout in seconds");
+
+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) ")");
+
+enum {
+ NCT6694_ACTION_NONE = 0,
+ NCT6694_ACTION_SIRQ,
+ NCT6694_ACTION_GPO,
+};
+
+struct __packed nct6694_wdt_setup {
+ __le32 pretimeout;
+ __le32 timeout;
+ u8 owner;
+ u8 scratch;
+ u8 control;
+ u8 status;
+ __le32 countdown;
+};
+
+struct __packed nct6694_wdt_cmd {
+ __le32 wdt_cmd;
+ __le32 reserved;
+};
+
+union __packed nct6694_wdt_msg {
+ struct nct6694_wdt_setup setup;
+ struct nct6694_wdt_cmd cmd;
+};
+
+struct nct6694_wdt_data {
+ struct watchdog_device wdev;
+ struct device *dev;
+ struct nct6694 *nct6694;
+ union nct6694_wdt_msg *msg;
+ unsigned char wdev_idx;
+};
+
+static int nct6694_wdt_setting(struct watchdog_device *wdev,
+ u32 timeout_val, u8 timeout_act,
+ u32 pretimeout_val, u8 pretimeout_act)
+{
+ struct nct6694_wdt_data *data = watchdog_get_drvdata(wdev);
+ struct nct6694_wdt_setup *setup = &data->msg->setup;
+ const struct nct6694_cmd_header cmd_hd = {
+ .mod = NCT6694_WDT_MOD,
+ .cmd = NCT6694_WDT_SETUP,
+ .sel = NCT6694_WDT_SETUP_SEL(data->wdev_idx),
+ .len = cpu_to_le16(sizeof(*setup))
+ };
+ unsigned int timeout_fmt, pretimeout_fmt;
+
+ if (pretimeout_val == 0)
+ pretimeout_act = NCT6694_ACTION_NONE;
+
+ timeout_fmt = (timeout_val * 1000) | (timeout_act << 24);
+ pretimeout_fmt = (pretimeout_val * 1000) | (pretimeout_act << 24);
+
+ memset(setup, 0, sizeof(*setup));
+ setup->timeout = cpu_to_le32(timeout_fmt);
+ setup->pretimeout = cpu_to_le32(pretimeout_fmt);
+
+ return nct6694_write_msg(data->nct6694, &cmd_hd, setup);
+}
+
+static int nct6694_wdt_start(struct watchdog_device *wdev)
+{
+ struct nct6694_wdt_data *data = watchdog_get_drvdata(wdev);
+ int ret;
+
+ ret = nct6694_wdt_setting(wdev, wdev->timeout, NCT6694_ACTION_GPO,
+ wdev->pretimeout, NCT6694_ACTION_GPO);
+ if (ret)
+ return ret;
+
+ dev_dbg(data->dev, "Setting WDT(%d): timeout = %d, pretimeout = %d\n",
+ data->wdev_idx, wdev->timeout, wdev->pretimeout);
+
+ return ret;
+}
+
+static int nct6694_wdt_stop(struct watchdog_device *wdev)
+{
+ struct nct6694_wdt_data *data = watchdog_get_drvdata(wdev);
+ struct nct6694_wdt_cmd *cmd = &data->msg->cmd;
+ const struct nct6694_cmd_header cmd_hd = {
+ .mod = NCT6694_WDT_MOD,
+ .cmd = NCT6694_WDT_COMMAND,
+ .sel = NCT6694_WDT_COMMAND_SEL(data->wdev_idx),
+ .len = cpu_to_le16(sizeof(*cmd))
+ };
+
+ memcpy(&cmd->wdt_cmd, "WDTC", 4);
+ cmd->reserved = 0;
+
+ return nct6694_write_msg(data->nct6694, &cmd_hd, cmd);
+}
+
+static int nct6694_wdt_ping(struct watchdog_device *wdev)
+{
+ struct nct6694_wdt_data *data = watchdog_get_drvdata(wdev);
+ struct nct6694_wdt_cmd *cmd = &data->msg->cmd;
+ const struct nct6694_cmd_header cmd_hd = {
+ .mod = NCT6694_WDT_MOD,
+ .cmd = NCT6694_WDT_COMMAND,
+ .sel = NCT6694_WDT_COMMAND_SEL(data->wdev_idx),
+ .len = cpu_to_le16(sizeof(*cmd))
+ };
+
+ memcpy(&cmd->wdt_cmd, "WDTS", 4);
+ cmd->reserved = 0;
+
+ return nct6694_write_msg(data->nct6694, &cmd_hd, cmd);
+}
+
+static int nct6694_wdt_set_timeout(struct watchdog_device *wdev,
+ unsigned int new_timeout)
+{
+ int ret;
+
+ ret = nct6694_wdt_setting(wdev, new_timeout, NCT6694_ACTION_GPO,
+ wdev->pretimeout, NCT6694_ACTION_GPO);
+ if (ret)
+ return ret;
+
+ wdev->timeout = new_timeout;
+
+ return 0;
+}
+
+static int nct6694_wdt_set_pretimeout(struct watchdog_device *wdev,
+ unsigned int new_pretimeout)
+{
+ int ret;
+
+ ret = nct6694_wdt_setting(wdev, wdev->timeout, NCT6694_ACTION_GPO,
+ new_pretimeout, NCT6694_ACTION_GPO);
+ if (ret)
+ return ret;
+
+ wdev->pretimeout = new_pretimeout;
+
+ return 0;
+}
+
+static unsigned int nct6694_wdt_get_time(struct watchdog_device *wdev)
+{
+ struct nct6694_wdt_data *data = watchdog_get_drvdata(wdev);
+ struct nct6694_wdt_setup *setup = &data->msg->setup;
+ const struct nct6694_cmd_header cmd_hd = {
+ .mod = NCT6694_WDT_MOD,
+ .cmd = NCT6694_WDT_SETUP,
+ .sel = NCT6694_WDT_SETUP_SEL(data->wdev_idx),
+ .len = cpu_to_le16(sizeof(*setup))
+ };
+ unsigned int timeleft_ms;
+ int ret;
+
+ ret = nct6694_read_msg(data->nct6694, &cmd_hd, setup);
+ if (ret)
+ return 0;
+
+ timeleft_ms = le32_to_cpu(setup->countdown);
+
+ return timeleft_ms / 1000;
+}
+
+static const struct watchdog_info nct6694_wdt_info = {
+ .options = WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE |
+ WDIOF_PRETIMEOUT,
+ .identity = DEVICE_NAME,
+};
+
+static const struct watchdog_ops nct6694_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = nct6694_wdt_start,
+ .stop = nct6694_wdt_stop,
+ .set_timeout = nct6694_wdt_set_timeout,
+ .set_pretimeout = nct6694_wdt_set_pretimeout,
+ .get_timeleft = nct6694_wdt_get_time,
+ .ping = nct6694_wdt_ping,
+};
+
+static void nct6694_wdt_ida_free(void *d)
+{
+ struct nct6694_wdt_data *data = d;
+ struct nct6694 *nct6694 = data->nct6694;
+
+ ida_free(&nct6694->wdt_ida, data->wdev_idx);
+}
+
+static int nct6694_wdt_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct nct6694 *nct6694 = dev_get_drvdata(dev->parent);
+ struct nct6694_wdt_data *data;
+ struct watchdog_device *wdev;
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->msg = devm_kzalloc(dev, sizeof(union nct6694_wdt_msg),
+ GFP_KERNEL);
+ if (!data->msg)
+ return -ENOMEM;
+
+ data->dev = dev;
+ data->nct6694 = nct6694;
+
+ ret = ida_alloc(&nct6694->wdt_ida, GFP_KERNEL);
+ if (ret < 0)
+ return ret;
+ data->wdev_idx = ret;
+
+ ret = devm_add_action_or_reset(dev, nct6694_wdt_ida_free, data);
+ if (ret)
+ return ret;
+
+ wdev = &data->wdev;
+ wdev->info = &nct6694_wdt_info;
+ wdev->ops = &nct6694_wdt_ops;
+ wdev->timeout = timeout[data->wdev_idx];
+ wdev->pretimeout = pretimeout[data->wdev_idx];
+ if (timeout[data->wdev_idx] < pretimeout[data->wdev_idx]) {
+ dev_warn(data->dev, "pretimeout < timeout. Setting to zero\n");
+ wdev->pretimeout = 0;
+ }
+
+ wdev->min_timeout = 1;
+ wdev->max_timeout = 255;
+
+ platform_set_drvdata(pdev, data);
+
+ watchdog_set_drvdata(&data->wdev, data);
+ watchdog_set_nowayout(&data->wdev, nowayout);
+ watchdog_stop_on_reboot(&data->wdev);
+
+ return devm_watchdog_register_device(dev, &data->wdev);
+}
+
+static struct platform_driver nct6694_wdt_driver = {
+ .driver = {
+ .name = DEVICE_NAME,
+ },
+ .probe = nct6694_wdt_probe,
+};
+
+module_platform_driver(nct6694_wdt_driver);
+
+MODULE_DESCRIPTION("USB-WDT driver for NCT6694");
+MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:nct6694-wdt");
diff --git a/drivers/watchdog/rzg2l_wdt.c b/drivers/watchdog/rzg2l_wdt.c
index 11bbe48160ec..1c9aa366d0a0 100644
--- a/drivers/watchdog/rzg2l_wdt.c
+++ b/drivers/watchdog/rzg2l_wdt.c
@@ -310,9 +310,7 @@ static int rzg2l_wdt_probe(struct platform_device *pdev)
watchdog_set_nowayout(&priv->wdev, nowayout);
watchdog_stop_on_unregister(&priv->wdev);
- ret = watchdog_init_timeout(&priv->wdev, 0, dev);
- if (ret)
- dev_warn(dev, "Specified timeout invalid, using default");
+ watchdog_init_timeout(&priv->wdev, 0, dev);
return devm_watchdog_register_device(&pdev->dev, &priv->wdev);
}
diff --git a/drivers/watchdog/rzv2h_wdt.c b/drivers/watchdog/rzv2h_wdt.c
index 8defd0241213..a694786837e1 100644
--- a/drivers/watchdog/rzv2h_wdt.c
+++ b/drivers/watchdog/rzv2h_wdt.c
@@ -21,11 +21,17 @@
#define WDTSR 0x04 /* WDT Status Register RW, 16 */
#define WDTRCR 0x06 /* WDT Reset Control Register RW, 8 */
+/* This register is only available on RZ/T2H and RZ/N2H SoCs */
+#define WDTDCR 0x00 /* WDT Debug Control Register RW, 32 */
+
#define WDTCR_TOPS_1024 0x00
+#define WDTCR_TOPS_4096 0x01
#define WDTCR_TOPS_16384 0x03
#define WDTCR_CKS_CLK_1 0x00
+#define WDTCR_CKS_CLK_4 0x10
#define WDTCR_CKS_CLK_256 0x50
+#define WDTCR_CKS_CLK_8192 0x80
#define WDTCR_RPES_0 0x300
#define WDTCR_RPES_75 0x000
@@ -35,8 +41,7 @@
#define WDTRCR_RSTIRQS BIT(7)
-#define MAX_TIMEOUT_CYCLES 16384
-#define CLOCK_DIV_BY_256 256
+#define WDTDCR_WDTSTOPCTRL BIT(0)
#define WDT_DEFAULT_TIMEOUT 60U
@@ -45,12 +50,29 @@ module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+enum rzv2h_wdt_count_source {
+ COUNT_SOURCE_LOCO,
+ COUNT_SOURCE_PCLK,
+};
+
+struct rzv2h_of_data {
+ u8 cks_min;
+ u8 cks_max;
+ u16 cks_div;
+ u8 tops;
+ u16 timeout_cycles;
+ enum rzv2h_wdt_count_source count_source;
+ bool wdtdcr;
+};
+
struct rzv2h_wdt_priv {
void __iomem *base;
+ void __iomem *wdtdcr;
struct clk *pclk;
struct clk *oscclk;
struct reset_control *rstc;
struct watchdog_device wdev;
+ const struct rzv2h_of_data *of_data;
};
static int rzv2h_wdt_ping(struct watchdog_device *wdev)
@@ -67,6 +89,20 @@ static int rzv2h_wdt_ping(struct watchdog_device *wdev)
return 0;
}
+static void rzt2h_wdt_wdtdcr_count_stop(struct rzv2h_wdt_priv *priv)
+{
+ u32 reg = readl(priv->wdtdcr + WDTDCR);
+
+ writel(reg | WDTDCR_WDTSTOPCTRL, priv->wdtdcr + WDTDCR);
+}
+
+static void rzt2h_wdt_wdtdcr_count_start(struct rzv2h_wdt_priv *priv)
+{
+ u32 reg = readl(priv->wdtdcr + WDTDCR);
+
+ writel(reg & ~WDTDCR_WDTSTOPCTRL, priv->wdtdcr + WDTDCR);
+}
+
static void rzv2h_wdt_setup(struct watchdog_device *wdev, u16 wdtcr)
{
struct rzv2h_wdt_priv *priv = watchdog_get_drvdata(wdev);
@@ -84,6 +120,7 @@ static void rzv2h_wdt_setup(struct watchdog_device *wdev, u16 wdtcr)
static int rzv2h_wdt_start(struct watchdog_device *wdev)
{
struct rzv2h_wdt_priv *priv = watchdog_get_drvdata(wdev);
+ const struct rzv2h_of_data *of_data = priv->of_data;
int ret;
ret = pm_runtime_resume_and_get(wdev->parent);
@@ -101,13 +138,20 @@ static int rzv2h_wdt_start(struct watchdog_device *wdev)
/*
* WDTCR
- * - CKS[7:4] - Clock Division Ratio Select - 0101b: oscclk/256
+ * - CKS[7:4] - Clock Division Ratio Select
+ * - 0101b: oscclk/256 for RZ/V2H(P)
+ * - 1000b: pclkl/8192 for RZ/T2H
* - RPSS[13:12] - Window Start Position Select - 11b: 100%
* - RPES[9:8] - Window End Position Select - 11b: 0%
- * - TOPS[1:0] - Timeout Period Select - 11b: 16384 cycles (3FFFh)
+ * - TOPS[1:0] - Timeout Period Select
+ * - 11b: 16384 cycles (3FFFh) for RZ/V2H(P)
+ * - 01b: 4096 cycles (0FFFh) for RZ/T2H
*/
- rzv2h_wdt_setup(wdev, WDTCR_CKS_CLK_256 | WDTCR_RPSS_100 |
- WDTCR_RPES_0 | WDTCR_TOPS_16384);
+ rzv2h_wdt_setup(wdev, of_data->cks_max | WDTCR_RPSS_100 |
+ WDTCR_RPES_0 | of_data->tops);
+
+ if (priv->of_data->wdtdcr)
+ rzt2h_wdt_wdtdcr_count_start(priv);
/*
* Down counting starts after writing the sequence 00h -> FFh to the
@@ -127,6 +171,9 @@ static int rzv2h_wdt_stop(struct watchdog_device *wdev)
if (ret)
return ret;
+ if (priv->of_data->wdtdcr)
+ rzt2h_wdt_wdtdcr_count_stop(priv);
+
ret = pm_runtime_put(wdev->parent);
if (ret < 0)
return ret;
@@ -179,14 +226,19 @@ static int rzv2h_wdt_restart(struct watchdog_device *wdev,
/*
* WDTCR
- * - CKS[7:4] - Clock Division Ratio Select - 0000b: oscclk/1
+ * - CKS[7:4] - Clock Division Ratio Select
+ * - 0000b: oscclk/1 for RZ/V2H(P)
+ * - 0100b: pclkl/4 for RZ/T2H
* - RPSS[13:12] - Window Start Position Select - 00b: 25%
* - RPES[9:8] - Window End Position Select - 00b: 75%
* - TOPS[1:0] - Timeout Period Select - 00b: 1024 cycles (03FFh)
*/
- rzv2h_wdt_setup(wdev, WDTCR_CKS_CLK_1 | WDTCR_RPSS_25 |
+ rzv2h_wdt_setup(wdev, priv->of_data->cks_min | WDTCR_RPSS_25 |
WDTCR_RPES_75 | WDTCR_TOPS_1024);
+ if (priv->of_data->wdtdcr)
+ rzt2h_wdt_wdtdcr_count_start(priv);
+
rzv2h_wdt_ping(wdev);
/* wait for underflow to trigger... */
@@ -203,41 +255,83 @@ static const struct watchdog_ops rzv2h_wdt_ops = {
.restart = rzv2h_wdt_restart,
};
+static int rzt2h_wdt_wdtdcr_init(struct platform_device *pdev,
+ struct rzv2h_wdt_priv *priv)
+{
+ int ret;
+
+ priv->wdtdcr = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(priv->wdtdcr))
+ return PTR_ERR(priv->wdtdcr);
+
+ ret = pm_runtime_resume_and_get(&pdev->dev);
+ if (ret)
+ return ret;
+
+ rzt2h_wdt_wdtdcr_count_stop(priv);
+
+ ret = pm_runtime_put(&pdev->dev);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
static int rzv2h_wdt_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rzv2h_wdt_priv *priv;
+ struct clk *count_clk;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
+ priv->of_data = of_device_get_match_data(dev);
+
priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
priv->pclk = devm_clk_get_prepared(dev, "pclk");
if (IS_ERR(priv->pclk))
- return dev_err_probe(dev, PTR_ERR(priv->pclk), "no pclk");
+ return dev_err_probe(dev, PTR_ERR(priv->pclk), "Failed to get pclk\n");
- priv->oscclk = devm_clk_get_prepared(dev, "oscclk");
+ priv->oscclk = devm_clk_get_optional_prepared(dev, "oscclk");
if (IS_ERR(priv->oscclk))
- return dev_err_probe(dev, PTR_ERR(priv->oscclk), "no oscclk");
+ return dev_err_probe(dev, PTR_ERR(priv->oscclk), "Failed to get oscclk\n");
- priv->rstc = devm_reset_control_get_exclusive(dev, NULL);
+ priv->rstc = devm_reset_control_get_optional_exclusive(dev, NULL);
if (IS_ERR(priv->rstc))
return dev_err_probe(dev, PTR_ERR(priv->rstc),
- "failed to get cpg reset");
+ "Failed to get cpg reset\n");
+
+ switch (priv->of_data->count_source) {
+ case COUNT_SOURCE_LOCO:
+ count_clk = priv->oscclk;
+ break;
+ case COUNT_SOURCE_PCLK:
+ count_clk = priv->pclk;
+ break;
+ default:
+ return dev_err_probe(dev, -EINVAL, "Invalid count source\n");
+ }
- priv->wdev.max_hw_heartbeat_ms = (MILLI * MAX_TIMEOUT_CYCLES * CLOCK_DIV_BY_256) /
- clk_get_rate(priv->oscclk);
+ priv->wdev.max_hw_heartbeat_ms = (MILLI * priv->of_data->timeout_cycles *
+ priv->of_data->cks_div) / clk_get_rate(count_clk);
dev_dbg(dev, "max hw timeout of %dms\n", priv->wdev.max_hw_heartbeat_ms);
ret = devm_pm_runtime_enable(dev);
if (ret)
return ret;
+ if (priv->of_data->wdtdcr) {
+ ret = rzt2h_wdt_wdtdcr_init(pdev, priv);
+ if (ret)
+ return dev_err_probe(dev, ret, "WDTDCR init failed\n");
+ }
+
priv->wdev.min_timeout = 1;
priv->wdev.timeout = WDT_DEFAULT_TIMEOUT;
priv->wdev.info = &rzv2h_wdt_ident;
@@ -247,15 +341,33 @@ static int rzv2h_wdt_probe(struct platform_device *pdev)
watchdog_set_nowayout(&priv->wdev, nowayout);
watchdog_stop_on_unregister(&priv->wdev);
- ret = watchdog_init_timeout(&priv->wdev, 0, dev);
- if (ret)
- dev_warn(dev, "Specified timeout invalid, using default");
+ watchdog_init_timeout(&priv->wdev, 0, dev);
return devm_watchdog_register_device(dev, &priv->wdev);
}
+static const struct rzv2h_of_data rzt2h_wdt_of_data = {
+ .cks_min = WDTCR_CKS_CLK_4,
+ .cks_max = WDTCR_CKS_CLK_8192,
+ .cks_div = 8192,
+ .tops = WDTCR_TOPS_4096,
+ .timeout_cycles = 4096,
+ .count_source = COUNT_SOURCE_PCLK,
+ .wdtdcr = true,
+};
+
+static const struct rzv2h_of_data rzv2h_wdt_of_data = {
+ .cks_min = WDTCR_CKS_CLK_1,
+ .cks_max = WDTCR_CKS_CLK_256,
+ .cks_div = 256,
+ .tops = WDTCR_TOPS_16384,
+ .timeout_cycles = 16384,
+ .count_source = COUNT_SOURCE_LOCO,
+};
+
static const struct of_device_id rzv2h_wdt_ids[] = {
- { .compatible = "renesas,r9a09g057-wdt", },
+ { .compatible = "renesas,r9a09g057-wdt", .data = &rzv2h_wdt_of_data },
+ { .compatible = "renesas,r9a09g077-wdt", .data = &rzt2h_wdt_of_data },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, rzv2h_wdt_ids);
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
index 40901bdac426..b774477190b6 100644
--- a/drivers/watchdog/s3c2410_wdt.c
+++ b/drivers/watchdog/s3c2410_wdt.c
@@ -27,13 +27,15 @@
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/delay.h>
+#include <linux/math64.h>
#define S3C2410_WTCON 0x00
#define S3C2410_WTDAT 0x04
#define S3C2410_WTCNT 0x08
#define S3C2410_WTCLRINT 0x0c
-#define S3C2410_WTCNT_MAXCNT 0xffff
+#define S3C2410_WTCNT_MAXCNT_16 0xffff
+#define S3C2410_WTCNT_MAXCNT_32 0xffffffff
#define S3C2410_WTCON_RSTEN BIT(0)
#define S3C2410_WTCON_INTEN BIT(2)
@@ -123,6 +125,10 @@
* %QUIRK_HAS_DBGACK_BIT: WTCON register has DBGACK_MASK bit. Setting the
* DBGACK_MASK bit disables the watchdog outputs when the SoC is in debug mode.
* Debug mode is determined by the DBGACK CPU signal.
+ *
+ * %QUIRK_HAS_32BIT_CNT: WTDAT and WTCNT are 32-bit registers. With these
+ * 32-bit registers, larger values will be set, which means that larger timeouts
+ * value can be set.
*/
#define QUIRK_HAS_WTCLRINT_REG BIT(0)
#define QUIRK_HAS_PMU_MASK_RESET BIT(1)
@@ -130,6 +136,7 @@
#define QUIRK_HAS_PMU_AUTO_DISABLE BIT(3)
#define QUIRK_HAS_PMU_CNT_EN BIT(4)
#define QUIRK_HAS_DBGACK_BIT BIT(5)
+#define QUIRK_HAS_32BIT_CNT BIT(6)
/* These quirks require that we have a PMU register map */
#define QUIRKS_HAVE_PMUREG \
@@ -198,6 +205,7 @@ struct s3c2410_wdt {
struct notifier_block freq_transition;
const struct s3c2410_wdt_variant *drv_data;
struct regmap *pmureg;
+ u32 max_cnt;
};
static const struct s3c2410_wdt_variant drv_data_s3c2410 = {
@@ -298,7 +306,8 @@ static const struct s3c2410_wdt_variant drv_data_exynosautov9_cl0 = {
.cnt_en_reg = EXYNOS850_CLUSTER0_NONCPU_OUT,
.cnt_en_bit = 7,
.quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET |
- QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN,
+ QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN |
+ QUIRK_HAS_DBGACK_BIT | QUIRK_HAS_32BIT_CNT,
};
static const struct s3c2410_wdt_variant drv_data_exynosautov9_cl1 = {
@@ -310,7 +319,8 @@ static const struct s3c2410_wdt_variant drv_data_exynosautov9_cl1 = {
.cnt_en_reg = EXYNOSAUTOV9_CLUSTER1_NONCPU_OUT,
.cnt_en_bit = 7,
.quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET |
- QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN,
+ QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN |
+ QUIRK_HAS_DBGACK_BIT | QUIRK_HAS_32BIT_CNT,
};
static const struct s3c2410_wdt_variant drv_data_gs101_cl0 = {
@@ -349,7 +359,7 @@ static const struct s3c2410_wdt_variant drv_data_exynosautov920_cl0 = {
.cnt_en_bit = 8,
.quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET |
QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN |
- QUIRK_HAS_DBGACK_BIT,
+ QUIRK_HAS_DBGACK_BIT | QUIRK_HAS_32BIT_CNT,
};
static const struct s3c2410_wdt_variant drv_data_exynosautov920_cl1 = {
@@ -362,7 +372,7 @@ static const struct s3c2410_wdt_variant drv_data_exynosautov920_cl1 = {
.cnt_en_bit = 8,
.quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET |
QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN |
- QUIRK_HAS_DBGACK_BIT,
+ QUIRK_HAS_DBGACK_BIT | QUIRK_HAS_32BIT_CNT,
};
static const struct of_device_id s3c2410_wdt_match[] = {
@@ -410,9 +420,14 @@ static inline unsigned long s3c2410wdt_get_freq(struct s3c2410_wdt *wdt)
static inline unsigned int s3c2410wdt_max_timeout(struct s3c2410_wdt *wdt)
{
const unsigned long freq = s3c2410wdt_get_freq(wdt);
+ const u64 n_max = (u64)(S3C2410_WTCON_PRESCALE_MAX + 1) *
+ S3C2410_WTCON_MAXDIV * wdt->max_cnt;
+ u64 t_max = div64_ul(n_max, freq);
- return S3C2410_WTCNT_MAXCNT / (freq / (S3C2410_WTCON_PRESCALE_MAX + 1)
- / S3C2410_WTCON_MAXDIV);
+ if (t_max > UINT_MAX)
+ t_max = UINT_MAX;
+
+ return t_max;
}
static int s3c2410wdt_disable_wdt_reset(struct s3c2410_wdt *wdt, bool mask)
@@ -566,7 +581,7 @@ static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd,
{
struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
unsigned long freq = s3c2410wdt_get_freq(wdt);
- unsigned int count;
+ unsigned long count;
unsigned int divisor = 1;
unsigned long wtcon;
@@ -576,7 +591,7 @@ static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd,
freq = DIV_ROUND_UP(freq, 128);
count = timeout * freq;
- dev_dbg(wdt->dev, "Heartbeat: count=%d, timeout=%d, freq=%lu\n",
+ dev_dbg(wdt->dev, "Heartbeat: count=%lu, timeout=%d, freq=%lu\n",
count, timeout, freq);
/* if the count is bigger than the watchdog register,
@@ -584,16 +599,16 @@ static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd,
actually make this value
*/
- if (count >= 0x10000) {
- divisor = DIV_ROUND_UP(count, 0xffff);
+ if (count > wdt->max_cnt) {
+ divisor = DIV_ROUND_UP(count, wdt->max_cnt);
- if (divisor > 0x100) {
+ if (divisor > S3C2410_WTCON_PRESCALE_MAX + 1) {
dev_err(wdt->dev, "timeout %d too big\n", timeout);
return -EINVAL;
}
}
- dev_dbg(wdt->dev, "Heartbeat: timeout=%d, divisor=%d, count=%d (%08x)\n",
+ dev_dbg(wdt->dev, "Heartbeat: timeout=%d, divisor=%d, count=%lu (%08lx)\n",
timeout, divisor, count, DIV_ROUND_UP(count, divisor));
count = DIV_ROUND_UP(count, divisor);
@@ -801,6 +816,11 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
if (IS_ERR(wdt->src_clk))
return dev_err_probe(dev, PTR_ERR(wdt->src_clk), "failed to get source clock\n");
+ if (wdt->drv_data->quirks & QUIRK_HAS_32BIT_CNT)
+ wdt->max_cnt = S3C2410_WTCNT_MAXCNT_32;
+ else
+ wdt->max_cnt = S3C2410_WTCNT_MAXCNT_16;
+
wdt->wdt_device.min_timeout = 1;
wdt->wdt_device.max_timeout = s3c2410wdt_max_timeout(wdt);
diff --git a/drivers/watchdog/visconti_wdt.c b/drivers/watchdog/visconti_wdt.c
index cef0794708e7..7795e7fbf67e 100644
--- a/drivers/watchdog/visconti_wdt.c
+++ b/drivers/watchdog/visconti_wdt.c
@@ -118,7 +118,6 @@ static int visconti_wdt_probe(struct platform_device *pdev)
struct visconti_wdt_priv *priv;
struct device *dev = &pdev->dev;
struct clk *clk;
- int ret;
unsigned long clk_freq;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@@ -153,9 +152,7 @@ static int visconti_wdt_probe(struct platform_device *pdev)
watchdog_stop_on_unregister(wdev);
/* This overrides the default timeout only if DT configuration was found */
- ret = watchdog_init_timeout(wdev, 0, dev);
- if (ret)
- dev_warn(dev, "Specified timeout value invalid, using default\n");
+ watchdog_init_timeout(wdev, 0, dev);
return devm_watchdog_register_device(dev, wdev);
}