Commit 254099d8 authored by Luca Ceresoli's avatar Luca Ceresoli Committed by Lee Jones

watchdog: max77620: Add support for the max77714 variant

The MAX77714 is a MFD chip whose watchdog has the same programming
procedures as the MAX77620 watchdog, but most register offsets and bit
masks are different, as well as some features.

Support the MAX77714 watchdog by adding a variant description table holding
the differences.

All the features implemented by this driver are available on the MAX77714
except for the lack of a WDTOFFC bit. Instead of using a "HAS_*" flag we
handle this by holding in the cnfg_glbl2_cfg_bits struct field the bits
(i.e. the features) to enable in the CNFG_GLBL2 register. These bits differ
among the two models. This implementation allows to avoid any conditional
code, keeping the execution flow unchanged.
Signed-off-by: default avatarLuca Ceresoli <luca@lucaceresoli.net>
Reviewed-by: default avatarGuenter Roeck <linux@roeck-us.net>
Signed-off-by: default avatarLee Jones <lee.jones@linaro.org>
parent 60b050ff
......@@ -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.
......
......@@ -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,12 @@ 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);
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 +152,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 +164,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 +182,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 +232,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 +255,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");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment