The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/wbwd/wbwd.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2011 Sandvine Incorporated ULC.
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  */
   26 /*
   27  * Support for Winbond watchdog.
   28  *
   29  * With minor abstractions it might be possible to add support for other
   30  * different Winbond Super I/O chips as well.  Winbond seems to have four
   31  * different types of chips, four different ways to get into extended config
   32  * mode.
   33  *
   34  * Note: there is no serialization between the debugging sysctl handlers and
   35  * the watchdog functions and possibly others poking the registers at the same
   36  * time.  For that at least possibly interfering sysctls are hidden by default.
   37  */
   38 
   39 #include <sys/cdefs.h>
   40 __FBSDID("$FreeBSD: releng/8.4/sys/dev/wbwd/wbwd.c 237341 2012-06-20 21:29:19Z rnoland $");
   41 
   42 #include <sys/param.h>
   43 #include <sys/kernel.h>
   44 #include <sys/systm.h>
   45 #include <sys/bus.h>
   46 #include <sys/eventhandler.h>
   47 #include <sys/lock.h>
   48 #include <sys/module.h>
   49 #include <sys/rman.h>
   50 #include <sys/sbuf.h>
   51 #include <sys/sysctl.h>
   52 #include <sys/watchdog.h>
   53 
   54 #include <isa/isavar.h>
   55 
   56 #include <machine/bus.h>
   57 #include <machine/resource.h>
   58 
   59 /*
   60  * Global registers.
   61  */
   62 #define WB_DEVICE_ID_REG        0x20    /* Device ID */
   63 #define WB_DEVICE_REV_REG       0x21    /* Device revision */
   64 #define WB_CR26                 0x26    /* Bit6: HEFRAS (base port selector) */
   65 
   66 /* LDN selection. */
   67 #define WB_LDN_REG              0x07
   68 #define WB_LDN_REG_LDN8         0x08    /* GPIO 2, Watchdog */
   69 
   70 /*
   71  * LDN8 (GPIO 2, Watchdog) specific registers and options.
   72  */
   73 /* CR30: LDN8 activation control. */
   74 #define WB_LDN8_CR30            0x30
   75 #define WB_LDN8_CR30_ACTIVE     0x01    /* 1: LD active */
   76 
   77 /* CRF5: Watchdog scale, P20. Mapped to reg_1. */
   78 #define WB_LDN8_CRF5            0xF5
   79 #define WB_LDN8_CRF5_SCALE      0x08    /* 0: 1s, 1: 60s */
   80 #define WB_LDN8_CRF5_KEYB_P20   0x04    /* 1: keyb P20 forces timeout */
   81 #define WB_LDN8_CRF5_KBRST      0x02    /* 1: timeout causes pin60 kbd reset */
   82 
   83 /* CRF6: Watchdog Timeout (0 == off). Mapped to reg_timeout. */
   84 #define WB_LDN8_CRF6            0xF6
   85 
   86 /* CRF7: Watchdog mouse, keyb, force, .. Mapped to reg_2. */
   87 #define WB_LDN8_CRF7            0xF7
   88 #define WB_LDN8_CRF7_MOUSE      0x80    /* 1: mouse irq resets wd timer */
   89 #define WB_LDN8_CRF7_KEYB       0x40    /* 1: keyb irq resets wd timer */
   90 #define WB_LDN8_CRF7_FORCE      0x20    /* 1: force timeout (self-clear) */
   91 #define WB_LDN8_CRF7_TS         0x10    /* 0: counting, 1: fired */
   92 #define WB_LDN8_CRF7_IRQS       0x0f    /* irq source for watchdog, 2 == SMI */
   93 #define WB_LDN8_CRF7_CLEAR_MASK \
   94     (WB_LDN8_CRF7_MOUSE|WB_LDN8_CRF7_KEYB|WB_LDN8_CRF7_TS|WB_LDN8_CRF7_IRQS)
   95 
   96 #define write_efir_1(sc, value)                                         \
   97         bus_space_write_1((sc)->bst, (sc)->bsh, 0, (value))
   98 #define read_efir_1(sc)                                                 \
   99         bus_space_read_1((sc)->bst, (sc)->bsh, 0)
  100 #define write_efdr_1(sc, value)                                         \
  101         bus_space_write_1((sc)->bst, (sc)->bsh, 1, (value))
  102 #define read_efdr_1(sc)                                                 \
  103         bus_space_read_1((sc)->bst, (sc)->bsh, 1)
  104 
  105 struct wb_softc {
  106         device_t                dev;
  107         struct resource         *portres;
  108         bus_space_tag_t         bst;
  109         bus_space_handle_t      bsh;
  110         int                     rid;
  111         eventhandler_tag        ev_tag;
  112         int                     (*ext_cfg_enter_f)(struct wb_softc *);
  113         void                    (*ext_cfg_exit_f)(struct wb_softc *);
  114         int                     debug_verbose;
  115 
  116         /*
  117          * Special feature to let the watchdog fire at a different
  118          * timeout as set by watchdog(4) but still use that API to
  119          * re-load it periodically.
  120          */
  121         unsigned int            timeout_override;
  122 
  123         /*
  124          * Space to save current state temporary and for sysctls.
  125          * We want to know the timeout value and usually need two
  126          * additional registers for options. Do not name them by
  127          * register as these might be different by chip.
  128          */
  129         uint8_t                 reg_timeout;
  130         uint8_t                 reg_1;
  131         uint8_t                 reg_2;
  132 };
  133 
  134 static int      ext_cfg_enter_0x87_0x87(struct wb_softc *);
  135 static void     ext_cfg_exit_0xaa(struct wb_softc *);
  136 
  137 struct winbond_superio_cfg {
  138         uint8_t                 efer;   /* and efir */
  139         int                     (*ext_cfg_enter_f)(struct wb_softc *);
  140         void                    (*ext_cfg_exit_f)(struct wb_softc *);
  141 } probe_addrs[] = {
  142         {
  143                 .efer                   = 0x2e,
  144                 .ext_cfg_enter_f        = ext_cfg_enter_0x87_0x87,
  145                 .ext_cfg_exit_f         = ext_cfg_exit_0xaa,
  146         },
  147         {
  148                 .efer                   = 0x4e,
  149                 .ext_cfg_enter_f        = ext_cfg_enter_0x87_0x87,
  150                 .ext_cfg_exit_f         = ext_cfg_exit_0xaa,
  151         },
  152 };
  153 
  154 struct winbond_vendor_device_id {
  155         uint16_t                vendor_id;
  156         uint8_t                 device_id;
  157         uint8_t                 device_rev;
  158         const char *            descr;
  159 } wb_devs[] = {
  160         {
  161                 .vendor_id      = 0x5ca3,
  162                 .device_id      = 0x52,
  163                 .device_rev     = 0x17,
  164                 .descr          = "Winbond 83627HF/F/HG/G Rev. G",
  165         },
  166         {
  167                 .vendor_id      = 0x5ca3,
  168                 .device_id      = 0x52,
  169                 .device_rev     = 0x3a,
  170                 .descr          = "Winbond 83627HF/F/HG/G Rev. J",
  171         },
  172         {
  173                 .vendor_id      = 0x5ca3,
  174                 .device_id      = 0x52,
  175                 .device_rev     = 0x41,
  176                 .descr          = "Winbond 83627HF/F/HG/G Rev. UD-A",
  177         },
  178         {
  179                 .vendor_id      = 0x5ca3,
  180                 .device_id      = 0xa0,
  181                 .device_rev     = 0x25,
  182                 .descr          = "Winbond 83627DHG IC ver. 5",   
  183         },
  184         {
  185                 .vendor_id      = 0x5ca3,
  186                 .device_id      = 0xb0,
  187                 .device_rev     = 0x73,
  188                 .descr          = "Winbond 83627DHG-P",   
  189         },
  190 };
  191 
  192 /*
  193  * Return the watchdog related registers as we last read them.  This will
  194  * usually not give the current timeout or state on whether the watchdog
  195  * fired.
  196  */
  197 static int
  198 sysctl_wb_debug(SYSCTL_HANDLER_ARGS)
  199 {
  200         struct wb_softc *sc;
  201         struct sbuf sb;
  202         int error;
  203 
  204         sc = arg1;
  205 
  206         sbuf_new_for_sysctl(&sb, NULL, 64, req);
  207 
  208         sbuf_printf(&sb, "LDN8 (GPIO2, Watchdog): ");
  209         sbuf_printf(&sb, "CRF5 0x%02x ", sc->reg_1);
  210         sbuf_printf(&sb, "CRF6 0x%02x ", sc->reg_timeout);
  211         sbuf_printf(&sb, "CRF7 0x%02x ", sc->reg_2);
  212 
  213         sbuf_trim(&sb);
  214         error = sbuf_finish(&sb);
  215         sbuf_delete(&sb);
  216         return (error);
  217 }
  218 
  219 /*
  220  * Read the current values before returning them.  Given this might poke
  221  * the registers the same time as the watchdog, this sysctl handler should
  222  * be marked CTLFLAG_SKIP to not show up by default.
  223  */
  224 static int
  225 sysctl_wb_debug_current(SYSCTL_HANDLER_ARGS)
  226 {
  227         struct wb_softc *sc;
  228 
  229         sc = arg1;
  230 
  231         /*
  232          * Enter extended function mode in case someone else has been
  233          * poking on the registers.  We will not leave it though.
  234          */
  235         if ((*sc->ext_cfg_enter_f)(sc) != 0)
  236                 return (ENXIO);
  237 
  238         /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */
  239         write_efir_1(sc, WB_LDN_REG);
  240         write_efdr_1(sc, WB_LDN_REG_LDN8);
  241 
  242         write_efir_1(sc, WB_LDN8_CRF5);
  243         sc->reg_1 = read_efdr_1(sc);
  244         write_efir_1(sc, WB_LDN8_CRF6);
  245         sc->reg_timeout = read_efdr_1(sc);
  246         write_efir_1(sc, WB_LDN8_CRF7);
  247         sc->reg_2 = read_efdr_1(sc);
  248 
  249         return (sysctl_wb_debug(oidp, arg1, arg2, req));
  250 }
  251 
  252 /*
  253  * Sysctl handlers to force a watchdog timeout or to test the NMI functionality
  254  * works as expetced.
  255  * For testing we could set a test_nmi flag in the softc that, in case of NMI, a
  256  * callback function from trap.c could check whether we fired and not report the
  257  * timeout but clear the flag for the sysctl again.  This is interesting given a
  258  * lot of boards have jumpers to change the action on watchdog timeout or
  259  * disable the watchdog completely.
  260  * XXX-BZ notyet: currently no general infrastructure exists to do this.
  261  */
  262 static int
  263 sysctl_wb_force_test_nmi(SYSCTL_HANDLER_ARGS)
  264 {
  265         struct wb_softc *sc;
  266         int error, test, val;
  267 
  268         sc = arg1;
  269         test = arg2;
  270 
  271 #ifdef notyet
  272         val = sc->test_nmi;
  273 #else
  274         val = 0;
  275 #endif
  276         error = sysctl_handle_int(oidp, &val, 0, req);
  277         if (error || !req->newptr)
  278                 return (error);
  279 
  280 #ifdef notyet
  281         /* Manually clear the test for a value of 0 and do nothing else. */
  282         if (test && val == 0) {
  283                 sc->test_nmi = 0;
  284                 return (0);
  285         }
  286 #endif
  287 
  288         /*
  289          * Enter extended function mode in case someone else has been
  290          * poking on the registers.  We will not leave it though.
  291          */
  292         if ((*sc->ext_cfg_enter_f)(sc) != 0)
  293                 return (ENXIO);
  294 
  295 #ifdef notyet
  296         /*
  297          * If we are testing the NMI functionality, set the flag before
  298          * forcing the timeout.
  299          */
  300         if (test)
  301                 sc->test_nmi = 1;
  302 #endif
  303 
  304         /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */
  305         write_efir_1(sc, WB_LDN_REG);
  306         write_efdr_1(sc, WB_LDN_REG_LDN8);
  307 
  308         /* Force watchdog to fire. */
  309         write_efir_1(sc, WB_LDN8_CRF7);
  310         sc->reg_2 = read_efdr_1(sc);
  311         sc->reg_2 |= WB_LDN8_CRF7_FORCE;
  312 
  313         write_efir_1(sc, WB_LDN8_CRF7);
  314         write_efdr_1(sc, sc->reg_2);
  315 
  316         return (0);
  317 }
  318 
  319 /*
  320  * Print current watchdog state.
  321  *
  322  * Note: it is the responsibility of the caller to update the registers
  323  * upfront.
  324  */
  325 static void
  326 wb_print_state(struct wb_softc *sc, const char *msg)
  327 {
  328 
  329         device_printf(sc->dev, "%s%sWatchdog %sabled. %s"
  330             "Scaling by %ds, timer at %d (%s=%ds%s). "
  331             "CRF5 0x%02x CRF7 0x%02x\n",
  332             (msg != NULL) ? msg : "", (msg != NULL) ? ": " : "",
  333             (sc->reg_timeout > 0x00) ? "en" : "dis",
  334             (sc->reg_2 & WB_LDN8_CRF7_TS) ? "Watchdog fired. " : "",
  335             (sc->reg_1 & WB_LDN8_CRF5_SCALE) ? 60 : 1,
  336             sc->reg_timeout,
  337             (sc->reg_timeout > 0x00) ? "<" : "",
  338             sc->reg_timeout * ((sc->reg_1 & WB_LDN8_CRF5_SCALE) ? 60 : 1),
  339             (sc->reg_timeout > 0x00) ? " left" : "",
  340             sc->reg_1, sc->reg_2);
  341 }
  342 
  343 /*
  344  * Functions to enter and exit extended function mode.  Possibly shared
  345  * between different chips.
  346  */
  347 static int
  348 ext_cfg_enter_0x87_0x87(struct wb_softc *sc)
  349 {
  350 
  351         /*
  352          * Enable extended function mode.
  353          * Winbond does not allow us to validate so always return success.
  354          */
  355         write_efir_1(sc, 0x87);
  356         write_efir_1(sc, 0x87);
  357 
  358         return (0);
  359 }
  360 
  361 static void
  362 ext_cfg_exit_0xaa(struct wb_softc *sc)
  363 {
  364 
  365         write_efir_1(sc, 0xaa);
  366 }
  367 
  368 /*
  369  * (Re)load the watchdog counter depending on timeout.  A timeout of 0 will
  370  * disable the watchdog.
  371  */
  372 static int
  373 wb_set_watchdog(struct wb_softc *sc, unsigned int timeout)
  374 {
  375 
  376         if (sc->debug_verbose)
  377                 wb_print_state(sc, "Before watchdog counter (re)load");
  378 
  379         /*
  380          * Enter extended function mode in case someone else has been
  381          * poking on the registers.  We will not leave it though.
  382          */
  383         if ((*sc->ext_cfg_enter_f)(sc) != 0)
  384                 return (ENXIO);
  385 
  386         /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog) */
  387         write_efir_1(sc, WB_LDN_REG);
  388         write_efdr_1(sc, WB_LDN_REG_LDN8);
  389 
  390         /* Disable and validate or arm/reset watchdog. */
  391         if (timeout == 0) {
  392                 /* Disable watchdog. */
  393                 write_efir_1(sc, WB_LDN8_CRF6);
  394                 write_efdr_1(sc, 0x00);
  395 
  396                 /* Re-check. */
  397                 write_efir_1(sc, WB_LDN8_CRF6);
  398                 sc->reg_timeout = read_efdr_1(sc);
  399                 
  400                 if (sc->reg_timeout != 0x00) {
  401                         device_printf(sc->dev, "Failed to disable watchdog: "
  402                             "0x%02x.\n", sc->reg_timeout);
  403                         return (EIO);
  404                 }
  405 
  406         } else {
  407                 /*
  408                  * In case an override is set, let it override.  It may lead
  409                  * to strange results as we do not check the input of the sysctl.
  410                  */
  411                 if (sc->timeout_override > 0)
  412                         timeout = sc->timeout_override;
  413 
  414                 /* Make sure we support the requested timeout. */
  415                 if (timeout > 255 * 60)
  416                         return (EINVAL);
  417 
  418                 /* Read current scaling factor. */
  419                 write_efir_1(sc, WB_LDN8_CRF5);
  420                 sc->reg_1 = read_efdr_1(sc);
  421 
  422                 if (timeout > 255) {
  423                         /* Set scaling factor to 60s. */
  424                         sc->reg_1 |= WB_LDN8_CRF5_SCALE;
  425                         sc->reg_timeout = (timeout / 60);
  426                         if (timeout % 60)
  427                                 sc->reg_timeout++;
  428                 } else {
  429                         /* Set scaling factor to 1s. */
  430                         sc->reg_1 &= ~WB_LDN8_CRF5_SCALE;
  431                         sc->reg_timeout = timeout;
  432                 }
  433 
  434                 /* In case we fired before we need to clear to fire again. */
  435                 write_efir_1(sc, WB_LDN8_CRF7);
  436                 sc->reg_2 = read_efdr_1(sc);
  437                 if (sc->reg_2 & WB_LDN8_CRF7_TS) {
  438                         sc->reg_2 &= ~WB_LDN8_CRF7_TS;
  439                         write_efir_1(sc, WB_LDN8_CRF7);
  440                         write_efdr_1(sc, sc->reg_2);
  441                 }
  442 
  443                 /* Write back scaling factor. */
  444                 write_efir_1(sc, WB_LDN8_CRF5);
  445                 write_efdr_1(sc, sc->reg_1);
  446 
  447                 /* Set timer and arm/reset the watchdog. */
  448                 write_efir_1(sc, WB_LDN8_CRF6);
  449                 write_efdr_1(sc, sc->reg_timeout);
  450         }
  451 
  452         if (sc->debug_verbose)
  453                 wb_print_state(sc, "After watchdog counter (re)load");
  454 
  455         return (0);
  456 }
  457 
  458 /*
  459  * watchdog(9) EVENTHANDLER function implementation to (re)load the counter
  460  * with the given timeout or disable the watchdog.
  461  */
  462 static void
  463 wb_watchdog_fn(void *private, u_int cmd, int *error)
  464 {
  465         struct wb_softc *sc;
  466         unsigned int timeout;
  467         int e;
  468 
  469         sc = private;
  470         KASSERT(sc != NULL, ("%s: watchdog handler function called without "
  471             "softc.", __func__));
  472 
  473         cmd &= WD_INTERVAL;
  474         if (cmd > 0 && cmd <= 63) {
  475                 /* Reset (and arm) watchdog. */
  476                 timeout = ((uint64_t)1 << cmd) / 1000000000;
  477                 if (timeout == 0)
  478                         timeout = 1;
  479                 e = wb_set_watchdog(sc, timeout);
  480                 if (e == 0) {
  481                         if (error != NULL)
  482                                 *error = 0;
  483                 } else {
  484                         /* On error, try to make sure the WD is disabled. */
  485                         wb_set_watchdog(sc, 0);
  486                 }
  487 
  488         } else {
  489                 /* Disable watchdog. */
  490                 e = wb_set_watchdog(sc, 0);
  491                 if (e != 0 && cmd == 0 && error != NULL) {
  492                         /* Failed to disable watchdog. */
  493                         *error = EOPNOTSUPP;
  494                 }
  495         }
  496 }
  497 
  498 /*
  499  * Probe/attach the Winbond Super I/O chip.
  500  *
  501  * Initial abstraction to possibly support more chips:
  502  * - Iterate over the well known base ports, try to enable extended function
  503  *   mode and read and match the device ID and device revision.  Unfortunately
  504  *   the Vendor ID is in the hardware monitoring section accessible by different
  505  *   base ports only.
  506  * - Also HEFRAS, which would tell use the base port, is only accessible after
  507  *   entering extended function mode, for which the base port is needed.
  508  *   At least check HEFRAS to match the current base port we are probing.
  509  * - On match set the description, remember functions to enter/exit extended
  510  *   function mode as well as the base port.
  511  */
  512 static int
  513 wb_probe_enable(device_t dev, int probe)
  514 {
  515         struct wb_softc *sc;
  516         int error, found, i, j;
  517         uint8_t dev_id, dev_rev, cr26;
  518 
  519         sc = device_get_softc(dev);
  520         bzero(sc, sizeof(*sc));
  521         sc->dev = dev;
  522 
  523         error = ENXIO;
  524         for (i = 0; i < sizeof(probe_addrs) / sizeof(*probe_addrs); i++) {
  525 
  526                 /* Allocate bus resources for IO index/data register access. */
  527                 sc->portres = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->rid,
  528                     probe_addrs[i].efer, probe_addrs[i].efer + 1, 2, RF_ACTIVE);
  529                 if (sc->portres == NULL)
  530                         continue;
  531                 sc->bst = rman_get_bustag(sc->portres);
  532                 sc->bsh = rman_get_bushandle(sc->portres);
  533 
  534                 found = 0;
  535                 error = (*probe_addrs[i].ext_cfg_enter_f)(sc);
  536                 if (error != 0)
  537                         goto cleanup;
  538 
  539                 /* Identify the SuperIO chip. */
  540                 write_efir_1(sc, WB_DEVICE_ID_REG);
  541                 dev_id = read_efdr_1(sc);
  542                 write_efir_1(sc, WB_DEVICE_REV_REG);
  543                 dev_rev = read_efdr_1(sc);
  544                 write_efir_1(sc, WB_CR26);
  545                 cr26 = read_efdr_1(sc);
  546 
  547                 /* HEFRAS of 0 means EFER at 0x2e, 1 means EFER at 0x4e. */
  548                 if (((cr26 & 0x40) == 0x00 && probe_addrs[i].efer != 0x2e) ||
  549                     ((cr26 & 0x40) == 0x40 && probe_addrs[i].efer != 0x4e)) {
  550                         device_printf(dev, "HEFRAS and EFER do not align: EFER "
  551                             "0x%02x DevID 0x%02x DevRev 0x%02x CR26 0x%02x\n",
  552                              probe_addrs[i].efer, dev_id, dev_rev, cr26);
  553                         goto cleanup;
  554                 }
  555 
  556                 for (j = 0; j < sizeof(wb_devs) / sizeof(*wb_devs); j++) {
  557                         if (wb_devs[j].device_id == dev_id &&
  558                             wb_devs[j].device_rev == dev_rev) {
  559                                 if (probe)
  560                                         device_set_desc(dev, wb_devs[j].descr);
  561                                 found++;
  562                                 break;
  563                         }
  564                 }
  565                 if (probe && found && bootverbose)
  566                         device_printf(dev, "%s EFER 0x%02x ID 0x%02x Rev 0x%02x"
  567                              " CR26 0x%02x (probing)\n", device_get_desc(dev),
  568                              probe_addrs[i].efer, dev_id, dev_rev, cr26);
  569 cleanup:
  570                 if (probe || !found) {
  571                         (*probe_addrs[i].ext_cfg_exit_f)(sc);
  572 
  573                         (void) bus_release_resource(dev, SYS_RES_IOPORT, sc->rid,
  574                             sc->portres);
  575                 }
  576 
  577                 /*
  578                  * Stop probing if have successfully identified the SuperIO.
  579                  * Remember the extended function mode enter/exit functions
  580                  * for operations.
  581                  */
  582                 if (found) {
  583                         sc->ext_cfg_enter_f = probe_addrs[i].ext_cfg_enter_f;
  584                         sc->ext_cfg_exit_f = probe_addrs[i].ext_cfg_exit_f;
  585                         error = BUS_PROBE_DEFAULT;
  586                         break;
  587                 } else
  588                         error = ENXIO;
  589         }
  590 
  591         return (error);
  592 }
  593 
  594 static int
  595 wb_probe(device_t dev)
  596 {
  597 
  598         /* Make sure we do not claim some ISA PNP device. */
  599         if (isa_get_logicalid(dev) != 0)
  600                 return (ENXIO);
  601 
  602         return (wb_probe_enable(dev, 1));
  603 }
  604 
  605 static int
  606 wb_attach(device_t dev)
  607 {
  608         struct wb_softc *sc;
  609         struct sysctl_ctx_list *sctx;
  610         struct sysctl_oid *soid;
  611         unsigned long timeout;
  612         int error;
  613 
  614         error = wb_probe_enable(dev, 0);
  615         if (error > 0)
  616                 return (ENXIO);
  617 
  618         sc = device_get_softc(dev);
  619         KASSERT(sc->ext_cfg_enter_f != NULL && sc->ext_cfg_exit_f != NULL,
  620             ("%s: successfull probe result but not setup correctly", __func__));
  621 
  622         /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */
  623         write_efir_1(sc, WB_LDN_REG);
  624         write_efdr_1(sc, WB_LDN_REG_LDN8);
  625 
  626         /* Make sure LDN8 is enabled (Do we need to? Also affects GPIO). */
  627         write_efir_1(sc, WB_LDN8_CR30);
  628         write_efdr_1(sc, WB_LDN8_CR30_ACTIVE);
  629 
  630         /* Read the current watchdog configuration. */
  631         write_efir_1(sc, WB_LDN8_CRF5);
  632         sc->reg_1 = read_efdr_1(sc);
  633         write_efir_1(sc, WB_LDN8_CRF6);
  634         sc->reg_timeout = read_efdr_1(sc);
  635         write_efir_1(sc, WB_LDN8_CRF7);
  636         sc->reg_2 = read_efdr_1(sc);
  637 
  638         /* Print current state if bootverbose or watchdog already enabled. */
  639         if (bootverbose || (sc->reg_timeout > 0x00))
  640                 wb_print_state(sc, "Before watchdog attach");
  641 
  642         /*
  643          * Clear a previous watchdog timeout event (if (still) set).
  644          * Disable all all interrupt reset sources (defaults).
  645          */
  646         sc->reg_1 &= ~(WB_LDN8_CRF5_KEYB_P20);
  647         sc->reg_1 |= WB_LDN8_CRF5_KBRST;
  648         write_efir_1(sc, WB_LDN8_CRF5);
  649         write_efdr_1(sc, sc->reg_1);
  650 
  651         sc->reg_2 &= ~WB_LDN8_CRF7_CLEAR_MASK;
  652         write_efir_1(sc, WB_LDN8_CRF7);
  653         write_efdr_1(sc, sc->reg_2);
  654 
  655         /* Read global timeout override tunable, Add per device sysctls. */
  656         if (TUNABLE_ULONG_FETCH("hw.wbwd.timeout_override", &timeout)) {
  657                 if (timeout > 0)
  658                         sc->timeout_override = timeout;
  659         }
  660         sctx = device_get_sysctl_ctx(dev);
  661         soid = device_get_sysctl_tree(dev);
  662         SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO,
  663             "timeout_override", CTLFLAG_RW, &sc->timeout_override, 0,
  664             "Timeout in seconds overriding default watchdog timeout");
  665         SYSCTL_ADD_INT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO,
  666             "debug_verbose", CTLFLAG_RW, &sc->debug_verbose, 0,
  667             "Enables extra debugging information");
  668         SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "debug",
  669             CTLTYPE_STRING|CTLFLAG_RD, sc, 0, sysctl_wb_debug, "A",
  670             "Selected register information from last change by driver");
  671         SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "debug_current",
  672             CTLTYPE_STRING|CTLFLAG_RD|CTLFLAG_SKIP, sc, 0,
  673              sysctl_wb_debug_current, "A",
  674              "Selected register information (may interfere)");
  675         SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "force_timeout",
  676             CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_SKIP, sc, 0,
  677             sysctl_wb_force_test_nmi, "I", "Enable to force watchdog to fire.");
  678 
  679         /* Register watchdog. */
  680         sc->ev_tag = EVENTHANDLER_REGISTER(watchdog_list, wb_watchdog_fn, sc,
  681             0);
  682 
  683         if (bootverbose)
  684                 wb_print_state(sc, "After watchdog attach");
  685 
  686         return (0);
  687 }
  688 
  689 static int
  690 wb_detach(device_t dev)
  691 {
  692         struct wb_softc *sc;
  693 
  694         sc = device_get_softc(dev);
  695 
  696         /* Unregister and stop the watchdog if running. */
  697         if (sc->ev_tag)
  698                 EVENTHANDLER_DEREGISTER(watchdog_list, sc->ev_tag);
  699         wb_set_watchdog(sc, 0);
  700 
  701         /* Disable extended function mode. */
  702         (*sc->ext_cfg_exit_f)(sc);
  703 
  704         /* Cleanup resources. */
  705         (void) bus_release_resource(dev, SYS_RES_IOPORT, sc->rid, sc->portres);
  706 
  707         /* Bus subroutines take care of sysctls already. */
  708 
  709         return (0);
  710 }
  711 
  712 static device_method_t wb_methods[] = {
  713         /* Device interface */
  714         DEVMETHOD(device_probe,         wb_probe),
  715         DEVMETHOD(device_attach,        wb_attach),
  716         DEVMETHOD(device_detach,        wb_detach),
  717 
  718         { 0, 0 }
  719 };
  720 
  721 static driver_t wb_isa_driver = {
  722         "wbwd",
  723         wb_methods,
  724         sizeof(struct wb_softc)
  725 };
  726 
  727 static devclass_t wb_devclass;
  728 
  729 DRIVER_MODULE(wb, isa, wb_isa_driver, wb_devclass, NULL, NULL);

Cache object: c841a9f7007ecf68c460ab1acecfa796


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.