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