1 /* 2 * mpc8xxx_wdt.c - MPC8xx/MPC83xx/MPC86xx watchdog userspace interface 3 * 4 * Authors: Dave Updegraff <dave@cray.org> 5 * Kumar Gala <galak@kernel.crashing.org> 6 * Attribution: from 83xx_wst: Florian Schirmer <jolt@tuxbox.org> 7 * ..and from sc520_wdt 8 * Copyright (c) 2008 MontaVista Software, Inc. 9 * Anton Vorontsov <avorontsov@ru.mvista.com> 10 * 11 * Note: it appears that you can only actually ENABLE or DISABLE the thing 12 * once after POR. Once enabled, you cannot disable, and vice versa. 13 * 14 * This program is free software; you can redistribute it and/or modify it 15 * under the terms of the GNU General Public License as published by the 16 * Free Software Foundation; either version 2 of the License, or (at your 17 * option) any later version. 18 */ 19 20 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 21 22 #include <linux/fs.h> 23 #include <linux/init.h> 24 #include <linux/kernel.h> 25 #include <linux/timer.h> 26 #include <linux/of_address.h> 27 #include <linux/of_platform.h> 28 #include <linux/module.h> 29 #include <linux/watchdog.h> 30 #include <linux/io.h> 31 #include <linux/uaccess.h> 32 #include <sysdev/fsl_soc.h> 33 34 struct mpc8xxx_wdt { 35 __be32 res0; 36 __be32 swcrr; /* System watchdog control register */ 37 #define SWCRR_SWTC 0xFFFF0000 /* Software Watchdog Time Count. */ 38 #define SWCRR_SWEN 0x00000004 /* Watchdog Enable bit. */ 39 #define SWCRR_SWRI 0x00000002 /* Software Watchdog Reset/Interrupt Select bit.*/ 40 #define SWCRR_SWPR 0x00000001 /* Software Watchdog Counter Prescale bit. */ 41 __be32 swcnr; /* System watchdog count register */ 42 u8 res1[2]; 43 __be16 swsrr; /* System watchdog service register */ 44 u8 res2[0xF0]; 45 }; 46 47 struct mpc8xxx_wdt_type { 48 int prescaler; 49 bool hw_enabled; 50 }; 51 52 struct mpc8xxx_wdt_ddata { 53 struct mpc8xxx_wdt __iomem *base; 54 struct watchdog_device wdd; 55 struct timer_list timer; 56 spinlock_t lock; 57 }; 58 59 static u16 timeout = 0xffff; 60 module_param(timeout, ushort, 0); 61 MODULE_PARM_DESC(timeout, 62 "Watchdog timeout in ticks. (0<timeout<65536, default=65535)"); 63 64 static bool reset = 1; 65 module_param(reset, bool, 0); 66 MODULE_PARM_DESC(reset, 67 "Watchdog Interrupt/Reset Mode. 0 = interrupt, 1 = reset"); 68 69 static bool nowayout = WATCHDOG_NOWAYOUT; 70 module_param(nowayout, bool, 0); 71 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " 72 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 73 74 static void mpc8xxx_wdt_keepalive(struct mpc8xxx_wdt_ddata *ddata) 75 { 76 /* Ping the WDT */ 77 spin_lock(&ddata->lock); 78 out_be16(&ddata->base->swsrr, 0x556c); 79 out_be16(&ddata->base->swsrr, 0xaa39); 80 spin_unlock(&ddata->lock); 81 } 82 83 static void mpc8xxx_wdt_timer_ping(unsigned long arg) 84 { 85 struct mpc8xxx_wdt_ddata *ddata = (void *)arg; 86 87 mpc8xxx_wdt_keepalive(ddata); 88 /* We're pinging it twice faster than needed, just to be sure. */ 89 mod_timer(&ddata->timer, jiffies + HZ * ddata->wdd.timeout / 2); 90 } 91 92 static int mpc8xxx_wdt_start(struct watchdog_device *w) 93 { 94 struct mpc8xxx_wdt_ddata *ddata = 95 container_of(w, struct mpc8xxx_wdt_ddata, wdd); 96 97 u32 tmp = SWCRR_SWEN | SWCRR_SWPR; 98 99 /* Good, fire up the show */ 100 if (reset) 101 tmp |= SWCRR_SWRI; 102 103 tmp |= timeout << 16; 104 105 out_be32(&ddata->base->swcrr, tmp); 106 107 del_timer_sync(&ddata->timer); 108 109 return 0; 110 } 111 112 static int mpc8xxx_wdt_ping(struct watchdog_device *w) 113 { 114 struct mpc8xxx_wdt_ddata *ddata = 115 container_of(w, struct mpc8xxx_wdt_ddata, wdd); 116 117 mpc8xxx_wdt_keepalive(ddata); 118 return 0; 119 } 120 121 static int mpc8xxx_wdt_stop(struct watchdog_device *w) 122 { 123 struct mpc8xxx_wdt_ddata *ddata = 124 container_of(w, struct mpc8xxx_wdt_ddata, wdd); 125 126 mod_timer(&ddata->timer, jiffies); 127 return 0; 128 } 129 130 static struct watchdog_info mpc8xxx_wdt_info = { 131 .options = WDIOF_KEEPALIVEPING, 132 .firmware_version = 1, 133 .identity = "MPC8xxx", 134 }; 135 136 static struct watchdog_ops mpc8xxx_wdt_ops = { 137 .owner = THIS_MODULE, 138 .start = mpc8xxx_wdt_start, 139 .ping = mpc8xxx_wdt_ping, 140 .stop = mpc8xxx_wdt_stop, 141 }; 142 143 static int mpc8xxx_wdt_probe(struct platform_device *ofdev) 144 { 145 int ret; 146 struct resource *res; 147 const struct mpc8xxx_wdt_type *wdt_type; 148 struct mpc8xxx_wdt_ddata *ddata; 149 u32 freq = fsl_get_sys_freq(); 150 bool enabled; 151 unsigned int timeout_sec; 152 153 wdt_type = of_device_get_match_data(&ofdev->dev); 154 if (!wdt_type) 155 return -EINVAL; 156 157 if (!freq || freq == -1) 158 return -EINVAL; 159 160 ddata = devm_kzalloc(&ofdev->dev, sizeof(*ddata), GFP_KERNEL); 161 if (!ddata) 162 return -ENOMEM; 163 164 res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); 165 ddata->base = devm_ioremap_resource(&ofdev->dev, res); 166 if (IS_ERR(ddata->base)) 167 return PTR_ERR(ddata->base); 168 169 enabled = in_be32(&ddata->base->swcrr) & SWCRR_SWEN; 170 if (!enabled && wdt_type->hw_enabled) { 171 pr_info("could not be enabled in software\n"); 172 return -ENODEV; 173 } 174 175 spin_lock_init(&ddata->lock); 176 setup_timer(&ddata->timer, mpc8xxx_wdt_timer_ping, 177 (unsigned long)ddata); 178 179 ddata->wdd.info = &mpc8xxx_wdt_info, 180 ddata->wdd.ops = &mpc8xxx_wdt_ops, 181 182 /* Calculate the timeout in seconds */ 183 timeout_sec = (timeout * wdt_type->prescaler) / freq; 184 185 ddata->wdd.timeout = timeout_sec; 186 187 watchdog_set_nowayout(&ddata->wdd, nowayout); 188 189 ret = watchdog_register_device(&ddata->wdd); 190 if (ret) { 191 pr_err("cannot register watchdog device (err=%d)\n", ret); 192 return ret; 193 } 194 195 pr_info("WDT driver for MPC8xxx initialized. mode:%s timeout=%d (%d seconds)\n", 196 reset ? "reset" : "interrupt", timeout, timeout_sec); 197 198 /* 199 * If the watchdog was previously enabled or we're running on 200 * MPC8xxx, we should ping the wdt from the kernel until the 201 * userspace handles it. 202 */ 203 if (enabled) 204 mod_timer(&ddata->timer, jiffies); 205 206 platform_set_drvdata(ofdev, ddata); 207 return 0; 208 } 209 210 static int mpc8xxx_wdt_remove(struct platform_device *ofdev) 211 { 212 struct mpc8xxx_wdt_ddata *ddata = platform_get_drvdata(ofdev); 213 214 pr_crit("Watchdog removed, expect the %s soon!\n", 215 reset ? "reset" : "machine check exception"); 216 del_timer_sync(&ddata->timer); 217 watchdog_unregister_device(&ddata->wdd); 218 219 return 0; 220 } 221 222 static const struct of_device_id mpc8xxx_wdt_match[] = { 223 { 224 .compatible = "mpc83xx_wdt", 225 .data = &(struct mpc8xxx_wdt_type) { 226 .prescaler = 0x10000, 227 }, 228 }, 229 { 230 .compatible = "fsl,mpc8610-wdt", 231 .data = &(struct mpc8xxx_wdt_type) { 232 .prescaler = 0x10000, 233 .hw_enabled = true, 234 }, 235 }, 236 { 237 .compatible = "fsl,mpc823-wdt", 238 .data = &(struct mpc8xxx_wdt_type) { 239 .prescaler = 0x800, 240 .hw_enabled = true, 241 }, 242 }, 243 {}, 244 }; 245 MODULE_DEVICE_TABLE(of, mpc8xxx_wdt_match); 246 247 static struct platform_driver mpc8xxx_wdt_driver = { 248 .probe = mpc8xxx_wdt_probe, 249 .remove = mpc8xxx_wdt_remove, 250 .driver = { 251 .name = "mpc8xxx_wdt", 252 .of_match_table = mpc8xxx_wdt_match, 253 }, 254 }; 255 256 static int __init mpc8xxx_wdt_init(void) 257 { 258 return platform_driver_register(&mpc8xxx_wdt_driver); 259 } 260 arch_initcall(mpc8xxx_wdt_init); 261 262 static void __exit mpc8xxx_wdt_exit(void) 263 { 264 platform_driver_unregister(&mpc8xxx_wdt_driver); 265 } 266 module_exit(mpc8xxx_wdt_exit); 267 268 MODULE_AUTHOR("Dave Updegraff, Kumar Gala"); 269 MODULE_DESCRIPTION("Driver for watchdog timer in MPC8xx/MPC83xx/MPC86xx " 270 "uProcessors"); 271 MODULE_LICENSE("GPL"); 272