aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/drivers/watchdog
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/watchdog')
-rw-r--r--drivers/watchdog/Kconfig4
-rw-r--r--drivers/watchdog/max77620_wdt.c85
2 files changed, 71 insertions, 18 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index ee9c57a5fafa..c4e82a8d863f 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -678,7 +678,7 @@ config MAX63XX_WATCHDOG
config MAX77620_WATCHDOG
tristate "Maxim Max77620 Watchdog Timer"
- depends on MFD_MAX77620 || COMPILE_TEST
+ depends on MFD_MAX77620 || MFD_MAX77714 || COMPILE_TEST
select WATCHDOG_CORE
help
This is the driver for the Max77620 watchdog timer.
@@ -1718,7 +1718,7 @@ config AR7_WDT
config TXX9_WDT
tristate "Toshiba TXx9 Watchdog Timer"
- depends on CPU_TX39XX || CPU_TX49XX || (MIPS && COMPILE_TEST)
+ depends on CPU_TX49XX || (MIPS && COMPILE_TEST)
select WATCHDOG_CORE
help
Hardware driver for the built-in watchdog timer on TXx9 MIPS SoCs.
diff --git a/drivers/watchdog/max77620_wdt.c b/drivers/watchdog/max77620_wdt.c
index be6a53c30002..b76ad6ba0915 100644
--- a/drivers/watchdog/max77620_wdt.c
+++ b/drivers/watchdog/max77620_wdt.c
@@ -3,8 +3,10 @@
* Maxim MAX77620 Watchdog Driver
*
* Copyright (C) 2016 NVIDIA CORPORATION. All rights reserved.
+ * Copyright (C) 2022 Luca Ceresoli
*
* Author: Laxman Dewangan <ldewangan@nvidia.com>
+ * Author: Luca Ceresoli <luca@lucaceresoli.net>
*/
#include <linux/err.h>
@@ -13,6 +15,7 @@
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/mfd/max77620.h>
+#include <linux/mfd/max77714.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -20,17 +23,57 @@
static bool nowayout = WATCHDOG_NOWAYOUT;
+/**
+ * struct max77620_variant - Data specific to a chip variant
+ * @wdt_info: watchdog descriptor
+ * @reg_onoff_cnfg2: ONOFF_CNFG2 register offset
+ * @reg_cnfg_glbl2: CNFG_GLBL2 register offset
+ * @reg_cnfg_glbl3: CNFG_GLBL3 register offset
+ * @wdtc_mask: WDTC bit mask in CNFG_GLBL3 (=bits to update to ping the watchdog)
+ * @bit_wd_rst_wk: WD_RST_WK bit offset within ONOFF_CNFG2
+ * @cnfg_glbl2_cfg_bits: configuration bits to enable in CNFG_GLBL2 register
+ */
+struct max77620_variant {
+ u8 reg_onoff_cnfg2;
+ u8 reg_cnfg_glbl2;
+ u8 reg_cnfg_glbl3;
+ u8 wdtc_mask;
+ u8 bit_wd_rst_wk;
+ u8 cnfg_glbl2_cfg_bits;
+};
+
struct max77620_wdt {
struct device *dev;
struct regmap *rmap;
+ const struct max77620_variant *drv_data;
struct watchdog_device wdt_dev;
};
+static const struct max77620_variant max77620_wdt_data = {
+ .reg_onoff_cnfg2 = MAX77620_REG_ONOFFCNFG2,
+ .reg_cnfg_glbl2 = MAX77620_REG_CNFGGLBL2,
+ .reg_cnfg_glbl3 = MAX77620_REG_CNFGGLBL3,
+ .wdtc_mask = MAX77620_WDTC_MASK,
+ .bit_wd_rst_wk = MAX77620_ONOFFCNFG2_WD_RST_WK,
+ /* Set WDT clear in OFF and sleep mode */
+ .cnfg_glbl2_cfg_bits = MAX77620_WDTSLPC | MAX77620_WDTOFFC,
+};
+
+static const struct max77620_variant max77714_wdt_data = {
+ .reg_onoff_cnfg2 = MAX77714_CNFG2_ONOFF,
+ .reg_cnfg_glbl2 = MAX77714_CNFG_GLBL2,
+ .reg_cnfg_glbl3 = MAX77714_CNFG_GLBL3,
+ .wdtc_mask = MAX77714_WDTC,
+ .bit_wd_rst_wk = MAX77714_WD_RST_WK,
+ /* Set WDT clear in sleep mode (there is no WDTOFFC on MAX77714) */
+ .cnfg_glbl2_cfg_bits = MAX77714_WDTSLPC,
+};
+
static int max77620_wdt_start(struct watchdog_device *wdt_dev)
{
struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev);
- return regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL2,
+ return regmap_update_bits(wdt->rmap, wdt->drv_data->reg_cnfg_glbl2,
MAX77620_WDTEN, MAX77620_WDTEN);
}
@@ -38,7 +81,7 @@ static int max77620_wdt_stop(struct watchdog_device *wdt_dev)
{
struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev);
- return regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL2,
+ return regmap_update_bits(wdt->rmap, wdt->drv_data->reg_cnfg_glbl2,
MAX77620_WDTEN, 0);
}
@@ -46,8 +89,8 @@ static int max77620_wdt_ping(struct watchdog_device *wdt_dev)
{
struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev);
- return regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL3,
- MAX77620_WDTC_MASK, 0x1);
+ return regmap_update_bits(wdt->rmap, wdt->drv_data->reg_cnfg_glbl3,
+ wdt->drv_data->wdtc_mask, 0x1);
}
static int max77620_wdt_set_timeout(struct watchdog_device *wdt_dev,
@@ -80,12 +123,17 @@ static int max77620_wdt_set_timeout(struct watchdog_device *wdt_dev,
break;
}
- ret = regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL3,
- MAX77620_WDTC_MASK, 0x1);
+ /*
+ * "If the value of TWD needs to be changed, clear the system
+ * watchdog timer first [...], then change the value of TWD."
+ * (MAX77714 datasheet but applies to MAX77620 too)
+ */
+ ret = regmap_update_bits(wdt->rmap, wdt->drv_data->reg_cnfg_glbl3,
+ wdt->drv_data->wdtc_mask, 0x1);
if (ret < 0)
return ret;
- ret = regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL2,
+ ret = regmap_update_bits(wdt->rmap, wdt->drv_data->reg_cnfg_glbl2,
MAX77620_TWD_MASK, regval);
if (ret < 0)
return ret;
@@ -109,6 +157,7 @@ static const struct watchdog_ops max77620_wdt_ops = {
static int max77620_wdt_probe(struct platform_device *pdev)
{
+ const struct platform_device_id *id = platform_get_device_id(pdev);
struct device *dev = &pdev->dev;
struct max77620_wdt *wdt;
struct watchdog_device *wdt_dev;
@@ -120,6 +169,8 @@ static int max77620_wdt_probe(struct platform_device *pdev)
return -ENOMEM;
wdt->dev = dev;
+ wdt->drv_data = (const struct max77620_variant *) id->driver_data;
+
wdt->rmap = dev_get_regmap(dev->parent, NULL);
if (!wdt->rmap) {
dev_err(wdt->dev, "Failed to get parent regmap\n");
@@ -136,25 +187,25 @@ static int max77620_wdt_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, wdt);
/* Enable WD_RST_WK - WDT expire results in a restart */
- ret = regmap_update_bits(wdt->rmap, MAX77620_REG_ONOFFCNFG2,
- MAX77620_ONOFFCNFG2_WD_RST_WK,
- MAX77620_ONOFFCNFG2_WD_RST_WK);
+ ret = regmap_update_bits(wdt->rmap, wdt->drv_data->reg_onoff_cnfg2,
+ wdt->drv_data->bit_wd_rst_wk,
+ wdt->drv_data->bit_wd_rst_wk);
if (ret < 0) {
dev_err(wdt->dev, "Failed to set WD_RST_WK: %d\n", ret);
return ret;
}
- /* Set WDT clear in OFF and sleep mode */
- ret = regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL2,
- MAX77620_WDTOFFC | MAX77620_WDTSLPC,
- MAX77620_WDTOFFC | MAX77620_WDTSLPC);
+ /* Set the "auto WDT clear" bits available on the chip */
+ ret = regmap_update_bits(wdt->rmap, wdt->drv_data->reg_cnfg_glbl2,
+ wdt->drv_data->cnfg_glbl2_cfg_bits,
+ wdt->drv_data->cnfg_glbl2_cfg_bits);
if (ret < 0) {
dev_err(wdt->dev, "Failed to set WDT OFF mode: %d\n", ret);
return ret;
}
/* Check if WDT running and if yes then set flags properly */
- ret = regmap_read(wdt->rmap, MAX77620_REG_CNFGGLBL2, &regval);
+ ret = regmap_read(wdt->rmap, wdt->drv_data->reg_cnfg_glbl2, &regval);
if (ret < 0) {
dev_err(wdt->dev, "Failed to read WDT CFG register: %d\n", ret);
return ret;
@@ -186,7 +237,8 @@ static int max77620_wdt_probe(struct platform_device *pdev)
}
static const struct platform_device_id max77620_wdt_devtype[] = {
- { .name = "max77620-watchdog", },
+ { "max77620-watchdog", (kernel_ulong_t)&max77620_wdt_data },
+ { "max77714-watchdog", (kernel_ulong_t)&max77714_wdt_data },
{ },
};
MODULE_DEVICE_TABLE(platform, max77620_wdt_devtype);
@@ -208,4 +260,5 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_AUTHOR("Luca Ceresoli <luca@lucaceresoli.net>");
MODULE_LICENSE("GPL v2");