diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-30 09:59:13 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-30 09:59:13 -0700 |
| commit | 19ce0a995ff230b364c7329fd488c70e91c171d9 (patch) | |
| tree | e24fe57a87d16a54cbffd087fb6af2083f7f2ad0 | |
| parent | 6bb340c7868fbfd7bd0e8a0e23397a2bcb528429 (diff) | |
| parent | 4b98b32aefb2c143a40cdaa254eb905f1bb06b5d (diff) | |
| download | linux-19ce0a995ff230b364c7329fd488c70e91c171d9.tar.gz linux-19ce0a995ff230b364c7329fd488c70e91c171d9.tar.bz2 linux-19ce0a995ff230b364c7329fd488c70e91c171d9.zip | |
Merge git://www.linux-watchdog.org/linux-watchdog
Pull second set of watchdog updates from Wim Van Sebroeck:
"This changeset contains following changes:
* Add support for multiple watchdog devices. We use dynamically
allocated device id's for this.
* Add locking into the generic watchdog infrastructure.
* Add support for dynamically allocated watchdog_device structs so
that we can deal with devices that get unbound.
* convert following drivers to the generic watchdog framework:
sch5627, sch5636 and sp805_wdt.
* Add DA9052/53 PMIC watchdog support
* Fix printk format warnings for iTCO_wdt.c"
* git://www.linux-watchdog.org/linux-watchdog:
watchdog: iTCO_wdt.c: fix printk format warnings
watchdog: sp805_wdt: Add clk_{un}prepare support
watchdog: sp805_wdt: convert to watchdog core
hwmon/sch56xx: Depend on watchdog for watchdog core functions
watchdog: sch56xx-common: set correct bits in register()
Watchdog: DA9052/53 PMIC watchdog support
watchdog: sch56xx-common: Add proper ref-counting of watchdog data
watchdog: sch56xx: Remove unnecessary checks for register changes
watchdog: sch56xx: Use watchdog core
watchdog: Add support for dynamically allocated watchdog_device structs
watchdog: Add Locking support
watchdog: watchdog_dev: Rewrite wrapper code
watchdog: use dev_ functions
watchdog: create all the proper device files
watchdog: Add a flag to indicate the watchdog doesn't reboot things
watchdog: Add multiple device support
watchdog: watchdog_core.h: make functions extern
watchdog: correct the name of the watchdog_core inlude file
watchdog: Add watchdog_active() routine
watchdog: watchdog_dev: include private header to pickup global symbol prototypes
| -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 @@ The Linux WatchDog Timer Driver Core kernel API. =============================================== -Last reviewed: 16-Mar-2012 +Last reviewed: 22-May-2012 Wim Van Sebroeck <wim@iguana.be> @@ -39,6 +39,10 @@ watchdog_device structure. The watchdog device structure looks like this: struct watchdog_device { + int id; + struct cdev cdev; + struct device *dev; + struct device *parent; const struct watchdog_info *info; const struct watchdog_ops *ops; unsigned int bootstatus; @@ -46,10 +50,20 @@ struct watchdog_device { unsigned int min_timeout; unsigned int max_timeout; void *driver_data; + struct mutex lock; unsigned long status; }; It contains following fields: +* id: set by watchdog_register_device, id 0 is special. It has both a + /dev/watchdog0 cdev (dynamic major, minor 0) as well as the old + /dev/watchdog miscdev. The id is set automatically when calling + watchdog_register_device. +* cdev: cdev for the dynamic /dev/watchdog<id> device nodes. This + field is also populated by watchdog_register_device. +* dev: device under the watchdog class (created by watchdog_register_device). +* parent: set this to the parent device (or NULL) before calling + watchdog_register_device. * info: a pointer to a watchdog_info structure. This structure gives some additional information about the watchdog timer itself. (Like it's unique name) * ops: a pointer to the list of watchdog operations that the watchdog supports. @@ -61,6 +75,7 @@ It contains following fields: * driver_data: a pointer to the drivers private data of a watchdog device. This data should only be accessed via the watchdog_set_drvdata and watchdog_get_drvdata routines. +* lock: Mutex for WatchDog Timer Driver Core internal use only. * status: this field contains a number of status bits that give extra information about the status of the device (Like: is the watchdog timer running/active, is the nowayout bit set, is the device opened via @@ -78,6 +93,8 @@ struct watchdog_ops { unsigned int (*status)(struct watchdog_device *); int (*set_timeout)(struct watchdog_device *, unsigned int); unsigned int (*get_timeleft)(struct watchdog_device *); + void (*ref)(struct watchdog_device *); + void (*unref)(struct watchdog_device *); long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); }; @@ -85,6 +102,21 @@ It is important that you first define the module owner of the watchdog timer driver's operations. This module owner will be used to lock the module when the watchdog is active. (This to avoid a system crash when you unload the module and /dev/watchdog is still open). + +If the watchdog_device struct is dynamically allocated, just locking the module +is not enough and a driver also needs to define the ref and unref operations to +ensure the structure holding the watchdog_device does not go away. + +The simplest (and usually sufficient) implementation of this is to: +1) Add a kref struct to the same structure which is holding the watchdog_device +2) Define a release callback for the kref which frees the struct holding both +3) Call kref_init on this kref *before* calling watchdog_register_device() +4) Define a ref operation calling kref_get on this kref +5) Define a unref operation calling kref_put on this kref +6) When it is time to cleanup: + * Do not kfree() the struct holding both, the last kref_put will do this! + * *After* calling watchdog_unregister_device() call kref_put on the kref + Some operations are mandatory and some are optional. The mandatory operations are: * 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: (Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the watchdog's info structure). * get_timeleft: this routines returns the time that's left before a reset. +* ref: the operation that calls kref_get on the kref of a dynamically + allocated watchdog_device struct. +* unref: the operation that calls kref_put on the kref of a dynamically + allocated watchdog_device struct. * ioctl: if this routine is present then it will be called first before we do our own internal ioctl call handling. This routine should return -ENOIOCTLCMD 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: (This bit should only be used by the WatchDog Timer Driver Core). * WDOG_NO_WAY_OUT: this bit stores the nowayout setting for the watchdog. If this bit is set then the watchdog timer will not be able to stop. +* WDOG_UNREGISTERED: this bit gets set by the WatchDog Timer Driver Core + after calling watchdog_unregister_device, and then checked before calling + any watchdog_ops, so that you can be sure that no operations (other then + unref) will get called after unregister, even if userspace still holds a + reference to /dev/watchdog To set the WDOG_NO_WAY_OUT status bit (before registering your watchdog 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 wd1_timeout: Default watchdog1 timeout in 1/10secs wd2_timeout: Default watchdog2 timeout in 1/10secs ------------------------------------------------- +da9052wdt: +timeout: Watchdog timeout in seconds. 2<= timeout <=131, default=2.048s +nowayout: Watchdog cannot be stopped once started + (default=kernel config parameter) +------------------------------------------------- davinci_wdt: heartbeat: Watchdog heartbeat period in seconds from 1 to 600, default 60 ------------------------------------------------- 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 config SENSORS_SCH5627 tristate "SMSC SCH5627" - depends on !PPC + depends on !PPC && WATCHDOG select SENSORS_SCH56XX_COMMON + select WATCHDOG_CORE help If you say yes here you get support for the hardware monitoring features of the SMSC SCH5627 Super-I/O chip including support for @@ -1048,8 +1049,9 @@ config SENSORS_SCH5627 config SENSORS_SCH5636 tristate "SMSC SCH5636" - depends on !PPC + depends on !PPC && WATCHDOG select SENSORS_SCH56XX_COMMON + select WATCHDOG_CORE help SMSC SCH5636 Super I/O chips include an embedded microcontroller for 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) } /* Note failing to register the watchdog is not a fatal error */ - data->watchdog = sch56xx_watchdog_register(data->addr, + data->watchdog = sch56xx_watchdog_register(&pdev->dev, data->addr, (build_code << 24) | (build_id << 8) | hwmon_rev, &data->update_lock, 1); 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) } /* Note failing to register the watchdog is not a fatal error */ - data->watchdog = sch56xx_watchdog_register(data->addr, + data->watchdog = sch56xx_watchdog_register(&pdev->dev, data->addr, (revision[0] << 8) | revision[1], &data->update_lock, 0); 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=" struct sch56xx_watchdog_data { u16 addr; - u32 revision; struct mutex *io_lock; - struct mutex watchdog_lock; - struct list_head list; /* member of the watchdog_data_list */ struct kref kref; - struct miscdevice watchdog_miscdev; - unsigned long watchdog_is_open; - char watchdog_name[10]; /* must be unique to avoid sysfs conflict */ - char watchdog_expect_close; + struct watchdog_info wdinfo; + struct watchdog_device wddev; u8 watchdog_preset; u8 watchdog_control; u8 watchdog_output_enable; @@ -82,15 +77,6 @@ struct sch56xx_watchdog_data { static struct platform_device *sch56xx_pdev; -/* - * Somewhat ugly :( global data pointer list with all sch56xx devices, so that - * we can find our device data as when using misc_register there is no other - * method to get to ones device data from the open fop. - */ -static LIST_HEAD(watchdog_data_list); -/* Note this lock not only protect list access, but also data.kref access */ -static DEFINE_MUTEX(watchdog_data_mutex); - /* Super I/O functions */ static inline int superio_inb(int base, int reg) { @@ -272,22 +258,22 @@ EXPORT_SYMBOL(sch56xx_read_virtual_reg12); * Watchdog routines */ -/* - * Release our data struct when the platform device has been released *and* - * all references to our watchdog device are released. - */ -static void sch56xx_watchdog_release_resources(struct kref *r) +/* Release our data struct when we're unregistered *and* + all references to our watchdog device are released */ +static void watchdog_release_resources(struct kref *r) { struct sch56xx_watchdog_data *data = container_of(r, struct sch56xx_watchdog_data, kref); kfree(data); } -static int watchdog_set_timeout(struct sch56xx_watchdog_data *data, - int timeout) +static int watchdog_set_timeout(struct watchdog_device *wddev, + unsigned int timeout) { - int ret, resolution; + struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev); + unsigned int resolution; u8 control; + int ret; /* 1 second or 60 second resolution? */ if (timeout <= 255) @@ -298,12 +284,6 @@ static int watchdog_set_timeout(struct sch56xx_watchdog_data *data, if (timeout < resolution || timeout > (resolution * 255)) return -EINVAL; - mutex_lock(&data->watchdog_lock); - if (!data->addr) { - ret = -ENODEV; - goto leave; - } - if (resolution == 1) control = data->watchdog_control | SCH56XX_WDOG_TIME_BASE_SEC; else @@ -316,7 +296,7 @@ static int watchdog_set_timeout(struct sch56xx_watchdog_data *data, control); mutex_unlock(data->io_lock); if (ret) - goto leave; + return ret; data->watchdog_control = control; } @@ -326,38 +306,17 @@ static int watchdog_set_timeout(struct sch56xx_watchdog_data *data, * the watchdog countdown. */ data->watchdog_preset = DIV_ROUND_UP(timeout, resolution); + wddev->timeout = data->watchdog_preset * resolution; - ret = data->watchdog_preset * resolution; -leave: - mutex_unlock(&data->watchdog_lock); - return ret; -} - -static int watchdog_get_timeout(struct sch56xx_watchdog_data *data) -{ - int timeout; - - mutex_lock(&data->watchdog_lock); - if (data->watchdog_control & SCH56XX_WDOG_TIME_BASE_SEC) - timeout = data->watchdog_preset; - else - timeout = data->watchdog_preset * 60; - mutex_unlock(&data->watchdog_lock); - - return timeout; + return 0; } -static int watchdog_start(struct sch56xx_watchdog_data *data) +static int watchdog_start(struct watchdog_device *wddev) { + struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev); int ret; u8 val; - mutex_lock(&data->watchdog_lock); - if (!data->addr) { - ret = -ENODEV; - goto leave_unlock_watchdog; - } - /* * The sch56xx's watchdog cannot really be started / stopped * it is always running, but we can avoid the timer expiring @@ -385,18 +344,14 @@ static int watchdog_start(struct sch56xx_watchdog_data *data) if (ret) goto leave; - /* 2. Enable output (if not already enabled) */ - if (!(data->watchdog_output_enable & SCH56XX_WDOG_OUTPUT_ENABLE)) { - val = data->watchdog_output_enable | - SCH56XX_WDOG_OUTPUT_ENABLE; - ret = sch56xx_write_virtual_reg(data->addr, - SCH56XX_REG_WDOG_OUTPUT_ENABLE, - val); - if (ret) - goto leave; + /* 2. Enable output */ + val = data->watchdog_output_enable | SCH56XX_WDOG_OUTPUT_ENABLE; + ret = sch56xx_write_virtual_reg(data->addr, + SCH56XX_REG_WDOG_OUTPUT_ENABLE, val); + if (ret) + goto leave; - data->watchdog_output_enable = val; - } + data->watchdog_output_enable = val; /* 3. Clear the watchdog event bit if set */ val = inb(data->addr + 9); @@ -405,234 +360,70 @@ static int watchdog_start(struct sch56xx_watchdog_data *data) leave: mutex_unlock(data->io_lock); -leave_unlock_watchdog: - mutex_unlock(&data->watchdog_lock); return ret; } -static int watchdog_trigger(struct sch56xx_watchdog_data *data) +static int watchdog_trigger(struct watchdog_device *wddev) { + struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev); int ret; - mutex_lock(&data->watchdog_lock); - if (!data->addr) { - ret = -ENODEV; - goto leave; - } - /* Reset the watchdog countdown counter */ mutex_lock(data->io_lock); ret = sch56xx_write_virtual_reg(data->addr, SCH56XX_REG_WDOG_PRESET, data->watchdog_preset); mutex_unlock(data->io_lock); -leave: - mutex_unlock(&data->watchdog_lock); + return ret; } -static int watchdog_stop_unlocked(struct sch56xx_watchdog_data *data) +static int watchdog_stop(struct watchdog_device *wddev) { + struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev); int ret = 0; u8 val; - if (!data->addr) - return -ENODEV; - - if (data->watchdog_output_enable & SCH56XX_WDOG_OUTPUT_ENABLE) { - val = data->watchdog_output_enable & - ~SCH56XX_WDOG_OUTPUT_ENABLE; - mutex_lock(data->io_lock); - ret = sch56xx_write_virtual_reg(data->addr, - SCH56XX_REG_WDOG_OUTPUT_ENABLE, - val); - mutex_unlock(data->io_lock); - if (ret) - return ret; - - data->watchdog_output_enable = val; - } - - return ret; -} - -static int watchdog_stop(struct sch56xx_watchdog_data *data) -{ - int ret; - - mutex_lock(&data->watchdog_lock); - ret = watchdog_stop_unlocked(data); - mutex_unlock(&data->watchdog_lock); - - return ret; -} - -static int watchdog_release(struct inode *inode, struct file *filp) -{ - struct sch56xx_watchdog_data *data = filp->private_data; - - if (data->watchdog_expect_close) { - watchdog_stop(data); - data->watchdog_expect_close = 0; - } else { - watchdog_trigger(data); - pr_crit("unexpected close, not stopping watchdog!\n"); - } - - clear_bit(0, &data->watchdog_is_open); - - mutex_lock(&watchdog_data_mutex); - kref_put(&data->kref, sch56xx_watchdog_release_resources); - mutex_unlock(&watchdog_data_mutex); + val = data->watchdog_output_enable & ~SCH56XX_WDOG_OUTPUT_ENABLE; + mutex_lock(data->io_lock); + ret = sch56xx_write_virtual_reg(data->addr, + SCH56XX_REG_WDOG_OUTPUT_ENABLE, val); + mutex_unlock(data->io_lock); + if (ret) + return ret; + data->watchdog_output_enable = val; return 0; } -static int watchdog_open(struct inode *inode, struct file *filp) +static void watchdog_ref(struct watchdog_device *wddev) { - struct sch56xx_watchdog_data *pos, *data = NULL; - int ret, watchdog_is_open; - - /* - * We get called from drivers/char/misc.c with misc_mtx hold, and we - * call misc_register() from sch56xx_watchdog_probe() with - * watchdog_data_mutex hold, as misc_register() takes the misc_mtx - * lock, this is a possible deadlock, so we use mutex_trylock here. - */ - if (!mutex_trylock(&watchdog_data_mutex)) - return -ERESTARTSYS; - list_for_each_entry(pos, &watchdog_data_list, list) { - if (pos->watchdog_miscdev.minor == iminor(inode)) { - data = pos; - break; - } - } - /* Note we can never not have found data, so we don't check for this */ - watchdog_is_open = test_and_set_bit(0, &data->watchdog_is_open); - if (!watchdog_is_open) - kref_get(&data->kref); - mutex_unlock(&watchdog_data_mutex); - - if (watchdog_is_open) - return -EBUSY; - - filp->private_data = data; - - /* Start the watchdog */ - ret = watchdog_start(data); - if (ret) { - watchdog_release(inode, filp); - return ret; - } + struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev); - return nonseekable_open(inode, filp); + kref_get(&data->kref); } -static ssize_t watchdog_write(struct file *filp, const char __user *buf, - size_t count, loff_t *offset) +static void watchdog_unref(struct watchdog_device *wddev) { - int ret; - struct sch56xx_watchdog_data *data = filp->private_data; - - if (count) { - if (!nowayout) { - size_t i; - - /* Clear it in case it was set with a previous write */ - data->watchdog_expect_close = 0; - - for (i = 0; i != count; i++) { - char c; - if (get_user(c, buf + i)) - return -EFAULT; - if (c == 'V') - data->watchdog_expect_close = 1; - } - } - ret = watchdog_trigger(data); - if (ret) - return ret; - } - return count; -} - -static long watchdog_ioctl(struct file *filp, unsigned int cmd, - unsigned long arg) -{ - struct watchdog_info ident = { - .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, - .identity = "sch56xx watchdog" - }; - int i, ret = 0; - struct sch56xx_watchdog_data *data = filp->private_data; - - switch (cmd) { - case WDIOC_GETSUPPORT: - ident.firmware_version = data->revision; - if (!nowayout) - ident.options |= WDIOF_MAGICCLOSE; - if (copy_to_user((void __user *)arg, &ident, sizeof(ident))) - ret = -EFAULT; - break; - - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - ret = put_user(0, (int __user *)arg); - break; - - case WDIOC_KEEPALIVE: - ret = watchdog_trigger(data); - break; + struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev); - case WDIOC_GETTIMEOUT: - i = watchdog_get_timeout(data); - ret = put_user(i, (int __user *)arg); - break; - - case WDIOC_SETTIMEOUT: - if (get_user(i, (int __user *)arg)) { - ret = -EFAULT; - break; - } - ret = watchdog_set_timeout(data, i); - if (ret >= 0) - ret = put_user(ret, (int __user *)arg); - break; - - case WDIOC_SETOPTIONS: - if (get_user(i, (int __user *)arg)) { - ret = -EFAULT; - break; - } - - if (i & WDIOS_DISABLECARD) - ret = watchdog_stop(data); - else if (i & WDIOS_ENABLECARD) - ret = watchdog_trigger(data); - else - ret = -EINVAL; - break; - - default: - ret = -ENOTTY; - } - return ret; + kref_put(&data->kref, watchdog_release_resources); } -static const struct file_operations watchdog_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .open = watchdog_open, - .release = watchdog_release, - .write = watchdog_write, - .unlocked_ioctl = watchdog_ioctl, +static const struct watchdog_ops watchdog_ops = { + .owner = THIS_MODULE, + .start = watchdog_start, + .stop = watchdog_stop, + .ping = watchdog_trigger, + .set_timeout = watchdog_set_timeout, + .ref = watchdog_ref, + .unref = watchdog_unref, }; -struct sch56xx_watchdog_data *sch56xx_watchdog_register( +struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent, u16 addr, u32 revision, struct mutex *io_lock, int check_enabled) { struct sch56xx_watchdog_data *data; - int i, err, control, output_enable; - const int watchdog_minors[] = { WATCHDOG_MINOR, 212, 213, 214, 215 }; + int err, control, output_enable; /* Cache the watchdog registers */ mutex_lock(io_lock); @@ -656,82 +447,55 @@ struct sch56xx_watchdog_data *sch56xx_watchdog_register( return NULL; data->addr = addr; - data->revision = revision; data->io_lock = io_lock; - data->watchdog_control = control; - data->watchdog_output_enable = output_enable; - mutex_init(&data->watchdog_lock); - INIT_LIST_HEAD(&data->list); kref_init(&data->kref); - err = watchdog_set_timeout(data, 60); - if (err < 0) - goto error; - - /* - * We take the data_mutex lock early so that watchdog_open() cannot - * run when misc_register() has completed, but we've not yet added - * our data to the watchdog_data_list. - */ - mutex_lock(&watchdog_data_mutex); - for (i = 0; i < ARRAY_SIZE(watchdog_minors); i++) { - /* Register our watchdog part */ - snprintf(data->watchdog_name, sizeof(data->watchdog_name), - "watchdog%c", (i == 0) ? '\0' : ('0' + i)); - data->watchdog_miscdev.name = data->watchdog_name; - data->watchdog_miscdev.fops = &watchdog_fops; - data->watchdog_miscdev.minor = watchdog_minors[i]; - err = misc_register(&data->watchdog_miscdev); - if (err == -EBUSY) - continue; - if (err) - break; + strlcpy(data->wdinfo.identity, "sch56xx watchdog", + sizeof(data->wdinfo.identity)); + data->wdinfo.firmware_version = revision; + data->wdinfo.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT; + if (!nowayout) + data->wdinfo.options |= WDIOF_MAGICCLOSE; + + data->wddev.info = &data->wdinfo; + data->wddev.ops = &watchdog_ops; + data->wddev.parent = parent; + data->wddev.timeout = 60; + data->wddev.min_timeout = 1; + data->wddev.max_timeout = 255 * 60; + if (nowayout) + set_bit(WDOG_NO_WAY_OUT, &data->wddev.status); + if (output_enable & SCH56XX_WDOG_OUTPUT_ENABLE) + set_bit(WDOG_ACTIVE, &data->wddev.status); + + /* Since the watchdog uses a downcounter there is no register to read + the BIOS set timeout from (if any was set at all) -> + Choose a preset which will give us a 1 minute timeout */ + if (control & SCH56XX_WDOG_TIME_BASE_SEC) + data->watchdog_preset = 60; /* seconds */ + else + data->watchdog_preset = 1; /* minute */ - list_add(&data->list, &watchdog_data_list); - pr_info("Registered /dev/%s chardev major 10, minor: %d\n", - data->watchdog_name, watchdog_minors[i]); - break; - } - mutex_unlock(&watchdog_data_mutex); + data->watchdog_control = control; + data->watchdog_output_enable = output_enable; + watchdog_set_drvdata(&data->wddev, data); + err = watchdog_register_device(&data->wddev); if (err) { pr_err("Registering watchdog chardev: %d\n", err); - goto error; - } - if (i == ARRAY_SIZE(watchdog_minors)) { - pr_warn("Couldn't register watchdog (no free minor)\n"); - goto error; + kfree(data); + return NULL; } return data; - -error: - kfree(data); - return NULL; } EXPORT_SYMBOL(sch56xx_watchdog_register); void sch56xx_watchdog_unregister(struct sch56xx_watchdog_data *data) { - mutex_lock(&watchdog_data_mutex); - misc_deregister(&data->watchdog_miscdev); - list_del(&data->list); - mutex_unlock(&watchdog_data_mutex); - - mutex_lock(&data->watchdog_lock); - if (data->watchdog_is_open) { - pr_warn("platform device unregistered with watchdog " - "open! Stopping watchdog.\n"); - watchdog_stop_unlocked(data); - } - /* Tell the wdog start/stop/trigger functions our dev is gone */ - data->addr = 0; - data->io_lock = NULL; - mutex_unlock(&data->watchdog_lock); - - mutex_lock(&watchdog_data_mutex); - kref_put(&data->kref, sch56xx_watchdog_release_resources); - mutex_unlock(&watchdog_data_mutex); + watchdog_unregister_device(&data->wddev); + kref_put(&data->kref, watchdog_release_resources); + /* Don't touch data after this it may have been free-ed! */ } EXPORT_SYMBOL(sch56xx_watchdog_unregister); 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); int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg, int high_nibble); -struct sch56xx_watchdog_data *sch56xx_watchdog_register( +struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent, u16 addr, u32 revision, struct mutex *io_lock, int check_enabled); 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 To compile this driver as a module, choose M here: the module will be called softdog. +config DA9052_WATCHDOG + tristate "Dialog DA9052 Watchdog" + depends on PMIC_DA9052 + select WATCHDOG_CORE + help + Support for the watchdog in the DA9052 PMIC. Watchdog trigger + cause system reset. + + Say Y here to include support for the DA9052 watchdog. + Alternatively say M to compile the driver as a module, + which will be called da9052_wdt. + config WM831X_WATCHDOG tristate "WM831x watchdog" depends on MFD_WM831X @@ -87,6 +99,7 @@ config WM8350_WATCHDOG config ARM_SP805_WATCHDOG tristate "ARM SP805 Watchdog" depends on ARM_AMBA + select WATCHDOG_CORE help ARM Primecell SP805 Watchdog timer. This will reboot your system when 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 obj-$(CONFIG_XEN_WDT) += xen_wdt.o # Architecture Independent +obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o 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 @@ +/* + * System monitoring driver for DA9052 PMICs. + * + * Copyright(c) 2012 Dialog Semiconductor Ltd. + * + * Author: Anthony Olech <Anthony.Olech@diasemi.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/uaccess.h> +#include <linux/platform_device.h> +#include <linux/time.h> +#include <linux/watchdog.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/jiffies.h> +#include <linux/delay.h> + +#include <linux/mfd/da9052/reg.h> +#include <linux/mfd/da9052/da9052.h> + +#define DA9052_DEF_TIMEOUT 4 +#define DA9052_TWDMIN 256 + +struct da9052_wdt_data { + struct watchdog_device wdt; + struct da9052 *da9052; + struct kref kref; + unsigned long jpast; +}; + +static const struct { + u8 reg_val; + int time; /* Seconds */ +} da9052_wdt_maps[] = { + { 1, 2 }, + { 2, 4 }, + { 3, 8 }, + { 4, 16 }, + { 5, 32 }, + { 5, 33 }, /* Actual time 32.768s so included both 32s and 33s */ + { 6, 65 }, + { 6, 66 }, /* Actual time 65.536s so include both, 65s and 66s */ + { 7, 131 }, +}; + + +static void da9052_wdt_release_resources(struct kref *r) +{ + struct da9052_wdt_data *driver_data = + container_of(r, struct da9052_wdt_data, kref); + + kfree(driver_data); +} + +static int da9052_wdt_set_timeout(struct watchdog_device *wdt_dev, + unsigned int timeout) +{ + struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev); + struct da9052 *da9052 = driver_data->da9052; + int ret, i; + + /* + * Disable the Watchdog timer before setting + * new time out. + */ + ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG, + DA9052_CONTROLD_TWDSCALE, 0); + if (ret < 0) { + dev_err(da9052->dev, "Failed to disable watchdog bit, %d\n", + ret); + return ret; + } + if (timeout) { + /* < |
