Linux Audio

Check our new training course

Loading...
v6.13.7
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * NXP LPC18xx Watchdog Timer (WDT)
  4 *
  5 * Copyright (c) 2015 Ariel D'Alessandro <ariel@vanguardiasur.com>
  6 *
 
 
 
 
  7 * Notes
  8 * -----
  9 * The Watchdog consists of a fixed divide-by-4 clock pre-scaler and a 24-bit
 10 * counter which decrements on every clock cycle.
 11 */
 12
 13#include <linux/clk.h>
 14#include <linux/io.h>
 15#include <linux/module.h>
 16#include <linux/of.h>
 17#include <linux/platform_device.h>
 18#include <linux/watchdog.h>
 19
 20/* Registers */
 21#define LPC18XX_WDT_MOD			0x00
 22#define LPC18XX_WDT_MOD_WDEN		BIT(0)
 23#define LPC18XX_WDT_MOD_WDRESET		BIT(1)
 24
 25#define LPC18XX_WDT_TC			0x04
 26#define LPC18XX_WDT_TC_MIN		0xff
 27#define LPC18XX_WDT_TC_MAX		0xffffff
 28
 29#define LPC18XX_WDT_FEED		0x08
 30#define LPC18XX_WDT_FEED_MAGIC1		0xaa
 31#define LPC18XX_WDT_FEED_MAGIC2		0x55
 32
 33#define LPC18XX_WDT_TV			0x0c
 34
 35/* Clock pre-scaler */
 36#define LPC18XX_WDT_CLK_DIV		4
 37
 38/* Timeout values in seconds */
 39#define LPC18XX_WDT_DEF_TIMEOUT		30U
 40
 41static int heartbeat;
 42module_param(heartbeat, int, 0);
 43MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds (default="
 44		 __MODULE_STRING(LPC18XX_WDT_DEF_TIMEOUT) ")");
 45
 46static bool nowayout = WATCHDOG_NOWAYOUT;
 47module_param(nowayout, bool, 0);
 48MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
 49		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 50
 51struct lpc18xx_wdt_dev {
 52	struct watchdog_device	wdt_dev;
 53	struct clk		*reg_clk;
 54	struct clk		*wdt_clk;
 55	unsigned long		clk_rate;
 56	void __iomem		*base;
 57	struct timer_list	timer;
 58	spinlock_t		lock;
 59};
 60
 61static int lpc18xx_wdt_feed(struct watchdog_device *wdt_dev)
 62{
 63	struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev);
 64	unsigned long flags;
 65
 66	/*
 67	 * An abort condition will occur if an interrupt happens during the feed
 68	 * sequence.
 69	 */
 70	spin_lock_irqsave(&lpc18xx_wdt->lock, flags);
 71	writel(LPC18XX_WDT_FEED_MAGIC1, lpc18xx_wdt->base + LPC18XX_WDT_FEED);
 72	writel(LPC18XX_WDT_FEED_MAGIC2, lpc18xx_wdt->base + LPC18XX_WDT_FEED);
 73	spin_unlock_irqrestore(&lpc18xx_wdt->lock, flags);
 74
 75	return 0;
 76}
 77
 78static void lpc18xx_wdt_timer_feed(struct timer_list *t)
 79{
 80	struct lpc18xx_wdt_dev *lpc18xx_wdt = from_timer(lpc18xx_wdt, t, timer);
 81	struct watchdog_device *wdt_dev = &lpc18xx_wdt->wdt_dev;
 82
 83	lpc18xx_wdt_feed(wdt_dev);
 84
 85	/* Use safe value (1/2 of real timeout) */
 86	mod_timer(&lpc18xx_wdt->timer, jiffies +
 87		  msecs_to_jiffies((wdt_dev->timeout * MSEC_PER_SEC) / 2));
 88}
 89
 90/*
 91 * Since LPC18xx Watchdog cannot be disabled in hardware, we must keep feeding
 92 * it with a timer until userspace watchdog software takes over.
 93 */
 94static int lpc18xx_wdt_stop(struct watchdog_device *wdt_dev)
 95{
 96	struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev);
 97
 98	lpc18xx_wdt_timer_feed(&lpc18xx_wdt->timer);
 99
100	return 0;
101}
102
103static void __lpc18xx_wdt_set_timeout(struct lpc18xx_wdt_dev *lpc18xx_wdt)
104{
105	unsigned int val;
106
107	val = DIV_ROUND_UP(lpc18xx_wdt->wdt_dev.timeout * lpc18xx_wdt->clk_rate,
108			   LPC18XX_WDT_CLK_DIV);
109	writel(val, lpc18xx_wdt->base + LPC18XX_WDT_TC);
110}
111
112static int lpc18xx_wdt_set_timeout(struct watchdog_device *wdt_dev,
113				   unsigned int new_timeout)
114{
115	struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev);
116
117	lpc18xx_wdt->wdt_dev.timeout = new_timeout;
118	__lpc18xx_wdt_set_timeout(lpc18xx_wdt);
119
120	return 0;
121}
122
123static unsigned int lpc18xx_wdt_get_timeleft(struct watchdog_device *wdt_dev)
124{
125	struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev);
126	unsigned int val;
127
128	val = readl(lpc18xx_wdt->base + LPC18XX_WDT_TV);
129	return (val * LPC18XX_WDT_CLK_DIV) / lpc18xx_wdt->clk_rate;
130}
131
132static int lpc18xx_wdt_start(struct watchdog_device *wdt_dev)
133{
134	struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev);
135	unsigned int val;
136
137	if (timer_pending(&lpc18xx_wdt->timer))
138		del_timer(&lpc18xx_wdt->timer);
139
140	val = readl(lpc18xx_wdt->base + LPC18XX_WDT_MOD);
141	val |= LPC18XX_WDT_MOD_WDEN;
142	val |= LPC18XX_WDT_MOD_WDRESET;
143	writel(val, lpc18xx_wdt->base + LPC18XX_WDT_MOD);
144
145	/*
146	 * Setting the WDEN bit in the WDMOD register is not sufficient to
147	 * enable the Watchdog. A valid feed sequence must be completed after
148	 * setting WDEN before the Watchdog is capable of generating a reset.
149	 */
150	lpc18xx_wdt_feed(wdt_dev);
151
152	return 0;
153}
154
155static int lpc18xx_wdt_restart(struct watchdog_device *wdt_dev,
156			       unsigned long action, void *data)
157{
158	struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev);
159	unsigned long flags;
160	int val;
161
162	/*
163	 * Incorrect feed sequence causes immediate watchdog reset if enabled.
164	 */
165	spin_lock_irqsave(&lpc18xx_wdt->lock, flags);
166
167	val = readl(lpc18xx_wdt->base + LPC18XX_WDT_MOD);
168	val |= LPC18XX_WDT_MOD_WDEN;
169	val |= LPC18XX_WDT_MOD_WDRESET;
170	writel(val, lpc18xx_wdt->base + LPC18XX_WDT_MOD);
171
172	writel(LPC18XX_WDT_FEED_MAGIC1, lpc18xx_wdt->base + LPC18XX_WDT_FEED);
173	writel(LPC18XX_WDT_FEED_MAGIC2, lpc18xx_wdt->base + LPC18XX_WDT_FEED);
174
175	writel(LPC18XX_WDT_FEED_MAGIC1, lpc18xx_wdt->base + LPC18XX_WDT_FEED);
176	writel(LPC18XX_WDT_FEED_MAGIC1, lpc18xx_wdt->base + LPC18XX_WDT_FEED);
177
178	spin_unlock_irqrestore(&lpc18xx_wdt->lock, flags);
179
180	return 0;
181}
182
183static const struct watchdog_info lpc18xx_wdt_info = {
184	.identity	= "NXP LPC18xx Watchdog",
185	.options	= WDIOF_SETTIMEOUT |
186			  WDIOF_KEEPALIVEPING |
187			  WDIOF_MAGICCLOSE,
188};
189
190static const struct watchdog_ops lpc18xx_wdt_ops = {
191	.owner		= THIS_MODULE,
192	.start		= lpc18xx_wdt_start,
193	.stop		= lpc18xx_wdt_stop,
194	.ping		= lpc18xx_wdt_feed,
195	.set_timeout	= lpc18xx_wdt_set_timeout,
196	.get_timeleft	= lpc18xx_wdt_get_timeleft,
197	.restart        = lpc18xx_wdt_restart,
198};
199
200static int lpc18xx_wdt_probe(struct platform_device *pdev)
201{
202	struct lpc18xx_wdt_dev *lpc18xx_wdt;
203	struct device *dev = &pdev->dev;
 
 
204
205	lpc18xx_wdt = devm_kzalloc(dev, sizeof(*lpc18xx_wdt), GFP_KERNEL);
206	if (!lpc18xx_wdt)
207		return -ENOMEM;
208
209	lpc18xx_wdt->base = devm_platform_ioremap_resource(pdev, 0);
 
210	if (IS_ERR(lpc18xx_wdt->base))
211		return PTR_ERR(lpc18xx_wdt->base);
212
213	lpc18xx_wdt->reg_clk = devm_clk_get_enabled(dev, "reg");
214	if (IS_ERR(lpc18xx_wdt->reg_clk)) {
215		dev_err(dev, "failed to get the reg clock\n");
216		return PTR_ERR(lpc18xx_wdt->reg_clk);
217	}
218
219	lpc18xx_wdt->wdt_clk = devm_clk_get_enabled(dev, "wdtclk");
220	if (IS_ERR(lpc18xx_wdt->wdt_clk)) {
221		dev_err(dev, "failed to get the wdt clock\n");
222		return PTR_ERR(lpc18xx_wdt->wdt_clk);
223	}
224
 
 
 
 
 
 
 
 
 
 
 
 
225	/* We use the clock rate to calculate timeouts */
226	lpc18xx_wdt->clk_rate = clk_get_rate(lpc18xx_wdt->wdt_clk);
227	if (lpc18xx_wdt->clk_rate == 0) {
228		dev_err(dev, "failed to get clock rate\n");
229		return -EINVAL;
 
230	}
231
232	lpc18xx_wdt->wdt_dev.info = &lpc18xx_wdt_info;
233	lpc18xx_wdt->wdt_dev.ops = &lpc18xx_wdt_ops;
234
235	lpc18xx_wdt->wdt_dev.min_timeout = DIV_ROUND_UP(LPC18XX_WDT_TC_MIN *
236				LPC18XX_WDT_CLK_DIV, lpc18xx_wdt->clk_rate);
237
238	lpc18xx_wdt->wdt_dev.max_timeout = (LPC18XX_WDT_TC_MAX *
239				LPC18XX_WDT_CLK_DIV) / lpc18xx_wdt->clk_rate;
240
241	lpc18xx_wdt->wdt_dev.timeout = min(lpc18xx_wdt->wdt_dev.max_timeout,
242					   LPC18XX_WDT_DEF_TIMEOUT);
243
244	spin_lock_init(&lpc18xx_wdt->lock);
245
246	lpc18xx_wdt->wdt_dev.parent = dev;
247	watchdog_set_drvdata(&lpc18xx_wdt->wdt_dev, lpc18xx_wdt);
248
249	watchdog_init_timeout(&lpc18xx_wdt->wdt_dev, heartbeat, dev);
250
251	__lpc18xx_wdt_set_timeout(lpc18xx_wdt);
252
253	timer_setup(&lpc18xx_wdt->timer, lpc18xx_wdt_timer_feed, 0);
254
255	watchdog_set_nowayout(&lpc18xx_wdt->wdt_dev, nowayout);
256	watchdog_set_restart_priority(&lpc18xx_wdt->wdt_dev, 128);
257
258	platform_set_drvdata(pdev, lpc18xx_wdt);
259
260	watchdog_stop_on_reboot(&lpc18xx_wdt->wdt_dev);
261	return devm_watchdog_register_device(dev, &lpc18xx_wdt->wdt_dev);
 
 
 
 
 
 
 
 
 
262}
263
264static void lpc18xx_wdt_remove(struct platform_device *pdev)
 
 
 
 
 
 
 
265{
266	struct lpc18xx_wdt_dev *lpc18xx_wdt = platform_get_drvdata(pdev);
267
268	dev_warn(&pdev->dev, "I quit now, hardware will probably reboot!\n");
269	del_timer_sync(&lpc18xx_wdt->timer);
 
 
 
 
 
 
270}
271
272static const struct of_device_id lpc18xx_wdt_match[] = {
273	{ .compatible = "nxp,lpc1850-wwdt" },
274	{}
275};
276MODULE_DEVICE_TABLE(of, lpc18xx_wdt_match);
277
278static struct platform_driver lpc18xx_wdt_driver = {
279	.driver = {
280		.name = "lpc18xx-wdt",
281		.of_match_table	= lpc18xx_wdt_match,
282	},
283	.probe = lpc18xx_wdt_probe,
284	.remove = lpc18xx_wdt_remove,
 
285};
286module_platform_driver(lpc18xx_wdt_driver);
287
288MODULE_AUTHOR("Ariel D'Alessandro <ariel@vanguardiasur.com.ar>");
289MODULE_DESCRIPTION("NXP LPC18xx Watchdog Timer Driver");
290MODULE_LICENSE("GPL v2");
v4.17
 
  1/*
  2 * NXP LPC18xx Watchdog Timer (WDT)
  3 *
  4 * Copyright (c) 2015 Ariel D'Alessandro <ariel@vanguardiasur.com>
  5 *
  6 * This program is free software; you can redistribute it and/or modify it
  7 * under the terms of the GNU General Public License version 2 as published by
  8 * the Free Software Foundation.
  9 *
 10 * Notes
 11 * -----
 12 * The Watchdog consists of a fixed divide-by-4 clock pre-scaler and a 24-bit
 13 * counter which decrements on every clock cycle.
 14 */
 15
 16#include <linux/clk.h>
 17#include <linux/io.h>
 18#include <linux/module.h>
 19#include <linux/of.h>
 20#include <linux/platform_device.h>
 21#include <linux/watchdog.h>
 22
 23/* Registers */
 24#define LPC18XX_WDT_MOD			0x00
 25#define LPC18XX_WDT_MOD_WDEN		BIT(0)
 26#define LPC18XX_WDT_MOD_WDRESET		BIT(1)
 27
 28#define LPC18XX_WDT_TC			0x04
 29#define LPC18XX_WDT_TC_MIN		0xff
 30#define LPC18XX_WDT_TC_MAX		0xffffff
 31
 32#define LPC18XX_WDT_FEED		0x08
 33#define LPC18XX_WDT_FEED_MAGIC1		0xaa
 34#define LPC18XX_WDT_FEED_MAGIC2		0x55
 35
 36#define LPC18XX_WDT_TV			0x0c
 37
 38/* Clock pre-scaler */
 39#define LPC18XX_WDT_CLK_DIV		4
 40
 41/* Timeout values in seconds */
 42#define LPC18XX_WDT_DEF_TIMEOUT		30U
 43
 44static int heartbeat;
 45module_param(heartbeat, int, 0);
 46MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds (default="
 47		 __MODULE_STRING(LPC18XX_WDT_DEF_TIMEOUT) ")");
 48
 49static bool nowayout = WATCHDOG_NOWAYOUT;
 50module_param(nowayout, bool, 0);
 51MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
 52		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 53
 54struct lpc18xx_wdt_dev {
 55	struct watchdog_device	wdt_dev;
 56	struct clk		*reg_clk;
 57	struct clk		*wdt_clk;
 58	unsigned long		clk_rate;
 59	void __iomem		*base;
 60	struct timer_list	timer;
 61	spinlock_t		lock;
 62};
 63
 64static int lpc18xx_wdt_feed(struct watchdog_device *wdt_dev)
 65{
 66	struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev);
 67	unsigned long flags;
 68
 69	/*
 70	 * An abort condition will occur if an interrupt happens during the feed
 71	 * sequence.
 72	 */
 73	spin_lock_irqsave(&lpc18xx_wdt->lock, flags);
 74	writel(LPC18XX_WDT_FEED_MAGIC1, lpc18xx_wdt->base + LPC18XX_WDT_FEED);
 75	writel(LPC18XX_WDT_FEED_MAGIC2, lpc18xx_wdt->base + LPC18XX_WDT_FEED);
 76	spin_unlock_irqrestore(&lpc18xx_wdt->lock, flags);
 77
 78	return 0;
 79}
 80
 81static void lpc18xx_wdt_timer_feed(struct timer_list *t)
 82{
 83	struct lpc18xx_wdt_dev *lpc18xx_wdt = from_timer(lpc18xx_wdt, t, timer);
 84	struct watchdog_device *wdt_dev = &lpc18xx_wdt->wdt_dev;
 85
 86	lpc18xx_wdt_feed(wdt_dev);
 87
 88	/* Use safe value (1/2 of real timeout) */
 89	mod_timer(&lpc18xx_wdt->timer, jiffies +
 90		  msecs_to_jiffies((wdt_dev->timeout * MSEC_PER_SEC) / 2));
 91}
 92
 93/*
 94 * Since LPC18xx Watchdog cannot be disabled in hardware, we must keep feeding
 95 * it with a timer until userspace watchdog software takes over.
 96 */
 97static int lpc18xx_wdt_stop(struct watchdog_device *wdt_dev)
 98{
 99	struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev);
100
101	lpc18xx_wdt_timer_feed(&lpc18xx_wdt->timer);
102
103	return 0;
104}
105
106static void __lpc18xx_wdt_set_timeout(struct lpc18xx_wdt_dev *lpc18xx_wdt)
107{
108	unsigned int val;
109
110	val = DIV_ROUND_UP(lpc18xx_wdt->wdt_dev.timeout * lpc18xx_wdt->clk_rate,
111			   LPC18XX_WDT_CLK_DIV);
112	writel(val, lpc18xx_wdt->base + LPC18XX_WDT_TC);
113}
114
115static int lpc18xx_wdt_set_timeout(struct watchdog_device *wdt_dev,
116				   unsigned int new_timeout)
117{
118	struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev);
119
120	lpc18xx_wdt->wdt_dev.timeout = new_timeout;
121	__lpc18xx_wdt_set_timeout(lpc18xx_wdt);
122
123	return 0;
124}
125
126static unsigned int lpc18xx_wdt_get_timeleft(struct watchdog_device *wdt_dev)
127{
128	struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev);
129	unsigned int val;
130
131	val = readl(lpc18xx_wdt->base + LPC18XX_WDT_TV);
132	return (val * LPC18XX_WDT_CLK_DIV) / lpc18xx_wdt->clk_rate;
133}
134
135static int lpc18xx_wdt_start(struct watchdog_device *wdt_dev)
136{
137	struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev);
138	unsigned int val;
139
140	if (timer_pending(&lpc18xx_wdt->timer))
141		del_timer(&lpc18xx_wdt->timer);
142
143	val = readl(lpc18xx_wdt->base + LPC18XX_WDT_MOD);
144	val |= LPC18XX_WDT_MOD_WDEN;
145	val |= LPC18XX_WDT_MOD_WDRESET;
146	writel(val, lpc18xx_wdt->base + LPC18XX_WDT_MOD);
147
148	/*
149	 * Setting the WDEN bit in the WDMOD register is not sufficient to
150	 * enable the Watchdog. A valid feed sequence must be completed after
151	 * setting WDEN before the Watchdog is capable of generating a reset.
152	 */
153	lpc18xx_wdt_feed(wdt_dev);
154
155	return 0;
156}
157
158static int lpc18xx_wdt_restart(struct watchdog_device *wdt_dev,
159			       unsigned long action, void *data)
160{
161	struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev);
162	unsigned long flags;
163	int val;
164
165	/*
166	 * Incorrect feed sequence causes immediate watchdog reset if enabled.
167	 */
168	spin_lock_irqsave(&lpc18xx_wdt->lock, flags);
169
170	val = readl(lpc18xx_wdt->base + LPC18XX_WDT_MOD);
171	val |= LPC18XX_WDT_MOD_WDEN;
172	val |= LPC18XX_WDT_MOD_WDRESET;
173	writel(val, lpc18xx_wdt->base + LPC18XX_WDT_MOD);
174
175	writel(LPC18XX_WDT_FEED_MAGIC1, lpc18xx_wdt->base + LPC18XX_WDT_FEED);
176	writel(LPC18XX_WDT_FEED_MAGIC2, lpc18xx_wdt->base + LPC18XX_WDT_FEED);
177
178	writel(LPC18XX_WDT_FEED_MAGIC1, lpc18xx_wdt->base + LPC18XX_WDT_FEED);
179	writel(LPC18XX_WDT_FEED_MAGIC1, lpc18xx_wdt->base + LPC18XX_WDT_FEED);
180
181	spin_unlock_irqrestore(&lpc18xx_wdt->lock, flags);
182
183	return 0;
184}
185
186static const struct watchdog_info lpc18xx_wdt_info = {
187	.identity	= "NXP LPC18xx Watchdog",
188	.options	= WDIOF_SETTIMEOUT |
189			  WDIOF_KEEPALIVEPING |
190			  WDIOF_MAGICCLOSE,
191};
192
193static const struct watchdog_ops lpc18xx_wdt_ops = {
194	.owner		= THIS_MODULE,
195	.start		= lpc18xx_wdt_start,
196	.stop		= lpc18xx_wdt_stop,
197	.ping		= lpc18xx_wdt_feed,
198	.set_timeout	= lpc18xx_wdt_set_timeout,
199	.get_timeleft	= lpc18xx_wdt_get_timeleft,
200	.restart        = lpc18xx_wdt_restart,
201};
202
203static int lpc18xx_wdt_probe(struct platform_device *pdev)
204{
205	struct lpc18xx_wdt_dev *lpc18xx_wdt;
206	struct device *dev = &pdev->dev;
207	struct resource *res;
208	int ret;
209
210	lpc18xx_wdt = devm_kzalloc(dev, sizeof(*lpc18xx_wdt), GFP_KERNEL);
211	if (!lpc18xx_wdt)
212		return -ENOMEM;
213
214	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
215	lpc18xx_wdt->base = devm_ioremap_resource(dev, res);
216	if (IS_ERR(lpc18xx_wdt->base))
217		return PTR_ERR(lpc18xx_wdt->base);
218
219	lpc18xx_wdt->reg_clk = devm_clk_get(dev, "reg");
220	if (IS_ERR(lpc18xx_wdt->reg_clk)) {
221		dev_err(dev, "failed to get the reg clock\n");
222		return PTR_ERR(lpc18xx_wdt->reg_clk);
223	}
224
225	lpc18xx_wdt->wdt_clk = devm_clk_get(dev, "wdtclk");
226	if (IS_ERR(lpc18xx_wdt->wdt_clk)) {
227		dev_err(dev, "failed to get the wdt clock\n");
228		return PTR_ERR(lpc18xx_wdt->wdt_clk);
229	}
230
231	ret = clk_prepare_enable(lpc18xx_wdt->reg_clk);
232	if (ret) {
233		dev_err(dev, "could not prepare or enable sys clock\n");
234		return ret;
235	}
236
237	ret = clk_prepare_enable(lpc18xx_wdt->wdt_clk);
238	if (ret) {
239		dev_err(dev, "could not prepare or enable wdt clock\n");
240		goto disable_reg_clk;
241	}
242
243	/* We use the clock rate to calculate timeouts */
244	lpc18xx_wdt->clk_rate = clk_get_rate(lpc18xx_wdt->wdt_clk);
245	if (lpc18xx_wdt->clk_rate == 0) {
246		dev_err(dev, "failed to get clock rate\n");
247		ret = -EINVAL;
248		goto disable_wdt_clk;
249	}
250
251	lpc18xx_wdt->wdt_dev.info = &lpc18xx_wdt_info;
252	lpc18xx_wdt->wdt_dev.ops = &lpc18xx_wdt_ops;
253
254	lpc18xx_wdt->wdt_dev.min_timeout = DIV_ROUND_UP(LPC18XX_WDT_TC_MIN *
255				LPC18XX_WDT_CLK_DIV, lpc18xx_wdt->clk_rate);
256
257	lpc18xx_wdt->wdt_dev.max_timeout = (LPC18XX_WDT_TC_MAX *
258				LPC18XX_WDT_CLK_DIV) / lpc18xx_wdt->clk_rate;
259
260	lpc18xx_wdt->wdt_dev.timeout = min(lpc18xx_wdt->wdt_dev.max_timeout,
261					   LPC18XX_WDT_DEF_TIMEOUT);
262
263	spin_lock_init(&lpc18xx_wdt->lock);
264
265	lpc18xx_wdt->wdt_dev.parent = dev;
266	watchdog_set_drvdata(&lpc18xx_wdt->wdt_dev, lpc18xx_wdt);
267
268	watchdog_init_timeout(&lpc18xx_wdt->wdt_dev, heartbeat, dev);
269
270	__lpc18xx_wdt_set_timeout(lpc18xx_wdt);
271
272	timer_setup(&lpc18xx_wdt->timer, lpc18xx_wdt_timer_feed, 0);
273
274	watchdog_set_nowayout(&lpc18xx_wdt->wdt_dev, nowayout);
275	watchdog_set_restart_priority(&lpc18xx_wdt->wdt_dev, 128);
276
277	platform_set_drvdata(pdev, lpc18xx_wdt);
278
279	ret = watchdog_register_device(&lpc18xx_wdt->wdt_dev);
280	if (ret)
281		goto disable_wdt_clk;
282
283	return 0;
284
285disable_wdt_clk:
286	clk_disable_unprepare(lpc18xx_wdt->wdt_clk);
287disable_reg_clk:
288	clk_disable_unprepare(lpc18xx_wdt->reg_clk);
289	return ret;
290}
291
292static void lpc18xx_wdt_shutdown(struct platform_device *pdev)
293{
294	struct lpc18xx_wdt_dev *lpc18xx_wdt = platform_get_drvdata(pdev);
295
296	lpc18xx_wdt_stop(&lpc18xx_wdt->wdt_dev);
297}
298
299static int lpc18xx_wdt_remove(struct platform_device *pdev)
300{
301	struct lpc18xx_wdt_dev *lpc18xx_wdt = platform_get_drvdata(pdev);
302
303	dev_warn(&pdev->dev, "I quit now, hardware will probably reboot!\n");
304	del_timer(&lpc18xx_wdt->timer);
305
306	watchdog_unregister_device(&lpc18xx_wdt->wdt_dev);
307	clk_disable_unprepare(lpc18xx_wdt->wdt_clk);
308	clk_disable_unprepare(lpc18xx_wdt->reg_clk);
309
310	return 0;
311}
312
313static const struct of_device_id lpc18xx_wdt_match[] = {
314	{ .compatible = "nxp,lpc1850-wwdt" },
315	{}
316};
317MODULE_DEVICE_TABLE(of, lpc18xx_wdt_match);
318
319static struct platform_driver lpc18xx_wdt_driver = {
320	.driver = {
321		.name = "lpc18xx-wdt",
322		.of_match_table	= lpc18xx_wdt_match,
323	},
324	.probe = lpc18xx_wdt_probe,
325	.remove = lpc18xx_wdt_remove,
326	.shutdown = lpc18xx_wdt_shutdown,
327};
328module_platform_driver(lpc18xx_wdt_driver);
329
330MODULE_AUTHOR("Ariel D'Alessandro <ariel@vanguardiasur.com.ar>");
331MODULE_DESCRIPTION("NXP LPC18xx Watchdog Timer Driver");
332MODULE_LICENSE("GPL v2");