Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 * Maxim MAX77620 Watchdog Driver
  3 *
  4 * Copyright (C) 2016 NVIDIA CORPORATION. All rights reserved.
  5 *
  6 * Author: Laxman Dewangan <ldewangan@nvidia.com>
  7 *
  8 * This program is free software; you can redistribute it and/or modify
  9 * it under the terms of the GNU General Public License version 2 as
 10 * published by the Free Software Foundation.
 11 */
 12
 13#include <linux/err.h>
 14#include <linux/init.h>
 15#include <linux/kernel.h>
 16#include <linux/module.h>
 17#include <linux/mfd/max77620.h>
 18#include <linux/platform_device.h>
 19#include <linux/regmap.h>
 20#include <linux/slab.h>
 21#include <linux/watchdog.h>
 22
 23static bool nowayout = WATCHDOG_NOWAYOUT;
 24
 25struct max77620_wdt {
 26	struct device			*dev;
 27	struct regmap			*rmap;
 28	struct watchdog_device		wdt_dev;
 29};
 30
 31static int max77620_wdt_start(struct watchdog_device *wdt_dev)
 32{
 33	struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev);
 34
 35	return regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL2,
 36				  MAX77620_WDTEN, MAX77620_WDTEN);
 37}
 38
 39static int max77620_wdt_stop(struct watchdog_device *wdt_dev)
 40{
 41	struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev);
 42
 43	return regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL2,
 44				  MAX77620_WDTEN, 0);
 45}
 46
 47static int max77620_wdt_ping(struct watchdog_device *wdt_dev)
 48{
 49	struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev);
 50
 51	return regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL3,
 52				  MAX77620_WDTC_MASK, 0x1);
 53}
 54
 55static int max77620_wdt_set_timeout(struct watchdog_device *wdt_dev,
 56				    unsigned int timeout)
 57{
 58	struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev);
 59	unsigned int wdt_timeout;
 60	u8 regval;
 61	int ret;
 62
 63	switch (timeout) {
 64	case 0 ... 2:
 65		regval = MAX77620_TWD_2s;
 66		wdt_timeout = 2;
 67		break;
 68
 69	case 3 ... 16:
 70		regval = MAX77620_TWD_16s;
 71		wdt_timeout = 16;
 72		break;
 73
 74	case 17 ... 64:
 75		regval = MAX77620_TWD_64s;
 76		wdt_timeout = 64;
 77		break;
 78
 79	default:
 80		regval = MAX77620_TWD_128s;
 81		wdt_timeout = 128;
 82		break;
 83	}
 84
 85	ret = regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL3,
 86				 MAX77620_WDTC_MASK, 0x1);
 87	if (ret < 0)
 88		return ret;
 89
 90	ret = regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL2,
 91				 MAX77620_TWD_MASK, regval);
 92	if (ret < 0)
 93		return ret;
 94
 95	wdt_dev->timeout = wdt_timeout;
 96
 97	return 0;
 98}
 99
100static const struct watchdog_info max77620_wdt_info = {
101	.identity = "max77620-watchdog",
102	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
103};
104
105static const struct watchdog_ops max77620_wdt_ops = {
106	.start		= max77620_wdt_start,
107	.stop		= max77620_wdt_stop,
108	.ping		= max77620_wdt_ping,
109	.set_timeout	= max77620_wdt_set_timeout,
110};
111
112static int max77620_wdt_probe(struct platform_device *pdev)
113{
114	struct max77620_wdt *wdt;
115	struct watchdog_device *wdt_dev;
116	unsigned int regval;
117	int ret;
118
119	wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
120	if (!wdt)
121		return -ENOMEM;
122
123	wdt->dev = &pdev->dev;
124	wdt->rmap = dev_get_regmap(pdev->dev.parent, NULL);
125	if (!wdt->rmap) {
126		dev_err(wdt->dev, "Failed to get parent regmap\n");
127		return -ENODEV;
128	}
129
130	wdt_dev = &wdt->wdt_dev;
131	wdt_dev->info = &max77620_wdt_info;
132	wdt_dev->ops = &max77620_wdt_ops;
133	wdt_dev->min_timeout = 2;
134	wdt_dev->max_timeout = 128;
135	wdt_dev->max_hw_heartbeat_ms = 128 * 1000;
136
137	platform_set_drvdata(pdev, wdt);
138
139	/* Enable WD_RST_WK - WDT expire results in a restart */
140	ret = regmap_update_bits(wdt->rmap, MAX77620_REG_ONOFFCNFG2,
141				 MAX77620_ONOFFCNFG2_WD_RST_WK,
142				 MAX77620_ONOFFCNFG2_WD_RST_WK);
143	if (ret < 0) {
144		dev_err(wdt->dev, "Failed to set WD_RST_WK: %d\n", ret);
145		return ret;
146	}
147
148	/* Set WDT clear in OFF and sleep mode */
149	ret = regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL2,
150				 MAX77620_WDTOFFC | MAX77620_WDTSLPC,
151				 MAX77620_WDTOFFC | MAX77620_WDTSLPC);
152	if (ret < 0) {
153		dev_err(wdt->dev, "Failed to set WDT OFF mode: %d\n", ret);
154		return ret;
155	}
156
157	/* Check if WDT running and if yes then set flags properly */
158	ret = regmap_read(wdt->rmap, MAX77620_REG_CNFGGLBL2, &regval);
159	if (ret < 0) {
160		dev_err(wdt->dev, "Failed to read WDT CFG register: %d\n", ret);
161		return ret;
162	}
163
164	switch (regval & MAX77620_TWD_MASK) {
165	case MAX77620_TWD_2s:
166		wdt_dev->timeout = 2;
167		break;
168	case MAX77620_TWD_16s:
169		wdt_dev->timeout = 16;
170		break;
171	case MAX77620_TWD_64s:
172		wdt_dev->timeout = 64;
173		break;
174	default:
175		wdt_dev->timeout = 128;
176		break;
177	}
178
179	if (regval & MAX77620_WDTEN)
180		set_bit(WDOG_HW_RUNNING, &wdt_dev->status);
181
182	watchdog_set_nowayout(wdt_dev, nowayout);
183	watchdog_set_drvdata(wdt_dev, wdt);
184
185	ret = watchdog_register_device(wdt_dev);
186	if (ret < 0) {
187		dev_err(&pdev->dev, "watchdog registration failed: %d\n", ret);
188		return ret;
189	}
190
191	return 0;
192}
193
194static int max77620_wdt_remove(struct platform_device *pdev)
195{
196	struct max77620_wdt *wdt = platform_get_drvdata(pdev);
197
198	max77620_wdt_stop(&wdt->wdt_dev);
199	watchdog_unregister_device(&wdt->wdt_dev);
200
201	return 0;
202}
203
204static struct platform_device_id max77620_wdt_devtype[] = {
205	{ .name = "max77620-watchdog", },
206	{ },
207};
208MODULE_DEVICE_TABLE(platform, max77620_wdt_devtype);
209
210static struct platform_driver max77620_wdt_driver = {
211	.driver	= {
212		.name	= "max77620-watchdog",
213	},
214	.probe	= max77620_wdt_probe,
215	.remove	= max77620_wdt_remove,
216	.id_table = max77620_wdt_devtype,
217};
218
219module_platform_driver(max77620_wdt_driver);
220
221MODULE_DESCRIPTION("Max77620 watchdog timer driver");
222
223module_param(nowayout, bool, 0);
224MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
225	"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
226
227MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
228MODULE_LICENSE("GPL v2");