Index: head/share/man/man4/wbwd.4 =================================================================== --- head/share/man/man4/wbwd.4 +++ head/share/man/man4/wbwd.4 @@ -25,16 +25,17 @@ .\" .\" $FreeBSD$ .\" -.Dd March 24, 2016 +.Dd October 16, 2019 .Dt WBWD 4 .Os .Sh NAME .Nm wbwd .Nd device driver for Winbond/Nuvoton Super I/O chips watchdog timer .Sh SYNOPSIS -To compile this driver into the kernel, place the following line in your +To compile this driver into the kernel, place the following lines in your kernel configuration file: .Bd -ragged -offset indent +.Cd "device superio" .Cd "device wbwd" .Ed .Pp @@ -91,10 +92,6 @@ .It Nuvoton NCT6792 .El -.Pp -Driver may be forced to attach to unknown chips by adding to -.Pa /boot/device.hints : -.Cd hint.wbwd.0.at="isa" .Sh SYSCTL VARIABLES The .Nm @@ -130,6 +127,7 @@ driver also provides further sysctl options that are hidden by default. See the source code for more information. .Sh SEE ALSO +.Xr superio 4 .Xr watchdog 4 , .Xr device.hints 5 , .Xr watchdog 8 , Index: head/sys/dev/wbwd/wbwd.c =================================================================== --- head/sys/dev/wbwd/wbwd.c +++ head/sys/dev/wbwd/wbwd.c @@ -47,14 +47,12 @@ #include #include #include -#include #include -#include #include #include #include -#include +#include #include #include @@ -101,13 +99,7 @@ struct wb_softc { device_t dev; - struct resource *portres; - bus_space_tag_t bst; - bus_space_handle_t bsh; - int rid; eventhandler_tag ev_tag; - int (*ext_cfg_enter_f)(struct wb_softc *, u_short); - void (*ext_cfg_exit_f)(struct wb_softc *, u_short); enum chips chip; uint8_t ctl_reg; uint8_t time_reg; @@ -132,26 +124,6 @@ uint8_t reg_2; }; -static int ext_cfg_enter_0x87_0x87(struct wb_softc *, u_short); -static void ext_cfg_exit_0xaa(struct wb_softc *, u_short); - -struct winbond_superio_cfg { - uint8_t efer; /* and efir */ - int (*ext_cfg_enter_f)(struct wb_softc *, u_short); - void (*ext_cfg_exit_f)(struct wb_softc *, u_short); -} probe_addrs[] = { - { - .efer = 0x2e, - .ext_cfg_enter_f = ext_cfg_enter_0x87_0x87, - .ext_cfg_exit_f = ext_cfg_exit_0xaa, - }, - { - .efer = 0x4e, - .ext_cfg_enter_f = ext_cfg_enter_0x87_0x87, - .ext_cfg_exit_f = ext_cfg_exit_0xaa, - }, -}; - struct winbond_vendor_device_id { uint8_t device_id; enum chips chip; @@ -264,66 +236,7 @@ }, }; -static void -write_efir_1(struct wb_softc *sc, u_short baseport, uint8_t value) -{ - MPASS(sc != NULL || baseport != 0); - if (sc != NULL) - bus_space_write_1((sc)->bst, (sc)->bsh, 0, (value)); - else - outb(baseport, value); -} - -static uint8_t __unused -read_efir_1(struct wb_softc *sc, u_short baseport) -{ - - MPASS(sc != NULL || baseport != 0); - if (sc != NULL) - return (bus_space_read_1((sc)->bst, (sc)->bsh, 0)); - else - return (inb(baseport)); -} - -static void -write_efdr_1(struct wb_softc *sc, u_short baseport, uint8_t value) -{ - - MPASS(sc != NULL || baseport != 0); - if (sc != NULL) - bus_space_write_1((sc)->bst, (sc)->bsh, 1, (value)); - else - outb(baseport + 1, value); -} - -static uint8_t -read_efdr_1(struct wb_softc *sc, u_short baseport) -{ - - MPASS(sc != NULL || baseport != 0); - if (sc != NULL) - return (bus_space_read_1((sc)->bst, (sc)->bsh, 1)); - else - return (inb(baseport + 1)); -} - -static void -write_reg(struct wb_softc *sc, uint8_t reg, uint8_t value) -{ - - write_efir_1(sc, 0, reg); - write_efdr_1(sc, 0, value); -} - -static uint8_t -read_reg(struct wb_softc *sc, uint8_t reg) -{ - - write_efir_1(sc, 0, reg); - return (read_efdr_1(sc, 0)); -} - /* * Return the watchdog related registers as we last read them. This will * usually not give the current timeout or state on whether the watchdog @@ -362,18 +275,10 @@ sc = arg1; - if ((*sc->ext_cfg_enter_f)(sc, 0) != 0) - return (ENXIO); + sc->reg_1 = superio_read(sc->dev, sc->ctl_reg); + sc->reg_timeout = superio_read(sc->dev, sc->time_reg); + sc->reg_2 = superio_read(sc->dev, sc->csr_reg); - /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */ - write_reg(sc, WB_LDN_REG, WB_LDN_REG_LDN8); - - sc->reg_1 = read_reg(sc, sc->ctl_reg); - sc->reg_timeout = read_reg(sc, sc->time_reg); - sc->reg_2 = read_reg(sc, sc->csr_reg); - - (*sc->ext_cfg_exit_f)(sc, 0); - return (sysctl_wb_debug(oidp, arg1, arg2, req)); } @@ -411,12 +316,7 @@ sc->test_nmi = 0; return (0); } -#endif - if ((*sc->ext_cfg_enter_f)(sc, 0) != 0) - return (ENXIO); - -#ifdef notyet /* * If we are testing the NMI functionality, set the flag before * forcing the timeout. @@ -425,16 +325,11 @@ sc->test_nmi = 1; #endif - /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */ - write_reg(sc, WB_LDN_REG, WB_LDN_REG_LDN8); - /* Force watchdog to fire. */ - sc->reg_2 = read_reg(sc, sc->csr_reg); + sc->reg_2 = superio_read(sc->dev, sc->csr_reg); sc->reg_2 |= WB_LDN8_CRF7_FORCE; - write_reg(sc, sc->csr_reg, sc->reg_2); + superio_write(sc->dev, sc->csr_reg, sc->reg_2); - (*sc->ext_cfg_exit_f)(sc, 0); - return (0); } @@ -450,7 +345,7 @@ device_printf(sc->dev, "%s%sWatchdog %sabled. %s" "Scaling by %ds, timer at %d (%s=%ds%s). " - "CRF5 0x%02x CRF7 0x%02x\n", + "CR%02X 0x%02x CR%02X 0x%02x\n", (msg != NULL) ? msg : "", (msg != NULL) ? ": " : "", (sc->reg_timeout > 0x00) ? "en" : "dis", (sc->reg_2 & WB_LDN8_CRF7_TS) ? "Watchdog fired. " : "", @@ -459,35 +354,10 @@ (sc->reg_timeout > 0x00) ? "<" : "", sc->reg_timeout * ((sc->reg_1 & WB_LDN8_CRF5_SCALE) ? 60 : 1), (sc->reg_timeout > 0x00) ? " left" : "", - sc->reg_1, sc->reg_2); + sc->ctl_reg, sc->reg_1, sc->csr_reg, sc->reg_2); } /* - * Functions to enter and exit extended function mode. Possibly shared - * between different chips. - */ -static int -ext_cfg_enter_0x87_0x87(struct wb_softc *sc, u_short baseport) -{ - - /* - * Enable extended function mode. - * Winbond does not allow us to validate so always return success. - */ - write_efir_1(sc, baseport, 0x87); - write_efir_1(sc, baseport, 0x87); - - return (0); -} - -static void -ext_cfg_exit_0xaa(struct wb_softc *sc, u_short baseport) -{ - - write_efir_1(sc, baseport, 0xaa); -} - -/* * (Re)load the watchdog counter depending on timeout. A timeout of 0 will * disable the watchdog. */ @@ -511,20 +381,14 @@ if (sc->debug_verbose) wb_print_state(sc, "Before watchdog counter (re)load"); - if ((*sc->ext_cfg_enter_f)(sc, 0) != 0) - return (ENXIO); - - /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog) */ - write_reg(sc, WB_LDN_REG, WB_LDN_REG_LDN8); - if (timeout == 0) { /* Disable watchdog. */ sc->reg_timeout = 0; - write_reg(sc, sc->time_reg, sc->reg_timeout); + superio_write(sc->dev, sc->time_reg, sc->reg_timeout); } else { /* Read current scaling factor. */ - sc->reg_1 = read_reg(sc, sc->ctl_reg); + sc->reg_1 = superio_read(sc->dev, sc->ctl_reg); if (timeout > 255) { /* Set scaling factor to 60s. */ @@ -539,21 +403,19 @@ } /* In case we fired before we need to clear to fire again. */ - sc->reg_2 = read_reg(sc, sc->csr_reg); + sc->reg_2 = superio_read(sc->dev, sc->csr_reg); if (sc->reg_2 & WB_LDN8_CRF7_TS) { sc->reg_2 &= ~WB_LDN8_CRF7_TS; - write_reg(sc, sc->csr_reg, sc->reg_2); + superio_write(sc->dev, sc->csr_reg, sc->reg_2); } /* Write back scaling factor. */ - write_reg(sc, sc->ctl_reg, sc->reg_1); + superio_write(sc->dev, sc->ctl_reg, sc->reg_1); /* Set timer and arm/reset the watchdog. */ - write_reg(sc, sc->time_reg, sc->reg_timeout); + superio_write(sc->dev, sc->time_reg, sc->reg_timeout); } - (*sc->ext_cfg_exit_f)(sc, 0); - if (sc->debug_verbose) wb_print_state(sc, "After watchdog counter (re)load"); return (0); @@ -599,217 +461,99 @@ } } -/* - * Probe/attach the Winbond Super I/O chip. - * - * Initial abstraction to possibly support more chips: - * - Iterate over the well known base ports, try to enable extended function - * mode and read and match the device ID and device revision. Unfortunately - * the Vendor ID is in the hardware monitoring section accessible by different - * base ports only. - * - Also HEFRAS, which would tell use the base port, is only accessible after - * entering extended function mode, for which the base port is needed. - * At least check HEFRAS to match the current base port we are probing. - * - On match set the description, remember functions to enter/exit extended - * function mode as well as the base port. - */ static int -wb_probe_enable(device_t dev, int probe) +wb_probe(device_t dev) { - struct wb_softc *sc; - int error, found, i, j; - uint8_t dev_id, dev_rev, cr26; char buf[128]; + struct wb_softc *sc; + int found, j; + uint8_t devid; + uint8_t revid; - if (dev == NULL) - sc = NULL; - else { - sc = device_get_softc(dev); - bzero(sc, sizeof(*sc)); - sc->dev = dev; - } + if (superio_vendor(dev) != SUPERIO_VENDOR_NUVOTON) + return (ENXIO); + if (superio_get_type(dev) != SUPERIO_DEV_WDT) + return (ENXIO); - error = ENXIO; + sc = device_get_softc(dev); + devid = superio_devid(dev) >> 8; + revid = superio_revid(dev); found = 0; - for (i = 0; i < nitems(probe_addrs); i++) { - - if (sc != NULL) { - /* Allocate bus resources for IO index/data register access. */ - sc->portres = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->rid, - probe_addrs[i].efer, probe_addrs[i].efer + 1, 2, RF_ACTIVE); - if (sc->portres == NULL) - continue; - sc->bst = rman_get_bustag(sc->portres); - sc->bsh = rman_get_bushandle(sc->portres); - } - - error = (*probe_addrs[i].ext_cfg_enter_f)(sc, probe_addrs[i].efer); - if (error != 0) - goto cleanup; - - /* Identify the SuperIO chip. */ - write_efir_1(sc, probe_addrs[i].efer, WB_DEVICE_ID_REG); - dev_id = read_efdr_1(sc, probe_addrs[i].efer); - write_efir_1(sc, probe_addrs[i].efer, WB_DEVICE_REV_REG); - dev_rev = read_efdr_1(sc, probe_addrs[i].efer); - write_efir_1(sc, probe_addrs[i].efer, WB_CR26); - cr26 = read_efdr_1(sc, probe_addrs[i].efer); - - if (dev_id == 0xff && dev_rev == 0xff) - goto cleanup; - - /* HEFRAS of 0 means EFER at 0x2e, 1 means EFER at 0x4e. */ - if (((cr26 & 0x40) == 0x00 && probe_addrs[i].efer != 0x2e) || - ((cr26 & 0x40) == 0x40 && probe_addrs[i].efer != 0x4e)) { - if (dev != NULL) - device_printf(dev, "HEFRAS and EFER do not " - "align: EFER 0x%02x DevID 0x%02x DevRev " - "0x%02x CR26 0x%02x\n", - probe_addrs[i].efer, dev_id, dev_rev, cr26); - goto cleanup; - } - - for (j = 0; j < nitems(wb_devs); j++) { - if (wb_devs[j].device_id == dev_id) { - found = 1; - break; - } - } - - if (probe && dev != NULL) { + for (j = 0; j < nitems(wb_devs); j++) { + if (wb_devs[j].device_id == devid) { + sc->chip = wb_devs[j].chip; snprintf(buf, sizeof(buf), "%s (0x%02x/0x%02x) Watchdog Timer", - found ? wb_devs[j].descr : - "Unknown Winbond/Nuvoton", dev_id, dev_rev); + wb_devs[j].descr, devid, revid); device_set_desc_copy(dev, buf); - } - - /* If this is hinted attach, try to guess the model. */ - if (dev != NULL && !found) { - found = 1; - j = 0; - } - -cleanup: - if (probe || !found) { - (*probe_addrs[i].ext_cfg_exit_f)(sc, probe_addrs[i].efer); - if (sc != NULL) - (void) bus_release_resource(dev, SYS_RES_IOPORT, - sc->rid, sc->portres); - } - - /* - * Stop probing if have successfully identified the SuperIO. - * Remember the extended function mode enter/exit functions - * for operations. - */ - if (found) { - if (sc != NULL) { - sc->ext_cfg_enter_f = probe_addrs[i].ext_cfg_enter_f; - sc->ext_cfg_exit_f = probe_addrs[i].ext_cfg_exit_f; - sc->chip = wb_devs[j].chip; - sc->ctl_reg = 0xf5; - sc->time_reg = 0xf6; - sc->csr_reg = 0xf7; - if (sc->chip == w83697hf || - sc->chip == w83697ug) { - sc->ctl_reg = 0xf3; - sc->time_reg = 0xf4; - } else if (sc->chip == nct6102) { - sc->ctl_reg = 0xf0; - sc->time_reg = 0xf1; - sc->csr_reg = 0xf2; - } - } return (BUS_PROBE_SPECIFIC); - } else - error = ENXIO; + } } - - return (error); + return (ENXIO); } -static void -wb_identify(driver_t *driver, device_t parent) -{ - - if (device_find_child(parent, driver->name, 0) == NULL) { - if (wb_probe_enable(NULL, 1) <= 0) - BUS_ADD_CHILD(parent, 0, driver->name, 0); - } -} - static int -wb_probe(device_t dev) -{ - - /* Make sure we do not claim some ISA PNP device. */ - if (isa_get_logicalid(dev) != 0) - return (ENXIO); - - return (wb_probe_enable(dev, 1)); -} - -static int wb_attach(device_t dev) { struct wb_softc *sc; struct sysctl_ctx_list *sctx; struct sysctl_oid *soid; unsigned long timeout; - int error; uint8_t t; - error = wb_probe_enable(dev, 0); - if (error > 0) - return (ENXIO); - sc = device_get_softc(dev); - KASSERT(sc->ext_cfg_enter_f != NULL && sc->ext_cfg_exit_f != NULL, - ("%s: successful probe result but not setup correctly", __func__)); + sc->dev = dev; - /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */ - write_reg(sc, WB_LDN_REG, WB_LDN_REG_LDN8); + sc->ctl_reg = 0xf5; + sc->time_reg = 0xf6; + sc->csr_reg = 0xf7; + if (sc->chip == w83697hf || sc->chip == w83697ug) { + sc->ctl_reg = 0xf3; + sc->time_reg = 0xf4; + } else if (sc->chip == nct6102) { + sc->ctl_reg = 0xf0; + sc->time_reg = 0xf1; + sc->csr_reg = 0xf2; + } /* Make sure WDT is enabled. */ - write_reg(sc, WB_LDN8_CR30, - read_reg(sc, WB_LDN8_CR30) | WB_LDN8_CR30_ACTIVE); + superio_dev_enable(dev, WB_LDN8_CR30_ACTIVE); switch (sc->chip) { case w83627hf: case w83627s: - t = read_reg(sc, 0x2B) & ~0x10; - write_reg(sc, 0x2B, t); /* set GPIO24 to WDT0 */ + t = superio_read(dev, 0x2B) & ~0x10; + superio_write(dev, 0x2B, t); /* set GPIO24 to WDT0 */ break; case w83697hf: /* Set pin 119 to WDTO# mode (= CR29, WDT0) */ - t = read_reg(sc, 0x29) & ~0x60; + t = superio_read(dev, 0x29) & ~0x60; t |= 0x20; - write_reg(sc, 0x29, t); + superio_write(dev, 0x29, t); break; case w83697ug: /* Set pin 118 to WDTO# mode */ - t = read_reg(sc, 0x2b) & ~0x04; - write_reg(sc, 0x2b, t); + t = superio_read(dev, 0x2b) & ~0x04; + superio_write(dev, 0x2b, t); break; case w83627thf: - t = (read_reg(sc, 0x2B) & ~0x08) | 0x04; - write_reg(sc, 0x2B, t); /* set GPIO3 to WDT0 */ + t = (superio_read(dev, 0x2B) & ~0x08) | 0x04; + superio_write(dev, 0x2B, t); /* set GPIO3 to WDT0 */ break; case w83627dhg: case w83627dhg_p: - t = read_reg(sc, 0x2D) & ~0x01; /* PIN77 -> WDT0# */ - write_reg(sc, 0x2D, t); /* set GPIO5 to WDT0 */ - t = read_reg(sc, sc->ctl_reg); + t = superio_read(dev, 0x2D) & ~0x01; /* PIN77 -> WDT0# */ + superio_write(dev, 0x2D, t); /* set GPIO5 to WDT0 */ + t = superio_read(dev, sc->ctl_reg); t |= 0x02; /* enable the WDTO# output low pulse * to the KBRST# pin */ - write_reg(sc, sc->ctl_reg, t); + superio_write(dev, sc->ctl_reg, t); break; case w83637hf: break; case w83687thf: - t = read_reg(sc, 0x2C) & ~0x80; /* PIN47 -> WDT0# */ - write_reg(sc, 0x2C, t); + t = superio_read(dev, 0x2C) & ~0x80; /* PIN47 -> WDT0# */ + superio_write(dev, 0x2C, t); break; case w83627ehf: case w83627uhg: @@ -829,19 +573,19 @@ * Don't touch its configuration, and hope the BIOS * does the right thing. */ - t = read_reg(sc, sc->ctl_reg); + t = superio_read(dev, sc->ctl_reg); t |= 0x02; /* enable the WDTO# output low pulse * to the KBRST# pin */ - write_reg(sc, sc->ctl_reg, t); + superio_write(dev, sc->ctl_reg, t); break; default: break; } /* Read the current watchdog configuration. */ - sc->reg_1 = read_reg(sc, sc->ctl_reg); - sc->reg_timeout = read_reg(sc, sc->time_reg); - sc->reg_2 = read_reg(sc, sc->csr_reg); + sc->reg_1 = superio_read(dev, sc->ctl_reg); + sc->reg_timeout = superio_read(dev, sc->time_reg); + sc->reg_2 = superio_read(dev, sc->csr_reg); /* Print current state if bootverbose or watchdog already enabled. */ if (bootverbose || (sc->reg_timeout > 0x00)) @@ -849,7 +593,7 @@ sc->reg_1 &= ~WB_LDN8_CRF5_KEYB_P20; sc->reg_1 |= WB_LDN8_CRF5_KBRST; - write_reg(sc, sc->ctl_reg, sc->reg_1); + superio_write(dev, sc->ctl_reg, sc->reg_1); /* * Clear a previous watchdog timeout event (if still set). @@ -857,10 +601,8 @@ * since one of my boards is getting stuck in reboot without it. */ sc->reg_2 &= ~(WB_LDN8_CRF7_MOUSE|WB_LDN8_CRF7_TS); - write_reg(sc, sc->csr_reg, sc->reg_2); + superio_write(dev, sc->csr_reg, sc->reg_2); - (*sc->ext_cfg_exit_f)(sc, 0); - /* Read global timeout override tunable, Add per device sysctls. */ if (TUNABLE_ULONG_FETCH("hw.wbwd.timeout_override", &timeout)) { if (timeout > 0) @@ -907,12 +649,6 @@ EVENTHANDLER_DEREGISTER(watchdog_list, sc->ev_tag); wb_set_watchdog(sc, 0); - /* Disable extended function mode. */ - (*sc->ext_cfg_exit_f)(sc, 0); - - /* Cleanup resources. */ - (void) bus_release_resource(dev, SYS_RES_IOPORT, sc->rid, sc->portres); - /* Bus subroutines take care of sysctls already. */ return (0); @@ -920,7 +656,6 @@ static device_method_t wb_methods[] = { /* Device interface */ - DEVMETHOD(device_identify, wb_identify), DEVMETHOD(device_probe, wb_probe), DEVMETHOD(device_attach, wb_attach), DEVMETHOD(device_detach, wb_detach), @@ -928,7 +663,7 @@ DEVMETHOD_END }; -static driver_t wb_isa_driver = { +static driver_t wb_driver = { "wbwd", wb_methods, sizeof(struct wb_softc) @@ -936,4 +671,6 @@ static devclass_t wb_devclass; -DRIVER_MODULE(wb, isa, wb_isa_driver, wb_devclass, NULL, NULL); +DRIVER_MODULE(wb, superio, wb_driver, wb_devclass, NULL, NULL); +MODULE_DEPEND(wb, superio, 1, 1, 1); +MODULE_VERSION(wb, 1);