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/superio/superio.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  * SPDX-License-Identifier: BSD-2-Clause
    3  *
    4  * Copyright (c) 2019 Andriy Gapon
    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  * $FreeBSD$
   28  */
   29 
   30 #include <sys/cdefs.h>
   31 __FBSDID("$FreeBSD$");
   32 
   33 #include <sys/param.h>
   34 #include <sys/systm.h>
   35 #include <sys/bus.h>
   36 #include <sys/conf.h>
   37 #include <sys/kernel.h>
   38 #include <sys/lock.h>
   39 #include <sys/mutex.h>
   40 #include <sys/malloc.h>
   41 #include <sys/module.h>
   42 #include <sys/proc.h>
   43 #include <sys/rman.h>
   44 #include <sys/sbuf.h>
   45 #include <sys/time.h>
   46 
   47 #include <machine/bus.h>
   48 #include <machine/resource.h>
   49 #include <machine/stdarg.h>
   50 
   51 #include <isa/isavar.h>
   52 
   53 #include <dev/superio/superio.h>
   54 #include <dev/superio/superio_io.h>
   55 
   56 #include "isa_if.h"
   57 
   58 typedef void (*sio_conf_enter_f)(struct resource*, uint16_t);
   59 typedef void (*sio_conf_exit_f)(struct resource*, uint16_t);
   60 
   61 struct sio_conf_methods {
   62         sio_conf_enter_f        enter;
   63         sio_conf_exit_f         exit;
   64         superio_vendor_t        vendor;
   65 };
   66 
   67 struct sio_device {
   68         uint8_t                 ldn;
   69         superio_dev_type_t      type;
   70 };
   71 
   72 struct superio_devinfo {
   73         STAILQ_ENTRY(superio_devinfo) link;
   74         struct resource_list    resources;
   75         device_t                dev;
   76         uint8_t                 ldn;
   77         superio_dev_type_t      type;
   78         uint16_t                iobase;
   79         uint16_t                iobase2;
   80         uint8_t                 irq;
   81         uint8_t                 dma;
   82 };
   83 
   84 struct siosc {
   85         struct mtx                      conf_lock;
   86         STAILQ_HEAD(, superio_devinfo)  devlist;
   87         struct resource*                io_res;
   88         struct cdev                     *chardev;
   89         int                             io_rid;
   90         uint16_t                        io_port;
   91         const struct sio_conf_methods   *methods;
   92         const struct sio_device         *known_devices;
   93         superio_vendor_t                vendor;
   94         uint16_t                        devid;
   95         uint8_t                         revid;
   96         uint8_t                         current_ldn;
   97         uint8_t                         ldn_reg;
   98         uint8_t                         enable_reg;
   99 };
  100 
  101 static  d_ioctl_t       superio_ioctl;
  102 
  103 static struct cdevsw superio_cdevsw = {
  104         .d_version =    D_VERSION,
  105         .d_ioctl =      superio_ioctl,
  106         .d_name =       "superio",
  107 };
  108 
  109 #define NUMPORTS        2
  110 
  111 static uint8_t
  112 sio_read(struct resource* res, uint8_t reg)
  113 {
  114         bus_write_1(res, 0, reg);
  115         return (bus_read_1(res, 1));
  116 }
  117 
  118 /* Read a word from two one-byte registers, big endian. */
  119 static uint16_t
  120 sio_readw(struct resource* res, uint8_t reg)
  121 {
  122         uint16_t v;
  123 
  124         v = sio_read(res, reg);
  125         v <<= 8;
  126         v |= sio_read(res, reg + 1);
  127         return (v);
  128 }
  129 
  130 static void
  131 sio_write(struct resource* res, uint8_t reg, uint8_t val)
  132 {
  133         bus_write_1(res, 0, reg);
  134         bus_write_1(res, 1, val);
  135 }
  136 
  137 static void
  138 sio_ldn_select(struct siosc *sc, uint8_t ldn)
  139 {
  140         mtx_assert(&sc->conf_lock, MA_OWNED);
  141         if (ldn == sc->current_ldn)
  142                 return;
  143         sio_write(sc->io_res, sc->ldn_reg, ldn);
  144         sc->current_ldn = ldn;
  145 }
  146 
  147 static uint8_t
  148 sio_ldn_read(struct siosc *sc, uint8_t ldn, uint8_t reg)
  149 {
  150         mtx_assert(&sc->conf_lock, MA_OWNED);
  151         if (reg >= sc->enable_reg) {
  152                 sio_ldn_select(sc, ldn);
  153                 KASSERT(sc->current_ldn == ldn, ("sio_ldn_select failed"));
  154         }
  155         return (sio_read(sc->io_res, reg));
  156 }
  157 
  158 static uint16_t
  159 sio_ldn_readw(struct siosc *sc, uint8_t ldn, uint8_t reg)
  160 {
  161         mtx_assert(&sc->conf_lock, MA_OWNED);
  162         if (reg >= sc->enable_reg) {
  163                 sio_ldn_select(sc, ldn);
  164                 KASSERT(sc->current_ldn == ldn, ("sio_ldn_select failed"));
  165         }
  166         return (sio_readw(sc->io_res, reg));
  167 }
  168 
  169 static void
  170 sio_ldn_write(struct siosc *sc, uint8_t ldn, uint8_t reg, uint8_t val)
  171 {
  172         mtx_assert(&sc->conf_lock, MA_OWNED);
  173         if (reg <= sc->ldn_reg) {
  174                 printf("ignored attempt to write special register 0x%x\n", reg);
  175                 return;
  176         }
  177         sio_ldn_select(sc, ldn);
  178         KASSERT(sc->current_ldn == ldn, ("sio_ldn_select failed"));
  179         sio_write(sc->io_res, reg, val);
  180 }
  181 
  182 static void
  183 sio_conf_enter(struct siosc *sc)
  184 {
  185         mtx_lock(&sc->conf_lock);
  186         sc->methods->enter(sc->io_res, sc->io_port);
  187 }
  188 
  189 static void
  190 sio_conf_exit(struct siosc *sc)
  191 {
  192         sc->methods->exit(sc->io_res, sc->io_port);
  193         sc->current_ldn = 0xff;
  194         mtx_unlock(&sc->conf_lock);
  195 }
  196 
  197 static void
  198 ite_conf_enter(struct resource* res, uint16_t port)
  199 {
  200         bus_write_1(res, 0, 0x87);
  201         bus_write_1(res, 0, 0x01);
  202         bus_write_1(res, 0, 0x55);
  203         bus_write_1(res, 0, port == 0x2e ? 0x55 : 0xaa);
  204 }
  205 
  206 static void
  207 ite_conf_exit(struct resource* res, uint16_t port)
  208 {
  209         sio_write(res, 0x02, 0x02);
  210 }
  211 
  212 static const struct sio_conf_methods ite_conf_methods = {
  213         .enter = ite_conf_enter,
  214         .exit = ite_conf_exit,
  215         .vendor = SUPERIO_VENDOR_ITE
  216 };
  217 
  218 static void
  219 nvt_conf_enter(struct resource* res, uint16_t port)
  220 {
  221         bus_write_1(res, 0, 0x87);
  222         bus_write_1(res, 0, 0x87);
  223 }
  224 
  225 static void
  226 nvt_conf_exit(struct resource* res, uint16_t port)
  227 {
  228         bus_write_1(res, 0, 0xaa);
  229 }
  230 
  231 static const struct sio_conf_methods nvt_conf_methods = {
  232         .enter = nvt_conf_enter,
  233         .exit = nvt_conf_exit,
  234         .vendor = SUPERIO_VENDOR_NUVOTON
  235 };
  236 
  237 static void
  238 fintek_conf_enter(struct resource* res, uint16_t port)
  239 {
  240         bus_write_1(res, 0, 0x87);
  241         bus_write_1(res, 0, 0x87);
  242 }
  243 
  244 static void
  245 fintek_conf_exit(struct resource* res, uint16_t port)
  246 {
  247         bus_write_1(res, 0, 0xaa);
  248 }
  249 
  250 static const struct sio_conf_methods fintek_conf_methods = {
  251         .enter = fintek_conf_enter,
  252         .exit = fintek_conf_exit,
  253         .vendor = SUPERIO_VENDOR_FINTEK
  254 };
  255 
  256 static const struct sio_conf_methods * const methods_table[] = {
  257         &ite_conf_methods,
  258         &nvt_conf_methods,
  259         &fintek_conf_methods,
  260         NULL
  261 };
  262 
  263 static const uint16_t ports_table[] = {
  264         0x2e, 0x4e, 0
  265 };
  266 
  267 const struct sio_device ite_devices[] = {
  268         { .ldn = 4, .type = SUPERIO_DEV_HWM },
  269         { .ldn = 7, .type = SUPERIO_DEV_WDT },
  270         { .type = SUPERIO_DEV_NONE },
  271 };
  272 
  273 const struct sio_device nvt_devices[] = {
  274         { .ldn = 8, .type = SUPERIO_DEV_WDT },
  275         { .type = SUPERIO_DEV_NONE },
  276 };
  277 
  278 const struct sio_device nct5104_devices[] = {
  279         { .ldn = 7, .type = SUPERIO_DEV_GPIO },
  280         { .ldn = 8, .type = SUPERIO_DEV_WDT },
  281         { .ldn = 15, .type = SUPERIO_DEV_GPIO },
  282         { .type = SUPERIO_DEV_NONE },
  283 };
  284 
  285 const struct sio_device fintek_devices[] = {
  286         { .ldn = 7, .type = SUPERIO_DEV_WDT },
  287         { .type = SUPERIO_DEV_NONE },
  288 };
  289 
  290 static const struct {
  291         superio_vendor_t        vendor;
  292         uint16_t                devid;
  293         uint16_t                mask;
  294         const char              *descr;
  295         const struct sio_device *devices;
  296 } superio_table[] = {
  297         {
  298                 .vendor = SUPERIO_VENDOR_ITE, .devid = 0x8712,
  299                 .devices = ite_devices,
  300         },
  301         {
  302                 .vendor = SUPERIO_VENDOR_ITE, .devid = 0x8716,
  303                 .devices = ite_devices,
  304         },
  305         {
  306                 .vendor = SUPERIO_VENDOR_ITE, .devid = 0x8718,
  307                 .devices = ite_devices,
  308         },
  309         {
  310                 .vendor = SUPERIO_VENDOR_ITE, .devid = 0x8720,
  311                 .devices = ite_devices,
  312         },
  313         {
  314                 .vendor = SUPERIO_VENDOR_ITE, .devid = 0x8721,
  315                 .devices = ite_devices,
  316         },
  317         {
  318                 .vendor = SUPERIO_VENDOR_ITE, .devid = 0x8726,
  319                 .devices = ite_devices,
  320         },
  321         {
  322                 .vendor = SUPERIO_VENDOR_ITE, .devid = 0x8728,
  323                 .devices = ite_devices,
  324         },
  325         {
  326                 .vendor = SUPERIO_VENDOR_ITE, .devid = 0x8771,
  327                 .devices = ite_devices,
  328         },
  329         {
  330                 .vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0x1061, .mask = 0x00,
  331                 .descr  = "Nuvoton NCT5104D/NCT6102D/NCT6106D (rev. A)",
  332                 .devices = nct5104_devices,
  333         },
  334         {
  335                 .vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0x5200, .mask = 0xff,
  336                 .descr = "Winbond 83627HF/F/HG/G",
  337                 .devices = nvt_devices,
  338         },
  339         {
  340                 .vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0x5900, .mask = 0xff,
  341                 .descr = "Winbond 83627S",
  342                 .devices = nvt_devices,
  343         },
  344         {
  345                 .vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0x6000, .mask = 0xff,
  346                 .descr = "Winbond 83697HF",
  347                 .devices = nvt_devices,
  348         },
  349         {
  350                 .vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0x6800, .mask = 0xff,
  351                 .descr = "Winbond 83697UG",
  352                 .devices = nvt_devices,
  353         },
  354         {
  355                 .vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0x7000, .mask = 0xff,
  356                 .descr = "Winbond 83637HF",
  357                 .devices = nvt_devices,
  358         },
  359         {
  360                 .vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0x8200, .mask = 0xff,
  361                 .descr = "Winbond 83627THF",
  362                 .devices = nvt_devices,
  363         },
  364         {
  365                 .vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0x8500, .mask = 0xff,
  366                 .descr = "Winbond 83687THF",
  367                 .devices = nvt_devices,
  368         },
  369         {
  370                 .vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0x8800, .mask = 0xff,
  371                 .descr = "Winbond 83627EHF",
  372                 .devices = nvt_devices,
  373         },
  374         {
  375                 .vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xa000, .mask = 0xff,
  376                 .descr = "Winbond 83627DHG",
  377                 .devices = nvt_devices,
  378         },
  379         {
  380                 .vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xa200, .mask = 0xff,
  381                 .descr = "Winbond 83627UHG",
  382                 .devices = nvt_devices,
  383         },
  384         {
  385                 .vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xa500, .mask = 0xff,
  386                 .descr = "Winbond 83667HG",
  387                 .devices = nvt_devices,
  388         },
  389         {
  390                 .vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xb000, .mask = 0xff,
  391                 .descr = "Winbond 83627DHG-P",
  392                 .devices = nvt_devices,
  393         },
  394         {
  395                 .vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xb300, .mask = 0xff,
  396                 .descr = "Winbond 83667HG-B",
  397                 .devices = nvt_devices,
  398         },
  399         {
  400                 .vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xb400, .mask = 0xff,
  401                 .descr = "Nuvoton NCT6775",
  402                 .devices = nvt_devices,
  403         },
  404         {
  405                 .vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xc300, .mask = 0xff,
  406                 .descr = "Nuvoton NCT6776",
  407                 .devices = nvt_devices,
  408         },
  409         {
  410                 .vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xc400, .mask = 0xff,
  411                 .descr = "Nuvoton NCT5104D/NCT6102D/NCT6106D (rev. B+)",
  412                 .devices = nct5104_devices,
  413         },
  414         {
  415                 .vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xc500, .mask = 0xff,
  416                 .descr = "Nuvoton NCT6779",
  417                 .devices = nvt_devices,
  418         },
  419         {
  420                 .vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xc800, .mask = 0xff,
  421                 .descr = "Nuvoton NCT6791",
  422                 .devices = nvt_devices,
  423         },
  424         {
  425                 .vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xc900, .mask = 0xff,
  426                 .descr = "Nuvoton NCT6792",
  427                 .devices = nvt_devices,
  428         },
  429         {
  430                 .vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xd100, .mask = 0xff,
  431                 .descr = "Nuvoton NCT6793",
  432                 .devices = nvt_devices,
  433         },
  434         {
  435                 .vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xd300, .mask = 0xff,
  436                 .descr = "Nuvoton NCT6795",
  437                 .devices = nvt_devices,
  438         },
  439         {
  440                 .vendor = SUPERIO_VENDOR_FINTEK, .devid = 0x1210, .mask = 0xff,
  441                 .descr = "Fintek F81803",
  442                 .devices = fintek_devices,
  443         },
  444         { 0, 0 }
  445 };
  446 
  447 static const char *
  448 devtype_to_str(superio_dev_type_t type)
  449 {
  450         switch (type) {
  451         case SUPERIO_DEV_NONE:
  452                 return ("none");
  453         case SUPERIO_DEV_HWM:
  454                 return ("HWM");
  455         case SUPERIO_DEV_WDT:
  456                 return ("WDT");
  457         case SUPERIO_DEV_GPIO:
  458                 return ("GPIO");
  459         case SUPERIO_DEV_MAX:
  460                 return ("invalid");
  461         }
  462         return ("invalid");
  463 }
  464 
  465 static int
  466 superio_detect(device_t dev, bool claim, struct siosc *sc)
  467 {
  468         struct resource *res;
  469         rman_res_t port;
  470         rman_res_t count;
  471         uint16_t devid;
  472         uint8_t revid;
  473         int error;
  474         int rid;
  475         int i, m;
  476 
  477         error = bus_get_resource(dev, SYS_RES_IOPORT, 0, &port, &count);
  478         if (error != 0)
  479                 return (error);
  480         if (port > UINT16_MAX || count < NUMPORTS) {
  481                 device_printf(dev, "unexpected I/O range size\n");
  482                 return (ENXIO);
  483         }
  484 
  485         /*
  486          * Make a temporary resource reservation for hardware probing.
  487          * If we can't get the resources we need then
  488          * we need to abort.  Possibly this indicates
  489          * the resources were used by another device
  490          * in which case the probe would have failed anyhow.
  491          */
  492         rid = 0;
  493         res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE);
  494         if (res == NULL) {
  495                 if (claim)
  496                         device_printf(dev, "failed to allocate I/O resource\n");
  497                 return (ENXIO);
  498         }
  499 
  500         for (m = 0; methods_table[m] != NULL; m++) {
  501                 methods_table[m]->enter(res, port);
  502                 if (methods_table[m]->vendor == SUPERIO_VENDOR_ITE) {
  503                         devid = sio_readw(res, 0x20);
  504                         revid = sio_read(res, 0x22);
  505                 } else if (methods_table[m]->vendor == SUPERIO_VENDOR_NUVOTON) {
  506                         devid = sio_read(res, 0x20);
  507                         revid = sio_read(res, 0x21);
  508                         devid = (devid << 8) | revid;
  509                 } else if (methods_table[m]->vendor == SUPERIO_VENDOR_FINTEK) {
  510                         devid = sio_read(res, 0x20);
  511                         revid = sio_read(res, 0x21);
  512                         devid = (devid << 8) | revid;
  513                 } else {
  514                         continue;
  515                 }
  516                 methods_table[m]->exit(res, port);
  517                 for (i = 0; superio_table[i].vendor != 0; i++) {
  518                         uint16_t mask;
  519 
  520                         mask = superio_table[i].mask;
  521                         if (superio_table[i].vendor !=
  522                             methods_table[m]->vendor)
  523                                 continue;
  524                         if ((superio_table[i].devid & ~mask) != (devid & ~mask))
  525                                 continue;
  526                         break;
  527                 }
  528 
  529                 /* Found a matching SuperIO entry. */
  530                 if (superio_table[i].vendor != 0)
  531                         break;
  532         }
  533 
  534         if (methods_table[m] == NULL)
  535                 error = ENXIO;
  536         else
  537                 error = 0;
  538         if (!claim || error != 0) {
  539                 bus_release_resource(dev, SYS_RES_IOPORT, rid, res);
  540                 return (error);
  541         }
  542 
  543         sc->methods = methods_table[m];
  544         sc->vendor = sc->methods->vendor;
  545         sc->known_devices = superio_table[i].devices;
  546         sc->io_res = res;
  547         sc->io_rid = rid;
  548         sc->io_port = port;
  549         sc->devid = devid;
  550         sc->revid = revid;
  551 
  552         KASSERT(sc->vendor == SUPERIO_VENDOR_ITE ||
  553             sc->vendor == SUPERIO_VENDOR_NUVOTON,
  554             ("Only ITE and Nuvoton SuperIO-s are supported"));
  555         sc->ldn_reg = 0x07;
  556         sc->enable_reg = 0x30;
  557         sc->current_ldn = 0xff; /* no device should have this */
  558 
  559         if (superio_table[i].descr != NULL) {
  560                 device_set_desc(dev, superio_table[i].descr);
  561         } else if (sc->vendor == SUPERIO_VENDOR_ITE) {
  562                 char descr[64];
  563 
  564                 snprintf(descr, sizeof(descr),
  565                     "ITE IT%4x SuperIO (revision 0x%02x)",
  566                     sc->devid, sc->revid);
  567                 device_set_desc_copy(dev, descr);
  568         }
  569         return (0);
  570 }
  571 
  572 static void
  573 superio_identify(driver_t *driver, device_t parent)
  574 {
  575         device_t        child;
  576         int i;
  577 
  578         /*
  579          * Don't create child devices if any already exist.
  580          * Those could be created via isa hints or if this
  581          * driver is loaded, unloaded and then loaded again.
  582          */
  583         if (device_find_child(parent, "superio", -1)) {
  584                 if (bootverbose)
  585                         printf("superio: device(s) already created\n");
  586                 return;
  587         }
  588 
  589         /*
  590          * Create a child for each candidate port.
  591          * It would be nice if we could somehow clean up those
  592          * that this driver fails to probe.
  593          */
  594         for (i = 0; ports_table[i] != 0; i++) {
  595                 child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE,
  596                     "superio", -1);
  597                 if (child == NULL) {
  598                         device_printf(parent, "failed to add superio child\n");
  599                         continue;
  600                 }
  601                 bus_set_resource(child, SYS_RES_IOPORT, 0, ports_table[i], 2);
  602                 if (superio_detect(child, false, NULL) != 0)
  603                         device_delete_child(parent, child);
  604         }
  605 }
  606 
  607 static int
  608 superio_probe(device_t dev)
  609 {
  610         struct siosc *sc;
  611         int error;
  612 
  613         /* Make sure we do not claim some ISA PNP device. */
  614         if (isa_get_logicalid(dev) != 0)
  615                 return (ENXIO);
  616 
  617         /*
  618          * XXX We can populate the softc now only because we return
  619          * BUS_PROBE_SPECIFIC
  620          */
  621         sc = device_get_softc(dev);
  622         error = superio_detect(dev, true, sc);
  623         if (error != 0)
  624                 return (error);
  625         return (BUS_PROBE_SPECIFIC);
  626 }
  627 
  628 static void
  629 superio_add_known_child(device_t dev, superio_dev_type_t type, uint8_t ldn)
  630 {
  631         struct siosc *sc = device_get_softc(dev);
  632         struct superio_devinfo *dinfo;
  633         device_t child;
  634 
  635         child = BUS_ADD_CHILD(dev, 0, NULL, -1);
  636         if (child == NULL) {
  637                 device_printf(dev, "failed to add child for ldn %d, type %s\n",
  638                     ldn, devtype_to_str(type));
  639                 return;
  640         }
  641         dinfo = device_get_ivars(child);
  642         dinfo->ldn = ldn;
  643         dinfo->type = type;
  644         sio_conf_enter(sc);
  645         dinfo->iobase = sio_ldn_readw(sc, ldn, 0x60);
  646         dinfo->iobase2 = sio_ldn_readw(sc, ldn, 0x62);
  647         dinfo->irq = sio_ldn_readw(sc, ldn, 0x70);
  648         dinfo->dma = sio_ldn_readw(sc, ldn, 0x74);
  649         sio_conf_exit(sc);
  650         STAILQ_INSERT_TAIL(&sc->devlist, dinfo, link);
  651 }
  652 
  653 static int
  654 superio_attach(device_t dev)
  655 {
  656         struct siosc *sc = device_get_softc(dev);
  657         int i;
  658 
  659         mtx_init(&sc->conf_lock, device_get_nameunit(dev), "superio", MTX_DEF);
  660         STAILQ_INIT(&sc->devlist);
  661 
  662         for (i = 0; sc->known_devices[i].type != SUPERIO_DEV_NONE; i++) {
  663                 superio_add_known_child(dev, sc->known_devices[i].type,
  664                     sc->known_devices[i].ldn);
  665         }
  666 
  667         bus_generic_probe(dev);
  668         bus_generic_attach(dev);
  669 
  670         sc->chardev = make_dev(&superio_cdevsw, device_get_unit(dev),
  671             UID_ROOT, GID_WHEEL, 0600, "superio%d", device_get_unit(dev));
  672         if (sc->chardev == NULL)
  673                 device_printf(dev, "failed to create character device\n");
  674         else
  675                 sc->chardev->si_drv1 = sc;
  676         return (0);
  677 }
  678 
  679 static int
  680 superio_detach(device_t dev)
  681 {
  682         struct siosc *sc = device_get_softc(dev);
  683         int error;
  684 
  685         error = bus_generic_detach(dev);
  686         if (error != 0)
  687                 return (error);
  688         if (sc->chardev != NULL)
  689                 destroy_dev(sc->chardev);
  690         device_delete_children(dev);
  691         bus_release_resource(dev, SYS_RES_IOPORT, sc->io_rid, sc->io_res);
  692         mtx_destroy(&sc->conf_lock);
  693         return (0);
  694 }
  695 
  696 static device_t
  697 superio_add_child(device_t dev, u_int order, const char *name, int unit)
  698 {
  699         struct superio_devinfo *dinfo;
  700         device_t child;
  701 
  702         child = device_add_child_ordered(dev, order, name, unit);
  703         if (child == NULL)
  704                 return (NULL);
  705         dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_NOWAIT | M_ZERO);
  706         if (dinfo == NULL) {
  707                 device_delete_child(dev, child);
  708                 return (NULL);
  709         }
  710         dinfo->ldn = 0xff;
  711         dinfo->type = SUPERIO_DEV_NONE;
  712         dinfo->dev = child;
  713         resource_list_init(&dinfo->resources);
  714         device_set_ivars(child, dinfo);
  715         return (child);
  716 }
  717 
  718 static int
  719 superio_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
  720 {
  721         struct superio_devinfo *dinfo;
  722 
  723         dinfo = device_get_ivars(child);
  724         switch (which) {
  725         case SUPERIO_IVAR_LDN:
  726                 *result = dinfo->ldn;
  727                 break;
  728         case SUPERIO_IVAR_TYPE:
  729                 *result = dinfo->type;
  730                 break;
  731         case SUPERIO_IVAR_IOBASE:
  732                 *result = dinfo->iobase;
  733                 break;
  734         case SUPERIO_IVAR_IOBASE2:
  735                 *result = dinfo->iobase2;
  736                 break;
  737         case SUPERIO_IVAR_IRQ:
  738                 *result = dinfo->irq;
  739                 break;
  740         case SUPERIO_IVAR_DMA:
  741                 *result = dinfo->dma;
  742                 break;
  743         default:
  744                 return (ENOENT);
  745         }
  746         return (0);
  747 }
  748 
  749 static int
  750 superio_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
  751 {
  752 
  753         switch (which) {
  754         case SUPERIO_IVAR_LDN:
  755         case SUPERIO_IVAR_TYPE:
  756         case SUPERIO_IVAR_IOBASE:
  757         case SUPERIO_IVAR_IOBASE2:
  758         case SUPERIO_IVAR_IRQ:
  759         case SUPERIO_IVAR_DMA:
  760                 return (EINVAL);
  761         default:
  762                 return (ENOENT);
  763         }
  764 }
  765 
  766 static struct resource_list *
  767 superio_get_resource_list(device_t dev, device_t child)
  768 {
  769         struct superio_devinfo *dinfo = device_get_ivars(child);
  770 
  771         return (&dinfo->resources);
  772 }
  773 
  774 static int
  775 superio_printf(struct superio_devinfo *dinfo, const char *fmt, ...)
  776 {
  777         va_list ap;
  778         int retval;
  779 
  780         retval = printf("superio:%s@ldn%0x2x: ",
  781             devtype_to_str(dinfo->type), dinfo->ldn);
  782         va_start(ap, fmt);
  783         retval += vprintf(fmt, ap);
  784         va_end(ap);
  785         return (retval);
  786 }
  787 
  788 static void
  789 superio_child_detached(device_t dev, device_t child)
  790 {
  791         struct superio_devinfo *dinfo;
  792         struct resource_list *rl;
  793 
  794         dinfo = device_get_ivars(child);
  795         rl = &dinfo->resources;
  796 
  797         if (resource_list_release_active(rl, dev, child, SYS_RES_IRQ) != 0)
  798                 superio_printf(dinfo, "Device leaked IRQ resources\n");
  799         if (resource_list_release_active(rl, dev, child, SYS_RES_MEMORY) != 0)
  800                 superio_printf(dinfo, "Device leaked memory resources\n");
  801         if (resource_list_release_active(rl, dev, child, SYS_RES_IOPORT) != 0)
  802                 superio_printf(dinfo, "Device leaked I/O resources\n");
  803 }
  804 
  805 static int
  806 superio_child_location(device_t parent, device_t child, struct sbuf *sb)
  807 {
  808         uint8_t ldn;
  809 
  810         ldn = superio_get_ldn(child);
  811         sbuf_printf(sb, "ldn=0x%02x", ldn);
  812         return (0);
  813 }
  814 
  815 static int
  816 superio_child_pnp(device_t parent, device_t child, struct sbuf *sb)
  817 {
  818         superio_dev_type_t type;
  819 
  820         type = superio_get_type(child);
  821         sbuf_printf(sb, "type=%s", devtype_to_str(type));
  822         return (0);
  823 }
  824 
  825 static int
  826 superio_print_child(device_t parent, device_t child)
  827 {
  828         superio_dev_type_t type;
  829         uint8_t ldn;
  830         int retval;
  831 
  832         ldn = superio_get_ldn(child);
  833         type = superio_get_type(child);
  834 
  835         retval = bus_print_child_header(parent, child);
  836         retval += printf(" at %s ldn 0x%02x", devtype_to_str(type), ldn);
  837         retval += bus_print_child_footer(parent, child);
  838 
  839         return (retval);
  840 }
  841 
  842 superio_vendor_t
  843 superio_vendor(device_t dev)
  844 {
  845         device_t sio_dev = device_get_parent(dev);
  846         struct siosc *sc = device_get_softc(sio_dev);
  847 
  848         return (sc->vendor);
  849 }
  850 
  851 uint16_t
  852 superio_devid(device_t dev)
  853 {
  854         device_t sio_dev = device_get_parent(dev);
  855         struct siosc *sc = device_get_softc(sio_dev);
  856 
  857         return (sc->devid);
  858 }
  859 
  860 uint8_t
  861 superio_revid(device_t dev)
  862 {
  863         device_t sio_dev = device_get_parent(dev);
  864         struct siosc *sc = device_get_softc(sio_dev);
  865 
  866         return (sc->revid);
  867 }
  868 
  869 uint8_t
  870 superio_read(device_t dev, uint8_t reg)
  871 {
  872         device_t sio_dev = device_get_parent(dev);
  873         struct siosc *sc = device_get_softc(sio_dev);
  874         struct superio_devinfo *dinfo = device_get_ivars(dev);
  875         uint8_t v;
  876 
  877         sio_conf_enter(sc);
  878         v = sio_ldn_read(sc, dinfo->ldn, reg);
  879         sio_conf_exit(sc);
  880         return (v);
  881 }
  882 
  883 void
  884 superio_write(device_t dev, uint8_t reg, uint8_t val)
  885 {
  886         device_t sio_dev = device_get_parent(dev);
  887         struct siosc *sc = device_get_softc(sio_dev);
  888         struct superio_devinfo *dinfo = device_get_ivars(dev);
  889 
  890         sio_conf_enter(sc);
  891         sio_ldn_write(sc, dinfo->ldn, reg, val);
  892         sio_conf_exit(sc);
  893 }
  894 
  895 bool
  896 superio_dev_enabled(device_t dev, uint8_t mask)
  897 {
  898         device_t sio_dev = device_get_parent(dev);
  899         struct siosc *sc = device_get_softc(sio_dev);
  900         struct superio_devinfo *dinfo = device_get_ivars(dev);
  901         uint8_t v;
  902 
  903         /* GPIO device is always active in ITE chips. */
  904         if (sc->vendor == SUPERIO_VENDOR_ITE && dinfo->ldn == 7)
  905                 return (true);
  906 
  907         v = superio_read(dev, sc->enable_reg);
  908         return ((v & mask) != 0);
  909 }
  910 
  911 void
  912 superio_dev_enable(device_t dev, uint8_t mask)
  913 {
  914         device_t sio_dev = device_get_parent(dev);
  915         struct siosc *sc = device_get_softc(sio_dev);
  916         struct superio_devinfo *dinfo = device_get_ivars(dev);
  917         uint8_t v;
  918 
  919         /* GPIO device is always active in ITE chips. */
  920         if (sc->vendor == SUPERIO_VENDOR_ITE && dinfo->ldn == 7)
  921                 return;
  922 
  923         sio_conf_enter(sc);
  924         v = sio_ldn_read(sc, dinfo->ldn, sc->enable_reg);
  925         v |= mask;
  926         sio_ldn_write(sc, dinfo->ldn, sc->enable_reg, v);
  927         sio_conf_exit(sc);
  928 }
  929 
  930 void
  931 superio_dev_disable(device_t dev, uint8_t mask)
  932 {
  933         device_t sio_dev = device_get_parent(dev);
  934         struct siosc *sc = device_get_softc(sio_dev);
  935         struct superio_devinfo *dinfo = device_get_ivars(dev);
  936         uint8_t v;
  937 
  938         /* GPIO device is always active in ITE chips. */
  939         if (sc->vendor == SUPERIO_VENDOR_ITE && dinfo->ldn == 7)
  940                 return;
  941 
  942         sio_conf_enter(sc);
  943         v = sio_ldn_read(sc, dinfo->ldn, sc->enable_reg);
  944         v &= ~mask;
  945         sio_ldn_write(sc, dinfo->ldn, sc->enable_reg, v);
  946         sio_conf_exit(sc);
  947 }
  948 
  949 device_t
  950 superio_find_dev(device_t superio, superio_dev_type_t type, int ldn)
  951 {
  952         struct siosc *sc = device_get_softc(superio);
  953         struct superio_devinfo *dinfo;
  954 
  955         if (ldn < -1 || ldn > UINT8_MAX)
  956                 return (NULL);          /* ERANGE */
  957         if (type == SUPERIO_DEV_NONE && ldn == -1)
  958                 return (NULL);          /* EINVAL */
  959 
  960         STAILQ_FOREACH(dinfo, &sc->devlist, link) {
  961                 if (ldn != -1 && dinfo->ldn != ldn)
  962                         continue;
  963                 if (type != SUPERIO_DEV_NONE && dinfo->type != type)
  964                         continue;
  965                 return (dinfo->dev);
  966         }
  967         return (NULL);
  968 }
  969 
  970 static int
  971 superio_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags,
  972     struct thread *td)
  973 {
  974         struct siosc *sc;
  975         struct superiocmd *s;
  976 
  977         sc = dev->si_drv1;
  978         s = (struct superiocmd *)data;
  979         switch (cmd) {
  980         case SUPERIO_CR_READ:
  981                 sio_conf_enter(sc);
  982                 s->val = sio_ldn_read(sc, s->ldn, s->cr);
  983                 sio_conf_exit(sc);
  984                 return (0);
  985         case SUPERIO_CR_WRITE:
  986                 sio_conf_enter(sc);
  987                 sio_ldn_write(sc, s->ldn, s->cr, s->val);
  988                 sio_conf_exit(sc);
  989                 return (0);
  990         default:
  991                 return (ENOTTY);
  992         }
  993 }
  994 
  995 static device_method_t superio_methods[] = {
  996         DEVMETHOD(device_identify,      superio_identify),
  997         DEVMETHOD(device_probe,         superio_probe),
  998         DEVMETHOD(device_attach,        superio_attach),
  999         DEVMETHOD(device_detach,        superio_detach),
 1000         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
 1001         DEVMETHOD(device_suspend,       bus_generic_suspend),
 1002         DEVMETHOD(device_resume,        bus_generic_resume),
 1003 
 1004         DEVMETHOD(bus_add_child,        superio_add_child),
 1005         DEVMETHOD(bus_child_detached,   superio_child_detached),
 1006         DEVMETHOD(bus_child_location,   superio_child_location),
 1007         DEVMETHOD(bus_child_pnpinfo,    superio_child_pnp),
 1008         DEVMETHOD(bus_print_child,      superio_print_child),
 1009         DEVMETHOD(bus_read_ivar,        superio_read_ivar),
 1010         DEVMETHOD(bus_write_ivar,       superio_write_ivar),
 1011         DEVMETHOD(bus_get_resource_list, superio_get_resource_list),
 1012         DEVMETHOD(bus_alloc_resource,   bus_generic_rl_alloc_resource),
 1013         DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource),
 1014         DEVMETHOD(bus_set_resource,     bus_generic_rl_set_resource),
 1015         DEVMETHOD(bus_get_resource,     bus_generic_rl_get_resource),
 1016         DEVMETHOD(bus_delete_resource,  bus_generic_rl_delete_resource),
 1017         DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
 1018         DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
 1019         DEVMETHOD(bus_setup_intr,       bus_generic_setup_intr),
 1020         DEVMETHOD(bus_teardown_intr,    bus_generic_teardown_intr),
 1021 
 1022         DEVMETHOD_END
 1023 };
 1024 
 1025 static driver_t superio_driver = {
 1026         "superio",
 1027         superio_methods,
 1028         sizeof(struct siosc)
 1029 };
 1030 
 1031 DRIVER_MODULE(superio, isa, superio_driver, 0, 0);
 1032 MODULE_VERSION(superio, 1);

Cache object: e99faa0107dddee2af5f33c8fb8dacab


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