Blame view

drivers/watchdog/sp805_wdt.c 8.74 KB
4a370278e   Viresh KUMAR   watchdog: Adding ...
1
2
3
4
5
6
  /*
   * drivers/char/watchdog/sp805-wdt.c
   *
   * Watchdog driver for ARM SP805 watchdog module
   *
   * Copyright (C) 2010 ST Microelectronics
da89947b4   Viresh Kumar   Update Viresh Kum...
7
   * Viresh Kumar <vireshk@kernel.org>
4a370278e   Viresh KUMAR   watchdog: Adding ...
8
9
10
11
12
   *
   * This file is licensed under the terms of the GNU General Public
   * License version 2 or later. This program is licensed "as is" without any
   * warranty of any kind, whether express or implied.
   */
dc0e4a3bb   Srinath Mannam   watchdog: sp805: ...
13
  #include <linux/acpi.h>
4a370278e   Viresh KUMAR   watchdog: Adding ...
14
15
16
17
18
  #include <linux/device.h>
  #include <linux/resource.h>
  #include <linux/amba/bus.h>
  #include <linux/bitops.h>
  #include <linux/clk.h>
4a370278e   Viresh KUMAR   watchdog: Adding ...
19
20
21
22
  #include <linux/io.h>
  #include <linux/ioport.h>
  #include <linux/kernel.h>
  #include <linux/math64.h>
4a370278e   Viresh KUMAR   watchdog: Adding ...
23
24
  #include <linux/module.h>
  #include <linux/moduleparam.h>
dc0e4a3bb   Srinath Mannam   watchdog: sp805: ...
25
  #include <linux/of.h>
16ac4abe0   Viresh Kumar   watchdog: sp805_w...
26
  #include <linux/pm.h>
4a370278e   Viresh KUMAR   watchdog: Adding ...
27
28
29
  #include <linux/slab.h>
  #include <linux/spinlock.h>
  #include <linux/types.h>
4a370278e   Viresh KUMAR   watchdog: Adding ...
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
  #include <linux/watchdog.h>
  
  /* default timeout in seconds */
  #define DEFAULT_TIMEOUT		60
  
  #define MODULE_NAME		"sp805-wdt"
  
  /* watchdog register offsets and masks */
  #define WDTLOAD			0x000
  	#define LOAD_MIN	0x00000001
  	#define LOAD_MAX	0xFFFFFFFF
  #define WDTVALUE		0x004
  #define WDTCONTROL		0x008
  	/* control register masks */
  	#define	INT_ENABLE	(1 << 0)
  	#define	RESET_ENABLE	(1 << 1)
fa5072ed8   Ray Jui   watchdog: sp805: ...
46
  	#define	ENABLE_MASK	(INT_ENABLE | RESET_ENABLE)
4a370278e   Viresh KUMAR   watchdog: Adding ...
47
48
49
50
51
52
53
54
55
56
  #define WDTINTCLR		0x00C
  #define WDTRIS			0x010
  #define WDTMIS			0x014
  	#define INT_MASK	(1 << 0)
  #define WDTLOCK			0xC00
  	#define	UNLOCK		0x1ACCE551
  	#define	LOCK		0x00000001
  
  /**
   * struct sp805_wdt: sp805 wdt device structure
4a516539f   Viresh Kumar   watchdog: sp805_w...
57
   * @wdd: instance of struct watchdog_device
bfae14b67   Viresh Kumar   watchdog: sp805: ...
58
59
60
61
62
63
   * @lock: spin lock protecting dev structure and io access
   * @base: base address of wdt
   * @clk: clock structure of wdt
   * @adev: amba device structure of wdt
   * @status: current status of wdt
   * @load_val: load value to be set for current timeout
4a370278e   Viresh KUMAR   watchdog: Adding ...
64
65
   */
  struct sp805_wdt {
4a516539f   Viresh Kumar   watchdog: sp805_w...
66
  	struct watchdog_device		wdd;
4a370278e   Viresh KUMAR   watchdog: Adding ...
67
68
69
  	spinlock_t			lock;
  	void __iomem			*base;
  	struct clk			*clk;
dc0e4a3bb   Srinath Mannam   watchdog: sp805: ...
70
  	u64				rate;
4a370278e   Viresh KUMAR   watchdog: Adding ...
71
  	struct amba_device		*adev;
4a370278e   Viresh KUMAR   watchdog: Adding ...
72
  	unsigned int			load_val;
4a370278e   Viresh KUMAR   watchdog: Adding ...
73
  };
86a1e1896   Wim Van Sebroeck   watchdog: nowayou...
74
  static bool nowayout = WATCHDOG_NOWAYOUT;
4a516539f   Viresh Kumar   watchdog: sp805_w...
75
76
77
  module_param(nowayout, bool, 0);
  MODULE_PARM_DESC(nowayout,
  		"Set to 1 to keep watchdog running after device release");
4a370278e   Viresh KUMAR   watchdog: Adding ...
78

fa5072ed8   Ray Jui   watchdog: sp805: ...
79
80
81
82
83
84
85
86
  /* returns true if wdt is running; otherwise returns false */
  static bool wdt_is_running(struct watchdog_device *wdd)
  {
  	struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
  	u32 wdtcontrol = readl_relaxed(wdt->base + WDTCONTROL);
  
  	return (wdtcontrol & ENABLE_MASK) == ENABLE_MASK;
  }
4a370278e   Viresh KUMAR   watchdog: Adding ...
87
  /* This routine finds load value that will reset system in required timout */
4a516539f   Viresh Kumar   watchdog: sp805_w...
88
  static int wdt_setload(struct watchdog_device *wdd, unsigned int timeout)
4a370278e   Viresh KUMAR   watchdog: Adding ...
89
  {
4a516539f   Viresh Kumar   watchdog: sp805_w...
90
  	struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
4a370278e   Viresh KUMAR   watchdog: Adding ...
91
  	u64 load, rate;
dc0e4a3bb   Srinath Mannam   watchdog: sp805: ...
92
  	rate = wdt->rate;
4a370278e   Viresh KUMAR   watchdog: Adding ...
93
94
95
96
  
  	/*
  	 * sp805 runs counter with given value twice, after the end of first
  	 * counter it gives an interrupt and then starts counter again. If
25985edce   Lucas De Marchi   Fix common misspe...
97
  	 * interrupt already occurred then it resets the system. This is why
4a370278e   Viresh KUMAR   watchdog: Adding ...
98
99
100
101
102
103
104
105
106
107
  	 * load is half of what should be required.
  	 */
  	load = div_u64(rate, 2) * timeout - 1;
  
  	load = (load > LOAD_MAX) ? LOAD_MAX : load;
  	load = (load < LOAD_MIN) ? LOAD_MIN : load;
  
  	spin_lock(&wdt->lock);
  	wdt->load_val = load;
  	/* roundup timeout to closest positive integer value */
938626d96   Viresh Kumar   watchdog: sp805: ...
108
  	wdd->timeout = div_u64((load + 1) * 2 + (rate / 2), rate);
4a370278e   Viresh KUMAR   watchdog: Adding ...
109
  	spin_unlock(&wdt->lock);
4a516539f   Viresh Kumar   watchdog: sp805_w...
110
111
  
  	return 0;
4a370278e   Viresh KUMAR   watchdog: Adding ...
112
113
114
  }
  
  /* returns number of seconds left for reset to occur */
4a516539f   Viresh Kumar   watchdog: sp805_w...
115
  static unsigned int wdt_timeleft(struct watchdog_device *wdd)
4a370278e   Viresh KUMAR   watchdog: Adding ...
116
  {
4a516539f   Viresh Kumar   watchdog: sp805_w...
117
  	struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
dc0e4a3bb   Srinath Mannam   watchdog: sp805: ...
118
  	u64 load;
4a370278e   Viresh KUMAR   watchdog: Adding ...
119
120
  
  	spin_lock(&wdt->lock);
d2e8919bc   Viresh Kumar   watchdog: sp805: ...
121
  	load = readl_relaxed(wdt->base + WDTVALUE);
4a370278e   Viresh KUMAR   watchdog: Adding ...
122
123
  
  	/*If the interrupt is inactive then time left is WDTValue + WDTLoad. */
d2e8919bc   Viresh Kumar   watchdog: sp805: ...
124
  	if (!(readl_relaxed(wdt->base + WDTRIS) & INT_MASK))
4a370278e   Viresh KUMAR   watchdog: Adding ...
125
126
  		load += wdt->load_val + 1;
  	spin_unlock(&wdt->lock);
dc0e4a3bb   Srinath Mannam   watchdog: sp805: ...
127
  	return div_u64(load, wdt->rate);
4a370278e   Viresh KUMAR   watchdog: Adding ...
128
  }
6c5c0d48b   Jongsung Kim   watchdog: sp805: ...
129
130
131
132
  static int
  wdt_restart(struct watchdog_device *wdd, unsigned long mode, void *cmd)
  {
  	struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
9a9eae785   Michael Walle   watchdog: sp805: ...
133
  	writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
6c5c0d48b   Jongsung Kim   watchdog: sp805: ...
134
135
136
  	writel_relaxed(0, wdt->base + WDTCONTROL);
  	writel_relaxed(0, wdt->base + WDTLOAD);
  	writel_relaxed(INT_ENABLE | RESET_ENABLE, wdt->base + WDTCONTROL);
9a9eae785   Michael Walle   watchdog: sp805: ...
137
138
  	/* Flush posted writes. */
  	readl_relaxed(wdt->base + WDTLOCK);
6c5c0d48b   Jongsung Kim   watchdog: sp805: ...
139
140
  	return 0;
  }
4a516539f   Viresh Kumar   watchdog: sp805_w...
141
  static int wdt_config(struct watchdog_device *wdd, bool ping)
4a370278e   Viresh KUMAR   watchdog: Adding ...
142
  {
4a516539f   Viresh Kumar   watchdog: sp805_w...
143
144
145
146
  	struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
  	int ret;
  
  	if (!ping) {
d9df0ef1e   Viresh Kumar   watchdog: sp805_w...
147

63fbbc169   Julia Lawall   watchdog: sp805_w...
148
  		ret = clk_prepare_enable(wdt->clk);
4a516539f   Viresh Kumar   watchdog: sp805_w...
149
150
151
152
153
  		if (ret) {
  			dev_err(&wdt->adev->dev, "clock enable fail");
  			return ret;
  		}
  	}
4a370278e   Viresh KUMAR   watchdog: Adding ...
154
  	spin_lock(&wdt->lock);
d2e8919bc   Viresh Kumar   watchdog: sp805: ...
155
156
  	writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
  	writel_relaxed(wdt->load_val, wdt->base + WDTLOAD);
55e071779   Sandeep Tripathy   watchdog: sp805: ...
157
  	writel_relaxed(INT_MASK, wdt->base + WDTINTCLR);
4a370278e   Viresh KUMAR   watchdog: Adding ...
158

55e071779   Sandeep Tripathy   watchdog: sp805: ...
159
  	if (!ping)
4a516539f   Viresh Kumar   watchdog: sp805_w...
160
161
  		writel_relaxed(INT_ENABLE | RESET_ENABLE, wdt->base +
  				WDTCONTROL);
4a370278e   Viresh KUMAR   watchdog: Adding ...
162

d2e8919bc   Viresh Kumar   watchdog: sp805: ...
163
  	writel_relaxed(LOCK, wdt->base + WDTLOCK);
4a370278e   Viresh KUMAR   watchdog: Adding ...
164

081d83a33   Nick Bowler   watchdog: sp805: ...
165
  	/* Flush posted writes. */
d2e8919bc   Viresh Kumar   watchdog: sp805: ...
166
  	readl_relaxed(wdt->base + WDTLOCK);
4a370278e   Viresh KUMAR   watchdog: Adding ...
167
  	spin_unlock(&wdt->lock);
4a516539f   Viresh Kumar   watchdog: sp805_w...
168
169
  
  	return 0;
4a370278e   Viresh KUMAR   watchdog: Adding ...
170
  }
4a516539f   Viresh Kumar   watchdog: sp805_w...
171
  static int wdt_ping(struct watchdog_device *wdd)
4a370278e   Viresh KUMAR   watchdog: Adding ...
172
  {
4a516539f   Viresh Kumar   watchdog: sp805_w...
173
  	return wdt_config(wdd, true);
4a370278e   Viresh KUMAR   watchdog: Adding ...
174
  }
4a516539f   Viresh Kumar   watchdog: sp805_w...
175
176
  /* enables watchdog timers reset */
  static int wdt_enable(struct watchdog_device *wdd)
4a370278e   Viresh KUMAR   watchdog: Adding ...
177
  {
4a516539f   Viresh Kumar   watchdog: sp805_w...
178
  	return wdt_config(wdd, false);
4a370278e   Viresh KUMAR   watchdog: Adding ...
179
  }
4a516539f   Viresh Kumar   watchdog: sp805_w...
180
181
  /* disables watchdog timers reset */
  static int wdt_disable(struct watchdog_device *wdd)
4a370278e   Viresh KUMAR   watchdog: Adding ...
182
  {
4a516539f   Viresh Kumar   watchdog: sp805_w...
183
  	struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
4a370278e   Viresh KUMAR   watchdog: Adding ...
184

4a516539f   Viresh Kumar   watchdog: sp805_w...
185
  	spin_lock(&wdt->lock);
4a370278e   Viresh KUMAR   watchdog: Adding ...
186

4a516539f   Viresh Kumar   watchdog: sp805_w...
187
188
189
  	writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
  	writel_relaxed(0, wdt->base + WDTCONTROL);
  	writel_relaxed(LOCK, wdt->base + WDTLOCK);
4a370278e   Viresh KUMAR   watchdog: Adding ...
190

4a516539f   Viresh Kumar   watchdog: sp805_w...
191
192
193
  	/* Flush posted writes. */
  	readl_relaxed(wdt->base + WDTLOCK);
  	spin_unlock(&wdt->lock);
4a370278e   Viresh KUMAR   watchdog: Adding ...
194

63fbbc169   Julia Lawall   watchdog: sp805_w...
195
  	clk_disable_unprepare(wdt->clk);
4a370278e   Viresh KUMAR   watchdog: Adding ...
196
197
198
  
  	return 0;
  }
4a516539f   Viresh Kumar   watchdog: sp805_w...
199
200
201
  static const struct watchdog_info wdt_info = {
  	.options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
  	.identity = MODULE_NAME,
4a370278e   Viresh KUMAR   watchdog: Adding ...
202
  };
4a516539f   Viresh Kumar   watchdog: sp805_w...
203
204
205
206
207
208
209
  static const struct watchdog_ops wdt_ops = {
  	.owner		= THIS_MODULE,
  	.start		= wdt_enable,
  	.stop		= wdt_disable,
  	.ping		= wdt_ping,
  	.set_timeout	= wdt_setload,
  	.get_timeleft	= wdt_timeleft,
6c5c0d48b   Jongsung Kim   watchdog: sp805: ...
210
  	.restart	= wdt_restart,
4a370278e   Viresh KUMAR   watchdog: Adding ...
211
  };
2d991a164   Bill Pemberton   watchdog: remove ...
212
  static int
aa25afad2   Russell King   ARM: amba: make p...
213
  sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
4a370278e   Viresh KUMAR   watchdog: Adding ...
214
  {
4a516539f   Viresh Kumar   watchdog: sp805_w...
215
  	struct sp805_wdt *wdt;
4a370278e   Viresh KUMAR   watchdog: Adding ...
216
  	int ret = 0;
fb35a5ad5   Viresh Kumar   watchdog: sp805: ...
217
  	wdt = devm_kzalloc(&adev->dev, sizeof(*wdt), GFP_KERNEL);
4a370278e   Viresh KUMAR   watchdog: Adding ...
218
  	if (!wdt) {
4a370278e   Viresh KUMAR   watchdog: Adding ...
219
  		ret = -ENOMEM;
fb35a5ad5   Viresh Kumar   watchdog: sp805: ...
220
221
  		goto err;
  	}
9d11e4f8c   Jingoo Han   watchdog: sp805_w...
222
223
224
  	wdt->base = devm_ioremap_resource(&adev->dev, &adev->res);
  	if (IS_ERR(wdt->base))
  		return PTR_ERR(wdt->base);
4a370278e   Viresh KUMAR   watchdog: Adding ...
225

dc0e4a3bb   Srinath Mannam   watchdog: sp805: ...
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
  	if (adev->dev.of_node) {
  		wdt->clk = devm_clk_get(&adev->dev, NULL);
  		if (IS_ERR(wdt->clk)) {
  			dev_err(&adev->dev, "Clock not found
  ");
  			return PTR_ERR(wdt->clk);
  		}
  		wdt->rate = clk_get_rate(wdt->clk);
  	} else if (has_acpi_companion(&adev->dev)) {
  		/*
  		 * When Driver probe with ACPI device, clock devices
  		 * are not available, so watchdog rate get from
  		 * clock-frequency property given in _DSD object.
  		 */
  		device_property_read_u64(&adev->dev, "clock-frequency",
  					 &wdt->rate);
  		if (!wdt->rate) {
  			dev_err(&adev->dev, "no clock-frequency property
  ");
  			return -ENODEV;
  		}
4a370278e   Viresh KUMAR   watchdog: Adding ...
247
248
249
  	}
  
  	wdt->adev = adev;
4a516539f   Viresh Kumar   watchdog: sp805_w...
250
251
  	wdt->wdd.info = &wdt_info;
  	wdt->wdd.ops = &wdt_ops;
6551881c8   Pratyush Anand   Watchdog: Fix par...
252
  	wdt->wdd.parent = &adev->dev;
4a516539f   Viresh Kumar   watchdog: sp805_w...
253

4a370278e   Viresh KUMAR   watchdog: Adding ...
254
  	spin_lock_init(&wdt->lock);
4a516539f   Viresh Kumar   watchdog: sp805_w...
255
256
  	watchdog_set_nowayout(&wdt->wdd, nowayout);
  	watchdog_set_drvdata(&wdt->wdd, wdt);
6c5c0d48b   Jongsung Kim   watchdog: sp805: ...
257
  	watchdog_set_restart_priority(&wdt->wdd, 128);
b80088588   Ray Jui   watchdog: sp805: ...
258
259
260
261
262
263
264
265
  
  	/*
  	 * If 'timeout-sec' devicetree property is specified, use that.
  	 * Otherwise, use DEFAULT_TIMEOUT
  	 */
  	wdt->wdd.timeout = DEFAULT_TIMEOUT;
  	watchdog_init_timeout(&wdt->wdd, 0, &adev->dev);
  	wdt_setload(&wdt->wdd, wdt->wdd.timeout);
4a370278e   Viresh KUMAR   watchdog: Adding ...
266

fa5072ed8   Ray Jui   watchdog: sp805: ...
267
268
269
270
271
272
273
274
  	/*
  	 * If HW is already running, enable/reset the wdt and set the running
  	 * bit to tell the wdt subsystem
  	 */
  	if (wdt_is_running(&wdt->wdd)) {
  		wdt_enable(&wdt->wdd);
  		set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
  	}
4a516539f   Viresh Kumar   watchdog: sp805_w...
275
  	ret = watchdog_register_device(&wdt->wdd);
199801cd7   Wolfram Sang   watchdog: sp805_w...
276
  	if (ret)
07bf971a3   Jingoo Han   watchdog: sp805_w...
277
  		goto err;
4a516539f   Viresh Kumar   watchdog: sp805_w...
278
  	amba_set_drvdata(adev, wdt);
4a370278e   Viresh KUMAR   watchdog: Adding ...
279
280
281
282
  
  	dev_info(&adev->dev, "registration successful
  ");
  	return 0;
4a370278e   Viresh KUMAR   watchdog: Adding ...
283
284
285
286
287
  err:
  	dev_err(&adev->dev, "Probe Failed!!!
  ");
  	return ret;
  }
4b12b896c   Bill Pemberton   watchdog: remove ...
288
  static int sp805_wdt_remove(struct amba_device *adev)
4a370278e   Viresh KUMAR   watchdog: Adding ...
289
  {
4a516539f   Viresh Kumar   watchdog: sp805_w...
290
291
292
  	struct sp805_wdt *wdt = amba_get_drvdata(adev);
  
  	watchdog_unregister_device(&wdt->wdd);
4a516539f   Viresh Kumar   watchdog: sp805_w...
293
  	watchdog_set_drvdata(&wdt->wdd, NULL);
4a370278e   Viresh KUMAR   watchdog: Adding ...
294
295
296
  
  	return 0;
  }
60d6dd530   Russell King   WATCHDOG: fix bui...
297
  static int __maybe_unused sp805_wdt_suspend(struct device *dev)
16ac4abe0   Viresh Kumar   watchdog: sp805_w...
298
  {
4a516539f   Viresh Kumar   watchdog: sp805_w...
299
300
301
302
  	struct sp805_wdt *wdt = dev_get_drvdata(dev);
  
  	if (watchdog_active(&wdt->wdd))
  		return wdt_disable(&wdt->wdd);
16ac4abe0   Viresh Kumar   watchdog: sp805_w...
303
304
305
  
  	return 0;
  }
60d6dd530   Russell King   WATCHDOG: fix bui...
306
  static int __maybe_unused sp805_wdt_resume(struct device *dev)
16ac4abe0   Viresh Kumar   watchdog: sp805_w...
307
  {
4a516539f   Viresh Kumar   watchdog: sp805_w...
308
  	struct sp805_wdt *wdt = dev_get_drvdata(dev);
16ac4abe0   Viresh Kumar   watchdog: sp805_w...
309

4a516539f   Viresh Kumar   watchdog: sp805_w...
310
311
  	if (watchdog_active(&wdt->wdd))
  		return wdt_enable(&wdt->wdd);
16ac4abe0   Viresh Kumar   watchdog: sp805_w...
312

4a516539f   Viresh Kumar   watchdog: sp805_w...
313
  	return 0;
16ac4abe0   Viresh Kumar   watchdog: sp805_w...
314
  }
16ac4abe0   Viresh Kumar   watchdog: sp805_w...
315
316
317
  
  static SIMPLE_DEV_PM_OPS(sp805_wdt_dev_pm_ops, sp805_wdt_suspend,
  		sp805_wdt_resume);
05ce42fff   Arvind Yadav   watchdog: sp805: ...
318
  static const struct amba_id sp805_wdt_ids[] = {
4a370278e   Viresh KUMAR   watchdog: Adding ...
319
320
321
322
323
324
  	{
  		.id	= 0x00141805,
  		.mask	= 0x00ffffff,
  	},
  	{ 0, 0 },
  };
17885b05b   Dave Martin   watchdog: sp805: ...
325
  MODULE_DEVICE_TABLE(amba, sp805_wdt_ids);
4a370278e   Viresh KUMAR   watchdog: Adding ...
326
327
328
  static struct amba_driver sp805_wdt_driver = {
  	.drv = {
  		.name	= MODULE_NAME,
16ac4abe0   Viresh Kumar   watchdog: sp805_w...
329
  		.pm	= &sp805_wdt_dev_pm_ops,
4a370278e   Viresh KUMAR   watchdog: Adding ...
330
331
332
  	},
  	.id_table	= sp805_wdt_ids,
  	.probe		= sp805_wdt_probe,
82268714b   Bill Pemberton   watchdog: remove ...
333
  	.remove = sp805_wdt_remove,
4a370278e   Viresh KUMAR   watchdog: Adding ...
334
  };
9e5ed094c   viresh kumar   ARM: 7362/1: AMBA...
335
  module_amba_driver(sp805_wdt_driver);
4a370278e   Viresh KUMAR   watchdog: Adding ...
336

da89947b4   Viresh Kumar   Update Viresh Kum...
337
  MODULE_AUTHOR("Viresh Kumar <vireshk@kernel.org>");
4a370278e   Viresh KUMAR   watchdog: Adding ...
338
339
  MODULE_DESCRIPTION("ARM SP805 Watchdog Driver");
  MODULE_LICENSE("GPL");