Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * Watchdog driver for DA9063 PMICs.
  4 *
  5 * Copyright(c) 2012 Dialog Semiconductor Ltd.
  6 *
  7 * Author: Mariusz Wojtasik <mariusz.wojtasik@diasemi.com>
  8 *
  9 */
 10
 11#include <linux/kernel.h>
 12#include <linux/module.h>
 13#include <linux/watchdog.h>
 14#include <linux/platform_device.h>
 15#include <linux/uaccess.h>
 16#include <linux/slab.h>
 17#include <linux/delay.h>
 18#include <linux/mfd/da9063/registers.h>
 19#include <linux/mfd/da9063/core.h>
 20#include <linux/regmap.h>
 21
 22/*
 23 * Watchdog selector to timeout in seconds.
 24 *   0: WDT disabled;
 25 *   others: timeout = 2048 ms * 2^(TWDSCALE-1).
 26 */
 27static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 };
 28#define DA9063_TWDSCALE_DISABLE		0
 29#define DA9063_TWDSCALE_MIN		1
 30#define DA9063_TWDSCALE_MAX		(ARRAY_SIZE(wdt_timeout) - 1)
 31#define DA9063_WDT_MIN_TIMEOUT		wdt_timeout[DA9063_TWDSCALE_MIN]
 32#define DA9063_WDT_MAX_TIMEOUT		wdt_timeout[DA9063_TWDSCALE_MAX]
 33#define DA9063_WDG_TIMEOUT		wdt_timeout[3]
 34#define DA9063_RESET_PROTECTION_MS	256
 35
 36static unsigned int da9063_wdt_timeout_to_sel(unsigned int secs)
 37{
 38	unsigned int i;
 39
 40	for (i = DA9063_TWDSCALE_MIN; i <= DA9063_TWDSCALE_MAX; i++) {
 41		if (wdt_timeout[i] >= secs)
 42			return i;
 43	}
 44
 45	return DA9063_TWDSCALE_MAX;
 46}
 47
 48static int _da9063_wdt_set_timeout(struct da9063 *da9063, unsigned int regval)
 49{
 50	return regmap_update_bits(da9063->regmap, DA9063_REG_CONTROL_D,
 51				  DA9063_TWDSCALE_MASK, regval);
 52}
 53
 54static int da9063_wdt_start(struct watchdog_device *wdd)
 55{
 56	struct da9063 *da9063 = watchdog_get_drvdata(wdd);
 57	unsigned int selector;
 58	int ret;
 59
 60	selector = da9063_wdt_timeout_to_sel(wdd->timeout);
 61	ret = _da9063_wdt_set_timeout(da9063, selector);
 62	if (ret)
 63		dev_err(da9063->dev, "Watchdog failed to start (err = %d)\n",
 64			ret);
 65
 66	return ret;
 67}
 68
 69static int da9063_wdt_stop(struct watchdog_device *wdd)
 70{
 71	struct da9063 *da9063 = watchdog_get_drvdata(wdd);
 72	int ret;
 73
 74	ret = regmap_update_bits(da9063->regmap, DA9063_REG_CONTROL_D,
 75				 DA9063_TWDSCALE_MASK, DA9063_TWDSCALE_DISABLE);
 76	if (ret)
 77		dev_alert(da9063->dev, "Watchdog failed to stop (err = %d)\n",
 78			  ret);
 79
 80	return ret;
 81}
 82
 83static int da9063_wdt_ping(struct watchdog_device *wdd)
 84{
 85	struct da9063 *da9063 = watchdog_get_drvdata(wdd);
 86	int ret;
 87
 88	ret = regmap_write(da9063->regmap, DA9063_REG_CONTROL_F,
 89			   DA9063_WATCHDOG);
 90	if (ret)
 91		dev_alert(da9063->dev, "Failed to ping the watchdog (err = %d)\n",
 92			  ret);
 93
 94	return ret;
 95}
 96
 97static int da9063_wdt_set_timeout(struct watchdog_device *wdd,
 98				  unsigned int timeout)
 99{
100	struct da9063 *da9063 = watchdog_get_drvdata(wdd);
101	unsigned int selector;
102	int ret;
103
104	selector = da9063_wdt_timeout_to_sel(timeout);
105	ret = _da9063_wdt_set_timeout(da9063, selector);
106	if (ret)
107		dev_err(da9063->dev, "Failed to set watchdog timeout (err = %d)\n",
108			ret);
109	else
110		wdd->timeout = wdt_timeout[selector];
111
112	return ret;
113}
114
115static int da9063_wdt_restart(struct watchdog_device *wdd, unsigned long action,
116			      void *data)
117{
118	struct da9063 *da9063 = watchdog_get_drvdata(wdd);
119	int ret;
120
121	ret = regmap_write(da9063->regmap, DA9063_REG_CONTROL_F,
122			   DA9063_SHUTDOWN);
123	if (ret)
124		dev_alert(da9063->dev, "Failed to shutdown (err = %d)\n",
125			  ret);
126
127	return ret;
128}
129
130static const struct watchdog_info da9063_watchdog_info = {
131	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
132	.identity = "DA9063 Watchdog",
133};
134
135static const struct watchdog_ops da9063_watchdog_ops = {
136	.owner = THIS_MODULE,
137	.start = da9063_wdt_start,
138	.stop = da9063_wdt_stop,
139	.ping = da9063_wdt_ping,
140	.set_timeout = da9063_wdt_set_timeout,
141	.restart = da9063_wdt_restart,
142};
143
144static int da9063_wdt_probe(struct platform_device *pdev)
145{
146	struct da9063 *da9063;
147	struct watchdog_device *wdd;
148
149	if (!pdev->dev.parent)
150		return -EINVAL;
151
152	da9063 = dev_get_drvdata(pdev->dev.parent);
153	if (!da9063)
154		return -EINVAL;
155
156	wdd = devm_kzalloc(&pdev->dev, sizeof(*wdd), GFP_KERNEL);
157	if (!wdd)
158		return -ENOMEM;
159
160	wdd->info = &da9063_watchdog_info;
161	wdd->ops = &da9063_watchdog_ops;
162	wdd->min_timeout = DA9063_WDT_MIN_TIMEOUT;
163	wdd->max_timeout = DA9063_WDT_MAX_TIMEOUT;
164	wdd->min_hw_heartbeat_ms = DA9063_RESET_PROTECTION_MS;
165	wdd->timeout = DA9063_WDG_TIMEOUT;
166	wdd->parent = &pdev->dev;
167
168	wdd->status = WATCHDOG_NOWAYOUT_INIT_STATUS;
169
170	watchdog_set_restart_priority(wdd, 128);
171
172	watchdog_set_drvdata(wdd, da9063);
173
174	return devm_watchdog_register_device(&pdev->dev, wdd);
175}
176
177static struct platform_driver da9063_wdt_driver = {
178	.probe = da9063_wdt_probe,
179	.driver = {
180		.name = DA9063_DRVNAME_WATCHDOG,
181	},
182};
183module_platform_driver(da9063_wdt_driver);
184
185MODULE_AUTHOR("Mariusz Wojtasik <mariusz.wojtasik@diasemi.com>");
186MODULE_DESCRIPTION("Watchdog driver for Dialog DA9063");
187MODULE_LICENSE("GPL");
188MODULE_ALIAS("platform:" DA9063_DRVNAME_WATCHDOG);