1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * watchdog driver for ZTE's zx2967 family 4 * 5 * Copyright (C) 2017 ZTE Ltd. 6 * 7 * Author: Baoyou Xie <baoyou.xie@linaro.org> 8 */ 9 10#include <linux/clk.h> 11#include <linux/io.h> 12#include <linux/mfd/syscon.h> 13#include <linux/module.h> 14#include <linux/of_address.h> 15#include <linux/platform_device.h> 16#include <linux/regmap.h> 17#include <linux/reset.h> 18#include <linux/watchdog.h> 19 20#define ZX2967_WDT_CFG_REG 0x4 21#define ZX2967_WDT_LOAD_REG 0x8 22#define ZX2967_WDT_REFRESH_REG 0x18 23#define ZX2967_WDT_START_REG 0x1c 24 25#define ZX2967_WDT_REFRESH_MASK GENMASK(5, 0) 26 27#define ZX2967_WDT_CFG_DIV(n) ((((n) & 0xff) - 1) << 8) 28#define ZX2967_WDT_START_EN 0x1 29 30/* 31 * Hardware magic number. 32 * When watchdog reg is written, the lowest 16 bits are valid, but 33 * the highest 16 bits should be always this number. 34 */ 35#define ZX2967_WDT_WRITEKEY (0x1234 << 16) 36#define ZX2967_WDT_VAL_MASK GENMASK(15, 0) 37 38#define ZX2967_WDT_DIV_DEFAULT 16 39#define ZX2967_WDT_DEFAULT_TIMEOUT 32 40#define ZX2967_WDT_MIN_TIMEOUT 1 41#define ZX2967_WDT_MAX_TIMEOUT 524 42#define ZX2967_WDT_MAX_COUNT 0xffff 43 44#define ZX2967_WDT_CLK_FREQ 0x8000 45 46#define ZX2967_WDT_FLAG_REBOOT_MON BIT(0) 47 48struct zx2967_wdt { 49 struct watchdog_device wdt_device; 50 void __iomem *reg_base; 51 struct clk *clock; 52}; 53 54static inline u32 zx2967_wdt_readl(struct zx2967_wdt *wdt, u16 reg) 55{ 56 return readl_relaxed(wdt->reg_base + reg); 57} 58 59static inline void zx2967_wdt_writel(struct zx2967_wdt *wdt, u16 reg, u32 val) 60{ 61 writel_relaxed(val | ZX2967_WDT_WRITEKEY, wdt->reg_base + reg); 62} 63 64static void zx2967_wdt_refresh(struct zx2967_wdt *wdt) 65{ 66 u32 val; 67 68 val = zx2967_wdt_readl(wdt, ZX2967_WDT_REFRESH_REG); 69 /* 70 * Bit 4-5, 1 and 2: refresh config info 71 * Bit 2-3, 1 and 2: refresh counter 72 * Bit 0-1, 1 and 2: refresh int-value 73 * we shift each group value between 1 and 2 to refresh all data. 74 */ 75 val ^= ZX2967_WDT_REFRESH_MASK; 76 zx2967_wdt_writel(wdt, ZX2967_WDT_REFRESH_REG, 77 val & ZX2967_WDT_VAL_MASK); 78} 79 80static int 81zx2967_wdt_set_timeout(struct watchdog_device *wdd, unsigned int timeout) 82{ 83 struct zx2967_wdt *wdt = watchdog_get_drvdata(wdd); 84 unsigned int divisor = ZX2967_WDT_DIV_DEFAULT; 85 u32 count; 86 87 count = timeout * ZX2967_WDT_CLK_FREQ; 88 if (count > divisor * ZX2967_WDT_MAX_COUNT) 89 divisor = DIV_ROUND_UP(count, ZX2967_WDT_MAX_COUNT); 90 count = DIV_ROUND_UP(count, divisor); 91 zx2967_wdt_writel(wdt, ZX2967_WDT_CFG_REG, 92 ZX2967_WDT_CFG_DIV(divisor) & ZX2967_WDT_VAL_MASK); 93 zx2967_wdt_writel(wdt, ZX2967_WDT_LOAD_REG, 94 count & ZX2967_WDT_VAL_MASK); 95 zx2967_wdt_refresh(wdt); 96 wdd->timeout = (count * divisor) / ZX2967_WDT_CLK_FREQ; 97 98 return 0; 99} 100 101static void __zx2967_wdt_start(struct zx2967_wdt *wdt) 102{ 103 u32 val; 104 105 val = zx2967_wdt_readl(wdt, ZX2967_WDT_START_REG); 106 val |= ZX2967_WDT_START_EN; 107 zx2967_wdt_writel(wdt, ZX2967_WDT_START_REG, 108 val & ZX2967_WDT_VAL_MASK); 109} 110 111static void __zx2967_wdt_stop(struct zx2967_wdt *wdt) 112{ 113 u32 val; 114 115 val = zx2967_wdt_readl(wdt, ZX2967_WDT_START_REG); 116 val &= ~ZX2967_WDT_START_EN; 117 zx2967_wdt_writel(wdt, ZX2967_WDT_START_REG, 118 val & ZX2967_WDT_VAL_MASK); 119} 120 121static int zx2967_wdt_start(struct watchdog_device *wdd) 122{ 123 struct zx2967_wdt *wdt = watchdog_get_drvdata(wdd); 124 125 zx2967_wdt_set_timeout(wdd, wdd->timeout); 126 __zx2967_wdt_start(wdt); 127 128 return 0; 129} 130 131static int zx2967_wdt_stop(struct watchdog_device *wdd) 132{ 133 struct zx2967_wdt *wdt = watchdog_get_drvdata(wdd); 134 135 __zx2967_wdt_stop(wdt); 136 137 return 0; 138} 139 140static int zx2967_wdt_keepalive(struct watchdog_device *wdd) 141{ 142 struct zx2967_wdt *wdt = watchdog_get_drvdata(wdd); 143 144 zx2967_wdt_refresh(wdt); 145 146 return 0; 147} 148 149#define ZX2967_WDT_OPTIONS \ 150 (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE) 151static const struct watchdog_info zx2967_wdt_ident = { 152 .options = ZX2967_WDT_OPTIONS, 153 .identity = "zx2967 watchdog", 154}; 155 156static const struct watchdog_ops zx2967_wdt_ops = { 157 .owner = THIS_MODULE, 158 .start = zx2967_wdt_start, 159 .stop = zx2967_wdt_stop, 160 .ping = zx2967_wdt_keepalive, 161 .set_timeout = zx2967_wdt_set_timeout, 162}; 163 164static void zx2967_wdt_reset_sysctrl(struct device *dev) 165{ 166 int ret; 167 void __iomem *regmap; 168 unsigned int offset, mask, config; 169 struct of_phandle_args out_args; 170 171 ret = of_parse_phandle_with_fixed_args(dev->of_node, 172 "zte,wdt-reset-sysctrl", 3, 0, &out_args); 173 if (ret) 174 return; 175 176 offset = out_args.args[0]; 177 config = out_args.args[1]; 178 mask = out_args.args[2]; 179 180 regmap = syscon_node_to_regmap(out_args.np); 181 if (IS_ERR(regmap)) { 182 of_node_put(out_args.np); 183 return; 184 } 185 186 regmap_update_bits(regmap, offset, mask, config); 187 of_node_put(out_args.np); 188} 189 190static void zx2967_clk_disable_unprepare(void *data) 191{ 192 clk_disable_unprepare(data); 193} 194 195static int zx2967_wdt_probe(struct platform_device *pdev) 196{ 197 struct device *dev = &pdev->dev; 198 struct zx2967_wdt *wdt; 199 int ret; 200 struct reset_control *rstc; 201 202 wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); 203 if (!wdt) 204 return -ENOMEM; 205 206 platform_set_drvdata(pdev, wdt); 207 208 wdt->wdt_device.info = &zx2967_wdt_ident; 209 wdt->wdt_device.ops = &zx2967_wdt_ops; 210 wdt->wdt_device.timeout = ZX2967_WDT_DEFAULT_TIMEOUT; 211 wdt->wdt_device.max_timeout = ZX2967_WDT_MAX_TIMEOUT; 212 wdt->wdt_device.min_timeout = ZX2967_WDT_MIN_TIMEOUT; 213 wdt->wdt_device.parent = dev; 214 215 wdt->reg_base = devm_platform_ioremap_resource(pdev, 0); 216 if (IS_ERR(wdt->reg_base)) 217 return PTR_ERR(wdt->reg_base); 218 219 zx2967_wdt_reset_sysctrl(dev); 220 221 wdt->clock = devm_clk_get(dev, NULL); 222 if (IS_ERR(wdt->clock)) { 223 dev_err(dev, "failed to find watchdog clock source\n"); 224 return PTR_ERR(wdt->clock); 225 } 226 227 ret = clk_prepare_enable(wdt->clock); 228 if (ret < 0) { 229 dev_err(dev, "failed to enable clock\n"); 230 return ret; 231 } 232 ret = devm_add_action_or_reset(dev, zx2967_clk_disable_unprepare, 233 wdt->clock); 234 if (ret) 235 return ret; 236 clk_set_rate(wdt->clock, ZX2967_WDT_CLK_FREQ); 237 238 rstc = devm_reset_control_get_exclusive(dev, NULL); 239 if (IS_ERR(rstc)) { 240 dev_err(dev, "failed to get rstc"); 241 return PTR_ERR(rstc); 242 } 243 244 reset_control_assert(rstc); 245 reset_control_deassert(rstc); 246 247 watchdog_set_drvdata(&wdt->wdt_device, wdt); 248 watchdog_init_timeout(&wdt->wdt_device, 249 ZX2967_WDT_DEFAULT_TIMEOUT, dev); 250 watchdog_set_nowayout(&wdt->wdt_device, WATCHDOG_NOWAYOUT); 251 252 ret = devm_watchdog_register_device(dev, &wdt->wdt_device); 253 if (ret) 254 return ret; 255 256 dev_info(dev, "watchdog enabled (timeout=%d sec, nowayout=%d)", 257 wdt->wdt_device.timeout, WATCHDOG_NOWAYOUT); 258 259 return 0; 260} 261 262static const struct of_device_id zx2967_wdt_match[] = { 263 { .compatible = "zte,zx296718-wdt", }, 264 {} 265}; 266MODULE_DEVICE_TABLE(of, zx2967_wdt_match); 267 268static struct platform_driver zx2967_wdt_driver = { 269 .probe = zx2967_wdt_probe, 270 .driver = { 271 .name = "zx2967-wdt", 272 .of_match_table = of_match_ptr(zx2967_wdt_match), 273 }, 274}; 275module_platform_driver(zx2967_wdt_driver); 276 277MODULE_AUTHOR("Baoyou Xie <baoyou.xie@linaro.org>"); 278MODULE_DESCRIPTION("ZTE zx2967 Watchdog Device Driver"); 279MODULE_LICENSE("GPL v2"); 280