1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * drivers/char/watchdog/pnx4008_wdt.c 4 * 5 * Watchdog driver for PNX4008 board 6 * 7 * Authors: Dmitry Chigirev <source@mvista.com>, 8 * Vitaly Wool <vitalywool@gmail.com> 9 * Based on sa1100 driver, 10 * Copyright (C) 2000 Oleg Drokin <green@crimea.edu> 11 * 12 * 2005-2006 (c) MontaVista Software, Inc. 13 * 14 * (C) 2012 Wolfram Sang, Pengutronix 15 */ 16 17 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 18 19 #include <linux/module.h> 20 #include <linux/moduleparam.h> 21 #include <linux/types.h> 22 #include <linux/kernel.h> 23 #include <linux/watchdog.h> 24 #include <linux/platform_device.h> 25 #include <linux/clk.h> 26 #include <linux/spinlock.h> 27 #include <linux/io.h> 28 #include <linux/slab.h> 29 #include <linux/err.h> 30 #include <linux/of.h> 31 #include <linux/delay.h> 32 #include <linux/reboot.h> 33 34 /* WatchDog Timer - Chapter 23 Page 207 */ 35 36 #define DEFAULT_HEARTBEAT 19 37 #define MAX_HEARTBEAT 60 38 39 /* Watchdog timer register set definition */ 40 #define WDTIM_INT(p) ((p) + 0x0) 41 #define WDTIM_CTRL(p) ((p) + 0x4) 42 #define WDTIM_COUNTER(p) ((p) + 0x8) 43 #define WDTIM_MCTRL(p) ((p) + 0xC) 44 #define WDTIM_MATCH0(p) ((p) + 0x10) 45 #define WDTIM_EMR(p) ((p) + 0x14) 46 #define WDTIM_PULSE(p) ((p) + 0x18) 47 #define WDTIM_RES(p) ((p) + 0x1C) 48 49 /* WDTIM_INT bit definitions */ 50 #define MATCH_INT 1 51 52 /* WDTIM_CTRL bit definitions */ 53 #define COUNT_ENAB 1 54 #define RESET_COUNT (1 << 1) 55 #define DEBUG_EN (1 << 2) 56 57 /* WDTIM_MCTRL bit definitions */ 58 #define MR0_INT 1 59 #undef RESET_COUNT0 60 #define RESET_COUNT0 (1 << 2) 61 #define STOP_COUNT0 (1 << 2) 62 #define M_RES1 (1 << 3) 63 #define M_RES2 (1 << 4) 64 #define RESFRC1 (1 << 5) 65 #define RESFRC2 (1 << 6) 66 67 /* WDTIM_EMR bit definitions */ 68 #define EXT_MATCH0 1 69 #define MATCH_OUTPUT_HIGH (2 << 4) /*a MATCH_CTRL setting */ 70 71 /* WDTIM_RES bit definitions */ 72 #define WDOG_RESET 1 /* read only */ 73 74 #define WDOG_COUNTER_RATE 13000000 /*the counter clock is 13 MHz fixed */ 75 76 static bool nowayout = WATCHDOG_NOWAYOUT; 77 static unsigned int heartbeat; 78 79 static DEFINE_SPINLOCK(io_lock); 80 static void __iomem *wdt_base; 81 static struct clk *wdt_clk; 82 83 static int pnx4008_wdt_start(struct watchdog_device *wdd) 84 { 85 spin_lock(&io_lock); 86 87 /* stop counter, initiate counter reset */ 88 writel(RESET_COUNT, WDTIM_CTRL(wdt_base)); 89 /*wait for reset to complete. 100% guarantee event */ 90 while (readl(WDTIM_COUNTER(wdt_base))) 91 cpu_relax(); 92 /* internal and external reset, stop after that */ 93 writel(M_RES2 | STOP_COUNT0 | RESET_COUNT0, WDTIM_MCTRL(wdt_base)); 94 /* configure match output */ 95 writel(MATCH_OUTPUT_HIGH, WDTIM_EMR(wdt_base)); 96 /* clear interrupt, just in case */ 97 writel(MATCH_INT, WDTIM_INT(wdt_base)); 98 /* the longest pulse period 65541/(13*10^6) seconds ~ 5 ms. */ 99 writel(0xFFFF, WDTIM_PULSE(wdt_base)); 100 writel(wdd->timeout * WDOG_COUNTER_RATE, WDTIM_MATCH0(wdt_base)); 101 /*enable counter, stop when debugger active */ 102 writel(COUNT_ENAB | DEBUG_EN, WDTIM_CTRL(wdt_base)); 103 104 spin_unlock(&io_lock); 105 return 0; 106 } 107 108 static int pnx4008_wdt_stop(struct watchdog_device *wdd) 109 { 110 spin_lock(&io_lock); 111 112 writel(0, WDTIM_CTRL(wdt_base)); /*stop counter */ 113 114 spin_unlock(&io_lock); 115 return 0; 116 } 117 118 static int pnx4008_wdt_set_timeout(struct watchdog_device *wdd, 119 unsigned int new_timeout) 120 { 121 wdd->timeout = new_timeout; 122 return 0; 123 } 124 125 static int pnx4008_restart_handler(struct watchdog_device *wdd, 126 unsigned long mode, void *cmd) 127 { 128 const char *boot_cmd = cmd; 129 130 /* 131 * Verify if a "cmd" passed from the userspace program rebooting 132 * the system; if available, handle it. 133 * - For details, see the 'reboot' syscall in kernel/reboot.c 134 * - If the received "cmd" is not supported, use the default mode. 135 */ 136 if (boot_cmd) { 137 if (boot_cmd[0] == 'h') 138 mode = REBOOT_HARD; 139 else if (boot_cmd[0] == 's') 140 mode = REBOOT_SOFT; 141 } 142 143 if (mode == REBOOT_SOFT) { 144 /* Force match output active */ 145 writel(EXT_MATCH0, WDTIM_EMR(wdt_base)); 146 /* Internal reset on match output (RESOUT_N not asserted) */ 147 writel(M_RES1, WDTIM_MCTRL(wdt_base)); 148 } else { 149 /* Instant assert of RESETOUT_N with pulse length 1mS */ 150 writel(13000, WDTIM_PULSE(wdt_base)); 151 writel(M_RES2 | RESFRC1 | RESFRC2, WDTIM_MCTRL(wdt_base)); 152 } 153 154 /* Wait for watchdog to reset system */ 155 mdelay(1000); 156 157 return NOTIFY_DONE; 158 } 159 160 static const struct watchdog_info pnx4008_wdt_ident = { 161 .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | 162 WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, 163 .identity = "PNX4008 Watchdog", 164 }; 165 166 static const struct watchdog_ops pnx4008_wdt_ops = { 167 .owner = THIS_MODULE, 168 .start = pnx4008_wdt_start, 169 .stop = pnx4008_wdt_stop, 170 .set_timeout = pnx4008_wdt_set_timeout, 171 .restart = pnx4008_restart_handler, 172 }; 173 174 static struct watchdog_device pnx4008_wdd = { 175 .info = &pnx4008_wdt_ident, 176 .ops = &pnx4008_wdt_ops, 177 .timeout = DEFAULT_HEARTBEAT, 178 .min_timeout = 1, 179 .max_timeout = MAX_HEARTBEAT, 180 }; 181 182 static int pnx4008_wdt_probe(struct platform_device *pdev) 183 { 184 struct device *dev = &pdev->dev; 185 int ret = 0; 186 187 watchdog_init_timeout(&pnx4008_wdd, heartbeat, dev); 188 189 wdt_base = devm_platform_ioremap_resource(pdev, 0); 190 if (IS_ERR(wdt_base)) 191 return PTR_ERR(wdt_base); 192 193 wdt_clk = devm_clk_get_enabled(dev, NULL); 194 if (IS_ERR(wdt_clk)) 195 return PTR_ERR(wdt_clk); 196 197 pnx4008_wdd.bootstatus = (readl(WDTIM_RES(wdt_base)) & WDOG_RESET) ? 198 WDIOF_CARDRESET : 0; 199 pnx4008_wdd.parent = dev; 200 watchdog_set_nowayout(&pnx4008_wdd, nowayout); 201 watchdog_set_restart_priority(&pnx4008_wdd, 128); 202 203 if (readl(WDTIM_CTRL(wdt_base)) & COUNT_ENAB) 204 set_bit(WDOG_HW_RUNNING, &pnx4008_wdd.status); 205 206 ret = devm_watchdog_register_device(dev, &pnx4008_wdd); 207 if (ret < 0) 208 return ret; 209 210 dev_info(dev, "heartbeat %d sec\n", pnx4008_wdd.timeout); 211 212 return 0; 213 } 214 215 #ifdef CONFIG_OF 216 static const struct of_device_id pnx4008_wdt_match[] = { 217 { .compatible = "nxp,pnx4008-wdt" }, 218 { } 219 }; 220 MODULE_DEVICE_TABLE(of, pnx4008_wdt_match); 221 #endif 222 223 static struct platform_driver platform_wdt_driver = { 224 .driver = { 225 .name = "pnx4008-watchdog", 226 .of_match_table = of_match_ptr(pnx4008_wdt_match), 227 }, 228 .probe = pnx4008_wdt_probe, 229 }; 230 231 module_platform_driver(platform_wdt_driver); 232 233 MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>"); 234 MODULE_AUTHOR("Wolfram Sang <kernel@pengutronix.de>"); 235 MODULE_DESCRIPTION("PNX4008 Watchdog Driver"); 236 237 module_param(heartbeat, uint, 0); 238 MODULE_PARM_DESC(heartbeat, 239 "Watchdog heartbeat period in seconds from 1 to " 240 __MODULE_STRING(MAX_HEARTBEAT) ", default " 241 __MODULE_STRING(DEFAULT_HEARTBEAT)); 242 243 module_param(nowayout, bool, 0); 244 MODULE_PARM_DESC(nowayout, 245 "Set to 1 to keep watchdog running after device release"); 246 247 MODULE_LICENSE("GPL"); 248 MODULE_ALIAS("platform:pnx4008-watchdog"); 249