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