Commit | Line | Data |
---|---|---|
2a7b753a MW |
1 | /* |
2 | * Copyright (C) 2015 Zodiac Inflight Innovations | |
3 | * | |
4 | * Author: Martyn Welch <martyn.welch@collabora.co.uk> | |
5 | * | |
6 | * Based on twl4030_wdt.c by Timo Kokkonen <timo.t.kokkonen at nokia.com>: | |
7 | * | |
8 | * Copyright (C) Nokia Corporation | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License as published by | |
12 | * the Free Software Foundation; either version 2 of the License, or | |
13 | * (at your option) any later version. | |
14 | * | |
15 | * This program is distributed in the hope that it will be useful, | |
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | * GNU General Public License for more details. | |
19 | */ | |
20 | ||
217209db | 21 | #include <linux/delay.h> |
2a7b753a | 22 | #include <linux/i2c.h> |
217209db EBS |
23 | #include <linux/ihex.h> |
24 | #include <linux/firmware.h> | |
2a7b753a MW |
25 | #include <linux/kernel.h> |
26 | #include <linux/module.h> | |
27 | #include <linux/slab.h> | |
28 | #include <linux/sysfs.h> | |
29 | #include <linux/types.h> | |
30 | #include <linux/version.h> | |
31 | #include <linux/watchdog.h> | |
32 | ||
33 | #define ZIIRAVE_TIMEOUT_MIN 3 | |
34 | #define ZIIRAVE_TIMEOUT_MAX 255 | |
35 | ||
36 | #define ZIIRAVE_PING_VALUE 0x0 | |
37 | ||
38 | #define ZIIRAVE_STATE_INITIAL 0x0 | |
39 | #define ZIIRAVE_STATE_OFF 0x1 | |
40 | #define ZIIRAVE_STATE_ON 0x2 | |
41 | ||
217209db EBS |
42 | #define ZIIRAVE_FW_NAME "ziirave_wdt.fw" |
43 | ||
0ce72f35 | 44 | static char *ziirave_reasons[] = {"power cycle", "hw watchdog", NULL, NULL, |
2a7b753a MW |
45 | "host request", NULL, "illegal configuration", |
46 | "illegal instruction", "illegal trap", | |
47 | "unknown"}; | |
48 | ||
49 | #define ZIIRAVE_WDT_FIRM_VER_MAJOR 0x1 | |
50 | #define ZIIRAVE_WDT_BOOT_VER_MAJOR 0x3 | |
51 | #define ZIIRAVE_WDT_RESET_REASON 0x5 | |
52 | #define ZIIRAVE_WDT_STATE 0x6 | |
53 | #define ZIIRAVE_WDT_TIMEOUT 0x7 | |
54 | #define ZIIRAVE_WDT_TIME_LEFT 0x8 | |
55 | #define ZIIRAVE_WDT_PING 0x9 | |
56 | #define ZIIRAVE_WDT_RESET_DURATION 0xa | |
57 | ||
217209db EBS |
58 | #define ZIIRAVE_FIRM_PKT_TOTAL_SIZE 20 |
59 | #define ZIIRAVE_FIRM_PKT_DATA_SIZE 16 | |
60 | #define ZIIRAVE_FIRM_FLASH_MEMORY_START 0x1600 | |
61 | #define ZIIRAVE_FIRM_FLASH_MEMORY_END 0x2bbf | |
62 | ||
63 | /* Received and ready for next Download packet. */ | |
64 | #define ZIIRAVE_FIRM_DOWNLOAD_ACK 1 | |
65 | /* Currently writing to flash. Retry Download status in a moment! */ | |
66 | #define ZIIRAVE_FIRM_DOWNLOAD_BUSY 2 | |
67 | ||
68 | /* Wait for ACK timeout in ms */ | |
69 | #define ZIIRAVE_FIRM_WAIT_FOR_ACK_TIMEOUT 50 | |
70 | ||
71 | /* Firmware commands */ | |
72 | #define ZIIRAVE_CMD_DOWNLOAD_START 0x10 | |
73 | #define ZIIRAVE_CMD_DOWNLOAD_END 0x11 | |
74 | #define ZIIRAVE_CMD_DOWNLOAD_SET_READ_ADDR 0x12 | |
75 | #define ZIIRAVE_CMD_DOWNLOAD_READ_BYTE 0x13 | |
76 | #define ZIIRAVE_CMD_RESET_PROCESSOR 0x0b | |
77 | #define ZIIRAVE_CMD_JUMP_TO_BOOTLOADER 0x0c | |
78 | #define ZIIRAVE_CMD_DOWNLOAD_PACKET 0x0e | |
79 | ||
2a7b753a MW |
80 | struct ziirave_wdt_rev { |
81 | unsigned char major; | |
82 | unsigned char minor; | |
83 | }; | |
84 | ||
85 | struct ziirave_wdt_data { | |
217209db | 86 | struct mutex sysfs_mutex; |
2a7b753a MW |
87 | struct watchdog_device wdd; |
88 | struct ziirave_wdt_rev bootloader_rev; | |
89 | struct ziirave_wdt_rev firmware_rev; | |
90 | int reset_reason; | |
91 | }; | |
92 | ||
93 | static int wdt_timeout; | |
94 | module_param(wdt_timeout, int, 0); | |
95 | MODULE_PARM_DESC(wdt_timeout, "Watchdog timeout in seconds"); | |
96 | ||
97 | static int reset_duration; | |
98 | module_param(reset_duration, int, 0); | |
99 | MODULE_PARM_DESC(reset_duration, | |
100 | "Watchdog reset pulse duration in milliseconds"); | |
101 | ||
102 | static bool nowayout = WATCHDOG_NOWAYOUT; | |
103 | module_param(nowayout, bool, 0); | |
104 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started default=" | |
105 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | |
106 | ||
107 | static int ziirave_wdt_revision(struct i2c_client *client, | |
108 | struct ziirave_wdt_rev *rev, u8 command) | |
109 | { | |
110 | int ret; | |
111 | ||
112 | ret = i2c_smbus_read_byte_data(client, command); | |
113 | if (ret < 0) | |
114 | return ret; | |
115 | ||
116 | rev->major = ret; | |
117 | ||
118 | ret = i2c_smbus_read_byte_data(client, command + 1); | |
119 | if (ret < 0) | |
120 | return ret; | |
121 | ||
122 | rev->minor = ret; | |
123 | ||
124 | return 0; | |
125 | } | |
126 | ||
127 | static int ziirave_wdt_set_state(struct watchdog_device *wdd, int state) | |
128 | { | |
129 | struct i2c_client *client = to_i2c_client(wdd->parent); | |
130 | ||
131 | return i2c_smbus_write_byte_data(client, ZIIRAVE_WDT_STATE, state); | |
132 | } | |
133 | ||
134 | static int ziirave_wdt_start(struct watchdog_device *wdd) | |
135 | { | |
136 | return ziirave_wdt_set_state(wdd, ZIIRAVE_STATE_ON); | |
137 | } | |
138 | ||
139 | static int ziirave_wdt_stop(struct watchdog_device *wdd) | |
140 | { | |
141 | return ziirave_wdt_set_state(wdd, ZIIRAVE_STATE_OFF); | |
142 | } | |
143 | ||
144 | static int ziirave_wdt_ping(struct watchdog_device *wdd) | |
145 | { | |
146 | struct i2c_client *client = to_i2c_client(wdd->parent); | |
147 | ||
148 | return i2c_smbus_write_byte_data(client, ZIIRAVE_WDT_PING, | |
149 | ZIIRAVE_PING_VALUE); | |
150 | } | |
151 | ||
152 | static int ziirave_wdt_set_timeout(struct watchdog_device *wdd, | |
153 | unsigned int timeout) | |
154 | { | |
155 | struct i2c_client *client = to_i2c_client(wdd->parent); | |
156 | int ret; | |
157 | ||
158 | ret = i2c_smbus_write_byte_data(client, ZIIRAVE_WDT_TIMEOUT, timeout); | |
159 | if (!ret) | |
160 | wdd->timeout = timeout; | |
161 | ||
162 | return ret; | |
163 | } | |
164 | ||
165 | static unsigned int ziirave_wdt_get_timeleft(struct watchdog_device *wdd) | |
166 | { | |
167 | struct i2c_client *client = to_i2c_client(wdd->parent); | |
168 | int ret; | |
169 | ||
170 | ret = i2c_smbus_read_byte_data(client, ZIIRAVE_WDT_TIME_LEFT); | |
171 | if (ret < 0) | |
172 | ret = 0; | |
173 | ||
174 | return ret; | |
175 | } | |
176 | ||
217209db EBS |
177 | static int ziirave_firm_wait_for_ack(struct watchdog_device *wdd) |
178 | { | |
179 | struct i2c_client *client = to_i2c_client(wdd->parent); | |
180 | int ret; | |
181 | unsigned long timeout; | |
182 | ||
183 | timeout = jiffies + msecs_to_jiffies(ZIIRAVE_FIRM_WAIT_FOR_ACK_TIMEOUT); | |
184 | do { | |
185 | if (time_after(jiffies, timeout)) | |
186 | return -ETIMEDOUT; | |
187 | ||
188 | usleep_range(5000, 10000); | |
189 | ||
190 | ret = i2c_smbus_read_byte(client); | |
191 | if (ret < 0) { | |
192 | dev_err(&client->dev, "Failed to read byte\n"); | |
193 | return ret; | |
194 | } | |
195 | } while (ret == ZIIRAVE_FIRM_DOWNLOAD_BUSY); | |
196 | ||
197 | return ret == ZIIRAVE_FIRM_DOWNLOAD_ACK ? 0 : -EIO; | |
198 | } | |
199 | ||
200 | static int ziirave_firm_set_read_addr(struct watchdog_device *wdd, u16 addr) | |
201 | { | |
202 | struct i2c_client *client = to_i2c_client(wdd->parent); | |
203 | u8 address[2]; | |
204 | ||
205 | address[0] = addr & 0xff; | |
206 | address[1] = (addr >> 8) & 0xff; | |
207 | ||
208 | return i2c_smbus_write_block_data(client, | |
209 | ZIIRAVE_CMD_DOWNLOAD_SET_READ_ADDR, | |
210 | ARRAY_SIZE(address), address); | |
211 | } | |
212 | ||
213 | static int ziirave_firm_write_block_data(struct watchdog_device *wdd, | |
214 | u8 command, u8 length, const u8 *data, | |
215 | bool wait_for_ack) | |
216 | { | |
217 | struct i2c_client *client = to_i2c_client(wdd->parent); | |
218 | int ret; | |
219 | ||
220 | ret = i2c_smbus_write_block_data(client, command, length, data); | |
221 | if (ret) { | |
222 | dev_err(&client->dev, | |
223 | "Failed to send command 0x%02x: %d\n", command, ret); | |
224 | return ret; | |
225 | } | |
226 | ||
227 | if (wait_for_ack) | |
228 | ret = ziirave_firm_wait_for_ack(wdd); | |
229 | ||
230 | return ret; | |
231 | } | |
232 | ||
233 | static int ziirave_firm_write_byte(struct watchdog_device *wdd, u8 command, | |
234 | u8 byte, bool wait_for_ack) | |
235 | { | |
236 | return ziirave_firm_write_block_data(wdd, command, 1, &byte, | |
237 | wait_for_ack); | |
238 | } | |
239 | ||
240 | /* | |
241 | * ziirave_firm_write_pkt() - Build and write a firmware packet | |
242 | * | |
243 | * A packet to send to the firmware is composed by following bytes: | |
244 | * Length | Addr0 | Addr1 | Data0 .. Data15 | Checksum | | |
245 | * Where, | |
246 | * Length: A data byte containing the length of the data. | |
247 | * Addr0: Low byte of the address. | |
248 | * Addr1: High byte of the address. | |
249 | * Data0 .. Data15: Array of 16 bytes of data. | |
250 | * Checksum: Checksum byte to verify data integrity. | |
251 | */ | |
252 | static int ziirave_firm_write_pkt(struct watchdog_device *wdd, | |
253 | const struct ihex_binrec *rec) | |
254 | { | |
255 | struct i2c_client *client = to_i2c_client(wdd->parent); | |
256 | u8 i, checksum = 0, packet[ZIIRAVE_FIRM_PKT_TOTAL_SIZE]; | |
257 | int ret; | |
258 | u16 addr; | |
259 | ||
260 | memset(packet, 0, ARRAY_SIZE(packet)); | |
261 | ||
262 | /* Packet length */ | |
263 | packet[0] = (u8)be16_to_cpu(rec->len); | |
264 | /* Packet address */ | |
265 | addr = (be32_to_cpu(rec->addr) & 0xffff) >> 1; | |
266 | packet[1] = addr & 0xff; | |
267 | packet[2] = (addr & 0xff00) >> 8; | |
268 | ||
269 | /* Packet data */ | |
270 | if (be16_to_cpu(rec->len) > ZIIRAVE_FIRM_PKT_DATA_SIZE) | |
271 | return -EMSGSIZE; | |
272 | memcpy(packet + 3, rec->data, be16_to_cpu(rec->len)); | |
273 | ||
274 | /* Packet checksum */ | |
275 | for (i = 0; i < ZIIRAVE_FIRM_PKT_TOTAL_SIZE - 1; i++) | |
276 | checksum += packet[i]; | |
277 | packet[ZIIRAVE_FIRM_PKT_TOTAL_SIZE - 1] = checksum; | |
278 | ||
279 | ret = ziirave_firm_write_block_data(wdd, ZIIRAVE_CMD_DOWNLOAD_PACKET, | |
280 | ARRAY_SIZE(packet), packet, true); | |
281 | if (ret) | |
282 | dev_err(&client->dev, | |
283 | "Failed to write firmware packet at address 0x%04x: %d\n", | |
284 | addr, ret); | |
285 | ||
286 | return ret; | |
287 | } | |
288 | ||
289 | static int ziirave_firm_verify(struct watchdog_device *wdd, | |
290 | const struct firmware *fw) | |
291 | { | |
292 | struct i2c_client *client = to_i2c_client(wdd->parent); | |
293 | const struct ihex_binrec *rec; | |
294 | int i, ret; | |
295 | u8 data[ZIIRAVE_FIRM_PKT_DATA_SIZE]; | |
296 | u16 addr; | |
297 | ||
298 | for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) { | |
299 | /* Zero length marks end of records */ | |
300 | if (!be16_to_cpu(rec->len)) | |
301 | break; | |
302 | ||
303 | addr = (be32_to_cpu(rec->addr) & 0xffff) >> 1; | |
304 | if (addr < ZIIRAVE_FIRM_FLASH_MEMORY_START || | |
305 | addr > ZIIRAVE_FIRM_FLASH_MEMORY_END) | |
306 | continue; | |
307 | ||
308 | ret = ziirave_firm_set_read_addr(wdd, addr); | |
309 | if (ret) { | |
310 | dev_err(&client->dev, | |
311 | "Failed to send SET_READ_ADDR command: %d\n", | |
312 | ret); | |
313 | return ret; | |
314 | } | |
315 | ||
316 | for (i = 0; i < ARRAY_SIZE(data); i++) { | |
317 | ret = i2c_smbus_read_byte_data(client, | |
318 | ZIIRAVE_CMD_DOWNLOAD_READ_BYTE); | |
319 | if (ret < 0) { | |
320 | dev_err(&client->dev, | |
321 | "Failed to READ DATA: %d\n", ret); | |
322 | return ret; | |
323 | } | |
324 | data[i] = ret; | |
325 | } | |
326 | ||
327 | if (memcmp(data, rec->data, be16_to_cpu(rec->len))) { | |
328 | dev_err(&client->dev, | |
329 | "Firmware mismatch at address 0x%04x\n", addr); | |
330 | return -EINVAL; | |
331 | } | |
332 | } | |
333 | ||
334 | return 0; | |
335 | } | |
336 | ||
337 | static int ziirave_firm_upload(struct watchdog_device *wdd, | |
338 | const struct firmware *fw) | |
339 | { | |
340 | struct i2c_client *client = to_i2c_client(wdd->parent); | |
341 | int ret, words_till_page_break; | |
342 | const struct ihex_binrec *rec; | |
343 | struct ihex_binrec *rec_new; | |
344 | ||
345 | ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_JUMP_TO_BOOTLOADER, 1, | |
346 | false); | |
347 | if (ret) | |
348 | return ret; | |
349 | ||
350 | msleep(500); | |
351 | ||
352 | ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_DOWNLOAD_START, 1, true); | |
353 | if (ret) | |
354 | return ret; | |
355 | ||
356 | msleep(500); | |
357 | ||
358 | for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) { | |
359 | /* Zero length marks end of records */ | |
360 | if (!be16_to_cpu(rec->len)) | |
361 | break; | |
362 | ||
363 | /* Check max data size */ | |
364 | if (be16_to_cpu(rec->len) > ZIIRAVE_FIRM_PKT_DATA_SIZE) { | |
365 | dev_err(&client->dev, "Firmware packet too long (%d)\n", | |
366 | be16_to_cpu(rec->len)); | |
367 | return -EMSGSIZE; | |
368 | } | |
369 | ||
370 | /* Calculate words till page break */ | |
371 | words_till_page_break = (64 - ((be32_to_cpu(rec->addr) >> 1) & | |
372 | 0x3f)); | |
373 | if ((be16_to_cpu(rec->len) >> 1) > words_till_page_break) { | |
374 | /* | |
375 | * Data in passes page boundary, so we need to split in | |
376 | * two blocks of data. Create a packet with the first | |
377 | * block of data. | |
378 | */ | |
379 | rec_new = kzalloc(sizeof(struct ihex_binrec) + | |
380 | (words_till_page_break << 1), | |
381 | GFP_KERNEL); | |
382 | if (!rec_new) | |
383 | return -ENOMEM; | |
384 | ||
385 | rec_new->len = cpu_to_be16(words_till_page_break << 1); | |
386 | rec_new->addr = rec->addr; | |
387 | memcpy(rec_new->data, rec->data, | |
388 | be16_to_cpu(rec_new->len)); | |
389 | ||
390 | ret = ziirave_firm_write_pkt(wdd, rec_new); | |
391 | kfree(rec_new); | |
392 | if (ret) | |
393 | return ret; | |
394 | ||
395 | /* Create a packet with the second block of data */ | |
396 | rec_new = kzalloc(sizeof(struct ihex_binrec) + | |
397 | be16_to_cpu(rec->len) - | |
398 | (words_till_page_break << 1), | |
399 | GFP_KERNEL); | |
400 | if (!rec_new) | |
401 | return -ENOMEM; | |
402 | ||
403 | /* Remaining bytes */ | |
404 | rec_new->len = rec->len - | |
405 | cpu_to_be16(words_till_page_break << 1); | |
406 | ||
407 | rec_new->addr = cpu_to_be32(be32_to_cpu(rec->addr) + | |
408 | (words_till_page_break << 1)); | |
409 | ||
410 | memcpy(rec_new->data, | |
411 | rec->data + (words_till_page_break << 1), | |
412 | be16_to_cpu(rec_new->len)); | |
413 | ||
414 | ret = ziirave_firm_write_pkt(wdd, rec_new); | |
415 | kfree(rec_new); | |
416 | if (ret) | |
417 | return ret; | |
418 | } else { | |
419 | ret = ziirave_firm_write_pkt(wdd, rec); | |
420 | if (ret) | |
421 | return ret; | |
422 | } | |
423 | } | |
424 | ||
425 | /* For end of download, the length field will be set to 0 */ | |
426 | rec_new = kzalloc(sizeof(struct ihex_binrec) + 1, GFP_KERNEL); | |
427 | if (!rec_new) | |
428 | return -ENOMEM; | |
429 | ||
430 | ret = ziirave_firm_write_pkt(wdd, rec_new); | |
431 | kfree(rec_new); | |
432 | if (ret) { | |
433 | dev_err(&client->dev, "Failed to send EMPTY packet: %d\n", ret); | |
434 | return ret; | |
435 | } | |
436 | ||
437 | /* This sleep seems to be required */ | |
438 | msleep(20); | |
439 | ||
440 | /* Start firmware verification */ | |
441 | ret = ziirave_firm_verify(wdd, fw); | |
442 | if (ret) { | |
443 | dev_err(&client->dev, | |
444 | "Failed to verify firmware: %d\n", ret); | |
445 | return ret; | |
446 | } | |
447 | ||
448 | /* End download operation */ | |
449 | ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_DOWNLOAD_END, 1, false); | |
450 | if (ret) | |
451 | return ret; | |
452 | ||
453 | /* Reset the processor */ | |
454 | ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_RESET_PROCESSOR, 1, | |
455 | false); | |
456 | if (ret) | |
457 | return ret; | |
458 | ||
459 | msleep(500); | |
460 | ||
461 | return 0; | |
462 | } | |
463 | ||
2a7b753a MW |
464 | static const struct watchdog_info ziirave_wdt_info = { |
465 | .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, | |
466 | .identity = "Zodiac RAVE Watchdog", | |
467 | }; | |
468 | ||
469 | static const struct watchdog_ops ziirave_wdt_ops = { | |
470 | .owner = THIS_MODULE, | |
471 | .start = ziirave_wdt_start, | |
472 | .stop = ziirave_wdt_stop, | |
473 | .ping = ziirave_wdt_ping, | |
474 | .set_timeout = ziirave_wdt_set_timeout, | |
475 | .get_timeleft = ziirave_wdt_get_timeleft, | |
476 | }; | |
477 | ||
478 | static ssize_t ziirave_wdt_sysfs_show_firm(struct device *dev, | |
479 | struct device_attribute *attr, | |
480 | char *buf) | |
481 | { | |
482 | struct i2c_client *client = to_i2c_client(dev->parent); | |
483 | struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client); | |
217209db EBS |
484 | int ret; |
485 | ||
486 | ret = mutex_lock_interruptible(&w_priv->sysfs_mutex); | |
487 | if (ret) | |
488 | return ret; | |
489 | ||
490 | ret = sprintf(buf, "02.%02u.%02u", w_priv->firmware_rev.major, | |
491 | w_priv->firmware_rev.minor); | |
492 | ||
493 | mutex_unlock(&w_priv->sysfs_mutex); | |
2a7b753a | 494 | |
217209db | 495 | return ret; |
2a7b753a MW |
496 | } |
497 | ||
498 | static DEVICE_ATTR(firmware_version, S_IRUGO, ziirave_wdt_sysfs_show_firm, | |
499 | NULL); | |
500 | ||
501 | static ssize_t ziirave_wdt_sysfs_show_boot(struct device *dev, | |
502 | struct device_attribute *attr, | |
503 | char *buf) | |
504 | { | |
505 | struct i2c_client *client = to_i2c_client(dev->parent); | |
506 | struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client); | |
217209db EBS |
507 | int ret; |
508 | ||
509 | ret = mutex_lock_interruptible(&w_priv->sysfs_mutex); | |
510 | if (ret) | |
511 | return ret; | |
2a7b753a | 512 | |
217209db EBS |
513 | ret = sprintf(buf, "01.%02u.%02u", w_priv->bootloader_rev.major, |
514 | w_priv->bootloader_rev.minor); | |
515 | ||
516 | mutex_unlock(&w_priv->sysfs_mutex); | |
517 | ||
518 | return ret; | |
2a7b753a MW |
519 | } |
520 | ||
521 | static DEVICE_ATTR(bootloader_version, S_IRUGO, ziirave_wdt_sysfs_show_boot, | |
522 | NULL); | |
523 | ||
524 | static ssize_t ziirave_wdt_sysfs_show_reason(struct device *dev, | |
525 | struct device_attribute *attr, | |
526 | char *buf) | |
527 | { | |
528 | struct i2c_client *client = to_i2c_client(dev->parent); | |
529 | struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client); | |
217209db | 530 | int ret; |
2a7b753a | 531 | |
217209db EBS |
532 | ret = mutex_lock_interruptible(&w_priv->sysfs_mutex); |
533 | if (ret) | |
534 | return ret; | |
535 | ||
536 | ret = sprintf(buf, "%s", ziirave_reasons[w_priv->reset_reason]); | |
537 | ||
538 | mutex_unlock(&w_priv->sysfs_mutex); | |
539 | ||
540 | return ret; | |
2a7b753a MW |
541 | } |
542 | ||
543 | static DEVICE_ATTR(reset_reason, S_IRUGO, ziirave_wdt_sysfs_show_reason, | |
544 | NULL); | |
545 | ||
217209db EBS |
546 | static ssize_t ziirave_wdt_sysfs_store_firm(struct device *dev, |
547 | struct device_attribute *attr, | |
548 | const char *buf, size_t count) | |
549 | { | |
550 | struct i2c_client *client = to_i2c_client(dev->parent); | |
551 | struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client); | |
552 | const struct firmware *fw; | |
553 | int err; | |
554 | ||
555 | err = request_ihex_firmware(&fw, ZIIRAVE_FW_NAME, dev); | |
556 | if (err) { | |
557 | dev_err(&client->dev, "Failed to request ihex firmware\n"); | |
558 | return err; | |
559 | } | |
560 | ||
561 | err = mutex_lock_interruptible(&w_priv->sysfs_mutex); | |
562 | if (err) | |
563 | goto release_firmware; | |
564 | ||
565 | err = ziirave_firm_upload(&w_priv->wdd, fw); | |
566 | if (err) { | |
567 | dev_err(&client->dev, "The firmware update failed: %d\n", err); | |
568 | goto unlock_mutex; | |
569 | } | |
570 | ||
571 | /* Update firmware version */ | |
572 | err = ziirave_wdt_revision(client, &w_priv->firmware_rev, | |
573 | ZIIRAVE_WDT_FIRM_VER_MAJOR); | |
574 | if (err) { | |
575 | dev_err(&client->dev, "Failed to read firmware version: %d\n", | |
576 | err); | |
577 | goto unlock_mutex; | |
578 | } | |
579 | ||
580 | dev_info(&client->dev, "Firmware updated to version 02.%02u.%02u\n", | |
581 | w_priv->firmware_rev.major, w_priv->firmware_rev.minor); | |
582 | ||
583 | /* Restore the watchdog timeout */ | |
584 | err = ziirave_wdt_set_timeout(&w_priv->wdd, w_priv->wdd.timeout); | |
585 | if (err) | |
586 | dev_err(&client->dev, "Failed to set timeout: %d\n", err); | |
587 | ||
588 | unlock_mutex: | |
589 | mutex_unlock(&w_priv->sysfs_mutex); | |
590 | ||
591 | release_firmware: | |
592 | release_firmware(fw); | |
593 | ||
594 | return err ? err : count; | |
595 | } | |
596 | ||
597 | static DEVICE_ATTR(update_firmware, S_IWUSR, NULL, | |
598 | ziirave_wdt_sysfs_store_firm); | |
599 | ||
2a7b753a MW |
600 | static struct attribute *ziirave_wdt_attrs[] = { |
601 | &dev_attr_firmware_version.attr, | |
602 | &dev_attr_bootloader_version.attr, | |
603 | &dev_attr_reset_reason.attr, | |
217209db | 604 | &dev_attr_update_firmware.attr, |
2a7b753a MW |
605 | NULL |
606 | }; | |
2c2f3080 | 607 | ATTRIBUTE_GROUPS(ziirave_wdt); |
2a7b753a MW |
608 | |
609 | static int ziirave_wdt_init_duration(struct i2c_client *client) | |
610 | { | |
611 | int ret; | |
612 | ||
613 | if (!reset_duration) { | |
614 | /* See if the reset pulse duration is provided in an of_node */ | |
615 | if (!client->dev.of_node) | |
616 | ret = -ENODEV; | |
617 | else | |
618 | ret = of_property_read_u32(client->dev.of_node, | |
619 | "reset-duration-ms", | |
620 | &reset_duration); | |
621 | if (ret) { | |
622 | dev_info(&client->dev, | |
623 | "Unable to set reset pulse duration, using default\n"); | |
624 | return 0; | |
625 | } | |
626 | } | |
627 | ||
628 | if (reset_duration < 1 || reset_duration > 255) | |
629 | return -EINVAL; | |
630 | ||
631 | dev_info(&client->dev, "Setting reset duration to %dms", | |
632 | reset_duration); | |
633 | ||
634 | return i2c_smbus_write_byte_data(client, ZIIRAVE_WDT_RESET_DURATION, | |
635 | reset_duration); | |
636 | } | |
637 | ||
638 | static int ziirave_wdt_probe(struct i2c_client *client, | |
639 | const struct i2c_device_id *id) | |
640 | { | |
641 | int ret; | |
642 | struct ziirave_wdt_data *w_priv; | |
643 | int val; | |
644 | ||
645 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | |
646 | return -ENODEV; | |
647 | ||
648 | w_priv = devm_kzalloc(&client->dev, sizeof(*w_priv), GFP_KERNEL); | |
649 | if (!w_priv) | |
650 | return -ENOMEM; | |
651 | ||
217209db EBS |
652 | mutex_init(&w_priv->sysfs_mutex); |
653 | ||
2a7b753a MW |
654 | w_priv->wdd.info = &ziirave_wdt_info; |
655 | w_priv->wdd.ops = &ziirave_wdt_ops; | |
656 | w_priv->wdd.min_timeout = ZIIRAVE_TIMEOUT_MIN; | |
657 | w_priv->wdd.max_timeout = ZIIRAVE_TIMEOUT_MAX; | |
658 | w_priv->wdd.parent = &client->dev; | |
2c2f3080 | 659 | w_priv->wdd.groups = ziirave_wdt_groups; |
2a7b753a MW |
660 | |
661 | ret = watchdog_init_timeout(&w_priv->wdd, wdt_timeout, &client->dev); | |
662 | if (ret) { | |
663 | dev_info(&client->dev, | |
664 | "Unable to select timeout value, using default\n"); | |
665 | } | |
666 | ||
667 | /* | |
668 | * The default value set in the watchdog should be perfectly valid, so | |
669 | * pass that in if we haven't provided one via the module parameter or | |
670 | * of property. | |
671 | */ | |
672 | if (w_priv->wdd.timeout == 0) { | |
673 | val = i2c_smbus_read_byte_data(client, ZIIRAVE_WDT_TIMEOUT); | |
674 | if (val < 0) | |
675 | return val; | |
676 | ||
677 | if (val < ZIIRAVE_TIMEOUT_MIN) | |
678 | return -ENODEV; | |
679 | ||
680 | w_priv->wdd.timeout = val; | |
681 | } else { | |
682 | ret = ziirave_wdt_set_timeout(&w_priv->wdd, | |
683 | w_priv->wdd.timeout); | |
684 | if (ret) | |
685 | return ret; | |
686 | ||
687 | dev_info(&client->dev, "Timeout set to %ds.", | |
688 | w_priv->wdd.timeout); | |
689 | } | |
690 | ||
691 | watchdog_set_nowayout(&w_priv->wdd, nowayout); | |
692 | ||
693 | i2c_set_clientdata(client, w_priv); | |
694 | ||
695 | /* If in unconfigured state, set to stopped */ | |
696 | val = i2c_smbus_read_byte_data(client, ZIIRAVE_WDT_STATE); | |
697 | if (val < 0) | |
698 | return val; | |
699 | ||
700 | if (val == ZIIRAVE_STATE_INITIAL) | |
701 | ziirave_wdt_stop(&w_priv->wdd); | |
702 | ||
703 | ret = ziirave_wdt_init_duration(client); | |
704 | if (ret) | |
705 | return ret; | |
706 | ||
707 | ret = ziirave_wdt_revision(client, &w_priv->firmware_rev, | |
708 | ZIIRAVE_WDT_FIRM_VER_MAJOR); | |
709 | if (ret) | |
710 | return ret; | |
711 | ||
712 | ret = ziirave_wdt_revision(client, &w_priv->bootloader_rev, | |
713 | ZIIRAVE_WDT_BOOT_VER_MAJOR); | |
714 | if (ret) | |
715 | return ret; | |
716 | ||
717 | w_priv->reset_reason = i2c_smbus_read_byte_data(client, | |
718 | ZIIRAVE_WDT_RESET_REASON); | |
719 | if (w_priv->reset_reason < 0) | |
720 | return w_priv->reset_reason; | |
721 | ||
722 | if (w_priv->reset_reason >= ARRAY_SIZE(ziirave_reasons) || | |
723 | !ziirave_reasons[w_priv->reset_reason]) | |
724 | return -ENODEV; | |
725 | ||
726 | ret = watchdog_register_device(&w_priv->wdd); | |
2a7b753a | 727 | |
2c2f3080 | 728 | return ret; |
2a7b753a MW |
729 | } |
730 | ||
731 | static int ziirave_wdt_remove(struct i2c_client *client) | |
732 | { | |
733 | struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client); | |
734 | ||
2a7b753a MW |
735 | watchdog_unregister_device(&w_priv->wdd); |
736 | ||
737 | return 0; | |
738 | } | |
739 | ||
740 | static struct i2c_device_id ziirave_wdt_id[] = { | |
22daf7a7 | 741 | { "rave-wdt", 0 }, |
2a7b753a MW |
742 | { } |
743 | }; | |
744 | MODULE_DEVICE_TABLE(i2c, ziirave_wdt_id); | |
745 | ||
746 | static const struct of_device_id zrv_wdt_of_match[] = { | |
747 | { .compatible = "zii,rave-wdt", }, | |
748 | { }, | |
749 | }; | |
750 | MODULE_DEVICE_TABLE(of, zrv_wdt_of_match); | |
751 | ||
752 | static struct i2c_driver ziirave_wdt_driver = { | |
753 | .driver = { | |
754 | .name = "ziirave_wdt", | |
755 | .of_match_table = zrv_wdt_of_match, | |
756 | }, | |
757 | .probe = ziirave_wdt_probe, | |
758 | .remove = ziirave_wdt_remove, | |
759 | .id_table = ziirave_wdt_id, | |
760 | }; | |
761 | ||
762 | module_i2c_driver(ziirave_wdt_driver); | |
763 | ||
764 | MODULE_AUTHOR("Martyn Welch <martyn.welch@collabora.co.uk"); | |
765 | MODULE_DESCRIPTION("Zodiac Aerospace RAVE Switch Watchdog Processor Driver"); | |
766 | MODULE_LICENSE("GPL"); |