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