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_timeout_to_sel(unsigned int secs) 39 { 40 unsigned int i; 41 42 for (i = DA9062_TWDSCALE_MIN; i <= DA9062_TWDSCALE_MAX; i++) { 43 if (wdt_timeout[i] >= secs) 44 return i; 45 } 46 47 return DA9062_TWDSCALE_MAX; 48 } 49 50 static int da9062_reset_watchdog_timer(struct da9062_watchdog *wdt) 51 { 52 return regmap_update_bits(wdt->hw->regmap, DA9062AA_CONTROL_F, 53 DA9062AA_WATCHDOG_MASK, 54 DA9062AA_WATCHDOG_MASK); 55 } 56 57 static int da9062_wdt_update_timeout_register(struct da9062_watchdog *wdt, 58 unsigned int regval) 59 { 60 struct da9062 *chip = wdt->hw; 61 int ret; 62 63 ret = da9062_reset_watchdog_timer(wdt); 64 if (ret) 65 return ret; 66 67 regmap_update_bits(chip->regmap, 68 DA9062AA_CONTROL_D, 69 DA9062AA_TWDSCALE_MASK, 70 DA9062_TWDSCALE_DISABLE); 71 72 usleep_range(150, 300); 73 74 return regmap_update_bits(chip->regmap, 75 DA9062AA_CONTROL_D, 76 DA9062AA_TWDSCALE_MASK, 77 regval); 78 } 79 80 static int da9062_wdt_start(struct watchdog_device *wdd) 81 { 82 struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); 83 unsigned int selector; 84 int ret; 85 86 selector = da9062_wdt_timeout_to_sel(wdt->wdtdev.timeout); 87 ret = da9062_wdt_update_timeout_register(wdt, selector); 88 if (ret) 89 dev_err(wdt->hw->dev, "Watchdog failed to start (err = %d)\n", 90 ret); 91 92 return ret; 93 } 94 95 static int da9062_wdt_stop(struct watchdog_device *wdd) 96 { 97 struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); 98 int ret; 99 100 ret = regmap_update_bits(wdt->hw->regmap, 101 DA9062AA_CONTROL_D, 102 DA9062AA_TWDSCALE_MASK, 103 DA9062_TWDSCALE_DISABLE); 104 if (ret) 105 dev_err(wdt->hw->dev, "Watchdog failed to stop (err = %d)\n", 106 ret); 107 108 return ret; 109 } 110 111 static int da9062_wdt_ping(struct watchdog_device *wdd) 112 { 113 struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); 114 int ret; 115 116 ret = da9062_reset_watchdog_timer(wdt); 117 if (ret) 118 dev_err(wdt->hw->dev, "Failed to ping the watchdog (err = %d)\n", 119 ret); 120 121 return ret; 122 } 123 124 static int da9062_wdt_set_timeout(struct watchdog_device *wdd, 125 unsigned int timeout) 126 { 127 struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); 128 unsigned int selector; 129 int ret; 130 131 selector = da9062_wdt_timeout_to_sel(timeout); 132 ret = da9062_wdt_update_timeout_register(wdt, selector); 133 if (ret) 134 dev_err(wdt->hw->dev, "Failed to set watchdog timeout (err = %d)\n", 135 ret); 136 else 137 wdd->timeout = wdt_timeout[selector]; 138 139 return ret; 140 } 141 142 static int da9062_wdt_restart(struct watchdog_device *wdd, unsigned long action, 143 void *data) 144 { 145 struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); 146 struct i2c_client *client = to_i2c_client(wdt->hw->dev); 147 int ret; 148 149 /* Don't use regmap because it is not atomic safe */ 150 ret = i2c_smbus_write_byte_data(client, DA9062AA_CONTROL_F, 151 DA9062AA_SHUTDOWN_MASK); 152 if (ret < 0) 153 dev_alert(wdt->hw->dev, "Failed to shutdown (err = %d)\n", 154 ret); 155 156 /* wait for reset to assert... */ 157 mdelay(500); 158 159 return ret; 160 } 161 162 static const struct watchdog_info da9062_watchdog_info = { 163 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, 164 .identity = "DA9062 WDT", 165 }; 166 167 static const struct watchdog_ops da9062_watchdog_ops = { 168 .owner = THIS_MODULE, 169 .start = da9062_wdt_start, 170 .stop = da9062_wdt_stop, 171 .ping = da9062_wdt_ping, 172 .set_timeout = da9062_wdt_set_timeout, 173 .restart = da9062_wdt_restart, 174 }; 175 176 static const struct of_device_id da9062_compatible_id_table[] = { 177 { .compatible = "dlg,da9062-watchdog", }, 178 { }, 179 }; 180 181 MODULE_DEVICE_TABLE(of, da9062_compatible_id_table); 182 183 static int da9062_wdt_probe(struct platform_device *pdev) 184 { 185 struct device *dev = &pdev->dev; 186 int ret; 187 struct da9062 *chip; 188 struct da9062_watchdog *wdt; 189 190 chip = dev_get_drvdata(dev->parent); 191 if (!chip) 192 return -EINVAL; 193 194 wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); 195 if (!wdt) 196 return -ENOMEM; 197 198 wdt->use_sw_pm = device_property_present(dev, "dlg,use-sw-pm"); 199 200 wdt->hw = chip; 201 202 wdt->wdtdev.info = &da9062_watchdog_info; 203 wdt->wdtdev.ops = &da9062_watchdog_ops; 204 wdt->wdtdev.min_timeout = DA9062_WDT_MIN_TIMEOUT; 205 wdt->wdtdev.max_timeout = DA9062_WDT_MAX_TIMEOUT; 206 wdt->wdtdev.min_hw_heartbeat_ms = DA9062_RESET_PROTECTION_MS; 207 wdt->wdtdev.timeout = DA9062_WDG_DEFAULT_TIMEOUT; 208 wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS; 209 wdt->wdtdev.parent = dev; 210 211 watchdog_set_restart_priority(&wdt->wdtdev, 128); 212 213 watchdog_set_drvdata(&wdt->wdtdev, wdt); 214 dev_set_drvdata(dev, &wdt->wdtdev); 215 216 ret = devm_watchdog_register_device(dev, &wdt->wdtdev); 217 if (ret < 0) 218 return ret; 219 220 return da9062_wdt_ping(&wdt->wdtdev); 221 } 222 223 static int __maybe_unused da9062_wdt_suspend(struct device *dev) 224 { 225 struct watchdog_device *wdd = dev_get_drvdata(dev); 226 struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); 227 228 if (!wdt->use_sw_pm) 229 return 0; 230 231 if (watchdog_active(wdd)) 232 return da9062_wdt_stop(wdd); 233 234 return 0; 235 } 236 237 static int __maybe_unused da9062_wdt_resume(struct device *dev) 238 { 239 struct watchdog_device *wdd = dev_get_drvdata(dev); 240 struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); 241 242 if (!wdt->use_sw_pm) 243 return 0; 244 245 if (watchdog_active(wdd)) 246 return da9062_wdt_start(wdd); 247 248 return 0; 249 } 250 251 static SIMPLE_DEV_PM_OPS(da9062_wdt_pm_ops, 252 da9062_wdt_suspend, da9062_wdt_resume); 253 254 static struct platform_driver da9062_wdt_driver = { 255 .probe = da9062_wdt_probe, 256 .driver = { 257 .name = "da9062-watchdog", 258 .pm = &da9062_wdt_pm_ops, 259 .of_match_table = da9062_compatible_id_table, 260 }, 261 }; 262 module_platform_driver(da9062_wdt_driver); 263 264 MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>"); 265 MODULE_DESCRIPTION("WDT device driver for Dialog DA9062 and DA9061"); 266 MODULE_LICENSE("GPL"); 267 MODULE_ALIAS("platform:da9062-watchdog"); 268