Blame view

drivers/watchdog/ziirave_wdt.c 18.6 KB
c942fddf8   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
2
3
4
5
6
7
8
9
  /*
   * Copyright (C) 2015 Zodiac Inflight Innovations
   *
   * Author: Martyn Welch <martyn.welch@collabora.co.uk>
   *
   * Based on twl4030_wdt.c by Timo Kokkonen <timo.t.kokkonen at nokia.com>:
   *
   * Copyright (C) Nokia Corporation
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
10
   */
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
11
  #include <linux/delay.h>
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
12
  #include <linux/i2c.h>
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
13
14
  #include <linux/ihex.h>
  #include <linux/firmware.h>
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
15
16
17
18
19
20
21
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/slab.h>
  #include <linux/sysfs.h>
  #include <linux/types.h>
  #include <linux/version.h>
  #include <linux/watchdog.h>
08f980a8f   Andrey Smirnov   watchdog: ziirave...
22
  #include <asm/unaligned.h>
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
23
24
  #define ZIIRAVE_TIMEOUT_MIN	3
  #define ZIIRAVE_TIMEOUT_MAX	255
39d0387d5   Andrey Smirnov   watchdog: ziirave...
25
  #define ZIIRAVE_TIMEOUT_DEFAULT	30
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
26
27
28
29
30
31
  
  #define ZIIRAVE_PING_VALUE	0x0
  
  #define ZIIRAVE_STATE_INITIAL	0x0
  #define ZIIRAVE_STATE_OFF	0x1
  #define ZIIRAVE_STATE_ON	0x2
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
32
  #define ZIIRAVE_FW_NAME		"ziirave_wdt.fw"
0ce72f354   Martyn Welch   watchdog: ziirave...
33
  static char *ziirave_reasons[] = {"power cycle", "hw watchdog", NULL, NULL,
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
34
35
36
37
38
39
40
41
42
43
44
45
  				  "host request", NULL, "illegal configuration",
  				  "illegal instruction", "illegal trap",
  				  "unknown"};
  
  #define ZIIRAVE_WDT_FIRM_VER_MAJOR	0x1
  #define ZIIRAVE_WDT_BOOT_VER_MAJOR	0x3
  #define ZIIRAVE_WDT_RESET_REASON	0x5
  #define ZIIRAVE_WDT_STATE		0x6
  #define ZIIRAVE_WDT_TIMEOUT		0x7
  #define ZIIRAVE_WDT_TIME_LEFT		0x8
  #define ZIIRAVE_WDT_PING		0x9
  #define ZIIRAVE_WDT_RESET_DURATION	0xa
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
46
47
  #define ZIIRAVE_FIRM_PKT_TOTAL_SIZE	20
  #define ZIIRAVE_FIRM_PKT_DATA_SIZE	16
d2ddc4505   Andrey Smirnov   watchdog: ziirave...
48
49
  #define ZIIRAVE_FIRM_FLASH_MEMORY_START	(2 * 0x1600)
  #define ZIIRAVE_FIRM_FLASH_MEMORY_END	(2 * 0x2bbf)
5870f4958   Andrey Smirnov   watchdog: ziirave...
50
  #define ZIIRAVE_FIRM_PAGE_SIZE		128
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
51
52
53
  
  /* Received and ready for next Download packet. */
  #define ZIIRAVE_FIRM_DOWNLOAD_ACK	1
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
54
55
56
57
58
59
60
61
62
  
  /* Firmware commands */
  #define ZIIRAVE_CMD_DOWNLOAD_START		0x10
  #define ZIIRAVE_CMD_DOWNLOAD_END		0x11
  #define ZIIRAVE_CMD_DOWNLOAD_SET_READ_ADDR	0x12
  #define ZIIRAVE_CMD_DOWNLOAD_READ_BYTE		0x13
  #define ZIIRAVE_CMD_RESET_PROCESSOR		0x0b
  #define ZIIRAVE_CMD_JUMP_TO_BOOTLOADER		0x0c
  #define ZIIRAVE_CMD_DOWNLOAD_PACKET		0x0e
910d0f968   Andrey Smirnov   watchdog: ziirave...
63
  #define ZIIRAVE_CMD_JUMP_TO_BOOTLOADER_MAGIC	1
0007cbd51   Andrey Smirnov   watchdog: ziirave...
64
  #define ZIIRAVE_CMD_RESET_PROCESSOR_MAGIC	1
910d0f968   Andrey Smirnov   watchdog: ziirave...
65

42abc1246   Andrey Smirnov   watchdog: ziirave...
66
67
  #define ZIIRAVE_FW_VERSION_FMT	"02.%02u.%02u"
  #define ZIIRAVE_BL_VERSION_FMT	"01.%02u.%02u"
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
68
69
70
71
72
73
  struct ziirave_wdt_rev {
  	unsigned char major;
  	unsigned char minor;
  };
  
  struct ziirave_wdt_data {
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
74
  	struct mutex sysfs_mutex;
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
  	struct watchdog_device wdd;
  	struct ziirave_wdt_rev bootloader_rev;
  	struct ziirave_wdt_rev firmware_rev;
  	int reset_reason;
  };
  
  static int wdt_timeout;
  module_param(wdt_timeout, int, 0);
  MODULE_PARM_DESC(wdt_timeout, "Watchdog timeout in seconds");
  
  static int reset_duration;
  module_param(reset_duration, int, 0);
  MODULE_PARM_DESC(reset_duration,
  		 "Watchdog reset pulse duration in milliseconds");
  
  static bool nowayout = WATCHDOG_NOWAYOUT;
  module_param(nowayout, bool, 0);
  MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started default="
  		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  
  static int ziirave_wdt_revision(struct i2c_client *client,
  				struct ziirave_wdt_rev *rev, u8 command)
  {
  	int ret;
  
  	ret = i2c_smbus_read_byte_data(client, command);
  	if (ret < 0)
  		return ret;
  
  	rev->major = ret;
  
  	ret = i2c_smbus_read_byte_data(client, command + 1);
  	if (ret < 0)
  		return ret;
  
  	rev->minor = ret;
  
  	return 0;
  }
  
  static int ziirave_wdt_set_state(struct watchdog_device *wdd, int state)
  {
  	struct i2c_client *client = to_i2c_client(wdd->parent);
  
  	return i2c_smbus_write_byte_data(client, ZIIRAVE_WDT_STATE, state);
  }
  
  static int ziirave_wdt_start(struct watchdog_device *wdd)
  {
  	return ziirave_wdt_set_state(wdd, ZIIRAVE_STATE_ON);
  }
  
  static int ziirave_wdt_stop(struct watchdog_device *wdd)
  {
  	return ziirave_wdt_set_state(wdd, ZIIRAVE_STATE_OFF);
  }
  
  static int ziirave_wdt_ping(struct watchdog_device *wdd)
  {
  	struct i2c_client *client = to_i2c_client(wdd->parent);
  
  	return i2c_smbus_write_byte_data(client, ZIIRAVE_WDT_PING,
  					 ZIIRAVE_PING_VALUE);
  }
  
  static int ziirave_wdt_set_timeout(struct watchdog_device *wdd,
  				   unsigned int timeout)
  {
  	struct i2c_client *client = to_i2c_client(wdd->parent);
  	int ret;
  
  	ret = i2c_smbus_write_byte_data(client, ZIIRAVE_WDT_TIMEOUT, timeout);
  	if (!ret)
  		wdd->timeout = timeout;
  
  	return ret;
  }
  
  static unsigned int ziirave_wdt_get_timeleft(struct watchdog_device *wdd)
  {
  	struct i2c_client *client = to_i2c_client(wdd->parent);
  	int ret;
  
  	ret = i2c_smbus_read_byte_data(client, ZIIRAVE_WDT_TIME_LEFT);
  	if (ret < 0)
  		ret = 0;
  
  	return ret;
  }
fe05178c7   Andrey Smirnov   watchdog: ziirave...
164
  static int ziirave_firm_read_ack(struct watchdog_device *wdd)
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
165
166
167
  {
  	struct i2c_client *client = to_i2c_client(wdd->parent);
  	int ret;
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
168

fe05178c7   Andrey Smirnov   watchdog: ziirave...
169
170
171
172
173
174
  	ret = i2c_smbus_read_byte(client);
  	if (ret < 0) {
  		dev_err(&client->dev, "Failed to read status byte
  ");
  		return ret;
  	}
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
175
176
177
  
  	return ret == ZIIRAVE_FIRM_DOWNLOAD_ACK ? 0 : -EIO;
  }
d2ddc4505   Andrey Smirnov   watchdog: ziirave...
178
  static int ziirave_firm_set_read_addr(struct watchdog_device *wdd, u32 addr)
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
179
180
  {
  	struct i2c_client *client = to_i2c_client(wdd->parent);
d2ddc4505   Andrey Smirnov   watchdog: ziirave...
181
  	const u16 addr16 = (u16)addr / 2;
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
182
  	u8 address[2];
d2ddc4505   Andrey Smirnov   watchdog: ziirave...
183
  	put_unaligned_le16(addr16, address);
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
184
185
186
  
  	return i2c_smbus_write_block_data(client,
  					  ZIIRAVE_CMD_DOWNLOAD_SET_READ_ADDR,
e6bd44865   Andrey Smirnov   watchdog: ziirave...
187
  					  sizeof(address), address);
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
188
  }
d2ddc4505   Andrey Smirnov   watchdog: ziirave...
189
190
191
192
193
  static bool ziirave_firm_addr_readonly(u32 addr)
  {
  	return addr < ZIIRAVE_FIRM_FLASH_MEMORY_START ||
  	       addr > ZIIRAVE_FIRM_FLASH_MEMORY_END;
  }
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
194
195
196
197
198
199
200
201
202
203
204
205
  /*
   * ziirave_firm_write_pkt() - Build and write a firmware packet
   *
   * A packet to send to the firmware is composed by following bytes:
   *     Length | Addr0 | Addr1 | Data0 .. Data15 | Checksum |
   * Where,
   *     Length: A data byte containing the length of the data.
   *     Addr0: Low byte of the address.
   *     Addr1: High byte of the address.
   *     Data0 .. Data15: Array of 16 bytes of data.
   *     Checksum: Checksum byte to verify data integrity.
   */
5870f4958   Andrey Smirnov   watchdog: ziirave...
206
207
  static int __ziirave_firm_write_pkt(struct watchdog_device *wdd,
  				    u32 addr, const u8 *data, u8 len)
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
208
  {
5870f4958   Andrey Smirnov   watchdog: ziirave...
209
  	const u16 addr16 = (u16)addr / 2;
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
210
211
212
  	struct i2c_client *client = to_i2c_client(wdd->parent);
  	u8 i, checksum = 0, packet[ZIIRAVE_FIRM_PKT_TOTAL_SIZE];
  	int ret;
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
213

08188e8db   Andrey Smirnov   watchdog: ziirave...
214
215
216
217
218
219
220
  	/* Check max data size */
  	if (len > ZIIRAVE_FIRM_PKT_DATA_SIZE) {
  		dev_err(&client->dev, "Firmware packet too long (%d)
  ",
  			len);
  		return -EMSGSIZE;
  	}
d2ddc4505   Andrey Smirnov   watchdog: ziirave...
221
222
223
224
225
226
227
228
229
  	/*
  	 * Ignore packets that are targeting program memory outisde of
  	 * app partition, since they will be ignored by the
  	 * bootloader. At the same time, we need to make sure we'll
  	 * allow zero length packet that will be sent as the last step
  	 * of firmware update
  	 */
  	if (len && ziirave_firm_addr_readonly(addr))
  		return 0;
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
230
  	/* Packet length */
5870f4958   Andrey Smirnov   watchdog: ziirave...
231
  	packet[0] = len;
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
232
  	/* Packet address */
08f980a8f   Andrey Smirnov   watchdog: ziirave...
233
  	put_unaligned_le16(addr16, packet + 1);
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
234

5870f4958   Andrey Smirnov   watchdog: ziirave...
235
  	memcpy(packet + 3, data, len);
10f98fef7   Andrey Smirnov   watchdog: ziirave...
236
  	memset(packet + 3 + len, 0, ZIIRAVE_FIRM_PKT_DATA_SIZE - len);
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
237
238
  
  	/* Packet checksum */
dc0dd2895   Andrey Smirnov   watchdog: ziirave...
239
  	for (i = 0; i < len + 3; i++)
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
240
241
  		checksum += packet[i];
  	packet[ZIIRAVE_FIRM_PKT_TOTAL_SIZE - 1] = checksum;
08c913fe3   Andrey Smirnov   watchdog: ziirave...
242
243
244
245
246
247
248
249
250
251
  	ret = i2c_smbus_write_block_data(client, ZIIRAVE_CMD_DOWNLOAD_PACKET,
  					 sizeof(packet), packet);
  	if (ret) {
  		dev_err(&client->dev,
  			"Failed to send DOWNLOAD_PACKET: %d
  ", ret);
  		return ret;
  	}
  
  	ret = ziirave_firm_read_ack(wdd);
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
252
253
254
255
  	if (ret)
  		dev_err(&client->dev,
  		      "Failed to write firmware packet at address 0x%04x: %d
  ",
d2ddc4505   Andrey Smirnov   watchdog: ziirave...
256
  		      addr, ret);
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
257
258
259
  
  	return ret;
  }
5870f4958   Andrey Smirnov   watchdog: ziirave...
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
  static int ziirave_firm_write_pkt(struct watchdog_device *wdd,
  				  u32 addr, const u8 *data, u8 len)
  {
  	const u8 max_write_len = ZIIRAVE_FIRM_PAGE_SIZE -
  		(addr - ALIGN_DOWN(addr, ZIIRAVE_FIRM_PAGE_SIZE));
  	int ret;
  
  	if (len > max_write_len) {
  		/*
  		 * If data crossed page boundary we need to split this
  		 * write in two
  		 */
  		ret = __ziirave_firm_write_pkt(wdd, addr, data, max_write_len);
  		if (ret)
  			return ret;
  
  		addr += max_write_len;
  		data += max_write_len;
  		len  -= max_write_len;
  	}
  
  	return __ziirave_firm_write_pkt(wdd, addr, data, len);
  }
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
283
284
285
286
287
288
289
  static int ziirave_firm_verify(struct watchdog_device *wdd,
  			       const struct firmware *fw)
  {
  	struct i2c_client *client = to_i2c_client(wdd->parent);
  	const struct ihex_binrec *rec;
  	int i, ret;
  	u8 data[ZIIRAVE_FIRM_PKT_DATA_SIZE];
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
290
291
  
  	for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) {
de8805380   Andrey Smirnov   watchdog: ziirave...
292
  		const u16 len = be16_to_cpu(rec->len);
d2ddc4505   Andrey Smirnov   watchdog: ziirave...
293
  		const u32 addr = be32_to_cpu(rec->addr);
de8805380   Andrey Smirnov   watchdog: ziirave...
294

d2ddc4505   Andrey Smirnov   watchdog: ziirave...
295
  		if (ziirave_firm_addr_readonly(addr))
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
296
297
298
299
300
301
302
303
304
305
  			continue;
  
  		ret = ziirave_firm_set_read_addr(wdd, addr);
  		if (ret) {
  			dev_err(&client->dev,
  				"Failed to send SET_READ_ADDR command: %d
  ",
  				ret);
  			return ret;
  		}
de8805380   Andrey Smirnov   watchdog: ziirave...
306
  		for (i = 0; i < len; i++) {
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
307
308
309
310
311
312
313
314
315
316
  			ret = i2c_smbus_read_byte_data(client,
  						ZIIRAVE_CMD_DOWNLOAD_READ_BYTE);
  			if (ret < 0) {
  				dev_err(&client->dev,
  					"Failed to READ DATA: %d
  ", ret);
  				return ret;
  			}
  			data[i] = ret;
  		}
de8805380   Andrey Smirnov   watchdog: ziirave...
317
  		if (memcmp(data, rec->data, len)) {
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
318
319
320
321
322
323
324
325
326
327
328
329
330
331
  			dev_err(&client->dev,
  				"Firmware mismatch at address 0x%04x
  ", addr);
  			return -EINVAL;
  		}
  	}
  
  	return 0;
  }
  
  static int ziirave_firm_upload(struct watchdog_device *wdd,
  			       const struct firmware *fw)
  {
  	struct i2c_client *client = to_i2c_client(wdd->parent);
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
332
  	const struct ihex_binrec *rec;
5870f4958   Andrey Smirnov   watchdog: ziirave...
333
  	int ret;
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
334

910d0f968   Andrey Smirnov   watchdog: ziirave...
335
336
337
  	ret = i2c_smbus_write_byte_data(client,
  					ZIIRAVE_CMD_JUMP_TO_BOOTLOADER,
  					ZIIRAVE_CMD_JUMP_TO_BOOTLOADER_MAGIC);
b774fcef7   Andrey Smirnov   watchdog: ziirave...
338
339
340
  	if (ret) {
  		dev_err(&client->dev, "Failed to jump to bootloader
  ");
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
341
  		return ret;
b774fcef7   Andrey Smirnov   watchdog: ziirave...
342
  	}
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
343
344
  
  	msleep(500);
fa0d2f44a   Andrey Smirnov   watchdog: ziirave...
345
  	ret = i2c_smbus_write_byte(client, ZIIRAVE_CMD_DOWNLOAD_START);
b774fcef7   Andrey Smirnov   watchdog: ziirave...
346
347
348
  	if (ret) {
  		dev_err(&client->dev, "Failed to start download
  ");
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
349
  		return ret;
b774fcef7   Andrey Smirnov   watchdog: ziirave...
350
  	}
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
351

fa0d2f44a   Andrey Smirnov   watchdog: ziirave...
352
353
354
355
356
357
  	ret = ziirave_firm_read_ack(wdd);
  	if (ret) {
  		dev_err(&client->dev, "No ACK for start download
  ");
  		return ret;
  	}
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
358
359
360
  	msleep(500);
  
  	for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) {
5870f4958   Andrey Smirnov   watchdog: ziirave...
361
362
363
364
  		ret = ziirave_firm_write_pkt(wdd, be32_to_cpu(rec->addr),
  					     rec->data, be16_to_cpu(rec->len));
  		if (ret)
  			return ret;
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
365
  	}
5870f4958   Andrey Smirnov   watchdog: ziirave...
366
367
368
369
370
  	/*
  	 * Finish firmware download process by sending a zero length
  	 * payload
  	 */
  	ret = ziirave_firm_write_pkt(wdd, 0, NULL, 0);
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
  	if (ret) {
  		dev_err(&client->dev, "Failed to send EMPTY packet: %d
  ", ret);
  		return ret;
  	}
  
  	/* This sleep seems to be required */
  	msleep(20);
  
  	/* Start firmware verification */
  	ret = ziirave_firm_verify(wdd, fw);
  	if (ret) {
  		dev_err(&client->dev,
  			"Failed to verify firmware: %d
  ", ret);
  		return ret;
  	}
  
  	/* End download operation */
c47825fb7   Andrey Smirnov   watchdog: ziirave...
390
  	ret = i2c_smbus_write_byte(client, ZIIRAVE_CMD_DOWNLOAD_END);
b774fcef7   Andrey Smirnov   watchdog: ziirave...
391
392
393
394
  	if (ret) {
  		dev_err(&client->dev,
  			"Failed to end firmware download: %d
  ", ret);
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
395
  		return ret;
b774fcef7   Andrey Smirnov   watchdog: ziirave...
396
  	}
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
397
398
  
  	/* Reset the processor */
0007cbd51   Andrey Smirnov   watchdog: ziirave...
399
400
401
  	ret = i2c_smbus_write_byte_data(client,
  					ZIIRAVE_CMD_RESET_PROCESSOR,
  					ZIIRAVE_CMD_RESET_PROCESSOR_MAGIC);
b774fcef7   Andrey Smirnov   watchdog: ziirave...
402
403
404
405
  	if (ret) {
  		dev_err(&client->dev,
  			"Failed to reset the watchdog: %d
  ", ret);
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
406
  		return ret;
b774fcef7   Andrey Smirnov   watchdog: ziirave...
407
  	}
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
408
409
410
411
412
  
  	msleep(500);
  
  	return 0;
  }
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
  static const struct watchdog_info ziirave_wdt_info = {
  	.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
  	.identity = "Zodiac RAVE Watchdog",
  };
  
  static const struct watchdog_ops ziirave_wdt_ops = {
  	.owner		= THIS_MODULE,
  	.start		= ziirave_wdt_start,
  	.stop		= ziirave_wdt_stop,
  	.ping		= ziirave_wdt_ping,
  	.set_timeout	= ziirave_wdt_set_timeout,
  	.get_timeleft	= ziirave_wdt_get_timeleft,
  };
  
  static ssize_t ziirave_wdt_sysfs_show_firm(struct device *dev,
  					   struct device_attribute *attr,
  					   char *buf)
  {
  	struct i2c_client *client = to_i2c_client(dev->parent);
  	struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client);
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
433
434
435
436
437
  	int ret;
  
  	ret = mutex_lock_interruptible(&w_priv->sysfs_mutex);
  	if (ret)
  		return ret;
42abc1246   Andrey Smirnov   watchdog: ziirave...
438
  	ret = sprintf(buf, ZIIRAVE_FW_VERSION_FMT, w_priv->firmware_rev.major,
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
439
440
441
  		      w_priv->firmware_rev.minor);
  
  	mutex_unlock(&w_priv->sysfs_mutex);
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
442

217209db0   Enric Balletbo i Serra   watchdog: ziirave...
443
  	return ret;
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
444
445
446
447
448
449
450
451
452
453
454
  }
  
  static DEVICE_ATTR(firmware_version, S_IRUGO, ziirave_wdt_sysfs_show_firm,
  		   NULL);
  
  static ssize_t ziirave_wdt_sysfs_show_boot(struct device *dev,
  					   struct device_attribute *attr,
  					   char *buf)
  {
  	struct i2c_client *client = to_i2c_client(dev->parent);
  	struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client);
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
455
456
457
458
459
  	int ret;
  
  	ret = mutex_lock_interruptible(&w_priv->sysfs_mutex);
  	if (ret)
  		return ret;
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
460

42abc1246   Andrey Smirnov   watchdog: ziirave...
461
  	ret = sprintf(buf, ZIIRAVE_BL_VERSION_FMT, w_priv->bootloader_rev.major,
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
462
463
464
465
466
  		      w_priv->bootloader_rev.minor);
  
  	mutex_unlock(&w_priv->sysfs_mutex);
  
  	return ret;
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
467
468
469
470
471
472
473
474
475
476
477
  }
  
  static DEVICE_ATTR(bootloader_version, S_IRUGO, ziirave_wdt_sysfs_show_boot,
  		   NULL);
  
  static ssize_t ziirave_wdt_sysfs_show_reason(struct device *dev,
  					     struct device_attribute *attr,
  					     char *buf)
  {
  	struct i2c_client *client = to_i2c_client(dev->parent);
  	struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client);
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
478
  	int ret;
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
479

217209db0   Enric Balletbo i Serra   watchdog: ziirave...
480
481
482
483
484
485
486
487
488
  	ret = mutex_lock_interruptible(&w_priv->sysfs_mutex);
  	if (ret)
  		return ret;
  
  	ret = sprintf(buf, "%s", ziirave_reasons[w_priv->reset_reason]);
  
  	mutex_unlock(&w_priv->sysfs_mutex);
  
  	return ret;
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
489
490
491
492
  }
  
  static DEVICE_ATTR(reset_reason, S_IRUGO, ziirave_wdt_sysfs_show_reason,
  		   NULL);
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
  static ssize_t ziirave_wdt_sysfs_store_firm(struct device *dev,
  					    struct device_attribute *attr,
  					    const char *buf, size_t count)
  {
  	struct i2c_client *client = to_i2c_client(dev->parent);
  	struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client);
  	const struct firmware *fw;
  	int err;
  
  	err = request_ihex_firmware(&fw, ZIIRAVE_FW_NAME, dev);
  	if (err) {
  		dev_err(&client->dev, "Failed to request ihex firmware
  ");
  		return err;
  	}
  
  	err = mutex_lock_interruptible(&w_priv->sysfs_mutex);
  	if (err)
  		goto release_firmware;
  
  	err = ziirave_firm_upload(&w_priv->wdd, fw);
  	if (err) {
  		dev_err(&client->dev, "The firmware update failed: %d
  ", err);
  		goto unlock_mutex;
  	}
  
  	/* Update firmware version */
  	err = ziirave_wdt_revision(client, &w_priv->firmware_rev,
  				   ZIIRAVE_WDT_FIRM_VER_MAJOR);
  	if (err) {
  		dev_err(&client->dev, "Failed to read firmware version: %d
  ",
  			err);
  		goto unlock_mutex;
  	}
42abc1246   Andrey Smirnov   watchdog: ziirave...
529
530
531
  	dev_info(&client->dev,
  		 "Firmware updated to version " ZIIRAVE_FW_VERSION_FMT "
  ",
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
  		 w_priv->firmware_rev.major, w_priv->firmware_rev.minor);
  
  	/* Restore the watchdog timeout */
  	err = ziirave_wdt_set_timeout(&w_priv->wdd, w_priv->wdd.timeout);
  	if (err)
  		dev_err(&client->dev, "Failed to set timeout: %d
  ", err);
  
  unlock_mutex:
  	mutex_unlock(&w_priv->sysfs_mutex);
  
  release_firmware:
  	release_firmware(fw);
  
  	return err ? err : count;
  }
  
  static DEVICE_ATTR(update_firmware, S_IWUSR, NULL,
  		   ziirave_wdt_sysfs_store_firm);
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
551
552
553
554
  static struct attribute *ziirave_wdt_attrs[] = {
  	&dev_attr_firmware_version.attr,
  	&dev_attr_bootloader_version.attr,
  	&dev_attr_reset_reason.attr,
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
555
  	&dev_attr_update_firmware.attr,
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
556
557
  	NULL
  };
2c2f3080d   Guenter Roeck   watchdog: ziirave...
558
  ATTRIBUTE_GROUPS(ziirave_wdt);
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
  
  static int ziirave_wdt_init_duration(struct i2c_client *client)
  {
  	int ret;
  
  	if (!reset_duration) {
  		/* See if the reset pulse duration is provided in an of_node */
  		if (!client->dev.of_node)
  			ret = -ENODEV;
  		else
  			ret = of_property_read_u32(client->dev.of_node,
  						   "reset-duration-ms",
  						   &reset_duration);
  		if (ret) {
  			dev_info(&client->dev,
d2c1d4258   Andrey Smirnov   watchdog: ziirave...
574
575
  			 "No reset pulse duration specified, using default
  ");
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
  			return 0;
  		}
  	}
  
  	if (reset_duration < 1 || reset_duration > 255)
  		return -EINVAL;
  
  	dev_info(&client->dev, "Setting reset duration to %dms",
  		 reset_duration);
  
  	return i2c_smbus_write_byte_data(client, ZIIRAVE_WDT_RESET_DURATION,
  					 reset_duration);
  }
  
  static int ziirave_wdt_probe(struct i2c_client *client,
  			     const struct i2c_device_id *id)
  {
  	int ret;
  	struct ziirave_wdt_data *w_priv;
  	int val;
f676ac830   Andrey Smirnov   watchdog: ziirave...
596
597
598
599
  	if (!i2c_check_functionality(client->adapter,
  				     I2C_FUNC_SMBUS_BYTE |
  				     I2C_FUNC_SMBUS_BYTE_DATA |
  				     I2C_FUNC_SMBUS_WRITE_BLOCK_DATA))
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
600
601
602
603
604
  		return -ENODEV;
  
  	w_priv = devm_kzalloc(&client->dev, sizeof(*w_priv), GFP_KERNEL);
  	if (!w_priv)
  		return -ENOMEM;
217209db0   Enric Balletbo i Serra   watchdog: ziirave...
605
  	mutex_init(&w_priv->sysfs_mutex);
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
606
607
608
609
610
  	w_priv->wdd.info = &ziirave_wdt_info;
  	w_priv->wdd.ops = &ziirave_wdt_ops;
  	w_priv->wdd.min_timeout = ZIIRAVE_TIMEOUT_MIN;
  	w_priv->wdd.max_timeout = ZIIRAVE_TIMEOUT_MAX;
  	w_priv->wdd.parent = &client->dev;
2c2f3080d   Guenter Roeck   watchdog: ziirave...
611
  	w_priv->wdd.groups = ziirave_wdt_groups;
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
612

154511636   Wolfram Sang   watchdog: ziirave...
613
  	watchdog_init_timeout(&w_priv->wdd, wdt_timeout, &client->dev);
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
614
615
616
617
618
619
620
621
  
  	/*
  	 * The default value set in the watchdog should be perfectly valid, so
  	 * pass that in if we haven't provided one via the module parameter or
  	 * of property.
  	 */
  	if (w_priv->wdd.timeout == 0) {
  		val = i2c_smbus_read_byte_data(client, ZIIRAVE_WDT_TIMEOUT);
4a9600c7e   Andrey Smirnov   watchdog: ziirave...
622
623
624
  		if (val < 0) {
  			dev_err(&client->dev, "Failed to read timeout
  ");
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
625
  			return val;
4a9600c7e   Andrey Smirnov   watchdog: ziirave...
626
  		}
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
627

39d0387d5   Andrey Smirnov   watchdog: ziirave...
628
629
630
  		if (val > ZIIRAVE_TIMEOUT_MAX ||
  		    val < ZIIRAVE_TIMEOUT_MIN)
  			val = ZIIRAVE_TIMEOUT_DEFAULT;
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
631
632
  
  		w_priv->wdd.timeout = val;
39d0387d5   Andrey Smirnov   watchdog: ziirave...
633
  	}
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
634

39d0387d5   Andrey Smirnov   watchdog: ziirave...
635
636
637
638
639
  	ret = ziirave_wdt_set_timeout(&w_priv->wdd, w_priv->wdd.timeout);
  	if (ret) {
  		dev_err(&client->dev, "Failed to set timeout
  ");
  		return ret;
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
640
  	}
39d0387d5   Andrey Smirnov   watchdog: ziirave...
641
642
  	dev_info(&client->dev, "Timeout set to %ds
  ", w_priv->wdd.timeout);
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
643
644
645
646
647
648
  	watchdog_set_nowayout(&w_priv->wdd, nowayout);
  
  	i2c_set_clientdata(client, w_priv);
  
  	/* If in unconfigured state, set to stopped */
  	val = i2c_smbus_read_byte_data(client, ZIIRAVE_WDT_STATE);
4a9600c7e   Andrey Smirnov   watchdog: ziirave...
649
650
651
  	if (val < 0) {
  		dev_err(&client->dev, "Failed to read state
  ");
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
652
  		return val;
4a9600c7e   Andrey Smirnov   watchdog: ziirave...
653
  	}
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
654
655
656
657
658
  
  	if (val == ZIIRAVE_STATE_INITIAL)
  		ziirave_wdt_stop(&w_priv->wdd);
  
  	ret = ziirave_wdt_init_duration(client);
4a9600c7e   Andrey Smirnov   watchdog: ziirave...
659
660
661
  	if (ret) {
  		dev_err(&client->dev, "Failed to init duration
  ");
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
662
  		return ret;
4a9600c7e   Andrey Smirnov   watchdog: ziirave...
663
  	}
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
664
665
666
  
  	ret = ziirave_wdt_revision(client, &w_priv->firmware_rev,
  				   ZIIRAVE_WDT_FIRM_VER_MAJOR);
4a9600c7e   Andrey Smirnov   watchdog: ziirave...
667
668
669
  	if (ret) {
  		dev_err(&client->dev, "Failed to read firmware version
  ");
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
670
  		return ret;
4a9600c7e   Andrey Smirnov   watchdog: ziirave...
671
  	}
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
672

42abc1246   Andrey Smirnov   watchdog: ziirave...
673
674
675
676
  	dev_info(&client->dev,
  		 "Firmware version: " ZIIRAVE_FW_VERSION_FMT "
  ",
  		 w_priv->firmware_rev.major, w_priv->firmware_rev.minor);
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
677
678
  	ret = ziirave_wdt_revision(client, &w_priv->bootloader_rev,
  				   ZIIRAVE_WDT_BOOT_VER_MAJOR);
4a9600c7e   Andrey Smirnov   watchdog: ziirave...
679
680
681
  	if (ret) {
  		dev_err(&client->dev, "Failed to read bootloader version
  ");
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
682
  		return ret;
4a9600c7e   Andrey Smirnov   watchdog: ziirave...
683
  	}
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
684

42abc1246   Andrey Smirnov   watchdog: ziirave...
685
686
687
688
  	dev_info(&client->dev,
  		 "Bootloader version: " ZIIRAVE_BL_VERSION_FMT "
  ",
  		 w_priv->bootloader_rev.major, w_priv->bootloader_rev.minor);
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
689
690
  	w_priv->reset_reason = i2c_smbus_read_byte_data(client,
  						ZIIRAVE_WDT_RESET_REASON);
4a9600c7e   Andrey Smirnov   watchdog: ziirave...
691
692
693
  	if (w_priv->reset_reason < 0) {
  		dev_err(&client->dev, "Failed to read reset reason
  ");
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
694
  		return w_priv->reset_reason;
4a9600c7e   Andrey Smirnov   watchdog: ziirave...
695
  	}
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
696
697
  
  	if (w_priv->reset_reason >= ARRAY_SIZE(ziirave_reasons) ||
4a9600c7e   Andrey Smirnov   watchdog: ziirave...
698
699
700
  	    !ziirave_reasons[w_priv->reset_reason]) {
  		dev_err(&client->dev, "Invalid reset reason
  ");
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
701
  		return -ENODEV;
4a9600c7e   Andrey Smirnov   watchdog: ziirave...
702
  	}
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
703
704
  
  	ret = watchdog_register_device(&w_priv->wdd);
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
705

2c2f3080d   Guenter Roeck   watchdog: ziirave...
706
  	return ret;
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
707
708
709
710
711
  }
  
  static int ziirave_wdt_remove(struct i2c_client *client)
  {
  	struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client);
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
712
713
714
715
  	watchdog_unregister_device(&w_priv->wdd);
  
  	return 0;
  }
68c82befe   Arvind Yadav   watchdog: ziirave...
716
  static const struct i2c_device_id ziirave_wdt_id[] = {
22daf7a71   Enric Balletbo i Serra   watchdog: ziirave...
717
  	{ "rave-wdt", 0 },
2a7b753a2   Martyn Welch   watchdog: Zodiac ...
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
  	{ }
  };
  MODULE_DEVICE_TABLE(i2c, ziirave_wdt_id);
  
  static const struct of_device_id zrv_wdt_of_match[] = {
  	{ .compatible = "zii,rave-wdt", },
  	{ },
  };
  MODULE_DEVICE_TABLE(of, zrv_wdt_of_match);
  
  static struct i2c_driver ziirave_wdt_driver = {
  	.driver = {
  		.name = "ziirave_wdt",
  		.of_match_table = zrv_wdt_of_match,
  	},
  	.probe = ziirave_wdt_probe,
  	.remove = ziirave_wdt_remove,
  	.id_table = ziirave_wdt_id,
  };
  
  module_i2c_driver(ziirave_wdt_driver);
  
  MODULE_AUTHOR("Martyn Welch <martyn.welch@collabora.co.uk");
  MODULE_DESCRIPTION("Zodiac Aerospace RAVE Switch Watchdog Processor Driver");
  MODULE_LICENSE("GPL");