Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Watchdog device driver for DA9062 and DA9061 PMICs
0004  * Copyright (C) 2015  Dialog Semiconductor Ltd.
0005  *
0006  */
0007 
0008 #include <linux/kernel.h>
0009 #include <linux/module.h>
0010 #include <linux/watchdog.h>
0011 #include <linux/platform_device.h>
0012 #include <linux/uaccess.h>
0013 #include <linux/slab.h>
0014 #include <linux/i2c.h>
0015 #include <linux/delay.h>
0016 #include <linux/jiffies.h>
0017 #include <linux/mfd/da9062/registers.h>
0018 #include <linux/mfd/da9062/core.h>
0019 #include <linux/property.h>
0020 #include <linux/regmap.h>
0021 #include <linux/of.h>
0022 
0023 static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 };
0024 #define DA9062_TWDSCALE_DISABLE     0
0025 #define DA9062_TWDSCALE_MIN     1
0026 #define DA9062_TWDSCALE_MAX     (ARRAY_SIZE(wdt_timeout) - 1)
0027 #define DA9062_WDT_MIN_TIMEOUT      wdt_timeout[DA9062_TWDSCALE_MIN]
0028 #define DA9062_WDT_MAX_TIMEOUT      wdt_timeout[DA9062_TWDSCALE_MAX]
0029 #define DA9062_WDG_DEFAULT_TIMEOUT  wdt_timeout[DA9062_TWDSCALE_MAX-1]
0030 #define DA9062_RESET_PROTECTION_MS  300
0031 
0032 struct da9062_watchdog {
0033     struct da9062 *hw;
0034     struct watchdog_device wdtdev;
0035     bool use_sw_pm;
0036 };
0037 
0038 static unsigned int da9062_wdt_read_timeout(struct da9062_watchdog *wdt)
0039 {
0040     unsigned int val;
0041 
0042     regmap_read(wdt->hw->regmap, DA9062AA_CONTROL_D, &val);
0043 
0044     return wdt_timeout[val & DA9062AA_TWDSCALE_MASK];
0045 }
0046 
0047 static unsigned int da9062_wdt_timeout_to_sel(unsigned int secs)
0048 {
0049     unsigned int i;
0050 
0051     for (i = DA9062_TWDSCALE_MIN; i <= DA9062_TWDSCALE_MAX; i++) {
0052         if (wdt_timeout[i] >= secs)
0053             return i;
0054     }
0055 
0056     return DA9062_TWDSCALE_MAX;
0057 }
0058 
0059 static int da9062_reset_watchdog_timer(struct da9062_watchdog *wdt)
0060 {
0061     return regmap_update_bits(wdt->hw->regmap, DA9062AA_CONTROL_F,
0062                   DA9062AA_WATCHDOG_MASK,
0063                   DA9062AA_WATCHDOG_MASK);
0064 }
0065 
0066 static int da9062_wdt_update_timeout_register(struct da9062_watchdog *wdt,
0067                           unsigned int regval)
0068 {
0069     struct da9062 *chip = wdt->hw;
0070 
0071     regmap_update_bits(chip->regmap,
0072                   DA9062AA_CONTROL_D,
0073                   DA9062AA_TWDSCALE_MASK,
0074                   DA9062_TWDSCALE_DISABLE);
0075 
0076     usleep_range(150, 300);
0077 
0078     return regmap_update_bits(chip->regmap,
0079                   DA9062AA_CONTROL_D,
0080                   DA9062AA_TWDSCALE_MASK,
0081                   regval);
0082 }
0083 
0084 static int da9062_wdt_start(struct watchdog_device *wdd)
0085 {
0086     struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
0087     unsigned int selector;
0088     int ret;
0089 
0090     selector = da9062_wdt_timeout_to_sel(wdt->wdtdev.timeout);
0091     ret = da9062_wdt_update_timeout_register(wdt, selector);
0092     if (ret)
0093         dev_err(wdt->hw->dev, "Watchdog failed to start (err = %d)\n",
0094             ret);
0095 
0096     return ret;
0097 }
0098 
0099 static int da9062_wdt_stop(struct watchdog_device *wdd)
0100 {
0101     struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
0102     int ret;
0103 
0104     ret = regmap_update_bits(wdt->hw->regmap,
0105                  DA9062AA_CONTROL_D,
0106                  DA9062AA_TWDSCALE_MASK,
0107                  DA9062_TWDSCALE_DISABLE);
0108     if (ret)
0109         dev_err(wdt->hw->dev, "Watchdog failed to stop (err = %d)\n",
0110             ret);
0111 
0112     return ret;
0113 }
0114 
0115 static int da9062_wdt_ping(struct watchdog_device *wdd)
0116 {
0117     struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
0118     int ret;
0119 
0120     /*
0121      * Prevent pings from occurring late in system poweroff/reboot sequence
0122      * and possibly locking out restart handler from accessing i2c bus.
0123      */
0124     if (system_state > SYSTEM_RUNNING)
0125         return 0;
0126 
0127     ret = da9062_reset_watchdog_timer(wdt);
0128     if (ret)
0129         dev_err(wdt->hw->dev, "Failed to ping the watchdog (err = %d)\n",
0130             ret);
0131 
0132     return ret;
0133 }
0134 
0135 static int da9062_wdt_set_timeout(struct watchdog_device *wdd,
0136                   unsigned int timeout)
0137 {
0138     struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
0139     unsigned int selector;
0140     int ret;
0141 
0142     selector = da9062_wdt_timeout_to_sel(timeout);
0143     ret = da9062_wdt_update_timeout_register(wdt, selector);
0144     if (ret)
0145         dev_err(wdt->hw->dev, "Failed to set watchdog timeout (err = %d)\n",
0146             ret);
0147     else
0148         wdd->timeout = wdt_timeout[selector];
0149 
0150     return ret;
0151 }
0152 
0153 static int da9062_wdt_restart(struct watchdog_device *wdd, unsigned long action,
0154                   void *data)
0155 {
0156     struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
0157     struct i2c_client *client = to_i2c_client(wdt->hw->dev);
0158     int ret;
0159 
0160     /* Don't use regmap because it is not atomic safe */
0161     ret = i2c_smbus_write_byte_data(client, DA9062AA_CONTROL_F,
0162                     DA9062AA_SHUTDOWN_MASK);
0163     if (ret < 0)
0164         dev_alert(wdt->hw->dev, "Failed to shutdown (err = %d)\n",
0165               ret);
0166 
0167     /* wait for reset to assert... */
0168     mdelay(500);
0169 
0170     return ret;
0171 }
0172 
0173 static const struct watchdog_info da9062_watchdog_info = {
0174     .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
0175     .identity = "DA9062 WDT",
0176 };
0177 
0178 static const struct watchdog_ops da9062_watchdog_ops = {
0179     .owner = THIS_MODULE,
0180     .start = da9062_wdt_start,
0181     .stop = da9062_wdt_stop,
0182     .ping = da9062_wdt_ping,
0183     .set_timeout = da9062_wdt_set_timeout,
0184     .restart = da9062_wdt_restart,
0185 };
0186 
0187 static const struct of_device_id da9062_compatible_id_table[] = {
0188     { .compatible = "dlg,da9062-watchdog", },
0189     { },
0190 };
0191 
0192 MODULE_DEVICE_TABLE(of, da9062_compatible_id_table);
0193 
0194 static int da9062_wdt_probe(struct platform_device *pdev)
0195 {
0196     struct device *dev = &pdev->dev;
0197     unsigned int timeout;
0198     struct da9062 *chip;
0199     struct da9062_watchdog *wdt;
0200 
0201     chip = dev_get_drvdata(dev->parent);
0202     if (!chip)
0203         return -EINVAL;
0204 
0205     wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
0206     if (!wdt)
0207         return -ENOMEM;
0208 
0209     wdt->use_sw_pm = device_property_present(dev, "dlg,use-sw-pm");
0210 
0211     wdt->hw = chip;
0212 
0213     wdt->wdtdev.info = &da9062_watchdog_info;
0214     wdt->wdtdev.ops = &da9062_watchdog_ops;
0215     wdt->wdtdev.min_timeout = DA9062_WDT_MIN_TIMEOUT;
0216     wdt->wdtdev.max_timeout = DA9062_WDT_MAX_TIMEOUT;
0217     wdt->wdtdev.min_hw_heartbeat_ms = DA9062_RESET_PROTECTION_MS;
0218     wdt->wdtdev.timeout = DA9062_WDG_DEFAULT_TIMEOUT;
0219     wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
0220     wdt->wdtdev.parent = dev;
0221 
0222     watchdog_set_restart_priority(&wdt->wdtdev, 128);
0223 
0224     watchdog_set_drvdata(&wdt->wdtdev, wdt);
0225     dev_set_drvdata(dev, &wdt->wdtdev);
0226 
0227     timeout = da9062_wdt_read_timeout(wdt);
0228     if (timeout)
0229         wdt->wdtdev.timeout = timeout;
0230 
0231     /* Set timeout from DT value if available */
0232     watchdog_init_timeout(&wdt->wdtdev, 0, dev);
0233 
0234     if (timeout) {
0235         da9062_wdt_set_timeout(&wdt->wdtdev, wdt->wdtdev.timeout);
0236         set_bit(WDOG_HW_RUNNING, &wdt->wdtdev.status);
0237     }
0238 
0239     return devm_watchdog_register_device(dev, &wdt->wdtdev);
0240 }
0241 
0242 static int __maybe_unused da9062_wdt_suspend(struct device *dev)
0243 {
0244     struct watchdog_device *wdd = dev_get_drvdata(dev);
0245     struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
0246 
0247     if (!wdt->use_sw_pm)
0248         return 0;
0249 
0250     if (watchdog_active(wdd))
0251         return da9062_wdt_stop(wdd);
0252 
0253     return 0;
0254 }
0255 
0256 static int __maybe_unused da9062_wdt_resume(struct device *dev)
0257 {
0258     struct watchdog_device *wdd = dev_get_drvdata(dev);
0259     struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
0260 
0261     if (!wdt->use_sw_pm)
0262         return 0;
0263 
0264     if (watchdog_active(wdd))
0265         return da9062_wdt_start(wdd);
0266 
0267     return 0;
0268 }
0269 
0270 static SIMPLE_DEV_PM_OPS(da9062_wdt_pm_ops,
0271              da9062_wdt_suspend, da9062_wdt_resume);
0272 
0273 static struct platform_driver da9062_wdt_driver = {
0274     .probe = da9062_wdt_probe,
0275     .driver = {
0276         .name = "da9062-watchdog",
0277         .pm = &da9062_wdt_pm_ops,
0278         .of_match_table = da9062_compatible_id_table,
0279     },
0280 };
0281 module_platform_driver(da9062_wdt_driver);
0282 
0283 MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>");
0284 MODULE_DESCRIPTION("WDT device driver for Dialog DA9062 and DA9061");
0285 MODULE_LICENSE("GPL");
0286 MODULE_ALIAS("platform:da9062-watchdog");