root/drivers/watchdog/bd70528_wdt.c

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

DEFINITIONS

This source file includes following definitions.
  1. bd70528_wdt_set
  2. bd70528_wdt_lock
  3. bd70528_wdt_unlock
  4. bd70528_wdt_set_locked
  5. bd70528_wdt_change
  6. bd70528_wdt_start
  7. bd70528_wdt_stop
  8. bd70528_wdt_set_timeout
  9. bd70528_wdt_probe

   1 // SPDX-License-Identifier: GPL-2.0
   2 // Copyright (C) 2018 ROHM Semiconductors
   3 // ROHM BD70528MWV watchdog driver
   4 
   5 #include <linux/bcd.h>
   6 #include <linux/kernel.h>
   7 #include <linux/mfd/rohm-bd70528.h>
   8 #include <linux/module.h>
   9 #include <linux/of.h>
  10 #include <linux/platform_device.h>
  11 #include <linux/regmap.h>
  12 #include <linux/watchdog.h>
  13 
  14 /*
  15  * Max time we can set is 1 hour, 59 minutes and 59 seconds
  16  * and Minimum time is 1 second
  17  */
  18 #define WDT_MAX_MS      ((2 * 60 * 60 - 1) * 1000)
  19 #define WDT_MIN_MS      1000
  20 #define DEFAULT_TIMEOUT 60
  21 
  22 #define WD_CTRL_MAGIC1 0x55
  23 #define WD_CTRL_MAGIC2 0xAA
  24 
  25 struct wdtbd70528 {
  26         struct device *dev;
  27         struct regmap *regmap;
  28         struct rohm_regmap_dev *mfd;
  29         struct watchdog_device wdt;
  30 };
  31 
  32 /**
  33  * bd70528_wdt_set - arm or disarm watchdog timer
  34  *
  35  * @data:       device data for the PMIC instance we want to operate on
  36  * @enable:     new state of WDT. zero to disable, non zero to enable
  37  * @old_state:  previous state of WDT will be filled here
  38  *
  39  * Arm or disarm WDT on BD70528 PMIC. Expected to be called only by
  40  * BD70528 RTC and BD70528 WDT drivers. The rtc_timer_lock must be taken
  41  * by calling bd70528_wdt_lock before calling bd70528_wdt_set.
  42  */
  43 int bd70528_wdt_set(struct rohm_regmap_dev *data, int enable, int *old_state)
  44 {
  45         int ret, i;
  46         unsigned int tmp;
  47         struct bd70528_data *bd70528 = container_of(data, struct bd70528_data,
  48                                                  chip);
  49         u8 wd_ctrl_arr[3] = { WD_CTRL_MAGIC1, WD_CTRL_MAGIC2, 0 };
  50         u8 *wd_ctrl = &wd_ctrl_arr[2];
  51 
  52         ret = regmap_read(bd70528->chip.regmap, BD70528_REG_WDT_CTRL, &tmp);
  53         if (ret)
  54                 return ret;
  55 
  56         *wd_ctrl = (u8)tmp;
  57 
  58         if (old_state) {
  59                 if (*wd_ctrl & BD70528_MASK_WDT_EN)
  60                         *old_state |= BD70528_WDT_STATE_BIT;
  61                 else
  62                         *old_state &= ~BD70528_WDT_STATE_BIT;
  63                 if ((!enable) == (!(*old_state & BD70528_WDT_STATE_BIT)))
  64                         return 0;
  65         }
  66 
  67         if (enable) {
  68                 if (*wd_ctrl & BD70528_MASK_WDT_EN)
  69                         return 0;
  70                 *wd_ctrl |= BD70528_MASK_WDT_EN;
  71         } else {
  72                 if (*wd_ctrl & BD70528_MASK_WDT_EN)
  73                         *wd_ctrl &= ~BD70528_MASK_WDT_EN;
  74                 else
  75                         return 0;
  76         }
  77 
  78         for (i = 0; i < 3; i++) {
  79                 ret = regmap_write(bd70528->chip.regmap, BD70528_REG_WDT_CTRL,
  80                                    wd_ctrl_arr[i]);
  81                 if (ret)
  82                         return ret;
  83         }
  84 
  85         ret = regmap_read(bd70528->chip.regmap, BD70528_REG_WDT_CTRL, &tmp);
  86         if ((tmp & BD70528_MASK_WDT_EN) != (*wd_ctrl & BD70528_MASK_WDT_EN)) {
  87                 dev_err(bd70528->chip.dev,
  88                         "Watchdog ctrl mismatch (hw) 0x%x (set) 0x%x\n",
  89                         tmp, *wd_ctrl);
  90                 ret = -EIO;
  91         }
  92 
  93         return ret;
  94 }
  95 EXPORT_SYMBOL(bd70528_wdt_set);
  96 
  97 /**
  98  * bd70528_wdt_lock - take WDT lock
  99  *
 100  * @bd70528:    device data for the PMIC instance we want to operate on
 101  *
 102  * Lock WDT for arming/disarming in order to avoid race condition caused
 103  * by WDT state changes initiated by WDT and RTC drivers.
 104  */
 105 void bd70528_wdt_lock(struct rohm_regmap_dev *data)
 106 {
 107         struct bd70528_data *bd70528 = container_of(data, struct bd70528_data,
 108                                                  chip);
 109 
 110         mutex_lock(&bd70528->rtc_timer_lock);
 111 }
 112 EXPORT_SYMBOL(bd70528_wdt_lock);
 113 
 114 /**
 115  * bd70528_wdt_unlock - unlock WDT lock
 116  *
 117  * @bd70528:    device data for the PMIC instance we want to operate on
 118  *
 119  * Unlock WDT lock which has previously been taken by call to
 120  * bd70528_wdt_lock.
 121  */
 122 void bd70528_wdt_unlock(struct rohm_regmap_dev *data)
 123 {
 124         struct bd70528_data *bd70528 = container_of(data, struct bd70528_data,
 125                                                  chip);
 126 
 127         mutex_unlock(&bd70528->rtc_timer_lock);
 128 }
 129 EXPORT_SYMBOL(bd70528_wdt_unlock);
 130 
 131 static int bd70528_wdt_set_locked(struct wdtbd70528 *w, int enable)
 132 {
 133         return bd70528_wdt_set(w->mfd, enable, NULL);
 134 }
 135 
 136 static int bd70528_wdt_change(struct wdtbd70528 *w, int enable)
 137 {
 138         int ret;
 139 
 140         bd70528_wdt_lock(w->mfd);
 141         ret = bd70528_wdt_set_locked(w, enable);
 142         bd70528_wdt_unlock(w->mfd);
 143 
 144         return ret;
 145 }
 146 
 147 static int bd70528_wdt_start(struct watchdog_device *wdt)
 148 {
 149         struct wdtbd70528 *w = watchdog_get_drvdata(wdt);
 150 
 151         dev_dbg(w->dev, "WDT ping...\n");
 152         return bd70528_wdt_change(w, 1);
 153 }
 154 
 155 static int bd70528_wdt_stop(struct watchdog_device *wdt)
 156 {
 157         struct wdtbd70528 *w = watchdog_get_drvdata(wdt);
 158 
 159         dev_dbg(w->dev, "WDT stopping...\n");
 160         return bd70528_wdt_change(w, 0);
 161 }
 162 
 163 static int bd70528_wdt_set_timeout(struct watchdog_device *wdt,
 164                                    unsigned int timeout)
 165 {
 166         unsigned int hours;
 167         unsigned int minutes;
 168         unsigned int seconds;
 169         int ret;
 170         struct wdtbd70528 *w = watchdog_get_drvdata(wdt);
 171 
 172         seconds = timeout;
 173         hours = timeout / (60 * 60);
 174         /* Maximum timeout is 1h 59m 59s => hours is 1 or 0 */
 175         if (hours)
 176                 seconds -= (60 * 60);
 177         minutes = seconds / 60;
 178         seconds = seconds % 60;
 179 
 180         bd70528_wdt_lock(w->mfd);
 181 
 182         ret = bd70528_wdt_set_locked(w, 0);
 183         if (ret)
 184                 goto out_unlock;
 185 
 186         ret = regmap_update_bits(w->regmap, BD70528_REG_WDT_HOUR,
 187                                  BD70528_MASK_WDT_HOUR, hours);
 188         if (ret) {
 189                 dev_err(w->dev, "Failed to set WDT hours\n");
 190                 goto out_en_unlock;
 191         }
 192         ret = regmap_update_bits(w->regmap, BD70528_REG_WDT_MINUTE,
 193                                  BD70528_MASK_WDT_MINUTE, bin2bcd(minutes));
 194         if (ret) {
 195                 dev_err(w->dev, "Failed to set WDT minutes\n");
 196                 goto out_en_unlock;
 197         }
 198         ret = regmap_update_bits(w->regmap, BD70528_REG_WDT_SEC,
 199                                  BD70528_MASK_WDT_SEC, bin2bcd(seconds));
 200         if (ret)
 201                 dev_err(w->dev, "Failed to set WDT seconds\n");
 202         else
 203                 dev_dbg(w->dev, "WDT tmo set to %u\n", timeout);
 204 
 205 out_en_unlock:
 206         ret = bd70528_wdt_set_locked(w, 1);
 207 out_unlock:
 208         bd70528_wdt_unlock(w->mfd);
 209 
 210         return ret;
 211 }
 212 
 213 static const struct watchdog_info bd70528_wdt_info = {
 214         .identity = "bd70528-wdt",
 215         .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
 216 };
 217 
 218 static const struct watchdog_ops bd70528_wdt_ops = {
 219         .start          = bd70528_wdt_start,
 220         .stop           = bd70528_wdt_stop,
 221         .set_timeout    = bd70528_wdt_set_timeout,
 222 };
 223 
 224 static int bd70528_wdt_probe(struct platform_device *pdev)
 225 {
 226         struct rohm_regmap_dev *bd70528;
 227         struct wdtbd70528 *w;
 228         int ret;
 229         unsigned int reg;
 230 
 231         bd70528 = dev_get_drvdata(pdev->dev.parent);
 232         if (!bd70528) {
 233                 dev_err(&pdev->dev, "No MFD driver data\n");
 234                 return -EINVAL;
 235         }
 236         w = devm_kzalloc(&pdev->dev, sizeof(*w), GFP_KERNEL);
 237         if (!w)
 238                 return -ENOMEM;
 239 
 240         w->regmap = bd70528->regmap;
 241         w->mfd = bd70528;
 242         w->dev = &pdev->dev;
 243 
 244         w->wdt.info = &bd70528_wdt_info;
 245         w->wdt.ops =  &bd70528_wdt_ops;
 246         w->wdt.min_hw_heartbeat_ms = WDT_MIN_MS;
 247         w->wdt.max_hw_heartbeat_ms = WDT_MAX_MS;
 248         w->wdt.parent = pdev->dev.parent;
 249         w->wdt.timeout = DEFAULT_TIMEOUT;
 250         watchdog_set_drvdata(&w->wdt, w);
 251         watchdog_init_timeout(&w->wdt, 0, pdev->dev.parent);
 252 
 253         ret = bd70528_wdt_set_timeout(&w->wdt, w->wdt.timeout);
 254         if (ret) {
 255                 dev_err(&pdev->dev, "Failed to set the watchdog timeout\n");
 256                 return ret;
 257         }
 258 
 259         bd70528_wdt_lock(w->mfd);
 260         ret = regmap_read(w->regmap, BD70528_REG_WDT_CTRL, &reg);
 261         bd70528_wdt_unlock(w->mfd);
 262 
 263         if (ret) {
 264                 dev_err(&pdev->dev, "Failed to get the watchdog state\n");
 265                 return ret;
 266         }
 267         if (reg & BD70528_MASK_WDT_EN) {
 268                 dev_dbg(&pdev->dev, "watchdog was running during probe\n");
 269                 set_bit(WDOG_HW_RUNNING, &w->wdt.status);
 270         }
 271 
 272         ret = devm_watchdog_register_device(&pdev->dev, &w->wdt);
 273         if (ret < 0)
 274                 dev_err(&pdev->dev, "watchdog registration failed: %d\n", ret);
 275 
 276         return ret;
 277 }
 278 
 279 static struct platform_driver bd70528_wdt = {
 280         .driver = {
 281                 .name = "bd70528-wdt"
 282         },
 283         .probe = bd70528_wdt_probe,
 284 };
 285 
 286 module_platform_driver(bd70528_wdt);
 287 
 288 MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
 289 MODULE_DESCRIPTION("BD70528 watchdog driver");
 290 MODULE_LICENSE("GPL");
 291 MODULE_ALIAS("platform:bd70528-wdt");

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