From owner-svn-src-all@FreeBSD.ORG Fri Dec 28 21:35:48 2012 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id A45AAD8D; Fri, 28 Dec 2012 21:35:48 +0000 (UTC) (envelope-from delphij@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) by mx1.freebsd.org (Postfix) with ESMTP id 863508FC0A; Fri, 28 Dec 2012 21:35:48 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.5/8.14.5) with ESMTP id qBSLZmFC008081; Fri, 28 Dec 2012 21:35:48 GMT (envelope-from delphij@svn.freebsd.org) Received: (from delphij@localhost) by svn.freebsd.org (8.14.5/8.14.5/Submit) id qBSLZmaM008080; Fri, 28 Dec 2012 21:35:48 GMT (envelope-from delphij@svn.freebsd.org) Message-Id: <201212282135.qBSLZmaM008080@svn.freebsd.org> From: Xin LI Date: Fri, 28 Dec 2012 21:35:48 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r244787 - head/sys/dev/wbwd X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 28 Dec 2012 21:35:48 -0000 Author: delphij Date: Fri Dec 28 21:35:47 2012 New Revision: 244787 URL: http://svnweb.freebsd.org/changeset/base/244787 Log: Add DEVICE_IDENTIFY method for wbwd(4), required on most of recent Supermicro motherboards. Tested on X8STi and X8DTH boards. Sponsored by: iXsystems, Inc. MFC after: 2 weeks Modified: head/sys/dev/wbwd/wbwd.c Modified: head/sys/dev/wbwd/wbwd.c ============================================================================== --- head/sys/dev/wbwd/wbwd.c Fri Dec 28 21:26:40 2012 (r244786) +++ head/sys/dev/wbwd/wbwd.c Fri Dec 28 21:35:47 2012 (r244787) @@ -1,5 +1,6 @@ /*- * Copyright (c) 2011 Sandvine Incorporated ULC. + * Copyright (c) 2012 iXsystems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -93,15 +94,6 @@ __FBSDID("$FreeBSD$"); #define WB_LDN8_CRF7_CLEAR_MASK \ (WB_LDN8_CRF7_MOUSE|WB_LDN8_CRF7_KEYB|WB_LDN8_CRF7_TS|WB_LDN8_CRF7_IRQS) -#define write_efir_1(sc, value) \ - bus_space_write_1((sc)->bst, (sc)->bsh, 0, (value)) -#define read_efir_1(sc) \ - bus_space_read_1((sc)->bst, (sc)->bsh, 0) -#define write_efdr_1(sc, value) \ - bus_space_write_1((sc)->bst, (sc)->bsh, 1, (value)) -#define read_efdr_1(sc) \ - bus_space_read_1((sc)->bst, (sc)->bsh, 1) - struct wb_softc { device_t dev; struct resource *portres; @@ -109,8 +101,8 @@ struct wb_softc { bus_space_handle_t bsh; int rid; eventhandler_tag ev_tag; - int (*ext_cfg_enter_f)(struct wb_softc *); - void (*ext_cfg_exit_f)(struct wb_softc *); + int (*ext_cfg_enter_f)(struct wb_softc *, u_short); + void (*ext_cfg_exit_f)(struct wb_softc *, u_short); int debug_verbose; /* @@ -131,13 +123,13 @@ struct wb_softc { uint8_t reg_2; }; -static int ext_cfg_enter_0x87_0x87(struct wb_softc *); -static void ext_cfg_exit_0xaa(struct wb_softc *); +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 *); - void (*ext_cfg_exit_f)(struct wb_softc *); + int (*ext_cfg_enter_f)(struct wb_softc *, u_short); + void (*ext_cfg_exit_f)(struct wb_softc *, u_short); } probe_addrs[] = { { .efer = 0x2e, @@ -189,6 +181,50 @@ struct winbond_vendor_device_id { }, }; +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)); +} + /* * Return the watchdog related registers as we last read them. This will * usually not give the current timeout or state on whether the watchdog @@ -231,19 +267,19 @@ sysctl_wb_debug_current(SYSCTL_HANDLER_A * Enter extended function mode in case someone else has been * poking on the registers. We will not leave it though. */ - if ((*sc->ext_cfg_enter_f)(sc) != 0) + if ((*sc->ext_cfg_enter_f)(sc, 0) != 0) return (ENXIO); /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */ - write_efir_1(sc, WB_LDN_REG); - write_efdr_1(sc, WB_LDN_REG_LDN8); + write_efir_1(sc, 0, WB_LDN_REG); + write_efdr_1(sc, 0, WB_LDN_REG_LDN8); - write_efir_1(sc, WB_LDN8_CRF5); - sc->reg_1 = read_efdr_1(sc); - write_efir_1(sc, WB_LDN8_CRF6); - sc->reg_timeout = read_efdr_1(sc); - write_efir_1(sc, WB_LDN8_CRF7); - sc->reg_2 = read_efdr_1(sc); + write_efir_1(sc, 0, WB_LDN8_CRF5); + sc->reg_1 = read_efdr_1(sc, 0); + write_efir_1(sc, 0, WB_LDN8_CRF6); + sc->reg_timeout = read_efdr_1(sc, 0); + write_efir_1(sc, 0, WB_LDN8_CRF7); + sc->reg_2 = read_efdr_1(sc, 0); return (sysctl_wb_debug(oidp, arg1, arg2, req)); } @@ -288,7 +324,7 @@ sysctl_wb_force_test_nmi(SYSCTL_HANDLER_ * Enter extended function mode in case someone else has been * poking on the registers. We will not leave it though. */ - if ((*sc->ext_cfg_enter_f)(sc) != 0) + if ((*sc->ext_cfg_enter_f)(sc, 0) != 0) return (ENXIO); #ifdef notyet @@ -301,16 +337,16 @@ sysctl_wb_force_test_nmi(SYSCTL_HANDLER_ #endif /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */ - write_efir_1(sc, WB_LDN_REG); - write_efdr_1(sc, WB_LDN_REG_LDN8); + write_efir_1(sc, 0, WB_LDN_REG); + write_efdr_1(sc, 0, WB_LDN_REG_LDN8); /* Force watchdog to fire. */ - write_efir_1(sc, WB_LDN8_CRF7); - sc->reg_2 = read_efdr_1(sc); + write_efir_1(sc, 0, WB_LDN8_CRF7); + sc->reg_2 = read_efdr_1(sc, 0); sc->reg_2 |= WB_LDN8_CRF7_FORCE; - write_efir_1(sc, WB_LDN8_CRF7); - write_efdr_1(sc, sc->reg_2); + write_efir_1(sc, 0, WB_LDN8_CRF7); + write_efdr_1(sc, 0, sc->reg_2); return (0); } @@ -344,24 +380,24 @@ wb_print_state(struct wb_softc *sc, cons * between different chips. */ static int -ext_cfg_enter_0x87_0x87(struct wb_softc *sc) +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, 0x87); - write_efir_1(sc, 0x87); + write_efir_1(sc, baseport, 0x87); + write_efir_1(sc, baseport, 0x87); return (0); } static void -ext_cfg_exit_0xaa(struct wb_softc *sc) +ext_cfg_exit_0xaa(struct wb_softc *sc, u_short baseport) { - write_efir_1(sc, 0xaa); + write_efir_1(sc, baseport, 0xaa); } /* @@ -379,22 +415,22 @@ wb_set_watchdog(struct wb_softc *sc, uns * Enter extended function mode in case someone else has been * poking on the registers. We will not leave it though. */ - if ((*sc->ext_cfg_enter_f)(sc) != 0) + if ((*sc->ext_cfg_enter_f)(sc, 0) != 0) return (ENXIO); /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog) */ - write_efir_1(sc, WB_LDN_REG); - write_efdr_1(sc, WB_LDN_REG_LDN8); + write_efir_1(sc, 0, WB_LDN_REG); + write_efdr_1(sc, 0, WB_LDN_REG_LDN8); /* Disable and validate or arm/reset watchdog. */ if (timeout == 0) { /* Disable watchdog. */ - write_efir_1(sc, WB_LDN8_CRF6); - write_efdr_1(sc, 0x00); + write_efir_1(sc, 0, WB_LDN8_CRF6); + write_efdr_1(sc, 0, 0x00); /* Re-check. */ - write_efir_1(sc, WB_LDN8_CRF6); - sc->reg_timeout = read_efdr_1(sc); + write_efir_1(sc, 0, WB_LDN8_CRF6); + sc->reg_timeout = read_efdr_1(sc, 0); if (sc->reg_timeout != 0x00) { device_printf(sc->dev, "Failed to disable watchdog: " @@ -415,8 +451,8 @@ wb_set_watchdog(struct wb_softc *sc, uns return (EINVAL); /* Read current scaling factor. */ - write_efir_1(sc, WB_LDN8_CRF5); - sc->reg_1 = read_efdr_1(sc); + write_efir_1(sc, 0, WB_LDN8_CRF5); + sc->reg_1 = read_efdr_1(sc, 0); if (timeout > 255) { /* Set scaling factor to 60s. */ @@ -431,21 +467,21 @@ wb_set_watchdog(struct wb_softc *sc, uns } /* In case we fired before we need to clear to fire again. */ - write_efir_1(sc, WB_LDN8_CRF7); - sc->reg_2 = read_efdr_1(sc); + write_efir_1(sc, 0, WB_LDN8_CRF7); + sc->reg_2 = read_efdr_1(sc, 0); if (sc->reg_2 & WB_LDN8_CRF7_TS) { sc->reg_2 &= ~WB_LDN8_CRF7_TS; - write_efir_1(sc, WB_LDN8_CRF7); - write_efdr_1(sc, sc->reg_2); + write_efir_1(sc, 0, WB_LDN8_CRF7); + write_efdr_1(sc, 0, sc->reg_2); } /* Write back scaling factor. */ - write_efir_1(sc, WB_LDN8_CRF5); - write_efdr_1(sc, sc->reg_1); + write_efir_1(sc, 0, WB_LDN8_CRF5); + write_efdr_1(sc, 0, sc->reg_1); /* Set timer and arm/reset the watchdog. */ - write_efir_1(sc, WB_LDN8_CRF6); - write_efdr_1(sc, sc->reg_timeout); + write_efir_1(sc, 0, WB_LDN8_CRF6); + write_efdr_1(sc, 0, sc->reg_timeout); } if (sc->debug_verbose) @@ -515,62 +551,71 @@ wb_probe_enable(device_t dev, int probe) int error, found, i, j; uint8_t dev_id, dev_rev, cr26; - sc = device_get_softc(dev); - bzero(sc, sizeof(*sc)); - sc->dev = dev; + if (dev == NULL) + sc = NULL; + else { + sc = device_get_softc(dev); + bzero(sc, sizeof(*sc)); + sc->dev = dev; + } error = ENXIO; for (i = 0; i < sizeof(probe_addrs) / sizeof(*probe_addrs); i++) { - /* 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); + 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); + } found = 0; - error = (*probe_addrs[i].ext_cfg_enter_f)(sc); + 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, WB_DEVICE_ID_REG); - dev_id = read_efdr_1(sc); - write_efir_1(sc, WB_DEVICE_REV_REG); - dev_rev = read_efdr_1(sc); - write_efir_1(sc, WB_CR26); - cr26 = read_efdr_1(sc); + 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); /* 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)) { - 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); + 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 < sizeof(wb_devs) / sizeof(*wb_devs); j++) { if (wb_devs[j].device_id == dev_id && wb_devs[j].device_rev == dev_rev) { - if (probe) + if (probe && dev != NULL) device_set_desc(dev, wb_devs[j].descr); found++; break; } } - if (probe && found && bootverbose) + if (probe && found && bootverbose && dev != NULL) device_printf(dev, "%s EFER 0x%02x ID 0x%02x Rev 0x%02x" " CR26 0x%02x (probing)\n", device_get_desc(dev), probe_addrs[i].efer, dev_id, dev_rev, cr26); cleanup: if (probe || !found) { - (*probe_addrs[i].ext_cfg_exit_f)(sc); + (*probe_addrs[i].ext_cfg_exit_f)(sc, probe_addrs[i].efer); - (void) bus_release_resource(dev, SYS_RES_IOPORT, sc->rid, - sc->portres); + if (sc != NULL) + (void) bus_release_resource(dev, SYS_RES_IOPORT, + sc->rid, sc->portres); } /* @@ -579,8 +624,10 @@ cleanup: * for operations. */ if (found) { - sc->ext_cfg_enter_f = probe_addrs[i].ext_cfg_enter_f; - sc->ext_cfg_exit_f = probe_addrs[i].ext_cfg_exit_f; + 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; + } error = BUS_PROBE_DEFAULT; break; } else @@ -590,6 +637,21 @@ cleanup: return (error); } +static void +wb_identify(driver_t *driver, device_t parent) +{ + device_t dev; + + if ((dev = device_find_child(parent, driver->name, 0)) == NULL) { + if (wb_probe_enable(dev, 1) != BUS_PROBE_DEFAULT) { + if (bootverbose) + device_printf(dev, "can not find compatible Winbond chip.\n"); + } else + dev = BUS_ADD_CHILD(parent, 0, driver->name, 0); + return; + } +} + static int wb_probe(device_t dev) { @@ -619,20 +681,20 @@ wb_attach(device_t dev) ("%s: successfull probe result but not setup correctly", __func__)); /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */ - write_efir_1(sc, WB_LDN_REG); - write_efdr_1(sc, WB_LDN_REG_LDN8); + write_efir_1(sc, 0, WB_LDN_REG); + write_efdr_1(sc, 0, WB_LDN_REG_LDN8); /* Make sure LDN8 is enabled (Do we need to? Also affects GPIO). */ - write_efir_1(sc, WB_LDN8_CR30); - write_efdr_1(sc, WB_LDN8_CR30_ACTIVE); + write_efir_1(sc, 0, WB_LDN8_CR30); + write_efdr_1(sc, 0, WB_LDN8_CR30_ACTIVE); /* Read the current watchdog configuration. */ - write_efir_1(sc, WB_LDN8_CRF5); - sc->reg_1 = read_efdr_1(sc); - write_efir_1(sc, WB_LDN8_CRF6); - sc->reg_timeout = read_efdr_1(sc); - write_efir_1(sc, WB_LDN8_CRF7); - sc->reg_2 = read_efdr_1(sc); + write_efir_1(sc, 0, WB_LDN8_CRF5); + sc->reg_1 = read_efdr_1(sc, 0); + write_efir_1(sc, 0, WB_LDN8_CRF6); + sc->reg_timeout = read_efdr_1(sc, 0); + write_efir_1(sc, 0, WB_LDN8_CRF7); + sc->reg_2 = read_efdr_1(sc, 0); /* Print current state if bootverbose or watchdog already enabled. */ if (bootverbose || (sc->reg_timeout > 0x00)) @@ -644,12 +706,12 @@ wb_attach(device_t dev) */ sc->reg_1 &= ~(WB_LDN8_CRF5_KEYB_P20); sc->reg_1 |= WB_LDN8_CRF5_KBRST; - write_efir_1(sc, WB_LDN8_CRF5); - write_efdr_1(sc, sc->reg_1); + write_efir_1(sc, 0, WB_LDN8_CRF5); + write_efdr_1(sc, 0, sc->reg_1); sc->reg_2 &= ~WB_LDN8_CRF7_CLEAR_MASK; - write_efir_1(sc, WB_LDN8_CRF7); - write_efdr_1(sc, sc->reg_2); + write_efir_1(sc, 0, WB_LDN8_CRF7); + write_efdr_1(sc, 0, sc->reg_2); /* Read global timeout override tunable, Add per device sysctls. */ if (TUNABLE_ULONG_FETCH("hw.wbwd.timeout_override", &timeout)) { @@ -698,7 +760,7 @@ wb_detach(device_t dev) wb_set_watchdog(sc, 0); /* Disable extended function mode. */ - (*sc->ext_cfg_exit_f)(sc); + (*sc->ext_cfg_exit_f)(sc, 0); /* Cleanup resources. */ (void) bus_release_resource(dev, SYS_RES_IOPORT, sc->rid, sc->portres); @@ -710,6 +772,7 @@ wb_detach(device_t dev) 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),