Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 * PIC32 watchdog driver
  3 *
  4 * Joshua Henderson <joshua.henderson@microchip.com>
  5 * Copyright (c) 2016, Microchip Technology Inc.
  6 *
  7 * This program is free software; you can redistribute it and/or
  8 * modify it under the terms of the GNU General Public License
  9 * as published by the Free Software Foundation; either version
 10 * 2 of the License, or (at your option) any later version.
 11 */
 12#include <linux/clk.h>
 13#include <linux/device.h>
 14#include <linux/err.h>
 15#include <linux/io.h>
 16#include <linux/kernel.h>
 17#include <linux/module.h>
 18#include <linux/of.h>
 19#include <linux/of_device.h>
 20#include <linux/platform_device.h>
 21#include <linux/pm.h>
 22#include <linux/watchdog.h>
 23
 24#include <asm/mach-pic32/pic32.h>
 25
 26/* Watchdog Timer Registers */
 27#define WDTCON_REG		0x00
 28
 29/* Watchdog Timer Control Register fields */
 30#define WDTCON_WIN_EN		BIT(0)
 31#define WDTCON_RMCS_MASK	0x0003
 32#define WDTCON_RMCS_SHIFT	0x0006
 33#define WDTCON_RMPS_MASK	0x001F
 34#define WDTCON_RMPS_SHIFT	0x0008
 35#define WDTCON_ON		BIT(15)
 36#define WDTCON_CLR_KEY		0x5743
 37
 38/* Reset Control Register fields for watchdog */
 39#define RESETCON_TIMEOUT_IDLE	BIT(2)
 40#define RESETCON_TIMEOUT_SLEEP	BIT(3)
 41#define RESETCON_WDT_TIMEOUT	BIT(4)
 42
 43struct pic32_wdt {
 44	void __iomem	*regs;
 45	void __iomem	*rst_base;
 46	struct clk	*clk;
 47};
 48
 49static inline bool pic32_wdt_is_win_enabled(struct pic32_wdt *wdt)
 50{
 51	return !!(readl(wdt->regs + WDTCON_REG) & WDTCON_WIN_EN);
 52}
 53
 54static inline u32 pic32_wdt_get_post_scaler(struct pic32_wdt *wdt)
 55{
 56	u32 v = readl(wdt->regs + WDTCON_REG);
 57
 58	return (v >> WDTCON_RMPS_SHIFT) & WDTCON_RMPS_MASK;
 59}
 60
 61static inline u32 pic32_wdt_get_clk_id(struct pic32_wdt *wdt)
 62{
 63	u32 v = readl(wdt->regs + WDTCON_REG);
 64
 65	return (v >> WDTCON_RMCS_SHIFT) & WDTCON_RMCS_MASK;
 66}
 67
 68static int pic32_wdt_bootstatus(struct pic32_wdt *wdt)
 69{
 70	u32 v = readl(wdt->rst_base);
 71
 72	writel(RESETCON_WDT_TIMEOUT, PIC32_CLR(wdt->rst_base));
 73
 74	return v & RESETCON_WDT_TIMEOUT;
 75}
 76
 77static u32 pic32_wdt_get_timeout_secs(struct pic32_wdt *wdt, struct device *dev)
 78{
 79	unsigned long rate;
 80	u32 period, ps, terminal;
 81
 82	rate = clk_get_rate(wdt->clk);
 83
 84	dev_dbg(dev, "wdt: clk_id %d, clk_rate %lu (prescale)\n",
 85		pic32_wdt_get_clk_id(wdt), rate);
 86
 87	/* default, prescaler of 32 (i.e. div-by-32) is implicit. */
 88	rate >>= 5;
 89	if (!rate)
 90		return 0;
 91
 92	/* calculate terminal count from postscaler. */
 93	ps = pic32_wdt_get_post_scaler(wdt);
 94	terminal = BIT(ps);
 95
 96	/* find time taken (in secs) to reach terminal count */
 97	period = terminal / rate;
 98	dev_dbg(dev,
 99		"wdt: clk_rate %lu (postscale) / terminal %d, timeout %dsec\n",
100		rate, terminal, period);
101
102	return period;
103}
104
105static void pic32_wdt_keepalive(struct pic32_wdt *wdt)
106{
107	/* write key through single half-word */
108	writew(WDTCON_CLR_KEY, wdt->regs + WDTCON_REG + 2);
109}
110
111static int pic32_wdt_start(struct watchdog_device *wdd)
112{
113	struct pic32_wdt *wdt = watchdog_get_drvdata(wdd);
114
115	writel(WDTCON_ON, PIC32_SET(wdt->regs + WDTCON_REG));
116	pic32_wdt_keepalive(wdt);
117
118	return 0;
119}
120
121static int pic32_wdt_stop(struct watchdog_device *wdd)
122{
123	struct pic32_wdt *wdt = watchdog_get_drvdata(wdd);
124
125	writel(WDTCON_ON, PIC32_CLR(wdt->regs + WDTCON_REG));
126
127	/*
128	 * Cannot touch registers in the CPU cycle following clearing the
129	 * ON bit.
130	 */
131	nop();
132
133	return 0;
134}
135
136static int pic32_wdt_ping(struct watchdog_device *wdd)
137{
138	struct pic32_wdt *wdt = watchdog_get_drvdata(wdd);
139
140	pic32_wdt_keepalive(wdt);
141
142	return 0;
143}
144
145static const struct watchdog_ops pic32_wdt_fops = {
146	.owner		= THIS_MODULE,
147	.start		= pic32_wdt_start,
148	.stop		= pic32_wdt_stop,
149	.ping		= pic32_wdt_ping,
150};
151
152static const struct watchdog_info pic32_wdt_ident = {
153	.options = WDIOF_KEEPALIVEPING |
154			WDIOF_MAGICCLOSE | WDIOF_CARDRESET,
155	.identity = "PIC32 Watchdog",
156};
157
158static struct watchdog_device pic32_wdd = {
159	.info		= &pic32_wdt_ident,
160	.ops		= &pic32_wdt_fops,
161};
162
163static const struct of_device_id pic32_wdt_dt_ids[] = {
164	{ .compatible = "microchip,pic32mzda-wdt", },
165	{ /* sentinel */ }
166};
167MODULE_DEVICE_TABLE(of, pic32_wdt_dt_ids);
168
169static int pic32_wdt_drv_probe(struct platform_device *pdev)
170{
171	int ret;
172	struct watchdog_device *wdd = &pic32_wdd;
173	struct pic32_wdt *wdt;
174	struct resource *mem;
175
176	wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
177	if (!wdt)
178		return -ENOMEM;
179
180	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
181	wdt->regs = devm_ioremap_resource(&pdev->dev, mem);
182	if (IS_ERR(wdt->regs))
183		return PTR_ERR(wdt->regs);
184
185	wdt->rst_base = devm_ioremap(&pdev->dev, PIC32_BASE_RESET, 0x10);
186	if (!wdt->rst_base)
187		return -ENOMEM;
188
189	wdt->clk = devm_clk_get(&pdev->dev, NULL);
190	if (IS_ERR(wdt->clk)) {
191		dev_err(&pdev->dev, "clk not found\n");
192		return PTR_ERR(wdt->clk);
193	}
194
195	ret = clk_prepare_enable(wdt->clk);
196	if (ret) {
197		dev_err(&pdev->dev, "clk enable failed\n");
198		return ret;
199	}
200
201	if (pic32_wdt_is_win_enabled(wdt)) {
202		dev_err(&pdev->dev, "windowed-clear mode is not supported.\n");
203		ret = -ENODEV;
204		goto out_disable_clk;
205	}
206
207	wdd->timeout = pic32_wdt_get_timeout_secs(wdt, &pdev->dev);
208	if (!wdd->timeout) {
209		dev_err(&pdev->dev,
210			"failed to read watchdog register timeout\n");
211		ret = -EINVAL;
212		goto out_disable_clk;
213	}
214
215	dev_info(&pdev->dev, "timeout %d\n", wdd->timeout);
216
217	wdd->bootstatus = pic32_wdt_bootstatus(wdt) ? WDIOF_CARDRESET : 0;
218
219	watchdog_set_nowayout(wdd, WATCHDOG_NOWAYOUT);
220	watchdog_set_drvdata(wdd, wdt);
221
222	ret = watchdog_register_device(wdd);
223	if (ret) {
224		dev_err(&pdev->dev, "watchdog register failed, err %d\n", ret);
225		goto out_disable_clk;
226	}
227
228	platform_set_drvdata(pdev, wdd);
229
230	return 0;
231
232out_disable_clk:
233	clk_disable_unprepare(wdt->clk);
234
235	return ret;
236}
237
238static int pic32_wdt_drv_remove(struct platform_device *pdev)
239{
240	struct watchdog_device *wdd = platform_get_drvdata(pdev);
241	struct pic32_wdt *wdt = watchdog_get_drvdata(wdd);
242
243	watchdog_unregister_device(wdd);
244	clk_disable_unprepare(wdt->clk);
245
246	return 0;
247}
248
249static struct platform_driver pic32_wdt_driver = {
250	.probe		= pic32_wdt_drv_probe,
251	.remove		= pic32_wdt_drv_remove,
252	.driver		= {
253		.name		= "pic32-wdt",
254		.of_match_table = of_match_ptr(pic32_wdt_dt_ids),
255	}
256};
257
258module_platform_driver(pic32_wdt_driver);
259
260MODULE_AUTHOR("Joshua Henderson <joshua.henderson@microchip.com>");
261MODULE_DESCRIPTION("Microchip PIC32 Watchdog Timer");
262MODULE_LICENSE("GPL");