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 21 #include <linux/i2c.h> 22 #include <linux/kernel.h> 23 #include <linux/module.h> 24 #include <linux/slab.h> 25 #include <linux/sysfs.h> 26 #include <linux/types.h> 27 #include <linux/version.h> 28 #include <linux/watchdog.h> 29 30 #define ZIIRAVE_TIMEOUT_MIN 3 31 #define ZIIRAVE_TIMEOUT_MAX 255 32 33 #define ZIIRAVE_PING_VALUE 0x0 34 35 #define ZIIRAVE_STATE_INITIAL 0x0 36 #define ZIIRAVE_STATE_OFF 0x1 37 #define ZIIRAVE_STATE_ON 0x2 38 39 static char *ziirave_reasons[] = {"power cycle", "hw watchdog", NULL, NULL, 40 "host request", NULL, "illegal configuration", 41 "illegal instruction", "illegal trap", 42 "unknown"}; 43 44 #define ZIIRAVE_WDT_FIRM_VER_MAJOR 0x1 45 #define ZIIRAVE_WDT_BOOT_VER_MAJOR 0x3 46 #define ZIIRAVE_WDT_RESET_REASON 0x5 47 #define ZIIRAVE_WDT_STATE 0x6 48 #define ZIIRAVE_WDT_TIMEOUT 0x7 49 #define ZIIRAVE_WDT_TIME_LEFT 0x8 50 #define ZIIRAVE_WDT_PING 0x9 51 #define ZIIRAVE_WDT_RESET_DURATION 0xa 52 53 struct ziirave_wdt_rev { 54 unsigned char major; 55 unsigned char minor; 56 }; 57 58 struct ziirave_wdt_data { 59 struct watchdog_device wdd; 60 struct ziirave_wdt_rev bootloader_rev; 61 struct ziirave_wdt_rev firmware_rev; 62 int reset_reason; 63 }; 64 65 static int wdt_timeout; 66 module_param(wdt_timeout, int, 0); 67 MODULE_PARM_DESC(wdt_timeout, "Watchdog timeout in seconds"); 68 69 static int reset_duration; 70 module_param(reset_duration, int, 0); 71 MODULE_PARM_DESC(reset_duration, 72 "Watchdog reset pulse duration in milliseconds"); 73 74 static bool nowayout = WATCHDOG_NOWAYOUT; 75 module_param(nowayout, bool, 0); 76 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started default=" 77 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 78 79 static int ziirave_wdt_revision(struct i2c_client *client, 80 struct ziirave_wdt_rev *rev, u8 command) 81 { 82 int ret; 83 84 ret = i2c_smbus_read_byte_data(client, command); 85 if (ret < 0) 86 return ret; 87 88 rev->major = ret; 89 90 ret = i2c_smbus_read_byte_data(client, command + 1); 91 if (ret < 0) 92 return ret; 93 94 rev->minor = ret; 95 96 return 0; 97 } 98 99 static int ziirave_wdt_set_state(struct watchdog_device *wdd, int state) 100 { 101 struct i2c_client *client = to_i2c_client(wdd->parent); 102 103 return i2c_smbus_write_byte_data(client, ZIIRAVE_WDT_STATE, state); 104 } 105 106 static int ziirave_wdt_start(struct watchdog_device *wdd) 107 { 108 return ziirave_wdt_set_state(wdd, ZIIRAVE_STATE_ON); 109 } 110 111 static int ziirave_wdt_stop(struct watchdog_device *wdd) 112 { 113 return ziirave_wdt_set_state(wdd, ZIIRAVE_STATE_OFF); 114 } 115 116 static int ziirave_wdt_ping(struct watchdog_device *wdd) 117 { 118 struct i2c_client *client = to_i2c_client(wdd->parent); 119 120 return i2c_smbus_write_byte_data(client, ZIIRAVE_WDT_PING, 121 ZIIRAVE_PING_VALUE); 122 } 123 124 static int ziirave_wdt_set_timeout(struct watchdog_device *wdd, 125 unsigned int timeout) 126 { 127 struct i2c_client *client = to_i2c_client(wdd->parent); 128 int ret; 129 130 ret = i2c_smbus_write_byte_data(client, ZIIRAVE_WDT_TIMEOUT, timeout); 131 if (!ret) 132 wdd->timeout = timeout; 133 134 return ret; 135 } 136 137 static unsigned int ziirave_wdt_get_timeleft(struct watchdog_device *wdd) 138 { 139 struct i2c_client *client = to_i2c_client(wdd->parent); 140 int ret; 141 142 ret = i2c_smbus_read_byte_data(client, ZIIRAVE_WDT_TIME_LEFT); 143 if (ret < 0) 144 ret = 0; 145 146 return ret; 147 } 148 149 static const struct watchdog_info ziirave_wdt_info = { 150 .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, 151 .identity = "Zodiac RAVE Watchdog", 152 }; 153 154 static const struct watchdog_ops ziirave_wdt_ops = { 155 .owner = THIS_MODULE, 156 .start = ziirave_wdt_start, 157 .stop = ziirave_wdt_stop, 158 .ping = ziirave_wdt_ping, 159 .set_timeout = ziirave_wdt_set_timeout, 160 .get_timeleft = ziirave_wdt_get_timeleft, 161 }; 162 163 static ssize_t ziirave_wdt_sysfs_show_firm(struct device *dev, 164 struct device_attribute *attr, 165 char *buf) 166 { 167 struct i2c_client *client = to_i2c_client(dev->parent); 168 struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client); 169 170 return sprintf(buf, "02.%02u.%02u", w_priv->firmware_rev.major, 171 w_priv->firmware_rev.minor); 172 } 173 174 static DEVICE_ATTR(firmware_version, S_IRUGO, ziirave_wdt_sysfs_show_firm, 175 NULL); 176 177 static ssize_t ziirave_wdt_sysfs_show_boot(struct device *dev, 178 struct device_attribute *attr, 179 char *buf) 180 { 181 struct i2c_client *client = to_i2c_client(dev->parent); 182 struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client); 183 184 return sprintf(buf, "01.%02u.%02u", w_priv->bootloader_rev.major, 185 w_priv->bootloader_rev.minor); 186 } 187 188 static DEVICE_ATTR(bootloader_version, S_IRUGO, ziirave_wdt_sysfs_show_boot, 189 NULL); 190 191 static ssize_t ziirave_wdt_sysfs_show_reason(struct device *dev, 192 struct device_attribute *attr, 193 char *buf) 194 { 195 struct i2c_client *client = to_i2c_client(dev->parent); 196 struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client); 197 198 return sprintf(buf, "%s", ziirave_reasons[w_priv->reset_reason]); 199 } 200 201 static DEVICE_ATTR(reset_reason, S_IRUGO, ziirave_wdt_sysfs_show_reason, 202 NULL); 203 204 static struct attribute *ziirave_wdt_attrs[] = { 205 &dev_attr_firmware_version.attr, 206 &dev_attr_bootloader_version.attr, 207 &dev_attr_reset_reason.attr, 208 NULL 209 }; 210 ATTRIBUTE_GROUPS(ziirave_wdt); 211 212 static int ziirave_wdt_init_duration(struct i2c_client *client) 213 { 214 int ret; 215 216 if (!reset_duration) { 217 /* See if the reset pulse duration is provided in an of_node */ 218 if (!client->dev.of_node) 219 ret = -ENODEV; 220 else 221 ret = of_property_read_u32(client->dev.of_node, 222 "reset-duration-ms", 223 &reset_duration); 224 if (ret) { 225 dev_info(&client->dev, 226 "Unable to set reset pulse duration, using default\n"); 227 return 0; 228 } 229 } 230 231 if (reset_duration < 1 || reset_duration > 255) 232 return -EINVAL; 233 234 dev_info(&client->dev, "Setting reset duration to %dms", 235 reset_duration); 236 237 return i2c_smbus_write_byte_data(client, ZIIRAVE_WDT_RESET_DURATION, 238 reset_duration); 239 } 240 241 static int ziirave_wdt_probe(struct i2c_client *client, 242 const struct i2c_device_id *id) 243 { 244 int ret; 245 struct ziirave_wdt_data *w_priv; 246 int val; 247 248 if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 249 return -ENODEV; 250 251 w_priv = devm_kzalloc(&client->dev, sizeof(*w_priv), GFP_KERNEL); 252 if (!w_priv) 253 return -ENOMEM; 254 255 w_priv->wdd.info = &ziirave_wdt_info; 256 w_priv->wdd.ops = &ziirave_wdt_ops; 257 w_priv->wdd.min_timeout = ZIIRAVE_TIMEOUT_MIN; 258 w_priv->wdd.max_timeout = ZIIRAVE_TIMEOUT_MAX; 259 w_priv->wdd.parent = &client->dev; 260 w_priv->wdd.groups = ziirave_wdt_groups; 261 262 ret = watchdog_init_timeout(&w_priv->wdd, wdt_timeout, &client->dev); 263 if (ret) { 264 dev_info(&client->dev, 265 "Unable to select timeout value, using default\n"); 266 } 267 268 /* 269 * The default value set in the watchdog should be perfectly valid, so 270 * pass that in if we haven't provided one via the module parameter or 271 * of property. 272 */ 273 if (w_priv->wdd.timeout == 0) { 274 val = i2c_smbus_read_byte_data(client, ZIIRAVE_WDT_TIMEOUT); 275 if (val < 0) 276 return val; 277 278 if (val < ZIIRAVE_TIMEOUT_MIN) 279 return -ENODEV; 280 281 w_priv->wdd.timeout = val; 282 } else { 283 ret = ziirave_wdt_set_timeout(&w_priv->wdd, 284 w_priv->wdd.timeout); 285 if (ret) 286 return ret; 287 288 dev_info(&client->dev, "Timeout set to %ds.", 289 w_priv->wdd.timeout); 290 } 291 292 watchdog_set_nowayout(&w_priv->wdd, nowayout); 293 294 i2c_set_clientdata(client, w_priv); 295 296 /* If in unconfigured state, set to stopped */ 297 val = i2c_smbus_read_byte_data(client, ZIIRAVE_WDT_STATE); 298 if (val < 0) 299 return val; 300 301 if (val == ZIIRAVE_STATE_INITIAL) 302 ziirave_wdt_stop(&w_priv->wdd); 303 304 ret = ziirave_wdt_init_duration(client); 305 if (ret) 306 return ret; 307 308 ret = ziirave_wdt_revision(client, &w_priv->firmware_rev, 309 ZIIRAVE_WDT_FIRM_VER_MAJOR); 310 if (ret) 311 return ret; 312 313 ret = ziirave_wdt_revision(client, &w_priv->bootloader_rev, 314 ZIIRAVE_WDT_BOOT_VER_MAJOR); 315 if (ret) 316 return ret; 317 318 w_priv->reset_reason = i2c_smbus_read_byte_data(client, 319 ZIIRAVE_WDT_RESET_REASON); 320 if (w_priv->reset_reason < 0) 321 return w_priv->reset_reason; 322 323 if (w_priv->reset_reason >= ARRAY_SIZE(ziirave_reasons) || 324 !ziirave_reasons[w_priv->reset_reason]) 325 return -ENODEV; 326 327 ret = watchdog_register_device(&w_priv->wdd); 328 329 return ret; 330 } 331 332 static int ziirave_wdt_remove(struct i2c_client *client) 333 { 334 struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client); 335 336 watchdog_unregister_device(&w_priv->wdd); 337 338 return 0; 339 } 340 341 static struct i2c_device_id ziirave_wdt_id[] = { 342 { "ziirave-wdt", 0 }, 343 { } 344 }; 345 MODULE_DEVICE_TABLE(i2c, ziirave_wdt_id); 346 347 static const struct of_device_id zrv_wdt_of_match[] = { 348 { .compatible = "zii,rave-wdt", }, 349 { }, 350 }; 351 MODULE_DEVICE_TABLE(of, zrv_wdt_of_match); 352 353 static struct i2c_driver ziirave_wdt_driver = { 354 .driver = { 355 .name = "ziirave_wdt", 356 .of_match_table = zrv_wdt_of_match, 357 }, 358 .probe = ziirave_wdt_probe, 359 .remove = ziirave_wdt_remove, 360 .id_table = ziirave_wdt_id, 361 }; 362 363 module_i2c_driver(ziirave_wdt_driver); 364 365 MODULE_AUTHOR("Martyn Welch <martyn.welch@collabora.co.uk"); 366 MODULE_DESCRIPTION("Zodiac Aerospace RAVE Switch Watchdog Processor Driver"); 367 MODULE_LICENSE("GPL"); 368