// SPDX-License-Identifier: GPL-2.0-or-later /* * W83977F Watchdog Timer Driver for Winbond W83977F I/O Chip * * (c) Copyright 2005 Jose Goncalves <jose.goncalves@inov.pt> * * Based on w83877f_wdt.c by Scott Jennings, * and wdt977.c by Woody Suwalski * * -----------------------
*/
staticbool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
/* * Start the watchdog
*/
staticint wdt_start(void)
{ unsignedlong flags;
spin_lock_irqsave(&spinlock, flags);
/* Unlock the SuperIO chip */
outb_p(UNLOCK_DATA, IO_INDEX_PORT);
outb_p(UNLOCK_DATA, IO_INDEX_PORT);
/* * Select device Aux2 (device=8) to set watchdog regs F2, F3 and F4. * F2 has the timeout in watchdog counter units. * F3 is set to enable watchdog LED blink at timeout. * F4 is used to just clear the TIMEOUT'ed state (bit 0).
*/
outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
outb_p(0x08, IO_DATA_PORT);
outb_p(0xF2, IO_INDEX_PORT);
outb_p(timeoutW, IO_DATA_PORT);
outb_p(0xF3, IO_INDEX_PORT);
outb_p(0x08, IO_DATA_PORT);
outb_p(0xF4, IO_INDEX_PORT);
outb_p(0x00, IO_DATA_PORT);
/* Set device Aux2 active */
outb_p(0x30, IO_INDEX_PORT);
outb_p(0x01, IO_DATA_PORT);
/* * Select device Aux1 (dev=7) to set GP16 as the watchdog output * (in reg E6) and GP13 as the watchdog LED output (in reg E3). * Map GP16 at pin 119. * In test mode watch the bit 0 on F4 to indicate "triggered" or * check watchdog LED on SBC.
*/
outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
outb_p(0x07, IO_DATA_PORT); if (!testmode) { unsigned pin_map;
/* Set device Aux1 active */
outb_p(0x30, IO_INDEX_PORT);
outb_p(0x01, IO_DATA_PORT);
/* Lock the SuperIO chip */
outb_p(LOCK_DATA, IO_INDEX_PORT);
spin_unlock_irqrestore(&spinlock, flags);
pr_info("activated\n");
return 0;
}
/* * Stop the watchdog
*/
staticint wdt_stop(void)
{ unsignedlong flags;
spin_lock_irqsave(&spinlock, flags);
/* Unlock the SuperIO chip */
outb_p(UNLOCK_DATA, IO_INDEX_PORT);
outb_p(UNLOCK_DATA, IO_INDEX_PORT);
/* * Select device Aux2 (device=8) to set watchdog regs F2, F3 and F4. * F2 is reset to its default value (watchdog timer disabled). * F3 is reset to its default state. * F4 clears the TIMEOUT'ed state (bit 0) - back to default.
*/
outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
outb_p(0x08, IO_DATA_PORT);
outb_p(0xF2, IO_INDEX_PORT);
outb_p(0xFF, IO_DATA_PORT);
outb_p(0xF3, IO_INDEX_PORT);
outb_p(0x00, IO_DATA_PORT);
outb_p(0xF4, IO_INDEX_PORT);
outb_p(0x00, IO_DATA_PORT);
outb_p(0xF2, IO_INDEX_PORT);
outb_p(0x00, IO_DATA_PORT);
/* * Select device Aux1 (dev=7) to set GP16 (in reg E6) and * Gp13 (in reg E3) as inputs.
*/
outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
outb_p(0x07, IO_DATA_PORT); if (!testmode) {
outb_p(0xE6, IO_INDEX_PORT);
outb_p(0x01, IO_DATA_PORT);
}
outb_p(0xE3, IO_INDEX_PORT);
outb_p(0x01, IO_DATA_PORT);
/* Lock the SuperIO chip */
outb_p(LOCK_DATA, IO_INDEX_PORT);
spin_unlock_irqrestore(&spinlock, flags);
pr_info("shutdown\n");
return 0;
}
/* * Send a keepalive ping to the watchdog * This is done by simply re-writing the timeout to reg. 0xF2
*/
/* * Convert seconds to watchdog counter time units, rounding up. * On PCM-5335 watchdog units are 30 seconds/step with 15 sec startup * value. This information is supplied in the PCM-5335 manual and was * checked by me on a real board. This is a bit strange because W83977f * datasheet says counter unit is in minutes!
*/ if (t < 15) return -EINVAL;
tmrval = ((t + 15) + 29) / 30;
if (tmrval > 255) return -EINVAL;
/* * timeout is the timeout in seconds, * timeoutW is the timeout in watchdog counter units.
*/
timeoutW = tmrval;
timeout = (timeoutW * 30) - 15; return 0;
}
/* * Get the watchdog status
*/
staticint wdt_get_status(int *status)
{ int new_status; unsignedlong flags;
spin_lock_irqsave(&spinlock, flags);
/* Unlock the SuperIO chip */
outb_p(UNLOCK_DATA, IO_INDEX_PORT);
outb_p(UNLOCK_DATA, IO_INDEX_PORT);
/* Lock the SuperIO chip */
outb_p(LOCK_DATA, IO_INDEX_PORT);
spin_unlock_irqrestore(&spinlock, flags);
*status = 0; if (new_status & 1)
*status |= WDIOF_CARDRESET;
return 0;
}
/* * /dev/watchdog handling
*/
staticint wdt_open(struct inode *inode, struct file *file)
{ /* If the watchdog is alive we don't need to start it again */ if (test_and_set_bit(0, &timer_alive)) return -EBUSY;
if (nowayout)
__module_get(THIS_MODULE);
wdt_start(); return stream_open(inode, file);
}
staticint wdt_release(struct inode *inode, struct file *file)
{ /* * Shut off the timer. * Lock it in if it's a module and we set nowayout
*/ if (expect_close == 42) {
wdt_stop();
clear_bit(0, &timer_alive);
} else {
wdt_keepalive();
pr_crit("unexpected close, not stopping watchdog!\n");
}
expect_close = 0; return 0;
}
/* * wdt_write: * @file: file handle to the watchdog * @buf: buffer to write (unused as data does not matter here * @count: count of bytes * @ppos: pointer to the position to write. No seeks allowed * * A write to a watchdog device is defined as a keepalive signal. Any * write of data will do, as we don't define content meaning.
*/
static ssize_t wdt_write(struct file *file, constchar __user *buf,
size_t count, loff_t *ppos)
{ /* See if we got the magic character 'V' and reload the timer */ if (count) { if (!nowayout) {
size_t ofs;
/* note: just in case someone wrote the
magic character long ago */
expect_close = 0;
/* scan to see whether or not we got the
magic character */ for (ofs = 0; ofs != count; ofs++) { char c; if (get_user(c, buf + ofs)) return -EFAULT; if (c == 'V')
expect_close = 42;
}
}
/* someone wrote to us, we should restart timer */
wdt_keepalive();
} return count;
}
/* * wdt_ioctl: * @inode: inode of the device * @file: file handle to the device * @cmd: watchdog command * @arg: argument pointer * * The watchdog API defines a common set of functions for all watchdogs * according to their available features.
*/
/* * Check that the timeout value is within it's range; * if not reset to the default
*/ if (wdt_set_timeout(timeout)) {
wdt_set_timeout(DEFAULT_TIMEOUT);
pr_info("timeout value must be 15 <= timeout <= 7635, using %d\n",
DEFAULT_TIMEOUT);
}
if (!request_region(IO_INDEX_PORT, 2, WATCHDOG_NAME)) {
pr_err("I/O address 0x%04x already in use\n", IO_INDEX_PORT);
rc = -EIO; goto err_out;
}
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.