1236769Sobrien// SPDX-License-Identifier: GPL-2.0 2236769Sobrien// Copyright (c) 2018 Nuvoton Technology corporation. 3236769Sobrien// Copyright (c) 2018 IBM Corp. 4236769Sobrien 5236769Sobrien#include <linux/bitops.h> 6236769Sobrien#include <linux/clk.h> 7236769Sobrien#include <linux/delay.h> 8236769Sobrien#include <linux/interrupt.h> 9236769Sobrien#include <linux/kernel.h> 10236769Sobrien#include <linux/module.h> 11236769Sobrien#include <linux/of_irq.h> 12236769Sobrien#include <linux/platform_device.h> 13236769Sobrien#include <linux/slab.h> 14236769Sobrien#include <linux/watchdog.h> 15236769Sobrien 16236769Sobrien#define NPCM_WTCR 0x1C 17236769Sobrien 18236769Sobrien#define NPCM_WTCLK (BIT(10) | BIT(11)) /* Clock divider */ 19236769Sobrien#define NPCM_WTE BIT(7) /* Enable */ 20236769Sobrien#define NPCM_WTIE BIT(6) /* Enable irq */ 21236769Sobrien#define NPCM_WTIS (BIT(4) | BIT(5)) /* Interval selection */ 22236769Sobrien#define NPCM_WTIF BIT(3) /* Interrupt flag*/ 23236769Sobrien#define NPCM_WTRF BIT(2) /* Reset flag */ 24236769Sobrien#define NPCM_WTRE BIT(1) /* Reset enable */ 25236769Sobrien#define NPCM_WTR BIT(0) /* Reset counter */ 26236769Sobrien 27236769Sobrien/* 28236769Sobrien * Watchdog timeouts 29236769Sobrien * 30236769Sobrien * 170 msec: WTCLK=01 WTIS=00 VAL= 0x400 31236769Sobrien * 670 msec: WTCLK=01 WTIS=01 VAL= 0x410 32236769Sobrien * 1360 msec: WTCLK=10 WTIS=00 VAL= 0x800 33236769Sobrien * 2700 msec: WTCLK=01 WTIS=10 VAL= 0x420 34236769Sobrien * 5360 msec: WTCLK=10 WTIS=01 VAL= 0x810 35236769Sobrien * 10700 msec: WTCLK=01 WTIS=11 VAL= 0x430 36236769Sobrien * 21600 msec: WTCLK=10 WTIS=10 VAL= 0x820 37236769Sobrien * 43000 msec: WTCLK=11 WTIS=00 VAL= 0xC00 38236769Sobrien * 85600 msec: WTCLK=10 WTIS=11 VAL= 0x830 39236769Sobrien * 172000 msec: WTCLK=11 WTIS=01 VAL= 0xC10 40236769Sobrien * 687000 msec: WTCLK=11 WTIS=10 VAL= 0xC20 41236769Sobrien * 2750000 msec: WTCLK=11 WTIS=11 VAL= 0xC30 42236769Sobrien */ 43236769Sobrien 44236769Sobrienstruct npcm_wdt { 45236769Sobrien struct watchdog_device wdd; 46236769Sobrien void __iomem *reg; 47236769Sobrien struct clk *clk; 48236769Sobrien}; 49236769Sobrien 50236769Sobrienstatic inline struct npcm_wdt *to_npcm_wdt(struct watchdog_device *wdd) 51236769Sobrien{ 52236769Sobrien return container_of(wdd, struct npcm_wdt, wdd); 53236769Sobrien} 54236769Sobrien 55236769Sobrienstatic int npcm_wdt_ping(struct watchdog_device *wdd) 56236769Sobrien{ 57236769Sobrien struct npcm_wdt *wdt = to_npcm_wdt(wdd); 58236769Sobrien u32 val; 59236769Sobrien 60236769Sobrien val = readl(wdt->reg); 61236769Sobrien writel(val | NPCM_WTR, wdt->reg); 62236769Sobrien 63236769Sobrien return 0; 64236769Sobrien} 65236769Sobrien 66236769Sobrienstatic int npcm_wdt_start(struct watchdog_device *wdd) 67236769Sobrien{ 68236769Sobrien struct npcm_wdt *wdt = to_npcm_wdt(wdd); 69236769Sobrien u32 val; 70236769Sobrien 71236769Sobrien if (wdt->clk) 72236769Sobrien clk_prepare_enable(wdt->clk); 73236769Sobrien 74236769Sobrien if (wdd->timeout < 2) 75236769Sobrien val = 0x800; 76236769Sobrien else if (wdd->timeout < 3) 77236769Sobrien val = 0x420; 78236769Sobrien else if (wdd->timeout < 6) 79236769Sobrien val = 0x810; 80236769Sobrien else if (wdd->timeout < 11) 81236769Sobrien val = 0x430; 82236769Sobrien else if (wdd->timeout < 22) 83236769Sobrien val = 0x820; 84236769Sobrien else if (wdd->timeout < 44) 85236769Sobrien val = 0xC00; 86236769Sobrien else if (wdd->timeout < 87) 87236769Sobrien val = 0x830; 88236769Sobrien else if (wdd->timeout < 173) 89236769Sobrien val = 0xC10; 90236769Sobrien else if (wdd->timeout < 688) 91236769Sobrien val = 0xC20; 92236769Sobrien else 93236769Sobrien val = 0xC30; 94236769Sobrien 95236769Sobrien val |= NPCM_WTRE | NPCM_WTE | NPCM_WTR | NPCM_WTIE; 96236769Sobrien 97236769Sobrien writel(val, wdt->reg); 98236769Sobrien 99236769Sobrien return 0; 100236769Sobrien} 101236769Sobrien 102236769Sobrienstatic int npcm_wdt_stop(struct watchdog_device *wdd) 103236769Sobrien{ 104236769Sobrien struct npcm_wdt *wdt = to_npcm_wdt(wdd); 105236769Sobrien 106236769Sobrien writel(0, wdt->reg); 107236769Sobrien 108236769Sobrien if (wdt->clk) 109236769Sobrien clk_disable_unprepare(wdt->clk); 110236769Sobrien 111236769Sobrien return 0; 112236769Sobrien} 113236769Sobrien 114236769Sobrienstatic int npcm_wdt_set_timeout(struct watchdog_device *wdd, 115236769Sobrien unsigned int timeout) 116236769Sobrien{ 117236769Sobrien if (timeout < 2) 118236769Sobrien wdd->timeout = 1; 119236769Sobrien else if (timeout < 3) 120236769Sobrien wdd->timeout = 2; 121236769Sobrien else if (timeout < 6) 122236769Sobrien wdd->timeout = 5; 123236769Sobrien else if (timeout < 11) 124236769Sobrien wdd->timeout = 10; 125236769Sobrien else if (timeout < 22) 126236769Sobrien wdd->timeout = 21; 127236769Sobrien else if (timeout < 44) 128236769Sobrien wdd->timeout = 43; 129236769Sobrien else if (timeout < 87) 130236769Sobrien wdd->timeout = 86; 131236769Sobrien else if (timeout < 173) 132236769Sobrien wdd->timeout = 172; 133236769Sobrien else if (timeout < 688) 134236769Sobrien wdd->timeout = 687; 135236769Sobrien else 136236769Sobrien wdd->timeout = 2750; 137236769Sobrien 138236769Sobrien if (watchdog_active(wdd)) 139236769Sobrien npcm_wdt_start(wdd); 140236769Sobrien 141236769Sobrien return 0; 142236769Sobrien} 143236769Sobrien 144236769Sobrienstatic irqreturn_t npcm_wdt_interrupt(int irq, void *data) 145236769Sobrien{ 146236769Sobrien struct npcm_wdt *wdt = data; 147236769Sobrien 148236769Sobrien watchdog_notify_pretimeout(&wdt->wdd); 149236769Sobrien 150236769Sobrien return IRQ_HANDLED; 151236769Sobrien} 152236769Sobrien 153236769Sobrienstatic int npcm_wdt_restart(struct watchdog_device *wdd, 154236769Sobrien unsigned long action, void *data) 155236769Sobrien{ 156236769Sobrien struct npcm_wdt *wdt = to_npcm_wdt(wdd); 157236769Sobrien 158236769Sobrien /* For reset, we start the WDT clock and leave it running. */ 159236769Sobrien if (wdt->clk) 160236769Sobrien clk_prepare_enable(wdt->clk); 161236769Sobrien 162236769Sobrien writel(NPCM_WTR | NPCM_WTRE | NPCM_WTE, wdt->reg); 163236769Sobrien udelay(1000); 164236769Sobrien 165236769Sobrien return 0; 166236769Sobrien} 167236769Sobrien 168236769Sobrienstatic bool npcm_is_running(struct watchdog_device *wdd) 169236769Sobrien{ 170236769Sobrien struct npcm_wdt *wdt = to_npcm_wdt(wdd); 171236769Sobrien 172236769Sobrien return readl(wdt->reg) & NPCM_WTE; 173236769Sobrien} 174236769Sobrien 175236769Sobrienstatic const struct watchdog_info npcm_wdt_info = { 176236769Sobrien .identity = KBUILD_MODNAME, 177236769Sobrien .options = WDIOF_SETTIMEOUT 178236769Sobrien | WDIOF_KEEPALIVEPING 179236769Sobrien | WDIOF_MAGICCLOSE, 180236769Sobrien}; 181236769Sobrien 182236769Sobrienstatic const struct watchdog_ops npcm_wdt_ops = { 183236769Sobrien .owner = THIS_MODULE, 184236769Sobrien .start = npcm_wdt_start, 185236769Sobrien .stop = npcm_wdt_stop, 186236769Sobrien .ping = npcm_wdt_ping, 187236769Sobrien .set_timeout = npcm_wdt_set_timeout, 188236769Sobrien .restart = npcm_wdt_restart, 189236769Sobrien}; 190236769Sobrien 191236769Sobrienstatic int npcm_wdt_probe(struct platform_device *pdev) 192236769Sobrien{ 193236769Sobrien struct device *dev = &pdev->dev; 194236769Sobrien struct npcm_wdt *wdt; 195236769Sobrien int irq; 196236769Sobrien int ret; 197236769Sobrien 198236769Sobrien wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); 199236769Sobrien if (!wdt) 200236769Sobrien return -ENOMEM; 201236769Sobrien 202 wdt->reg = devm_platform_ioremap_resource(pdev, 0); 203 if (IS_ERR(wdt->reg)) 204 return PTR_ERR(wdt->reg); 205 206 wdt->clk = devm_clk_get_optional(&pdev->dev, NULL); 207 if (IS_ERR(wdt->clk)) 208 return PTR_ERR(wdt->clk); 209 210 irq = platform_get_irq(pdev, 0); 211 if (irq < 0) 212 return irq; 213 214 wdt->wdd.info = &npcm_wdt_info; 215 wdt->wdd.ops = &npcm_wdt_ops; 216 wdt->wdd.min_timeout = 1; 217 wdt->wdd.max_timeout = 2750; 218 wdt->wdd.parent = dev; 219 220 wdt->wdd.timeout = 86; 221 watchdog_init_timeout(&wdt->wdd, 0, dev); 222 223 /* Ensure timeout is able to be represented by the hardware */ 224 npcm_wdt_set_timeout(&wdt->wdd, wdt->wdd.timeout); 225 226 if (npcm_is_running(&wdt->wdd)) { 227 /* Restart with the default or device-tree specified timeout */ 228 npcm_wdt_start(&wdt->wdd); 229 set_bit(WDOG_HW_RUNNING, &wdt->wdd.status); 230 } 231 232 ret = devm_request_irq(dev, irq, npcm_wdt_interrupt, 0, "watchdog", 233 wdt); 234 if (ret) 235 return ret; 236 237 ret = devm_watchdog_register_device(dev, &wdt->wdd); 238 if (ret) 239 return ret; 240 241 dev_info(dev, "NPCM watchdog driver enabled\n"); 242 243 return 0; 244} 245 246#ifdef CONFIG_OF 247static const struct of_device_id npcm_wdt_match[] = { 248 {.compatible = "nuvoton,wpcm450-wdt"}, 249 {.compatible = "nuvoton,npcm750-wdt"}, 250 {}, 251}; 252MODULE_DEVICE_TABLE(of, npcm_wdt_match); 253#endif 254 255static struct platform_driver npcm_wdt_driver = { 256 .probe = npcm_wdt_probe, 257 .driver = { 258 .name = "npcm-wdt", 259 .of_match_table = of_match_ptr(npcm_wdt_match), 260 }, 261}; 262module_platform_driver(npcm_wdt_driver); 263 264MODULE_AUTHOR("Joel Stanley"); 265MODULE_DESCRIPTION("Watchdog driver for NPCM"); 266MODULE_LICENSE("GPL v2"); 267