1 /* 2 * Maxim MAX77620 Watchdog Driver 3 * 4 * Copyright (C) 2016 NVIDIA CORPORATION. All rights reserved. 5 * 6 * Author: Laxman Dewangan <ldewangan@nvidia.com> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 */ 12 13 #include <linux/err.h> 14 #include <linux/init.h> 15 #include <linux/kernel.h> 16 #include <linux/module.h> 17 #include <linux/mod_devicetable.h> 18 #include <linux/mfd/max77620.h> 19 #include <linux/platform_device.h> 20 #include <linux/regmap.h> 21 #include <linux/slab.h> 22 #include <linux/watchdog.h> 23 24 static bool nowayout = WATCHDOG_NOWAYOUT; 25 26 struct max77620_wdt { 27 struct device *dev; 28 struct regmap *rmap; 29 struct watchdog_device wdt_dev; 30 }; 31 32 static int max77620_wdt_start(struct watchdog_device *wdt_dev) 33 { 34 struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev); 35 36 return regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL2, 37 MAX77620_WDTEN, MAX77620_WDTEN); 38 } 39 40 static int max77620_wdt_stop(struct watchdog_device *wdt_dev) 41 { 42 struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev); 43 44 return regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL2, 45 MAX77620_WDTEN, 0); 46 } 47 48 static int max77620_wdt_ping(struct watchdog_device *wdt_dev) 49 { 50 struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev); 51 52 return regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL3, 53 MAX77620_WDTC_MASK, 0x1); 54 } 55 56 static int max77620_wdt_set_timeout(struct watchdog_device *wdt_dev, 57 unsigned int timeout) 58 { 59 struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev); 60 unsigned int wdt_timeout; 61 u8 regval; 62 int ret; 63 64 switch (timeout) { 65 case 0 ... 2: 66 regval = MAX77620_TWD_2s; 67 wdt_timeout = 2; 68 break; 69 70 case 3 ... 16: 71 regval = MAX77620_TWD_16s; 72 wdt_timeout = 16; 73 break; 74 75 case 17 ... 64: 76 regval = MAX77620_TWD_64s; 77 wdt_timeout = 64; 78 break; 79 80 default: 81 regval = MAX77620_TWD_128s; 82 wdt_timeout = 128; 83 break; 84 } 85 86 ret = regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL3, 87 MAX77620_WDTC_MASK, 0x1); 88 if (ret < 0) 89 return ret; 90 91 ret = regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL2, 92 MAX77620_TWD_MASK, regval); 93 if (ret < 0) 94 return ret; 95 96 wdt_dev->timeout = wdt_timeout; 97 98 return 0; 99 } 100 101 static const struct watchdog_info max77620_wdt_info = { 102 .identity = "max77620-watchdog", 103 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, 104 }; 105 106 static const struct watchdog_ops max77620_wdt_ops = { 107 .start = max77620_wdt_start, 108 .stop = max77620_wdt_stop, 109 .ping = max77620_wdt_ping, 110 .set_timeout = max77620_wdt_set_timeout, 111 }; 112 113 static int max77620_wdt_probe(struct platform_device *pdev) 114 { 115 struct max77620_wdt *wdt; 116 struct watchdog_device *wdt_dev; 117 unsigned int regval; 118 int ret; 119 120 wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); 121 if (!wdt) 122 return -ENOMEM; 123 124 wdt->dev = &pdev->dev; 125 wdt->rmap = dev_get_regmap(pdev->dev.parent, NULL); 126 if (!wdt->rmap) { 127 dev_err(wdt->dev, "Failed to get parent regmap\n"); 128 return -ENODEV; 129 } 130 131 wdt_dev = &wdt->wdt_dev; 132 wdt_dev->info = &max77620_wdt_info; 133 wdt_dev->ops = &max77620_wdt_ops; 134 wdt_dev->min_timeout = 2; 135 wdt_dev->max_timeout = 128; 136 wdt_dev->max_hw_heartbeat_ms = 128 * 1000; 137 138 platform_set_drvdata(pdev, wdt); 139 140 /* Enable WD_RST_WK - WDT expire results in a restart */ 141 ret = regmap_update_bits(wdt->rmap, MAX77620_REG_ONOFFCNFG2, 142 MAX77620_ONOFFCNFG2_WD_RST_WK, 143 MAX77620_ONOFFCNFG2_WD_RST_WK); 144 if (ret < 0) { 145 dev_err(wdt->dev, "Failed to set WD_RST_WK: %d\n", ret); 146 return ret; 147 } 148 149 /* Set WDT clear in OFF and sleep mode */ 150 ret = regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL2, 151 MAX77620_WDTOFFC | MAX77620_WDTSLPC, 152 MAX77620_WDTOFFC | MAX77620_WDTSLPC); 153 if (ret < 0) { 154 dev_err(wdt->dev, "Failed to set WDT OFF mode: %d\n", ret); 155 return ret; 156 } 157 158 /* Check if WDT running and if yes then set flags properly */ 159 ret = regmap_read(wdt->rmap, MAX77620_REG_CNFGGLBL2, ®val); 160 if (ret < 0) { 161 dev_err(wdt->dev, "Failed to read WDT CFG register: %d\n", ret); 162 return ret; 163 } 164 165 switch (regval & MAX77620_TWD_MASK) { 166 case MAX77620_TWD_2s: 167 wdt_dev->timeout = 2; 168 break; 169 case MAX77620_TWD_16s: 170 wdt_dev->timeout = 16; 171 break; 172 case MAX77620_TWD_64s: 173 wdt_dev->timeout = 64; 174 break; 175 default: 176 wdt_dev->timeout = 128; 177 break; 178 } 179 180 if (regval & MAX77620_WDTEN) 181 set_bit(WDOG_HW_RUNNING, &wdt_dev->status); 182 183 watchdog_set_nowayout(wdt_dev, nowayout); 184 watchdog_set_drvdata(wdt_dev, wdt); 185 186 ret = watchdog_register_device(wdt_dev); 187 if (ret < 0) { 188 dev_err(&pdev->dev, "watchdog registration failed: %d\n", ret); 189 return ret; 190 } 191 192 return 0; 193 } 194 195 static int max77620_wdt_remove(struct platform_device *pdev) 196 { 197 struct max77620_wdt *wdt = platform_get_drvdata(pdev); 198 199 max77620_wdt_stop(&wdt->wdt_dev); 200 watchdog_unregister_device(&wdt->wdt_dev); 201 202 return 0; 203 } 204 205 static const struct platform_device_id max77620_wdt_devtype[] = { 206 { .name = "max77620-watchdog", }, 207 { }, 208 }; 209 MODULE_DEVICE_TABLE(platform, max77620_wdt_devtype); 210 211 static struct platform_driver max77620_wdt_driver = { 212 .driver = { 213 .name = "max77620-watchdog", 214 }, 215 .probe = max77620_wdt_probe, 216 .remove = max77620_wdt_remove, 217 .id_table = max77620_wdt_devtype, 218 }; 219 220 module_platform_driver(max77620_wdt_driver); 221 222 MODULE_DESCRIPTION("Max77620 watchdog timer driver"); 223 224 module_param(nowayout, bool, 0); 225 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " 226 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 227 228 MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); 229 MODULE_LICENSE("GPL v2"); 230