660 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/* cpwd.c - driver implementation for hardware watchdog3 * timers found on Sun Microsystems CP1400 and CP1500 boards.4 *5 * This device supports both the generic Linux watchdog6 * interface and Solaris-compatible ioctls as best it is7 * able.8 *9 * NOTE: CP1400 systems appear to have a defective intr_mask10 * register on the PLD, preventing the disabling of11 * timer interrupts. We use a timer to periodically12 * reset 'stopped' watchdogs on affected platforms.13 *14 * Copyright (c) 2000 Eric Brower (ebrower@usa.net)15 * Copyright (C) 2008 David S. Miller <davem@davemloft.net>16 */17 18#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt19 20#include <linux/kernel.h>21#include <linux/module.h>22#include <linux/fs.h>23#include <linux/errno.h>24#include <linux/major.h>25#include <linux/miscdevice.h>26#include <linux/interrupt.h>27#include <linux/ioport.h>28#include <linux/timer.h>29#include <linux/compat.h>30#include <linux/slab.h>31#include <linux/mutex.h>32#include <linux/io.h>33#include <linux/of.h>34#include <linux/platform_device.h>35#include <linux/uaccess.h>36 37#include <asm/irq.h>38#include <asm/watchdog.h>39 40#define DRIVER_NAME "cpwd"41 42#define WD_OBPNAME "watchdog"43#define WD_BADMODEL "SUNW,501-5336"44#define WD_BTIMEOUT (jiffies + (HZ * 1000))45#define WD_BLIMIT 0xFFFF46 47#define WD0_MINOR 21248#define WD1_MINOR 21349#define WD2_MINOR 21450 51/* Internal driver definitions. */52#define WD0_ID 053#define WD1_ID 154#define WD2_ID 255#define WD_NUMDEVS 356 57#define WD_INTR_OFF 058#define WD_INTR_ON 159 60#define WD_STAT_INIT 0x01 /* Watchdog timer is initialized */61#define WD_STAT_BSTOP 0x02 /* Watchdog timer is brokenstopped */62#define WD_STAT_SVCD 0x04 /* Watchdog interrupt occurred */63 64/* Register value definitions65 */66#define WD0_INTR_MASK 0x01 /* Watchdog device interrupt masks */67#define WD1_INTR_MASK 0x0268#define WD2_INTR_MASK 0x0469 70#define WD_S_RUNNING 0x01 /* Watchdog device status running */71#define WD_S_EXPIRED 0x02 /* Watchdog device status expired */72 73struct cpwd {74 void __iomem *regs;75 spinlock_t lock;76 77 unsigned int irq;78 79 unsigned long timeout;80 bool enabled;81 bool reboot;82 bool broken;83 bool initialized;84 85 struct {86 struct miscdevice misc;87 void __iomem *regs;88 u8 intr_mask;89 u8 runstatus;90 u16 timeout;91 } devs[WD_NUMDEVS];92};93 94static DEFINE_MUTEX(cpwd_mutex);95static struct cpwd *cpwd_device;96 97/* Sun uses Altera PLD EPF8820ATC144-498 * providing three hardware watchdogs:99 *100 * 1) RIC - sends an interrupt when triggered101 * 2) XIR - asserts XIR_B_RESET when triggered, resets CPU102 * 3) POR - asserts POR_B_RESET when triggered, resets CPU, backplane, board103 *104 *** Timer register block definition (struct wd_timer_regblk)105 *106 * dcntr and limit registers (halfword access):107 * -------------------108 * | 15 | ...| 1 | 0 |109 * -------------------110 * |- counter val -|111 * -------------------112 * dcntr - Current 16-bit downcounter value.113 * When downcounter reaches '0' watchdog expires.114 * Reading this register resets downcounter with115 * 'limit' value.116 * limit - 16-bit countdown value in 1/10th second increments.117 * Writing this register begins countdown with input value.118 * Reading from this register does not affect counter.119 * NOTES: After watchdog reset, dcntr and limit contain '1'120 *121 * status register (byte access):122 * ---------------------------123 * | 7 | ... | 2 | 1 | 0 |124 * --------------+------------125 * |- UNUSED -| EXP | RUN |126 * ---------------------------127 * status- Bit 0 - Watchdog is running128 * Bit 1 - Watchdog has expired129 *130 *** PLD register block definition (struct wd_pld_regblk)131 *132 * intr_mask register (byte access):133 * ---------------------------------134 * | 7 | ... | 3 | 2 | 1 | 0 |135 * +-------------+------------------136 * |- UNUSED -| WD3 | WD2 | WD1 |137 * ---------------------------------138 * WD3 - 1 == Interrupt disabled for watchdog 3139 * WD2 - 1 == Interrupt disabled for watchdog 2140 * WD1 - 1 == Interrupt disabled for watchdog 1141 *142 * pld_status register (byte access):143 * UNKNOWN, MAGICAL MYSTERY REGISTER144 *145 */146#define WD_TIMER_REGSZ 16147#define WD0_OFF 0148#define WD1_OFF (WD_TIMER_REGSZ * 1)149#define WD2_OFF (WD_TIMER_REGSZ * 2)150#define PLD_OFF (WD_TIMER_REGSZ * 3)151 152#define WD_DCNTR 0x00153#define WD_LIMIT 0x04154#define WD_STATUS 0x08155 156#define PLD_IMASK (PLD_OFF + 0x00)157#define PLD_STATUS (PLD_OFF + 0x04)158 159static struct timer_list cpwd_timer;160 161static int wd0_timeout;162static int wd1_timeout;163static int wd2_timeout;164 165module_param(wd0_timeout, int, 0);166MODULE_PARM_DESC(wd0_timeout, "Default watchdog0 timeout in 1/10secs");167module_param(wd1_timeout, int, 0);168MODULE_PARM_DESC(wd1_timeout, "Default watchdog1 timeout in 1/10secs");169module_param(wd2_timeout, int, 0);170MODULE_PARM_DESC(wd2_timeout, "Default watchdog2 timeout in 1/10secs");171 172MODULE_AUTHOR("Eric Brower <ebrower@usa.net>");173MODULE_DESCRIPTION("Hardware watchdog driver for Sun Microsystems CP1400/1500");174MODULE_LICENSE("GPL");175 176static void cpwd_writew(u16 val, void __iomem *addr)177{178 writew(cpu_to_le16(val), addr);179}180static u16 cpwd_readw(void __iomem *addr)181{182 u16 val = readw(addr);183 184 return le16_to_cpu(val);185}186 187static void cpwd_writeb(u8 val, void __iomem *addr)188{189 writeb(val, addr);190}191 192static u8 cpwd_readb(void __iomem *addr)193{194 return readb(addr);195}196 197/* Enable or disable watchdog interrupts198 * Because of the CP1400 defect this should only be199 * called during initialzation or by wd_[start|stop]timer()200 *201 * index - sub-device index, or -1 for 'all'202 * enable - non-zero to enable interrupts, zero to disable203 */204static void cpwd_toggleintr(struct cpwd *p, int index, int enable)205{206 unsigned char curregs = cpwd_readb(p->regs + PLD_IMASK);207 unsigned char setregs =208 (index == -1) ?209 (WD0_INTR_MASK | WD1_INTR_MASK | WD2_INTR_MASK) :210 (p->devs[index].intr_mask);211 212 if (enable == WD_INTR_ON)213 curregs &= ~setregs;214 else215 curregs |= setregs;216 217 cpwd_writeb(curregs, p->regs + PLD_IMASK);218}219 220/* Restarts timer with maximum limit value and221 * does not unset 'brokenstop' value.222 */223static void cpwd_resetbrokentimer(struct cpwd *p, int index)224{225 cpwd_toggleintr(p, index, WD_INTR_ON);226 cpwd_writew(WD_BLIMIT, p->devs[index].regs + WD_LIMIT);227}228 229/* Timer method called to reset stopped watchdogs--230 * because of the PLD bug on CP1400, we cannot mask231 * interrupts within the PLD so me must continually232 * reset the timers ad infinitum.233 */234static void cpwd_brokentimer(struct timer_list *unused)235{236 struct cpwd *p = cpwd_device;237 int id, tripped = 0;238 239 /* kill a running timer instance, in case we240 * were called directly instead of by kernel timer241 */242 if (timer_pending(&cpwd_timer))243 del_timer(&cpwd_timer);244 245 for (id = 0; id < WD_NUMDEVS; id++) {246 if (p->devs[id].runstatus & WD_STAT_BSTOP) {247 ++tripped;248 cpwd_resetbrokentimer(p, id);249 }250 }251 252 if (tripped) {253 /* there is at least one timer brokenstopped-- reschedule */254 cpwd_timer.expires = WD_BTIMEOUT;255 add_timer(&cpwd_timer);256 }257}258 259/* Reset countdown timer with 'limit' value and continue countdown.260 * This will not start a stopped timer.261 */262static void cpwd_pingtimer(struct cpwd *p, int index)263{264 if (cpwd_readb(p->devs[index].regs + WD_STATUS) & WD_S_RUNNING)265 cpwd_readw(p->devs[index].regs + WD_DCNTR);266}267 268/* Stop a running watchdog timer-- the timer actually keeps269 * running, but the interrupt is masked so that no action is270 * taken upon expiration.271 */272static void cpwd_stoptimer(struct cpwd *p, int index)273{274 if (cpwd_readb(p->devs[index].regs + WD_STATUS) & WD_S_RUNNING) {275 cpwd_toggleintr(p, index, WD_INTR_OFF);276 277 if (p->broken) {278 p->devs[index].runstatus |= WD_STAT_BSTOP;279 cpwd_brokentimer(NULL);280 }281 }282}283 284/* Start a watchdog timer with the specified limit value285 * If the watchdog is running, it will be restarted with286 * the provided limit value.287 *288 * This function will enable interrupts on the specified289 * watchdog.290 */291static void cpwd_starttimer(struct cpwd *p, int index)292{293 if (p->broken)294 p->devs[index].runstatus &= ~WD_STAT_BSTOP;295 296 p->devs[index].runstatus &= ~WD_STAT_SVCD;297 298 cpwd_writew(p->devs[index].timeout, p->devs[index].regs + WD_LIMIT);299 cpwd_toggleintr(p, index, WD_INTR_ON);300}301 302static int cpwd_getstatus(struct cpwd *p, int index)303{304 unsigned char stat = cpwd_readb(p->devs[index].regs + WD_STATUS);305 unsigned char intr = cpwd_readb(p->devs[index].regs + PLD_IMASK);306 unsigned char ret = WD_STOPPED;307 308 /* determine STOPPED */309 if (!stat)310 return ret;311 312 /* determine EXPIRED vs FREERUN vs RUNNING */313 else if (WD_S_EXPIRED & stat) {314 ret = WD_EXPIRED;315 } else if (WD_S_RUNNING & stat) {316 if (intr & p->devs[index].intr_mask) {317 ret = WD_FREERUN;318 } else {319 /* Fudge WD_EXPIRED status for defective CP1400--320 * IF timer is running321 * AND brokenstop is set322 * AND an interrupt has been serviced323 * we are WD_EXPIRED.324 *325 * IF timer is running326 * AND brokenstop is set327 * AND no interrupt has been serviced328 * we are WD_FREERUN.329 */330 if (p->broken &&331 (p->devs[index].runstatus & WD_STAT_BSTOP)) {332 if (p->devs[index].runstatus & WD_STAT_SVCD) {333 ret = WD_EXPIRED;334 } else {335 /* we could as well pretend336 * we are expired */337 ret = WD_FREERUN;338 }339 } else {340 ret = WD_RUNNING;341 }342 }343 }344 345 /* determine SERVICED */346 if (p->devs[index].runstatus & WD_STAT_SVCD)347 ret |= WD_SERVICED;348 349 return ret;350}351 352static irqreturn_t cpwd_interrupt(int irq, void *dev_id)353{354 struct cpwd *p = dev_id;355 356 /* Only WD0 will interrupt-- others are NMI and we won't357 * see them here....358 */359 spin_lock_irq(&p->lock);360 361 cpwd_stoptimer(p, WD0_ID);362 p->devs[WD0_ID].runstatus |= WD_STAT_SVCD;363 364 spin_unlock_irq(&p->lock);365 366 return IRQ_HANDLED;367}368 369static int cpwd_open(struct inode *inode, struct file *f)370{371 struct cpwd *p = cpwd_device;372 373 mutex_lock(&cpwd_mutex);374 switch (iminor(inode)) {375 case WD0_MINOR:376 case WD1_MINOR:377 case WD2_MINOR:378 break;379 380 default:381 mutex_unlock(&cpwd_mutex);382 return -ENODEV;383 }384 385 /* Register IRQ on first open of device */386 if (!p->initialized) {387 if (request_irq(p->irq, &cpwd_interrupt,388 IRQF_SHARED, DRIVER_NAME, p)) {389 pr_err("Cannot register IRQ %d\n", p->irq);390 mutex_unlock(&cpwd_mutex);391 return -EBUSY;392 }393 p->initialized = true;394 }395 396 mutex_unlock(&cpwd_mutex);397 398 return stream_open(inode, f);399}400 401static int cpwd_release(struct inode *inode, struct file *file)402{403 return 0;404}405 406static long cpwd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)407{408 static const struct watchdog_info info = {409 .options = WDIOF_SETTIMEOUT,410 .firmware_version = 1,411 .identity = DRIVER_NAME,412 };413 void __user *argp = (void __user *)arg;414 struct inode *inode = file_inode(file);415 int index = iminor(inode) - WD0_MINOR;416 struct cpwd *p = cpwd_device;417 int setopt = 0;418 419 switch (cmd) {420 /* Generic Linux IOCTLs */421 case WDIOC_GETSUPPORT:422 if (copy_to_user(argp, &info, sizeof(struct watchdog_info)))423 return -EFAULT;424 break;425 426 case WDIOC_GETSTATUS:427 case WDIOC_GETBOOTSTATUS:428 if (put_user(0, (int __user *)argp))429 return -EFAULT;430 break;431 432 case WDIOC_KEEPALIVE:433 cpwd_pingtimer(p, index);434 break;435 436 case WDIOC_SETOPTIONS:437 if (copy_from_user(&setopt, argp, sizeof(unsigned int)))438 return -EFAULT;439 440 if (setopt & WDIOS_DISABLECARD) {441 if (p->enabled)442 return -EINVAL;443 cpwd_stoptimer(p, index);444 } else if (setopt & WDIOS_ENABLECARD) {445 cpwd_starttimer(p, index);446 } else {447 return -EINVAL;448 }449 break;450 451 /* Solaris-compatible IOCTLs */452 case WIOCGSTAT:453 setopt = cpwd_getstatus(p, index);454 if (copy_to_user(argp, &setopt, sizeof(unsigned int)))455 return -EFAULT;456 break;457 458 case WIOCSTART:459 cpwd_starttimer(p, index);460 break;461 462 case WIOCSTOP:463 if (p->enabled)464 return -EINVAL;465 466 cpwd_stoptimer(p, index);467 break;468 469 default:470 return -EINVAL;471 }472 473 return 0;474}475 476static long cpwd_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)477{478 return cpwd_ioctl(file, cmd, (unsigned long)compat_ptr(arg));479}480 481static ssize_t cpwd_write(struct file *file, const char __user *buf,482 size_t count, loff_t *ppos)483{484 struct inode *inode = file_inode(file);485 struct cpwd *p = cpwd_device;486 int index = iminor(inode);487 488 if (count) {489 cpwd_pingtimer(p, index);490 return 1;491 }492 493 return 0;494}495 496static ssize_t cpwd_read(struct file *file, char __user *buffer,497 size_t count, loff_t *ppos)498{499 return -EINVAL;500}501 502static const struct file_operations cpwd_fops = {503 .owner = THIS_MODULE,504 .unlocked_ioctl = cpwd_ioctl,505 .compat_ioctl = cpwd_compat_ioctl,506 .open = cpwd_open,507 .write = cpwd_write,508 .read = cpwd_read,509 .release = cpwd_release,510};511 512static int cpwd_probe(struct platform_device *op)513{514 struct device_node *options;515 const char *str_prop;516 const void *prop_val;517 int i, err = -EINVAL;518 struct cpwd *p;519 520 if (cpwd_device)521 return -EINVAL;522 523 p = devm_kzalloc(&op->dev, sizeof(*p), GFP_KERNEL);524 if (!p)525 return -ENOMEM;526 527 p->irq = op->archdata.irqs[0];528 529 spin_lock_init(&p->lock);530 531 p->regs = of_ioremap(&op->resource[0], 0,532 4 * WD_TIMER_REGSZ, DRIVER_NAME);533 if (!p->regs) {534 pr_err("Unable to map registers\n");535 return -ENOMEM;536 }537 538 options = of_find_node_by_path("/options");539 if (!options) {540 err = -ENODEV;541 pr_err("Unable to find /options node\n");542 goto out_iounmap;543 }544 545 prop_val = of_get_property(options, "watchdog-enable?", NULL);546 p->enabled = (prop_val ? true : false);547 548 prop_val = of_get_property(options, "watchdog-reboot?", NULL);549 p->reboot = (prop_val ? true : false);550 551 str_prop = of_get_property(options, "watchdog-timeout", NULL);552 if (str_prop)553 p->timeout = simple_strtoul(str_prop, NULL, 10);554 555 of_node_put(options);556 557 /* CP1400s seem to have broken PLD implementations-- the558 * interrupt_mask register cannot be written, so no timer559 * interrupts can be masked within the PLD.560 */561 str_prop = of_get_property(op->dev.of_node, "model", NULL);562 p->broken = (str_prop && !strcmp(str_prop, WD_BADMODEL));563 564 if (!p->enabled)565 cpwd_toggleintr(p, -1, WD_INTR_OFF);566 567 for (i = 0; i < WD_NUMDEVS; i++) {568 static const char *cpwd_names[] = { "RIC", "XIR", "POR" };569 static int *parms[] = { &wd0_timeout,570 &wd1_timeout,571 &wd2_timeout };572 struct miscdevice *mp = &p->devs[i].misc;573 574 mp->minor = WD0_MINOR + i;575 mp->name = cpwd_names[i];576 mp->fops = &cpwd_fops;577 578 p->devs[i].regs = p->regs + (i * WD_TIMER_REGSZ);579 p->devs[i].intr_mask = (WD0_INTR_MASK << i);580 p->devs[i].runstatus &= ~WD_STAT_BSTOP;581 p->devs[i].runstatus |= WD_STAT_INIT;582 p->devs[i].timeout = p->timeout;583 if (*parms[i])584 p->devs[i].timeout = *parms[i];585 586 err = misc_register(&p->devs[i].misc);587 if (err) {588 pr_err("Could not register misc device for dev %d\n",589 i);590 goto out_unregister;591 }592 }593 594 if (p->broken) {595 timer_setup(&cpwd_timer, cpwd_brokentimer, 0);596 cpwd_timer.expires = WD_BTIMEOUT;597 598 pr_info("PLD defect workaround enabled for model %s\n",599 WD_BADMODEL);600 }601 602 platform_set_drvdata(op, p);603 cpwd_device = p;604 return 0;605 606out_unregister:607 for (i--; i >= 0; i--)608 misc_deregister(&p->devs[i].misc);609 610out_iounmap:611 of_iounmap(&op->resource[0], p->regs, 4 * WD_TIMER_REGSZ);612 613 return err;614}615 616static void cpwd_remove(struct platform_device *op)617{618 struct cpwd *p = platform_get_drvdata(op);619 int i;620 621 for (i = 0; i < WD_NUMDEVS; i++) {622 misc_deregister(&p->devs[i].misc);623 624 if (!p->enabled) {625 cpwd_stoptimer(p, i);626 if (p->devs[i].runstatus & WD_STAT_BSTOP)627 cpwd_resetbrokentimer(p, i);628 }629 }630 631 if (p->broken)632 del_timer_sync(&cpwd_timer);633 634 if (p->initialized)635 free_irq(p->irq, p);636 637 of_iounmap(&op->resource[0], p->regs, 4 * WD_TIMER_REGSZ);638 639 cpwd_device = NULL;640}641 642static const struct of_device_id cpwd_match[] = {643 {644 .name = "watchdog",645 },646 {},647};648MODULE_DEVICE_TABLE(of, cpwd_match);649 650static struct platform_driver cpwd_driver = {651 .driver = {652 .name = DRIVER_NAME,653 .of_match_table = cpwd_match,654 },655 .probe = cpwd_probe,656 .remove_new = cpwd_remove,657};658 659module_platform_driver(cpwd_driver);660