1502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown/* 2502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown * Watchdog driver for the wm831x PMICs 3502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown * 4502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown * Copyright (C) 2009 Wolfson Microelectronics 5502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown * 6502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown * This program is free software; you can redistribute it and/or 7502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown * modify it under the terms of the GNU General Public License 8502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown * as published by the Free Software Foundation 9502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown */ 10502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 11502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown#include <linux/module.h> 12502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown#include <linux/moduleparam.h> 13502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown#include <linux/types.h> 14502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown#include <linux/kernel.h> 1500411ee9308e4b5f4b04caaa01685f955e259373Mark Brown#include <linux/slab.h> 16502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown#include <linux/platform_device.h> 17502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown#include <linux/watchdog.h> 18502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown#include <linux/uaccess.h> 19502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown#include <linux/gpio.h> 20502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 21502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown#include <linux/mfd/wm831x/core.h> 22502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown#include <linux/mfd/wm831x/pdata.h> 23502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown#include <linux/mfd/wm831x/watchdog.h> 24502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 2586a1e1896c2710402e29a875d8d830244274244dWim Van Sebroeckstatic bool nowayout = WATCHDOG_NOWAYOUT; 2686a1e1896c2710402e29a875d8d830244274244dWim Van Sebroeckmodule_param(nowayout, bool, 0); 27502a0106b2cc31940f690dc6693fddfd3b97cab5Mark BrownMODULE_PARM_DESC(nowayout, 28502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown "Watchdog cannot be stopped once started (default=" 29502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 30502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 3100411ee9308e4b5f4b04caaa01685f955e259373Mark Brownstruct wm831x_wdt_drvdata { 3200411ee9308e4b5f4b04caaa01685f955e259373Mark Brown struct watchdog_device wdt; 3300411ee9308e4b5f4b04caaa01685f955e259373Mark Brown struct wm831x *wm831x; 3400411ee9308e4b5f4b04caaa01685f955e259373Mark Brown struct mutex lock; 3500411ee9308e4b5f4b04caaa01685f955e259373Mark Brown int update_gpio; 3600411ee9308e4b5f4b04caaa01685f955e259373Mark Brown int update_state; 3700411ee9308e4b5f4b04caaa01685f955e259373Mark Brown}; 38502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 39502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown/* We can't use the sub-second values here but they're included 40502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown * for completeness. */ 41502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brownstatic struct { 4200411ee9308e4b5f4b04caaa01685f955e259373Mark Brown unsigned int time; /* Seconds */ 4300411ee9308e4b5f4b04caaa01685f955e259373Mark Brown u16 val; /* WDOG_TO value */ 44502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown} wm831x_wdt_cfgs[] = { 45502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown { 1, 2 }, 46502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown { 2, 3 }, 47502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown { 4, 4 }, 48502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown { 8, 5 }, 49502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown { 16, 6 }, 50502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown { 32, 7 }, 51502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown { 33, 7 }, /* Actually 32.768s so include both, others round down */ 52502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown}; 53502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 5400411ee9308e4b5f4b04caaa01685f955e259373Mark Brownstatic int wm831x_wdt_start(struct watchdog_device *wdt_dev) 55502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown{ 5600411ee9308e4b5f4b04caaa01685f955e259373Mark Brown struct wm831x_wdt_drvdata *driver_data = watchdog_get_drvdata(wdt_dev); 5700411ee9308e4b5f4b04caaa01685f955e259373Mark Brown struct wm831x *wm831x = driver_data->wm831x; 58502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown int ret; 59502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 6000411ee9308e4b5f4b04caaa01685f955e259373Mark Brown mutex_lock(&driver_data->lock); 61502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 62502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown ret = wm831x_reg_unlock(wm831x); 63502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown if (ret == 0) { 64502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown ret = wm831x_set_bits(wm831x, WM831X_WATCHDOG, 65502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown WM831X_WDOG_ENA, WM831X_WDOG_ENA); 66502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown wm831x_reg_lock(wm831x); 67502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown } else { 68502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown dev_err(wm831x->dev, "Failed to unlock security key: %d\n", 69502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown ret); 70502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown } 71502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 7200411ee9308e4b5f4b04caaa01685f955e259373Mark Brown mutex_unlock(&driver_data->lock); 73502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 74502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown return ret; 75502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown} 76502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 7700411ee9308e4b5f4b04caaa01685f955e259373Mark Brownstatic int wm831x_wdt_stop(struct watchdog_device *wdt_dev) 78502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown{ 7900411ee9308e4b5f4b04caaa01685f955e259373Mark Brown struct wm831x_wdt_drvdata *driver_data = watchdog_get_drvdata(wdt_dev); 8000411ee9308e4b5f4b04caaa01685f955e259373Mark Brown struct wm831x *wm831x = driver_data->wm831x; 81502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown int ret; 82502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 8300411ee9308e4b5f4b04caaa01685f955e259373Mark Brown mutex_lock(&driver_data->lock); 84502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 85502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown ret = wm831x_reg_unlock(wm831x); 86502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown if (ret == 0) { 87502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown ret = wm831x_set_bits(wm831x, WM831X_WATCHDOG, 88502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown WM831X_WDOG_ENA, 0); 89502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown wm831x_reg_lock(wm831x); 90502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown } else { 91502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown dev_err(wm831x->dev, "Failed to unlock security key: %d\n", 92502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown ret); 93502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown } 94502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 9500411ee9308e4b5f4b04caaa01685f955e259373Mark Brown mutex_unlock(&driver_data->lock); 96502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 97502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown return ret; 98502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown} 99502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 10000411ee9308e4b5f4b04caaa01685f955e259373Mark Brownstatic int wm831x_wdt_ping(struct watchdog_device *wdt_dev) 101502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown{ 10200411ee9308e4b5f4b04caaa01685f955e259373Mark Brown struct wm831x_wdt_drvdata *driver_data = watchdog_get_drvdata(wdt_dev); 10300411ee9308e4b5f4b04caaa01685f955e259373Mark Brown struct wm831x *wm831x = driver_data->wm831x; 104502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown int ret; 105502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown u16 reg; 106502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 10700411ee9308e4b5f4b04caaa01685f955e259373Mark Brown mutex_lock(&driver_data->lock); 108502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 10900411ee9308e4b5f4b04caaa01685f955e259373Mark Brown if (driver_data->update_gpio) { 11000411ee9308e4b5f4b04caaa01685f955e259373Mark Brown gpio_set_value_cansleep(driver_data->update_gpio, 11100411ee9308e4b5f4b04caaa01685f955e259373Mark Brown driver_data->update_state); 11200411ee9308e4b5f4b04caaa01685f955e259373Mark Brown driver_data->update_state = !driver_data->update_state; 113502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown ret = 0; 114502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown goto out; 115502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown } 116502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 117502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown reg = wm831x_reg_read(wm831x, WM831X_WATCHDOG); 118502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 119502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown if (!(reg & WM831X_WDOG_RST_SRC)) { 120502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown dev_err(wm831x->dev, "Hardware watchdog update unsupported\n"); 121502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown ret = -EINVAL; 122502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown goto out; 123502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown } 124502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 125502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown reg |= WM831X_WDOG_RESET; 126502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 127502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown ret = wm831x_reg_unlock(wm831x); 128502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown if (ret == 0) { 129502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown ret = wm831x_reg_write(wm831x, WM831X_WATCHDOG, reg); 130502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown wm831x_reg_lock(wm831x); 131502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown } else { 132502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown dev_err(wm831x->dev, "Failed to unlock security key: %d\n", 133502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown ret); 134502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown } 135502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 136502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brownout: 13700411ee9308e4b5f4b04caaa01685f955e259373Mark Brown mutex_unlock(&driver_data->lock); 138502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 139502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown return ret; 140502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown} 141502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 14200411ee9308e4b5f4b04caaa01685f955e259373Mark Brownstatic int wm831x_wdt_set_timeout(struct watchdog_device *wdt_dev, 14300411ee9308e4b5f4b04caaa01685f955e259373Mark Brown unsigned int timeout) 144502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown{ 14500411ee9308e4b5f4b04caaa01685f955e259373Mark Brown struct wm831x_wdt_drvdata *driver_data = watchdog_get_drvdata(wdt_dev); 14600411ee9308e4b5f4b04caaa01685f955e259373Mark Brown struct wm831x *wm831x = driver_data->wm831x; 14700411ee9308e4b5f4b04caaa01685f955e259373Mark Brown int ret, i; 148502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 14900411ee9308e4b5f4b04caaa01685f955e259373Mark Brown for (i = 0; i < ARRAY_SIZE(wm831x_wdt_cfgs); i++) 15000411ee9308e4b5f4b04caaa01685f955e259373Mark Brown if (wm831x_wdt_cfgs[i].time == timeout) 15100411ee9308e4b5f4b04caaa01685f955e259373Mark Brown break; 15200411ee9308e4b5f4b04caaa01685f955e259373Mark Brown if (i == ARRAY_SIZE(wm831x_wdt_cfgs)) 153f9849100851b28c8ad83e86d68d5110497a4e9d6Mark Brown return -EINVAL; 154502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 15500411ee9308e4b5f4b04caaa01685f955e259373Mark Brown ret = wm831x_reg_unlock(wm831x); 15600411ee9308e4b5f4b04caaa01685f955e259373Mark Brown if (ret == 0) { 15700411ee9308e4b5f4b04caaa01685f955e259373Mark Brown ret = wm831x_set_bits(wm831x, WM831X_WATCHDOG, 15800411ee9308e4b5f4b04caaa01685f955e259373Mark Brown WM831X_WDOG_TO_MASK, 15900411ee9308e4b5f4b04caaa01685f955e259373Mark Brown wm831x_wdt_cfgs[i].val); 16000411ee9308e4b5f4b04caaa01685f955e259373Mark Brown wm831x_reg_lock(wm831x); 16100411ee9308e4b5f4b04caaa01685f955e259373Mark Brown } else { 16200411ee9308e4b5f4b04caaa01685f955e259373Mark Brown dev_err(wm831x->dev, "Failed to unlock security key: %d\n", 16300411ee9308e4b5f4b04caaa01685f955e259373Mark Brown ret); 164502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown } 165502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 1660197c1c49ef1ff386b2ebb6d3b0fc85a8e174b5cWim Van Sebroeck wdt_dev->timeout = timeout; 1670197c1c49ef1ff386b2ebb6d3b0fc85a8e174b5cWim Van Sebroeck 16800411ee9308e4b5f4b04caaa01685f955e259373Mark Brown return ret; 169502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown} 170502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 17100411ee9308e4b5f4b04caaa01685f955e259373Mark Brownstatic const struct watchdog_info wm831x_wdt_info = { 172502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, 173502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown .identity = "WM831x Watchdog", 174502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown}; 175502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 17600411ee9308e4b5f4b04caaa01685f955e259373Mark Brownstatic const struct watchdog_ops wm831x_wdt_ops = { 177502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown .owner = THIS_MODULE, 17800411ee9308e4b5f4b04caaa01685f955e259373Mark Brown .start = wm831x_wdt_start, 17900411ee9308e4b5f4b04caaa01685f955e259373Mark Brown .stop = wm831x_wdt_stop, 18000411ee9308e4b5f4b04caaa01685f955e259373Mark Brown .ping = wm831x_wdt_ping, 18100411ee9308e4b5f4b04caaa01685f955e259373Mark Brown .set_timeout = wm831x_wdt_set_timeout, 182502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown}; 183502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 1842d991a164a61858012651e13c59521975504e260Bill Pembertonstatic int wm831x_wdt_probe(struct platform_device *pdev) 185502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown{ 18600411ee9308e4b5f4b04caaa01685f955e259373Mark Brown struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); 187bc8fdfbe75058c6569dd6a08bdc2a411db533c47Jingoo Han struct wm831x_pdata *chip_pdata = dev_get_platdata(pdev->dev.parent); 188502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown struct wm831x_watchdog_pdata *pdata; 18900411ee9308e4b5f4b04caaa01685f955e259373Mark Brown struct wm831x_wdt_drvdata *driver_data; 19000411ee9308e4b5f4b04caaa01685f955e259373Mark Brown struct watchdog_device *wm831x_wdt; 19100411ee9308e4b5f4b04caaa01685f955e259373Mark Brown int reg, ret, i; 192502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 193502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown ret = wm831x_reg_read(wm831x, WM831X_WATCHDOG); 194502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown if (ret < 0) { 195502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown dev_err(wm831x->dev, "Failed to read watchdog status: %d\n", 196502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown ret); 197502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown goto err; 198502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown } 199502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown reg = ret; 200502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 201502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown if (reg & WM831X_WDOG_DEBUG) 202502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown dev_warn(wm831x->dev, "Watchdog is paused\n"); 203502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 2047b9bb6d8cfe80580329318caf6c3a137762ecea3Mark Brown driver_data = devm_kzalloc(&pdev->dev, sizeof(*driver_data), 2057b9bb6d8cfe80580329318caf6c3a137762ecea3Mark Brown GFP_KERNEL); 20600411ee9308e4b5f4b04caaa01685f955e259373Mark Brown if (!driver_data) { 20700411ee9308e4b5f4b04caaa01685f955e259373Mark Brown ret = -ENOMEM; 20800411ee9308e4b5f4b04caaa01685f955e259373Mark Brown goto err; 20900411ee9308e4b5f4b04caaa01685f955e259373Mark Brown } 21000411ee9308e4b5f4b04caaa01685f955e259373Mark Brown 21100411ee9308e4b5f4b04caaa01685f955e259373Mark Brown mutex_init(&driver_data->lock); 21200411ee9308e4b5f4b04caaa01685f955e259373Mark Brown driver_data->wm831x = wm831x; 21300411ee9308e4b5f4b04caaa01685f955e259373Mark Brown 21400411ee9308e4b5f4b04caaa01685f955e259373Mark Brown wm831x_wdt = &driver_data->wdt; 21500411ee9308e4b5f4b04caaa01685f955e259373Mark Brown 21600411ee9308e4b5f4b04caaa01685f955e259373Mark Brown wm831x_wdt->info = &wm831x_wdt_info; 21700411ee9308e4b5f4b04caaa01685f955e259373Mark Brown wm831x_wdt->ops = &wm831x_wdt_ops; 218ff0b3cd4a416bc727b0797b95b229b278d2a28f2Wim Van Sebroeck watchdog_set_nowayout(wm831x_wdt, nowayout); 21900411ee9308e4b5f4b04caaa01685f955e259373Mark Brown watchdog_set_drvdata(wm831x_wdt, driver_data); 22000411ee9308e4b5f4b04caaa01685f955e259373Mark Brown 22100411ee9308e4b5f4b04caaa01685f955e259373Mark Brown reg = wm831x_reg_read(wm831x, WM831X_WATCHDOG); 22200411ee9308e4b5f4b04caaa01685f955e259373Mark Brown reg &= WM831X_WDOG_TO_MASK; 22300411ee9308e4b5f4b04caaa01685f955e259373Mark Brown for (i = 0; i < ARRAY_SIZE(wm831x_wdt_cfgs); i++) 22400411ee9308e4b5f4b04caaa01685f955e259373Mark Brown if (wm831x_wdt_cfgs[i].val == reg) 22500411ee9308e4b5f4b04caaa01685f955e259373Mark Brown break; 22600411ee9308e4b5f4b04caaa01685f955e259373Mark Brown if (i == ARRAY_SIZE(wm831x_wdt_cfgs)) 22700411ee9308e4b5f4b04caaa01685f955e259373Mark Brown dev_warn(wm831x->dev, 22800411ee9308e4b5f4b04caaa01685f955e259373Mark Brown "Unknown watchdog timeout: %x\n", reg); 22900411ee9308e4b5f4b04caaa01685f955e259373Mark Brown else 23000411ee9308e4b5f4b04caaa01685f955e259373Mark Brown wm831x_wdt->timeout = wm831x_wdt_cfgs[i].time; 23100411ee9308e4b5f4b04caaa01685f955e259373Mark Brown 232502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown /* Apply any configuration */ 233bc8fdfbe75058c6569dd6a08bdc2a411db533c47Jingoo Han if (chip_pdata) 234502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown pdata = chip_pdata->watchdog; 235bc8fdfbe75058c6569dd6a08bdc2a411db533c47Jingoo Han else 236502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown pdata = NULL; 237502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 238502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown if (pdata) { 239502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown reg &= ~(WM831X_WDOG_SECACT_MASK | WM831X_WDOG_PRIMACT_MASK | 240502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown WM831X_WDOG_RST_SRC); 241502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 242502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown reg |= pdata->primary << WM831X_WDOG_PRIMACT_SHIFT; 243502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown reg |= pdata->secondary << WM831X_WDOG_SECACT_SHIFT; 244502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown reg |= pdata->software << WM831X_WDOG_RST_SRC_SHIFT; 245502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 246502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown if (pdata->update_gpio) { 2477a5da030c6ecdd6229f079902a17e641c7f2fbd6Jingoo Han ret = devm_gpio_request_one(&pdev->dev, 2487a5da030c6ecdd6229f079902a17e641c7f2fbd6Jingoo Han pdata->update_gpio, 2497a5da030c6ecdd6229f079902a17e641c7f2fbd6Jingoo Han GPIOF_OUT_INIT_LOW, 2507a5da030c6ecdd6229f079902a17e641c7f2fbd6Jingoo Han "Watchdog update"); 251502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown if (ret < 0) { 252502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown dev_err(wm831x->dev, 253502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown "Failed to request update GPIO: %d\n", 254502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown ret); 2557b9bb6d8cfe80580329318caf6c3a137762ecea3Mark Brown goto err; 256502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown } 257502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 25800411ee9308e4b5f4b04caaa01685f955e259373Mark Brown driver_data->update_gpio = pdata->update_gpio; 259502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 260502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown /* Make sure the watchdog takes hardware updates */ 261502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown reg |= WM831X_WDOG_RST_SRC; 262502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown } 263502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 264502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown ret = wm831x_reg_unlock(wm831x); 265502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown if (ret == 0) { 266502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown ret = wm831x_reg_write(wm831x, WM831X_WATCHDOG, reg); 267502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown wm831x_reg_lock(wm831x); 268502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown } else { 269502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown dev_err(wm831x->dev, 270502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown "Failed to unlock security key: %d\n", ret); 2717a5da030c6ecdd6229f079902a17e641c7f2fbd6Jingoo Han goto err; 272502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown } 273502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown } 274502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 27500411ee9308e4b5f4b04caaa01685f955e259373Mark Brown ret = watchdog_register_device(&driver_data->wdt); 276502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown if (ret != 0) { 27700411ee9308e4b5f4b04caaa01685f955e259373Mark Brown dev_err(wm831x->dev, "watchdog_register_device() failed: %d\n", 27800411ee9308e4b5f4b04caaa01685f955e259373Mark Brown ret); 2797a5da030c6ecdd6229f079902a17e641c7f2fbd6Jingoo Han goto err; 280502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown } 281502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 2821ae995dca9da3a5671aa471e1c355e47437a1056Jingoo Han platform_set_drvdata(pdev, driver_data); 28300411ee9308e4b5f4b04caaa01685f955e259373Mark Brown 284502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown return 0; 285502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 286502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brownerr: 287502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown return ret; 288502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown} 289502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 2904b12b896c27c3b54592816606679f5b02f638930Bill Pembertonstatic int wm831x_wdt_remove(struct platform_device *pdev) 291502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown{ 2921ae995dca9da3a5671aa471e1c355e47437a1056Jingoo Han struct wm831x_wdt_drvdata *driver_data = platform_get_drvdata(pdev); 29300411ee9308e4b5f4b04caaa01685f955e259373Mark Brown 29400411ee9308e4b5f4b04caaa01685f955e259373Mark Brown watchdog_unregister_device(&driver_data->wdt); 295502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 296502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown return 0; 297502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown} 298502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 299502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brownstatic struct platform_driver wm831x_wdt_driver = { 300502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown .probe = wm831x_wdt_probe, 30182268714bdf06bc06135efb707a9de590ab2d294Bill Pemberton .remove = wm831x_wdt_remove, 302502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown .driver = { 303502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown .name = "wm831x-watchdog", 304502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown }, 305502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown}; 306502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 307216f3ad9aa5731024b9c96e63b676f9f65078dd5Mark Brownmodule_platform_driver(wm831x_wdt_driver); 308502a0106b2cc31940f690dc6693fddfd3b97cab5Mark Brown 309502a0106b2cc31940f690dc6693fddfd3b97cab5Mark BrownMODULE_AUTHOR("Mark Brown"); 310502a0106b2cc31940f690dc6693fddfd3b97cab5Mark BrownMODULE_DESCRIPTION("WM831x Watchdog"); 311502a0106b2cc31940f690dc6693fddfd3b97cab5Mark BrownMODULE_LICENSE("GPL"); 312502a0106b2cc31940f690dc6693fddfd3b97cab5Mark BrownMODULE_ALIAS("platform:wm831x-watchdog"); 313