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/i386/pci/pci_cfgreg.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) 1997, Stefan Esser <se@freebsd.org>
    3  * Copyright (c) 2000, Michael Smith <msmith@freebsd.org>
    4  * Copyright (c) 2000, BSDi
    5  * Copyright (c) 2004, Scott Long <scottl@freebsd.org>
    6  * All rights reserved.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice unmodified, this list of conditions, and the following
   13  *    disclaimer.
   14  * 2. Redistributions in binary form must reproduce the above copyright
   15  *    notice, this list of conditions and the following disclaimer in the
   16  *    documentation and/or other materials provided with the distribution.
   17  *
   18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   28  */
   29 
   30 #include <sys/cdefs.h>
   31 __FBSDID("$FreeBSD: releng/6.1/sys/i386/pci/pci_cfgreg.c 139790 2005-01-06 22:18:23Z imp $");
   32 
   33 #include <sys/param.h>
   34 #include <sys/systm.h>
   35 #include <sys/bus.h>
   36 #include <sys/lock.h>
   37 #include <sys/mutex.h>
   38 #include <sys/malloc.h>
   39 #include <sys/queue.h>
   40 #include <dev/pci/pcivar.h>
   41 #include <dev/pci/pcireg.h>
   42 #include <machine/pci_cfgreg.h>
   43 #include <machine/pc/bios.h>
   44 
   45 #include <vm/vm.h>
   46 #include <vm/vm_param.h>
   47 #include <vm/vm_kern.h>
   48 #include <vm/vm_extern.h>
   49 #include <vm/pmap.h>
   50 #include <machine/pmap.h>
   51 
   52 #define PRVERB(a) do {                                                  \
   53         if (bootverbose)                                                \
   54                 printf a ;                                              \
   55 } while(0)
   56 
   57 #define PCIE_CACHE 8
   58 struct pcie_cfg_elem {
   59         TAILQ_ENTRY(pcie_cfg_elem)      elem;
   60         vm_offset_t     vapage;
   61         vm_paddr_t      papage;
   62 };
   63 
   64 enum {
   65         CFGMECH_NONE = 0,
   66         CFGMECH_1,
   67         CFGMECH_2,
   68         CFGMECH_PCIE,
   69 };
   70 
   71 static TAILQ_HEAD(pcie_cfg_list, pcie_cfg_elem) pcie_list[MAXCPU];
   72 static uint32_t pciebar;
   73 static int cfgmech;
   74 static int devmax;
   75 static struct mtx pcicfg_mtx;
   76 
   77 static int      pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
   78 static void     pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
   79 static int      pcireg_cfgopen(void);
   80 
   81 static int      pciereg_cfgopen(void);
   82 static int      pciereg_cfgread(int bus, int slot, int func, int reg,
   83                                 int bytes);
   84 static void     pciereg_cfgwrite(int bus, int slot, int func, int reg,
   85                                  int data, int bytes);
   86 
   87 /*
   88  * Some BIOS writers seem to want to ignore the spec and put
   89  * 0 in the intline rather than 255 to indicate none.  Some use
   90  * numbers in the range 128-254 to indicate something strange and
   91  * apparently undocumented anywhere.  Assume these are completely bogus
   92  * and map them to 255, which means "none".
   93  */
   94 static __inline int 
   95 pci_i386_map_intline(int line)
   96 {
   97         if (line == 0 || line >= 128)
   98                 return (PCI_INVALID_IRQ);
   99         return (line);
  100 }
  101 
  102 static u_int16_t
  103 pcibios_get_version(void)
  104 {
  105         struct bios_regs args;
  106 
  107         if (PCIbios.ventry == 0) {
  108                 PRVERB(("pcibios: No call entry point\n"));
  109                 return (0);
  110         }
  111         args.eax = PCIBIOS_BIOS_PRESENT;
  112         if (bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL))) {
  113                 PRVERB(("pcibios: BIOS_PRESENT call failed\n"));
  114                 return (0);
  115         }
  116         if (args.edx != 0x20494350) {
  117                 PRVERB(("pcibios: BIOS_PRESENT didn't return 'PCI ' in edx\n"));
  118                 return (0);
  119         }
  120         return (args.ebx & 0xffff);
  121 }
  122 
  123 /* 
  124  * Initialise access to PCI configuration space 
  125  */
  126 int
  127 pci_cfgregopen(void)
  128 {
  129         static int              opened = 0;
  130         u_int16_t               vid, did;
  131         u_int16_t               v;
  132 
  133         if (opened)
  134                 return(1);
  135 
  136         if (pcireg_cfgopen() == 0)
  137                 return(0);
  138 
  139         v = pcibios_get_version();
  140         if (v > 0)
  141                 PRVERB(("pcibios: BIOS version %x.%02x\n", (v & 0xff00) >> 8,
  142                     v & 0xff));
  143         mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN);
  144         opened = 1;
  145 
  146         /* $PIR requires PCI BIOS 2.10 or greater. */
  147         if (v >= 0x0210)
  148                 pci_pir_open();
  149 
  150         /*
  151          * Grope around in the PCI config space to see if this is a
  152          * chipset that is capable of doing memory-mapped config cycles.
  153          * This also implies that it can do PCIe extended config cycles.
  154          */
  155 
  156         /* Check for the Intel 7520 and 925 chipsets */
  157         vid = pci_cfgregread(0, 0, 0, 0x0, 2);
  158         did = pci_cfgregread(0, 0, 0, 0x2, 2);
  159         if ((vid == 0x8086) && (did == 0x3590)) {
  160                 pciebar = pci_cfgregread(0, 0, 0, 0xce, 2) << 16;
  161                 pciereg_cfgopen();
  162         } else if ((vid == 0x8086) && (did == 0x2580)) {
  163                 pciebar = pci_cfgregread(0, 0, 0, 0x48, 4);
  164                 pciereg_cfgopen();
  165         }
  166 
  167         return(1);
  168 }
  169 
  170 /* 
  171  * Read configuration space register
  172  */
  173 u_int32_t
  174 pci_cfgregread(int bus, int slot, int func, int reg, int bytes)
  175 {
  176         uint32_t line;
  177 
  178         /*
  179          * Some BIOS writers seem to want to ignore the spec and put
  180          * 0 in the intline rather than 255 to indicate none.  The rest of
  181          * the code uses 255 as an invalid IRQ.
  182          */
  183         if (reg == PCIR_INTLINE && bytes == 1) {
  184                 line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1);
  185                 return (pci_i386_map_intline(line));
  186         }
  187         return (pcireg_cfgread(bus, slot, func, reg, bytes));
  188 }
  189 
  190 /* 
  191  * Write configuration space register 
  192  */
  193 void
  194 pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
  195 {
  196 
  197         pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
  198 }
  199 
  200 /* 
  201  * Configuration space access using direct register operations
  202  */
  203 
  204 /* enable configuration space accesses and return data port address */
  205 static int
  206 pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
  207 {
  208         int dataport = 0;
  209 
  210         if (bus <= PCI_BUSMAX
  211             && slot < devmax
  212             && func <= PCI_FUNCMAX
  213             && reg <= PCI_REGMAX
  214             && bytes != 3
  215             && (unsigned) bytes <= 4
  216             && (reg & (bytes - 1)) == 0) {
  217                 switch (cfgmech) {
  218                 case CFGMECH_1:
  219                         outl(CONF1_ADDR_PORT, (1 << 31)
  220                             | (bus << 16) | (slot << 11) 
  221                             | (func << 8) | (reg & ~0x03));
  222                         dataport = CONF1_DATA_PORT + (reg & 0x03);
  223                         break;
  224                 case CFGMECH_2:
  225                         outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1));
  226                         outb(CONF2_FORWARD_PORT, bus);
  227                         dataport = 0xc000 | (slot << 8) | reg;
  228                         break;
  229                 }
  230         }
  231         return (dataport);
  232 }
  233 
  234 /* disable configuration space accesses */
  235 static void
  236 pci_cfgdisable(void)
  237 {
  238         switch (cfgmech) {
  239         case CFGMECH_1:
  240                 outl(CONF1_ADDR_PORT, 0);
  241                 break;
  242         case CFGMECH_2:
  243                 outb(CONF2_ENABLE_PORT, 0);
  244                 outb(CONF2_FORWARD_PORT, 0);
  245                 break;
  246         }
  247 }
  248 
  249 static int
  250 pcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
  251 {
  252         int data = -1;
  253         int port;
  254 
  255         if (cfgmech == CFGMECH_PCIE) {
  256                 data = pciereg_cfgread(bus, slot, func, reg, bytes);
  257                 return (data);
  258         }
  259 
  260         mtx_lock_spin(&pcicfg_mtx);
  261         port = pci_cfgenable(bus, slot, func, reg, bytes);
  262         if (port != 0) {
  263                 switch (bytes) {
  264                 case 1:
  265                         data = inb(port);
  266                         break;
  267                 case 2:
  268                         data = inw(port);
  269                         break;
  270                 case 4:
  271                         data = inl(port);
  272                         break;
  273                 }
  274                 pci_cfgdisable();
  275         }
  276         mtx_unlock_spin(&pcicfg_mtx);
  277         return (data);
  278 }
  279 
  280 static void
  281 pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
  282 {
  283         int port;
  284 
  285         if (cfgmech == CFGMECH_PCIE) {
  286                 pciereg_cfgwrite(bus, slot, func, reg, data, bytes);
  287                 return;
  288         }
  289 
  290         mtx_lock_spin(&pcicfg_mtx);
  291         port = pci_cfgenable(bus, slot, func, reg, bytes);
  292         if (port != 0) {
  293                 switch (bytes) {
  294                 case 1:
  295                         outb(port, data);
  296                         break;
  297                 case 2:
  298                         outw(port, data);
  299                         break;
  300                 case 4:
  301                         outl(port, data);
  302                         break;
  303                 }
  304                 pci_cfgdisable();
  305         }
  306         mtx_unlock_spin(&pcicfg_mtx);
  307 }
  308 
  309 /* check whether the configuration mechanism has been correctly identified */
  310 static int
  311 pci_cfgcheck(int maxdev)
  312 {
  313         uint32_t id, class;
  314         uint8_t header;
  315         uint8_t device;
  316         int port;
  317 
  318         if (bootverbose) 
  319                 printf("pci_cfgcheck:\tdevice ");
  320 
  321         for (device = 0; device < maxdev; device++) {
  322                 if (bootverbose) 
  323                         printf("%d ", device);
  324 
  325                 port = pci_cfgenable(0, device, 0, 0, 4);
  326                 id = inl(port);
  327                 if (id == 0 || id == 0xffffffff)
  328                         continue;
  329 
  330                 port = pci_cfgenable(0, device, 0, 8, 4);
  331                 class = inl(port) >> 8;
  332                 if (bootverbose)
  333                         printf("[class=%06x] ", class);
  334                 if (class == 0 || (class & 0xf870ff) != 0)
  335                         continue;
  336 
  337                 port = pci_cfgenable(0, device, 0, 14, 1);
  338                 header = inb(port);
  339                 if (bootverbose)
  340                         printf("[hdr=%02x] ", header);
  341                 if ((header & 0x7e) != 0)
  342                         continue;
  343 
  344                 if (bootverbose)
  345                         printf("is there (id=%08x)\n", id);
  346 
  347                 pci_cfgdisable();
  348                 return (1);
  349         }
  350         if (bootverbose) 
  351                 printf("-- nothing found\n");
  352 
  353         pci_cfgdisable();
  354         return (0);
  355 }
  356 
  357 static int
  358 pcireg_cfgopen(void)
  359 {
  360         uint32_t mode1res, oldval1;
  361         uint8_t mode2res, oldval2;
  362 
  363         oldval1 = inl(CONF1_ADDR_PORT);
  364 
  365         if (bootverbose) {
  366                 printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n",
  367                     oldval1);
  368         }
  369 
  370         if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
  371 
  372                 cfgmech = CFGMECH_1;
  373                 devmax = 32;
  374 
  375                 outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
  376                 DELAY(1);
  377                 mode1res = inl(CONF1_ADDR_PORT);
  378                 outl(CONF1_ADDR_PORT, oldval1);
  379 
  380                 if (bootverbose)
  381                         printf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n", 
  382                             mode1res, CONF1_ENABLE_CHK);
  383 
  384                 if (mode1res) {
  385                         if (pci_cfgcheck(32)) 
  386                                 return (cfgmech);
  387                 }
  388 
  389                 outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
  390                 mode1res = inl(CONF1_ADDR_PORT);
  391                 outl(CONF1_ADDR_PORT, oldval1);
  392 
  393                 if (bootverbose)
  394                         printf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n", 
  395                             mode1res, CONF1_ENABLE_CHK1);
  396 
  397                 if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
  398                         if (pci_cfgcheck(32)) 
  399                                 return (cfgmech);
  400                 }
  401         }
  402 
  403         oldval2 = inb(CONF2_ENABLE_PORT);
  404 
  405         if (bootverbose) {
  406                 printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n",
  407                     oldval2);
  408         }
  409 
  410         if ((oldval2 & 0xf0) == 0) {
  411 
  412                 cfgmech = CFGMECH_2;
  413                 devmax = 16;
  414 
  415                 outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
  416                 mode2res = inb(CONF2_ENABLE_PORT);
  417                 outb(CONF2_ENABLE_PORT, oldval2);
  418 
  419                 if (bootverbose)
  420                         printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", 
  421                             mode2res, CONF2_ENABLE_CHK);
  422 
  423                 if (mode2res == CONF2_ENABLE_RES) {
  424                         if (bootverbose)
  425                                 printf("pci_open(2a):\tnow trying mechanism 2\n");
  426 
  427                         if (pci_cfgcheck(16)) 
  428                                 return (cfgmech);
  429                 }
  430         }
  431 
  432         cfgmech = CFGMECH_NONE;
  433         devmax = 0;
  434         return (cfgmech);
  435 }
  436 
  437 static int
  438 pciereg_cfgopen(void)
  439 {
  440         struct pcie_cfg_list *pcielist;
  441         struct pcie_cfg_elem *pcie_array, *elem;
  442 #ifdef SMP
  443         struct pcpu *pc;
  444 #endif
  445         vm_offset_t va;
  446         int i;
  447 
  448         if (bootverbose)
  449                 printf("Setting up PCIe mappings for BAR 0x%x\n", pciebar);
  450 
  451 #ifdef SMP
  452         SLIST_FOREACH(pc, &cpuhead, pc_allcpu)
  453 #endif
  454         {
  455 
  456                 pcie_array = malloc(sizeof(struct pcie_cfg_elem) * PCIE_CACHE,
  457                     M_DEVBUF, M_NOWAIT);
  458                 if (pcie_array == NULL)
  459                         return (0);
  460 
  461                 va = kmem_alloc_nofault(kernel_map, PCIE_CACHE * PAGE_SIZE);
  462                 if (va == 0) {
  463                         free(pcie_array, M_DEVBUF);
  464                         return (0);
  465                 }
  466 
  467 #ifdef SMP
  468                 pcielist = &pcie_list[pc->pc_cpuid];
  469 #else
  470                 pcielist = &pcie_list[0];
  471 #endif
  472                 TAILQ_INIT(pcielist);
  473                 for (i = 0; i < PCIE_CACHE; i++) {
  474                         elem = &pcie_array[i];
  475                         elem->vapage = va + (i * PAGE_SIZE);
  476                         elem->papage = 0;
  477                         TAILQ_INSERT_HEAD(pcielist, elem, elem);
  478                 }
  479         }
  480 
  481         
  482         cfgmech = CFGMECH_PCIE;
  483         devmax = 32;
  484         return (1);
  485 }
  486 
  487 #define PCIE_PADDR(bar, reg, bus, slot, func)   \
  488         ((bar)                          |       \
  489         (((bus) & 0xff) << 20)          |       \
  490         (((slot) & 0x1f) << 15)         |       \
  491         (((func) & 0x7) << 12)          |       \
  492         ((reg) & 0xfff))
  493 
  494 /*
  495  * Find an element in the cache that matches the physical page desired, or
  496  * create a new mapping from the least recently used element.
  497  * A very simple LRU algorithm is used here, does it need to be more
  498  * efficient?
  499  */
  500 static __inline struct pcie_cfg_elem *
  501 pciereg_findelem(vm_paddr_t papage)
  502 {
  503         struct pcie_cfg_list *pcielist;
  504         struct pcie_cfg_elem *elem;
  505 
  506         pcielist = &pcie_list[PCPU_GET(cpuid)];
  507         TAILQ_FOREACH(elem, pcielist, elem) {
  508                 if (elem->papage == papage)
  509                         break;
  510         }
  511 
  512         if (elem == NULL) {
  513                 elem = TAILQ_LAST(pcielist, pcie_cfg_list);
  514                 if (elem->papage != 0) {
  515                         pmap_kremove(elem->vapage);
  516                         invlpg(elem->vapage);
  517                 }
  518                 pmap_kenter(elem->vapage, papage);
  519                 elem->papage = papage;
  520         }
  521 
  522         if (elem != TAILQ_FIRST(pcielist)) {
  523                 TAILQ_REMOVE(pcielist, elem, elem);
  524                 TAILQ_INSERT_HEAD(pcielist, elem, elem);
  525         }
  526         return (elem);
  527 }
  528 
  529 static int
  530 pciereg_cfgread(int bus, int slot, int func, int reg, int bytes)
  531 {
  532         struct pcie_cfg_elem *elem;
  533         volatile vm_offset_t va;
  534         vm_paddr_t pa, papage;
  535         int data;
  536 
  537         critical_enter();
  538         pa = PCIE_PADDR(pciebar, reg, bus, slot, func);
  539         papage = pa & ~PAGE_MASK;
  540         elem = pciereg_findelem(papage);
  541         va = elem->vapage | (pa & PAGE_MASK);
  542 
  543         switch (bytes) {
  544         case 4:
  545                 data = *(volatile uint32_t *)(va);
  546                 break;
  547         case 2:
  548                 data = *(volatile uint16_t *)(va);
  549                 break;
  550         case 1:
  551                 data = *(volatile uint8_t *)(va);
  552                 break;
  553         default:
  554                 panic("pciereg_cfgread: invalid width");
  555         }
  556 
  557         critical_exit();
  558         return (data);
  559 }
  560 
  561 static void
  562 pciereg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
  563 {
  564         struct pcie_cfg_elem *elem;
  565         volatile vm_offset_t va;
  566         vm_paddr_t pa, papage;
  567 
  568         critical_enter();
  569         pa = PCIE_PADDR(pciebar, reg, bus, slot, func);
  570         papage = pa & ~PAGE_MASK;
  571         elem = pciereg_findelem(papage);
  572         va = elem->vapage | (pa & PAGE_MASK);
  573 
  574         switch (bytes) {
  575         case 4:
  576                 *(volatile uint32_t *)(va) = data;
  577                 break;
  578         case 2:
  579                 *(volatile uint16_t *)(va) = data;
  580                 break;
  581         case 1:
  582                 *(volatile uint8_t *)(va) = data;
  583                 break;
  584         default:
  585                 panic("pciereg_cfgwrite: invalid width");
  586         }
  587 
  588         critical_exit();
  589 }

Cache object: c7968fd8e5b51bac0b1f7e9f5b93cf56


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