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