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 25struct 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 */ 43int 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} 95EXPORT_SYMBOL(bd70528_wdt_set); 96 97/** 98 * bd70528_wdt_lock - take WDT lock 99 * 100 * @data: 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 */ 105void 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} 112EXPORT_SYMBOL(bd70528_wdt_lock); 113 114/** 115 * bd70528_wdt_unlock - unlock WDT lock 116 * 117 * @data: 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 */ 122void 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} 129EXPORT_SYMBOL(bd70528_wdt_unlock); 130 131static int bd70528_wdt_set_locked(struct wdtbd70528 *w, int enable) 132{ 133 return bd70528_wdt_set(w->mfd, enable, NULL); 134} 135 136static 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 147static 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 155static 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 163static 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 205out_en_unlock: 206 ret = bd70528_wdt_set_locked(w, 1); 207out_unlock: 208 bd70528_wdt_unlock(w->mfd); 209 210 return ret; 211} 212 213static const struct watchdog_info bd70528_wdt_info = { 214 .identity = "bd70528-wdt", 215 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, 216}; 217 218static 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 224static 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, ®); 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 279static struct platform_driver bd70528_wdt = { 280 .driver = { 281 .name = "bd70528-wdt" 282 }, 283 .probe = bd70528_wdt_probe, 284}; 285 286module_platform_driver(bd70528_wdt); 287 288MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>"); 289MODULE_DESCRIPTION("BD70528 watchdog driver"); 290MODULE_LICENSE("GPL"); 291MODULE_ALIAS("platform:bd70528-wdt"); 292