Commit 00411ee9 authored by Mark Brown's avatar Mark Brown Committed by Wim Van Sebroeck

watchdog: Convert wm831x driver to watchdog core

Fairly large code churn but not much doing with that and the overall
result is a definite win.
Signed-off-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: default avatarWim Van Sebroeck <wim@iguana.be>
parent 25dc46e3
...@@ -66,6 +66,7 @@ config SOFT_WATCHDOG ...@@ -66,6 +66,7 @@ config SOFT_WATCHDOG
config WM831X_WATCHDOG config WM831X_WATCHDOG
tristate "WM831x watchdog" tristate "WM831x watchdog"
depends on MFD_WM831X depends on MFD_WM831X
select WATCHDOG_CORE
help help
Support for the watchdog in the WM831x AudioPlus PMICs. When Support for the watchdog in the WM831x AudioPlus PMICs. When
the watchdog triggers the system will be reset. the watchdog triggers the system will be reset.
......
...@@ -12,8 +12,7 @@ ...@@ -12,8 +12,7 @@
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/fs.h> #include <linux/slab.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/watchdog.h> #include <linux/watchdog.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
...@@ -29,18 +28,18 @@ MODULE_PARM_DESC(nowayout, ...@@ -29,18 +28,18 @@ MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default=" "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
static unsigned long wm831x_wdt_users; struct wm831x_wdt_drvdata {
static struct miscdevice wm831x_wdt_miscdev; struct watchdog_device wdt;
static int wm831x_wdt_expect_close; struct wm831x *wm831x;
static DEFINE_MUTEX(wdt_mutex); struct mutex lock;
static struct wm831x *wm831x; int update_gpio;
static unsigned int update_gpio; int update_state;
static unsigned int update_state; };
/* We can't use the sub-second values here but they're included /* We can't use the sub-second values here but they're included
* for completeness. */ * for completeness. */
static struct { static struct {
int time; /* Seconds */ unsigned int time; /* Seconds */
u16 val; /* WDOG_TO value */ u16 val; /* WDOG_TO value */
} wm831x_wdt_cfgs[] = { } wm831x_wdt_cfgs[] = {
{ 1, 2 }, { 1, 2 },
...@@ -52,32 +51,13 @@ static struct { ...@@ -52,32 +51,13 @@ static struct {
{ 33, 7 }, /* Actually 32.768s so include both, others round down */ { 33, 7 }, /* Actually 32.768s so include both, others round down */
}; };
static int wm831x_wdt_set_timeout(struct wm831x *wm831x, u16 value) static int wm831x_wdt_start(struct watchdog_device *wdt_dev)
{
int ret;
mutex_lock(&wdt_mutex);
ret = wm831x_reg_unlock(wm831x);
if (ret == 0) {
ret = wm831x_set_bits(wm831x, WM831X_WATCHDOG,
WM831X_WDOG_TO_MASK, value);
wm831x_reg_lock(wm831x);
} else {
dev_err(wm831x->dev, "Failed to unlock security key: %d\n",
ret);
}
mutex_unlock(&wdt_mutex);
return ret;
}
static int wm831x_wdt_start(struct wm831x *wm831x)
{ {
struct wm831x_wdt_drvdata *driver_data = watchdog_get_drvdata(wdt_dev);
struct wm831x *wm831x = driver_data->wm831x;
int ret; int ret;
mutex_lock(&wdt_mutex); mutex_lock(&driver_data->lock);
ret = wm831x_reg_unlock(wm831x); ret = wm831x_reg_unlock(wm831x);
if (ret == 0) { if (ret == 0) {
...@@ -89,16 +69,18 @@ static int wm831x_wdt_start(struct wm831x *wm831x) ...@@ -89,16 +69,18 @@ static int wm831x_wdt_start(struct wm831x *wm831x)
ret); ret);
} }
mutex_unlock(&wdt_mutex); mutex_unlock(&driver_data->lock);
return ret; return ret;
} }
static int wm831x_wdt_stop(struct wm831x *wm831x) static int wm831x_wdt_stop(struct watchdog_device *wdt_dev)
{ {
struct wm831x_wdt_drvdata *driver_data = watchdog_get_drvdata(wdt_dev);
struct wm831x *wm831x = driver_data->wm831x;
int ret; int ret;
mutex_lock(&wdt_mutex); mutex_lock(&driver_data->lock);
ret = wm831x_reg_unlock(wm831x); ret = wm831x_reg_unlock(wm831x);
if (ret == 0) { if (ret == 0) {
...@@ -110,26 +92,28 @@ static int wm831x_wdt_stop(struct wm831x *wm831x) ...@@ -110,26 +92,28 @@ static int wm831x_wdt_stop(struct wm831x *wm831x)
ret); ret);
} }
mutex_unlock(&wdt_mutex); mutex_unlock(&driver_data->lock);
return ret; return ret;
} }
static int wm831x_wdt_kick(struct wm831x *wm831x) static int wm831x_wdt_ping(struct watchdog_device *wdt_dev)
{ {
struct wm831x_wdt_drvdata *driver_data = watchdog_get_drvdata(wdt_dev);
struct wm831x *wm831x = driver_data->wm831x;
int ret; int ret;
u16 reg; u16 reg;
mutex_lock(&wdt_mutex); mutex_lock(&driver_data->lock);
if (update_gpio) { if (driver_data->update_gpio) {
gpio_set_value_cansleep(update_gpio, update_state); gpio_set_value_cansleep(driver_data->update_gpio,
update_state = !update_state; driver_data->update_state);
driver_data->update_state = !driver_data->update_state;
ret = 0; ret = 0;
goto out; goto out;
} }
reg = wm831x_reg_read(wm831x, WM831X_WATCHDOG); reg = wm831x_reg_read(wm831x, WM831X_WATCHDOG);
if (!(reg & WM831X_WDOG_RST_SRC)) { if (!(reg & WM831X_WDOG_RST_SRC)) {
...@@ -150,182 +134,59 @@ static int wm831x_wdt_kick(struct wm831x *wm831x) ...@@ -150,182 +134,59 @@ static int wm831x_wdt_kick(struct wm831x *wm831x)
} }
out: out:
mutex_unlock(&wdt_mutex); mutex_unlock(&driver_data->lock);
return ret; return ret;
} }
static int wm831x_wdt_open(struct inode *inode, struct file *file) static int wm831x_wdt_set_timeout(struct watchdog_device *wdt_dev,
unsigned int timeout)
{ {
int ret; struct wm831x_wdt_drvdata *driver_data = watchdog_get_drvdata(wdt_dev);
struct wm831x *wm831x = driver_data->wm831x;
if (!wm831x) int ret, i;
return -ENODEV;
if (test_and_set_bit(0, &wm831x_wdt_users))
return -EBUSY;
ret = wm831x_wdt_start(wm831x);
if (ret != 0)
return ret;
return nonseekable_open(inode, file);
}
static int wm831x_wdt_release(struct inode *inode, struct file *file)
{
if (wm831x_wdt_expect_close)
wm831x_wdt_stop(wm831x);
else {
dev_warn(wm831x->dev, "Watchdog device closed uncleanly\n");
wm831x_wdt_kick(wm831x);
}
clear_bit(0, &wm831x_wdt_users);
return 0;
}
static ssize_t wm831x_wdt_write(struct file *file,
const char __user *data, size_t count,
loff_t *ppos)
{
size_t i;
if (count) {
wm831x_wdt_kick(wm831x);
if (!nowayout) {
/* In case it was set long ago */
wm831x_wdt_expect_close = 0;
/* scan to see whether or not we got the magic
character */
for (i = 0; i != count; i++) {
char c;
if (get_user(c, data + i))
return -EFAULT;
if (c == 'V')
wm831x_wdt_expect_close = 42;
}
}
}
return count;
}
static const struct watchdog_info ident = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.identity = "WM831x Watchdog",
};
static long wm831x_wdt_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
int ret = -ENOTTY, time, i;
void __user *argp = (void __user *)arg;
int __user *p = argp;
u16 reg;
switch (cmd) {
case WDIOC_GETSUPPORT:
ret = copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
break;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
ret = put_user(0, p);
break;
case WDIOC_SETOPTIONS:
{
int options;
if (get_user(options, p))
return -EFAULT;
ret = -EINVAL;
/* Setting both simultaneously means at least one must fail */
if (options == WDIOS_DISABLECARD)
ret = wm831x_wdt_start(wm831x);
if (options == WDIOS_ENABLECARD)
ret = wm831x_wdt_stop(wm831x);
break;
}
case WDIOC_KEEPALIVE:
ret = wm831x_wdt_kick(wm831x);
break;
case WDIOC_SETTIMEOUT:
ret = get_user(time, p);
if (ret)
break;
if (time == 0) {
if (nowayout)
ret = -EINVAL;
else
wm831x_wdt_stop(wm831x);
break;
}
for (i = 0; i < ARRAY_SIZE(wm831x_wdt_cfgs); i++) for (i = 0; i < ARRAY_SIZE(wm831x_wdt_cfgs); i++)
if (wm831x_wdt_cfgs[i].time == time) if (wm831x_wdt_cfgs[i].time == timeout)
break; break;
if (i == ARRAY_SIZE(wm831x_wdt_cfgs)) if (i == ARRAY_SIZE(wm831x_wdt_cfgs))
ret = -EINVAL; ret = -EINVAL;
else
ret = wm831x_wdt_set_timeout(wm831x,
wm831x_wdt_cfgs[i].val);
break;
case WDIOC_GETTIMEOUT:
reg = wm831x_reg_read(wm831x, WM831X_WATCHDOG);
reg &= WM831X_WDOG_TO_MASK;
for (i = 0; i < ARRAY_SIZE(wm831x_wdt_cfgs); i++)
if (wm831x_wdt_cfgs[i].val == reg)
break;
if (i == ARRAY_SIZE(wm831x_wdt_cfgs)) {
dev_warn(wm831x->dev,
"Unknown watchdog configuration: %x\n", reg);
ret = -EINVAL;
} else
ret = put_user(wm831x_wdt_cfgs[i].time, p);
ret = wm831x_reg_unlock(wm831x);
if (ret == 0) {
ret = wm831x_set_bits(wm831x, WM831X_WATCHDOG,
WM831X_WDOG_TO_MASK,
wm831x_wdt_cfgs[i].val);
wm831x_reg_lock(wm831x);
} else {
dev_err(wm831x->dev, "Failed to unlock security key: %d\n",
ret);
} }
return ret; return ret;
} }
static const struct file_operations wm831x_wdt_fops = { static const struct watchdog_info wm831x_wdt_info = {
.owner = THIS_MODULE, .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.llseek = no_llseek, .identity = "WM831x Watchdog",
.write = wm831x_wdt_write,
.unlocked_ioctl = wm831x_wdt_ioctl,
.open = wm831x_wdt_open,
.release = wm831x_wdt_release,
}; };
static struct miscdevice wm831x_wdt_miscdev = { static const struct watchdog_ops wm831x_wdt_ops = {
.minor = WATCHDOG_MINOR, .owner = THIS_MODULE,
.name = "watchdog", .start = wm831x_wdt_start,
.fops = &wm831x_wdt_fops, .stop = wm831x_wdt_stop,
.ping = wm831x_wdt_ping,
.set_timeout = wm831x_wdt_set_timeout,
}; };
static int __devinit wm831x_wdt_probe(struct platform_device *pdev) static int __devinit wm831x_wdt_probe(struct platform_device *pdev)
{ {
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
struct wm831x_pdata *chip_pdata; struct wm831x_pdata *chip_pdata;
struct wm831x_watchdog_pdata *pdata; struct wm831x_watchdog_pdata *pdata;
int reg, ret; struct wm831x_wdt_drvdata *driver_data;
struct watchdog_device *wm831x_wdt;
if (wm831x) { int reg, ret, i;
dev_err(&pdev->dev, "wm831x watchdog already registered\n");
return -EBUSY;
}
wm831x = dev_get_drvdata(pdev->dev.parent);
ret = wm831x_reg_read(wm831x, WM831X_WATCHDOG); ret = wm831x_reg_read(wm831x, WM831X_WATCHDOG);
if (ret < 0) { if (ret < 0) {
...@@ -338,6 +199,36 @@ static int __devinit wm831x_wdt_probe(struct platform_device *pdev) ...@@ -338,6 +199,36 @@ static int __devinit wm831x_wdt_probe(struct platform_device *pdev)
if (reg & WM831X_WDOG_DEBUG) if (reg & WM831X_WDOG_DEBUG)
dev_warn(wm831x->dev, "Watchdog is paused\n"); dev_warn(wm831x->dev, "Watchdog is paused\n");
driver_data = kzalloc(sizeof(*driver_data), GFP_KERNEL);
if (!driver_data) {
dev_err(wm831x->dev, "Unable to alloacate watchdog device\n");
ret = -ENOMEM;
goto err;
}
mutex_init(&driver_data->lock);
driver_data->wm831x = wm831x;
wm831x_wdt = &driver_data->wdt;
wm831x_wdt->info = &wm831x_wdt_info;
wm831x_wdt->ops = &wm831x_wdt_ops;
watchdog_set_drvdata(wm831x_wdt, driver_data);
if (nowayout)
wm831x_wdt->status |= WDOG_NO_WAY_OUT;
reg = wm831x_reg_read(wm831x, WM831X_WATCHDOG);
reg &= WM831X_WDOG_TO_MASK;
for (i = 0; i < ARRAY_SIZE(wm831x_wdt_cfgs); i++)
if (wm831x_wdt_cfgs[i].val == reg)
break;
if (i == ARRAY_SIZE(wm831x_wdt_cfgs))
dev_warn(wm831x->dev,
"Unknown watchdog timeout: %x\n", reg);
else
wm831x_wdt->timeout = wm831x_wdt_cfgs[i].time;
/* Apply any configuration */ /* Apply any configuration */
if (pdev->dev.parent->platform_data) { if (pdev->dev.parent->platform_data) {
chip_pdata = pdev->dev.parent->platform_data; chip_pdata = pdev->dev.parent->platform_data;
...@@ -361,7 +252,7 @@ static int __devinit wm831x_wdt_probe(struct platform_device *pdev) ...@@ -361,7 +252,7 @@ static int __devinit wm831x_wdt_probe(struct platform_device *pdev)
dev_err(wm831x->dev, dev_err(wm831x->dev,
"Failed to request update GPIO: %d\n", "Failed to request update GPIO: %d\n",
ret); ret);
goto err; goto err_alloc;
} }
ret = gpio_direction_output(pdata->update_gpio, 0); ret = gpio_direction_output(pdata->update_gpio, 0);
...@@ -372,7 +263,7 @@ static int __devinit wm831x_wdt_probe(struct platform_device *pdev) ...@@ -372,7 +263,7 @@ static int __devinit wm831x_wdt_probe(struct platform_device *pdev)
goto err_gpio; goto err_gpio;
} }
update_gpio = pdata->update_gpio; driver_data->update_gpio = pdata->update_gpio;
/* Make sure the watchdog takes hardware updates */ /* Make sure the watchdog takes hardware updates */
reg |= WM831X_WDOG_RST_SRC; reg |= WM831X_WDOG_RST_SRC;
...@@ -389,33 +280,34 @@ static int __devinit wm831x_wdt_probe(struct platform_device *pdev) ...@@ -389,33 +280,34 @@ static int __devinit wm831x_wdt_probe(struct platform_device *pdev)
} }
} }
wm831x_wdt_miscdev.parent = &pdev->dev; ret = watchdog_register_device(&driver_data->wdt);
ret = misc_register(&wm831x_wdt_miscdev);
if (ret != 0) { if (ret != 0) {
dev_err(wm831x->dev, "Failed to register miscdev: %d\n", ret); dev_err(wm831x->dev, "watchdog_register_device() failed: %d\n",
ret);
goto err_gpio; goto err_gpio;
} }
dev_set_drvdata(&pdev->dev, driver_data);
return 0; return 0;
err_gpio: err_gpio:
if (update_gpio) { if (driver_data->update_gpio)
gpio_free(update_gpio); gpio_free(driver_data->update_gpio);
update_gpio = 0; err_alloc:
} kfree(driver_data);
err: err:
return ret; return ret;
} }
static int __devexit wm831x_wdt_remove(struct platform_device *pdev) static int __devexit wm831x_wdt_remove(struct platform_device *pdev)
{ {
if (update_gpio) { struct wm831x_wdt_drvdata *driver_data = dev_get_drvdata(&pdev->dev);
gpio_free(update_gpio);
update_gpio = 0; watchdog_unregister_device(&driver_data->wdt);
}
misc_deregister(&wm831x_wdt_miscdev); if (driver_data->update_gpio)
gpio_free(driver_data->update_gpio);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment