Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 * Watchdog driver for CSR SiRFprimaII and SiRFatlasVI
  3 *
  4 * Copyright (c) 2013 Cambridge Silicon Radio Limited, a CSR plc group company.
  5 *
  6 * Licensed under GPLv2 or later.
  7 */
  8
  9#include <linux/module.h>
 10#include <linux/watchdog.h>
 11#include <linux/platform_device.h>
 12#include <linux/moduleparam.h>
 13#include <linux/of.h>
 14#include <linux/io.h>
 15#include <linux/uaccess.h>
 16
 17#define CLOCK_FREQ	1000000
 18
 19#define SIRFSOC_TIMER_COUNTER_LO	0x0000
 20#define SIRFSOC_TIMER_MATCH_0		0x0008
 21#define SIRFSOC_TIMER_INT_EN		0x0024
 22#define SIRFSOC_TIMER_WATCHDOG_EN	0x0028
 23#define SIRFSOC_TIMER_LATCH		0x0030
 24#define SIRFSOC_TIMER_LATCHED_LO	0x0034
 25
 26#define SIRFSOC_TIMER_WDT_INDEX		5
 27
 28#define SIRFSOC_WDT_MIN_TIMEOUT		30		/* 30 secs */
 29#define SIRFSOC_WDT_MAX_TIMEOUT		(10 * 60)	/* 10 mins */
 30#define SIRFSOC_WDT_DEFAULT_TIMEOUT	30		/* 30 secs */
 31
 32static unsigned int timeout;
 33static bool nowayout = WATCHDOG_NOWAYOUT;
 34
 35module_param(timeout, uint, 0);
 36module_param(nowayout, bool, 0);
 37
 38MODULE_PARM_DESC(timeout, "Default watchdog timeout (in seconds)");
 39MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
 40			__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 41
 42static void __iomem *sirfsoc_wdt_base(struct watchdog_device *wdd)
 43{
 44	return (void __iomem __force *)watchdog_get_drvdata(wdd);
 45}
 46
 47static unsigned int sirfsoc_wdt_gettimeleft(struct watchdog_device *wdd)
 48{
 49	u32 counter, match;
 50	void __iomem *wdt_base;
 51	int time_left;
 52
 53	wdt_base = sirfsoc_wdt_base(wdd);
 54	counter = readl(wdt_base + SIRFSOC_TIMER_COUNTER_LO);
 55	match = readl(wdt_base +
 56		SIRFSOC_TIMER_MATCH_0 + (SIRFSOC_TIMER_WDT_INDEX << 2));
 57
 58	time_left = match - counter;
 59
 60	return time_left / CLOCK_FREQ;
 61}
 62
 63static int sirfsoc_wdt_updatetimeout(struct watchdog_device *wdd)
 64{
 65	u32 counter, timeout_ticks;
 66	void __iomem *wdt_base;
 67
 68	timeout_ticks = wdd->timeout * CLOCK_FREQ;
 69	wdt_base = sirfsoc_wdt_base(wdd);
 70
 71	/* Enable the latch before reading the LATCH_LO register */
 72	writel(1, wdt_base + SIRFSOC_TIMER_LATCH);
 73
 74	/* Set the TO value */
 75	counter = readl(wdt_base + SIRFSOC_TIMER_LATCHED_LO);
 76
 77	counter += timeout_ticks;
 78
 79	writel(counter, wdt_base +
 80		SIRFSOC_TIMER_MATCH_0 + (SIRFSOC_TIMER_WDT_INDEX << 2));
 81
 82	return 0;
 83}
 84
 85static int sirfsoc_wdt_enable(struct watchdog_device *wdd)
 86{
 87	void __iomem *wdt_base = sirfsoc_wdt_base(wdd);
 88	sirfsoc_wdt_updatetimeout(wdd);
 89
 90	/*
 91	 * NOTE: If interrupt is not enabled
 92	 * then WD-Reset doesn't get generated at all.
 93	 */
 94	writel(readl(wdt_base + SIRFSOC_TIMER_INT_EN)
 95		| (1 << SIRFSOC_TIMER_WDT_INDEX),
 96		wdt_base + SIRFSOC_TIMER_INT_EN);
 97	writel(1, wdt_base + SIRFSOC_TIMER_WATCHDOG_EN);
 98
 99	return 0;
100}
101
102static int sirfsoc_wdt_disable(struct watchdog_device *wdd)
103{
104	void __iomem *wdt_base = sirfsoc_wdt_base(wdd);
105
106	writel(0, wdt_base + SIRFSOC_TIMER_WATCHDOG_EN);
107	writel(readl(wdt_base + SIRFSOC_TIMER_INT_EN)
108		& (~(1 << SIRFSOC_TIMER_WDT_INDEX)),
109		wdt_base + SIRFSOC_TIMER_INT_EN);
110
111	return 0;
112}
113
114static int sirfsoc_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
115{
116	wdd->timeout = to;
117	sirfsoc_wdt_updatetimeout(wdd);
118
119	return 0;
120}
121
122#define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)
123
124static const struct watchdog_info sirfsoc_wdt_ident = {
125	.options          =     OPTIONS,
126	.firmware_version =	0,
127	.identity         =	"SiRFSOC Watchdog",
128};
129
130static const struct watchdog_ops sirfsoc_wdt_ops = {
131	.owner = THIS_MODULE,
132	.start = sirfsoc_wdt_enable,
133	.stop = sirfsoc_wdt_disable,
134	.get_timeleft = sirfsoc_wdt_gettimeleft,
135	.ping = sirfsoc_wdt_updatetimeout,
136	.set_timeout = sirfsoc_wdt_settimeout,
137};
138
139static struct watchdog_device sirfsoc_wdd = {
140	.info = &sirfsoc_wdt_ident,
141	.ops = &sirfsoc_wdt_ops,
142	.timeout = SIRFSOC_WDT_DEFAULT_TIMEOUT,
143	.min_timeout = SIRFSOC_WDT_MIN_TIMEOUT,
144	.max_timeout = SIRFSOC_WDT_MAX_TIMEOUT,
145};
146
147static int sirfsoc_wdt_probe(struct platform_device *pdev)
148{
149	struct resource *res;
150	int ret;
151	void __iomem *base;
152
153	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
154	base = devm_ioremap_resource(&pdev->dev, res);
155	if (IS_ERR(base))
156		return PTR_ERR(base);
157
158	watchdog_set_drvdata(&sirfsoc_wdd, (__force void *)base);
159
160	watchdog_init_timeout(&sirfsoc_wdd, timeout, &pdev->dev);
161	watchdog_set_nowayout(&sirfsoc_wdd, nowayout);
162	sirfsoc_wdd.parent = &pdev->dev;
163
164	ret = watchdog_register_device(&sirfsoc_wdd);
165	if (ret)
166		return ret;
167
168	platform_set_drvdata(pdev, &sirfsoc_wdd);
169
170	return 0;
171}
172
173static void sirfsoc_wdt_shutdown(struct platform_device *pdev)
174{
175	struct watchdog_device *wdd = platform_get_drvdata(pdev);
176
177	sirfsoc_wdt_disable(wdd);
178}
179
180static int sirfsoc_wdt_remove(struct platform_device *pdev)
181{
182	sirfsoc_wdt_shutdown(pdev);
183	return 0;
184}
185
186#ifdef	CONFIG_PM_SLEEP
187static int sirfsoc_wdt_suspend(struct device *dev)
188{
189	return 0;
190}
191
192static int sirfsoc_wdt_resume(struct device *dev)
193{
194	struct watchdog_device *wdd = dev_get_drvdata(dev);
195
196	/*
197	 * NOTE: Since timer controller registers settings are saved
198	 * and restored back by the timer-prima2.c, so we need not
199	 * update WD settings except refreshing timeout.
200	 */
201	sirfsoc_wdt_updatetimeout(wdd);
202
203	return 0;
204}
205#endif
206
207static SIMPLE_DEV_PM_OPS(sirfsoc_wdt_pm_ops,
208		sirfsoc_wdt_suspend, sirfsoc_wdt_resume);
209
210static const struct of_device_id sirfsoc_wdt_of_match[] = {
211	{ .compatible = "sirf,prima2-tick"},
212	{},
213};
214MODULE_DEVICE_TABLE(of, sirfsoc_wdt_of_match);
215
216static struct platform_driver sirfsoc_wdt_driver = {
217	.driver = {
218		.name = "sirfsoc-wdt",
219		.pm = &sirfsoc_wdt_pm_ops,
220		.of_match_table	= sirfsoc_wdt_of_match,
221	},
222	.probe = sirfsoc_wdt_probe,
223	.remove = sirfsoc_wdt_remove,
224	.shutdown = sirfsoc_wdt_shutdown,
225};
226module_platform_driver(sirfsoc_wdt_driver);
227
228MODULE_DESCRIPTION("SiRF SoC watchdog driver");
229MODULE_AUTHOR("Xianglong Du <Xianglong.Du@csr.com>");
230MODULE_LICENSE("GPL v2");
231MODULE_ALIAS("platform:sirfsoc-wdt");