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