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  * Copyright (c) 2012 iXsystems, Inc.
    4  * All rights reserved.
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   25  * SUCH DAMAGE.
   26  */
   27 /*
   28  * Support for Winbond watchdog.
   29  *
   30  * With minor abstractions it might be possible to add support for other
   31  * different Winbond Super I/O chips as well.  Winbond seems to have four
   32  * different types of chips, four different ways to get into extended config
   33  * mode.
   34  *
   35  * Note: there is no serialization between the debugging sysctl handlers and
   36  * the watchdog functions and possibly others poking the registers at the same
   37  * time.  For that at least possibly interfering sysctls are hidden by default.
   38  */
   39 
   40 #include <sys/cdefs.h>
   41 __FBSDID("$FreeBSD: releng/11.2/sys/dev/wbwd/wbwd.c 331722 2018-03-29 02:50:57Z eadler $");
   42 
   43 #include <sys/param.h>
   44 #include <sys/kernel.h>
   45 #include <sys/systm.h>
   46 #include <sys/bus.h>
   47 #include <sys/eventhandler.h>
   48 #include <sys/lock.h>
   49 #include <sys/module.h>
   50 #include <sys/rman.h>
   51 #include <sys/sbuf.h>
   52 #include <sys/sysctl.h>
   53 #include <sys/watchdog.h>
   54 
   55 #include <isa/isavar.h>
   56 
   57 #include <machine/bus.h>
   58 #include <machine/resource.h>
   59 
   60 /*
   61  * Global registers.
   62  */
   63 #define WB_DEVICE_ID_REG        0x20    /* Device ID */
   64 #define WB_DEVICE_REV_REG       0x21    /* Device revision */
   65 #define WB_CR26                 0x26    /* Bit6: HEFRAS (base port selector) */
   66 
   67 /* LDN selection. */
   68 #define WB_LDN_REG              0x07
   69 #define WB_LDN_REG_LDN8         0x08    /* GPIO 2, Watchdog */
   70 
   71 /*
   72  * LDN8 (GPIO 2, Watchdog) specific registers and options.
   73  */
   74 /* CR30: LDN8 activation control. */
   75 #define WB_LDN8_CR30            0x30
   76 #define WB_LDN8_CR30_ACTIVE     0x01    /* 1: LD active */
   77 
   78 /* CRF5: Watchdog scale, P20. Mapped to reg_1. */
   79 #define WB_LDN8_CRF5            0xF5
   80 #define WB_LDN8_CRF5_SCALE      0x08    /* 0: 1s, 1: 60s */
   81 #define WB_LDN8_CRF5_KEYB_P20   0x04    /* 1: keyb P20 forces timeout */
   82 #define WB_LDN8_CRF5_KBRST      0x02    /* 1: timeout causes pin60 kbd reset */
   83 
   84 /* CRF6: Watchdog Timeout (0 == off). Mapped to reg_timeout. */
   85 #define WB_LDN8_CRF6            0xF6
   86 
   87 /* CRF7: Watchdog mouse, keyb, force, .. Mapped to reg_2. */
   88 #define WB_LDN8_CRF7            0xF7
   89 #define WB_LDN8_CRF7_MOUSE      0x80    /* 1: mouse irq resets wd timer */
   90 #define WB_LDN8_CRF7_KEYB       0x40    /* 1: keyb irq resets wd timer */
   91 #define WB_LDN8_CRF7_FORCE      0x20    /* 1: force timeout (self-clear) */
   92 #define WB_LDN8_CRF7_TS         0x10    /* 0: counting, 1: fired */
   93 #define WB_LDN8_CRF7_IRQS       0x0f    /* irq source for watchdog, 2 == SMI */
   94 
   95 enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf,
   96              w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg,
   97              w83627dhg_p, w83667hg_b, nct6775, nct6776, nct6779, nct6791,
   98              nct6792, nct6793, nct6795, nct6102 };
   99 
  100 struct wb_softc {
  101         device_t                dev;
  102         struct resource         *portres;
  103         bus_space_tag_t         bst;
  104         bus_space_handle_t      bsh;
  105         int                     rid;
  106         eventhandler_tag        ev_tag;
  107         int                     (*ext_cfg_enter_f)(struct wb_softc *, u_short);
  108         void                    (*ext_cfg_exit_f)(struct wb_softc *, u_short);
  109         enum chips              chip;
  110         uint8_t                 ctl_reg;
  111         uint8_t                 time_reg;
  112         uint8_t                 csr_reg;
  113         int                     debug_verbose;
  114 
  115         /*
  116          * Special feature to let the watchdog fire at a different
  117          * timeout as set by watchdog(4) but still use that API to
  118          * re-load it periodically.
  119          */
  120         unsigned int            timeout_override;
  121 
  122         /*
  123          * Space to save current state temporary and for sysctls.
  124          * We want to know the timeout value and usually need two
  125          * additional registers for options. Do not name them by
  126          * register as these might be different by chip.
  127          */
  128         uint8_t                 reg_timeout;
  129         uint8_t                 reg_1;
  130         uint8_t                 reg_2;
  131 };
  132 
  133 static int      ext_cfg_enter_0x87_0x87(struct wb_softc *, u_short);
  134 static void     ext_cfg_exit_0xaa(struct wb_softc *, u_short);
  135 
  136 struct winbond_superio_cfg {
  137         uint8_t                 efer;   /* and efir */
  138         int                     (*ext_cfg_enter_f)(struct wb_softc *, u_short);
  139         void                    (*ext_cfg_exit_f)(struct wb_softc *, u_short);
  140 } probe_addrs[] = {
  141         {
  142                 .efer                   = 0x2e,
  143                 .ext_cfg_enter_f        = ext_cfg_enter_0x87_0x87,
  144                 .ext_cfg_exit_f         = ext_cfg_exit_0xaa,
  145         },
  146         {
  147                 .efer                   = 0x4e,
  148                 .ext_cfg_enter_f        = ext_cfg_enter_0x87_0x87,
  149                 .ext_cfg_exit_f         = ext_cfg_exit_0xaa,
  150         },
  151 };
  152 
  153 struct winbond_vendor_device_id {
  154         uint8_t                 device_id;
  155         enum chips              chip;
  156         const char *            descr;
  157 } wb_devs[] = {
  158         {
  159                 .device_id      = 0x52,
  160                 .chip           = w83627hf,
  161                 .descr          = "Winbond 83627HF/F/HG/G",
  162         },
  163         {
  164                 .device_id      = 0x59,
  165                 .chip           = w83627s,
  166                 .descr          = "Winbond 83627S",
  167         },
  168         {
  169                 .device_id      = 0x60,
  170                 .chip           = w83697hf,
  171                 .descr          = "Winbond 83697HF",
  172         },
  173         {
  174                 .device_id      = 0x68,
  175                 .chip           = w83697ug,
  176                 .descr          = "Winbond 83697UG",
  177         },
  178         {
  179                 .device_id      = 0x70,
  180                 .chip           = w83637hf,
  181                 .descr          = "Winbond 83637HF",
  182         },
  183         {
  184                 .device_id      = 0x82,
  185                 .chip           = w83627thf,
  186                 .descr          = "Winbond 83627THF",
  187         },
  188         {
  189                 .device_id      = 0x85,
  190                 .chip           = w83687thf,
  191                 .descr          = "Winbond 83687THF",
  192         },
  193         {
  194                 .device_id      = 0x88,
  195                 .chip           = w83627ehf,
  196                 .descr          = "Winbond 83627EHF",
  197         },
  198         {
  199                 .device_id      = 0xa0,
  200                 .chip           = w83627dhg,
  201                 .descr          = "Winbond 83627DHG",
  202         },
  203         {
  204                 .device_id      = 0xa2,
  205                 .chip           = w83627uhg,
  206                 .descr          = "Winbond 83627UHG",
  207         },
  208         {
  209                 .device_id      = 0xa5,
  210                 .chip           = w83667hg,
  211                 .descr          = "Winbond 83667HG",
  212         },
  213         {
  214                 .device_id      = 0xb0,
  215                 .chip           = w83627dhg_p,
  216                 .descr          = "Winbond 83627DHG-P",
  217         },
  218         {
  219                 .device_id      = 0xb3,
  220                 .chip           = w83667hg_b,
  221                 .descr          = "Winbond 83667HG-B",
  222         },
  223         {
  224                 .device_id      = 0xb4,
  225                 .chip           = nct6775,
  226                 .descr          = "Nuvoton NCT6775",
  227         },
  228         {
  229                 .device_id      = 0xc3,
  230                 .chip           = nct6776,
  231                 .descr          = "Nuvoton NCT6776",
  232         },
  233         {
  234                 .device_id      = 0xc4,
  235                 .chip           = nct6102,
  236                 .descr          = "Nuvoton NCT6102",
  237         },
  238         {
  239                 .device_id      = 0xc5,
  240                 .chip           = nct6779,
  241                 .descr          = "Nuvoton NCT6779",
  242         },
  243         {
  244                 .device_id      = 0xc8,
  245                 .chip           = nct6791,
  246                 .descr          = "Nuvoton NCT6791",
  247         },
  248         {
  249                 .device_id      = 0xc9,
  250                 .chip           = nct6792,
  251                 .descr          = "Nuvoton NCT6792",
  252         },
  253         {
  254                 .device_id      = 0xd1,
  255                 .chip           = nct6793,
  256                 .descr          = "Nuvoton NCT6793",
  257         },
  258         {
  259                 .device_id      = 0xd3,
  260                 .chip           = nct6795,
  261                 .descr          = "Nuvoton NCT6795",
  262         },
  263 };
  264 
  265 static void
  266 write_efir_1(struct wb_softc *sc, u_short baseport, uint8_t value)
  267 {
  268 
  269         MPASS(sc != NULL || baseport != 0);
  270         if (sc != NULL)
  271                 bus_space_write_1((sc)->bst, (sc)->bsh, 0, (value));
  272         else
  273                 outb(baseport, value);
  274 }
  275 
  276 static uint8_t __unused
  277 read_efir_1(struct wb_softc *sc, u_short baseport)
  278 {
  279 
  280         MPASS(sc != NULL || baseport != 0);
  281         if (sc != NULL)
  282                 return (bus_space_read_1((sc)->bst, (sc)->bsh, 0));
  283         else
  284                 return (inb(baseport));
  285 }
  286 
  287 static void
  288 write_efdr_1(struct wb_softc *sc, u_short baseport, uint8_t value)
  289 {
  290 
  291         MPASS(sc != NULL || baseport != 0);
  292         if (sc != NULL)
  293                 bus_space_write_1((sc)->bst, (sc)->bsh, 1, (value));
  294         else
  295                 outb(baseport + 1, value);
  296 }
  297 
  298 static uint8_t
  299 read_efdr_1(struct wb_softc *sc, u_short baseport)
  300 {
  301 
  302         MPASS(sc != NULL || baseport != 0);
  303         if (sc != NULL)
  304                 return (bus_space_read_1((sc)->bst, (sc)->bsh, 1));
  305         else
  306                 return (inb(baseport + 1));
  307 }
  308 
  309 static void
  310 write_reg(struct wb_softc *sc, uint8_t reg, uint8_t value)
  311 {
  312 
  313         write_efir_1(sc, 0, reg);
  314         write_efdr_1(sc, 0, value);
  315 }
  316 
  317 static uint8_t
  318 read_reg(struct wb_softc *sc, uint8_t reg)
  319 {
  320 
  321         write_efir_1(sc, 0, reg);
  322         return (read_efdr_1(sc, 0));
  323 }
  324 
  325 /*
  326  * Return the watchdog related registers as we last read them.  This will
  327  * usually not give the current timeout or state on whether the watchdog
  328  * fired.
  329  */
  330 static int
  331 sysctl_wb_debug(SYSCTL_HANDLER_ARGS)
  332 {
  333         struct wb_softc *sc;
  334         struct sbuf sb;
  335         int error;
  336 
  337         sc = arg1;
  338 
  339         sbuf_new_for_sysctl(&sb, NULL, 64, req);
  340 
  341         sbuf_printf(&sb, "LDN8 (GPIO2, Watchdog): ");
  342         sbuf_printf(&sb, "CR%02X 0x%02x ", sc->ctl_reg, sc->reg_1);
  343         sbuf_printf(&sb, "CR%02X 0x%02x ", sc->time_reg, sc->reg_timeout);
  344         sbuf_printf(&sb, "CR%02X 0x%02x", sc->csr_reg, sc->reg_2);
  345 
  346         error = sbuf_finish(&sb);
  347         sbuf_delete(&sb);
  348         return (error);
  349 }
  350 
  351 /*
  352  * Read the current values before returning them.  Given this might poke
  353  * the registers the same time as the watchdog, this sysctl handler should
  354  * be marked CTLFLAG_SKIP to not show up by default.
  355  */
  356 static int
  357 sysctl_wb_debug_current(SYSCTL_HANDLER_ARGS)
  358 {
  359         struct wb_softc *sc;
  360 
  361         sc = arg1;
  362 
  363         if ((*sc->ext_cfg_enter_f)(sc, 0) != 0)
  364                 return (ENXIO);
  365 
  366         /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */
  367         write_reg(sc, WB_LDN_REG, WB_LDN_REG_LDN8);
  368 
  369         sc->reg_1 = read_reg(sc, sc->ctl_reg);
  370         sc->reg_timeout = read_reg(sc, sc->time_reg);
  371         sc->reg_2 = read_reg(sc, sc->csr_reg);
  372 
  373         (*sc->ext_cfg_exit_f)(sc, 0);
  374 
  375         return (sysctl_wb_debug(oidp, arg1, arg2, req));
  376 }
  377 
  378 /*
  379  * Sysctl handlers to force a watchdog timeout or to test the NMI functionality
  380  * works as expetced.
  381  * For testing we could set a test_nmi flag in the softc that, in case of NMI, a
  382  * callback function from trap.c could check whether we fired and not report the
  383  * timeout but clear the flag for the sysctl again.  This is interesting given a
  384  * lot of boards have jumpers to change the action on watchdog timeout or
  385  * disable the watchdog completely.
  386  * XXX-BZ notyet: currently no general infrastructure exists to do this.
  387  */
  388 static int
  389 sysctl_wb_force_test_nmi(SYSCTL_HANDLER_ARGS)
  390 {
  391         struct wb_softc *sc;
  392         int error, test, val;
  393 
  394         sc = arg1;
  395         test = arg2;
  396 
  397 #ifdef notyet
  398         val = sc->test_nmi;
  399 #else
  400         val = 0;
  401 #endif
  402         error = sysctl_handle_int(oidp, &val, 0, req);
  403         if (error || !req->newptr)
  404                 return (error);
  405 
  406 #ifdef notyet
  407         /* Manually clear the test for a value of 0 and do nothing else. */
  408         if (test && val == 0) {
  409                 sc->test_nmi = 0;
  410                 return (0);
  411         }
  412 #endif
  413 
  414         if ((*sc->ext_cfg_enter_f)(sc, 0) != 0)
  415                 return (ENXIO);
  416 
  417 #ifdef notyet
  418         /*
  419          * If we are testing the NMI functionality, set the flag before
  420          * forcing the timeout.
  421          */
  422         if (test)
  423                 sc->test_nmi = 1;
  424 #endif
  425 
  426         /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */
  427         write_reg(sc, WB_LDN_REG, WB_LDN_REG_LDN8);
  428 
  429         /* Force watchdog to fire. */
  430         sc->reg_2 = read_reg(sc, sc->csr_reg);
  431         sc->reg_2 |= WB_LDN8_CRF7_FORCE;
  432         write_reg(sc, sc->csr_reg, sc->reg_2);
  433 
  434         (*sc->ext_cfg_exit_f)(sc, 0);
  435 
  436         return (0);
  437 }
  438 
  439 /*
  440  * Print current watchdog state.
  441  *
  442  * Note: it is the responsibility of the caller to update the registers
  443  * upfront.
  444  */
  445 static void
  446 wb_print_state(struct wb_softc *sc, const char *msg)
  447 {
  448 
  449         device_printf(sc->dev, "%s%sWatchdog %sabled. %s"
  450             "Scaling by %ds, timer at %d (%s=%ds%s). "
  451             "CRF5 0x%02x CRF7 0x%02x\n",
  452             (msg != NULL) ? msg : "", (msg != NULL) ? ": " : "",
  453             (sc->reg_timeout > 0x00) ? "en" : "dis",
  454             (sc->reg_2 & WB_LDN8_CRF7_TS) ? "Watchdog fired. " : "",
  455             (sc->reg_1 & WB_LDN8_CRF5_SCALE) ? 60 : 1,
  456             sc->reg_timeout,
  457             (sc->reg_timeout > 0x00) ? "<" : "",
  458             sc->reg_timeout * ((sc->reg_1 & WB_LDN8_CRF5_SCALE) ? 60 : 1),
  459             (sc->reg_timeout > 0x00) ? " left" : "",
  460             sc->reg_1, sc->reg_2);
  461 }
  462 
  463 /*
  464  * Functions to enter and exit extended function mode.  Possibly shared
  465  * between different chips.
  466  */
  467 static int
  468 ext_cfg_enter_0x87_0x87(struct wb_softc *sc, u_short baseport)
  469 {
  470 
  471         /*
  472          * Enable extended function mode.
  473          * Winbond does not allow us to validate so always return success.
  474          */
  475         write_efir_1(sc, baseport, 0x87);
  476         write_efir_1(sc, baseport, 0x87);
  477 
  478         return (0);
  479 }
  480 
  481 static void
  482 ext_cfg_exit_0xaa(struct wb_softc *sc, u_short baseport)
  483 {
  484 
  485         write_efir_1(sc, baseport, 0xaa);
  486 }
  487 
  488 /*
  489  * (Re)load the watchdog counter depending on timeout.  A timeout of 0 will
  490  * disable the watchdog.
  491  */
  492 static int
  493 wb_set_watchdog(struct wb_softc *sc, unsigned int timeout)
  494 {
  495 
  496         if (timeout != 0) {
  497                 /*
  498                  * In case an override is set, let it override.  It may lead
  499                  * to strange results as we do not check the input of the sysctl.
  500                  */
  501                 if (sc->timeout_override > 0)
  502                         timeout = sc->timeout_override;
  503 
  504                 /* Make sure we support the requested timeout. */
  505                 if (timeout > 255 * 60)
  506                         return (EINVAL);
  507         }
  508 
  509         if (sc->debug_verbose)
  510                 wb_print_state(sc, "Before watchdog counter (re)load");
  511 
  512         if ((*sc->ext_cfg_enter_f)(sc, 0) != 0)
  513                 return (ENXIO);
  514 
  515         /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog) */
  516         write_reg(sc, WB_LDN_REG, WB_LDN_REG_LDN8);
  517 
  518         if (timeout == 0) {
  519                 /* Disable watchdog. */
  520                 sc->reg_timeout = 0;
  521                 write_reg(sc, sc->time_reg, sc->reg_timeout);
  522 
  523         } else {
  524                 /* Read current scaling factor. */
  525                 sc->reg_1 = read_reg(sc, sc->ctl_reg);
  526 
  527                 if (timeout > 255) {
  528                         /* Set scaling factor to 60s. */
  529                         sc->reg_1 |= WB_LDN8_CRF5_SCALE;
  530                         sc->reg_timeout = (timeout / 60);
  531                         if (timeout % 60)
  532                                 sc->reg_timeout++;
  533                 } else {
  534                         /* Set scaling factor to 1s. */
  535                         sc->reg_1 &= ~WB_LDN8_CRF5_SCALE;
  536                         sc->reg_timeout = timeout;
  537                 }
  538 
  539                 /* In case we fired before we need to clear to fire again. */
  540                 sc->reg_2 = read_reg(sc, sc->csr_reg);
  541                 if (sc->reg_2 & WB_LDN8_CRF7_TS) {
  542                         sc->reg_2 &= ~WB_LDN8_CRF7_TS;
  543                         write_reg(sc, sc->csr_reg, sc->reg_2);
  544                 }
  545 
  546                 /* Write back scaling factor. */
  547                 write_reg(sc, sc->ctl_reg, sc->reg_1);
  548 
  549                 /* Set timer and arm/reset the watchdog. */
  550                 write_reg(sc, sc->time_reg, sc->reg_timeout);
  551         }
  552 
  553         (*sc->ext_cfg_exit_f)(sc, 0);
  554 
  555         if (sc->debug_verbose)
  556                 wb_print_state(sc, "After watchdog counter (re)load");
  557         return (0);
  558 }
  559 
  560 /*
  561  * watchdog(9) EVENTHANDLER function implementation to (re)load the counter
  562  * with the given timeout or disable the watchdog.
  563  */
  564 static void
  565 wb_watchdog_fn(void *private, u_int cmd, int *error)
  566 {
  567         struct wb_softc *sc;
  568         unsigned int timeout;
  569         int e;
  570 
  571         sc = private;
  572         KASSERT(sc != NULL, ("%s: watchdog handler function called without "
  573             "softc.", __func__));
  574 
  575         cmd &= WD_INTERVAL;
  576         if (cmd > 0 && cmd <= 63) {
  577                 /* Reset (and arm) watchdog. */
  578                 timeout = ((uint64_t)1 << cmd) / 1000000000;
  579                 if (timeout == 0)
  580                         timeout = 1;
  581                 e = wb_set_watchdog(sc, timeout);
  582                 if (e == 0) {
  583                         if (error != NULL)
  584                                 *error = 0;
  585                 } else {
  586                         /* On error, try to make sure the WD is disabled. */
  587                         wb_set_watchdog(sc, 0);
  588                 }
  589 
  590         } else {
  591                 /* Disable watchdog. */
  592                 e = wb_set_watchdog(sc, 0);
  593                 if (e != 0 && cmd == 0 && error != NULL) {
  594                         /* Failed to disable watchdog. */
  595                         *error = EOPNOTSUPP;
  596                 }
  597         }
  598 }
  599 
  600 /*
  601  * Probe/attach the Winbond Super I/O chip.
  602  *
  603  * Initial abstraction to possibly support more chips:
  604  * - Iterate over the well known base ports, try to enable extended function
  605  *   mode and read and match the device ID and device revision.  Unfortunately
  606  *   the Vendor ID is in the hardware monitoring section accessible by different
  607  *   base ports only.
  608  * - Also HEFRAS, which would tell use the base port, is only accessible after
  609  *   entering extended function mode, for which the base port is needed.
  610  *   At least check HEFRAS to match the current base port we are probing.
  611  * - On match set the description, remember functions to enter/exit extended
  612  *   function mode as well as the base port.
  613  */
  614 static int
  615 wb_probe_enable(device_t dev, int probe)
  616 {
  617         struct wb_softc *sc;
  618         int error, found, i, j;
  619         uint8_t dev_id, dev_rev, cr26;
  620         char buf[128];
  621 
  622         if (dev == NULL)
  623                 sc = NULL;
  624         else {
  625                 sc = device_get_softc(dev);
  626                 bzero(sc, sizeof(*sc));
  627                 sc->dev = dev;
  628         }
  629 
  630         error = ENXIO;
  631         found = 0;
  632         for (i = 0; i < nitems(probe_addrs); i++) {
  633 
  634                 if (sc != NULL) {
  635                         /* Allocate bus resources for IO index/data register access. */
  636                         sc->portres = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->rid,
  637                             probe_addrs[i].efer, probe_addrs[i].efer + 1, 2, RF_ACTIVE);
  638                         if (sc->portres == NULL)
  639                                 continue;
  640                         sc->bst = rman_get_bustag(sc->portres);
  641                         sc->bsh = rman_get_bushandle(sc->portres);
  642                 }
  643 
  644                 error = (*probe_addrs[i].ext_cfg_enter_f)(sc, probe_addrs[i].efer);
  645                 if (error != 0)
  646                         goto cleanup;
  647 
  648                 /* Identify the SuperIO chip. */
  649                 write_efir_1(sc, probe_addrs[i].efer, WB_DEVICE_ID_REG);
  650                 dev_id = read_efdr_1(sc, probe_addrs[i].efer);
  651                 write_efir_1(sc, probe_addrs[i].efer, WB_DEVICE_REV_REG);
  652                 dev_rev = read_efdr_1(sc, probe_addrs[i].efer);
  653                 write_efir_1(sc, probe_addrs[i].efer, WB_CR26);
  654                 cr26 = read_efdr_1(sc, probe_addrs[i].efer);
  655 
  656                 if (dev_id == 0xff && dev_rev == 0xff)
  657                         goto cleanup;
  658 
  659                 /* HEFRAS of 0 means EFER at 0x2e, 1 means EFER at 0x4e. */
  660                 if (((cr26 & 0x40) == 0x00 && probe_addrs[i].efer != 0x2e) ||
  661                     ((cr26 & 0x40) == 0x40 && probe_addrs[i].efer != 0x4e)) {
  662                         if (dev != NULL)
  663                                 device_printf(dev, "HEFRAS and EFER do not "
  664                                     "align: EFER 0x%02x DevID 0x%02x DevRev "
  665                                     "0x%02x CR26 0x%02x\n",
  666                                     probe_addrs[i].efer, dev_id, dev_rev, cr26);
  667                         goto cleanup;
  668                 }
  669 
  670                 for (j = 0; j < nitems(wb_devs); j++) {
  671                         if (wb_devs[j].device_id == dev_id) {
  672                                 found = 1;
  673                                 break;
  674                         }
  675                 }
  676 
  677                 if (probe && dev != NULL) {
  678                         snprintf(buf, sizeof(buf),
  679                             "%s (0x%02x/0x%02x) Watchdog Timer",
  680                             found ? wb_devs[j].descr :
  681                              "Unknown Winbond/Nuvoton", dev_id, dev_rev);
  682                         device_set_desc_copy(dev, buf);
  683                 }
  684 
  685                 /* If this is hinted attach, try to guess the model. */
  686                 if (dev != NULL && !found) {
  687                         found = 1;
  688                         j = 0;
  689                 }
  690 
  691 cleanup:
  692                 if (probe || !found) {
  693                         (*probe_addrs[i].ext_cfg_exit_f)(sc, probe_addrs[i].efer);
  694                         if (sc != NULL)
  695                                 (void) bus_release_resource(dev, SYS_RES_IOPORT,
  696                                     sc->rid, sc->portres);
  697                 }
  698 
  699                 /*
  700                  * Stop probing if have successfully identified the SuperIO.
  701                  * Remember the extended function mode enter/exit functions
  702                  * for operations.
  703                  */
  704                 if (found) {
  705                         if (sc != NULL) {
  706                                 sc->ext_cfg_enter_f = probe_addrs[i].ext_cfg_enter_f;
  707                                 sc->ext_cfg_exit_f = probe_addrs[i].ext_cfg_exit_f;
  708                                 sc->chip = wb_devs[j].chip;
  709                                 sc->ctl_reg = 0xf5;
  710                                 sc->time_reg = 0xf6;
  711                                 sc->csr_reg = 0xf7;
  712                                 if (sc->chip == w83697hf ||
  713                                     sc->chip == w83697ug) {
  714                                         sc->ctl_reg = 0xf3;
  715                                         sc->time_reg = 0xf4;
  716                                 } else if (sc->chip == nct6102) {
  717                                         sc->ctl_reg = 0xf0;
  718                                         sc->time_reg = 0xf1;
  719                                         sc->csr_reg = 0xf2;
  720                                 }
  721                         }
  722                         return (BUS_PROBE_SPECIFIC);
  723                 } else
  724                         error = ENXIO;
  725         }
  726 
  727         return (error);
  728 }
  729 
  730 static void
  731 wb_identify(driver_t *driver, device_t parent)
  732 {
  733 
  734         if (device_find_child(parent, driver->name, 0) == NULL) {
  735                 if (wb_probe_enable(NULL, 1) <= 0)
  736                         BUS_ADD_CHILD(parent, 0, driver->name, 0);
  737         }
  738 }
  739 
  740 static int
  741 wb_probe(device_t dev)
  742 {
  743 
  744         /* Make sure we do not claim some ISA PNP device. */
  745         if (isa_get_logicalid(dev) != 0)
  746                 return (ENXIO);
  747 
  748         return (wb_probe_enable(dev, 1));
  749 }
  750 
  751 static int
  752 wb_attach(device_t dev)
  753 {
  754         struct wb_softc *sc;
  755         struct sysctl_ctx_list *sctx;
  756         struct sysctl_oid *soid;
  757         unsigned long timeout;
  758         int error;
  759         uint8_t t;
  760 
  761         error = wb_probe_enable(dev, 0);
  762         if (error > 0)
  763                 return (ENXIO);
  764 
  765         sc = device_get_softc(dev);
  766         KASSERT(sc->ext_cfg_enter_f != NULL && sc->ext_cfg_exit_f != NULL,
  767             ("%s: successful probe result but not setup correctly", __func__));
  768 
  769         /* Watchdog is configured as part of LDN 8 (GPIO Port2, Watchdog). */
  770         write_reg(sc, WB_LDN_REG, WB_LDN_REG_LDN8);
  771 
  772         /* Make sure WDT is enabled. */
  773         write_reg(sc, WB_LDN8_CR30,
  774             read_reg(sc, WB_LDN8_CR30) | WB_LDN8_CR30_ACTIVE);
  775 
  776         switch (sc->chip) {
  777         case w83627hf:
  778         case w83627s:
  779                 t = read_reg(sc, 0x2B) & ~0x10;
  780                 write_reg(sc, 0x2B, t); /* set GPIO24 to WDT0 */
  781                 break;
  782         case w83697hf:
  783                 /* Set pin 119 to WDTO# mode (= CR29, WDT0) */
  784                 t = read_reg(sc, 0x29) & ~0x60;
  785                 t |= 0x20;
  786                 write_reg(sc, 0x29, t);
  787                 break;
  788         case w83697ug:
  789                 /* Set pin 118 to WDTO# mode */
  790                 t = read_reg(sc, 0x2b) & ~0x04;
  791                 write_reg(sc, 0x2b, t);
  792                 break;
  793         case w83627thf:
  794                 t = (read_reg(sc, 0x2B) & ~0x08) | 0x04;
  795                 write_reg(sc, 0x2B, t); /* set GPIO3 to WDT0 */
  796                 break;
  797         case w83627dhg:
  798         case w83627dhg_p:
  799                 t = read_reg(sc, 0x2D) & ~0x01; /* PIN77 -> WDT0# */
  800                 write_reg(sc, 0x2D, t); /* set GPIO5 to WDT0 */
  801                 t = read_reg(sc, sc->ctl_reg);
  802                 t |= 0x02;      /* enable the WDTO# output low pulse
  803                                  * to the KBRST# pin */
  804                 write_reg(sc, sc->ctl_reg, t);
  805                 break;
  806         case w83637hf:
  807                 break;
  808         case w83687thf:
  809                 t = read_reg(sc, 0x2C) & ~0x80; /* PIN47 -> WDT0# */
  810                 write_reg(sc, 0x2C, t);
  811                 break;
  812         case w83627ehf:
  813         case w83627uhg:
  814         case w83667hg:
  815         case w83667hg_b:
  816         case nct6775:
  817         case nct6776:
  818         case nct6779:
  819         case nct6791:
  820         case nct6792:
  821         case nct6793:
  822         case nct6795:
  823         case nct6102:
  824                 /*
  825                  * These chips have a fixed WDTO# output pin (W83627UHG),
  826                  * or support more than one WDTO# output pin.
  827                  * Don't touch its configuration, and hope the BIOS
  828                  * does the right thing.
  829                  */
  830                 t = read_reg(sc, sc->ctl_reg);
  831                 t |= 0x02;      /* enable the WDTO# output low pulse
  832                                  * to the KBRST# pin */
  833                 write_reg(sc, sc->ctl_reg, t);
  834                 break;
  835         default:
  836                 break;
  837         }
  838 
  839         /* Read the current watchdog configuration. */
  840         sc->reg_1 = read_reg(sc, sc->ctl_reg);
  841         sc->reg_timeout = read_reg(sc, sc->time_reg);
  842         sc->reg_2 = read_reg(sc, sc->csr_reg);
  843 
  844         /* Print current state if bootverbose or watchdog already enabled. */
  845         if (bootverbose || (sc->reg_timeout > 0x00))
  846                 wb_print_state(sc, "Before watchdog attach");
  847 
  848         sc->reg_1 &= ~WB_LDN8_CRF5_KEYB_P20;
  849         sc->reg_1 |= WB_LDN8_CRF5_KBRST;
  850         write_reg(sc, sc->ctl_reg, sc->reg_1);
  851 
  852         /*
  853          * Clear a previous watchdog timeout event (if still set).
  854          * Disable timer reset on mouse interrupts.  Leave reset on keyboard,
  855          * since one of my boards is getting stuck in reboot without it.
  856          */
  857         sc->reg_2 &= ~(WB_LDN8_CRF7_MOUSE|WB_LDN8_CRF7_TS);
  858         write_reg(sc, sc->csr_reg, sc->reg_2);
  859 
  860         (*sc->ext_cfg_exit_f)(sc, 0);
  861 
  862         /* Read global timeout override tunable, Add per device sysctls. */
  863         if (TUNABLE_ULONG_FETCH("hw.wbwd.timeout_override", &timeout)) {
  864                 if (timeout > 0)
  865                         sc->timeout_override = timeout;
  866         }
  867         sctx = device_get_sysctl_ctx(dev);
  868         soid = device_get_sysctl_tree(dev);
  869         SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO,
  870             "timeout_override", CTLFLAG_RW, &sc->timeout_override, 0,
  871             "Timeout in seconds overriding default watchdog timeout");
  872         SYSCTL_ADD_INT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO,
  873             "debug_verbose", CTLFLAG_RW, &sc->debug_verbose, 0,
  874             "Enables extra debugging information");
  875         SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "debug",
  876             CTLTYPE_STRING|CTLFLAG_RD, sc, 0, sysctl_wb_debug, "A",
  877             "Selected register information from last change by driver");
  878         SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "debug_current",
  879             CTLTYPE_STRING|CTLFLAG_RD|CTLFLAG_SKIP, sc, 0,
  880              sysctl_wb_debug_current, "A",
  881              "Selected register information (may interfere)");
  882         SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "force_timeout",
  883             CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_SKIP, sc, 0,
  884             sysctl_wb_force_test_nmi, "I", "Enable to force watchdog to fire.");
  885 
  886         /* Register watchdog. */
  887         sc->ev_tag = EVENTHANDLER_REGISTER(watchdog_list, wb_watchdog_fn, sc,
  888             0);
  889 
  890         if (bootverbose)
  891                 wb_print_state(sc, "After watchdog attach");
  892 
  893         return (0);
  894 }
  895 
  896 static int
  897 wb_detach(device_t dev)
  898 {
  899         struct wb_softc *sc;
  900 
  901         sc = device_get_softc(dev);
  902 
  903         /* Unregister and stop the watchdog if running. */
  904         if (sc->ev_tag)
  905                 EVENTHANDLER_DEREGISTER(watchdog_list, sc->ev_tag);
  906         wb_set_watchdog(sc, 0);
  907 
  908         /* Disable extended function mode. */
  909         (*sc->ext_cfg_exit_f)(sc, 0);
  910 
  911         /* Cleanup resources. */
  912         (void) bus_release_resource(dev, SYS_RES_IOPORT, sc->rid, sc->portres);
  913 
  914         /* Bus subroutines take care of sysctls already. */
  915 
  916         return (0);
  917 }
  918 
  919 static device_method_t wb_methods[] = {
  920         /* Device interface */
  921         DEVMETHOD(device_identify,      wb_identify),
  922         DEVMETHOD(device_probe,         wb_probe),
  923         DEVMETHOD(device_attach,        wb_attach),
  924         DEVMETHOD(device_detach,        wb_detach),
  925 
  926         DEVMETHOD_END
  927 };
  928 
  929 static driver_t wb_isa_driver = {
  930         "wbwd",
  931         wb_methods,
  932         sizeof(struct wb_softc)
  933 };
  934 
  935 static devclass_t wb_devclass;
  936 
  937 DRIVER_MODULE(wb, isa, wb_isa_driver, wb_devclass, NULL, NULL);

Cache object: ca3663cf9132fbabc4f26f53074cedee


[ 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.