gemini: drop Linux 4.1 support
[openwrt/staging/yousong.git] / target / linux / gemini / files / drivers / watchdog / gemini_wdt.c
1 /*
2 * Watchdog driver for Cortina Systems Gemini SoC
3 *
4 * Copyright (C) 2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10
11 #include <linux/kernel.h>
12 #include <linux/init.h>
13 #include <linux/module.h>
14 #include <linux/io.h>
15 #include <linux/fs.h>
16 #include <linux/uaccess.h>
17 #include <linux/miscdevice.h>
18 #include <linux/platform_device.h>
19 #include <linux/watchdog.h>
20 #include <linux/slab.h>
21
22 #define GEMINI_WDCOUNTER 0x0
23 #define GEMINI_WDLOAD 0x4
24 #define GEMINI_WDRESTART 0x8
25
26 #define WDRESTART_MAGIC 0x5AB9
27
28 #define GEMINI_WDCR 0xC
29
30 #define WDCR_CLOCK_5MHZ (1 << 4)
31 #define WDCR_SYS_RST (1 << 1)
32 #define WDCR_ENABLE (1 << 0)
33
34 #define WDT_CLOCK 5000000 /* 5 MHz */
35 #define WDT_DEFAULT_TIMEOUT 13
36 #define WDT_MAX_TIMEOUT (0xFFFFFFFF / WDT_CLOCK)
37
38 /* status bits */
39 #define WDT_ACTIVE 0
40 #define WDT_OK_TO_CLOSE 1
41
42 static unsigned int timeout = WDT_DEFAULT_TIMEOUT;
43 static int nowayout = WATCHDOG_NOWAYOUT;
44
45 static DEFINE_SPINLOCK(gemini_wdt_lock);
46
47 static struct platform_device *gemini_wdt_dev;
48
49 struct gemini_wdt_struct {
50 struct resource *res;
51 struct device *dev;
52 void __iomem *base;
53 unsigned long status;
54 };
55
56 static struct watchdog_info gemini_wdt_info = {
57 .identity = "Gemini watchdog",
58 .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING |
59 WDIOF_SETTIMEOUT,
60 };
61
62 /* Disable the watchdog. */
63 static void gemini_wdt_stop(struct gemini_wdt_struct *gemini_wdt)
64 {
65 spin_lock(&gemini_wdt_lock);
66
67 __raw_writel(0, gemini_wdt->base + GEMINI_WDCR);
68
69 clear_bit(WDT_ACTIVE, &gemini_wdt->status);
70
71 spin_unlock(&gemini_wdt_lock);
72 }
73
74 /* Service the watchdog */
75 static void gemini_wdt_service(struct gemini_wdt_struct *gemini_wdt)
76 {
77 __raw_writel(WDRESTART_MAGIC, gemini_wdt->base + GEMINI_WDRESTART);
78 }
79
80 /* Enable and reset the watchdog. */
81 static void gemini_wdt_start(struct gemini_wdt_struct *gemini_wdt)
82 {
83 spin_lock(&gemini_wdt_lock);
84
85 __raw_writel(timeout * WDT_CLOCK, gemini_wdt->base + GEMINI_WDLOAD);
86
87 gemini_wdt_service(gemini_wdt);
88
89 /* set clock before enabling */
90 __raw_writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST,
91 gemini_wdt->base + GEMINI_WDCR);
92
93 __raw_writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST | WDCR_ENABLE,
94 gemini_wdt->base + GEMINI_WDCR);
95
96 set_bit(WDT_ACTIVE, &gemini_wdt->status);
97
98 spin_unlock(&gemini_wdt_lock);
99 }
100
101 /* Watchdog device is opened, and watchdog starts running. */
102 static int gemini_wdt_open(struct inode *inode, struct file *file)
103 {
104 struct gemini_wdt_struct *gemini_wdt = platform_get_drvdata(gemini_wdt_dev);
105
106 if (test_bit(WDT_ACTIVE, &gemini_wdt->status))
107 return -EBUSY;
108
109 file->private_data = gemini_wdt;
110
111 gemini_wdt_start(gemini_wdt);
112
113 return nonseekable_open(inode, file);
114 }
115
116 /* Close the watchdog device. */
117 static int gemini_wdt_close(struct inode *inode, struct file *file)
118 {
119 struct gemini_wdt_struct *gemini_wdt = file->private_data;
120
121 /* Disable the watchdog if possible */
122 if (test_bit(WDT_OK_TO_CLOSE, &gemini_wdt->status))
123 gemini_wdt_stop(gemini_wdt);
124 else
125 dev_warn(gemini_wdt->dev, "Device closed unexpectedly - timer will not stop\n");
126
127 return 0;
128 }
129
130 /* Handle commands from user-space. */
131 static long gemini_wdt_ioctl(struct file *file, unsigned int cmd,
132 unsigned long arg)
133 {
134 struct gemini_wdt_struct *gemini_wdt = file->private_data;
135
136 int value;
137
138 switch (cmd) {
139 case WDIOC_KEEPALIVE:
140 gemini_wdt_service(gemini_wdt);
141 return 0;
142
143 case WDIOC_GETSUPPORT:
144 return copy_to_user((struct watchdog_info *)arg, &gemini_wdt_info,
145 sizeof(gemini_wdt_info)) ? -EFAULT : 0;
146
147 case WDIOC_SETTIMEOUT:
148 if (get_user(value, (int *)arg))
149 return -EFAULT;
150
151 if ((value < 1) || (value > WDT_MAX_TIMEOUT))
152 return -EINVAL;
153
154 timeout = value;
155
156 /* restart wdt to use new timeout */
157 gemini_wdt_stop(gemini_wdt);
158 gemini_wdt_start(gemini_wdt);
159
160 /* Fall through */
161 case WDIOC_GETTIMEOUT:
162 return put_user(timeout, (int *)arg);
163
164 case WDIOC_GETTIMELEFT:
165 value = __raw_readl(gemini_wdt->base + GEMINI_WDCOUNTER);
166 return put_user(value / WDT_CLOCK, (int *)arg);
167
168 default:
169 return -ENOTTY;
170 }
171 }
172
173 /* Refresh the watchdog whenever device is written to. */
174 static ssize_t gemini_wdt_write(struct file *file, const char *data,
175 size_t len, loff_t *ppos)
176 {
177 struct gemini_wdt_struct *gemini_wdt = file->private_data;
178
179 if (len) {
180 if (!nowayout) {
181 size_t i;
182
183 clear_bit(WDT_OK_TO_CLOSE, &gemini_wdt->status);
184 for (i = 0; i != len; i++) {
185 char c;
186
187 if (get_user(c, data + i))
188 return -EFAULT;
189 if (c == 'V')
190 set_bit(WDT_OK_TO_CLOSE,
191 &gemini_wdt->status);
192 }
193 }
194 gemini_wdt_service(gemini_wdt);
195 }
196
197 return len;
198 }
199
200 static const struct file_operations gemini_wdt_fops = {
201 .owner = THIS_MODULE,
202 .llseek = no_llseek,
203 .unlocked_ioctl = gemini_wdt_ioctl,
204 .open = gemini_wdt_open,
205 .release = gemini_wdt_close,
206 .write = gemini_wdt_write,
207 };
208
209 static struct miscdevice gemini_wdt_miscdev = {
210 .minor = WATCHDOG_MINOR,
211 .name = "watchdog",
212 .fops = &gemini_wdt_fops,
213 };
214
215 static void gemini_wdt_shutdown(struct platform_device *pdev)
216 {
217 struct gemini_wdt_struct *gemini_wdt = platform_get_drvdata(pdev);
218
219 gemini_wdt_stop(gemini_wdt);
220 }
221
222 static int gemini_wdt_probe(struct platform_device *pdev)
223 {
224 int ret;
225 int res_size;
226 struct resource *res;
227 void __iomem *base;
228 struct gemini_wdt_struct *gemini_wdt;
229 unsigned int reg;
230
231 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
232 if (!res) {
233 dev_err(&pdev->dev, "can't get device resources\n");
234 return -ENODEV;
235 }
236
237 res_size = resource_size(res);
238 if (!request_mem_region(res->start, res_size, res->name)) {
239 dev_err(&pdev->dev, "can't allocate %d bytes at %d address\n",
240 res_size, res->start);
241 return -ENOMEM;
242 }
243
244 base = ioremap(res->start, res_size);
245 if (!base) {
246 dev_err(&pdev->dev, "ioremap failed\n");
247 ret = -EIO;
248 goto fail0;
249 }
250
251 gemini_wdt = kzalloc(sizeof(struct gemini_wdt_struct), GFP_KERNEL);
252 if (!gemini_wdt) {
253 dev_err(&pdev->dev, "can't allocate interface\n");
254 ret = -ENOMEM;
255 goto fail1;
256 }
257
258 /* Setup gemini_wdt driver structure */
259 gemini_wdt->base = base;
260 gemini_wdt->res = res;
261
262 /* Set up platform driver data */
263 platform_set_drvdata(pdev, gemini_wdt);
264 gemini_wdt_dev = pdev;
265
266 if (gemini_wdt_miscdev.parent) {
267 ret = -EBUSY;
268 goto fail2;
269 }
270
271 gemini_wdt_miscdev.parent = &pdev->dev;
272
273 reg = __raw_readw(gemini_wdt->base + GEMINI_WDCR);
274 if (reg & WDCR_ENABLE) {
275 /* Watchdog was enabled by the bootloader, disable it. */
276 reg &= ~(WDCR_ENABLE);
277 __raw_writel(reg, gemini_wdt->base + GEMINI_WDCR);
278 }
279
280 ret = misc_register(&gemini_wdt_miscdev);
281 if (ret)
282 goto fail2;
283
284 return 0;
285
286 fail2:
287 platform_set_drvdata(pdev, NULL);
288 kfree(gemini_wdt);
289 fail1:
290 iounmap(base);
291 fail0:
292 release_mem_region(res->start, res_size);
293
294 return ret;
295 }
296
297 static int gemini_wdt_remove(struct platform_device *pdev)
298 {
299 struct gemini_wdt_struct *gemini_wdt = platform_get_drvdata(pdev);
300
301 platform_set_drvdata(pdev, NULL);
302 misc_deregister(&gemini_wdt_miscdev);
303 gemini_wdt_dev = NULL;
304 iounmap(gemini_wdt->base);
305 release_mem_region(gemini_wdt->res->start, resource_size(gemini_wdt->res));
306
307 kfree(gemini_wdt);
308
309 return 0;
310 }
311
312 #ifdef CONFIG_PM
313 static int gemini_wdt_suspend(struct platform_device *pdev, pm_message_t message)
314 {
315 struct gemini_wdt_struct *gemini_wdt = platform_get_drvdata(pdev);
316 unsigned int reg;
317
318 reg = __raw_readw(gemini_wdt->base + GEMINI_WDCR);
319 reg &= ~(WDCR_WDENABLE);
320 __raw_writel(reg, gemini_wdt->base + GEMINI_WDCR);
321
322 return 0;
323 }
324
325 static int gemini_wdt_resume(struct platform_device *pdev)
326 {
327 struct gemini_wdt_struct *gemini_wdt = platform_get_drvdata(pdev);
328 unsigned int reg;
329
330 if (gemini_wdt->status) {
331 reg = __raw_readw(gemini_wdt->base + GEMINI_WDCR);
332 reg |= WDCR_WDENABLE;
333 __raw_writel(reg, gemini_wdt->base + GEMINI_WDCR);
334 }
335
336 return 0;
337 }
338 #else
339 #define gemini_wdt_suspend NULL
340 #define gemini_wdt_resume NULL
341 #endif
342
343 static struct platform_driver gemini_wdt_driver = {
344 .probe = gemini_wdt_probe,
345 .remove = gemini_wdt_remove,
346 .shutdown = gemini_wdt_shutdown,
347 .suspend = gemini_wdt_suspend,
348 .resume = gemini_wdt_resume,
349 .driver = {
350 .name = "gemini-wdt",
351 .owner = THIS_MODULE,
352 },
353 };
354
355 static int __init gemini_wdt_init(void)
356 {
357 return platform_driver_probe(&gemini_wdt_driver, gemini_wdt_probe);
358 }
359
360 static void __exit gemini_wdt_exit(void)
361 {
362 platform_driver_unregister(&gemini_wdt_driver);
363 }
364
365 module_init(gemini_wdt_init);
366 module_exit(gemini_wdt_exit);
367
368 module_param(timeout, uint, 0);
369 MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds");
370
371 module_param(nowayout, int, 0);
372 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
373
374 MODULE_AUTHOR("Paulius Zaleckas");
375 MODULE_DESCRIPTION("Watchdog driver for Gemini");
376 MODULE_LICENSE("GPL");
377 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
378 MODULE_ALIAS("platform:gemini-wdt");