npcm_wdt.c (5731B)
1// SPDX-License-Identifier: GPL-2.0 2// Copyright (c) 2018 Nuvoton Technology corporation. 3// Copyright (c) 2018 IBM Corp. 4 5#include <linux/bitops.h> 6#include <linux/delay.h> 7#include <linux/interrupt.h> 8#include <linux/kernel.h> 9#include <linux/module.h> 10#include <linux/of_irq.h> 11#include <linux/platform_device.h> 12#include <linux/slab.h> 13#include <linux/watchdog.h> 14 15#define NPCM_WTCR 0x1C 16 17#define NPCM_WTCLK (BIT(10) | BIT(11)) /* Clock divider */ 18#define NPCM_WTE BIT(7) /* Enable */ 19#define NPCM_WTIE BIT(6) /* Enable irq */ 20#define NPCM_WTIS (BIT(4) | BIT(5)) /* Interval selection */ 21#define NPCM_WTIF BIT(3) /* Interrupt flag*/ 22#define NPCM_WTRF BIT(2) /* Reset flag */ 23#define NPCM_WTRE BIT(1) /* Reset enable */ 24#define NPCM_WTR BIT(0) /* Reset counter */ 25 26/* 27 * Watchdog timeouts 28 * 29 * 170 msec: WTCLK=01 WTIS=00 VAL= 0x400 30 * 670 msec: WTCLK=01 WTIS=01 VAL= 0x410 31 * 1360 msec: WTCLK=10 WTIS=00 VAL= 0x800 32 * 2700 msec: WTCLK=01 WTIS=10 VAL= 0x420 33 * 5360 msec: WTCLK=10 WTIS=01 VAL= 0x810 34 * 10700 msec: WTCLK=01 WTIS=11 VAL= 0x430 35 * 21600 msec: WTCLK=10 WTIS=10 VAL= 0x820 36 * 43000 msec: WTCLK=11 WTIS=00 VAL= 0xC00 37 * 85600 msec: WTCLK=10 WTIS=11 VAL= 0x830 38 * 172000 msec: WTCLK=11 WTIS=01 VAL= 0xC10 39 * 687000 msec: WTCLK=11 WTIS=10 VAL= 0xC20 40 * 2750000 msec: WTCLK=11 WTIS=11 VAL= 0xC30 41 */ 42 43struct npcm_wdt { 44 struct watchdog_device wdd; 45 void __iomem *reg; 46}; 47 48static inline struct npcm_wdt *to_npcm_wdt(struct watchdog_device *wdd) 49{ 50 return container_of(wdd, struct npcm_wdt, wdd); 51} 52 53static int npcm_wdt_ping(struct watchdog_device *wdd) 54{ 55 struct npcm_wdt *wdt = to_npcm_wdt(wdd); 56 u32 val; 57 58 val = readl(wdt->reg); 59 writel(val | NPCM_WTR, wdt->reg); 60 61 return 0; 62} 63 64static int npcm_wdt_start(struct watchdog_device *wdd) 65{ 66 struct npcm_wdt *wdt = to_npcm_wdt(wdd); 67 u32 val; 68 69 if (wdd->timeout < 2) 70 val = 0x800; 71 else if (wdd->timeout < 3) 72 val = 0x420; 73 else if (wdd->timeout < 6) 74 val = 0x810; 75 else if (wdd->timeout < 11) 76 val = 0x430; 77 else if (wdd->timeout < 22) 78 val = 0x820; 79 else if (wdd->timeout < 44) 80 val = 0xC00; 81 else if (wdd->timeout < 87) 82 val = 0x830; 83 else if (wdd->timeout < 173) 84 val = 0xC10; 85 else if (wdd->timeout < 688) 86 val = 0xC20; 87 else 88 val = 0xC30; 89 90 val |= NPCM_WTRE | NPCM_WTE | NPCM_WTR | NPCM_WTIE; 91 92 writel(val, wdt->reg); 93 94 return 0; 95} 96 97static int npcm_wdt_stop(struct watchdog_device *wdd) 98{ 99 struct npcm_wdt *wdt = to_npcm_wdt(wdd); 100 101 writel(0, wdt->reg); 102 103 return 0; 104} 105 106static int npcm_wdt_set_timeout(struct watchdog_device *wdd, 107 unsigned int timeout) 108{ 109 if (timeout < 2) 110 wdd->timeout = 1; 111 else if (timeout < 3) 112 wdd->timeout = 2; 113 else if (timeout < 6) 114 wdd->timeout = 5; 115 else if (timeout < 11) 116 wdd->timeout = 10; 117 else if (timeout < 22) 118 wdd->timeout = 21; 119 else if (timeout < 44) 120 wdd->timeout = 43; 121 else if (timeout < 87) 122 wdd->timeout = 86; 123 else if (timeout < 173) 124 wdd->timeout = 172; 125 else if (timeout < 688) 126 wdd->timeout = 687; 127 else 128 wdd->timeout = 2750; 129 130 if (watchdog_active(wdd)) 131 npcm_wdt_start(wdd); 132 133 return 0; 134} 135 136static irqreturn_t npcm_wdt_interrupt(int irq, void *data) 137{ 138 struct npcm_wdt *wdt = data; 139 140 watchdog_notify_pretimeout(&wdt->wdd); 141 142 return IRQ_HANDLED; 143} 144 145static int npcm_wdt_restart(struct watchdog_device *wdd, 146 unsigned long action, void *data) 147{ 148 struct npcm_wdt *wdt = to_npcm_wdt(wdd); 149 150 writel(NPCM_WTR | NPCM_WTRE | NPCM_WTE, wdt->reg); 151 udelay(1000); 152 153 return 0; 154} 155 156static bool npcm_is_running(struct watchdog_device *wdd) 157{ 158 struct npcm_wdt *wdt = to_npcm_wdt(wdd); 159 160 return readl(wdt->reg) & NPCM_WTE; 161} 162 163static const struct watchdog_info npcm_wdt_info = { 164 .identity = KBUILD_MODNAME, 165 .options = WDIOF_SETTIMEOUT 166 | WDIOF_KEEPALIVEPING 167 | WDIOF_MAGICCLOSE, 168}; 169 170static const struct watchdog_ops npcm_wdt_ops = { 171 .owner = THIS_MODULE, 172 .start = npcm_wdt_start, 173 .stop = npcm_wdt_stop, 174 .ping = npcm_wdt_ping, 175 .set_timeout = npcm_wdt_set_timeout, 176 .restart = npcm_wdt_restart, 177}; 178 179static int npcm_wdt_probe(struct platform_device *pdev) 180{ 181 struct device *dev = &pdev->dev; 182 struct npcm_wdt *wdt; 183 int irq; 184 int ret; 185 186 wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); 187 if (!wdt) 188 return -ENOMEM; 189 190 wdt->reg = devm_platform_ioremap_resource(pdev, 0); 191 if (IS_ERR(wdt->reg)) 192 return PTR_ERR(wdt->reg); 193 194 irq = platform_get_irq(pdev, 0); 195 if (irq < 0) 196 return irq; 197 198 wdt->wdd.info = &npcm_wdt_info; 199 wdt->wdd.ops = &npcm_wdt_ops; 200 wdt->wdd.min_timeout = 1; 201 wdt->wdd.max_timeout = 2750; 202 wdt->wdd.parent = dev; 203 204 wdt->wdd.timeout = 86; 205 watchdog_init_timeout(&wdt->wdd, 0, dev); 206 207 /* Ensure timeout is able to be represented by the hardware */ 208 npcm_wdt_set_timeout(&wdt->wdd, wdt->wdd.timeout); 209 210 if (npcm_is_running(&wdt->wdd)) { 211 /* Restart with the default or device-tree specified timeout */ 212 npcm_wdt_start(&wdt->wdd); 213 set_bit(WDOG_HW_RUNNING, &wdt->wdd.status); 214 } 215 216 ret = devm_request_irq(dev, irq, npcm_wdt_interrupt, 0, "watchdog", 217 wdt); 218 if (ret) 219 return ret; 220 221 ret = devm_watchdog_register_device(dev, &wdt->wdd); 222 if (ret) 223 return ret; 224 225 dev_info(dev, "NPCM watchdog driver enabled\n"); 226 227 return 0; 228} 229 230#ifdef CONFIG_OF 231static const struct of_device_id npcm_wdt_match[] = { 232 {.compatible = "nuvoton,wpcm450-wdt"}, 233 {.compatible = "nuvoton,npcm750-wdt"}, 234 {}, 235}; 236MODULE_DEVICE_TABLE(of, npcm_wdt_match); 237#endif 238 239static struct platform_driver npcm_wdt_driver = { 240 .probe = npcm_wdt_probe, 241 .driver = { 242 .name = "npcm-wdt", 243 .of_match_table = of_match_ptr(npcm_wdt_match), 244 }, 245}; 246module_platform_driver(npcm_wdt_driver); 247 248MODULE_AUTHOR("Joel Stanley"); 249MODULE_DESCRIPTION("Watchdog driver for NPCM"); 250MODULE_LICENSE("GPL v2");