root/drivers/watchdog/da9062_wdt.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. da9062_wdt_timeout_to_sel
  2. da9062_reset_watchdog_timer
  3. da9062_wdt_update_timeout_register
  4. da9062_wdt_start
  5. da9062_wdt_stop
  6. da9062_wdt_ping
  7. da9062_wdt_set_timeout
  8. da9062_wdt_restart
  9. da9062_wdt_probe

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

/* [<][>][^][v][top][bottom][index][help] */