Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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