diff options
author | Jarkko Nikula <jarkko.nikula@jollamobile.com> | 2012-09-11 02:01:10 -0400 |
---|---|---|
committer | Wim Van Sebroeck <wim@iguana.be> | 2012-12-19 16:24:40 -0500 |
commit | b2c4e4b2696287671723ef986f0db23cf4f52f15 (patch) | |
tree | f5c949159ada0b4ad75aebd16c05104b13975f30 | |
parent | 5235f57a6f460d5620acfcf236ca29ecca993325 (diff) |
watchdog: Convert twl4030_wdt to watchdog core
Convert the twl4030_wdt watchdog driver to watchdog core.
While at there use devm_kzalloc and set the default timeout in order to be
able test this driver with a simple shell script.
Signed-off-by: Jarkko Nikula <jarkko.nikula@jollamobile.com>
Tested-by: Aaro Koskinen <aaro.koskinen@iki.fi>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
-rw-r--r-- | drivers/watchdog/Kconfig | 1 | ||||
-rw-r--r-- | drivers/watchdog/twl4030_wdt.c | 183 |
2 files changed, 35 insertions, 149 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index ad1bb9382a96..ae5af82ac31a 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
@@ -300,6 +300,7 @@ config COH901327_WATCHDOG | |||
300 | config TWL4030_WATCHDOG | 300 | config TWL4030_WATCHDOG |
301 | tristate "TWL4030 Watchdog" | 301 | tristate "TWL4030 Watchdog" |
302 | depends on TWL4030_CORE | 302 | depends on TWL4030_CORE |
303 | select WATCHDOG_CORE | ||
303 | help | 304 | help |
304 | Support for TI TWL4030 watchdog. Say 'Y' here to enable the | 305 | Support for TI TWL4030 watchdog. Say 'Y' here to enable the |
305 | watchdog timer support for TWL4030 chips. | 306 | watchdog timer support for TWL4030 chips. |
diff --git a/drivers/watchdog/twl4030_wdt.c b/drivers/watchdog/twl4030_wdt.c index 9f54b1da7185..01d63674c94f 100644 --- a/drivers/watchdog/twl4030_wdt.c +++ b/drivers/watchdog/twl4030_wdt.c | |||
@@ -22,26 +22,12 @@ | |||
22 | #include <linux/types.h> | 22 | #include <linux/types.h> |
23 | #include <linux/slab.h> | 23 | #include <linux/slab.h> |
24 | #include <linux/kernel.h> | 24 | #include <linux/kernel.h> |
25 | #include <linux/fs.h> | ||
26 | #include <linux/watchdog.h> | 25 | #include <linux/watchdog.h> |
27 | #include <linux/platform_device.h> | 26 | #include <linux/platform_device.h> |
28 | #include <linux/miscdevice.h> | ||
29 | #include <linux/uaccess.h> | ||
30 | #include <linux/i2c/twl.h> | 27 | #include <linux/i2c/twl.h> |
31 | 28 | ||
32 | #define TWL4030_WATCHDOG_CFG_REG_OFFS 0x3 | 29 | #define TWL4030_WATCHDOG_CFG_REG_OFFS 0x3 |
33 | 30 | ||
34 | #define TWL4030_WDT_STATE_OPEN 0x1 | ||
35 | #define TWL4030_WDT_STATE_ACTIVE 0x8 | ||
36 | |||
37 | static struct platform_device *twl4030_wdt_dev; | ||
38 | |||
39 | struct twl4030_wdt { | ||
40 | struct miscdevice miscdev; | ||
41 | int timer_margin; | ||
42 | unsigned long state; | ||
43 | }; | ||
44 | |||
45 | static bool nowayout = WATCHDOG_NOWAYOUT; | 31 | static bool nowayout = WATCHDOG_NOWAYOUT; |
46 | module_param(nowayout, bool, 0); | 32 | module_param(nowayout, bool, 0); |
47 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " | 33 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " |
@@ -53,171 +39,71 @@ static int twl4030_wdt_write(unsigned char val) | |||
53 | TWL4030_WATCHDOG_CFG_REG_OFFS); | 39 | TWL4030_WATCHDOG_CFG_REG_OFFS); |
54 | } | 40 | } |
55 | 41 | ||
56 | static int twl4030_wdt_enable(struct twl4030_wdt *wdt) | 42 | static int twl4030_wdt_start(struct watchdog_device *wdt) |
57 | { | 43 | { |
58 | return twl4030_wdt_write(wdt->timer_margin + 1); | 44 | return twl4030_wdt_write(wdt->timeout + 1); |
59 | } | 45 | } |
60 | 46 | ||
61 | static int twl4030_wdt_disable(struct twl4030_wdt *wdt) | 47 | static int twl4030_wdt_stop(struct watchdog_device *wdt) |
62 | { | 48 | { |
63 | return twl4030_wdt_write(0); | 49 | return twl4030_wdt_write(0); |
64 | } | 50 | } |
65 | 51 | ||
66 | static int twl4030_wdt_set_timeout(struct twl4030_wdt *wdt, int timeout) | 52 | static int twl4030_wdt_set_timeout(struct watchdog_device *wdt, |
67 | { | 53 | unsigned int timeout) |
68 | if (timeout < 0 || timeout > 30) { | ||
69 | dev_warn(wdt->miscdev.parent, | ||
70 | "Timeout can only be in the range [0-30] seconds"); | ||
71 | return -EINVAL; | ||
72 | } | ||
73 | wdt->timer_margin = timeout; | ||
74 | return twl4030_wdt_enable(wdt); | ||
75 | } | ||
76 | |||
77 | static ssize_t twl4030_wdt_write_fop(struct file *file, | ||
78 | const char __user *data, size_t len, loff_t *ppos) | ||
79 | { | 54 | { |
80 | struct twl4030_wdt *wdt = file->private_data; | 55 | wdt->timeout = timeout; |
81 | |||
82 | if (len) | ||
83 | twl4030_wdt_enable(wdt); | ||
84 | |||
85 | return len; | ||
86 | } | ||
87 | |||
88 | static long twl4030_wdt_ioctl(struct file *file, | ||
89 | unsigned int cmd, unsigned long arg) | ||
90 | { | ||
91 | void __user *argp = (void __user *)arg; | ||
92 | int __user *p = argp; | ||
93 | int new_margin; | ||
94 | struct twl4030_wdt *wdt = file->private_data; | ||
95 | |||
96 | static const struct watchdog_info twl4030_wd_ident = { | ||
97 | .identity = "TWL4030 Watchdog", | ||
98 | .options = WDIOF_SETTIMEOUT, | ||
99 | .firmware_version = 0, | ||
100 | }; | ||
101 | |||
102 | switch (cmd) { | ||
103 | case WDIOC_GETSUPPORT: | ||
104 | return copy_to_user(argp, &twl4030_wd_ident, | ||
105 | sizeof(twl4030_wd_ident)) ? -EFAULT : 0; | ||
106 | |||
107 | case WDIOC_GETSTATUS: | ||
108 | case WDIOC_GETBOOTSTATUS: | ||
109 | return put_user(0, p); | ||
110 | |||
111 | case WDIOC_KEEPALIVE: | ||
112 | twl4030_wdt_enable(wdt); | ||
113 | break; | ||
114 | |||
115 | case WDIOC_SETTIMEOUT: | ||
116 | if (get_user(new_margin, p)) | ||
117 | return -EFAULT; | ||
118 | if (twl4030_wdt_set_timeout(wdt, new_margin)) | ||
119 | return -EINVAL; | ||
120 | return put_user(wdt->timer_margin, p); | ||
121 | |||
122 | case WDIOC_GETTIMEOUT: | ||
123 | return put_user(wdt->timer_margin, p); | ||
124 | |||
125 | default: | ||
126 | return -ENOTTY; | ||
127 | } | ||
128 | |||
129 | return 0; | 56 | return 0; |
130 | } | 57 | } |
131 | 58 | ||
132 | static int twl4030_wdt_open(struct inode *inode, struct file *file) | 59 | static const struct watchdog_info twl4030_wdt_info = { |
133 | { | 60 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, |
134 | struct twl4030_wdt *wdt = platform_get_drvdata(twl4030_wdt_dev); | 61 | .identity = "TWL4030 Watchdog", |
135 | 62 | }; | |
136 | /* /dev/watchdog can only be opened once */ | ||
137 | if (test_and_set_bit(0, &wdt->state)) | ||
138 | return -EBUSY; | ||
139 | |||
140 | wdt->state |= TWL4030_WDT_STATE_ACTIVE; | ||
141 | file->private_data = (void *) wdt; | ||
142 | |||
143 | twl4030_wdt_enable(wdt); | ||
144 | return nonseekable_open(inode, file); | ||
145 | } | ||
146 | |||
147 | static int twl4030_wdt_release(struct inode *inode, struct file *file) | ||
148 | { | ||
149 | struct twl4030_wdt *wdt = file->private_data; | ||
150 | if (nowayout) { | ||
151 | dev_alert(wdt->miscdev.parent, | ||
152 | "Unexpected close, watchdog still running!\n"); | ||
153 | twl4030_wdt_enable(wdt); | ||
154 | } else { | ||
155 | if (twl4030_wdt_disable(wdt)) | ||
156 | return -EFAULT; | ||
157 | wdt->state &= ~TWL4030_WDT_STATE_ACTIVE; | ||
158 | } | ||
159 | |||
160 | clear_bit(0, &wdt->state); | ||
161 | return 0; | ||
162 | } | ||
163 | 63 | ||
164 | static const struct file_operations twl4030_wdt_fops = { | 64 | static const struct watchdog_ops twl4030_wdt_ops = { |
165 | .owner = THIS_MODULE, | 65 | .owner = THIS_MODULE, |
166 | .llseek = no_llseek, | 66 | .start = twl4030_wdt_start, |
167 | .open = twl4030_wdt_open, | 67 | .stop = twl4030_wdt_stop, |
168 | .release = twl4030_wdt_release, | 68 | .set_timeout = twl4030_wdt_set_timeout, |
169 | .unlocked_ioctl = twl4030_wdt_ioctl, | ||
170 | .write = twl4030_wdt_write_fop, | ||
171 | }; | 69 | }; |
172 | 70 | ||
173 | static int twl4030_wdt_probe(struct platform_device *pdev) | 71 | static int twl4030_wdt_probe(struct platform_device *pdev) |
174 | { | 72 | { |
175 | int ret = 0; | 73 | int ret = 0; |
176 | struct twl4030_wdt *wdt; | 74 | struct watchdog_device *wdt; |
177 | 75 | ||
178 | wdt = kzalloc(sizeof(struct twl4030_wdt), GFP_KERNEL); | 76 | wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); |
179 | if (!wdt) | 77 | if (!wdt) |
180 | return -ENOMEM; | 78 | return -ENOMEM; |
181 | 79 | ||
182 | wdt->state = 0; | 80 | wdt->info = &twl4030_wdt_info; |
183 | wdt->timer_margin = 30; | 81 | wdt->ops = &twl4030_wdt_ops; |
184 | wdt->miscdev.parent = &pdev->dev; | 82 | wdt->status = 0; |
185 | wdt->miscdev.fops = &twl4030_wdt_fops; | 83 | wdt->timeout = 30; |
186 | wdt->miscdev.minor = WATCHDOG_MINOR; | 84 | wdt->min_timeout = 1; |
187 | wdt->miscdev.name = "watchdog"; | 85 | wdt->max_timeout = 30; |
188 | 86 | ||
87 | watchdog_set_nowayout(wdt, nowayout); | ||
189 | platform_set_drvdata(pdev, wdt); | 88 | platform_set_drvdata(pdev, wdt); |
190 | 89 | ||
191 | twl4030_wdt_dev = pdev; | 90 | twl4030_wdt_stop(wdt); |
192 | 91 | ||
193 | twl4030_wdt_disable(wdt); | 92 | ret = watchdog_register_device(wdt); |
194 | |||
195 | ret = misc_register(&wdt->miscdev); | ||
196 | if (ret) { | 93 | if (ret) { |
197 | dev_err(wdt->miscdev.parent, | ||
198 | "Failed to register misc device\n"); | ||
199 | platform_set_drvdata(pdev, NULL); | 94 | platform_set_drvdata(pdev, NULL); |
200 | kfree(wdt); | ||
201 | twl4030_wdt_dev = NULL; | ||
202 | return ret; | 95 | return ret; |
203 | } | 96 | } |
97 | |||
204 | return 0; | 98 | return 0; |
205 | } | 99 | } |
206 | 100 | ||
207 | static int twl4030_wdt_remove(struct platform_device *pdev) | 101 | static int twl4030_wdt_remove(struct platform_device *pdev) |
208 | { | 102 | { |
209 | struct twl4030_wdt *wdt = platform_get_drvdata(pdev); | 103 | struct watchdog_device *wdt = platform_get_drvdata(pdev); |
210 | |||
211 | if (wdt->state & TWL4030_WDT_STATE_ACTIVE) | ||
212 | if (twl4030_wdt_disable(wdt)) | ||
213 | return -EFAULT; | ||
214 | |||
215 | wdt->state &= ~TWL4030_WDT_STATE_ACTIVE; | ||
216 | misc_deregister(&wdt->miscdev); | ||
217 | 104 | ||
105 | watchdog_unregister_device(wdt); | ||
218 | platform_set_drvdata(pdev, NULL); | 106 | platform_set_drvdata(pdev, NULL); |
219 | kfree(wdt); | ||
220 | twl4030_wdt_dev = NULL; | ||
221 | 107 | ||
222 | return 0; | 108 | return 0; |
223 | } | 109 | } |
@@ -225,18 +111,18 @@ static int twl4030_wdt_remove(struct platform_device *pdev) | |||
225 | #ifdef CONFIG_PM | 111 | #ifdef CONFIG_PM |
226 | static int twl4030_wdt_suspend(struct platform_device *pdev, pm_message_t state) | 112 | static int twl4030_wdt_suspend(struct platform_device *pdev, pm_message_t state) |
227 | { | 113 | { |
228 | struct twl4030_wdt *wdt = platform_get_drvdata(pdev); | 114 | struct watchdog_device *wdt = platform_get_drvdata(pdev); |
229 | if (wdt->state & TWL4030_WDT_STATE_ACTIVE) | 115 | if (watchdog_active(wdt)) |
230 | return twl4030_wdt_disable(wdt); | 116 | return twl4030_wdt_stop(wdt); |
231 | 117 | ||
232 | return 0; | 118 | return 0; |
233 | } | 119 | } |
234 | 120 | ||
235 | static int twl4030_wdt_resume(struct platform_device *pdev) | 121 | static int twl4030_wdt_resume(struct platform_device *pdev) |
236 | { | 122 | { |
237 | struct twl4030_wdt *wdt = platform_get_drvdata(pdev); | 123 | struct watchdog_device *wdt = platform_get_drvdata(pdev); |
238 | if (wdt->state & TWL4030_WDT_STATE_ACTIVE) | 124 | if (watchdog_active(wdt)) |
239 | return twl4030_wdt_enable(wdt); | 125 | return twl4030_wdt_start(wdt); |
240 | 126 | ||
241 | return 0; | 127 | return 0; |
242 | } | 128 | } |
@@ -260,6 +146,5 @@ module_platform_driver(twl4030_wdt_driver); | |||
260 | 146 | ||
261 | MODULE_AUTHOR("Nokia Corporation"); | 147 | MODULE_AUTHOR("Nokia Corporation"); |
262 | MODULE_LICENSE("GPL"); | 148 | MODULE_LICENSE("GPL"); |
263 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
264 | MODULE_ALIAS("platform:twl4030_wdt"); | 149 | MODULE_ALIAS("platform:twl4030_wdt"); |
265 | 150 | ||