1 #include <linux/miscdevice.h>
2 #include <linux/module.h>
3 #include <linux/moduleparam.h>
5 #include <linux/delay.h>
6 #include <linux/timer.h>
7 #include <linux/uaccess.h>
8 #include <linux/jiffies.h>
9 #include <linux/platform_device.h>
10 #include <linux/time.h>
11 #include <linux/watchdog.h>
12 #include <linux/types.h>
13 #include <linux/kernel.h>
16 #include <linux/mfd/da9052/reg.h>
17 #include <linux/mfd/da9052/da9052.h>
18 #include <linux/mfd/da9052/wdt.h>
20 #define DRIVER_NAME "da9052-wdt"
22 #define DA9052_STROBING_FILTER_ENABLE 0x0001
23 #define DA9052_STROBING_FILTER_DISABLE 0x0002
24 #define DA9052_SET_STROBING_MODE_MANUAL 0x0004
25 #define DA9052_SET_STROBING_MODE_AUTO 0x0008
27 #define KERNEL_MODULE 0
31 static u8 sm_strobe_filter_flag = DISABLE;
32 static u8 sm_strobe_mode_flag = DA9052_STROBE_MANUAL;
33 static u32 sm_mon_interval = DA9052_ADC_TWDMIN_TIME;
34 static u8 sm_str_req = DISABLE;
35 static u8 da9052_sm_scale = DA9052_WDT_DISABLE;
36 module_param(sm_strobe_filter_flag, byte, 0);
37 MODULE_PARM_DESC(sm_strobe_filter_flag,
38 "DA9052 SM driver strobe filter flag default = DISABLE");
40 module_param(sm_strobe_mode_flag, byte, 0);
41 MODULE_PARM_DESC(sm_strobe_mode_flag,
42 "DA9052 SM driver watchdog strobing mode default\
43 = DA9052_STROBE_MANUAL");
45 module_param(da9052_sm_scale, byte, 0);
46 MODULE_PARM_DESC(da9052_sm_scale,
47 "DA9052 SM driver scaling value used to calculate the\
48 time for the strobing filter default = 0");
50 module_param(sm_str_req, byte, 0);
51 MODULE_PARM_DESC(sm_str_req,
52 "DA9052 SM driver strobe request flag default = DISABLE");
54 static int nowayout = WATCHDOG_NOWAYOUT;
55 module_param(nowayout, int, 0);
56 MODULE_PARM_DESC(nowayout,
57 "Watchdog cannot be stopped once started (default="
58 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
60 static struct timer_list *monitoring_timer;
63 struct platform_device *pdev;
64 struct da9052 *da9052;
66 static struct miscdevice da9052_wdt_miscdev;
67 static unsigned long da9052_wdt_users;
68 static int da9052_wdt_expect_close;
70 static struct da9052_wdt *get_wdt_da9052(void)
72 /*return dev_get_drvdata(da9052_wdt_miscdev.parent);*/
73 return platform_get_drvdata
74 (to_platform_device(da9052_wdt_miscdev.parent));
77 void start_strobing(struct work_struct *work)
79 struct da9052_ssc_msg msg;
81 struct da9052_wdt *wdt = get_wdt_da9052();
85 mod_timer(monitoring_timer, jiffies + sm_mon_interval);
88 msg.addr = DA9052_CONTROLD_REG;
90 da9052_lock(wdt->da9052);
91 ret = wdt->da9052->read(wdt->da9052, &msg);
93 da9052_unlock(wdt->da9052);
96 da9052_unlock(wdt->da9052);
98 msg.data = (msg.data | DA9052_CONTROLD_WATCHDOG);
99 da9052_lock(wdt->da9052);
100 ret = wdt->da9052->write(wdt->da9052, &msg);
102 da9052_unlock(wdt->da9052);
105 da9052_unlock(wdt->da9052);
107 sm_str_req = DISABLE;
109 mod_timer(monitoring_timer, jiffies + sm_mon_interval);
114 void timer_callback(void)
116 if (((sm_strobe_mode_flag) &&
117 (sm_strobe_mode_flag == DA9052_STROBE_MANUAL)) ||
118 (sm_strobe_mode_flag == DA9052_STROBE_AUTO)) {
119 schedule_work(&strobing_action);
121 if (sm_strobe_mode_flag == DA9052_STROBE_MANUAL) {
122 mod_timer(monitoring_timer, jiffies +
128 static int da9052_sm_hw_init(struct da9052_wdt *wdt)
130 /* Create timer structure */
131 monitoring_timer = kzalloc(sizeof(struct timer_list), GFP_KERNEL);
132 if (!monitoring_timer)
135 init_timer(monitoring_timer);
136 monitoring_timer->expires = jiffies + sm_mon_interval;
137 monitoring_timer->function = (void *)&timer_callback;
139 sm_strobe_filter_flag = DA9052_SM_STROBE_CONF;
140 sm_strobe_mode_flag = DA9052_STROBE_MANUAL;
145 static int da9052_sm_hw_deinit(struct da9052_wdt *wdt)
147 struct da9052_ssc_msg msg;
150 if (monitoring_timer != NULL)
151 del_timer(monitoring_timer);
152 kfree(monitoring_timer);
154 msg.addr = DA9052_CONTROLD_REG;
157 da9052_lock(wdt->da9052);
158 ret = wdt->da9052->read(wdt->da9052, &msg);
161 da9052_unlock(wdt->da9052);
163 msg.data = (msg.data & ~(DA9052_CONTROLD_TWDSCALE));
164 da9052_lock(wdt->da9052);
165 ret = wdt->da9052->write(wdt->da9052, &msg);
168 da9052_unlock(wdt->da9052);
172 da9052_unlock(wdt->da9052);
176 s32 da9052_sm_set_strobing_filter(struct da9052_wdt *wdt,
177 u8 strobing_filter_state)
179 struct da9052_ssc_msg msg;
182 msg.addr = DA9052_CONTROLD_REG;
184 da9052_lock(wdt->da9052);
185 ret = wdt->da9052->read(wdt->da9052, &msg);
188 da9052_unlock(wdt->da9052);
190 msg.data = (msg.data & DA9052_CONTROLD_TWDSCALE);
192 if (strobing_filter_state == ENABLE) {
193 sm_strobe_filter_flag = ENABLE;
194 if (DA9052_WDT_DISABLE == msg.data) {
195 sm_str_req = DISABLE;
196 del_timer(monitoring_timer);
199 if (DA9052_SCALE_64X == msg.data)
200 sm_mon_interval = msecs_to_jiffies(DA9052_X64_WINDOW);
201 else if (DA9052_SCALE_32X == msg.data)
202 sm_mon_interval = msecs_to_jiffies(DA9052_X32_WINDOW);
203 else if (DA9052_SCALE_16X == msg.data)
204 sm_mon_interval = msecs_to_jiffies(DA9052_X16_WINDOW);
205 else if (DA9052_SCALE_8X == msg.data)
206 sm_mon_interval = msecs_to_jiffies(DA9052_X8_WINDOW);
207 else if (DA9052_SCALE_4X == msg.data)
208 sm_mon_interval = msecs_to_jiffies(DA9052_X4_WINDOW);
209 else if (DA9052_SCALE_2X == msg.data)
210 sm_mon_interval = msecs_to_jiffies(DA9052_X2_WINDOW);
212 sm_mon_interval = msecs_to_jiffies(DA9052_X1_WINDOW);
214 } else if (strobing_filter_state == DISABLE) {
215 sm_strobe_filter_flag = DISABLE;
216 sm_mon_interval = msecs_to_jiffies(DA9052_ADC_TWDMIN_TIME);
217 if (DA9052_WDT_DISABLE == msg.data) {
218 sm_str_req = DISABLE;
219 del_timer(monitoring_timer);
223 return STROBING_FILTER_ERROR;
225 mod_timer(monitoring_timer, jiffies + sm_mon_interval);
229 da9052_unlock(wdt->da9052);
233 int da9052_sm_set_strobing_mode(u8 strobing_mode_state)
235 if (strobing_mode_state == DA9052_STROBE_AUTO)
236 sm_strobe_mode_flag = DA9052_STROBE_AUTO;
237 else if (strobing_mode_state == DA9052_STROBE_MANUAL)
238 sm_strobe_mode_flag = DA9052_STROBE_MANUAL;
240 return STROBING_MODE_ERROR;
245 int da9052_sm_strobe_wdt(void)
251 s32 da9052_sm_set_wdt(struct da9052_wdt *wdt, u8 wdt_scaling)
253 struct da9052_ssc_msg msg;
257 if (wdt_scaling > DA9052_SCALE_64X)
258 return INVALID_SCALING_VALUE;
260 msg.addr = DA9052_CONTROLD_REG;
262 da9052_lock(wdt->da9052);
263 ret = wdt->da9052->read(wdt->da9052, &msg);
266 da9052_unlock(wdt->da9052);
268 if (!((DA9052_WDT_DISABLE == (msg.data & DA9052_CONTROLD_TWDSCALE)) &&
269 (DA9052_WDT_DISABLE == wdt_scaling))) {
270 msg.data = (msg.data & ~(DA9052_CONTROLD_TWDSCALE));
271 msg.addr = DA9052_CONTROLD_REG;
274 da9052_lock(wdt->da9052);
275 ret = wdt->da9052->write(wdt->da9052, &msg);
278 da9052_unlock(wdt->da9052);
281 da9052_lock(wdt->da9052);
282 ret = wdt->da9052->read(wdt->da9052, &msg);
285 da9052_unlock(wdt->da9052);
288 msg.data |= wdt_scaling;
290 da9052_lock(wdt->da9052);
291 ret = wdt->da9052->write(wdt->da9052, &msg);
294 da9052_unlock(wdt->da9052);
296 sm_str_req = DISABLE;
297 if (DA9052_WDT_DISABLE == wdt_scaling) {
298 del_timer(monitoring_timer);
301 if (sm_strobe_filter_flag == ENABLE) {
302 if (DA9052_SCALE_64X == wdt_scaling) {
304 msecs_to_jiffies(DA9052_X64_WINDOW);
305 } else if (DA9052_SCALE_32X == wdt_scaling) {
307 msecs_to_jiffies(DA9052_X32_WINDOW);
308 } else if (DA9052_SCALE_16X == wdt_scaling) {
310 msecs_to_jiffies(DA9052_X16_WINDOW);
311 } else if (DA9052_SCALE_8X == wdt_scaling) {
313 msecs_to_jiffies(DA9052_X8_WINDOW);
314 } else if (DA9052_SCALE_4X == wdt_scaling) {
316 msecs_to_jiffies(DA9052_X4_WINDOW);
317 } else if (DA9052_SCALE_2X == wdt_scaling) {
319 msecs_to_jiffies(DA9052_X2_WINDOW);
322 msecs_to_jiffies(DA9052_X1_WINDOW);
325 sm_mon_interval = msecs_to_jiffies(
326 DA9052_ADC_TWDMIN_TIME);
328 mod_timer(monitoring_timer, jiffies + sm_mon_interval);
333 da9052_unlock(wdt->da9052);
337 static int da9052_wdt_open(struct inode *inode, struct file *file)
339 struct da9052_wdt *wdt = get_wdt_da9052();
341 printk(KERN_INFO"IN WDT OPEN \n");
344 printk(KERN_INFO"Returning no device\n");
347 printk(KERN_INFO"IN WDT OPEN 1\n");
349 if (test_and_set_bit(0, &da9052_wdt_users))
352 ret = da9052_sm_hw_init(wdt);
354 printk(KERN_ERR "Watchdog hw init failed\n");
358 return nonseekable_open(inode, file);
361 static int da9052_wdt_release(struct inode *inode, struct file *file)
363 struct da9052_wdt *wdt = get_wdt_da9052();
365 if (da9052_wdt_expect_close == 42)
366 da9052_sm_hw_deinit(wdt);
368 da9052_sm_strobe_wdt();
369 da9052_wdt_expect_close = 0;
370 clear_bit(0, &da9052_wdt_users);
374 static ssize_t da9052_wdt_write(struct file *file,
375 const char __user *data, size_t count,
382 /* In case it was set long ago */
383 da9052_wdt_expect_close = 0;
384 for (i = 0; i != count; i++) {
386 if (get_user(c, data + i))
389 da9052_wdt_expect_close = 42;
392 da9052_sm_strobe_wdt();
397 static struct watchdog_info da9052_wdt_info = {
398 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
399 .identity = "DA9052_SM Watchdog",
402 static long da9052_wdt_ioctl(struct file *file, unsigned int cmd,
405 struct da9052_wdt *wdt = get_wdt_da9052();
406 void __user *argp = (void __user *)arg;
407 int __user *p = argp;
408 unsigned char new_value;
412 case WDIOC_GETSUPPORT:
413 return copy_to_user(argp, &da9052_wdt_info,
414 sizeof(da9052_wdt_info)) ? -EFAULT : 0;
415 case WDIOC_GETSTATUS:
416 case WDIOC_GETBOOTSTATUS:
417 return put_user(0, p);
418 case WDIOC_SETOPTIONS:
419 if (get_user(new_value, p))
421 if (new_value & DA9052_STROBING_FILTER_ENABLE)
422 da9052_sm_set_strobing_filter(wdt, ENABLE);
423 if (new_value & DA9052_STROBING_FILTER_DISABLE)
424 da9052_sm_set_strobing_filter(wdt, DISABLE);
425 if (new_value & DA9052_SET_STROBING_MODE_MANUAL)
426 da9052_sm_set_strobing_mode(DA9052_STROBE_MANUAL);
427 if (new_value & DA9052_SET_STROBING_MODE_AUTO)
428 da9052_sm_set_strobing_mode(DA9052_STROBE_AUTO);
430 case WDIOC_KEEPALIVE:
431 if (da9052_sm_strobe_wdt())
435 case WDIOC_SETTIMEOUT:
436 if (get_user(new_value, p))
438 da9052_sm_scale = new_value;
439 if (da9052_sm_set_wdt(wdt, da9052_sm_scale))
441 case WDIOC_GETTIMEOUT:
442 return put_user(sm_mon_interval, p);
449 static const struct file_operations da9052_wdt_fops = {
450 .owner = THIS_MODULE,
452 .unlocked_ioctl = da9052_wdt_ioctl,
453 .write = da9052_wdt_write,
454 .open = da9052_wdt_open,
455 .release = da9052_wdt_release,
458 static struct miscdevice da9052_wdt_miscdev = {
460 .name = "da9052-wdt",
461 .fops = &da9052_wdt_fops,
464 static int __devinit da9052_sm_probe(struct platform_device *pdev)
467 struct da9052_wdt *wdt;
468 struct da9052_ssc_msg msg;
470 wdt = kzalloc(sizeof(*wdt), GFP_KERNEL);
474 wdt->da9052 = dev_get_drvdata(pdev->dev.parent);
475 platform_set_drvdata(pdev, wdt);
477 msg.addr = DA9052_CONTROLD_REG;
480 da9052_lock(wdt->da9052);
481 ret = wdt->da9052->read(wdt->da9052, &msg);
483 da9052_unlock(wdt->da9052);
486 printk(KERN_INFO"DA9052 SM probe - 0 \n");
488 msg.data = (msg.data & ~(DA9052_CONTROLD_TWDSCALE));
489 ret = wdt->da9052->write(wdt->da9052, &msg);
491 da9052_unlock(wdt->da9052);
494 da9052_unlock(wdt->da9052);
496 da9052_wdt_miscdev.parent = &pdev->dev;
498 ret = misc_register(&da9052_wdt_miscdev);
500 platform_set_drvdata(pdev, NULL);
506 platform_set_drvdata(pdev, NULL);
511 static int __devexit da9052_sm_remove(struct platform_device *dev)
513 misc_deregister(&da9052_wdt_miscdev);
518 static struct platform_driver da9052_sm_driver = {
519 .probe = da9052_sm_probe,
520 .remove = __devexit_p(da9052_sm_remove),
523 .owner = THIS_MODULE,
527 static int __init da9052_sm_init(void)
529 return platform_driver_register(&da9052_sm_driver);
531 module_init(da9052_sm_init);
533 static void __exit da9052_sm_exit(void)
535 platform_driver_unregister(&da9052_sm_driver);
537 module_exit(da9052_sm_exit);
539 MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>")
540 MODULE_DESCRIPTION("DA9052 SM Device Driver");
541 MODULE_LICENSE("GPL");
542 MODULE_ALIAS("platform:" DRIVER_NAME);