1 /* 2 * Watchdog device driver for DA9062 and DA9061 PMICs 3 * Copyright (C) 2015 Dialog Semiconductor Ltd. 4 * 5 * This program is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU General Public License 7 * as published by the Free Software Foundation; either version 2 8 * of the License, or (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 */ 15 16 #include <linux/kernel.h> 17 #include <linux/module.h> 18 #include <linux/watchdog.h> 19 #include <linux/platform_device.h> 20 #include <linux/uaccess.h> 21 #include <linux/slab.h> 22 #include <linux/delay.h> 23 #include <linux/jiffies.h> 24 #include <linux/mfd/da9062/registers.h> 25 #include <linux/mfd/da9062/core.h> 26 #include <linux/regmap.h> 27 #include <linux/of.h> 28 29 static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 }; 30 #define DA9062_TWDSCALE_DISABLE 0 31 #define DA9062_TWDSCALE_MIN 1 32 #define DA9062_TWDSCALE_MAX (ARRAY_SIZE(wdt_timeout) - 1) 33 #define DA9062_WDT_MIN_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MIN] 34 #define DA9062_WDT_MAX_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MAX] 35 #define DA9062_WDG_DEFAULT_TIMEOUT wdt_timeout[DA9062_TWDSCALE_MAX-1] 36 #define DA9062_RESET_PROTECTION_MS 300 37 38 struct da9062_watchdog { 39 struct da9062 *hw; 40 struct watchdog_device wdtdev; 41 unsigned long j_time_stamp; 42 }; 43 44 static void da9062_set_window_start(struct da9062_watchdog *wdt) 45 { 46 wdt->j_time_stamp = jiffies; 47 } 48 49 static unsigned int da9062_wdt_timeout_to_sel(unsigned int secs) 50 { 51 unsigned int i; 52 53 for (i = DA9062_TWDSCALE_MIN; i <= DA9062_TWDSCALE_MAX; i++) { 54 if (wdt_timeout[i] >= secs) 55 return i; 56 } 57 58 return DA9062_TWDSCALE_MAX; 59 } 60 61 static int da9062_reset_watchdog_timer(struct da9062_watchdog *wdt) 62 { 63 int ret; 64 65 ret = regmap_update_bits(wdt->hw->regmap, 66 DA9062AA_CONTROL_F, 67 DA9062AA_WATCHDOG_MASK, 68 DA9062AA_WATCHDOG_MASK); 69 70 da9062_set_window_start(wdt); 71 72 return ret; 73 } 74 75 static int da9062_wdt_update_timeout_register(struct da9062_watchdog *wdt, 76 unsigned int regval) 77 { 78 struct da9062 *chip = wdt->hw; 79 int ret; 80 81 ret = da9062_reset_watchdog_timer(wdt); 82 if (ret) 83 return ret; 84 85 regmap_update_bits(chip->regmap, 86 DA9062AA_CONTROL_D, 87 DA9062AA_TWDSCALE_MASK, 88 DA9062_TWDSCALE_DISABLE); 89 90 usleep_range(150, 300); 91 92 return regmap_update_bits(chip->regmap, 93 DA9062AA_CONTROL_D, 94 DA9062AA_TWDSCALE_MASK, 95 regval); 96 } 97 98 static int da9062_wdt_start(struct watchdog_device *wdd) 99 { 100 struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); 101 unsigned int selector; 102 int ret; 103 104 selector = da9062_wdt_timeout_to_sel(wdt->wdtdev.timeout); 105 ret = da9062_wdt_update_timeout_register(wdt, selector); 106 if (ret) 107 dev_err(wdt->hw->dev, "Watchdog failed to start (err = %d)\n", 108 ret); 109 110 return ret; 111 } 112 113 static int da9062_wdt_stop(struct watchdog_device *wdd) 114 { 115 struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); 116 int ret; 117 118 ret = da9062_reset_watchdog_timer(wdt); 119 if (ret) { 120 dev_err(wdt->hw->dev, "Failed to ping the watchdog (err = %d)\n", 121 ret); 122 return ret; 123 } 124 125 ret = regmap_update_bits(wdt->hw->regmap, 126 DA9062AA_CONTROL_D, 127 DA9062AA_TWDSCALE_MASK, 128 DA9062_TWDSCALE_DISABLE); 129 if (ret) 130 dev_err(wdt->hw->dev, "Watchdog failed to stop (err = %d)\n", 131 ret); 132 133 return ret; 134 } 135 136 static int da9062_wdt_ping(struct watchdog_device *wdd) 137 { 138 struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); 139 int ret; 140 141 ret = da9062_reset_watchdog_timer(wdt); 142 if (ret) 143 dev_err(wdt->hw->dev, "Failed to ping the watchdog (err = %d)\n", 144 ret); 145 146 return ret; 147 } 148 149 static int da9062_wdt_set_timeout(struct watchdog_device *wdd, 150 unsigned int timeout) 151 { 152 struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); 153 unsigned int selector; 154 int ret; 155 156 selector = da9062_wdt_timeout_to_sel(timeout); 157 ret = da9062_wdt_update_timeout_register(wdt, selector); 158 if (ret) 159 dev_err(wdt->hw->dev, "Failed to set watchdog timeout (err = %d)\n", 160 ret); 161 else 162 wdd->timeout = wdt_timeout[selector]; 163 164 return ret; 165 } 166 167 static int da9062_wdt_restart(struct watchdog_device *wdd, unsigned long action, 168 void *data) 169 { 170 struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); 171 int ret; 172 173 ret = regmap_write(wdt->hw->regmap, 174 DA9062AA_CONTROL_F, 175 DA9062AA_SHUTDOWN_MASK); 176 if (ret) 177 dev_alert(wdt->hw->dev, "Failed to shutdown (err = %d)\n", 178 ret); 179 180 /* wait for reset to assert... */ 181 mdelay(500); 182 183 return ret; 184 } 185 186 static const struct watchdog_info da9062_watchdog_info = { 187 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, 188 .identity = "DA9062 WDT", 189 }; 190 191 static const struct watchdog_ops da9062_watchdog_ops = { 192 .owner = THIS_MODULE, 193 .start = da9062_wdt_start, 194 .stop = da9062_wdt_stop, 195 .ping = da9062_wdt_ping, 196 .set_timeout = da9062_wdt_set_timeout, 197 .restart = da9062_wdt_restart, 198 }; 199 200 static const struct of_device_id da9062_compatible_id_table[] = { 201 { .compatible = "dlg,da9062-watchdog", }, 202 { }, 203 }; 204 205 MODULE_DEVICE_TABLE(of, da9062_compatible_id_table); 206 207 static int da9062_wdt_probe(struct platform_device *pdev) 208 { 209 int ret; 210 struct da9062 *chip; 211 struct da9062_watchdog *wdt; 212 213 chip = dev_get_drvdata(pdev->dev.parent); 214 if (!chip) 215 return -EINVAL; 216 217 wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); 218 if (!wdt) 219 return -ENOMEM; 220 221 wdt->hw = chip; 222 223 wdt->wdtdev.info = &da9062_watchdog_info; 224 wdt->wdtdev.ops = &da9062_watchdog_ops; 225 wdt->wdtdev.min_timeout = DA9062_WDT_MIN_TIMEOUT; 226 wdt->wdtdev.max_timeout = DA9062_WDT_MAX_TIMEOUT; 227 wdt->wdtdev.min_hw_heartbeat_ms = DA9062_RESET_PROTECTION_MS; 228 wdt->wdtdev.timeout = DA9062_WDG_DEFAULT_TIMEOUT; 229 wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS; 230 wdt->wdtdev.parent = &pdev->dev; 231 232 watchdog_set_restart_priority(&wdt->wdtdev, 128); 233 234 watchdog_set_drvdata(&wdt->wdtdev, wdt); 235 236 ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdtdev); 237 if (ret < 0) { 238 dev_err(wdt->hw->dev, 239 "watchdog registration failed (%d)\n", ret); 240 return ret; 241 } 242 243 da9062_set_window_start(wdt); 244 245 return da9062_wdt_ping(&wdt->wdtdev); 246 } 247 248 static struct platform_driver da9062_wdt_driver = { 249 .probe = da9062_wdt_probe, 250 .driver = { 251 .name = "da9062-watchdog", 252 .of_match_table = da9062_compatible_id_table, 253 }, 254 }; 255 module_platform_driver(da9062_wdt_driver); 256 257 MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>"); 258 MODULE_DESCRIPTION("WDT device driver for Dialog DA9062 and DA9061"); 259 MODULE_LICENSE("GPL"); 260 MODULE_ALIAS("platform:da9062-watchdog"); 261