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/ichwd/ichwd.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) 2004 Texas A&M University
    3  * All rights reserved.
    4  *
    5  * Developer: Wm. Daryl Hawkins
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   26  * SUCH DAMAGE.
   27  */
   28 
   29 /*
   30  * Intel ICH Watchdog Timer (WDT) driver
   31  *
   32  * Originally developed by Wm. Daryl Hawkins of Texas A&M
   33  * Heavily modified by <des@FreeBSD.org>
   34  *
   35  * This is a tricky one.  The ICH WDT can't be treated as a regular PCI
   36  * device as it's actually an integrated function of the ICH LPC interface
   37  * bridge.  Detection is also awkward, because we can only infer the
   38  * presence of the watchdog timer from the fact that the machine has an
   39  * ICH chipset, or, on ACPI 2.x systems, by the presence of the 'WDDT'
   40  * ACPI table (although this driver does not support the ACPI detection
   41  * method).
   42  *
   43  * There is one slight problem on non-ACPI or ACPI 1.x systems: we have no
   44  * way of knowing if the WDT is permanently disabled (either by the BIOS
   45  * or in hardware).
   46  *
   47  * The WDT is programmed through I/O registers in the ACPI I/O space.
   48  * Intel swears it's always at offset 0x60, so we use that.
   49  *
   50  * For details about the ICH WDT, see Intel Application Note AP-725
   51  * (document no. 292273-001).  The WDT is also described in the individual
   52  * chipset datasheets, e.g. Intel82801EB ICH5 / 82801ER ICH5R Datasheet
   53  * (document no. 252516-001) sections 9.10 and 9.11.
   54  */
   55 
   56 #include <sys/cdefs.h>
   57 __FBSDID("$FreeBSD: releng/5.3/sys/dev/ichwd/ichwd.c 130402 2004-06-13 05:00:19Z jmg $");
   58 
   59 #include <sys/param.h>
   60 #include <sys/kernel.h>
   61 #include <sys/module.h>
   62 #include <sys/systm.h>
   63 #include <sys/bus.h>
   64 #include <machine/bus.h>
   65 #include <sys/rman.h>
   66 #include <machine/resource.h>
   67 #include <sys/watchdog.h>
   68 
   69 #include <dev/pci/pcivar.h>
   70 
   71 #include <dev/ichwd/ichwd.h>
   72 
   73 static struct ichwd_device ichwd_devices[] = {
   74         { VENDORID_INTEL, DEVICEID_82801AA, "Intel 82801AA watchdog timer" },
   75         { VENDORID_INTEL, DEVICEID_82801AB, "Intel 82801AB watchdog timer" },
   76         { VENDORID_INTEL, DEVICEID_82801BA, "Intel 82801BA watchdog timer" },
   77         { VENDORID_INTEL, DEVICEID_82801BAM, "Intel 82801BAM watchdog timer" },
   78         { VENDORID_INTEL, DEVICEID_82801CA, "Intel 82801CA watchdog timer" },
   79         { VENDORID_INTEL, DEVICEID_82801CAM, "Intel 82801CAM watchdog timer" },
   80         { VENDORID_INTEL, DEVICEID_82801DB, "Intel 82801DB watchdog timer" },
   81         { VENDORID_INTEL, DEVICEID_82801DBM, "Intel 82801DBM watchdog timer" },
   82         { VENDORID_INTEL, DEVICEID_82801E, "Intel 82801E watchdog timer" },
   83         { VENDORID_INTEL, DEVICEID_82801EBR, "Intel 82801EB/ER watchdog timer" },
   84         { 0, 0, NULL },
   85 };
   86 
   87 static devclass_t ichwd_devclass;
   88 
   89 #define ichwd_read_1(sc, off) \
   90         bus_space_read_1((sc)->smi_bst, (sc)->smi_bsh, (off))
   91 #define ichwd_read_2(sc, off) \
   92         bus_space_read_2((sc)->smi_bst, (sc)->smi_bsh, (off))
   93 #define ichwd_read_4(sc, off) \
   94         bus_space_read_4((sc)->smi_bst, (sc)->smi_bsh, (off))
   95 
   96 #define ichwd_write_1(sc, off, val) \
   97         bus_space_write_1((sc)->smi_bst, (sc)->smi_bsh, (off), (val))
   98 #define ichwd_write_2(sc, off, val) \
   99         bus_space_write_2((sc)->smi_bst, (sc)->smi_bsh, (off), (val))
  100 #define ichwd_write_4(sc, off, val) \
  101         bus_space_write_4((sc)->smi_bst, (sc)->smi_bsh, (off), (val))
  102 
  103 static __inline void
  104 ichwd_intr_enable(struct ichwd_softc *sc)
  105 {
  106         ichwd_write_4(sc, SMI_EN, ichwd_read_4(sc, SMI_EN) | SMI_TCO_EN);
  107 }
  108 
  109 static __inline void
  110 ichwd_intr_disable(struct ichwd_softc *sc)
  111 {
  112         ichwd_write_4(sc, SMI_EN, ichwd_read_4(sc, SMI_EN) & ~SMI_TCO_EN);
  113 }
  114 
  115 static __inline void
  116 ichwd_sts_reset(struct ichwd_softc *sc)
  117 {
  118         ichwd_write_2(sc, TCO1_STS, TCO_TIMEOUT);
  119         ichwd_write_2(sc, TCO2_STS, TCO_BOOT_STS);
  120         ichwd_write_2(sc, TCO2_STS, TCO_SECOND_TO_STS);
  121 }
  122 
  123 static __inline void
  124 ichwd_tmr_enable(struct ichwd_softc *sc)
  125 {
  126         uint16_t cnt;
  127 
  128         cnt = ichwd_read_2(sc, TCO1_CNT) & TCO_CNT_PRESERVE;
  129         ichwd_write_2(sc, TCO1_CNT, cnt & ~TCO_TMR_HALT);
  130         sc->active = 1;
  131         if (bootverbose)
  132                 device_printf(sc->device, "timer enabled\n");
  133 }
  134 
  135 static __inline void
  136 ichwd_tmr_disable(struct ichwd_softc *sc)
  137 {
  138         uint16_t cnt;
  139 
  140         cnt = ichwd_read_2(sc, TCO1_CNT) & TCO_CNT_PRESERVE;
  141         ichwd_write_2(sc, TCO1_CNT, cnt | TCO_TMR_HALT);
  142         sc->active = 0;
  143         if (bootverbose)
  144                 device_printf(sc->device, "timer disabled\n");
  145 }
  146 
  147 static __inline void
  148 ichwd_tmr_reload(struct ichwd_softc *sc)
  149 {
  150         ichwd_write_1(sc, TCO_RLD, 1);
  151         if (bootverbose)
  152                 device_printf(sc->device, "timer reloaded\n");
  153 }
  154 
  155 static __inline void
  156 ichwd_tmr_set(struct ichwd_softc *sc, uint8_t timeout)
  157 {
  158         ichwd_write_1(sc, TCO_TMR, timeout);
  159         sc->timeout = timeout;
  160         if (bootverbose)
  161                 device_printf(sc->device, "timeout set to %u ticks\n", timeout);
  162 }
  163 
  164 /*
  165  * Watchdog event handler.
  166  */
  167 static void
  168 ichwd_event(void *arg, unsigned int cmd, int *error)
  169 {
  170         struct ichwd_softc *sc = arg;
  171         unsigned int timeout;
  172 
  173         cmd &= WD_INTERVAL;
  174 
  175         /* disable / enable */
  176         if (cmd == 0) {
  177                 if (sc->active)
  178                         ichwd_tmr_disable(sc);
  179                 *error = 0;
  180                 return;
  181         }
  182         if (!sc->active)
  183                 ichwd_tmr_enable(sc);
  184 
  185         /* convert from power-of-to-ns to WDT ticks */
  186         if (cmd >= 64) {
  187                 *error = EINVAL;
  188                 return;
  189         }
  190         timeout = ((uint64_t)1 << cmd) / ICHWD_TICK;
  191         if (timeout < ICHWD_MIN_TIMEOUT || timeout > ICHWD_MAX_TIMEOUT) {
  192                 *error = EINVAL;
  193                 return;
  194         }
  195 
  196         /* set new initial value */
  197         if (timeout != sc->timeout)
  198                 ichwd_tmr_set(sc, timeout);
  199 
  200         /* reload */
  201         ichwd_tmr_reload(sc);
  202 
  203         *error = 0;
  204         return;
  205 }
  206 
  207 static unsigned long pmbase;
  208 
  209 /*
  210  * Look for an ICH LPC interface bridge.  If one is found, register an
  211  * ichwd device.  There can be only one.
  212  */
  213 static void
  214 ichwd_identify(driver_t *driver, device_t parent)
  215 {
  216         struct ichwd_device *id;
  217         device_t ich = NULL;
  218         device_t dev;
  219 
  220         /* look for an ICH LPC interface bridge */
  221         for (id = ichwd_devices; id->desc != NULL; ++id)
  222                 if ((ich = pci_find_device(id->vendor, id->device)) != NULL)
  223                         break;
  224         if (ich == NULL)
  225                 return;
  226 
  227         if (bootverbose)
  228                 printf("%s(): found ICH chipset: %s\n", __func__, id->desc);
  229 
  230         /* get for ACPI base address */
  231         pmbase = pci_read_config(ich, ICH_PMBASE, 2) & ICH_PMBASE_MASK;
  232         if (pmbase == 0) {
  233                 if (bootverbose)
  234                         printf("%s(): ICH PMBASE register is empty\n",
  235                             __func__);
  236                 return;
  237         }
  238 
  239         /* try to clear the NO_REBOOT bit */
  240         pci_write_config(ich, ICH_GEN_STA, 0x00, 1);
  241         if (pci_read_config(ich, ICH_GEN_STA, 1) & ICH_GEN_STA_NO_REBOOT) {
  242                 if (bootverbose)
  243                         printf("%s(): ICH WDT present but disabled\n",
  244                             __func__);
  245                 return;
  246         }
  247 
  248         /* good, add child to bus */
  249         if ((dev = device_find_child(parent, driver->name, 0)) == NULL)
  250                 dev = BUS_ADD_CHILD(parent, 0, driver->name, 0);
  251 
  252         if (dev != NULL)
  253                 device_set_desc_copy(dev, id->desc);
  254 }
  255 
  256 static int
  257 ichwd_probe(device_t dev)
  258 {
  259         (void)dev;
  260         return (0);
  261 }
  262 
  263 static int
  264 ichwd_attach(device_t dev)
  265 {
  266         struct ichwd_softc *sc;
  267 
  268         sc = device_get_softc(dev);
  269         sc->device = dev;
  270 
  271         /* allocate I/O register space */
  272         sc->smi_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->smi_rid,
  273             pmbase + SMI_BASE, pmbase + SMI_BASE + SMI_LEN - 1, SMI_LEN,
  274             RF_ACTIVE|RF_SHAREABLE);
  275         if (sc->smi_res == NULL) {
  276                 device_printf(dev, "unable to reserve SMI registers\n");
  277                 goto fail;
  278         }
  279         sc->smi_bst = rman_get_bustag(sc->smi_res);
  280         sc->smi_bsh = rman_get_bushandle(sc->smi_res);
  281         sc->tco_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->tco_rid,
  282             pmbase + TCO_BASE, pmbase + TCO_BASE + TCO_LEN - 1, TCO_LEN,
  283             RF_ACTIVE|RF_SHAREABLE);
  284         if (sc->tco_res == NULL) {
  285                 device_printf(dev, "unable to reserve TCO registers\n");
  286                 goto fail;
  287         }
  288         sc->tco_bst = rman_get_bustag(sc->tco_res);
  289         sc->tco_bsh = rman_get_bushandle(sc->tco_res);
  290 
  291         /* reset the watchdog status registers */
  292         ichwd_sts_reset(sc);
  293 
  294         /* make sure the WDT starts out inactive */
  295         ichwd_tmr_disable(sc);
  296 
  297         /* register the watchdog event handler */
  298         sc->ev_tag = EVENTHANDLER_REGISTER(watchdog_list, ichwd_event, sc, 0);
  299 
  300         /* enable watchdog timeout interrupts */
  301         ichwd_intr_enable(sc);
  302 
  303         return (0);
  304  fail:
  305         sc = device_get_softc(dev);
  306         if (sc->tco_res != NULL)
  307                 bus_release_resource(dev, SYS_RES_IOPORT,
  308                     sc->tco_rid, sc->tco_res);
  309         if (sc->smi_res != NULL)
  310                 bus_release_resource(dev, SYS_RES_IOPORT,
  311                     sc->smi_rid, sc->smi_res);
  312         return (ENXIO);
  313 }
  314 
  315 static int
  316 ichwd_detach(device_t dev)
  317 {
  318         struct ichwd_softc *sc;
  319 
  320         device_printf(dev, "detaching\n");
  321 
  322         sc = device_get_softc(dev);
  323 
  324         /* halt the watchdog timer */
  325         if (sc->active)
  326                 ichwd_tmr_disable(sc);
  327 
  328         /* disable watchdog timeout interrupts */
  329         ichwd_intr_disable(sc);
  330 
  331         /* deregister event handler */
  332         if (sc->ev_tag != NULL)
  333                 EVENTHANDLER_DEREGISTER(watchdog_list, sc->ev_tag);
  334         sc->ev_tag = NULL;
  335 
  336         /* reset the watchdog status registers */
  337         ichwd_sts_reset(sc);
  338 
  339         /* deallocate I/O register space */
  340         bus_release_resource(dev, SYS_RES_IOPORT, sc->tco_rid, sc->tco_res);
  341         bus_release_resource(dev, SYS_RES_IOPORT, sc->smi_rid, sc->smi_res);
  342 
  343         return (0);
  344 }
  345 
  346 static device_method_t ichwd_methods[] = {
  347         DEVMETHOD(device_identify, ichwd_identify),
  348         DEVMETHOD(device_probe, ichwd_probe),
  349         DEVMETHOD(device_attach, ichwd_attach),
  350         DEVMETHOD(device_detach, ichwd_detach),
  351         {0,0}
  352 };
  353 
  354 static driver_t ichwd_driver = {
  355         "ichwd",
  356         ichwd_methods,
  357         sizeof(struct ichwd_softc),
  358 };
  359 
  360 static int
  361 ichwd_modevent(module_t mode, int type, void *data)
  362 {
  363         int error = 0;
  364 
  365         switch (type) {
  366         case MOD_LOAD:
  367                 printf("ichwd module loaded\n");
  368                 break;
  369         case MOD_UNLOAD:
  370                 printf("ichwd module unloaded\n");
  371                 break;
  372         case MOD_SHUTDOWN:
  373                 printf("ichwd module shutting down\n");
  374                 break;
  375         }
  376         return (error);
  377 }
  378 
  379 DRIVER_MODULE(ichwd, nexus, ichwd_driver, ichwd_devclass, ichwd_modevent, NULL);
  380 /*
  381  * this doesn't seem to work, though I can't figure out why.
  382  * currently not a big issue since watchdog is standard.
  383 MODULE_DEPEND(ichwd, watchdog, 1, 1, 1);
  384  */

Cache object: e4f0804647f4d62e2121671ec54f9f33


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