Blame view

drivers/watchdog/max63xx_wdt.c 6.99 KB
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  /*
   * drivers/char/watchdog/max63xx_wdt.c
   *
   * Driver for max63{69,70,71,72,73,74} watchdog timers
   *
   * Copyright (C) 2009 Marc Zyngier <maz@misterjones.org>
   *
   * This file is licensed under the terms of the GNU General Public
   * License version 2. This program is licensed "as is" without any
   * warranty of any kind, whether express or implied.
   *
   * This driver assumes the watchdog pins are memory mapped (as it is
   * the case for the Arcom Zeus). Should it be connected over GPIOs or
   * another interface, some abstraction will have to be introduced.
   */
4c271bb67   Thierry Reding   watchdog: Convert...
16
  #include <linux/err.h>
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
17
18
19
20
  #include <linux/module.h>
  #include <linux/moduleparam.h>
  #include <linux/types.h>
  #include <linux/kernel.h>
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
21
  #include <linux/watchdog.h>
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
22
23
24
  #include <linux/bitops.h>
  #include <linux/platform_device.h>
  #include <linux/spinlock.h>
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
25
  #include <linux/io.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
26
  #include <linux/slab.h>
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
27
28
29
  
  #define DEFAULT_HEARTBEAT 60
  #define MAX_HEARTBEAT     60
a0f368336   Axel Lin   watchdog: Convert...
30
  static unsigned int heartbeat = DEFAULT_HEARTBEAT;
86a1e1896   Wim Van Sebroeck   watchdog: nowayou...
31
  static bool nowayout  = WATCHDOG_NOWAYOUT;
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
32
33
34
35
36
37
  
  /*
   * Memory mapping: a single byte, 3 first lower bits to select bit 3
   * to ping the watchdog.
   */
  #define MAX6369_WDSET	(7 << 0)
5f3b27569   Wim Van Sebroeck   watchdog: cleanup...
38
  #define MAX6369_WDI	(1 << 3)
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
39

b9be9660b   Vivien Didelot   watchdog: max63xx...
40
  #define MAX6369_WDSET_DISABLED	3
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
41

66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
42
  static int nodelay;
b9be9660b   Vivien Didelot   watchdog: max63xx...
43
44
45
46
47
48
49
50
51
52
53
54
55
  
  struct max63xx_wdt {
  	struct watchdog_device wdd;
  	const struct max63xx_timeout *timeout;
  
  	/* memory mapping */
  	void __iomem *base;
  	spinlock_t lock;
  
  	/* WDI and WSET bits write access routines */
  	void (*ping)(struct max63xx_wdt *wdt);
  	void (*set)(struct max63xx_wdt *wdt, u8 set);
  };
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
  
  /*
   * The timeout values used are actually the absolute minimum the chip
   * offers. Typical values on my board are slightly over twice as long
   * (10s setting ends up with a 25s timeout), and can be up to 3 times
   * the nominal setting (according to the datasheet). So please take
   * these values with a grain of salt. Same goes for the initial delay
   * "feature". Only max6373/74 have a few settings without this initial
   * delay (selected with the "nodelay" parameter).
   *
   * I also decided to remove from the tables any timeout smaller than a
   * second, as it looked completly overkill...
   */
  
  /* Timeouts in second */
  struct max63xx_timeout {
b9be9660b   Vivien Didelot   watchdog: max63xx...
72
73
74
  	const u8 wdset;
  	const u8 tdelay;
  	const u8 twd;
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
75
  };
b9be9660b   Vivien Didelot   watchdog: max63xx...
76
  static const struct max63xx_timeout max6369_table[] = {
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
77
78
79
80
81
  	{ 5,  1,  1 },
  	{ 6, 10, 10 },
  	{ 7, 60, 60 },
  	{ },
  };
b9be9660b   Vivien Didelot   watchdog: max63xx...
82
  static const struct max63xx_timeout max6371_table[] = {
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
83
84
85
86
  	{ 6, 60,  3 },
  	{ 7, 60, 60 },
  	{ },
  };
b9be9660b   Vivien Didelot   watchdog: max63xx...
87
  static const struct max63xx_timeout max6373_table[] = {
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
88
89
90
91
92
93
94
  	{ 2, 60,  1 },
  	{ 5,  0,  1 },
  	{ 1,  3,  3 },
  	{ 7, 60, 10 },
  	{ 6,  0, 10 },
  	{ },
  };
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
  static struct max63xx_timeout *
  max63xx_select_timeout(struct max63xx_timeout *table, int value)
  {
  	while (table->twd) {
  		if (value <= table->twd) {
  			if (nodelay && table->tdelay == 0)
  				return table;
  
  			if (!nodelay)
  				return table;
  		}
  
  		table++;
  	}
  
  	return NULL;
  }
a0f368336   Axel Lin   watchdog: Convert...
112
  static int max63xx_wdt_ping(struct watchdog_device *wdd)
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
113
  {
b9be9660b   Vivien Didelot   watchdog: max63xx...
114
  	struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd);
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
115

b9be9660b   Vivien Didelot   watchdog: max63xx...
116
  	wdt->ping(wdt);
a0f368336   Axel Lin   watchdog: Convert...
117
  	return 0;
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
118
  }
a0f368336   Axel Lin   watchdog: Convert...
119
  static int max63xx_wdt_start(struct watchdog_device *wdd)
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
120
  {
b9be9660b   Vivien Didelot   watchdog: max63xx...
121
  	struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd);
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
122

b9be9660b   Vivien Didelot   watchdog: max63xx...
123
  	wdt->set(wdt, wdt->timeout->wdset);
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
124
125
  
  	/* check for a edge triggered startup */
b9be9660b   Vivien Didelot   watchdog: max63xx...
126
127
  	if (wdt->timeout->tdelay == 0)
  		wdt->ping(wdt);
a0f368336   Axel Lin   watchdog: Convert...
128
  	return 0;
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
129
  }
a0f368336   Axel Lin   watchdog: Convert...
130
  static int max63xx_wdt_stop(struct watchdog_device *wdd)
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
131
  {
b9be9660b   Vivien Didelot   watchdog: max63xx...
132
  	struct max63xx_wdt *wdt = watchdog_get_drvdata(wdd);
b1183e064   Marc Zyngier   [WATCHDOG] max63x...
133

b9be9660b   Vivien Didelot   watchdog: max63xx...
134
  	wdt->set(wdt, MAX6369_WDSET_DISABLED);
a0f368336   Axel Lin   watchdog: Convert...
135
  	return 0;
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
136
  }
a0f368336   Axel Lin   watchdog: Convert...
137
138
139
140
141
  static const struct watchdog_ops max63xx_wdt_ops = {
  	.owner = THIS_MODULE,
  	.start = max63xx_wdt_start,
  	.stop = max63xx_wdt_stop,
  	.ping = max63xx_wdt_ping,
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
142
  };
b9be9660b   Vivien Didelot   watchdog: max63xx...
143
144
145
  static const struct watchdog_info max63xx_wdt_info = {
  	.options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
  	.identity = "max63xx Watchdog",
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
146
  };
b9be9660b   Vivien Didelot   watchdog: max63xx...
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
  static void max63xx_mmap_ping(struct max63xx_wdt *wdt)
  {
  	u8 val;
  
  	spin_lock(&wdt->lock);
  
  	val = __raw_readb(wdt->base);
  
  	__raw_writeb(val | MAX6369_WDI, wdt->base);
  	__raw_writeb(val & ~MAX6369_WDI, wdt->base);
  
  	spin_unlock(&wdt->lock);
  }
  
  static void max63xx_mmap_set(struct max63xx_wdt *wdt, u8 set)
  {
  	u8 val;
  
  	spin_lock(&wdt->lock);
  
  	val = __raw_readb(wdt->base);
  	val &= ~MAX6369_WDSET;
  	val |= set & MAX6369_WDSET;
  	__raw_writeb(val, wdt->base);
  
  	spin_unlock(&wdt->lock);
  }
  
  static int max63xx_mmap_init(struct platform_device *p, struct max63xx_wdt *wdt)
  {
  	struct resource *mem = platform_get_resource(p, IORESOURCE_MEM, 0);
  
  	wdt->base = devm_ioremap_resource(&p->dev, mem);
  	if (IS_ERR(wdt->base))
  		return PTR_ERR(wdt->base);
  
  	spin_lock_init(&wdt->lock);
  
  	wdt->ping = max63xx_mmap_ping;
  	wdt->set = max63xx_mmap_set;
  	return 0;
  }
2d991a164   Bill Pemberton   watchdog: remove ...
189
  static int max63xx_wdt_probe(struct platform_device *pdev)
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
190
  {
b9be9660b   Vivien Didelot   watchdog: max63xx...
191
  	struct max63xx_wdt *wdt;
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
192
  	struct max63xx_timeout *table;
b9be9660b   Vivien Didelot   watchdog: max63xx...
193
194
195
196
197
  	int err;
  
  	wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
  	if (!wdt)
  		return -ENOMEM;
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
198
199
200
201
202
  
  	table = (struct max63xx_timeout *)pdev->id_entry->driver_data;
  
  	if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
  		heartbeat = DEFAULT_HEARTBEAT;
b9be9660b   Vivien Didelot   watchdog: max63xx...
203
204
205
206
207
  	wdt->timeout = max63xx_select_timeout(table, heartbeat);
  	if (!wdt->timeout) {
  		dev_err(&pdev->dev, "unable to satisfy %ds heartbeat request
  ",
  			heartbeat);
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
208
209
  		return -EINVAL;
  	}
b9be9660b   Vivien Didelot   watchdog: max63xx...
210
211
212
213
214
215
  	err = max63xx_mmap_init(pdev, wdt);
  	if (err)
  		return err;
  
  	platform_set_drvdata(pdev, &wdt->wdd);
  	watchdog_set_drvdata(&wdt->wdd, wdt);
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
216

b9be9660b   Vivien Didelot   watchdog: max63xx...
217
218
219
220
  	wdt->wdd.parent = &pdev->dev;
  	wdt->wdd.timeout = wdt->timeout->twd;
  	wdt->wdd.info = &max63xx_wdt_info;
  	wdt->wdd.ops = &max63xx_wdt_ops;
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
221

b9be9660b   Vivien Didelot   watchdog: max63xx...
222
  	watchdog_set_nowayout(&wdt->wdd, nowayout);
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
223

b9be9660b   Vivien Didelot   watchdog: max63xx...
224
225
226
  	err = watchdog_register_device(&wdt->wdd);
  	if (err)
  		return err;
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
227

b9be9660b   Vivien Didelot   watchdog: max63xx...
228
229
230
231
  	dev_info(&pdev->dev, "using %ds heartbeat with %ds initial delay
  ",
  		 wdt->timeout->twd, wdt->timeout->tdelay);
  	return 0;
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
232
  }
4b12b896c   Bill Pemberton   watchdog: remove ...
233
  static int max63xx_wdt_remove(struct platform_device *pdev)
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
234
  {
b9be9660b   Vivien Didelot   watchdog: max63xx...
235
236
237
  	struct watchdog_device *wdd = platform_get_drvdata(pdev);
  
  	watchdog_unregister_device(wdd);
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
238
239
  	return 0;
  }
8c7c72c9b   Krzysztof Kozlowski   watchdog: max63xx...
240
  static const struct platform_device_id max63xx_id_table[] = {
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
241
242
243
244
245
246
247
248
249
250
251
252
  	{ "max6369_wdt", (kernel_ulong_t)max6369_table, },
  	{ "max6370_wdt", (kernel_ulong_t)max6369_table, },
  	{ "max6371_wdt", (kernel_ulong_t)max6371_table, },
  	{ "max6372_wdt", (kernel_ulong_t)max6371_table, },
  	{ "max6373_wdt", (kernel_ulong_t)max6373_table, },
  	{ "max6374_wdt", (kernel_ulong_t)max6373_table, },
  	{ },
  };
  MODULE_DEVICE_TABLE(platform, max63xx_id_table);
  
  static struct platform_driver max63xx_wdt_driver = {
  	.probe		= max63xx_wdt_probe,
82268714b   Bill Pemberton   watchdog: remove ...
253
  	.remove		= max63xx_wdt_remove,
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
254
255
256
  	.id_table	= max63xx_id_table,
  	.driver		= {
  		.name	= "max63xx_wdt",
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
257
258
  	},
  };
b8ec61189   Axel Lin   watchdog: convert...
259
  module_platform_driver(max63xx_wdt_driver);
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
260
261
262
263
264
265
266
267
268
  
  MODULE_AUTHOR("Marc Zyngier <maz@misterjones.org>");
  MODULE_DESCRIPTION("max63xx Watchdog Driver");
  
  module_param(heartbeat, int, 0);
  MODULE_PARM_DESC(heartbeat,
  		 "Watchdog heartbeat period in seconds from 1 to "
  		 __MODULE_STRING(MAX_HEARTBEAT) ", default "
  		 __MODULE_STRING(DEFAULT_HEARTBEAT));
86a1e1896   Wim Van Sebroeck   watchdog: nowayou...
269
  module_param(nowayout, bool, 0);
66aaa7a55   Marc Zyngier   [WATCHDOG] suppor...
270
271
272
273
274
275
276
  MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
  		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  
  module_param(nodelay, int, 0);
  MODULE_PARM_DESC(nodelay,
  		 "Force selection of a timeout setting without initial delay "
  		 "(max6373/74 only, default=0)");
29efefb90   Uwe Kleine-König   watchdog: max63xx...
277
  MODULE_LICENSE("GPL v2");