Linux Audio

Check our new training course

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