From 343b2e7b6ed19e4b6ca2bf76c0ca6b8544dd4320 Mon Sep 17 00:00:00 2001 From: Xin LI Date: Mon, 27 Jun 2011 21:54:13 -0700 Subject: [PATCH] Driver for Winbond watchdog. --- share/man/man4/Makefile | 3 + share/man/man4/winbondwd.4 | 88 ++++++++++ sys/amd64/conf/NOTES | 2 + sys/conf/files.amd64 | 1 + sys/conf/files.i386 | 1 + sys/dev/winbondwd/winbondwd.c | 368 ++++++++++++++++++++++++++++++++++++++++ sys/dev/winbondwd/winbondwd.h | 47 +++++ sys/i386/conf/NOTES | 2 + sys/modules/Makefile | 3 + sys/modules/winbondwd/Makefile | 8 + 10 files changed, 523 insertions(+), 0 deletions(-) create mode 100644 share/man/man4/winbondwd.4 create mode 100644 sys/dev/winbondwd/winbondwd.c create mode 100644 sys/dev/winbondwd/winbondwd.h create mode 100644 sys/modules/winbondwd/Makefile diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index 7ccccfb..777e2fd 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -447,6 +447,7 @@ MAN= aac.4 \ tun.4 \ twa.4 \ twe.4 \ + tws.4 \ tx.4 \ txp.4 \ u3g.4 \ @@ -503,6 +504,7 @@ MAN= aac.4 \ watchdog.4 \ wb.4 \ wi.4 \ + ${_winbondwd.4} \ witness.4 \ wlan.4 \ wlan_acl.4 \ @@ -706,6 +708,7 @@ _speaker.4= speaker.4 _spkr.4= spkr.4 _tpm.4= tpm.4 _urtw.4= urtw.4 +_winbondwd.4= winbondwd.4 _wpi.4= wpi.4 _xen.4= xen.4 diff --git a/share/man/man4/winbondwd.4 b/share/man/man4/winbondwd.4 new file mode 100644 index 0000000..6fd2719 --- /dev/null +++ b/share/man/man4/winbondwd.4 @@ -0,0 +1,88 @@ +.\"- +.\" Copyright (c) 2011 Xin LI +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd July 1, 2011 +.Dt WINBONDWD 4 +.Os +.Sh NAME +.Nm winbondwd +.Nd device driver for the Winbond Super I/O watchdog timer +.Sh SYNOPSIS +To compile this driver into the kernel, +place the following line in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device winbondwd" +.Ed +.Pp +Alternatively, to load the driver as a +module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +winbondwd_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +driver provides +.Xr watchdog 4 +support for the watchdog interrupt timer present on +all Winbond super I/O controllers. +.Pp +The Winbond super I/O controller have a built-in watchdog timer, +which can be enabled and disabled by user's program and set between +1 to 255 seconds or 1 to 255 minutes. +Supported watchdog intervals range from 1 to 255 seconds. +.Pp +On some systems the watchdog timer is enabled and set to 5 minutes +by BIOS on boot. +The +.Nm +driver will detect and print out the existing setting, however, +it will not make any changes unless told by the userland through +the +.Xr watchdog 4 +interface, +for instance by using the +.Xr watchdogd 8 +daemon. +.Sh SEE ALSO +.Xr watchdog 4 , +.Xr watchdog 8 , +.Xr watchdogd 8 , +.Xr watchdog 9 +.Sh HISTORY +The +.Nm +driver first appeared in +.Fx 9.0 . +.Sh AUTHORS +.An -nosplit +The +.Nm +driver and this manual page were written by +.An Xin LI Aq delphij@FreeBSD.org . diff --git a/sys/amd64/conf/NOTES b/sys/amd64/conf/NOTES index 4a47ace..3b25aea 100644 --- a/sys/amd64/conf/NOTES +++ b/sys/amd64/conf/NOTES @@ -453,9 +453,11 @@ device tpm # # ichwd: Intel ICH watchdog timer # amdsbwd: AMD SB7xx watchdog timer +# winbondwd: Winbond watchdog timer # device ichwd device amdsbwd +device winbondwd # # Temperature sensors: diff --git a/sys/conf/files.amd64 b/sys/conf/files.amd64 index 1388d01..18dbea6 100644 --- a/sys/conf/files.amd64 +++ b/sys/conf/files.amd64 @@ -223,6 +223,7 @@ dev/tpm/tpm.c optional tpm dev/tpm/tpm_acpi.c optional tpm acpi dev/tpm/tpm_isa.c optional tpm isa dev/uart/uart_cpu_amd64.c optional uart +dev/winbondwd/winbondwd.c optional winbondwd dev/wpi/if_wpi.c optional wpi isa/syscons_isa.c optional sc isa/vga_isa.c optional vga diff --git a/sys/conf/files.i386 b/sys/conf/files.i386 index 41a1772..112115d 100644 --- a/sys/conf/files.i386 +++ b/sys/conf/files.i386 @@ -238,6 +238,7 @@ dev/tpm/tpm_isa.c optional tpm isa dev/uart/uart_cpu_i386.c optional uart dev/acpica/acpi_if.m standard dev/acpi_support/acpi_wmi_if.m standard +dev/winbondwd/winbondwd.c optional winbondwd dev/wpi/if_wpi.c optional wpi i386/acpica/acpi_machdep.c optional acpi acpi_wakecode.o optional acpi \ diff --git a/sys/dev/winbondwd/winbondwd.c b/sys/dev/winbondwd/winbondwd.c new file mode 100644 index 0000000..fa53735 --- /dev/null +++ b/sys/dev/winbondwd/winbondwd.c @@ -0,0 +1,368 @@ +/*- + * Copyright (c) 2010 iXsystems, Inc. + * All rights reserved. + * Written by: Xin LI + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Winbond Watchdog Timer (WDT) driver + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +static devclass_t winbondwd_devclass; + +#define winbondwd_read_1(sc, off) \ + bus_space_read_1((sc)->wb_bst, (sc)->wb_bsh, (off)) + +#define winbondwd_write_1(sc, off, val) \ + bus_space_write_1((sc)->wb_bst, (sc)->wb_bsh, (off), (val)) + +/* + * Enter Winbond Extended Functions State + */ +static __inline void +winbondwd_config_enter(struct winbondwd_softc *sc) +{ + + winbondwd_write_1(sc, 0, 0x87); + winbondwd_write_1(sc, 0, 0x87); +} + +/* + * Leave Winbond Extended Functions State + */ +static __inline void +winbondwd_config_leave(struct winbondwd_softc *sc) +{ + + winbondwd_write_1(sc, 0, 0xaa); +} + +static __inline unsigned char +winbondwd_read_reg(struct winbondwd_softc *sc, unsigned char reg) +{ + + winbondwd_write_1(sc, 0, reg); + return (winbondwd_read_1(sc, 1)); +} + +/* + * Write specified extended function register + */ +static __inline void +winbondwd_write_reg(struct winbondwd_softc *sc, unsigned char reg, unsigned char val) +{ + + winbondwd_write_1(sc, 0, reg); + winbondwd_write_1(sc, 1, val); +} + +/* + * Select the watchdog device (GPIO Port 2, Logical device 8) + */ +static void +winbondwd_select(struct winbondwd_softc *sc) +{ + + winbondwd_config_enter(sc); + winbondwd_write_reg(sc, /* LDN bit 7:1 */ 0x7, /* GPIO Port 2 */ 0x8); + winbondwd_write_reg(sc, /* CR30 */ 0x30, /* Activate */ 0x1); +} + +/* + * Deselect the watchdog device (only a config_leave is needed) + */ +static void +winbondwd_deselect(struct winbondwd_softc *sc) +{ + + winbondwd_config_leave(sc); +} + +/* + * Show timeout + */ +static void +winbondwd_show_timeout(struct winbondwd_softc *sc) +{ + const char *mode_str; + unsigned char timeout, mode; + + winbondwd_select(sc); + timeout = winbondwd_read_reg(sc, 0xf6 /* Timeout CR */); + if (timeout == 0) { + sc->active = 0; + if (bootverbose) + device_printf(sc->device, + "Winbond watchdog not running\n"); + } else { + sc->active = 1; + mode = winbondwd_read_reg(sc, 0xf5 /* Bit 3: count mode */); + mode_str = (mode & (1 << 2))? "minute" : "second"; + device_printf(sc->device, + "Winbond watchdog will timeout after %hhu %s%s\n", + timeout, mode_str, ((timeout > 1)? "s" : "")); + } + winbondwd_deselect(sc); +} + +/* + * Set timeout in seconds; 0 = disable + */ +static void +winbondwd_set_timeout(struct winbondwd_softc *sc, unsigned char timeout) +{ + unsigned char mode; + + /* Don't bother to disable if the watchdog is not running */ + if (sc->active == 0 && timeout == 0) + return; + + winbondwd_select(sc); + mode = winbondwd_read_reg(sc, 0xf5 /* Bit 3: count mode */); + mode &= ~(1 << 2); /* Choose seconds mode */ + winbondwd_write_reg(sc, 0xf5, mode); + + winbondwd_write_reg(sc, 0xf6 /* Timeout CR */, timeout); + winbondwd_deselect(sc); + + if (bootverbose) { + if (timeout == 0) + device_printf(sc->device, + "Winbond watchdog disabled.\n"); + else + device_printf(sc->device, + "Winbond watchdog timeout after %hhu seconds.\n", + timeout); + } + + sc->active = (timeout == 0) ? 0 : 1; +} + +/* + * Watchdog event handler - called by the framework to enable or disable + * the watchdog or change the initial timeout value. + */ +static void +winbondwd_event(void *arg, unsigned int cmd, int *error) +{ + struct winbondwd_softc *sc = arg; + unsigned char rtimeout; + uint64_t timeout; + + if (cmd == 0) + winbondwd_set_timeout(sc, 0); + else { + timeout = (uint64_t)1 << (cmd & WD_INTERVAL); + if (timeout < (uint64_t)0xff * 1000 * 1000 * 1000) { + rtimeout = timeout / (1000 * 1000 * 1000); + if (rtimeout == 0) + rtimeout = 0xff; + winbondwd_set_timeout(sc, rtimeout); + } else { + device_printf(sc->device, + "Value %u too big, disabling\n", cmd & WD_INTERVAL); + /* Proposed timeout can not be satisified */ + winbondwd_set_timeout(sc, 0); + } + } +} + +/* + * A hack to probe Winbond chip's base port. + */ +static unsigned int +winbondwd_baseport_probe(void) +{ + unsigned char val; + int i; + const unsigned int baseport_candidates[] = { 0x2e, 0x4e, 0 }; + + for (i = 0; baseport_candidates[i] != 0; i++) { + /* + * Enter config mode. + * + * Output magic number twice to the index register + */ + outb(baseport_candidates[i], 0x87); + outb(baseport_candidates[i], 0x87); + + /* + * I know this is ugly but I didn't found a way to do + * this in a cleaner manner. + */ + /* Get data from CR 0x20 (Device ID) */ + outb(baseport_candidates[i], 0x20); + val = inb(baseport_candidates[i]+1); + + if (bootverbose) + printf("winbondwd0: CR20 probing port 0x%x got 0x%x\n", baseport_candidates[i], val); + + if (val != 0xff) { + outb(baseport_candidates[i], 0xaa); + return baseport_candidates[i]; + } + } + + return (unsigned int)(-1); +} + + + +/* + * Look for Winbond device. + */ +static void +winbondwd_identify(driver_t *driver, device_t parent) +{ + unsigned int baseport; + device_t dev; + + if ((dev = device_find_child(parent, driver->name, 0)) == NULL) { + if (resource_int_value("winbondwd", 0, "baseport", &baseport) != 0) { + baseport = winbondwd_baseport_probe(); + if (baseport == (unsigned int)(-1)) { + printf("winbondwd0: Compatible Winbond Super I/O not found.\n"); + return; + } + } + + dev = BUS_ADD_CHILD(parent, 0, driver->name, 0); + + bus_set_resource(dev, SYS_RES_IOPORT, 0, baseport, 2); + } + + if (dev == NULL) + return; +} + +static int +winbondwd_probe(device_t dev) +{ + + /* Do not claim some ISA PnP device by accident. */ + if (isa_get_logicalid(dev) != 0) + return (ENXIO); + return (0); +} + +static int +winbondwd_attach(device_t dev) +{ + struct winbondwd_softc *sc; + unsigned int baseport; + + sc = device_get_softc(dev); + sc->device = dev; + + if (resource_int_value("winbondwd", 0, "baseport", &baseport) != 0) { + baseport = winbondwd_baseport_probe(); + if (baseport == (unsigned int)(-1)) { + device_printf(dev, + "No compatible Winbond Super I/O found.\n"); + return (ENXIO); + } + } + + /* allocate I/O register space */ + sc->wb_rid = 0; + sc->wb_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->wb_rid, + baseport, baseport + 1, 2, RF_ACTIVE | RF_SHAREABLE); + if (sc->wb_res == NULL) { + device_printf(dev, "Unable to reserve Extended Function Registers\n"); + goto fail; + } + sc->wb_bst = rman_get_bustag(sc->wb_res); + sc->wb_bsh = rman_get_bushandle(sc->wb_res); + + /* Display the device status */ + winbondwd_show_timeout(sc); + + /* register the watchdog event handler */ + sc->ev_tag = EVENTHANDLER_REGISTER(watchdog_list, winbondwd_event, sc, 0); + + return (0); + +fail: + if (sc->wb_res != NULL) + bus_release_resource(dev, SYS_RES_IOPORT, + sc->wb_rid, sc->wb_res); + return (ENXIO); +} + +static int +winbondwd_detach(device_t dev) +{ + struct winbondwd_softc *sc; + + sc = device_get_softc(dev); + + /* deregister event handler */ + if (sc->ev_tag != NULL) + EVENTHANDLER_DEREGISTER(watchdog_list, sc->ev_tag); + sc->ev_tag = NULL; + + /* Disable the watchdog */ + if (sc->active) + winbondwd_set_timeout(sc, 0); + + /* deallocate I/O register space */ + bus_release_resource(dev, SYS_RES_IOPORT, sc->wb_rid, sc->wb_res); + + return (0); +} + +static device_method_t winbondwd_methods[] = { + DEVMETHOD(device_identify, winbondwd_identify), + DEVMETHOD(device_probe, winbondwd_probe), + DEVMETHOD(device_attach, winbondwd_attach), + DEVMETHOD(device_detach, winbondwd_detach), + DEVMETHOD(device_shutdown, winbondwd_detach), + {0,0} +}; + +static driver_t winbondwd_driver = { + "winbondwd", + winbondwd_methods, + sizeof(struct winbondwd_softc), +}; + +DRIVER_MODULE(winbondwd, isa, winbondwd_driver, winbondwd_devclass, NULL, NULL); diff --git a/sys/dev/winbondwd/winbondwd.h b/sys/dev/winbondwd/winbondwd.h new file mode 100644 index 0000000..57a1a23 --- /dev/null +++ b/sys/dev/winbondwd/winbondwd.h @@ -0,0 +1,47 @@ +/*- + * Copyright (c) 2010 iXsystems, Inc. + * All rights reserved. + * Written by: Xin LI + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _WINBONDWD_H_ +#define _WINBONDWD_H_ + +struct winbondwd_softc { + device_t device; + + int active; + unsigned int timeout; + + int wb_rid; + struct resource *wb_res; + bus_space_tag_t wb_bst; + bus_space_handle_t wb_bsh; + + eventhandler_tag ev_tag; +}; + +#endif /* _WINBONDWD_H_ */ diff --git a/sys/i386/conf/NOTES b/sys/i386/conf/NOTES index 866e641..80d82a2 100644 --- a/sys/i386/conf/NOTES +++ b/sys/i386/conf/NOTES @@ -828,9 +828,11 @@ hint.pcf.0.irq="5" # # ichwd: Intel ICH watchdog timer # amdsbwd: AMD SB7xx watchdog timer +# winbondwd: Winbond watchdog timer # device ichwd device amdsbwd +device winbondwd # # Temperature sensors: diff --git a/sys/modules/Makefile b/sys/modules/Makefile index 2dbc3d9..de533e4 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -320,6 +320,7 @@ SUBDIR= ${_3dfx} \ vx \ wb \ ${_wi} \ + ${_winbondwd} \ wlan \ wlan_acl \ wlan_amrr \ @@ -469,6 +470,7 @@ _stg= stg _streams= streams _svr4= svr4 _wi= wi +_winbondwd= winbondwd _xe= xe .if ${MK_ZFS} != "no" || defined(ALL_MODULES) _zfs= zfs @@ -623,6 +625,7 @@ _twa= twa _vesa= vesa _x86bios= x86bios _wi= wi +_winbondwd= winbondwd _wpi= wpi _wpifw= wpifw .if ${MK_ZFS} != "no" || defined(ALL_MODULES) diff --git a/sys/modules/winbondwd/Makefile b/sys/modules/winbondwd/Makefile new file mode 100644 index 0000000..382d37f --- /dev/null +++ b/sys/modules/winbondwd/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../dev/winbondwd + +KMOD= winbondwd +SRCS= winbondwd.c device_if.h bus_if.h pci_if.h isa_if.h + +.include -- 1.7.5.4