diff options
-rw-r--r-- | Documentation/watchdog/watchdog-kernel-api.txt | 43 | ||||
-rw-r--r-- | Documentation/watchdog/watchdog-parameters.txt | 5 | ||||
-rw-r--r-- | drivers/hwmon/Kconfig | 6 | ||||
-rw-r--r-- | drivers/hwmon/sch5627.c | 2 | ||||
-rw-r--r-- | drivers/hwmon/sch5636.c | 2 | ||||
-rw-r--r-- | drivers/hwmon/sch56xx-common.c | 406 | ||||
-rw-r--r-- | drivers/hwmon/sch56xx-common.h | 2 | ||||
-rw-r--r-- | drivers/watchdog/Kconfig | 13 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 1 | ||||
-rw-r--r-- | drivers/watchdog/da9052_wdt.c | 251 | ||||
-rw-r--r-- | drivers/watchdog/iTCO_wdt.c | 6 | ||||
-rw-r--r-- | drivers/watchdog/sp805_wdt.c | 249 | ||||
-rw-r--r-- | drivers/watchdog/via_wdt.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/watchdog_core.c | 74 | ||||
-rw-r--r-- | drivers/watchdog/watchdog_core.h (renamed from drivers/watchdog/watchdog_dev.h) | 8 | ||||
-rw-r--r-- | drivers/watchdog/watchdog_dev.c | 375 | ||||
-rw-r--r-- | include/linux/watchdog.h | 28 |
17 files changed, 891 insertions, 582 deletions
diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt index 25fe4304f2fc..086638f6c82d 100644 --- a/Documentation/watchdog/watchdog-kernel-api.txt +++ b/Documentation/watchdog/watchdog-kernel-api.txt | |||
@@ -1,6 +1,6 @@ | |||
1 | The Linux WatchDog Timer Driver Core kernel API. | 1 | The Linux WatchDog Timer Driver Core kernel API. |
2 | =============================================== | 2 | =============================================== |
3 | Last reviewed: 16-Mar-2012 | 3 | Last reviewed: 22-May-2012 |
4 | 4 | ||
5 | Wim Van Sebroeck <wim@iguana.be> | 5 | Wim Van Sebroeck <wim@iguana.be> |
6 | 6 | ||
@@ -39,6 +39,10 @@ watchdog_device structure. | |||
39 | The watchdog device structure looks like this: | 39 | The watchdog device structure looks like this: |
40 | 40 | ||
41 | struct watchdog_device { | 41 | struct watchdog_device { |
42 | int id; | ||
43 | struct cdev cdev; | ||
44 | struct device *dev; | ||
45 | struct device *parent; | ||
42 | const struct watchdog_info *info; | 46 | const struct watchdog_info *info; |
43 | const struct watchdog_ops *ops; | 47 | const struct watchdog_ops *ops; |
44 | unsigned int bootstatus; | 48 | unsigned int bootstatus; |
@@ -46,10 +50,20 @@ struct watchdog_device { | |||
46 | unsigned int min_timeout; | 50 | unsigned int min_timeout; |
47 | unsigned int max_timeout; | 51 | unsigned int max_timeout; |
48 | void *driver_data; | 52 | void *driver_data; |
53 | struct mutex lock; | ||
49 | unsigned long status; | 54 | unsigned long status; |
50 | }; | 55 | }; |
51 | 56 | ||
52 | It contains following fields: | 57 | It contains following fields: |
58 | * id: set by watchdog_register_device, id 0 is special. It has both a | ||
59 | /dev/watchdog0 cdev (dynamic major, minor 0) as well as the old | ||
60 | /dev/watchdog miscdev. The id is set automatically when calling | ||
61 | watchdog_register_device. | ||
62 | * cdev: cdev for the dynamic /dev/watchdog<id> device nodes. This | ||
63 | field is also populated by watchdog_register_device. | ||
64 | * dev: device under the watchdog class (created by watchdog_register_device). | ||
65 | * parent: set this to the parent device (or NULL) before calling | ||
66 | watchdog_register_device. | ||
53 | * info: a pointer to a watchdog_info structure. This structure gives some | 67 | * info: a pointer to a watchdog_info structure. This structure gives some |
54 | additional information about the watchdog timer itself. (Like it's unique name) | 68 | additional information about the watchdog timer itself. (Like it's unique name) |
55 | * ops: a pointer to the list of watchdog operations that the watchdog supports. | 69 | * ops: a pointer to the list of watchdog operations that the watchdog supports. |
@@ -61,6 +75,7 @@ It contains following fields: | |||
61 | * driver_data: a pointer to the drivers private data of a watchdog device. | 75 | * driver_data: a pointer to the drivers private data of a watchdog device. |
62 | This data should only be accessed via the watchdog_set_drvdata and | 76 | This data should only be accessed via the watchdog_set_drvdata and |
63 | watchdog_get_drvdata routines. | 77 | watchdog_get_drvdata routines. |
78 | * lock: Mutex for WatchDog Timer Driver Core internal use only. | ||
64 | * status: this field contains a number of status bits that give extra | 79 | * status: this field contains a number of status bits that give extra |
65 | information about the status of the device (Like: is the watchdog timer | 80 | information about the status of the device (Like: is the watchdog timer |
66 | running/active, is the nowayout bit set, is the device opened via | 81 | running/active, is the nowayout bit set, is the device opened via |
@@ -78,6 +93,8 @@ struct watchdog_ops { | |||
78 | unsigned int (*status)(struct watchdog_device *); | 93 | unsigned int (*status)(struct watchdog_device *); |
79 | int (*set_timeout)(struct watchdog_device *, unsigned int); | 94 | int (*set_timeout)(struct watchdog_device *, unsigned int); |
80 | unsigned int (*get_timeleft)(struct watchdog_device *); | 95 | unsigned int (*get_timeleft)(struct watchdog_device *); |
96 | void (*ref)(struct watchdog_device *); | ||
97 | void (*unref)(struct watchdog_device *); | ||
81 | long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); | 98 | long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); |
82 | }; | 99 | }; |
83 | 100 | ||
@@ -85,6 +102,21 @@ It is important that you first define the module owner of the watchdog timer | |||
85 | driver's operations. This module owner will be used to lock the module when | 102 | driver's operations. This module owner will be used to lock the module when |
86 | the watchdog is active. (This to avoid a system crash when you unload the | 103 | the watchdog is active. (This to avoid a system crash when you unload the |
87 | module and /dev/watchdog is still open). | 104 | module and /dev/watchdog is still open). |
105 | |||
106 | If the watchdog_device struct is dynamically allocated, just locking the module | ||
107 | is not enough and a driver also needs to define the ref and unref operations to | ||
108 | ensure the structure holding the watchdog_device does not go away. | ||
109 | |||
110 | The simplest (and usually sufficient) implementation of this is to: | ||
111 | 1) Add a kref struct to the same structure which is holding the watchdog_device | ||
112 | 2) Define a release callback for the kref which frees the struct holding both | ||
113 | 3) Call kref_init on this kref *before* calling watchdog_register_device() | ||
114 | 4) Define a ref operation calling kref_get on this kref | ||
115 | 5) Define a unref operation calling kref_put on this kref | ||
116 | 6) When it is time to cleanup: | ||
117 | * Do not kfree() the struct holding both, the last kref_put will do this! | ||
118 | * *After* calling watchdog_unregister_device() call kref_put on the kref | ||
119 | |||
88 | Some operations are mandatory and some are optional. The mandatory operations | 120 | Some operations are mandatory and some are optional. The mandatory operations |
89 | are: | 121 | are: |
90 | * start: this is a pointer to the routine that starts the watchdog timer | 122 | * start: this is a pointer to the routine that starts the watchdog timer |
@@ -125,6 +157,10 @@ they are supported. These optional routines/operations are: | |||
125 | (Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the | 157 | (Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the |
126 | watchdog's info structure). | 158 | watchdog's info structure). |
127 | * get_timeleft: this routines returns the time that's left before a reset. | 159 | * get_timeleft: this routines returns the time that's left before a reset. |
160 | * ref: the operation that calls kref_get on the kref of a dynamically | ||
161 | allocated watchdog_device struct. | ||
162 | * unref: the operation that calls kref_put on the kref of a dynamically | ||
163 | allocated watchdog_device struct. | ||
128 | * ioctl: if this routine is present then it will be called first before we do | 164 | * ioctl: if this routine is present then it will be called first before we do |
129 | our own internal ioctl call handling. This routine should return -ENOIOCTLCMD | 165 | our own internal ioctl call handling. This routine should return -ENOIOCTLCMD |
130 | if a command is not supported. The parameters that are passed to the ioctl | 166 | if a command is not supported. The parameters that are passed to the ioctl |
@@ -144,6 +180,11 @@ bit-operations. The status bits that are defined are: | |||
144 | (This bit should only be used by the WatchDog Timer Driver Core). | 180 | (This bit should only be used by the WatchDog Timer Driver Core). |
145 | * WDOG_NO_WAY_OUT: this bit stores the nowayout setting for the watchdog. | 181 | * WDOG_NO_WAY_OUT: this bit stores the nowayout setting for the watchdog. |
146 | If this bit is set then the watchdog timer will not be able to stop. | 182 | If this bit is set then the watchdog timer will not be able to stop. |
183 | * WDOG_UNREGISTERED: this bit gets set by the WatchDog Timer Driver Core | ||
184 | after calling watchdog_unregister_device, and then checked before calling | ||
185 | any watchdog_ops, so that you can be sure that no operations (other then | ||
186 | unref) will get called after unregister, even if userspace still holds a | ||
187 | reference to /dev/watchdog | ||
147 | 188 | ||
148 | To set the WDOG_NO_WAY_OUT status bit (before registering your watchdog | 189 | To set the WDOG_NO_WAY_OUT status bit (before registering your watchdog |
149 | timer device) you can either: | 190 | timer device) you can either: |
diff --git a/Documentation/watchdog/watchdog-parameters.txt b/Documentation/watchdog/watchdog-parameters.txt index 17ddd822b456..04fddbacdbde 100644 --- a/Documentation/watchdog/watchdog-parameters.txt +++ b/Documentation/watchdog/watchdog-parameters.txt | |||
@@ -78,6 +78,11 @@ wd0_timeout: Default watchdog0 timeout in 1/10secs | |||
78 | wd1_timeout: Default watchdog1 timeout in 1/10secs | 78 | wd1_timeout: Default watchdog1 timeout in 1/10secs |
79 | wd2_timeout: Default watchdog2 timeout in 1/10secs | 79 | wd2_timeout: Default watchdog2 timeout in 1/10secs |
80 | ------------------------------------------------- | 80 | ------------------------------------------------- |
81 | da9052wdt: | ||
82 | timeout: Watchdog timeout in seconds. 2<= timeout <=131, default=2.048s | ||
83 | nowayout: Watchdog cannot be stopped once started | ||
84 | (default=kernel config parameter) | ||
85 | ------------------------------------------------- | ||
81 | davinci_wdt: | 86 | davinci_wdt: |
82 | heartbeat: Watchdog heartbeat period in seconds from 1 to 600, default 60 | 87 | heartbeat: Watchdog heartbeat period in seconds from 1 to 600, default 60 |
83 | ------------------------------------------------- | 88 | ------------------------------------------------- |
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 7cd9bf42108b..6f1d167cb1ea 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig | |||
@@ -1036,8 +1036,9 @@ config SENSORS_SCH56XX_COMMON | |||
1036 | 1036 | ||
1037 | config SENSORS_SCH5627 | 1037 | config SENSORS_SCH5627 |
1038 | tristate "SMSC SCH5627" | 1038 | tristate "SMSC SCH5627" |
1039 | depends on !PPC | 1039 | depends on !PPC && WATCHDOG |
1040 | select SENSORS_SCH56XX_COMMON | 1040 | select SENSORS_SCH56XX_COMMON |
1041 | select WATCHDOG_CORE | ||
1041 | help | 1042 | help |
1042 | If you say yes here you get support for the hardware monitoring | 1043 | If you say yes here you get support for the hardware monitoring |
1043 | features of the SMSC SCH5627 Super-I/O chip including support for | 1044 | features of the SMSC SCH5627 Super-I/O chip including support for |
@@ -1048,8 +1049,9 @@ config SENSORS_SCH5627 | |||
1048 | 1049 | ||
1049 | config SENSORS_SCH5636 | 1050 | config SENSORS_SCH5636 |
1050 | tristate "SMSC SCH5636" | 1051 | tristate "SMSC SCH5636" |
1051 | depends on !PPC | 1052 | depends on !PPC && WATCHDOG |
1052 | select SENSORS_SCH56XX_COMMON | 1053 | select SENSORS_SCH56XX_COMMON |
1054 | select WATCHDOG_CORE | ||
1053 | help | 1055 | help |
1054 | SMSC SCH5636 Super I/O chips include an embedded microcontroller for | 1056 | SMSC SCH5636 Super I/O chips include an embedded microcontroller for |
1055 | hardware monitoring solutions, allowing motherboard manufacturers to | 1057 | hardware monitoring solutions, allowing motherboard manufacturers to |
diff --git a/drivers/hwmon/sch5627.c b/drivers/hwmon/sch5627.c index 8ec6dfbccb64..8342275378b8 100644 --- a/drivers/hwmon/sch5627.c +++ b/drivers/hwmon/sch5627.c | |||
@@ -579,7 +579,7 @@ static int __devinit sch5627_probe(struct platform_device *pdev) | |||
579 | } | 579 | } |
580 | 580 | ||
581 | /* Note failing to register the watchdog is not a fatal error */ | 581 | /* Note failing to register the watchdog is not a fatal error */ |
582 | data->watchdog = sch56xx_watchdog_register(data->addr, | 582 | data->watchdog = sch56xx_watchdog_register(&pdev->dev, data->addr, |
583 | (build_code << 24) | (build_id << 8) | hwmon_rev, | 583 | (build_code << 24) | (build_id << 8) | hwmon_rev, |
584 | &data->update_lock, 1); | 584 | &data->update_lock, 1); |
585 | 585 | ||
diff --git a/drivers/hwmon/sch5636.c b/drivers/hwmon/sch5636.c index 906d4ed32d81..96a7e68718ca 100644 --- a/drivers/hwmon/sch5636.c +++ b/drivers/hwmon/sch5636.c | |||
@@ -510,7 +510,7 @@ static int __devinit sch5636_probe(struct platform_device *pdev) | |||
510 | } | 510 | } |
511 | 511 | ||
512 | /* Note failing to register the watchdog is not a fatal error */ | 512 | /* Note failing to register the watchdog is not a fatal error */ |
513 | data->watchdog = sch56xx_watchdog_register(data->addr, | 513 | data->watchdog = sch56xx_watchdog_register(&pdev->dev, data->addr, |
514 | (revision[0] << 8) | revision[1], | 514 | (revision[0] << 8) | revision[1], |
515 | &data->update_lock, 0); | 515 | &data->update_lock, 0); |
516 | 516 | ||
diff --git a/drivers/hwmon/sch56xx-common.c b/drivers/hwmon/sch56xx-common.c index ce52fc57d41d..4380f5d07be2 100644 --- a/drivers/hwmon/sch56xx-common.c +++ b/drivers/hwmon/sch56xx-common.c | |||
@@ -66,15 +66,10 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" | |||
66 | 66 | ||
67 | struct sch56xx_watchdog_data { | 67 | struct sch56xx_watchdog_data { |
68 | u16 addr; | 68 | u16 addr; |
69 | u32 revision; | ||
70 | struct mutex *io_lock; | 69 | struct mutex *io_lock; |
71 | struct mutex watchdog_lock; | ||
72 | struct list_head list; /* member of the watchdog_data_list */ | ||
73 | struct kref kref; | 70 | struct kref kref; |
74 | struct miscdevice watchdog_miscdev; | 71 | struct watchdog_info wdinfo; |
75 | unsigned long watchdog_is_open; | 72 | struct watchdog_device wddev; |
76 | char watchdog_name[10]; /* must be unique to avoid sysfs conflict */ | ||
77 | char watchdog_expect_close; | ||
78 | u8 watchdog_preset; | 73 | u8 watchdog_preset; |
79 | u8 watchdog_control; | 74 | u8 watchdog_control; |
80 | u8 watchdog_output_enable; | 75 | u8 watchdog_output_enable; |
@@ -82,15 +77,6 @@ struct sch56xx_watchdog_data { | |||
82 | 77 | ||
83 | static struct platform_device *sch56xx_pdev; | 78 | static struct platform_device *sch56xx_pdev; |
84 | 79 | ||
85 | /* | ||
86 | * Somewhat ugly :( global data pointer list with all sch56xx devices, so that | ||
87 | * we can find our device data as when using misc_register there is no other | ||
88 | * method to get to ones device data from the open fop. | ||
89 | */ | ||
90 | static LIST_HEAD(watchdog_data_list); | ||
91 | /* Note this lock not only protect list access, but also data.kref access */ | ||
92 | static DEFINE_MUTEX(watchdog_data_mutex); | ||
93 | |||
94 | /* Super I/O functions */ | 80 | /* Super I/O functions */ |
95 | static inline int superio_inb(int base, int reg) | 81 | static inline int superio_inb(int base, int reg) |
96 | { | 82 | { |
@@ -272,22 +258,22 @@ EXPORT_SYMBOL(sch56xx_read_virtual_reg12); | |||
272 | * Watchdog routines | 258 | * Watchdog routines |
273 | */ | 259 | */ |
274 | 260 | ||
275 | /* | 261 | /* Release our data struct when we're unregistered *and* |
276 | * Release our data struct when the platform device has been released *and* | 262 | all references to our watchdog device are released */ |
277 | * all references to our watchdog device are released. | 263 | static void watchdog_release_resources(struct kref *r) |
278 | */ | ||
279 | static void sch56xx_watchdog_release_resources(struct kref *r) | ||
280 | { | 264 | { |
281 | struct sch56xx_watchdog_data *data = | 265 | struct sch56xx_watchdog_data *data = |
282 | container_of(r, struct sch56xx_watchdog_data, kref); | 266 | container_of(r, struct sch56xx_watchdog_data, kref); |
283 | kfree(data); | 267 | kfree(data); |
284 | } | 268 | } |
285 | 269 | ||
286 | static int watchdog_set_timeout(struct sch56xx_watchdog_data *data, | 270 | static int watchdog_set_timeout(struct watchdog_device *wddev, |
287 | int timeout) | 271 | unsigned int timeout) |
288 | { | 272 | { |
289 | int ret, resolution; | 273 | struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev); |
274 | unsigned int resolution; | ||
290 | u8 control; | 275 | u8 control; |
276 | int ret; | ||
291 | 277 | ||
292 | /* 1 second or 60 second resolution? */ | 278 | /* 1 second or 60 second resolution? */ |
293 | if (timeout <= 255) | 279 | if (timeout <= 255) |
@@ -298,12 +284,6 @@ static int watchdog_set_timeout(struct sch56xx_watchdog_data *data, | |||
298 | if (timeout < resolution || timeout > (resolution * 255)) | 284 | if (timeout < resolution || timeout > (resolution * 255)) |
299 | return -EINVAL; | 285 | return -EINVAL; |
300 | 286 | ||
301 | mutex_lock(&data->watchdog_lock); | ||
302 | if (!data->addr) { | ||
303 | ret = -ENODEV; | ||
304 | goto leave; | ||
305 | } | ||
306 | |||
307 | if (resolution == 1) | 287 | if (resolution == 1) |
308 | control = data->watchdog_control | SCH56XX_WDOG_TIME_BASE_SEC; | 288 | control = data->watchdog_control | SCH56XX_WDOG_TIME_BASE_SEC; |
309 | else | 289 | else |
@@ -316,7 +296,7 @@ static int watchdog_set_timeout(struct sch56xx_watchdog_data *data, | |||
316 | control); | 296 | control); |
317 | mutex_unlock(data->io_lock); | 297 | mutex_unlock(data->io_lock); |
318 | if (ret) | 298 | if (ret) |
319 | goto leave; | 299 | return ret; |
320 | 300 | ||
321 | data->watchdog_control = control; | 301 | data->watchdog_control = control; |
322 | } | 302 | } |
@@ -326,38 +306,17 @@ static int watchdog_set_timeout(struct sch56xx_watchdog_data *data, | |||
326 | * the watchdog countdown. | 306 | * the watchdog countdown. |
327 | */ | 307 | */ |
328 | data->watchdog_preset = DIV_ROUND_UP(timeout, resolution); | 308 | data->watchdog_preset = DIV_ROUND_UP(timeout, resolution); |
309 | wddev->timeout = data->watchdog_preset * resolution; | ||
329 | 310 | ||
330 | ret = data->watchdog_preset * resolution; | 311 | return 0; |
331 | leave: | ||
332 | mutex_unlock(&data->watchdog_lock); | ||
333 | return ret; | ||
334 | } | ||
335 | |||
336 | static int watchdog_get_timeout(struct sch56xx_watchdog_data *data) | ||
337 | { | ||
338 | int timeout; | ||
339 | |||
340 | mutex_lock(&data->watchdog_lock); | ||
341 | if (data->watchdog_control & SCH56XX_WDOG_TIME_BASE_SEC) | ||
342 | timeout = data->watchdog_preset; | ||
343 | else | ||
344 | timeout = data->watchdog_preset * 60; | ||
345 | mutex_unlock(&data->watchdog_lock); | ||
346 | |||
347 | return timeout; | ||
348 | } | 312 | } |
349 | 313 | ||
350 | static int watchdog_start(struct sch56xx_watchdog_data *data) | 314 | static int watchdog_start(struct watchdog_device *wddev) |
351 | { | 315 | { |
316 | struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev); | ||
352 | int ret; | 317 | int ret; |
353 | u8 val; | 318 | u8 val; |
354 | 319 | ||
355 | mutex_lock(&data->watchdog_lock); | ||
356 | if (!data->addr) { | ||
357 | ret = -ENODEV; | ||
358 | goto leave_unlock_watchdog; | ||
359 | } | ||
360 | |||
361 | /* | 320 | /* |
362 | * The sch56xx's watchdog cannot really be started / stopped | 321 | * The sch56xx's watchdog cannot really be started / stopped |
363 | * it is always running, but we can avoid the timer expiring | 322 | * it is always running, but we can avoid the timer expiring |
@@ -385,18 +344,14 @@ static int watchdog_start(struct sch56xx_watchdog_data *data) | |||
385 | if (ret) | 344 | if (ret) |
386 | goto leave; | 345 | goto leave; |
387 | 346 | ||
388 | /* 2. Enable output (if not already enabled) */ | 347 | /* 2. Enable output */ |
389 | if (!(data->watchdog_output_enable & SCH56XX_WDOG_OUTPUT_ENABLE)) { | 348 | val = data->watchdog_output_enable | SCH56XX_WDOG_OUTPUT_ENABLE; |
390 | val = data->watchdog_output_enable | | 349 | ret = sch56xx_write_virtual_reg(data->addr, |
391 | SCH56XX_WDOG_OUTPUT_ENABLE; | 350 | SCH56XX_REG_WDOG_OUTPUT_ENABLE, val); |
392 | ret = sch56xx_write_virtual_reg(data->addr, | 351 | if (ret) |
393 | SCH56XX_REG_WDOG_OUTPUT_ENABLE, | 352 | goto leave; |
394 | val); | ||
395 | if (ret) | ||
396 | goto leave; | ||
397 | 353 | ||
398 | data->watchdog_output_enable = val; | 354 | data->watchdog_output_enable = val; |
399 | } | ||
400 | 355 | ||
401 | /* 3. Clear the watchdog event bit if set */ | 356 | /* 3. Clear the watchdog event bit if set */ |
402 | val = inb(data->addr + 9); | 357 | val = inb(data->addr + 9); |
@@ -405,234 +360,70 @@ static int watchdog_start(struct sch56xx_watchdog_data *data) | |||
405 | 360 | ||
406 | leave: | 361 | leave: |
407 | mutex_unlock(data->io_lock); | 362 | mutex_unlock(data->io_lock); |
408 | leave_unlock_watchdog: | ||
409 | mutex_unlock(&data->watchdog_lock); | ||
410 | return ret; | 363 | return ret; |
411 | } | 364 | } |
412 | 365 | ||
413 | static int watchdog_trigger(struct sch56xx_watchdog_data *data) | 366 | static int watchdog_trigger(struct watchdog_device *wddev) |
414 | { | 367 | { |
368 | struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev); | ||
415 | int ret; | 369 | int ret; |
416 | 370 | ||
417 | mutex_lock(&data->watchdog_lock); | ||
418 | if (!data->addr) { | ||
419 | ret = -ENODEV; | ||
420 | goto leave; | ||
421 | } | ||
422 | |||
423 | /* Reset the watchdog countdown counter */ | 371 | /* Reset the watchdog countdown counter */ |
424 | mutex_lock(data->io_lock); | 372 | mutex_lock(data->io_lock); |
425 | ret = sch56xx_write_virtual_reg(data->addr, SCH56XX_REG_WDOG_PRESET, | 373 | ret = sch56xx_write_virtual_reg(data->addr, SCH56XX_REG_WDOG_PRESET, |
426 | data->watchdog_preset); | 374 | data->watchdog_preset); |
427 | mutex_unlock(data->io_lock); | 375 | mutex_unlock(data->io_lock); |
428 | leave: | 376 | |
429 | mutex_unlock(&data->watchdog_lock); | ||
430 | return ret; | 377 | return ret; |
431 | } | 378 | } |
432 | 379 | ||
433 | static int watchdog_stop_unlocked(struct sch56xx_watchdog_data *data) | 380 | static int watchdog_stop(struct watchdog_device *wddev) |
434 | { | 381 | { |
382 | struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev); | ||
435 | int ret = 0; | 383 | int ret = 0; |
436 | u8 val; | 384 | u8 val; |
437 | 385 | ||
438 | if (!data->addr) | 386 | val = data->watchdog_output_enable & ~SCH56XX_WDOG_OUTPUT_ENABLE; |
439 | return -ENODEV; | 387 | mutex_lock(data->io_lock); |
440 | 388 | ret = sch56xx_write_virtual_reg(data->addr, | |
441 | if (data->watchdog_output_enable & SCH56XX_WDOG_OUTPUT_ENABLE) { | 389 | SCH56XX_REG_WDOG_OUTPUT_ENABLE, val); |
442 | val = data->watchdog_output_enable & | 390 | mutex_unlock(data->io_lock); |
443 | ~SCH56XX_WDOG_OUTPUT_ENABLE; | 391 | if (ret) |
444 | mutex_lock(data->io_lock); | 392 | return ret; |
445 | ret = sch56xx_write_virtual_reg(data->addr, | ||
446 | SCH56XX_REG_WDOG_OUTPUT_ENABLE, | ||
447 | val); | ||
448 | mutex_unlock(data->io_lock); | ||
449 | if (ret) | ||
450 | return ret; | ||
451 | |||
452 | data->watchdog_output_enable = val; | ||
453 | } | ||
454 | |||
455 | return ret; | ||
456 | } | ||
457 | |||
458 | static int watchdog_stop(struct sch56xx_watchdog_data *data) | ||
459 | { | ||
460 | int ret; | ||
461 | |||
462 | mutex_lock(&data->watchdog_lock); | ||
463 | ret = watchdog_stop_unlocked(data); | ||
464 | mutex_unlock(&data->watchdog_lock); | ||
465 | |||
466 | return ret; | ||
467 | } | ||
468 | |||
469 | static int watchdog_release(struct inode *inode, struct file *filp) | ||
470 | { | ||
471 | struct sch56xx_watchdog_data *data = filp->private_data; | ||
472 | |||
473 | if (data->watchdog_expect_close) { | ||
474 | watchdog_stop(data); | ||
475 | data->watchdog_expect_close = 0; | ||
476 | } else { | ||
477 | watchdog_trigger(data); | ||
478 | pr_crit("unexpected close, not stopping watchdog!\n"); | ||
479 | } | ||
480 | |||
481 | clear_bit(0, &data->watchdog_is_open); | ||
482 | |||
483 | mutex_lock(&watchdog_data_mutex); | ||
484 | kref_put(&data->kref, sch56xx_watchdog_release_resources); | ||
485 | mutex_unlock(&watchdog_data_mutex); | ||
486 | 393 | ||
394 | data->watchdog_output_enable = val; | ||
487 | return 0; | 395 | return 0; |
488 | } | 396 | } |
489 | 397 | ||
490 | static int watchdog_open(struct inode *inode, struct file *filp) | 398 | static void watchdog_ref(struct watchdog_device *wddev) |
491 | { | 399 | { |
492 | struct sch56xx_watchdog_data *pos, *data = NULL; | 400 | struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev); |
493 | int ret, watchdog_is_open; | ||
494 | |||
495 | /* | ||
496 | * We get called from drivers/char/misc.c with misc_mtx hold, and we | ||
497 | * call misc_register() from sch56xx_watchdog_probe() with | ||
498 | * watchdog_data_mutex hold, as misc_register() takes the misc_mtx | ||
499 | * lock, this is a possible deadlock, so we use mutex_trylock here. | ||
500 | */ | ||
501 | if (!mutex_trylock(&watchdog_data_mutex)) | ||
502 | return -ERESTARTSYS; | ||
503 | list_for_each_entry(pos, &watchdog_data_list, list) { | ||
504 | if (pos->watchdog_miscdev.minor == iminor(inode)) { | ||
505 | data = pos; | ||
506 | break; | ||
507 | } | ||
508 | } | ||
509 | /* Note we can never not have found data, so we don't check for this */ | ||
510 | watchdog_is_open = test_and_set_bit(0, &data->watchdog_is_open); | ||
511 | if (!watchdog_is_open) | ||
512 | kref_get(&data->kref); | ||
513 | mutex_unlock(&watchdog_data_mutex); | ||
514 | |||
515 | if (watchdog_is_open) | ||
516 | return -EBUSY; | ||
517 | |||
518 | filp->private_data = data; | ||
519 | |||
520 | /* Start the watchdog */ | ||
521 | ret = watchdog_start(data); | ||
522 | if (ret) { | ||
523 | watchdog_release(inode, filp); | ||
524 | return ret; | ||
525 | } | ||
526 | 401 | ||
527 | return nonseekable_open(inode, filp); | 402 | kref_get(&data->kref); |
528 | } | 403 | } |
529 | 404 | ||
530 | static ssize_t watchdog_write(struct file *filp, const char __user *buf, | 405 | static void watchdog_unref(struct watchdog_device *wddev) |
531 | size_t count, loff_t *offset) | ||
532 | { | 406 | { |
533 | int ret; | 407 | struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev); |
534 | struct sch56xx_watchdog_data *data = filp->private_data; | ||
535 | |||
536 | if (count) { | ||
537 | if (!nowayout) { | ||
538 | size_t i; | ||
539 | |||
540 | /* Clear it in case it was set with a previous write */ | ||
541 | data->watchdog_expect_close = 0; | ||
542 | |||
543 | for (i = 0; i != count; i++) { | ||
544 | char c; | ||
545 | if (get_user(c, buf + i)) | ||
546 | return -EFAULT; | ||
547 | if (c == 'V') | ||
548 | data->watchdog_expect_close = 1; | ||
549 | } | ||
550 | } | ||
551 | ret = watchdog_trigger(data); | ||
552 | if (ret) | ||
553 | return ret; | ||
554 | } | ||
555 | return count; | ||
556 | } | ||
557 | |||
558 | static long watchdog_ioctl(struct file *filp, unsigned int cmd, | ||
559 | unsigned long arg) | ||
560 | { | ||
561 | struct watchdog_info ident = { | ||
562 | .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, | ||
563 | .identity = "sch56xx watchdog" | ||
564 | }; | ||
565 | int i, ret = 0; | ||
566 | struct sch56xx_watchdog_data *data = filp->private_data; | ||
567 | |||
568 | switch (cmd) { | ||
569 | case WDIOC_GETSUPPORT: | ||
570 | ident.firmware_version = data->revision; | ||
571 | if (!nowayout) | ||
572 | ident.options |= WDIOF_MAGICCLOSE; | ||
573 | if (copy_to_user((void __user *)arg, &ident, sizeof(ident))) | ||
574 | ret = -EFAULT; | ||
575 | break; | ||
576 | |||
577 | case WDIOC_GETSTATUS: | ||
578 | case WDIOC_GETBOOTSTATUS: | ||
579 | ret = put_user(0, (int __user *)arg); | ||
580 | break; | ||
581 | |||
582 | case WDIOC_KEEPALIVE: | ||
583 | ret = watchdog_trigger(data); | ||
584 | break; | ||
585 | 408 | ||
586 | case WDIOC_GETTIMEOUT: | 409 | kref_put(&data->kref, watchdog_release_resources); |
587 | i = watchdog_get_timeout(data); | ||
588 | ret = put_user(i, (int __user *)arg); | ||
589 | break; | ||
590 | |||
591 | case WDIOC_SETTIMEOUT: | ||
592 | if (get_user(i, (int __user *)arg)) { | ||
593 | ret = -EFAULT; | ||
594 | break; | ||
595 | } | ||
596 | ret = watchdog_set_timeout(data, i); | ||
597 | if (ret >= 0) | ||
598 | ret = put_user(ret, (int __user *)arg); | ||
599 | break; | ||
600 | |||
601 | case WDIOC_SETOPTIONS: | ||
602 | if (get_user(i, (int __user *)arg)) { | ||
603 | ret = -EFAULT; | ||
604 | break; | ||
605 | } | ||
606 | |||
607 | if (i & WDIOS_DISABLECARD) | ||
608 | ret = watchdog_stop(data); | ||
609 | else if (i & WDIOS_ENABLECARD) | ||
610 | ret = watchdog_trigger(data); | ||
611 | else | ||
612 | ret = -EINVAL; | ||
613 | break; | ||
614 | |||
615 | default: | ||
616 | ret = -ENOTTY; | ||
617 | } | ||
618 | return ret; | ||
619 | } | 410 | } |
620 | 411 | ||
621 | static const struct file_operations watchdog_fops = { | 412 | static const struct watchdog_ops watchdog_ops = { |
622 | .owner = THIS_MODULE, | 413 | .owner = THIS_MODULE, |
623 | .llseek = no_llseek, | 414 | .start = watchdog_start, |
624 | .open = watchdog_open, | 415 | .stop = watchdog_stop, |
625 | .release = watchdog_release, | 416 | .ping = watchdog_trigger, |
626 | .write = watchdog_write, | 417 | .set_timeout = watchdog_set_timeout, |
627 | .unlocked_ioctl = watchdog_ioctl, | 418 | .ref = watchdog_ref, |
419 | .unref = watchdog_unref, | ||
628 | }; | 420 | }; |
629 | 421 | ||
630 | struct sch56xx_watchdog_data *sch56xx_watchdog_register( | 422 | struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent, |
631 | u16 addr, u32 revision, struct mutex *io_lock, int check_enabled) | 423 | u16 addr, u32 revision, struct mutex *io_lock, int check_enabled) |
632 | { | 424 | { |
633 | struct sch56xx_watchdog_data *data; | 425 | struct sch56xx_watchdog_data *data; |
634 | int i, err, control, output_enable; | 426 | int err, control, output_enable; |
635 | const int watchdog_minors[] = { WATCHDOG_MINOR, 212, 213, 214, 215 }; | ||
636 | 427 | ||
637 | /* Cache the watchdog registers */ | 428 | /* Cache the watchdog registers */ |
638 | mutex_lock(io_lock); | 429 | mutex_lock(io_lock); |
@@ -656,82 +447,55 @@ struct sch56xx_watchdog_data *sch56xx_watchdog_register( | |||
656 | return NULL; | 447 | return NULL; |
657 | 448 | ||
658 | data->addr = addr; | 449 | data->addr = addr; |
659 | data->revision = revision; | ||
660 | data->io_lock = io_lock; | 450 | data->io_lock = io_lock; |
661 | data->watchdog_control = control; | ||
662 | data->watchdog_output_enable = output_enable; | ||
663 | mutex_init(&data->watchdog_lock); | ||
664 | INIT_LIST_HEAD(&data->list); | ||
665 | kref_init(&data->kref); | 451 | kref_init(&data->kref); |
666 | 452 | ||
667 | err = watchdog_set_timeout(data, 60); | 453 | strlcpy(data->wdinfo.identity, "sch56xx watchdog", |
668 | if (err < 0) | 454 | sizeof(data->wdinfo.identity)); |
669 | goto error; | 455 | data->wdinfo.firmware_version = revision; |
670 | 456 | data->wdinfo.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT; | |
671 | /* | 457 | if (!nowayout) |
672 | * We take the data_mutex lock early so that watchdog_open() cannot | 458 | data->wdinfo.options |= WDIOF_MAGICCLOSE; |
673 | * run when misc_register() has completed, but we've not yet added | 459 | |
674 | * our data to the watchdog_data_list. | 460 | data->wddev.info = &data->wdinfo; |
675 | */ | 461 | data->wddev.ops = &watchdog_ops; |
676 | mutex_lock(&watchdog_data_mutex); | 462 | data->wddev.parent = parent; |
677 | for (i = 0; i < ARRAY_SIZE(watchdog_minors); i++) { | 463 | data->wddev.timeout = 60; |
678 | /* Register our watchdog part */ | 464 | data->wddev.min_timeout = 1; |
679 | snprintf(data->watchdog_name, sizeof(data->watchdog_name), | 465 | data->wddev.max_timeout = 255 * 60; |
680 | "watchdog%c", (i == 0) ? '\0' : ('0' + i)); | 466 | if (nowayout) |
681 | data->watchdog_miscdev.name = data->watchdog_name; | 467 | set_bit(WDOG_NO_WAY_OUT, &data->wddev.status); |
682 | data->watchdog_miscdev.fops = &watchdog_fops; | 468 | if (output_enable & SCH56XX_WDOG_OUTPUT_ENABLE) |
683 | data->watchdog_miscdev.minor = watchdog_minors[i]; | 469 | set_bit(WDOG_ACTIVE, &data->wddev.status); |
684 | err = misc_register(&data->watchdog_miscdev); | 470 | |
685 | if (err == -EBUSY) | 471 | /* Since the watchdog uses a downcounter there is no register to read |
686 | continue; | 472 | the BIOS set timeout from (if any was set at all) -> |
687 | if (err) | 473 | Choose a preset which will give us a 1 minute timeout */ |
688 | break; | 474 | if (control & SCH56XX_WDOG_TIME_BASE_SEC) |
475 | data->watchdog_preset = 60; /* seconds */ | ||
476 | else | ||
477 | data->watchdog_preset = 1; /* minute */ | ||
689 | 478 | ||
690 | list_add(&data->list, &watchdog_data_list); | 479 | data->watchdog_control = control; |
691 | pr_info("Registered /dev/%s chardev major 10, minor: %d\n", | 480 | data->watchdog_output_enable = output_enable; |
692 | data->watchdog_name, watchdog_minors[i]); | ||
693 | break; | ||
694 | } | ||
695 | mutex_unlock(&watchdog_data_mutex); | ||
696 | 481 | ||
482 | watchdog_set_drvdata(&data->wddev, data); | ||
483 | err = watchdog_register_device(&data->wddev); | ||
697 | if (err) { | 484 | if (err) { |
698 | pr_err("Registering watchdog chardev: %d\n", err); | 485 | pr_err("Registering watchdog chardev: %d\n", err); |
699 | goto error; | 486 | kfree(data); |
700 | } | 487 | return NULL; |
701 | if (i == ARRAY_SIZE(watchdog_minors)) { | ||
702 | pr_warn("Couldn't register watchdog (no free minor)\n"); | ||
703 | goto error; | ||
704 | } | 488 | } |
705 | 489 | ||
706 | return data; | 490 | return data; |
707 | |||
708 | error: | ||
709 | kfree(data); | ||
710 | return NULL; | ||
711 | } | 491 | } |
712 | EXPORT_SYMBOL(sch56xx_watchdog_register); | 492 | EXPORT_SYMBOL(sch56xx_watchdog_register); |
713 | 493 | ||
714 | void sch56xx_watchdog_unregister(struct sch56xx_watchdog_data *data) | 494 | void sch56xx_watchdog_unregister(struct sch56xx_watchdog_data *data) |
715 | { | 495 | { |
716 | mutex_lock(&watchdog_data_mutex); | 496 | watchdog_unregister_device(&data->wddev); |
717 | misc_deregister(&data->watchdog_miscdev); | 497 | kref_put(&data->kref, watchdog_release_resources); |
718 | list_del(&data->list); | 498 | /* Don't touch data after this it may have been free-ed! */ |
719 | mutex_unlock(&watchdog_data_mutex); | ||
720 | |||
721 | mutex_lock(&data->watchdog_lock); | ||
722 | if (data->watchdog_is_open) { | ||
723 | pr_warn("platform device unregistered with watchdog " | ||
724 | "open! Stopping watchdog.\n"); | ||
725 | watchdog_stop_unlocked(data); | ||
726 | } | ||
727 | /* Tell the wdog start/stop/trigger functions our dev is gone */ | ||
728 | data->addr = 0; | ||
729 | data->io_lock = NULL; | ||
730 | mutex_unlock(&data->watchdog_lock); | ||
731 | |||
732 | mutex_lock(&watchdog_data_mutex); | ||
733 | kref_put(&data->kref, sch56xx_watchdog_release_resources); | ||
734 | mutex_unlock(&watchdog_data_mutex); | ||
735 | } | 499 | } |
736 | EXPORT_SYMBOL(sch56xx_watchdog_unregister); | 500 | EXPORT_SYMBOL(sch56xx_watchdog_unregister); |
737 | 501 | ||
diff --git a/drivers/hwmon/sch56xx-common.h b/drivers/hwmon/sch56xx-common.h index 7475086eb978..704ea2c6d28a 100644 --- a/drivers/hwmon/sch56xx-common.h +++ b/drivers/hwmon/sch56xx-common.h | |||
@@ -27,6 +27,6 @@ int sch56xx_read_virtual_reg16(u16 addr, u16 reg); | |||
27 | int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg, | 27 | int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg, |
28 | int high_nibble); | 28 | int high_nibble); |
29 | 29 | ||
30 | struct sch56xx_watchdog_data *sch56xx_watchdog_register( | 30 | struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent, |
31 | u16 addr, u32 revision, struct mutex *io_lock, int check_enabled); | 31 | u16 addr, u32 revision, struct mutex *io_lock, int check_enabled); |
32 | void sch56xx_watchdog_unregister(struct sch56xx_watchdog_data *data); | 32 | void sch56xx_watchdog_unregister(struct sch56xx_watchdog_data *data); |
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index d92d7488be16..fe819b76de56 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
@@ -64,6 +64,18 @@ config SOFT_WATCHDOG | |||
64 | To compile this driver as a module, choose M here: the | 64 | To compile this driver as a module, choose M here: the |
65 | module will be called softdog. | 65 | module will be called softdog. |
66 | 66 | ||
67 | config DA9052_WATCHDOG | ||
68 | tristate "Dialog DA9052 Watchdog" | ||
69 | depends on PMIC_DA9052 | ||
70 | select WATCHDOG_CORE | ||
71 | help | ||
72 | Support for the watchdog in the DA9052 PMIC. Watchdog trigger | ||
73 | cause system reset. | ||
74 | |||
75 | Say Y here to include support for the DA9052 watchdog. | ||
76 | Alternatively say M to compile the driver as a module, | ||
77 | which will be called da9052_wdt. | ||
78 | |||
67 | config WM831X_WATCHDOG | 79 | config WM831X_WATCHDOG |
68 | tristate "WM831x watchdog" | 80 | tristate "WM831x watchdog" |
69 | depends on MFD_WM831X | 81 | depends on MFD_WM831X |
@@ -87,6 +99,7 @@ config WM8350_WATCHDOG | |||
87 | config ARM_SP805_WATCHDOG | 99 | config ARM_SP805_WATCHDOG |
88 | tristate "ARM SP805 Watchdog" | 100 | tristate "ARM SP805 Watchdog" |
89 | depends on ARM_AMBA | 101 | depends on ARM_AMBA |
102 | select WATCHDOG_CORE | ||
90 | help | 103 | help |
91 | ARM Primecell SP805 Watchdog timer. This will reboot your system when | 104 | ARM Primecell SP805 Watchdog timer. This will reboot your system when |
92 | the timeout is reached. | 105 | the timeout is reached. |
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 442bfbe0882a..572b39bed06a 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile | |||
@@ -163,6 +163,7 @@ obj-$(CONFIG_WATCHDOG_CP1XXX) += cpwd.o | |||
163 | obj-$(CONFIG_XEN_WDT) += xen_wdt.o | 163 | obj-$(CONFIG_XEN_WDT) += xen_wdt.o |
164 | 164 | ||
165 | # Architecture Independent | 165 | # Architecture Independent |
166 | obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o | ||
166 | obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o | 167 | obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o |
167 | obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o | 168 | obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o |
168 | obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o | 169 | obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o |
diff --git a/drivers/watchdog/da9052_wdt.c b/drivers/watchdog/da9052_wdt.c new file mode 100644 index 000000000000..3f75129eb0a9 --- /dev/null +++ b/drivers/watchdog/da9052_wdt.c | |||
@@ -0,0 +1,251 @@ | |||
1 | /* | ||
2 | * System monitoring driver for DA9052 PMICs. | ||
3 | * | ||
4 | * Copyright(c) 2012 Dialog Semiconductor Ltd. | ||
5 | * | ||
6 | * Author: Anthony Olech <Anthony.Olech@diasemi.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #include <linux/module.h> | ||
16 | #include <linux/delay.h> | ||
17 | #include <linux/uaccess.h> | ||
18 | #include <linux/platform_device.h> | ||
19 | #include <linux/time.h> | ||
20 | #include <linux/watchdog.h> | ||
21 | #include <linux/types.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/jiffies.h> | ||
24 | #include <linux/delay.h> | ||
25 | |||
26 | #include <linux/mfd/da9052/reg.h> | ||
27 | #include <linux/mfd/da9052/da9052.h> | ||
28 | |||
29 | #define DA9052_DEF_TIMEOUT 4 | ||
30 | #define DA9052_TWDMIN 256 | ||
31 | |||
32 | struct da9052_wdt_data { | ||
33 | struct watchdog_device wdt; | ||
34 | struct da9052 *da9052; | ||
35 | struct kref kref; | ||
36 | unsigned long jpast; | ||
37 | }; | ||
38 | |||
39 | static const struct { | ||
40 | u8 reg_val; | ||
41 | int time; /* Seconds */ | ||
42 | } da9052_wdt_maps[] = { | ||
43 | { 1, 2 }, | ||
44 | { 2, 4 }, | ||
45 | { 3, 8 }, | ||
46 | { 4, 16 }, | ||
47 | { 5, 32 }, | ||
48 | { 5, 33 }, /* Actual time 32.768s so included both 32s and 33s */ | ||
49 | { 6, 65 }, | ||
50 | { 6, 66 }, /* Actual time 65.536s so include both, 65s and 66s */ | ||
51 | { 7, 131 }, | ||
52 | }; | ||
53 | |||
54 | |||
55 | static void da9052_wdt_release_resources(struct kref *r) | ||
56 | { | ||
57 | struct da9052_wdt_data *driver_data = | ||
58 | container_of(r, struct da9052_wdt_data, kref); | ||
59 | |||
60 | kfree(driver_data); | ||
61 | } | ||
62 | |||
63 | static int da9052_wdt_set_timeout(struct watchdog_device *wdt_dev, | ||
64 | unsigned int timeout) | ||
65 | { | ||
66 | struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); | ||
67 | struct da9052 *da9052 = driver_data->da9052; | ||
68 | int ret, i; | ||
69 | |||
70 | /* | ||
71 | * Disable the Watchdog timer before setting | ||
72 | * new time out. | ||
73 | */ | ||
74 | ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG, | ||
75 | DA9052_CONTROLD_TWDSCALE, 0); | ||
76 | if (ret < 0) { | ||
77 | dev_err(da9052->dev, "Failed to disable watchdog bit, %d\n", | ||
78 | ret); | ||
79 | return ret; | ||
80 | } | ||
81 | if (timeout) { | ||
82 | /* | ||
83 | * To change the timeout, da9052 needs to | ||
84 | * be disabled for at least 150 us. | ||
85 | */ | ||
86 | udelay(150); | ||
87 | |||
88 | /* Set the desired timeout */ | ||
89 | for (i = 0; i < ARRAY_SIZE(da9052_wdt_maps); i++) | ||
90 | if (da9052_wdt_maps[i].time == timeout) | ||
91 | break; | ||
92 | |||
93 | if (i == ARRAY_SIZE(da9052_wdt_maps)) | ||
94 | ret = -EINVAL; | ||
95 | else | ||
96 | ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG, | ||
97 | DA9052_CONTROLD_TWDSCALE, | ||
98 | da9052_wdt_maps[i].reg_val); | ||
99 | if (ret < 0) { | ||
100 | dev_err(da9052->dev, | ||
101 | "Failed to update timescale bit, %d\n", ret); | ||
102 | return ret; | ||
103 | } | ||
104 | |||
105 | wdt_dev->timeout = timeout; | ||
106 | driver_data->jpast = jiffies; | ||
107 | } | ||
108 | |||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | static void da9052_wdt_ref(struct watchdog_device *wdt_dev) | ||
113 | { | ||
114 | struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); | ||
115 | |||
116 | kref_get(&driver_data->kref); | ||
117 | } | ||
118 | |||
119 | static void da9052_wdt_unref(struct watchdog_device *wdt_dev) | ||
120 | { | ||
121 | struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); | ||
122 | |||
123 | kref_put(&driver_data->kref, da9052_wdt_release_resources); | ||
124 | } | ||
125 | |||
126 | static int da9052_wdt_start(struct watchdog_device *wdt_dev) | ||
127 | { | ||
128 | return da9052_wdt_set_timeout(wdt_dev, wdt_dev->timeout); | ||
129 | } | ||
130 | |||
131 | static int da9052_wdt_stop(struct watchdog_device *wdt_dev) | ||
132 | { | ||
133 | return da9052_wdt_set_timeout(wdt_dev, 0); | ||
134 | } | ||
135 | |||
136 | static int da9052_wdt_ping(struct watchdog_device *wdt_dev) | ||
137 | { | ||
138 | struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); | ||
139 | struct da9052 *da9052 = driver_data->da9052; | ||
140 | unsigned long msec, jnow = jiffies; | ||
141 | int ret; | ||
142 | |||
143 | /* | ||
144 | * We have a minimum time for watchdog window called TWDMIN. A write | ||
145 | * to the watchdog before this elapsed time should cause an error. | ||
146 | */ | ||
147 | msec = (jnow - driver_data->jpast) * 1000/HZ; | ||
148 | if (msec < DA9052_TWDMIN) | ||
149 | mdelay(msec); | ||
150 | |||
151 | /* Reset the watchdog timer */ | ||
152 | ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG, | ||
153 | DA9052_CONTROLD_WATCHDOG, 1 << 7); | ||
154 | if (ret < 0) | ||
155 | goto err_strobe; | ||
156 | |||
157 | /* | ||
158 | * FIXME: Reset the watchdog core, in general PMIC | ||
159 | * is supposed to do this | ||
160 | */ | ||
161 | ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG, | ||
162 | DA9052_CONTROLD_WATCHDOG, 0 << 7); | ||
163 | err_strobe: | ||
164 | return ret; | ||
165 | } | ||
166 | |||
167 | static struct watchdog_info da9052_wdt_info = { | ||
168 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, | ||
169 | .identity = "DA9052 Watchdog", | ||
170 | }; | ||
171 | |||
172 | static const struct watchdog_ops da9052_wdt_ops = { | ||
173 | .owner = THIS_MODULE, | ||
174 | .start = da9052_wdt_start, | ||
175 | .stop = da9052_wdt_stop, | ||
176 | .ping = da9052_wdt_ping, | ||
177 | .set_timeout = da9052_wdt_set_timeout, | ||
178 | .ref = da9052_wdt_ref, | ||
179 | .unref = da9052_wdt_unref, | ||
180 | }; | ||
181 | |||
182 | |||
183 | static int __devinit da9052_wdt_probe(struct platform_device *pdev) | ||
184 | { | ||
185 | struct da9052 *da9052 = dev_get_drvdata(pdev->dev.parent); | ||
186 | struct da9052_wdt_data *driver_data; | ||
187 | struct watchdog_device *da9052_wdt; | ||
188 | int ret; | ||
189 | |||
190 | driver_data = devm_kzalloc(&pdev->dev, sizeof(*driver_data), | ||
191 | GFP_KERNEL); | ||
192 | if (!driver_data) { | ||
193 | dev_err(da9052->dev, "Unable to alloacate watchdog device\n"); | ||
194 | ret = -ENOMEM; | ||
195 | goto err; | ||
196 | } | ||
197 | driver_data->da9052 = da9052; | ||
198 | |||
199 | da9052_wdt = &driver_data->wdt; | ||
200 | |||
201 | da9052_wdt->timeout = DA9052_DEF_TIMEOUT; | ||
202 | da9052_wdt->info = &da9052_wdt_info; | ||
203 | da9052_wdt->ops = &da9052_wdt_ops; | ||
204 | watchdog_set_drvdata(da9052_wdt, driver_data); | ||
205 | |||
206 | kref_init(&driver_data->kref); | ||
207 | |||
208 | ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG, | ||
209 | DA9052_CONTROLD_TWDSCALE, 0); | ||
210 | if (ret < 0) { | ||
211 | dev_err(&pdev->dev, "Failed to disable watchdog bits, %d\n", | ||
212 | ret); | ||
213 | goto err; | ||
214 | } | ||
215 | |||
216 | ret = watchdog_register_device(&driver_data->wdt); | ||
217 | if (ret != 0) { | ||
218 | dev_err(da9052->dev, "watchdog_register_device() failed: %d\n", | ||
219 | ret); | ||
220 | goto err; | ||
221 | } | ||
222 | |||
223 | dev_set_drvdata(&pdev->dev, driver_data); | ||
224 | err: | ||
225 | return ret; | ||
226 | } | ||
227 | |||
228 | static int __devexit da9052_wdt_remove(struct platform_device *pdev) | ||
229 | { | ||
230 | struct da9052_wdt_data *driver_data = dev_get_drvdata(&pdev->dev); | ||
231 | |||
232 | watchdog_unregister_device(&driver_data->wdt); | ||
233 | kref_put(&driver_data->kref, da9052_wdt_release_resources); | ||
234 | |||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | static struct platform_driver da9052_wdt_driver = { | ||
239 | .probe = da9052_wdt_probe, | ||
240 | .remove = __devexit_p(da9052_wdt_remove), | ||
241 | .driver = { | ||
242 | .name = "da9052-watchdog", | ||
243 | }, | ||
244 | }; | ||
245 | |||
246 | module_platform_driver(da9052_wdt_driver); | ||
247 | |||
248 | MODULE_AUTHOR("Anthony Olech <Anthony.Olech@diasemi.com>"); | ||
249 | MODULE_DESCRIPTION("DA9052 SM Device Driver"); | ||
250 | MODULE_LICENSE("GPL"); | ||
251 | MODULE_ALIAS("platform:da9052-watchdog"); | ||
diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 741528b032e2..bc47e9012f37 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c | |||
@@ -575,7 +575,7 @@ static int __devinit iTCO_wdt_probe(struct platform_device *dev) | |||
575 | if (!request_region(iTCO_wdt_private.smi_res->start, | 575 | if (!request_region(iTCO_wdt_private.smi_res->start, |
576 | resource_size(iTCO_wdt_private.smi_res), dev->name)) { | 576 | resource_size(iTCO_wdt_private.smi_res), dev->name)) { |
577 | pr_err("I/O address 0x%04llx already in use, device disabled\n", | 577 | pr_err("I/O address 0x%04llx already in use, device disabled\n", |
578 | SMI_EN); | 578 | (u64)SMI_EN); |
579 | ret = -EBUSY; | 579 | ret = -EBUSY; |
580 | goto unmap_gcs; | 580 | goto unmap_gcs; |
581 | } | 581 | } |
@@ -592,13 +592,13 @@ static int __devinit iTCO_wdt_probe(struct platform_device *dev) | |||
592 | if (!request_region(iTCO_wdt_private.tco_res->start, | 592 | if (!request_region(iTCO_wdt_private.tco_res->start, |
593 | resource_size(iTCO_wdt_private.tco_res), dev->name)) { | 593 | resource_size(iTCO_wdt_private.tco_res), dev->name)) { |
594 | pr_err("I/O address 0x%04llx already in use, device disabled\n", | 594 | pr_err("I/O address 0x%04llx already in use, device disabled\n", |
595 | TCOBASE); | 595 | (u64)TCOBASE); |
596 | ret = -EBUSY; | 596 | ret = -EBUSY; |
597 | goto unreg_smi; | 597 | goto unreg_smi; |
598 | } | 598 | } |
599 | 599 | ||
600 | pr_info("Found a %s TCO device (Version=%d, TCOBASE=0x%04llx)\n", | 600 | pr_info("Found a %s TCO device (Version=%d, TCOBASE=0x%04llx)\n", |
601 | ich_info->name, ich_info->iTCO_version, TCOBASE); | 601 | ich_info->name, ich_info->iTCO_version, (u64)TCOBASE); |
602 | 602 | ||
603 | /* Clear out the (probably old) status */ | 603 | /* Clear out the (probably old) status */ |
604 | outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */ | 604 | outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */ |
diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c index bbb170e50055..afcd13676542 100644 --- a/drivers/watchdog/sp805_wdt.c +++ b/drivers/watchdog/sp805_wdt.c | |||
@@ -16,20 +16,17 @@ | |||
16 | #include <linux/amba/bus.h> | 16 | #include <linux/amba/bus.h> |
17 | #include <linux/bitops.h> | 17 | #include <linux/bitops.h> |
18 | #include <linux/clk.h> | 18 | #include <linux/clk.h> |
19 | #include <linux/fs.h> | ||
20 | #include <linux/init.h> | 19 | #include <linux/init.h> |
21 | #include <linux/io.h> | 20 | #include <linux/io.h> |
22 | #include <linux/ioport.h> | 21 | #include <linux/ioport.h> |
23 | #include <linux/kernel.h> | 22 | #include <linux/kernel.h> |
24 | #include <linux/math64.h> | 23 | #include <linux/math64.h> |
25 | #include <linux/miscdevice.h> | ||
26 | #include <linux/module.h> | 24 | #include <linux/module.h> |
27 | #include <linux/moduleparam.h> | 25 | #include <linux/moduleparam.h> |
28 | #include <linux/pm.h> | 26 | #include <linux/pm.h> |
29 | #include <linux/slab.h> | 27 | #include <linux/slab.h> |
30 | #include <linux/spinlock.h> | 28 | #include <linux/spinlock.h> |
31 | #include <linux/types.h> | 29 | #include <linux/types.h> |
32 | #include <linux/uaccess.h> | ||
33 | #include <linux/watchdog.h> | 30 | #include <linux/watchdog.h> |
34 | 31 | ||
35 | /* default timeout in seconds */ | 32 | /* default timeout in seconds */ |
@@ -56,6 +53,7 @@ | |||
56 | 53 | ||
57 | /** | 54 | /** |
58 | * struct sp805_wdt: sp805 wdt device structure | 55 | * struct sp805_wdt: sp805 wdt device structure |
56 | * @wdd: instance of struct watchdog_device | ||
59 | * @lock: spin lock protecting dev structure and io access | 57 | * @lock: spin lock protecting dev structure and io access |
60 | * @base: base address of wdt | 58 | * @base: base address of wdt |
61 | * @clk: clock structure of wdt | 59 | * @clk: clock structure of wdt |
@@ -65,24 +63,24 @@ | |||
65 | * @timeout: current programmed timeout | 63 | * @timeout: current programmed timeout |
66 | */ | 64 | */ |
67 | struct sp805_wdt { | 65 | struct sp805_wdt { |
66 | struct watchdog_device wdd; | ||
68 | spinlock_t lock; | 67 | spinlock_t lock; |
69 | void __iomem *base; | 68 | void __iomem *base; |
70 | struct clk *clk; | 69 | struct clk *clk; |
71 | struct amba_device *adev; | 70 | struct amba_device *adev; |
72 | unsigned long status; | ||
73 | #define WDT_BUSY 0 | ||
74 | #define WDT_CAN_BE_CLOSED 1 | ||
75 | unsigned int load_val; | 71 | unsigned int load_val; |
76 | unsigned int timeout; | 72 | unsigned int timeout; |
77 | }; | 73 | }; |
78 | 74 | ||
79 | /* local variables */ | ||
80 | static struct sp805_wdt *wdt; | ||
81 | static bool nowayout = WATCHDOG_NOWAYOUT; | 75 | static bool nowayout = WATCHDOG_NOWAYOUT; |
76 | module_param(nowayout, bool, 0); | ||
77 | MODULE_PARM_DESC(nowayout, | ||
78 | "Set to 1 to keep watchdog running after device release"); | ||
82 | 79 | ||
83 | /* This routine finds load value that will reset system in required timout */ | 80 | /* This routine finds load value that will reset system in required timout */ |
84 | static void wdt_setload(unsigned int timeout) | 81 | static int wdt_setload(struct watchdog_device *wdd, unsigned int timeout) |
85 | { | 82 | { |
83 | struct sp805_wdt *wdt = watchdog_get_drvdata(wdd); | ||
86 | u64 load, rate; | 84 | u64 load, rate; |
87 | 85 | ||
88 | rate = clk_get_rate(wdt->clk); | 86 | rate = clk_get_rate(wdt->clk); |
@@ -103,11 +101,14 @@ static void wdt_setload(unsigned int timeout) | |||
103 | /* roundup timeout to closest positive integer value */ | 101 | /* roundup timeout to closest positive integer value */ |
104 | wdt->timeout = div_u64((load + 1) * 2 + (rate / 2), rate); | 102 | wdt->timeout = div_u64((load + 1) * 2 + (rate / 2), rate); |
105 | spin_unlock(&wdt->lock); | 103 | spin_unlock(&wdt->lock); |
104 | |||
105 | return 0; | ||
106 | } | 106 | } |
107 | 107 | ||
108 | /* returns number of seconds left for reset to occur */ | 108 | /* returns number of seconds left for reset to occur */ |
109 | static u32 wdt_timeleft(void) | 109 | static unsigned int wdt_timeleft(struct watchdog_device *wdd) |
110 | { | 110 | { |
111 | struct sp805_wdt *wdt = watchdog_get_drvdata(wdd); | ||
111 | u64 load, rate; | 112 | u64 load, rate; |
112 | 113 | ||
113 | rate = clk_get_rate(wdt->clk); | 114 | rate = clk_get_rate(wdt->clk); |
@@ -123,166 +124,96 @@ static u32 wdt_timeleft(void) | |||
123 | return div_u64(load, rate); | 124 | return div_u64(load, rate); |
124 | } | 125 | } |
125 | 126 | ||
126 | /* enables watchdog timers reset */ | 127 | static int wdt_config(struct watchdog_device *wdd, bool ping) |
127 | static void wdt_enable(void) | ||
128 | { | 128 | { |
129 | spin_lock(&wdt->lock); | 129 | struct sp805_wdt *wdt = watchdog_get_drvdata(wdd); |
130 | int ret; | ||
130 | 131 | ||
131 | writel_relaxed(UNLOCK, wdt->base + WDTLOCK); | 132 | if (!ping) { |
132 | writel_relaxed(wdt->load_val, wdt->base + WDTLOAD); | 133 | ret = clk_prepare(wdt->clk); |
133 | writel_relaxed(INT_MASK, wdt->base + WDTINTCLR); | 134 | if (ret) { |
134 | writel_relaxed(INT_ENABLE | RESET_ENABLE, wdt->base + WDTCONTROL); | 135 | dev_err(&wdt->adev->dev, "clock prepare fail"); |
135 | writel_relaxed(LOCK, wdt->base + WDTLOCK); | 136 | return ret; |
137 | } | ||
136 | 138 | ||
137 | /* Flush posted writes. */ | 139 | ret = clk_enable(wdt->clk); |
138 | readl_relaxed(wdt->base + WDTLOCK); | 140 | if (ret) { |
139 | spin_unlock(&wdt->lock); | 141 | dev_err(&wdt->adev->dev, "clock enable fail"); |
140 | } | 142 | clk_unprepare(wdt->clk); |
143 | return ret; | ||
144 | } | ||
145 | } | ||
141 | 146 | ||
142 | /* disables watchdog timers reset */ | ||
143 | static void wdt_disable(void) | ||
144 | { | ||
145 | spin_lock(&wdt->lock); | 147 | spin_lock(&wdt->lock); |
146 | 148 | ||
147 | writel_relaxed(UNLOCK, wdt->base + WDTLOCK); | 149 | writel_relaxed(UNLOCK, wdt->base + WDTLOCK); |
148 | writel_relaxed(0, wdt->base + WDTCONTROL); | 150 | writel_relaxed(wdt->load_val, wdt->base + WDTLOAD); |
151 | |||
152 | if (!ping) { | ||
153 | writel_relaxed(INT_MASK, wdt->base + WDTINTCLR); | ||
154 | writel_relaxed(INT_ENABLE | RESET_ENABLE, wdt->base + | ||
155 | WDTCONTROL); | ||
156 | } | ||
157 | |||
149 | writel_relaxed(LOCK, wdt->base + WDTLOCK); | 158 | writel_relaxed(LOCK, wdt->base + WDTLOCK); |
150 | 159 | ||
151 | /* Flush posted writes. */ | 160 | /* Flush posted writes. */ |
152 | readl_relaxed(wdt->base + WDTLOCK); | 161 | readl_relaxed(wdt->base + WDTLOCK); |
153 | spin_unlock(&wdt->lock); | 162 | spin_unlock(&wdt->lock); |
163 | |||
164 | return 0; | ||
154 | } | 165 | } |
155 | 166 | ||
156 | static ssize_t sp805_wdt_write(struct file *file, const char *data, | 167 | static int wdt_ping(struct watchdog_device *wdd) |
157 | size_t len, loff_t *ppos) | ||
158 | { | 168 | { |
159 | if (len) { | 169 | return wdt_config(wdd, true); |
160 | if (!nowayout) { | ||
161 | size_t i; | ||
162 | |||
163 | clear_bit(WDT_CAN_BE_CLOSED, &wdt->status); | ||
164 | |||
165 | for (i = 0; i != len; i++) { | ||
166 | char c; | ||
167 | |||
168 | if (get_user(c, data + i)) | ||
169 | return -EFAULT; | ||
170 | /* Check for Magic Close character */ | ||
171 | if (c == 'V') { | ||
172 | set_bit(WDT_CAN_BE_CLOSED, | ||
173 | &wdt->status); | ||
174 | break; | ||
175 | } | ||
176 | } | ||
177 | } | ||
178 | wdt_enable(); | ||
179 | } | ||
180 | return len; | ||
181 | } | 170 | } |
182 | 171 | ||
183 | static const struct watchdog_info ident = { | 172 | /* enables watchdog timers reset */ |
184 | .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, | 173 | static int wdt_enable(struct watchdog_device *wdd) |
185 | .identity = MODULE_NAME, | ||
186 | }; | ||
187 | |||
188 | static long sp805_wdt_ioctl(struct file *file, unsigned int cmd, | ||
189 | unsigned long arg) | ||
190 | { | 174 | { |
191 | int ret = -ENOTTY; | 175 | return wdt_config(wdd, false); |
192 | unsigned int timeout; | ||
193 | |||
194 | switch (cmd) { | ||
195 | case WDIOC_GETSUPPORT: | ||
196 | ret = copy_to_user((struct watchdog_info *)arg, &ident, | ||
197 | sizeof(ident)) ? -EFAULT : 0; | ||
198 | break; | ||
199 | |||
200 | case WDIOC_GETSTATUS: | ||
201 | ret = put_user(0, (int *)arg); | ||
202 | break; | ||
203 | |||
204 | case WDIOC_KEEPALIVE: | ||
205 | wdt_enable(); | ||
206 | ret = 0; | ||
207 | break; | ||
208 | |||
209 | case WDIOC_SETTIMEOUT: | ||
210 | ret = get_user(timeout, (unsigned int *)arg); | ||
211 | if (ret) | ||
212 | break; | ||
213 | |||
214 | wdt_setload(timeout); | ||
215 | |||
216 | wdt_enable(); | ||
217 | /* Fall through */ | ||
218 | |||
219 | case WDIOC_GETTIMEOUT: | ||
220 | ret = put_user(wdt->timeout, (unsigned int *)arg); | ||
221 | break; | ||
222 | case WDIOC_GETTIMELEFT: | ||
223 | ret = put_user(wdt_timeleft(), (unsigned int *)arg); | ||
224 | break; | ||
225 | } | ||
226 | return ret; | ||
227 | } | 176 | } |
228 | 177 | ||
229 | static int sp805_wdt_open(struct inode *inode, struct file *file) | 178 | /* disables watchdog timers reset */ |
179 | static int wdt_disable(struct watchdog_device *wdd) | ||
230 | { | 180 | { |
231 | int ret = 0; | 181 | struct sp805_wdt *wdt = watchdog_get_drvdata(wdd); |
232 | |||
233 | if (test_and_set_bit(WDT_BUSY, &wdt->status)) | ||
234 | return -EBUSY; | ||
235 | |||
236 | ret = clk_enable(wdt->clk); | ||
237 | if (ret) { | ||
238 | dev_err(&wdt->adev->dev, "clock enable fail"); | ||
239 | goto err; | ||
240 | } | ||
241 | |||
242 | wdt_enable(); | ||
243 | 182 | ||
244 | /* can not be closed, once enabled */ | 183 | spin_lock(&wdt->lock); |
245 | clear_bit(WDT_CAN_BE_CLOSED, &wdt->status); | ||
246 | return nonseekable_open(inode, file); | ||
247 | 184 | ||
248 | err: | 185 | writel_relaxed(UNLOCK, wdt->base + WDTLOCK); |
249 | clear_bit(WDT_BUSY, &wdt->status); | 186 | writel_relaxed(0, wdt->base + WDTCONTROL); |
250 | return ret; | 187 | writel_relaxed(LOCK, wdt->base + WDTLOCK); |
251 | } | ||
252 | 188 | ||
253 | static int sp805_wdt_release(struct inode *inode, struct file *file) | 189 | /* Flush posted writes. */ |
254 | { | 190 | readl_relaxed(wdt->base + WDTLOCK); |
255 | if (!test_bit(WDT_CAN_BE_CLOSED, &wdt->status)) { | 191 | spin_unlock(&wdt->lock); |
256 | clear_bit(WDT_BUSY, &wdt->status); | ||
257 | dev_warn(&wdt->adev->dev, "Device closed unexpectedly\n"); | ||
258 | return 0; | ||
259 | } | ||
260 | 192 | ||
261 | wdt_disable(); | ||
262 | clk_disable(wdt->clk); | 193 | clk_disable(wdt->clk); |
263 | clear_bit(WDT_BUSY, &wdt->status); | 194 | clk_unprepare(wdt->clk); |
264 | 195 | ||
265 | return 0; | 196 | return 0; |
266 | } | 197 | } |
267 | 198 | ||
268 | static const struct file_operations sp805_wdt_fops = { | 199 | static const struct watchdog_info wdt_info = { |
269 | .owner = THIS_MODULE, | 200 | .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, |
270 | .llseek = no_llseek, | 201 | .identity = MODULE_NAME, |
271 | .write = sp805_wdt_write, | ||
272 | .unlocked_ioctl = sp805_wdt_ioctl, | ||
273 | .open = sp805_wdt_open, | ||
274 | .release = sp805_wdt_release, | ||
275 | }; | 202 | }; |
276 | 203 | ||
277 | static struct miscdevice sp805_wdt_miscdev = { | 204 | static const struct watchdog_ops wdt_ops = { |
278 | .minor = WATCHDOG_MINOR, | 205 | .owner = THIS_MODULE, |
279 | .name = "watchdog", | 206 | .start = wdt_enable, |
280 | .fops = &sp805_wdt_fops, | 207 | .stop = wdt_disable, |
208 | .ping = wdt_ping, | ||
209 | .set_timeout = wdt_setload, | ||
210 | .get_timeleft = wdt_timeleft, | ||
281 | }; | 211 | }; |
282 | 212 | ||
283 | static int __devinit | 213 | static int __devinit |
284 | sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id) | 214 | sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id) |
285 | { | 215 | { |
216 | struct sp805_wdt *wdt; | ||
286 | int ret = 0; | 217 | int ret = 0; |
287 | 218 | ||
288 | if (!devm_request_mem_region(&adev->dev, adev->res.start, | 219 | if (!devm_request_mem_region(&adev->dev, adev->res.start, |
@@ -315,19 +246,26 @@ sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id) | |||
315 | } | 246 | } |
316 | 247 | ||
317 | wdt->adev = adev; | 248 | wdt->adev = adev; |
249 | wdt->wdd.info = &wdt_info; | ||
250 | wdt->wdd.ops = &wdt_ops; | ||
251 | |||
318 | spin_lock_init(&wdt->lock); | 252 | spin_lock_init(&wdt->lock); |
319 | wdt_setload(DEFAULT_TIMEOUT); | 253 | watchdog_set_nowayout(&wdt->wdd, nowayout); |
254 | watchdog_set_drvdata(&wdt->wdd, wdt); | ||
255 | wdt_setload(&wdt->wdd, DEFAULT_TIMEOUT); | ||
320 | 256 | ||
321 | ret = misc_register(&sp805_wdt_miscdev); | 257 | ret = watchdog_register_device(&wdt->wdd); |
322 | if (ret < 0) { | 258 | if (ret) { |
323 | dev_warn(&adev->dev, "cannot register misc device\n"); | 259 | dev_err(&adev->dev, "watchdog_register_device() failed: %d\n", |
324 | goto err_misc_register; | 260 | ret); |
261 | goto err_register; | ||
325 | } | 262 | } |
263 | amba_set_drvdata(adev, wdt); | ||
326 | 264 | ||
327 | dev_info(&adev->dev, "registration successful\n"); | 265 | dev_info(&adev->dev, "registration successful\n"); |
328 | return 0; | 266 | return 0; |
329 | 267 | ||
330 | err_misc_register: | 268 | err_register: |
331 | clk_put(wdt->clk); | 269 | clk_put(wdt->clk); |
332 | err: | 270 | err: |
333 | dev_err(&adev->dev, "Probe Failed!!!\n"); | 271 | dev_err(&adev->dev, "Probe Failed!!!\n"); |
@@ -336,7 +274,11 @@ err: | |||
336 | 274 | ||
337 | static int __devexit sp805_wdt_remove(struct amba_device *adev) | 275 | static int __devexit sp805_wdt_remove(struct amba_device *adev) |
338 | { | 276 | { |
339 | misc_deregister(&sp805_wdt_miscdev); | 277 | struct sp805_wdt *wdt = amba_get_drvdata(adev); |
278 | |||
279 | watchdog_unregister_device(&wdt->wdd); | ||
280 | amba_set_drvdata(adev, NULL); | ||
281 | watchdog_set_drvdata(&wdt->wdd, NULL); | ||
340 | clk_put(wdt->clk); | 282 | clk_put(wdt->clk); |
341 | 283 | ||
342 | return 0; | 284 | return 0; |
@@ -345,28 +287,22 @@ static int __devexit sp805_wdt_remove(struct amba_device *adev) | |||
345 | #ifdef CONFIG_PM | 287 | #ifdef CONFIG_PM |
346 | static int sp805_wdt_suspend(struct device *dev) | 288 | static int sp805_wdt_suspend(struct device *dev) |
347 | { | 289 | { |
348 | if (test_bit(WDT_BUSY, &wdt->status)) { | 290 | struct sp805_wdt *wdt = dev_get_drvdata(dev); |
349 | wdt_disable(); | 291 | |
350 | clk_disable(wdt->clk); | 292 | if (watchdog_active(&wdt->wdd)) |
351 | } | 293 | return wdt_disable(&wdt->wdd); |
352 | 294 | ||
353 | return 0; | 295 | return 0; |
354 | } | 296 | } |
355 | 297 | ||
356 | static int sp805_wdt_resume(struct device *dev) | 298 | static int sp805_wdt_resume(struct device *dev) |
357 | { | 299 | { |
358 | int ret = 0; | 300 | struct sp805_wdt *wdt = dev_get_drvdata(dev); |
359 | 301 | ||
360 | if (test_bit(WDT_BUSY, &wdt->status)) { | 302 | if (watchdog_active(&wdt->wdd)) |
361 | ret = clk_enable(wdt->clk); | 303 | return wdt_enable(&wdt->wdd); |
362 | if (ret) { | ||
363 | dev_err(dev, "clock enable fail"); | ||
364 | return ret; | ||
365 | } | ||
366 | wdt_enable(); | ||
367 | } | ||
368 | 304 | ||
369 | return ret; | 305 | return 0; |
370 | } | 306 | } |
371 | #endif /* CONFIG_PM */ | 307 | #endif /* CONFIG_PM */ |
372 | 308 | ||
@@ -395,11 +331,6 @@ static struct amba_driver sp805_wdt_driver = { | |||
395 | 331 | ||
396 | module_amba_driver(sp805_wdt_driver); | 332 | module_amba_driver(sp805_wdt_driver); |
397 | 333 | ||
398 | module_param(nowayout, bool, 0); | ||
399 | MODULE_PARM_DESC(nowayout, | ||
400 | "Set to 1 to keep watchdog running after device release"); | ||
401 | |||
402 | MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>"); | 334 | MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>"); |
403 | MODULE_DESCRIPTION("ARM SP805 Watchdog Driver"); | 335 | MODULE_DESCRIPTION("ARM SP805 Watchdog Driver"); |
404 | MODULE_LICENSE("GPL"); | 336 | MODULE_LICENSE("GPL"); |
405 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
diff --git a/drivers/watchdog/via_wdt.c b/drivers/watchdog/via_wdt.c index 5603e31afdab..aa50da3ccfe3 100644 --- a/drivers/watchdog/via_wdt.c +++ b/drivers/watchdog/via_wdt.c | |||
@@ -91,7 +91,7 @@ static inline void wdt_reset(void) | |||
91 | static void wdt_timer_tick(unsigned long data) | 91 | static void wdt_timer_tick(unsigned long data) |
92 | { | 92 | { |
93 | if (time_before(jiffies, next_heartbeat) || | 93 | if (time_before(jiffies, next_heartbeat) || |
94 | (!test_bit(WDOG_ACTIVE, &wdt_dev.status))) { | 94 | (!watchdog_active(&wdt_dev))) { |
95 | wdt_reset(); | 95 | wdt_reset(); |
96 | mod_timer(&timer, jiffies + WDT_HEARTBEAT); | 96 | mod_timer(&timer, jiffies + WDT_HEARTBEAT); |
97 | } else | 97 | } else |
diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index 14d768bfa267..6aa46a90ff02 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c | |||
@@ -34,8 +34,13 @@ | |||
34 | #include <linux/kernel.h> /* For printk/panic/... */ | 34 | #include <linux/kernel.h> /* For printk/panic/... */ |
35 | #include <linux/watchdog.h> /* For watchdog specific items */ | 35 | #include <linux/watchdog.h> /* For watchdog specific items */ |
36 | #include <linux/init.h> /* For __init/__exit/... */ | 36 | #include <linux/init.h> /* For __init/__exit/... */ |
37 | #include <linux/idr.h> /* For ida_* macros */ | ||
38 | #include <linux/err.h> /* For IS_ERR macros */ | ||
37 | 39 | ||
38 | #include "watchdog_dev.h" /* For watchdog_dev_register/... */ | 40 | #include "watchdog_core.h" /* For watchdog_dev_register/... */ |
41 | |||
42 | static DEFINE_IDA(watchdog_ida); | ||
43 | static struct class *watchdog_class; | ||
39 | 44 | ||
40 | /** | 45 | /** |
41 | * watchdog_register_device() - register a watchdog device | 46 | * watchdog_register_device() - register a watchdog device |
@@ -49,7 +54,7 @@ | |||
49 | */ | 54 | */ |
50 | int watchdog_register_device(struct watchdog_device *wdd) | 55 | int watchdog_register_device(struct watchdog_device *wdd) |
51 | { | 56 | { |
52 | int ret; | 57 | int ret, id, devno; |
53 | 58 | ||
54 | if (wdd == NULL || wdd->info == NULL || wdd->ops == NULL) | 59 | if (wdd == NULL || wdd->info == NULL || wdd->ops == NULL) |
55 | return -EINVAL; | 60 | return -EINVAL; |
@@ -74,10 +79,38 @@ int watchdog_register_device(struct watchdog_device *wdd) | |||
74 | * corrupted in a later stage then we expect a kernel panic! | 79 | * corrupted in a later stage then we expect a kernel panic! |
75 | */ | 80 | */ |
76 | 81 | ||
77 | /* We only support 1 watchdog device via the /dev/watchdog interface */ | 82 | mutex_init(&wdd->lock); |
83 | id = ida_simple_get(&watchdog_ida, 0, MAX_DOGS, GFP_KERNEL); | ||
84 | if (id < 0) | ||
85 | return id; | ||
86 | wdd->id = id; | ||
87 | |||
78 | ret = watchdog_dev_register(wdd); | 88 | ret = watchdog_dev_register(wdd); |
79 | if (ret) { | 89 | if (ret) { |
80 | pr_err("error registering /dev/watchdog (err=%d)\n", ret); | 90 | ida_simple_remove(&watchdog_ida, id); |
91 | if (!(id == 0 && ret == -EBUSY)) | ||
92 | return ret; | ||
93 | |||
94 | /* Retry in case a legacy watchdog module exists */ | ||
95 | id = ida_simple_get(&watchdog_ida, 1, MAX_DOGS, GFP_KERNEL); | ||
96 | if (id < 0) | ||
97 | return id; | ||
98 | wdd->id = id; | ||
99 | |||
100 | ret = watchdog_dev_register(wdd); | ||
101 | if (ret) { | ||
102 | ida_simple_remove(&watchdog_ida, id); | ||
103 | return ret; | ||
104 | } | ||
105 | } | ||
106 | |||
107 | devno = wdd->cdev.dev; | ||
108 | wdd->dev = device_create(watchdog_class, wdd->parent, devno, | ||
109 | NULL, "watchdog%d", wdd->id); | ||
110 | if (IS_ERR(wdd->dev)) { | ||
111 | watchdog_dev_unregister(wdd); | ||
112 | ida_simple_remove(&watchdog_ida, id); | ||
113 | ret = PTR_ERR(wdd->dev); | ||
81 | return ret; | 114 | return ret; |
82 | } | 115 | } |
83 | 116 | ||
@@ -95,6 +128,7 @@ EXPORT_SYMBOL_GPL(watchdog_register_device); | |||
95 | void watchdog_unregister_device(struct watchdog_device *wdd) | 128 | void watchdog_unregister_device(struct watchdog_device *wdd) |
96 | { | 129 | { |
97 | int ret; | 130 | int ret; |
131 | int devno = wdd->cdev.dev; | ||
98 | 132 | ||
99 | if (wdd == NULL) | 133 | if (wdd == NULL) |
100 | return; | 134 | return; |
@@ -102,9 +136,41 @@ void watchdog_unregister_device(struct watchdog_device *wdd) | |||
102 | ret = watchdog_dev_unregister(wdd); | 136 | ret = watchdog_dev_unregister(wdd); |
103 | if (ret) | 137 | if (ret) |
104 | pr_err("error unregistering /dev/watchdog (err=%d)\n", ret); | 138 | pr_err("error unregistering /dev/watchdog (err=%d)\n", ret); |
139 | device_destroy(watchdog_class, devno); | ||
140 | ida_simple_remove(&watchdog_ida, wdd->id); | ||
141 | wdd->dev = NULL; | ||
105 | } | 142 | } |
106 | EXPORT_SYMBOL_GPL(watchdog_unregister_device); | 143 | EXPORT_SYMBOL_GPL(watchdog_unregister_device); |
107 | 144 | ||
145 | static int __init watchdog_init(void) | ||
146 | { | ||
147 | int err; | ||
148 | |||
149 | watchdog_class = class_create(THIS_MODULE, "watchdog"); | ||
150 | if (IS_ERR(watchdog_class)) { | ||
151 | pr_err("couldn't create class\n"); | ||
152 | return PTR_ERR(watchdog_class); | ||
153 | } | ||
154 | |||
155 | err = watchdog_dev_init(); | ||
156 | if (err < 0) { | ||
157 | class_destroy(watchdog_class); | ||
158 | return err; | ||
159 | } | ||
160 | |||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | static void __exit watchdog_exit(void) | ||
165 | { | ||
166 | watchdog_dev_exit(); | ||
167 | class_destroy(watchdog_class); | ||
168 | ida_destroy(&watchdog_ida); | ||
169 | } | ||
170 | |||
171 | subsys_initcall(watchdog_init); | ||
172 | module_exit(watchdog_exit); | ||
173 | |||
108 | MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>"); | 174 | MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>"); |
109 | MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>"); | 175 | MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>"); |
110 | MODULE_DESCRIPTION("WatchDog Timer Driver Core"); | 176 | MODULE_DESCRIPTION("WatchDog Timer Driver Core"); |
diff --git a/drivers/watchdog/watchdog_dev.h b/drivers/watchdog/watchdog_core.h index bc7612be25ce..6c951418fca7 100644 --- a/drivers/watchdog/watchdog_dev.h +++ b/drivers/watchdog/watchdog_core.h | |||
@@ -26,8 +26,12 @@ | |||
26 | * This material is provided "AS-IS" and at no charge. | 26 | * This material is provided "AS-IS" and at no charge. |
27 | */ | 27 | */ |
28 | 28 | ||
29 | #define MAX_DOGS 32 /* Maximum number of watchdog devices */ | ||
30 | |||
29 | /* | 31 | /* |
30 | * Functions/procedures to be called by the core | 32 | * Functions/procedures to be called by the core |
31 | */ | 33 | */ |
32 | int watchdog_dev_register(struct watchdog_device *); | 34 | extern int watchdog_dev_register(struct watchdog_device *); |
33 | int watchdog_dev_unregister(struct watchdog_device *); | 35 | extern int watchdog_dev_unregister(struct watchdog_device *); |
36 | extern int __init watchdog_dev_init(void); | ||
37 | extern void __exit watchdog_dev_exit(void); | ||
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 8558da912c42..672d169bf1da 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c | |||
@@ -42,10 +42,12 @@ | |||
42 | #include <linux/init.h> /* For __init/__exit/... */ | 42 | #include <linux/init.h> /* For __init/__exit/... */ |
43 | #include <linux/uaccess.h> /* For copy_to_user/put_user/... */ | 43 | #include <linux/uaccess.h> /* For copy_to_user/put_user/... */ |
44 | 44 | ||
45 | /* make sure we only register one /dev/watchdog device */ | 45 | #include "watchdog_core.h" |
46 | static unsigned long watchdog_dev_busy; | 46 | |
47 | /* the dev_t structure to store the dynamically allocated watchdog devices */ | ||
48 | static dev_t watchdog_devt; | ||
47 | /* the watchdog device behind /dev/watchdog */ | 49 | /* the watchdog device behind /dev/watchdog */ |
48 | static struct watchdog_device *wdd; | 50 | static struct watchdog_device *old_wdd; |
49 | 51 | ||
50 | /* | 52 | /* |
51 | * watchdog_ping: ping the watchdog. | 53 | * watchdog_ping: ping the watchdog. |
@@ -59,13 +61,26 @@ static struct watchdog_device *wdd; | |||
59 | 61 | ||
60 | static int watchdog_ping(struct watchdog_device *wddev) | 62 | static int watchdog_ping(struct watchdog_device *wddev) |
61 | { | 63 | { |
62 | if (test_bit(WDOG_ACTIVE, &wddev->status)) { | 64 | int err = 0; |
63 | if (wddev->ops->ping) | 65 | |
64 | return wddev->ops->ping(wddev); /* ping the watchdog */ | 66 | mutex_lock(&wddev->lock); |
65 | else | 67 | |
66 | return wddev->ops->start(wddev); /* restart watchdog */ | 68 | if (test_bit(WDOG_UNREGISTERED, &wddev->status)) { |
69 | err = -ENODEV; | ||
70 | goto out_ping; | ||
67 | } | 71 | } |
68 | return 0; | 72 | |
73 | if (!watchdog_active(wddev)) | ||
74 | goto out_ping; | ||
75 | |||
76 | if (wddev->ops->ping) | ||
77 | err = wddev->ops->ping(wddev); /* ping the watchdog */ | ||
78 | else | ||
79 | err = wddev->ops->start(wddev); /* restart watchdog */ | ||
80 | |||
81 | out_ping: | ||
82 | mutex_unlock(&wddev->lock); | ||
83 | return err; | ||
69 | } | 84 | } |
70 | 85 | ||
71 | /* | 86 | /* |
@@ -79,16 +94,25 @@ static int watchdog_ping(struct watchdog_device *wddev) | |||
79 | 94 | ||
80 | static int watchdog_start(struct watchdog_device *wddev) | 95 | static int watchdog_start(struct watchdog_device *wddev) |
81 | { | 96 | { |
82 | int err; | 97 | int err = 0; |
83 | 98 | ||
84 | if (!test_bit(WDOG_ACTIVE, &wddev->status)) { | 99 | mutex_lock(&wddev->lock); |
85 | err = wddev->ops->start(wddev); | ||
86 | if (err < 0) | ||
87 | return err; | ||
88 | 100 | ||
89 | set_bit(WDOG_ACTIVE, &wddev->status); | 101 | if (test_bit(WDOG_UNREGISTERED, &wddev->status)) { |
102 | err = -ENODEV; | ||
103 | goto out_start; | ||
90 | } | 104 | } |
91 | return 0; | 105 | |
106 | if (watchdog_active(wddev)) | ||
107 | goto out_start; | ||
108 | |||
109 | err = wddev->ops->start(wddev); | ||
110 | if (err == 0) | ||
111 | set_bit(WDOG_ACTIVE, &wddev->status); | ||
112 | |||
113 | out_start: | ||
114 | mutex_unlock(&wddev->lock); | ||
115 | return err; | ||
92 | } | 116 | } |
93 | 117 | ||
94 | /* | 118 | /* |
@@ -103,22 +127,155 @@ static int watchdog_start(struct watchdog_device *wddev) | |||
103 | 127 | ||
104 | static int watchdog_stop(struct watchdog_device *wddev) | 128 | static int watchdog_stop(struct watchdog_device *wddev) |
105 | { | 129 | { |
106 | int err = -EBUSY; | 130 | int err = 0; |
107 | 131 | ||
108 | if (test_bit(WDOG_NO_WAY_OUT, &wddev->status)) { | 132 | mutex_lock(&wddev->lock); |
109 | pr_info("%s: nowayout prevents watchdog to be stopped!\n", | 133 | |
110 | wddev->info->identity); | 134 | if (test_bit(WDOG_UNREGISTERED, &wddev->status)) { |
111 | return err; | 135 | err = -ENODEV; |
136 | goto out_stop; | ||
112 | } | 137 | } |
113 | 138 | ||
114 | if (test_bit(WDOG_ACTIVE, &wddev->status)) { | 139 | if (!watchdog_active(wddev)) |
115 | err = wddev->ops->stop(wddev); | 140 | goto out_stop; |
116 | if (err < 0) | ||
117 | return err; | ||
118 | 141 | ||
142 | if (test_bit(WDOG_NO_WAY_OUT, &wddev->status)) { | ||
143 | dev_info(wddev->dev, "nowayout prevents watchdog being stopped!\n"); | ||
144 | err = -EBUSY; | ||
145 | goto out_stop; | ||
146 | } | ||
147 | |||
148 | err = wddev->ops->stop(wddev); | ||
149 | if (err == 0) | ||
119 | clear_bit(WDOG_ACTIVE, &wddev->status); | 150 | clear_bit(WDOG_ACTIVE, &wddev->status); |
151 | |||
152 | out_stop: | ||
153 | mutex_unlock(&wddev->lock); | ||
154 | return err; | ||
155 | } | ||
156 | |||
157 | /* | ||
158 | * watchdog_get_status: wrapper to get the watchdog status | ||
159 | * @wddev: the watchdog device to get the status from | ||
160 | * @status: the status of the watchdog device | ||
161 | * | ||
162 | * Get the watchdog's status flags. | ||
163 | */ | ||
164 | |||
165 | static int watchdog_get_status(struct watchdog_device *wddev, | ||
166 | unsigned int *status) | ||
167 | { | ||
168 | int err = 0; | ||
169 | |||
170 | *status = 0; | ||
171 | if (!wddev->ops->status) | ||
172 | return -EOPNOTSUPP; | ||
173 | |||
174 | mutex_lock(&wddev->lock); | ||
175 | |||
176 | if (test_bit(WDOG_UNREGISTERED, &wddev->status)) { | ||
177 | err = -ENODEV; | ||
178 | goto out_status; | ||
120 | } | 179 | } |
121 | return 0; | 180 | |
181 | *status = wddev->ops->status(wddev); | ||
182 | |||
183 | out_status: | ||
184 | mutex_unlock(&wddev->lock); | ||
185 | return err; | ||
186 | } | ||
187 | |||
188 | /* | ||
189 | * watchdog_set_timeout: set the watchdog timer timeout | ||
190 | * @wddev: the watchdog device to set the timeout for | ||
191 | * @timeout: timeout to set in seconds | ||
192 | */ | ||
193 | |||
194 | static int watchdog_set_timeout(struct watchdog_device *wddev, | ||
195 | unsigned int timeout) | ||
196 | { | ||
197 | int err; | ||
198 | |||
199 | if ((wddev->ops->set_timeout == NULL) || | ||
200 | !(wddev->info->options & WDIOF_SETTIMEOUT)) | ||
201 | return -EOPNOTSUPP; | ||
202 | |||
203 | if ((wddev->max_timeout != 0) && | ||
204 | (timeout < wddev->min_timeout || timeout > wddev->max_timeout)) | ||
205 | return -EINVAL; | ||
206 | |||
207 | mutex_lock(&wddev->lock); | ||
208 | |||
209 | if (test_bit(WDOG_UNREGISTERED, &wddev->status)) { | ||
210 | err = -ENODEV; | ||
211 | goto out_timeout; | ||
212 | } | ||
213 | |||
214 | err = wddev->ops->set_timeout(wddev, timeout); | ||
215 | |||
216 | out_timeout: | ||
217 | mutex_unlock(&wddev->lock); | ||
218 | return err; | ||
219 | } | ||
220 | |||
221 | /* | ||
222 | * watchdog_get_timeleft: wrapper to get the time left before a reboot | ||
223 | * @wddev: the watchdog device to get the remaining time from | ||
224 | * @timeleft: the time that's left | ||
225 | * | ||
226 | * Get the time before a watchdog will reboot (if not pinged). | ||
227 | */ | ||
228 | |||
229 | static int watchdog_get_timeleft(struct watchdog_device *wddev, | ||
230 | unsigned int *timeleft) | ||
231 | { | ||
232 | int err = 0; | ||
233 | |||
234 | *timeleft = 0; | ||
235 | if (!wddev->ops->get_timeleft) | ||
236 | return -EOPNOTSUPP; | ||
237 | |||
238 | mutex_lock(&wddev->lock); | ||
239 | |||
240 | if (test_bit(WDOG_UNREGISTERED, &wddev->status)) { | ||
241 | err = -ENODEV; | ||
242 | goto out_timeleft; | ||
243 | } | ||
244 | |||
245 | *timeleft = wddev->ops->get_timeleft(wddev); | ||
246 | |||
247 | out_timeleft: | ||
248 | mutex_unlock(&wddev->lock); | ||
249 | return err; | ||
250 | } | ||
251 | |||
252 | /* | ||
253 | * watchdog_ioctl_op: call the watchdog drivers ioctl op if defined | ||
254 | * @wddev: the watchdog device to do the ioctl on | ||
255 | * @cmd: watchdog command | ||
256 | * @arg: argument pointer | ||
257 | */ | ||
258 | |||
259 | static int watchdog_ioctl_op(struct watchdog_device *wddev, unsigned int cmd, | ||
260 | unsigned long arg) | ||
261 | { | ||
262 | int err; | ||
263 | |||
264 | if (!wddev->ops->ioctl) | ||
265 | return -ENOIOCTLCMD; | ||
266 | |||
267 | mutex_lock(&wddev->lock); | ||
268 | |||
269 | if (test_bit(WDOG_UNREGISTERED, &wddev->status)) { | ||
270 | err = -ENODEV; | ||
271 | goto out_ioctl; | ||
272 | } | ||
273 | |||
274 | err = wddev->ops->ioctl(wddev, cmd, arg); | ||
275 | |||
276 | out_ioctl: | ||
277 | mutex_unlock(&wddev->lock); | ||
278 | return err; | ||
122 | } | 279 | } |
123 | 280 | ||
124 | /* | 281 | /* |
@@ -136,6 +293,7 @@ static int watchdog_stop(struct watchdog_device *wddev) | |||
136 | static ssize_t watchdog_write(struct file *file, const char __user *data, | 293 | static ssize_t watchdog_write(struct file *file, const char __user *data, |
137 | size_t len, loff_t *ppos) | 294 | size_t len, loff_t *ppos) |
138 | { | 295 | { |
296 | struct watchdog_device *wdd = file->private_data; | ||
139 | size_t i; | 297 | size_t i; |
140 | char c; | 298 | char c; |
141 | 299 | ||
@@ -175,23 +333,24 @@ static ssize_t watchdog_write(struct file *file, const char __user *data, | |||
175 | static long watchdog_ioctl(struct file *file, unsigned int cmd, | 333 | static long watchdog_ioctl(struct file *file, unsigned int cmd, |
176 | unsigned long arg) | 334 | unsigned long arg) |
177 | { | 335 | { |
336 | struct watchdog_device *wdd = file->private_data; | ||
178 | void __user *argp = (void __user *)arg; | 337 | void __user *argp = (void __user *)arg; |
179 | int __user *p = argp; | 338 | int __user *p = argp; |
180 | unsigned int val; | 339 | unsigned int val; |
181 | int err; | 340 | int err; |
182 | 341 | ||
183 | if (wdd->ops->ioctl) { | 342 | err = watchdog_ioctl_op(wdd, cmd, arg); |
184 | err = wdd->ops->ioctl(wdd, cmd, arg); | 343 | if (err != -ENOIOCTLCMD) |
185 | if (err != -ENOIOCTLCMD) | 344 | return err; |
186 | return err; | ||
187 | } | ||
188 | 345 | ||
189 | switch (cmd) { | 346 | switch (cmd) { |
190 | case WDIOC_GETSUPPORT: | 347 | case WDIOC_GETSUPPORT: |
191 | return copy_to_user(argp, wdd->info, | 348 | return copy_to_user(argp, wdd->info, |
192 | sizeof(struct watchdog_info)) ? -EFAULT : 0; | 349 | sizeof(struct watchdog_info)) ? -EFAULT : 0; |
193 | case WDIOC_GETSTATUS: | 350 | case WDIOC_GETSTATUS: |
194 | val = wdd->ops->status ? wdd->ops->status(wdd) : 0; | 351 | err = watchdog_get_status(wdd, &val); |
352 | if (err) | ||
353 | return err; | ||
195 | return put_user(val, p); | 354 | return put_user(val, p); |
196 | case WDIOC_GETBOOTSTATUS: | 355 | case WDIOC_GETBOOTSTATUS: |
197 | return put_user(wdd->bootstatus, p); | 356 | return put_user(wdd->bootstatus, p); |
@@ -215,15 +374,9 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, | |||
215 | watchdog_ping(wdd); | 374 | watchdog_ping(wdd); |
216 | return 0; | 375 | return 0; |
217 | case WDIOC_SETTIMEOUT: | 376 | case WDIOC_SETTIMEOUT: |
218 | if ((wdd->ops->set_timeout == NULL) || | ||
219 | !(wdd->info->options & WDIOF_SETTIMEOUT)) | ||
220 | return -EOPNOTSUPP; | ||
221 | if (get_user(val, p)) | 377 | if (get_user(val, p)) |
222 | return -EFAULT; | 378 | return -EFAULT; |
223 | if ((wdd->max_timeout != 0) && | 379 | err = watchdog_set_timeout(wdd, val); |
224 | (val < wdd->min_timeout || val > wdd->max_timeout)) | ||
225 | return -EINVAL; | ||
226 | err = wdd->ops->set_timeout(wdd, val); | ||
227 | if (err < 0) | 380 | if (err < 0) |
228 | return err; | 381 | return err; |
229 | /* If the watchdog is active then we send a keepalive ping | 382 | /* If the watchdog is active then we send a keepalive ping |
@@ -237,21 +390,21 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, | |||
237 | return -EOPNOTSUPP; | 390 | return -EOPNOTSUPP; |
238 | return put_user(wdd->timeout, p); | 391 | return put_user(wdd->timeout, p); |
239 | case WDIOC_GETTIMELEFT: | 392 | case WDIOC_GETTIMELEFT: |
240 | if (!wdd->ops->get_timeleft) | 393 | err = watchdog_get_timeleft(wdd, &val); |
241 | return -EOPNOTSUPP; | 394 | if (err) |
242 | 395 | return err; | |
243 | return put_user(wdd->ops->get_timeleft(wdd), p); | 396 | return put_user(val, p); |
244 | default: | 397 | default: |
245 | return -ENOTTY; | 398 | return -ENOTTY; |
246 | } | 399 | } |
247 | } | 400 | } |
248 | 401 | ||
249 | /* | 402 | /* |
250 | * watchdog_open: open the /dev/watchdog device. | 403 | * watchdog_open: open the /dev/watchdog* devices. |
251 | * @inode: inode of device | 404 | * @inode: inode of device |
252 | * @file: file handle to device | 405 | * @file: file handle to device |
253 | * | 406 | * |
254 | * When the /dev/watchdog device gets opened, we start the watchdog. | 407 | * When the /dev/watchdog* device gets opened, we start the watchdog. |
255 | * Watch out: the /dev/watchdog device is single open, so we make sure | 408 | * Watch out: the /dev/watchdog device is single open, so we make sure |
256 | * it can only be opened once. | 409 | * it can only be opened once. |
257 | */ | 410 | */ |
@@ -259,6 +412,13 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, | |||
259 | static int watchdog_open(struct inode *inode, struct file *file) | 412 | static int watchdog_open(struct inode *inode, struct file *file) |
260 | { | 413 | { |
261 | int err = -EBUSY; | 414 | int err = -EBUSY; |
415 | struct watchdog_device *wdd; | ||
416 | |||
417 | /* Get the corresponding watchdog device */ | ||
418 | if (imajor(inode) == MISC_MAJOR) | ||
419 | wdd = old_wdd; | ||
420 | else | ||
421 | wdd = container_of(inode->i_cdev, struct watchdog_device, cdev); | ||
262 | 422 | ||
263 | /* the watchdog is single open! */ | 423 | /* the watchdog is single open! */ |
264 | if (test_and_set_bit(WDOG_DEV_OPEN, &wdd->status)) | 424 | if (test_and_set_bit(WDOG_DEV_OPEN, &wdd->status)) |
@@ -275,6 +435,11 @@ static int watchdog_open(struct inode *inode, struct file *file) | |||
275 | if (err < 0) | 435 | if (err < 0) |
276 | goto out_mod; | 436 | goto out_mod; |
277 | 437 | ||
438 | file->private_data = wdd; | ||
439 | |||
440 | if (wdd->ops->ref) | ||
441 | wdd->ops->ref(wdd); | ||
442 | |||
278 | /* dev/watchdog is a virtual (and thus non-seekable) filesystem */ | 443 | /* dev/watchdog is a virtual (and thus non-seekable) filesystem */ |
279 | return nonseekable_open(inode, file); | 444 | return nonseekable_open(inode, file); |
280 | 445 | ||
@@ -286,9 +451,9 @@ out: | |||
286 | } | 451 | } |
287 | 452 | ||
288 | /* | 453 | /* |
289 | * watchdog_release: release the /dev/watchdog device. | 454 | * watchdog_release: release the watchdog device. |
290 | * @inode: inode of device | 455 | * @inode: inode of device |
291 | * @file: file handle to device | 456 | * @file: file handle to device |
292 | * | 457 | * |
293 | * This is the code for when /dev/watchdog gets closed. We will only | 458 | * This is the code for when /dev/watchdog gets closed. We will only |
294 | * stop the watchdog when we have received the magic char (and nowayout | 459 | * stop the watchdog when we have received the magic char (and nowayout |
@@ -297,6 +462,7 @@ out: | |||
297 | 462 | ||
298 | static int watchdog_release(struct inode *inode, struct file *file) | 463 | static int watchdog_release(struct inode *inode, struct file *file) |
299 | { | 464 | { |
465 | struct watchdog_device *wdd = file->private_data; | ||
300 | int err = -EBUSY; | 466 | int err = -EBUSY; |
301 | 467 | ||
302 | /* | 468 | /* |
@@ -310,7 +476,10 @@ static int watchdog_release(struct inode *inode, struct file *file) | |||
310 | 476 | ||
311 | /* If the watchdog was not stopped, send a keepalive ping */ | 477 | /* If the watchdog was not stopped, send a keepalive ping */ |
312 | if (err < 0) { | 478 | if (err < 0) { |
313 | pr_crit("%s: watchdog did not stop!\n", wdd->info->identity); | 479 | mutex_lock(&wdd->lock); |
480 | if (!test_bit(WDOG_UNREGISTERED, &wdd->status)) | ||
481 | dev_crit(wdd->dev, "watchdog did not stop!\n"); | ||
482 | mutex_unlock(&wdd->lock); | ||
314 | watchdog_ping(wdd); | 483 | watchdog_ping(wdd); |
315 | } | 484 | } |
316 | 485 | ||
@@ -320,6 +489,10 @@ static int watchdog_release(struct inode *inode, struct file *file) | |||
320 | /* make sure that /dev/watchdog can be re-opened */ | 489 | /* make sure that /dev/watchdog can be re-opened */ |
321 | clear_bit(WDOG_DEV_OPEN, &wdd->status); | 490 | clear_bit(WDOG_DEV_OPEN, &wdd->status); |
322 | 491 | ||
492 | /* Note wdd may be gone after this, do not use after this! */ | ||
493 | if (wdd->ops->unref) | ||
494 | wdd->ops->unref(wdd); | ||
495 | |||
323 | return 0; | 496 | return 0; |
324 | } | 497 | } |
325 | 498 | ||
@@ -338,62 +511,92 @@ static struct miscdevice watchdog_miscdev = { | |||
338 | }; | 511 | }; |
339 | 512 | ||
340 | /* | 513 | /* |
341 | * watchdog_dev_register: | 514 | * watchdog_dev_register: register a watchdog device |
342 | * @watchdog: watchdog device | 515 | * @watchdog: watchdog device |
343 | * | 516 | * |
344 | * Register a watchdog device as /dev/watchdog. /dev/watchdog | 517 | * Register a watchdog device including handling the legacy |
345 | * is actually a miscdevice and thus we set it up like that. | 518 | * /dev/watchdog node. /dev/watchdog is actually a miscdevice and |
519 | * thus we set it up like that. | ||
346 | */ | 520 | */ |
347 | 521 | ||
348 | int watchdog_dev_register(struct watchdog_device *watchdog) | 522 | int watchdog_dev_register(struct watchdog_device *watchdog) |
349 | { | 523 | { |
350 | int err; | 524 | int err, devno; |
351 | 525 | ||
352 | /* Only one device can register for /dev/watchdog */ | 526 | if (watchdog->id == 0) { |
353 | if (test_and_set_bit(0, &watchdog_dev_busy)) { | 527 | watchdog_miscdev.parent = watchdog->parent; |
354 | pr_err("only one watchdog can use /dev/watchdog\n"); | 528 | err = misc_register(&watchdog_miscdev); |
355 | return -EBUSY; | 529 | if (err != 0) { |
530 | pr_err("%s: cannot register miscdev on minor=%d (err=%d).\n", | ||
531 | watchdog->info->identity, WATCHDOG_MINOR, err); | ||
532 | if (err == -EBUSY) | ||
533 | pr_err("%s: a legacy watchdog module is probably present.\n", | ||
534 | watchdog->info->identity); | ||
535 | return err; | ||
536 | } | ||
537 | old_wdd = watchdog; | ||
356 | } | 538 | } |
357 | 539 | ||
358 | wdd = watchdog; | 540 | /* Fill in the data structures */ |
359 | 541 | devno = MKDEV(MAJOR(watchdog_devt), watchdog->id); | |
360 | err = misc_register(&watchdog_miscdev); | 542 | cdev_init(&watchdog->cdev, &watchdog_fops); |
361 | if (err != 0) { | 543 | watchdog->cdev.owner = watchdog->ops->owner; |
362 | pr_err("%s: cannot register miscdev on minor=%d (err=%d)\n", | 544 | |
363 | watchdog->info->identity, WATCHDOG_MINOR, err); | 545 | /* Add the device */ |
364 | goto out; | 546 | err = cdev_add(&watchdog->cdev, devno, 1); |
547 | if (err) { | ||
548 | pr_err("watchdog%d unable to add device %d:%d\n", | ||
549 | watchdog->id, MAJOR(watchdog_devt), watchdog->id); | ||
550 | if (watchdog->id == 0) { | ||
551 | misc_deregister(&watchdog_miscdev); | ||
552 | old_wdd = NULL; | ||
553 | } | ||
365 | } | 554 | } |
366 | |||
367 | return 0; | ||
368 | |||
369 | out: | ||
370 | wdd = NULL; | ||
371 | clear_bit(0, &watchdog_dev_busy); | ||
372 | return err; | 555 | return err; |
373 | } | 556 | } |
374 | 557 | ||
375 | /* | 558 | /* |
376 | * watchdog_dev_unregister: | 559 | * watchdog_dev_unregister: unregister a watchdog device |
377 | * @watchdog: watchdog device | 560 | * @watchdog: watchdog device |
378 | * | 561 | * |
379 | * Deregister the /dev/watchdog device. | 562 | * Unregister the watchdog and if needed the legacy /dev/watchdog device. |
380 | */ | 563 | */ |
381 | 564 | ||
382 | int watchdog_dev_unregister(struct watchdog_device *watchdog) | 565 | int watchdog_dev_unregister(struct watchdog_device *watchdog) |
383 | { | 566 | { |
384 | /* Check that a watchdog device was registered in the past */ | 567 | mutex_lock(&watchdog->lock); |
385 | if (!test_bit(0, &watchdog_dev_busy) || !wdd) | 568 | set_bit(WDOG_UNREGISTERED, &watchdog->status); |
386 | return -ENODEV; | 569 | mutex_unlock(&watchdog->lock); |
387 | 570 | ||
388 | /* We can only unregister the watchdog device that was registered */ | 571 | cdev_del(&watchdog->cdev); |
389 | if (watchdog != wdd) { | 572 | if (watchdog->id == 0) { |
390 | pr_err("%s: watchdog was not registered as /dev/watchdog\n", | 573 | misc_deregister(&watchdog_miscdev); |
391 | watchdog->info->identity); | 574 | old_wdd = NULL; |
392 | return -ENODEV; | ||
393 | } | 575 | } |
394 | |||
395 | misc_deregister(&watchdog_miscdev); | ||
396 | wdd = NULL; | ||
397 | clear_bit(0, &watchdog_dev_busy); | ||
398 | return 0; | 576 | return 0; |
399 | } | 577 | } |
578 | |||
579 | /* | ||
580 | * watchdog_dev_init: init dev part of watchdog core | ||
581 | * | ||
582 | * Allocate a range of chardev nodes to use for watchdog devices | ||
583 | */ | ||
584 | |||
585 | int __init watchdog_dev_init(void) | ||
586 | { | ||
587 | int err = alloc_chrdev_region(&watchdog_devt, 0, MAX_DOGS, "watchdog"); | ||
588 | if (err < 0) | ||
589 | pr_err("watchdog: unable to allocate char dev region\n"); | ||
590 | return err; | ||
591 | } | ||
592 | |||
593 | /* | ||
594 | * watchdog_dev_exit: exit dev part of watchdog core | ||
595 | * | ||
596 | * Release the range of chardev nodes used for watchdog devices | ||
597 | */ | ||
598 | |||
599 | void __exit watchdog_dev_exit(void) | ||
600 | { | ||
601 | unregister_chrdev_region(watchdog_devt, MAX_DOGS); | ||
602 | } | ||
diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index ac40716b44e9..da70f0facd2b 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h | |||
@@ -45,6 +45,8 @@ struct watchdog_info { | |||
45 | #define WDIOF_SETTIMEOUT 0x0080 /* Set timeout (in seconds) */ | 45 | #define WDIOF_SETTIMEOUT 0x0080 /* Set timeout (in seconds) */ |
46 | #define WDIOF_MAGICCLOSE 0x0100 /* Supports magic close char */ | 46 | #define WDIOF_MAGICCLOSE 0x0100 /* Supports magic close char */ |
47 | #define WDIOF_PRETIMEOUT 0x0200 /* Pretimeout (in seconds), get/set */ | 47 | #define WDIOF_PRETIMEOUT 0x0200 /* Pretimeout (in seconds), get/set */ |
48 | #define WDIOF_ALARMONLY 0x0400 /* Watchdog triggers a management or | ||
49 | other external alarm not a reboot */ | ||
48 | #define WDIOF_KEEPALIVEPING 0x8000 /* Keep alive ping reply */ | 50 | #define WDIOF_KEEPALIVEPING 0x8000 /* Keep alive ping reply */ |
49 | 51 | ||
50 | #define WDIOS_DISABLECARD 0x0001 /* Turn off the watchdog timer */ | 52 | #define WDIOS_DISABLECARD 0x0001 /* Turn off the watchdog timer */ |
@@ -54,6 +56,8 @@ struct watchdog_info { | |||
54 | #ifdef __KERNEL__ | 56 | #ifdef __KERNEL__ |
55 | 57 | ||
56 | #include <linux/bitops.h> | 58 | #include <linux/bitops.h> |
59 | #include <linux/device.h> | ||
60 | #include <linux/cdev.h> | ||
57 | 61 | ||
58 | struct watchdog_ops; | 62 | struct watchdog_ops; |
59 | struct watchdog_device; | 63 | struct watchdog_device; |
@@ -67,6 +71,8 @@ struct watchdog_device; | |||
67 | * @status: The routine that shows the status of the watchdog device. | 71 | * @status: The routine that shows the status of the watchdog device. |
68 | * @set_timeout:The routine for setting the watchdog devices timeout value. | 72 | * @set_timeout:The routine for setting the watchdog devices timeout value. |
69 | * @get_timeleft:The routine that get's the time that's left before a reset. | 73 | * @get_timeleft:The routine that get's the time that's left before a reset. |
74 | * @ref: The ref operation for dyn. allocated watchdog_device structs | ||
75 | * @unref: The unref operation for dyn. allocated watchdog_device structs | ||
70 | * @ioctl: The routines that handles extra ioctl calls. | 76 | * @ioctl: The routines that handles extra ioctl calls. |
71 | * | 77 | * |
72 | * The watchdog_ops structure contains a list of low-level operations | 78 | * The watchdog_ops structure contains a list of low-level operations |
@@ -84,11 +90,17 @@ struct watchdog_ops { | |||
84 | unsigned int (*status)(struct watchdog_device *); | 90 | unsigned int (*status)(struct watchdog_device *); |
85 | int (*set_timeout)(struct watchdog_device *, unsigned int); | 91 | int (*set_timeout)(struct watchdog_device *, unsigned int); |
86 | unsigned int (*get_timeleft)(struct watchdog_device *); | 92 | unsigned int (*get_timeleft)(struct watchdog_device *); |
93 | void (*ref)(struct watchdog_device *); | ||
94 | void (*unref)(struct watchdog_device *); | ||
87 | long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); | 95 | long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); |
88 | }; | 96 | }; |
89 | 97 | ||
90 | /** struct watchdog_device - The structure that defines a watchdog device | 98 | /** struct watchdog_device - The structure that defines a watchdog device |
91 | * | 99 | * |
100 | * @id: The watchdog's ID. (Allocated by watchdog_register_device) | ||
101 | * @cdev: The watchdog's Character device. | ||
102 | * @dev: The device for our watchdog | ||
103 | * @parent: The parent bus device | ||
92 | * @info: Pointer to a watchdog_info structure. | 104 | * @info: Pointer to a watchdog_info structure. |
93 | * @ops: Pointer to the list of watchdog operations. | 105 | * @ops: Pointer to the list of watchdog operations. |
94 | * @bootstatus: Status of the watchdog device at boot. | 106 | * @bootstatus: Status of the watchdog device at boot. |
@@ -96,6 +108,7 @@ struct watchdog_ops { | |||
96 | * @min_timeout:The watchdog devices minimum timeout value. | 108 | * @min_timeout:The watchdog devices minimum timeout value. |
97 | * @max_timeout:The watchdog devices maximum timeout value. | 109 | * @max_timeout:The watchdog devices maximum timeout value. |
98 | * @driver-data:Pointer to the drivers private data. | 110 | * @driver-data:Pointer to the drivers private data. |
111 | * @lock: Lock for watchdog core internal use only. | ||
99 | * @status: Field that contains the devices internal status bits. | 112 | * @status: Field that contains the devices internal status bits. |
100 | * | 113 | * |
101 | * The watchdog_device structure contains all information about a | 114 | * The watchdog_device structure contains all information about a |
@@ -103,8 +116,15 @@ struct watchdog_ops { | |||
103 | * | 116 | * |
104 | * The driver-data field may not be accessed directly. It must be accessed | 117 | * The driver-data field may not be accessed directly. It must be accessed |
105 | * via the watchdog_set_drvdata and watchdog_get_drvdata helpers. | 118 | * via the watchdog_set_drvdata and watchdog_get_drvdata helpers. |
119 | * | ||
120 | * The lock field is for watchdog core internal use only and should not be | ||
121 | * touched. | ||
106 | */ | 122 | */ |
107 | struct watchdog_device { | 123 | struct watchdog_device { |
124 | int id; | ||
125 | struct cdev cdev; | ||
126 | struct device *dev; | ||
127 | struct device *parent; | ||
108 | const struct watchdog_info *info; | 128 | const struct watchdog_info *info; |
109 | const struct watchdog_ops *ops; | 129 | const struct watchdog_ops *ops; |
110 | unsigned int bootstatus; | 130 | unsigned int bootstatus; |
@@ -112,12 +132,14 @@ struct watchdog_device { | |||
112 | unsigned int min_timeout; | 132 | unsigned int min_timeout; |
113 | unsigned int max_timeout; | 133 | unsigned int max_timeout; |
114 | void *driver_data; | 134 | void *driver_data; |
135 | struct mutex lock; | ||
115 | unsigned long status; | 136 | unsigned long status; |
116 | /* Bit numbers for status flags */ | 137 | /* Bit numbers for status flags */ |
117 | #define WDOG_ACTIVE 0 /* Is the watchdog running/active */ | 138 | #define WDOG_ACTIVE 0 /* Is the watchdog running/active */ |
118 | #define WDOG_DEV_OPEN 1 /* Opened via /dev/watchdog ? */ | 139 | #define WDOG_DEV_OPEN 1 /* Opened via /dev/watchdog ? */ |
119 | #define WDOG_ALLOW_RELEASE 2 /* Did we receive the magic char ? */ | 140 | #define WDOG_ALLOW_RELEASE 2 /* Did we receive the magic char ? */ |
120 | #define WDOG_NO_WAY_OUT 3 /* Is 'nowayout' feature set ? */ | 141 | #define WDOG_NO_WAY_OUT 3 /* Is 'nowayout' feature set ? */ |
142 | #define WDOG_UNREGISTERED 4 /* Has the device been unregistered */ | ||
121 | }; | 143 | }; |
122 | 144 | ||
123 | #ifdef CONFIG_WATCHDOG_NOWAYOUT | 145 | #ifdef CONFIG_WATCHDOG_NOWAYOUT |
@@ -128,6 +150,12 @@ struct watchdog_device { | |||
128 | #define WATCHDOG_NOWAYOUT_INIT_STATUS 0 | 150 | #define WATCHDOG_NOWAYOUT_INIT_STATUS 0 |
129 | #endif | 151 | #endif |
130 | 152 | ||
153 | /* Use the following function to check wether or not the watchdog is active */ | ||
154 | static inline bool watchdog_active(struct watchdog_device *wdd) | ||
155 | { | ||
156 | return test_bit(WDOG_ACTIVE, &wdd->status); | ||
157 | } | ||
158 | |||
131 | /* Use the following function to set the nowayout feature */ | 159 | /* Use the following function to set the nowayout feature */ |
132 | static inline void watchdog_set_nowayout(struct watchdog_device *wdd, bool nowayout) | 160 | static inline void watchdog_set_nowayout(struct watchdog_device *wdd, bool nowayout) |
133 | { | 161 | { |