// SPDX-License-Identifier: GPL-2.0-only /* cpwd.c - driver implementation for hardware watchdog * timers found on Sun Microsystems CP1400 and CP1500 boards. * * This device supports both the generic Linux watchdog * interface and Solaris-compatible ioctls as best it is * able. * * NOTE: CP1400 systems appear to have a defective intr_mask * register on the PLD, preventing the disabling of * timer interrupts. We use a timer to periodically * reset 'stopped' watchdogs on affected platforms. * * Copyright (c) 2000 Eric Brower (ebrower@usa.net) * Copyright (C) 2008 David S. Miller <davem@davemloft.net>
*/
/* Enable or disable watchdog interrupts * Because of the CP1400 defect this should only be * called during initialzation or by wd_[start|stop]timer() * * index - sub-device index, or -1 for 'all' * enable - non-zero to enable interrupts, zero to disable
*/ staticvoid cpwd_toggleintr(struct cpwd *p, int index, int enable)
{ unsignedchar curregs = cpwd_readb(p->regs + PLD_IMASK); unsignedchar setregs =
(index == -1) ?
(WD0_INTR_MASK | WD1_INTR_MASK | WD2_INTR_MASK) :
(p->devs[index].intr_mask);
/* Restarts timer with maximum limit value and * does not unset 'brokenstop' value.
*/ staticvoid cpwd_resetbrokentimer(struct cpwd *p, int index)
{
cpwd_toggleintr(p, index, WD_INTR_ON);
cpwd_writew(WD_BLIMIT, p->devs[index].regs + WD_LIMIT);
}
/* Timer method called to reset stopped watchdogs-- * because of the PLD bug on CP1400, we cannot mask * interrupts within the PLD so me must continually * reset the timers ad infinitum.
*/ staticvoid cpwd_brokentimer(struct timer_list *unused)
{ struct cpwd *p = cpwd_device; int id, tripped = 0;
/* kill a running timer instance, in case we * were called directly instead of by kernel timer
*/ if (timer_pending(&cpwd_timer))
timer_delete(&cpwd_timer);
for (id = 0; id < WD_NUMDEVS; id++) { if (p->devs[id].runstatus & WD_STAT_BSTOP) {
++tripped;
cpwd_resetbrokentimer(p, id);
}
}
if (tripped) { /* there is at least one timer brokenstopped-- reschedule */
cpwd_timer.expires = WD_BTIMEOUT;
add_timer(&cpwd_timer);
}
}
/* Reset countdown timer with 'limit' value and continue countdown. * This will not start a stopped timer.
*/ staticvoid cpwd_pingtimer(struct cpwd *p, int index)
{ if (cpwd_readb(p->devs[index].regs + WD_STATUS) & WD_S_RUNNING)
cpwd_readw(p->devs[index].regs + WD_DCNTR);
}
/* Stop a running watchdog timer-- the timer actually keeps * running, but the interrupt is masked so that no action is * taken upon expiration.
*/ staticvoid cpwd_stoptimer(struct cpwd *p, int index)
{ if (cpwd_readb(p->devs[index].regs + WD_STATUS) & WD_S_RUNNING) {
cpwd_toggleintr(p, index, WD_INTR_OFF);
if (p->broken) {
p->devs[index].runstatus |= WD_STAT_BSTOP;
cpwd_brokentimer(NULL);
}
}
}
/* Start a watchdog timer with the specified limit value * If the watchdog is running, it will be restarted with * the provided limit value. * * This function will enable interrupts on the specified * watchdog.
*/ staticvoid cpwd_starttimer(struct cpwd *p, int index)
{ if (p->broken)
p->devs[index].runstatus &= ~WD_STAT_BSTOP;
staticint cpwd_getstatus(struct cpwd *p, int index)
{ unsignedchar stat = cpwd_readb(p->devs[index].regs + WD_STATUS); unsignedchar intr = cpwd_readb(p->devs[index].regs + PLD_IMASK); unsignedchar ret = WD_STOPPED;
/* determine STOPPED */ if (!stat) return ret;
/* determine EXPIRED vs FREERUN vs RUNNING */ elseif (WD_S_EXPIRED & stat) {
ret = WD_EXPIRED;
} elseif (WD_S_RUNNING & stat) { if (intr & p->devs[index].intr_mask) {
ret = WD_FREERUN;
} else { /* Fudge WD_EXPIRED status for defective CP1400-- * IF timer is running * AND brokenstop is set * AND an interrupt has been serviced * we are WD_EXPIRED. * * IF timer is running * AND brokenstop is set * AND no interrupt has been serviced * we are WD_FREERUN.
*/ if (p->broken &&
(p->devs[index].runstatus & WD_STAT_BSTOP)) { if (p->devs[index].runstatus & WD_STAT_SVCD) {
ret = WD_EXPIRED;
} else { /* we could as well pretend
* we are expired */
ret = WD_FREERUN;
}
} else {
ret = WD_RUNNING;
}
}
}
/* determine SERVICED */ if (p->devs[index].runstatus & WD_STAT_SVCD)
ret |= WD_SERVICED;
/* CP1400s seem to have broken PLD implementations-- the * interrupt_mask register cannot be written, so no timer * interrupts can be masked within the PLD.
*/
str_prop = of_get_property(op->dev.of_node, "model", NULL);
p->broken = (str_prop && !strcmp(str_prop, WD_BADMODEL));
if (!p->enabled)
cpwd_toggleintr(p, -1, WD_INTR_OFF);
for (i = 0; i < WD_NUMDEVS; i++) { staticconstchar *cpwd_names[] = { "RIC", "XIR", "POR" }; staticint *parms[] = { &wd0_timeout,
&wd1_timeout,
&wd2_timeout }; struct miscdevice *mp = &p->devs[i].misc;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.