1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Watchdog device driver for DA9062 and DA9061 PMICs 4 * Copyright (C) 2015 Dialog Semiconductor Ltd. 5 * 6 */ 7 8 #include <linux/kernel.h> 9 #include <linux/module.h> 10 #include <linux/watchdog.h> 11 #include <linux/platform_device.h> 12 #include <linux/uaccess.h> 13 #include <linux/slab.h> 14 #include <linux/i2c.h> 15 #include <linux/delay.h> 16 #include <linux/jiffies.h> 17 #include <linux/mfd/da9062/registers.h> 18 #include <linux/mfd/da9062/core.h> 19 #include <linux/property.h> 20 #include <linux/regmap.h> 21 #include <linux/of.h> 22 23 static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 }; 24 #define DA9062_TWDSCALE_DISABLE 0 25 #define DA9062_TWDSCALE_MIN 1 26 #define DA9062_TWDSCALE_MAX (ARRAY_SIZE(wdt_timeout) - 1) 27 #define DA9062_WDT_MIN_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MIN] 28 #define DA9062_WDT_MAX_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MAX] 29 #define DA9062_WDG_DEFAULT_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MAX-1] 30 #define DA9062_RESET_PROTECTION_MS 300 31 32 struct da9062_watchdog { 33 struct da9062 *hw; 34 struct watchdog_device wdtdev; 35 bool use_sw_pm; 36 }; 37 38 static unsigned int da9062_wdt_read_timeout(struct da9062_watchdog *wdt) 39 { 40 unsigned int val; 41 42 regmap_read(wdt->hw->regmap, DA9062AA_CONTROL_D, &val); 43 44 return wdt_timeout[val & DA9062AA_TWDSCALE_MASK]; 45 } 46 47 static unsigned int da9062_wdt_timeout_to_sel(unsigned int secs) 48 { 49 unsigned int i; 50 51 for (i = DA9062_TWDSCALE_MIN; i <= DA9062_TWDSCALE_MAX; i++) { 52 if (wdt_timeout[i] >= secs) 53 return i; 54 } 55 56 return DA9062_TWDSCALE_MAX; 57 } 58 59 static int da9062_reset_watchdog_timer(struct da9062_watchdog *wdt) 60 { 61 return regmap_update_bits(wdt->hw->regmap, DA9062AA_CONTROL_F, 62 DA9062AA_WATCHDOG_MASK, 63 DA9062AA_WATCHDOG_MASK); 64 } 65 66 static int da9062_wdt_update_timeout_register(struct da9062_watchdog *wdt, 67 unsigned int regval) 68 { 69 struct da9062 *chip = wdt->hw; 70 71 regmap_update_bits(chip->regmap, 72 DA9062AA_CONTROL_D, 73 DA9062AA_TWDSCALE_MASK, 74 DA9062_TWDSCALE_DISABLE); 75 76 usleep_range(150, 300); 77 78 return regmap_update_bits(chip->regmap, 79 DA9062AA_CONTROL_D, 80 DA9062AA_TWDSCALE_MASK, 81 regval); 82 } 83 84 static int da9062_wdt_start(struct watchdog_device *wdd) 85 { 86 struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); 87 unsigned int selector; 88 int ret; 89 90 selector = da9062_wdt_timeout_to_sel(wdt->wdtdev.timeout); 91 ret = da9062_wdt_update_timeout_register(wdt, selector); 92 if (ret) 93 dev_err(wdt->hw->dev, "Watchdog failed to start (err = %d)\n", 94 ret); 95 96 return ret; 97 } 98 99 static int da9062_wdt_stop(struct watchdog_device *wdd) 100 { 101 struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); 102 int ret; 103 104 ret = regmap_update_bits(wdt->hw->regmap, 105 DA9062AA_CONTROL_D, 106 DA9062AA_TWDSCALE_MASK, 107 DA9062_TWDSCALE_DISABLE); 108 if (ret) 109 dev_err(wdt->hw->dev, "Watchdog failed to stop (err = %d)\n", 110 ret); 111 112 return ret; 113 } 114 115 static int da9062_wdt_ping(struct watchdog_device *wdd) 116 { 117 struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); 118 int ret; 119 120 /* 121 * Prevent pings from occurring late in system poweroff/reboot sequence 122 * and possibly locking out restart handler from accessing i2c bus. 123 */ 124 if (system_state > SYSTEM_RUNNING) 125 return 0; 126 127 ret = da9062_reset_watchdog_timer(wdt); 128 if (ret) 129 dev_err(wdt->hw->dev, "Failed to ping the watchdog (err = %d)\n", 130 ret); 131 132 return ret; 133 } 134 135 static int da9062_wdt_set_timeout(struct watchdog_device *wdd, 136 unsigned int timeout) 137 { 138 struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); 139 unsigned int selector; 140 int ret; 141 142 selector = da9062_wdt_timeout_to_sel(timeout); 143 ret = da9062_wdt_update_timeout_register(wdt, selector); 144 if (ret) 145 dev_err(wdt->hw->dev, "Failed to set watchdog timeout (err = %d)\n", 146 ret); 147 else 148 wdd->timeout = wdt_timeout[selector]; 149 150 return ret; 151 } 152 153 static int da9062_wdt_restart(struct watchdog_device *wdd, unsigned long action, 154 void *data) 155 { 156 struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); 157 struct i2c_client *client = to_i2c_client(wdt->hw->dev); 158 union i2c_smbus_data msg; 159 int ret; 160 161 /* 162 * Don't use regmap because it is not atomic safe. Additionally, use 163 * unlocked flavor of i2c_smbus_xfer to avoid scenario where i2c bus 164 * might be previously locked by some process unable to release the 165 * lock due to interrupts already being disabled at this late stage. 166 */ 167 msg.byte = DA9062AA_SHUTDOWN_MASK; 168 ret = __i2c_smbus_xfer(client->adapter, client->addr, client->flags, 169 I2C_SMBUS_WRITE, DA9062AA_CONTROL_F, 170 I2C_SMBUS_BYTE_DATA, &msg); 171 172 if (ret < 0) 173 dev_alert(wdt->hw->dev, "Failed to shutdown (err = %d)\n", 174 ret); 175 176 /* wait for reset to assert... */ 177 mdelay(500); 178 179 return ret; 180 } 181 182 static const struct watchdog_info da9062_watchdog_info = { 183 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, 184 .identity = "DA9062 WDT", 185 }; 186 187 static const struct watchdog_ops da9062_watchdog_ops = { 188 .owner = THIS_MODULE, 189 .start = da9062_wdt_start, 190 .stop = da9062_wdt_stop, 191 .ping = da9062_wdt_ping, 192 .set_timeout = da9062_wdt_set_timeout, 193 .restart = da9062_wdt_restart, 194 }; 195 196 static const struct of_device_id da9062_compatible_id_table[] = { 197 { .compatible = "dlg,da9062-watchdog", }, 198 { }, 199 }; 200 201 MODULE_DEVICE_TABLE(of, da9062_compatible_id_table); 202 203 static int da9062_wdt_probe(struct platform_device *pdev) 204 { 205 struct device *dev = &pdev->dev; 206 unsigned int timeout; 207 struct da9062 *chip; 208 struct da9062_watchdog *wdt; 209 210 chip = dev_get_drvdata(dev->parent); 211 if (!chip) 212 return -EINVAL; 213 214 wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); 215 if (!wdt) 216 return -ENOMEM; 217 218 wdt->use_sw_pm = device_property_present(dev, "dlg,use-sw-pm"); 219 220 wdt->hw = chip; 221 222 wdt->wdtdev.info = &da9062_watchdog_info; 223 wdt->wdtdev.ops = &da9062_watchdog_ops; 224 wdt->wdtdev.min_timeout = DA9062_WDT_MIN_TIMEOUT; 225 wdt->wdtdev.max_timeout = DA9062_WDT_MAX_TIMEOUT; 226 wdt->wdtdev.min_hw_heartbeat_ms = DA9062_RESET_PROTECTION_MS; 227 wdt->wdtdev.timeout = DA9062_WDG_DEFAULT_TIMEOUT; 228 wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS; 229 wdt->wdtdev.parent = dev; 230 231 watchdog_set_restart_priority(&wdt->wdtdev, 128); 232 233 watchdog_set_drvdata(&wdt->wdtdev, wdt); 234 dev_set_drvdata(dev, &wdt->wdtdev); 235 236 timeout = da9062_wdt_read_timeout(wdt); 237 if (timeout) 238 wdt->wdtdev.timeout = timeout; 239 240 /* Set timeout from DT value if available */ 241 watchdog_init_timeout(&wdt->wdtdev, 0, dev); 242 243 if (timeout) { 244 da9062_wdt_set_timeout(&wdt->wdtdev, wdt->wdtdev.timeout); 245 set_bit(WDOG_HW_RUNNING, &wdt->wdtdev.status); 246 } 247 248 return devm_watchdog_register_device(dev, &wdt->wdtdev); 249 } 250 251 static int __maybe_unused da9062_wdt_suspend(struct device *dev) 252 { 253 struct watchdog_device *wdd = dev_get_drvdata(dev); 254 struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); 255 256 if (!wdt->use_sw_pm) 257 return 0; 258 259 if (watchdog_active(wdd)) 260 return da9062_wdt_stop(wdd); 261 262 return 0; 263 } 264 265 static int __maybe_unused da9062_wdt_resume(struct device *dev) 266 { 267 struct watchdog_device *wdd = dev_get_drvdata(dev); 268 struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); 269 270 if (!wdt->use_sw_pm) 271 return 0; 272 273 if (watchdog_active(wdd)) 274 return da9062_wdt_start(wdd); 275 276 return 0; 277 } 278 279 static SIMPLE_DEV_PM_OPS(da9062_wdt_pm_ops, 280 da9062_wdt_suspend, da9062_wdt_resume); 281 282 static struct platform_driver da9062_wdt_driver = { 283 .probe = da9062_wdt_probe, 284 .driver = { 285 .name = "da9062-watchdog", 286 .pm = &da9062_wdt_pm_ops, 287 .of_match_table = da9062_compatible_id_table, 288 }, 289 }; 290 module_platform_driver(da9062_wdt_driver); 291 292 MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>"); 293 MODULE_DESCRIPTION("WDT device driver for Dialog DA9062 and DA9061"); 294 MODULE_LICENSE("GPL"); 295 MODULE_ALIAS("platform:da9062-watchdog"); 296