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/amdsbwd/amdsbwd.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) 2009 Andriy Gapon <avg@FreeBSD.org>
    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 /*
   28  * This is a driver for watchdog timer present in AMD SB600/SB7xx
   29  * south bridges and other watchdog timers advertised via WDRT ACPI table.
   30  * Please see the following specifications for the descriptions of the
   31  * registers and flags:
   32  * - AMD SB600 Register Reference Guide, Public Version,  Rev. 3.03 (SB600 RRG)
   33  *   http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/46155_sb600_rrg_pub_3.03.pdf
   34  * - AMD SB700/710/750 Register Reference Guide (RRG)
   35  *   http://developer.amd.com/assets/43009_sb7xx_rrg_pub_1.00.pdf
   36  * - AMD SB700/710/750 Register Programming Requirements (RPR)
   37  *   http://developer.amd.com/assets/42413_sb7xx_rpr_pub_1.00.pdf
   38  * Please see the following for Watchdog Resource Table specification:
   39  * - Watchdog Timer Hardware Requirements for Windows Server 2003 (WDRT)
   40  *   http://www.microsoft.com/whdc/system/sysinternals/watchdog.mspx
   41  * AMD SB600/SB7xx watchdog hardware seems to conform to the above,
   42  * but my system doesn't provide the table.
   43  */
   44 
   45 #include <sys/cdefs.h>
   46 __FBSDID("$FreeBSD: releng/8.2/sys/dev/amdsbwd/amdsbwd.c 209418 2010-06-22 07:55:30Z avg $");
   47 
   48 #include <sys/param.h>
   49 #include <sys/kernel.h>
   50 #include <sys/module.h>
   51 #include <sys/systm.h>
   52 #include <sys/sysctl.h>
   53 #include <sys/bus.h>
   54 #include <machine/bus.h>
   55 #include <sys/rman.h>
   56 #include <machine/resource.h>
   57 #include <sys/watchdog.h>
   58 
   59 #include <dev/pci/pcivar.h>
   60 #include <isa/isavar.h>
   61 
   62 /* RRG 2.3.3.1.1, page 161. */
   63 #define AMDSB_PMIO_INDEX                0xcd6
   64 #define AMDSB_PMIO_DATA                 (PMIO_INDEX + 1)
   65 #define AMDSB_PMIO_WIDTH                2
   66 /* RRG 2.3.3.2, page 181. */
   67 #define AMDSB_PM_RESET_STATUS0          0x44
   68 #define AMDSB_PM_RESET_STATUS1          0x45
   69 #define         AMDSB_WD_RST_STS        0x02
   70 /* RRG 2.3.3.2, page 188; RPR 2.36, page 30. */
   71 #define AMDSB_PM_WDT_CTRL               0x69
   72 #define         AMDSB_WDT_DISABLE       0x01
   73 #define         AMDSB_WDT_RES_MASK      (0x02 | 0x04)
   74 #define         AMDSB_WDT_RES_32US      0x00
   75 #define         AMDSB_WDT_RES_10MS      0x02
   76 #define         AMDSB_WDT_RES_100MS     0x04
   77 #define         AMDSB_WDT_RES_1S        0x06
   78 #define AMDSB_PM_WDT_BASE_LSB           0x6c
   79 #define AMDSB_PM_WDT_BASE_MSB           0x6f
   80 /* RRG 2.3.4, page 223, WDRT. */
   81 #define AMDSB_WD_CTRL                   0x00
   82 #define         AMDSB_WD_RUN            0x01
   83 #define         AMDSB_WD_FIRED          0x02
   84 #define         AMDSB_WD_SHUTDOWN       0x04
   85 #define         AMDSB_WD_DISABLE        0x08
   86 #define         AMDSB_WD_RESERVED       0x70
   87 #define         AMDSB_WD_RELOAD         0x80
   88 #define AMDSB_WD_COUNT                  0x04
   89 #define         AMDSB_WD_COUNT_MASK     0xffff
   90 #define AMDSB_WDIO_REG_WIDTH            4
   91 /* WDRT */
   92 #define MAXCOUNT_MIN_VALUE              511
   93 /* RRG 2.3.1.1, page 122; SB600 RRG 2.3.1.1, page 97. */
   94 #define AMDSB7xx_SMBUS_DEVID            0x43851002
   95 
   96 #define amdsbwd_verbose_printf(dev, ...)        \
   97         do {                                            \
   98                 if (bootverbose)                        \
   99                         device_printf(dev, __VA_ARGS__);\
  100         } while (0)
  101 
  102 struct amdsbwd_softc {
  103         device_t                dev;
  104         eventhandler_tag        ev_tag;
  105         struct resource         *res_ctrl;
  106         struct resource         *res_count;
  107         int                     rid_ctrl;
  108         int                     rid_count;
  109         int                     ms_per_tick;
  110         int                     max_ticks;
  111         int                     active;
  112         unsigned int            timeout;
  113 };
  114 
  115 static void     amdsbwd_identify(driver_t *driver, device_t parent);
  116 static int      amdsbwd_probe(device_t dev);
  117 static int      amdsbwd_attach(device_t dev);
  118 static int      amdsbwd_detach(device_t dev);
  119 
  120 static device_method_t amdsbwd_methods[] = {
  121         DEVMETHOD(device_identify,      amdsbwd_identify),
  122         DEVMETHOD(device_probe,         amdsbwd_probe),
  123         DEVMETHOD(device_attach,        amdsbwd_attach),
  124         DEVMETHOD(device_detach,        amdsbwd_detach),
  125 #if 0
  126         DEVMETHOD(device_shutdown,      amdsbwd_detach),
  127 #endif
  128         {0, 0}
  129 };
  130 
  131 static devclass_t       amdsbwd_devclass;
  132 static driver_t         amdsbwd_driver = {
  133         "amdsbwd",
  134         amdsbwd_methods,
  135         sizeof(struct amdsbwd_softc)
  136 };
  137 
  138 DRIVER_MODULE(amdsbwd, isa, amdsbwd_driver, amdsbwd_devclass, NULL, NULL);
  139 
  140 
  141 static uint8_t
  142 pmio_read(struct resource *res, uint8_t reg)
  143 {
  144         bus_write_1(res, 0, reg);       /* Index */
  145         return (bus_read_1(res, 1));    /* Data */
  146 }
  147 
  148 static void
  149 pmio_write(struct resource *res, uint8_t reg, uint8_t val)
  150 {
  151         bus_write_1(res, 0, reg);       /* Index */
  152         bus_write_1(res, 1, val);       /* Data */
  153 }
  154 
  155 static uint32_t
  156 wdctrl_read(struct amdsbwd_softc *sc)
  157 {
  158         return (bus_read_4(sc->res_ctrl, 0));
  159 }
  160 
  161 static void
  162 wdctrl_write(struct amdsbwd_softc *sc, uint32_t val)
  163 {
  164         bus_write_4(sc->res_ctrl, 0, val);
  165 }
  166 
  167 static __unused uint32_t
  168 wdcount_read(struct amdsbwd_softc *sc)
  169 {
  170         return (bus_read_4(sc->res_count, 0));
  171 }
  172 
  173 static void
  174 wdcount_write(struct amdsbwd_softc *sc, uint32_t val)
  175 {
  176         bus_write_4(sc->res_count, 0, val);
  177 }
  178 
  179 static void
  180 amdsbwd_tmr_enable(struct amdsbwd_softc *sc)
  181 {
  182         uint32_t val;
  183 
  184         val = wdctrl_read(sc);
  185         val |= AMDSB_WD_RUN;
  186         wdctrl_write(sc, val);
  187         sc->active = 1;
  188         amdsbwd_verbose_printf(sc->dev, "timer enabled\n");
  189 }
  190 
  191 static void
  192 amdsbwd_tmr_disable(struct amdsbwd_softc *sc)
  193 {
  194         uint32_t val;
  195 
  196         val = wdctrl_read(sc);
  197         val &= ~AMDSB_WD_RUN;
  198         wdctrl_write(sc, val);
  199         sc->active = 0;
  200         amdsbwd_verbose_printf(sc->dev, "timer disabled\n");
  201 }
  202 
  203 static void
  204 amdsbwd_tmr_reload(struct amdsbwd_softc *sc)
  205 {
  206         uint32_t val;
  207 
  208         val = wdctrl_read(sc);
  209         val |= AMDSB_WD_RELOAD;
  210         wdctrl_write(sc, val);
  211 }
  212 
  213 static void
  214 amdsbwd_tmr_set(struct amdsbwd_softc *sc, uint16_t timeout)
  215 {
  216 
  217         timeout &= AMDSB_WD_COUNT_MASK;
  218         wdcount_write(sc, timeout);
  219         sc->timeout = timeout;
  220         amdsbwd_verbose_printf(sc->dev, "timeout set to %u ticks\n", timeout);
  221 }
  222 
  223 static void
  224 amdsbwd_event(void *arg, unsigned int cmd, int *error)
  225 {
  226         struct amdsbwd_softc *sc = arg;
  227         unsigned int timeout;
  228 
  229         /* convert from power-of-two-ns to WDT ticks */
  230         cmd &= WD_INTERVAL;
  231         if (cmd < WD_TO_1SEC)
  232                 cmd = 0;
  233         if (cmd) {
  234                 timeout = ((uint64_t)1 << (cmd - WD_TO_1MS)) / sc->ms_per_tick;
  235                 if (timeout > sc->max_ticks)
  236                         timeout = sc->max_ticks;
  237                 if (timeout != sc->timeout) {
  238                         amdsbwd_tmr_set(sc, timeout);
  239                         if (!sc->active)
  240                                 amdsbwd_tmr_enable(sc);
  241                 }
  242                 amdsbwd_tmr_reload(sc);
  243                 *error = 0;
  244         } else {
  245                 if (sc->active)
  246                         amdsbwd_tmr_disable(sc);
  247         }
  248 }
  249 
  250 static void
  251 amdsbwd_identify(driver_t *driver, device_t parent)
  252 {
  253         device_t                child;
  254         device_t                smb_dev;
  255 
  256         if (resource_disabled("amdsbwd", 0))
  257                 return;
  258         if (device_find_child(parent, "amdsbwd", -1) != NULL)
  259                 return;
  260 
  261         /*
  262          * Try to identify SB600/SB7xx by PCI Device ID of SMBus device
  263          * that should be present at bus 0, device 20, function 0.
  264          */
  265         smb_dev = pci_find_bsf(0, 20, 0);
  266         if (smb_dev == NULL)
  267                 return;
  268         if (pci_get_devid(smb_dev) != AMDSB7xx_SMBUS_DEVID)
  269                 return;
  270 
  271         child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "amdsbwd", -1);
  272         if (child == NULL)
  273                 device_printf(parent, "add amdsbwd child failed\n");
  274 }
  275 
  276 static int
  277 amdsbwd_probe(device_t dev)
  278 {
  279         struct resource         *res;
  280         uint32_t                addr;
  281         uint32_t                val;
  282         int                     rid;
  283         int                     rc;
  284         int                     i;
  285 
  286         /* Do not claim some ISA PnP device by accident. */
  287         if (isa_get_logicalid(dev) != 0)
  288                 return (ENXIO);
  289 
  290         rc = bus_set_resource(dev, SYS_RES_IOPORT, 0, AMDSB_PMIO_INDEX,
  291             AMDSB_PMIO_WIDTH);
  292         if (rc != 0) {
  293                 device_printf(dev, "bus_set_resource for IO failed\n");
  294                 return (ENXIO);
  295         }
  296         rid = 0;
  297         res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0ul, ~0ul,
  298             AMDSB_PMIO_WIDTH, RF_ACTIVE | RF_SHAREABLE);
  299         if (res == NULL) {
  300                 device_printf(dev, "bus_alloc_resource for IO failed\n");
  301                 return (ENXIO);
  302         }
  303 
  304         /* Report cause of previous reset for user's convenience. */
  305         val = pmio_read(res, AMDSB_PM_RESET_STATUS0);
  306         if (val != 0)
  307                 amdsbwd_verbose_printf(dev, "ResetStatus0 = %#04x\n", val);
  308         val = pmio_read(res, AMDSB_PM_RESET_STATUS1);
  309         if (val != 0)
  310                 amdsbwd_verbose_printf(dev, "ResetStatus1 = %#04x\n", val);
  311         if ((val & AMDSB_WD_RST_STS) != 0)
  312                 device_printf(dev, "Previous Reset was caused by Watchdog\n");
  313 
  314         /* Find base address of memory mapped WDT registers. */
  315         for (addr = 0, i = 0; i < 4; i++) {
  316                 addr <<= 8;
  317                 addr |= pmio_read(res, AMDSB_PM_WDT_BASE_MSB - i);
  318         }
  319         amdsbwd_verbose_printf(dev, "memory base address = %#010x\n", addr);
  320         rc = bus_set_resource(dev, SYS_RES_MEMORY, 0, addr + AMDSB_WD_CTRL,
  321             AMDSB_WDIO_REG_WIDTH);
  322         if (rc != 0) {
  323                 device_printf(dev, "bus_set_resource for control failed\n");
  324                 return (ENXIO);
  325         }
  326         rc = bus_set_resource(dev, SYS_RES_MEMORY, 1, addr + AMDSB_WD_COUNT,
  327             AMDSB_WDIO_REG_WIDTH);
  328         if (rc != 0) {
  329                 device_printf(dev, "bus_set_resource for count failed\n");
  330                 return (ENXIO);
  331         }
  332 
  333         /* Set watchdog timer tick to 10ms. */
  334         val = pmio_read(res, AMDSB_PM_WDT_CTRL);
  335         val &= ~AMDSB_WDT_RES_MASK;
  336         val |= AMDSB_WDT_RES_10MS;
  337         pmio_write(res, AMDSB_PM_WDT_CTRL, val);
  338 
  339         /* Enable watchdog device (in stopped state). */
  340         val = pmio_read(res, AMDSB_PM_WDT_CTRL);
  341         val &= ~AMDSB_WDT_DISABLE;
  342         pmio_write(res, AMDSB_PM_WDT_CTRL, val);
  343 
  344         /*
  345          * XXX TODO: Ensure that watchdog decode is enabled
  346          * (register 0x41, bit 3).
  347          */
  348         bus_release_resource(dev, SYS_RES_IOPORT, rid, res);
  349         bus_delete_resource(dev, SYS_RES_IOPORT, rid);
  350 
  351         device_set_desc(dev, "AMD SB600/SB7xx Watchdog Timer");
  352         return (0);
  353 }
  354 
  355 static int
  356 amdsbwd_attach_sb(device_t dev, struct amdsbwd_softc *sc)
  357 {
  358         sc->max_ticks = UINT16_MAX;
  359         sc->ms_per_tick = 10;
  360         sc->rid_ctrl = 0;
  361         sc->rid_count = 1;
  362 
  363         sc->res_ctrl = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
  364             &sc->rid_ctrl, RF_ACTIVE);
  365         if (sc->res_ctrl == NULL) {
  366                 device_printf(dev, "bus_alloc_resource for ctrl failed\n");
  367                 return (ENXIO);
  368         }
  369         sc->res_count = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
  370             &sc->rid_count, RF_ACTIVE);
  371         if (sc->res_count == NULL) {
  372                 device_printf(dev, "bus_alloc_resource for count failed\n");
  373                 return (ENXIO);
  374         }
  375         return (0);
  376 }
  377 
  378 static int
  379 amdsbwd_attach(device_t dev)
  380 {
  381         struct amdsbwd_softc    *sc;
  382         int                     rc;
  383 
  384         sc = device_get_softc(dev);
  385         sc->dev = dev;
  386 
  387         rc = amdsbwd_attach_sb(dev, sc);
  388         if (rc != 0)
  389                 goto fail;
  390 
  391         /* Setup initial state of Watchdog Control. */
  392         wdctrl_write(sc, AMDSB_WD_FIRED);
  393 
  394         if (wdctrl_read(sc) & AMDSB_WD_DISABLE) {
  395                 device_printf(dev, "watchdog hardware is disabled\n");
  396                 goto fail;
  397         }
  398 
  399         sc->ev_tag = EVENTHANDLER_REGISTER(watchdog_list, amdsbwd_event, sc,
  400             EVENTHANDLER_PRI_ANY);
  401 
  402         return (0);
  403 
  404 fail:
  405         amdsbwd_detach(dev);
  406         return (ENXIO);
  407 }
  408 
  409 static int
  410 amdsbwd_detach(device_t dev)
  411 {
  412         struct amdsbwd_softc *sc;
  413 
  414         sc = device_get_softc(dev);
  415         if (sc->ev_tag != NULL)
  416                 EVENTHANDLER_DEREGISTER(watchdog_list, sc->ev_tag);
  417 
  418         if (sc->active)
  419                 amdsbwd_tmr_disable(sc);
  420 
  421         if (sc->res_ctrl != NULL)
  422                 bus_release_resource(dev, SYS_RES_MEMORY, sc->rid_ctrl,
  423                     sc->res_ctrl);
  424 
  425         if (sc->res_count != NULL)
  426                 bus_release_resource(dev, SYS_RES_MEMORY, sc->rid_count,
  427                     sc->res_count);
  428 
  429         return (0);
  430 }
  431 

Cache object: cb78ce0fbbc0a6219418ff2b8b3358c8


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