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 | { |
