296 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Watchdog device driver for DA9062 and DA9061 PMICs4 * 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 23static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 };24#define DA9062_TWDSCALE_DISABLE 025#define DA9062_TWDSCALE_MIN 126#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 30031 32struct da9062_watchdog {33 struct da9062 *hw;34 struct watchdog_device wdtdev;35 bool use_sw_pm;36};37 38static 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 47static 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 59static 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 66static 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 84static 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 99static 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 115static 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 sequence122 * 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 135static 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 else148 wdd->timeout = wdt_timeout[selector];149 150 return ret;151}152 153static 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, use163 * unlocked flavor of i2c_smbus_xfer to avoid scenario where i2c bus164 * might be previously locked by some process unable to release the165 * 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 182static const struct watchdog_info da9062_watchdog_info = {183 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,184 .identity = "DA9062 WDT",185};186 187static 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 196static const struct of_device_id da9062_compatible_id_table[] = {197 { .compatible = "dlg,da9062-watchdog", },198 { },199};200 201MODULE_DEVICE_TABLE(of, da9062_compatible_id_table);202 203static 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 251static 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 265static 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 279static SIMPLE_DEV_PM_OPS(da9062_wdt_pm_ops,280 da9062_wdt_suspend, da9062_wdt_resume);281 282static 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};290module_platform_driver(da9062_wdt_driver);291 292MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>");293MODULE_DESCRIPTION("WDT device driver for Dialog DA9062 and DA9061");294MODULE_LICENSE("GPL");295MODULE_ALIAS("platform:da9062-watchdog");296