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 43 struct npcm_wdt { 44 struct watchdog_device wdd; 45 void __iomem *reg; 46 }; 47 48 static inline struct npcm_wdt *to_npcm_wdt(struct watchdog_device *wdd) 49 { 50 return container_of(wdd, struct npcm_wdt, wdd); 51 } 52 53 static 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 64 static 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 97 static 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 106 107 static int npcm_wdt_set_timeout(struct watchdog_device *wdd, 108 unsigned int timeout) 109 { 110 if (timeout < 2) 111 wdd->timeout = 1; 112 else if (timeout < 3) 113 wdd->timeout = 2; 114 else if (timeout < 6) 115 wdd->timeout = 5; 116 else if (timeout < 11) 117 wdd->timeout = 10; 118 else if (timeout < 22) 119 wdd->timeout = 21; 120 else if (timeout < 44) 121 wdd->timeout = 43; 122 else if (timeout < 87) 123 wdd->timeout = 86; 124 else if (timeout < 173) 125 wdd->timeout = 172; 126 else if (timeout < 688) 127 wdd->timeout = 687; 128 else 129 wdd->timeout = 2750; 130 131 if (watchdog_active(wdd)) 132 npcm_wdt_start(wdd); 133 134 return 0; 135 } 136 137 static irqreturn_t npcm_wdt_interrupt(int irq, void *data) 138 { 139 struct npcm_wdt *wdt = data; 140 141 watchdog_notify_pretimeout(&wdt->wdd); 142 143 return IRQ_HANDLED; 144 } 145 146 static int npcm_wdt_restart(struct watchdog_device *wdd, 147 unsigned long action, void *data) 148 { 149 struct npcm_wdt *wdt = to_npcm_wdt(wdd); 150 151 writel(NPCM_WTR | NPCM_WTRE | NPCM_WTE, wdt->reg); 152 udelay(1000); 153 154 return 0; 155 } 156 157 static bool npcm_is_running(struct watchdog_device *wdd) 158 { 159 struct npcm_wdt *wdt = to_npcm_wdt(wdd); 160 161 return readl(wdt->reg) & NPCM_WTE; 162 } 163 164 static const struct watchdog_info npcm_wdt_info = { 165 .identity = KBUILD_MODNAME, 166 .options = WDIOF_SETTIMEOUT 167 | WDIOF_KEEPALIVEPING 168 | WDIOF_MAGICCLOSE, 169 }; 170 171 static const struct watchdog_ops npcm_wdt_ops = { 172 .owner = THIS_MODULE, 173 .start = npcm_wdt_start, 174 .stop = npcm_wdt_stop, 175 .ping = npcm_wdt_ping, 176 .set_timeout = npcm_wdt_set_timeout, 177 .restart = npcm_wdt_restart, 178 }; 179 180 static int npcm_wdt_probe(struct platform_device *pdev) 181 { 182 struct device *dev = &pdev->dev; 183 struct npcm_wdt *wdt; 184 struct resource *res; 185 int irq; 186 int ret; 187 188 wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); 189 if (!wdt) 190 return -ENOMEM; 191 192 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 193 wdt->reg = devm_ioremap_resource(dev, res); 194 if (IS_ERR(wdt->reg)) 195 return PTR_ERR(wdt->reg); 196 197 irq = platform_get_irq(pdev, 0); 198 if (irq < 0) 199 return irq; 200 201 wdt->wdd.info = &npcm_wdt_info; 202 wdt->wdd.ops = &npcm_wdt_ops; 203 wdt->wdd.min_timeout = 1; 204 wdt->wdd.max_timeout = 2750; 205 wdt->wdd.parent = dev; 206 207 wdt->wdd.timeout = 86; 208 watchdog_init_timeout(&wdt->wdd, 0, dev); 209 210 /* Ensure timeout is able to be represented by the hardware */ 211 npcm_wdt_set_timeout(&wdt->wdd, wdt->wdd.timeout); 212 213 if (npcm_is_running(&wdt->wdd)) { 214 /* Restart with the default or device-tree specified timeout */ 215 npcm_wdt_start(&wdt->wdd); 216 set_bit(WDOG_HW_RUNNING, &wdt->wdd.status); 217 } 218 219 ret = devm_request_irq(dev, irq, npcm_wdt_interrupt, 0, 220 "watchdog", wdt); 221 if (ret) 222 return ret; 223 224 ret = devm_watchdog_register_device(dev, &wdt->wdd); 225 if (ret) { 226 dev_err(dev, "failed to register watchdog\n"); 227 return ret; 228 } 229 230 dev_info(dev, "NPCM watchdog driver enabled\n"); 231 232 return 0; 233 } 234 235 #ifdef CONFIG_OF 236 static const struct of_device_id npcm_wdt_match[] = { 237 {.compatible = "nuvoton,npcm750-wdt"}, 238 {}, 239 }; 240 MODULE_DEVICE_TABLE(of, npcm_wdt_match); 241 #endif 242 243 static struct platform_driver npcm_wdt_driver = { 244 .probe = npcm_wdt_probe, 245 .driver = { 246 .name = "npcm-wdt", 247 .of_match_table = of_match_ptr(npcm_wdt_match), 248 }, 249 }; 250 module_platform_driver(npcm_wdt_driver); 251 252 MODULE_AUTHOR("Joel Stanley"); 253 MODULE_DESCRIPTION("Watchdog driver for NPCM"); 254 MODULE_LICENSE("GPL v2"); 255