brintos

brintos / linux-shallow public Read only

0
0
Text · 7.1 KiB · 33835c0 Raw
265 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Maxim MAX77620 Watchdog Driver4 *5 * Copyright (C) 2016 NVIDIA CORPORATION. All rights reserved.6 * Copyright (C) 2022 Luca Ceresoli7 *8 * Author: Laxman Dewangan <ldewangan@nvidia.com>9 * Author: Luca Ceresoli <luca.ceresoli@bootlin.com>10 */11 12#include <linux/err.h>13#include <linux/init.h>14#include <linux/kernel.h>15#include <linux/module.h>16#include <linux/mod_devicetable.h>17#include <linux/mfd/max77620.h>18#include <linux/mfd/max77714.h>19#include <linux/platform_device.h>20#include <linux/regmap.h>21#include <linux/slab.h>22#include <linux/watchdog.h>23 24static bool nowayout = WATCHDOG_NOWAYOUT;25 26/**27 * struct max77620_variant - Data specific to a chip variant28 * @wdt_info:            watchdog descriptor29 * @reg_onoff_cnfg2:     ONOFF_CNFG2 register offset30 * @reg_cnfg_glbl2:      CNFG_GLBL2 register offset31 * @reg_cnfg_glbl3:      CNFG_GLBL3 register offset32 * @wdtc_mask:           WDTC bit mask in CNFG_GLBL3 (=bits to update to ping the watchdog)33 * @bit_wd_rst_wk:       WD_RST_WK bit offset within ONOFF_CNFG234 * @cnfg_glbl2_cfg_bits: configuration bits to enable in CNFG_GLBL2 register35 */36struct max77620_variant {37	u8 reg_onoff_cnfg2;38	u8 reg_cnfg_glbl2;39	u8 reg_cnfg_glbl3;40	u8 wdtc_mask;41	u8 bit_wd_rst_wk;42	u8 cnfg_glbl2_cfg_bits;43};44 45struct max77620_wdt {46	struct device			*dev;47	struct regmap			*rmap;48	const struct max77620_variant	*drv_data;49	struct watchdog_device		wdt_dev;50};51 52static const struct max77620_variant max77620_wdt_data = {53	.reg_onoff_cnfg2     = MAX77620_REG_ONOFFCNFG2,54	.reg_cnfg_glbl2      = MAX77620_REG_CNFGGLBL2,55	.reg_cnfg_glbl3      = MAX77620_REG_CNFGGLBL3,56	.wdtc_mask           = MAX77620_WDTC_MASK,57	.bit_wd_rst_wk       = MAX77620_ONOFFCNFG2_WD_RST_WK,58	/* Set WDT clear in OFF and sleep mode */59	.cnfg_glbl2_cfg_bits = MAX77620_WDTSLPC | MAX77620_WDTOFFC,60};61 62static const struct max77620_variant max77714_wdt_data = {63	.reg_onoff_cnfg2     = MAX77714_CNFG2_ONOFF,64	.reg_cnfg_glbl2      = MAX77714_CNFG_GLBL2,65	.reg_cnfg_glbl3      = MAX77714_CNFG_GLBL3,66	.wdtc_mask           = MAX77714_WDTC,67	.bit_wd_rst_wk       = MAX77714_WD_RST_WK,68	/* Set WDT clear in sleep mode (there is no WDTOFFC on MAX77714) */69	.cnfg_glbl2_cfg_bits = MAX77714_WDTSLPC,70};71 72static int max77620_wdt_start(struct watchdog_device *wdt_dev)73{74	struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev);75 76	return regmap_update_bits(wdt->rmap, wdt->drv_data->reg_cnfg_glbl2,77				  MAX77620_WDTEN, MAX77620_WDTEN);78}79 80static int max77620_wdt_stop(struct watchdog_device *wdt_dev)81{82	struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev);83 84	return regmap_update_bits(wdt->rmap, wdt->drv_data->reg_cnfg_glbl2,85				  MAX77620_WDTEN, 0);86}87 88static int max77620_wdt_ping(struct watchdog_device *wdt_dev)89{90	struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev);91 92	return regmap_update_bits(wdt->rmap, wdt->drv_data->reg_cnfg_glbl3,93				  wdt->drv_data->wdtc_mask, 0x1);94}95 96static int max77620_wdt_set_timeout(struct watchdog_device *wdt_dev,97				    unsigned int timeout)98{99	struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev);100	unsigned int wdt_timeout;101	u8 regval;102	int ret;103 104	switch (timeout) {105	case 0 ... 2:106		regval = MAX77620_TWD_2s;107		wdt_timeout = 2;108		break;109 110	case 3 ... 16:111		regval = MAX77620_TWD_16s;112		wdt_timeout = 16;113		break;114 115	case 17 ... 64:116		regval = MAX77620_TWD_64s;117		wdt_timeout = 64;118		break;119 120	default:121		regval = MAX77620_TWD_128s;122		wdt_timeout = 128;123		break;124	}125 126	/*127	 * "If the value of TWD needs to be changed, clear the system128	 * watchdog timer first [...], then change the value of TWD."129	 * (MAX77714 datasheet but applies to MAX77620 too)130	 */131	ret = regmap_update_bits(wdt->rmap, wdt->drv_data->reg_cnfg_glbl3,132				 wdt->drv_data->wdtc_mask, 0x1);133	if (ret < 0)134		return ret;135 136	ret = regmap_update_bits(wdt->rmap, wdt->drv_data->reg_cnfg_glbl2,137				 MAX77620_TWD_MASK, regval);138	if (ret < 0)139		return ret;140 141	wdt_dev->timeout = wdt_timeout;142 143	return 0;144}145 146static const struct watchdog_info max77620_wdt_info = {147	.identity = "max77620-watchdog",148	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,149};150 151static const struct watchdog_ops max77620_wdt_ops = {152	.start		= max77620_wdt_start,153	.stop		= max77620_wdt_stop,154	.ping		= max77620_wdt_ping,155	.set_timeout	= max77620_wdt_set_timeout,156};157 158static int max77620_wdt_probe(struct platform_device *pdev)159{160	const struct platform_device_id *id = platform_get_device_id(pdev);161	struct device *dev = &pdev->dev;162	struct max77620_wdt *wdt;163	struct watchdog_device *wdt_dev;164	unsigned int regval;165	int ret;166 167	wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);168	if (!wdt)169		return -ENOMEM;170 171	wdt->dev = dev;172	wdt->drv_data = (const struct max77620_variant *) id->driver_data;173 174	wdt->rmap = dev_get_regmap(dev->parent, NULL);175	if (!wdt->rmap) {176		dev_err(wdt->dev, "Failed to get parent regmap\n");177		return -ENODEV;178	}179 180	wdt_dev = &wdt->wdt_dev;181	wdt_dev->info = &max77620_wdt_info;182	wdt_dev->ops = &max77620_wdt_ops;183	wdt_dev->min_timeout = 2;184	wdt_dev->max_timeout = 128;185	wdt_dev->max_hw_heartbeat_ms = 128 * 1000;186 187	platform_set_drvdata(pdev, wdt);188 189	/* Enable WD_RST_WK - WDT expire results in a restart */190	ret = regmap_update_bits(wdt->rmap, wdt->drv_data->reg_onoff_cnfg2,191				 wdt->drv_data->bit_wd_rst_wk,192				 wdt->drv_data->bit_wd_rst_wk);193	if (ret < 0) {194		dev_err(wdt->dev, "Failed to set WD_RST_WK: %d\n", ret);195		return ret;196	}197 198	/* Set the "auto WDT clear" bits available on the chip */199	ret = regmap_update_bits(wdt->rmap, wdt->drv_data->reg_cnfg_glbl2,200				 wdt->drv_data->cnfg_glbl2_cfg_bits,201				 wdt->drv_data->cnfg_glbl2_cfg_bits);202	if (ret < 0) {203		dev_err(wdt->dev, "Failed to set WDT OFF mode: %d\n", ret);204		return ret;205	}206 207	/* Check if WDT running and if yes then set flags properly */208	ret = regmap_read(wdt->rmap, wdt->drv_data->reg_cnfg_glbl2, &regval);209	if (ret < 0) {210		dev_err(wdt->dev, "Failed to read WDT CFG register: %d\n", ret);211		return ret;212	}213 214	switch (regval & MAX77620_TWD_MASK) {215	case MAX77620_TWD_2s:216		wdt_dev->timeout = 2;217		break;218	case MAX77620_TWD_16s:219		wdt_dev->timeout = 16;220		break;221	case MAX77620_TWD_64s:222		wdt_dev->timeout = 64;223		break;224	default:225		wdt_dev->timeout = 128;226		break;227	}228 229	if (regval & MAX77620_WDTEN)230		set_bit(WDOG_HW_RUNNING, &wdt_dev->status);231 232	watchdog_set_nowayout(wdt_dev, nowayout);233	watchdog_set_drvdata(wdt_dev, wdt);234 235	watchdog_stop_on_unregister(wdt_dev);236	return devm_watchdog_register_device(dev, wdt_dev);237}238 239static const struct platform_device_id max77620_wdt_devtype[] = {240	{ "max77620-watchdog", (kernel_ulong_t)&max77620_wdt_data },241	{ "max77714-watchdog", (kernel_ulong_t)&max77714_wdt_data },242	{ },243};244MODULE_DEVICE_TABLE(platform, max77620_wdt_devtype);245 246static struct platform_driver max77620_wdt_driver = {247	.driver	= {248		.name	= "max77620-watchdog",249	},250	.probe	= max77620_wdt_probe,251	.id_table = max77620_wdt_devtype,252};253 254module_platform_driver(max77620_wdt_driver);255 256MODULE_DESCRIPTION("Max77620 watchdog timer driver");257 258module_param(nowayout, bool, 0);259MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "260	"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");261 262MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");263MODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>");264MODULE_LICENSE("GPL v2");265