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$");
   32 
   33 #include "opt_xbox.h"
   34 
   35 #include <sys/param.h>
   36 #include <sys/systm.h>
   37 #include <sys/bus.h>
   38 #include <sys/lock.h>
   39 #include <sys/kernel.h>
   40 #include <sys/mutex.h>
   41 #include <sys/malloc.h>
   42 #include <sys/queue.h>
   43 #include <sys/sysctl.h>
   44 #include <dev/pci/pcivar.h>
   45 #include <dev/pci/pcireg.h>
   46 #include <machine/pci_cfgreg.h>
   47 #include <machine/pc/bios.h>
   48 
   49 #include <vm/vm.h>
   50 #include <vm/vm_param.h>
   51 #include <vm/vm_kern.h>
   52 #include <vm/vm_extern.h>
   53 #include <vm/pmap.h>
   54 #include <machine/pmap.h>
   55 
   56 #ifdef XBOX
   57 #include <machine/xbox.h>
   58 #endif
   59 
   60 #define PRVERB(a) do {                                                  \
   61         if (bootverbose)                                                \
   62                 printf a ;                                              \
   63 } while(0)
   64 
   65 #define PCIE_CACHE 8
   66 struct pcie_cfg_elem {
   67         TAILQ_ENTRY(pcie_cfg_elem)      elem;
   68         vm_offset_t     vapage;
   69         vm_paddr_t      papage;
   70 };
   71 
   72 enum {
   73         CFGMECH_NONE = 0,
   74         CFGMECH_1,
   75         CFGMECH_2,
   76         CFGMECH_PCIE,
   77 };
   78 
   79 SYSCTL_DECL(_hw_pci);
   80 
   81 static TAILQ_HEAD(pcie_cfg_list, pcie_cfg_elem) pcie_list[MAXCPU];
   82 static uint64_t pcie_base;
   83 static int pcie_minbus, pcie_maxbus;
   84 static uint32_t pcie_badslots;
   85 static int cfgmech;
   86 static int devmax;
   87 static struct mtx pcicfg_mtx;
   88 static int mcfg_enable = 0;
   89 TUNABLE_INT("hw.pci.mcfg", &mcfg_enable);
   90 SYSCTL_INT(_hw_pci, OID_AUTO, mcfg, CTLFLAG_RDTUN, &mcfg_enable, 0,
   91     "Enable support for PCI-e memory mapped config access");
   92 
   93 static uint32_t pci_docfgregread(int bus, int slot, int func, int reg,
   94                     int bytes);
   95 static int      pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
   96 static void     pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
   97 static int      pcireg_cfgopen(void);
   98 static int      pciereg_cfgread(int bus, unsigned slot, unsigned func,
   99                     unsigned reg, unsigned bytes);
  100 static void     pciereg_cfgwrite(int bus, unsigned slot, unsigned func,
  101                     unsigned reg, int data, unsigned bytes);
  102 
  103 /*
  104  * Some BIOS writers seem to want to ignore the spec and put
  105  * 0 in the intline rather than 255 to indicate none.  Some use
  106  * numbers in the range 128-254 to indicate something strange and
  107  * apparently undocumented anywhere.  Assume these are completely bogus
  108  * and map them to 255, which means "none".
  109  */
  110 static __inline int 
  111 pci_i386_map_intline(int line)
  112 {
  113         if (line == 0 || line >= 128)
  114                 return (PCI_INVALID_IRQ);
  115         return (line);
  116 }
  117 
  118 static u_int16_t
  119 pcibios_get_version(void)
  120 {
  121         struct bios_regs args;
  122 
  123         if (PCIbios.ventry == 0) {
  124                 PRVERB(("pcibios: No call entry point\n"));
  125                 return (0);
  126         }
  127         args.eax = PCIBIOS_BIOS_PRESENT;
  128         if (bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL))) {
  129                 PRVERB(("pcibios: BIOS_PRESENT call failed\n"));
  130                 return (0);
  131         }
  132         if (args.edx != 0x20494350) {
  133                 PRVERB(("pcibios: BIOS_PRESENT didn't return 'PCI ' in edx\n"));
  134                 return (0);
  135         }
  136         return (args.ebx & 0xffff);
  137 }
  138 
  139 /* 
  140  * Initialise access to PCI configuration space 
  141  */
  142 int
  143 pci_cfgregopen(void)
  144 {
  145         static int              opened = 0;
  146         uint64_t                pciebar;
  147         u_int16_t               vid, did;
  148         u_int16_t               v;
  149 
  150         if (opened)
  151                 return (1);
  152 
  153         if (cfgmech == CFGMECH_NONE && pcireg_cfgopen() == 0)
  154                 return (0);
  155 
  156         v = pcibios_get_version();
  157         if (v > 0)
  158                 PRVERB(("pcibios: BIOS version %x.%02x\n", (v & 0xff00) >> 8,
  159                     v & 0xff));
  160         mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN);
  161         opened = 1;
  162 
  163         /* $PIR requires PCI BIOS 2.10 or greater. */
  164         if (v >= 0x0210)
  165                 pci_pir_open();
  166 
  167         if (cfgmech == CFGMECH_PCIE)
  168                 return (1);     
  169 
  170         /*
  171          * Grope around in the PCI config space to see if this is a
  172          * chipset that is capable of doing memory-mapped config cycles.
  173          * This also implies that it can do PCIe extended config cycles.
  174          */
  175 
  176         /* Check for supported chipsets */
  177         vid = pci_cfgregread(0, 0, 0, PCIR_VENDOR, 2);
  178         did = pci_cfgregread(0, 0, 0, PCIR_DEVICE, 2);
  179         switch (vid) {
  180         case 0x8086:
  181                 switch (did) {
  182                 case 0x3590:
  183                 case 0x3592:
  184                         /* Intel 7520 or 7320 */
  185                         pciebar = pci_cfgregread(0, 0, 0, 0xce, 2) << 16;
  186                         pcie_cfgregopen(pciebar, 0, 255);
  187                         break;
  188                 case 0x2580:
  189                 case 0x2584:
  190                 case 0x2590:
  191                         /* Intel 915, 925, or 915GM */
  192                         pciebar = pci_cfgregread(0, 0, 0, 0x48, 4);
  193                         pcie_cfgregopen(pciebar, 0, 255);
  194                         break;
  195                 }
  196         }
  197 
  198         return(1);
  199 }
  200 
  201 static uint32_t
  202 pci_docfgregread(int bus, int slot, int func, int reg, int bytes)
  203 {
  204 
  205         if (cfgmech == CFGMECH_PCIE &&
  206             (bus >= pcie_minbus && bus <= pcie_maxbus) &&
  207             (bus != 0 || !(1 << slot & pcie_badslots)))
  208                 return (pciereg_cfgread(bus, slot, func, reg, bytes));
  209         else
  210                 return (pcireg_cfgread(bus, slot, func, reg, bytes));
  211 }
  212 
  213 /* 
  214  * Read configuration space register
  215  */
  216 u_int32_t
  217 pci_cfgregread(int bus, int slot, int func, int reg, int bytes)
  218 {
  219         uint32_t line;
  220 
  221         /*
  222          * Some BIOS writers seem to want to ignore the spec and put
  223          * 0 in the intline rather than 255 to indicate none.  The rest of
  224          * the code uses 255 as an invalid IRQ.
  225          */
  226         if (reg == PCIR_INTLINE && bytes == 1) {
  227                 line = pci_docfgregread(bus, slot, func, PCIR_INTLINE, 1);
  228                 return (pci_i386_map_intline(line));
  229         }
  230         return (pci_docfgregread(bus, slot, func, reg, bytes));
  231 }
  232 
  233 /* 
  234  * Write configuration space register 
  235  */
  236 void
  237 pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
  238 {
  239 
  240         if (cfgmech == CFGMECH_PCIE &&
  241             (bus >= pcie_minbus && bus <= pcie_maxbus) &&
  242             (bus != 0 || !(1 << slot & pcie_badslots)))
  243                 pciereg_cfgwrite(bus, slot, func, reg, data, bytes);
  244         else
  245                 pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
  246 }
  247 
  248 /* 
  249  * Configuration space access using direct register operations
  250  */
  251 
  252 /* enable configuration space accesses and return data port address */
  253 static int
  254 pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
  255 {
  256         int dataport = 0;
  257 
  258 #ifdef XBOX
  259         if (arch_i386_is_xbox) {
  260                 /*
  261                  * The Xbox MCPX chipset is a derivative of the nForce 1
  262                  * chipset. It almost has the same bus layout; some devices
  263                  * cannot be used, because they have been removed.
  264                  */
  265 
  266                 /*
  267                  * Devices 00:00.1 and 00:00.2 used to be memory controllers on
  268                  * the nForce chipset, but on the Xbox, using them will lockup
  269                  * the chipset.
  270                  */
  271                 if (bus == 0 && slot == 0 && (func == 1 || func == 2))
  272                         return dataport;
  273                 
  274                 /*
  275                  * Bus 1 only contains a VGA controller at 01:00.0. When you try
  276                  * to probe beyond that device, you only get garbage, which
  277                  * could cause lockups.
  278                  */
  279                 if (bus == 1 && (slot != 0 || func != 0))
  280                         return dataport;
  281                 
  282                 /*
  283                  * Bus 2 used to contain the AGP controller, but the Xbox MCPX
  284                  * doesn't have one. Probing it can cause lockups.
  285                  */
  286                 if (bus >= 2)
  287                         return dataport;
  288         }
  289 #endif
  290 
  291         if (bus <= PCI_BUSMAX
  292             && slot < devmax
  293             && func <= PCI_FUNCMAX
  294             && (unsigned)reg <= PCI_REGMAX
  295             && bytes != 3
  296             && (unsigned)bytes <= 4
  297             && (reg & (bytes - 1)) == 0) {
  298                 switch (cfgmech) {
  299                 case CFGMECH_PCIE:
  300                 case CFGMECH_1:
  301                         outl(CONF1_ADDR_PORT, (1 << 31)
  302                             | (bus << 16) | (slot << 11) 
  303                             | (func << 8) | (reg & ~0x03));
  304                         dataport = CONF1_DATA_PORT + (reg & 0x03);
  305                         break;
  306                 case CFGMECH_2:
  307                         outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1));
  308                         outb(CONF2_FORWARD_PORT, bus);
  309                         dataport = 0xc000 | (slot << 8) | reg;
  310                         break;
  311                 }
  312         }
  313         return (dataport);
  314 }
  315 
  316 /* disable configuration space accesses */
  317 static void
  318 pci_cfgdisable(void)
  319 {
  320         switch (cfgmech) {
  321         case CFGMECH_PCIE:
  322         case CFGMECH_1:
  323                 /*
  324                  * Do nothing for the config mechanism 1 case.
  325                  * Writing a 0 to the address port can apparently
  326                  * confuse some bridges and cause spurious
  327                  * access failures.
  328                  */
  329                 break;
  330         case CFGMECH_2:
  331                 outb(CONF2_ENABLE_PORT, 0);
  332                 break;
  333         }
  334 }
  335 
  336 static int
  337 pcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
  338 {
  339         int data = -1;
  340         int port;
  341 
  342         mtx_lock_spin(&pcicfg_mtx);
  343         port = pci_cfgenable(bus, slot, func, reg, bytes);
  344         if (port != 0) {
  345                 switch (bytes) {
  346                 case 1:
  347                         data = inb(port);
  348                         break;
  349                 case 2:
  350                         data = inw(port);
  351                         break;
  352                 case 4:
  353                         data = inl(port);
  354                         break;
  355                 }
  356                 pci_cfgdisable();
  357         }
  358         mtx_unlock_spin(&pcicfg_mtx);
  359         return (data);
  360 }
  361 
  362 static void
  363 pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
  364 {
  365         int port;
  366 
  367         mtx_lock_spin(&pcicfg_mtx);
  368         port = pci_cfgenable(bus, slot, func, reg, bytes);
  369         if (port != 0) {
  370                 switch (bytes) {
  371                 case 1:
  372                         outb(port, data);
  373                         break;
  374                 case 2:
  375                         outw(port, data);
  376                         break;
  377                 case 4:
  378                         outl(port, data);
  379                         break;
  380                 }
  381                 pci_cfgdisable();
  382         }
  383         mtx_unlock_spin(&pcicfg_mtx);
  384 }
  385 
  386 /* check whether the configuration mechanism has been correctly identified */
  387 static int
  388 pci_cfgcheck(int maxdev)
  389 {
  390         uint32_t id, class;
  391         uint8_t header;
  392         uint8_t device;
  393         int port;
  394 
  395         if (bootverbose) 
  396                 printf("pci_cfgcheck:\tdevice ");
  397 
  398         for (device = 0; device < maxdev; device++) {
  399                 if (bootverbose) 
  400                         printf("%d ", device);
  401 
  402                 port = pci_cfgenable(0, device, 0, 0, 4);
  403                 id = inl(port);
  404                 if (id == 0 || id == 0xffffffff)
  405                         continue;
  406 
  407                 port = pci_cfgenable(0, device, 0, 8, 4);
  408                 class = inl(port) >> 8;
  409                 if (bootverbose)
  410                         printf("[class=%06x] ", class);
  411                 if (class == 0 || (class & 0xf870ff) != 0)
  412                         continue;
  413 
  414                 port = pci_cfgenable(0, device, 0, 14, 1);
  415                 header = inb(port);
  416                 if (bootverbose)
  417                         printf("[hdr=%02x] ", header);
  418                 if ((header & 0x7e) != 0)
  419                         continue;
  420 
  421                 if (bootverbose)
  422                         printf("is there (id=%08x)\n", id);
  423 
  424                 pci_cfgdisable();
  425                 return (1);
  426         }
  427         if (bootverbose) 
  428                 printf("-- nothing found\n");
  429 
  430         pci_cfgdisable();
  431         return (0);
  432 }
  433 
  434 static int
  435 pcireg_cfgopen(void)
  436 {
  437         uint32_t mode1res, oldval1;
  438         uint8_t mode2res, oldval2;
  439 
  440         /* Check for type #1 first. */
  441         oldval1 = inl(CONF1_ADDR_PORT);
  442 
  443         if (bootverbose) {
  444                 printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n",
  445                     oldval1);
  446         }
  447 
  448         cfgmech = CFGMECH_1;
  449         devmax = 32;
  450 
  451         outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
  452         DELAY(1);
  453         mode1res = inl(CONF1_ADDR_PORT);
  454         outl(CONF1_ADDR_PORT, oldval1);
  455 
  456         if (bootverbose)
  457                 printf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n",  mode1res,
  458                     CONF1_ENABLE_CHK);
  459 
  460         if (mode1res) {
  461                 if (pci_cfgcheck(32)) 
  462                         return (cfgmech);
  463         }
  464 
  465         outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
  466         mode1res = inl(CONF1_ADDR_PORT);
  467         outl(CONF1_ADDR_PORT, oldval1);
  468 
  469         if (bootverbose)
  470                 printf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n",  mode1res,
  471                     CONF1_ENABLE_CHK1);
  472 
  473         if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
  474                 if (pci_cfgcheck(32)) 
  475                         return (cfgmech);
  476         }
  477 
  478         /* Type #1 didn't work, so try type #2. */
  479         oldval2 = inb(CONF2_ENABLE_PORT);
  480 
  481         if (bootverbose) {
  482                 printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n",
  483                     oldval2);
  484         }
  485 
  486         if ((oldval2 & 0xf0) == 0) {
  487 
  488                 cfgmech = CFGMECH_2;
  489                 devmax = 16;
  490 
  491                 outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
  492                 mode2res = inb(CONF2_ENABLE_PORT);
  493                 outb(CONF2_ENABLE_PORT, oldval2);
  494 
  495                 if (bootverbose)
  496                         printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", 
  497                             mode2res, CONF2_ENABLE_CHK);
  498 
  499                 if (mode2res == CONF2_ENABLE_RES) {
  500                         if (bootverbose)
  501                                 printf("pci_open(2a):\tnow trying mechanism 2\n");
  502 
  503                         if (pci_cfgcheck(16)) 
  504                                 return (cfgmech);
  505                 }
  506         }
  507 
  508         /* Nothing worked, so punt. */
  509         cfgmech = CFGMECH_NONE;
  510         devmax = 0;
  511         return (cfgmech);
  512 }
  513 
  514 int
  515 pcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus)
  516 {
  517         struct pcie_cfg_list *pcielist;
  518         struct pcie_cfg_elem *pcie_array, *elem;
  519 #ifdef SMP
  520         struct pcpu *pc;
  521 #endif
  522         vm_offset_t va;
  523         uint32_t val1, val2;
  524         int i, slot;
  525 
  526         if (!mcfg_enable)
  527                 return (0);
  528 
  529         if (minbus != 0)
  530                 return (0);
  531 
  532 #ifndef PAE
  533         if (base >= 0x100000000) {
  534                 if (bootverbose)
  535                         printf(
  536             "PCI: Memory Mapped PCI configuration area base 0x%jx too high\n",
  537                             (uintmax_t)base);
  538                 return (0);
  539         }
  540 #endif
  541                 
  542         if (bootverbose)
  543                 printf("PCIe: Memory Mapped configuration base @ 0x%jx\n",
  544                     (uintmax_t)base);
  545 
  546 #ifdef SMP
  547         SLIST_FOREACH(pc, &cpuhead, pc_allcpu)
  548 #endif
  549         {
  550 
  551                 pcie_array = malloc(sizeof(struct pcie_cfg_elem) * PCIE_CACHE,
  552                     M_DEVBUF, M_NOWAIT);
  553                 if (pcie_array == NULL)
  554                         return (0);
  555 
  556                 va = kmem_alloc_nofault(kernel_map, PCIE_CACHE * PAGE_SIZE);
  557                 if (va == 0) {
  558                         free(pcie_array, M_DEVBUF);
  559                         return (0);
  560                 }
  561 
  562 #ifdef SMP
  563                 pcielist = &pcie_list[pc->pc_cpuid];
  564 #else
  565                 pcielist = &pcie_list[0];
  566 #endif
  567                 TAILQ_INIT(pcielist);
  568                 for (i = 0; i < PCIE_CACHE; i++) {
  569                         elem = &pcie_array[i];
  570                         elem->vapage = va + (i * PAGE_SIZE);
  571                         elem->papage = 0;
  572                         TAILQ_INSERT_HEAD(pcielist, elem, elem);
  573                 }
  574         }
  575 
  576         pcie_base = base;
  577         pcie_minbus = minbus;
  578         pcie_maxbus = maxbus;
  579         cfgmech = CFGMECH_PCIE;
  580         devmax = 32;
  581 
  582         /*
  583          * On some AMD systems, some of the devices on bus 0 are
  584          * inaccessible using memory-mapped PCI config access.  Walk
  585          * bus 0 looking for such devices.  For these devices, we will
  586          * fall back to using type 1 config access instead.
  587          */
  588         if (pci_cfgregopen() != 0) {
  589                 for (slot = 0; slot <= PCI_SLOTMAX; slot++) {
  590                         val1 = pcireg_cfgread(0, slot, 0, 0, 4);
  591                         if (val1 == 0xffffffff)
  592                                 continue;
  593 
  594                         val2 = pciereg_cfgread(0, slot, 0, 0, 4);
  595                         if (val2 != val1)
  596                                 pcie_badslots |= (1 << slot);
  597                 }
  598         }
  599 
  600         return (1);
  601 }
  602 
  603 #define PCIE_PADDR(bar, reg, bus, slot, func)   \
  604         ((bar)                          |       \
  605         (((bus) & 0xff) << 20)          |       \
  606         (((slot) & 0x1f) << 15)         |       \
  607         (((func) & 0x7) << 12)          |       \
  608         ((reg) & 0xfff))
  609 
  610 /*
  611  * Find an element in the cache that matches the physical page desired, or
  612  * create a new mapping from the least recently used element.
  613  * A very simple LRU algorithm is used here, does it need to be more
  614  * efficient?
  615  */
  616 static __inline struct pcie_cfg_elem *
  617 pciereg_findelem(vm_paddr_t papage)
  618 {
  619         struct pcie_cfg_list *pcielist;
  620         struct pcie_cfg_elem *elem;
  621 
  622         pcielist = &pcie_list[PCPU_GET(cpuid)];
  623         TAILQ_FOREACH(elem, pcielist, elem) {
  624                 if (elem->papage == papage)
  625                         break;
  626         }
  627 
  628         if (elem == NULL) {
  629                 elem = TAILQ_LAST(pcielist, pcie_cfg_list);
  630                 if (elem->papage != 0) {
  631                         pmap_kremove(elem->vapage);
  632                         invlpg(elem->vapage);
  633                 }
  634                 pmap_kenter(elem->vapage, papage);
  635                 elem->papage = papage;
  636         }
  637 
  638         if (elem != TAILQ_FIRST(pcielist)) {
  639                 TAILQ_REMOVE(pcielist, elem, elem);
  640                 TAILQ_INSERT_HEAD(pcielist, elem, elem);
  641         }
  642         return (elem);
  643 }
  644 
  645 static int
  646 pciereg_cfgread(int bus, unsigned slot, unsigned func, unsigned reg,
  647     unsigned bytes)
  648 {
  649         struct pcie_cfg_elem *elem;
  650         volatile vm_offset_t va;
  651         vm_paddr_t pa, papage;
  652         int data = -1;
  653 
  654         if (bus < pcie_minbus || bus > pcie_maxbus || slot > PCI_SLOTMAX ||
  655             func > PCI_FUNCMAX || reg > PCIE_REGMAX)
  656                 return (-1);
  657 
  658         critical_enter();
  659         pa = PCIE_PADDR(pcie_base, reg, bus, slot, func);
  660         papage = pa & ~PAGE_MASK;
  661         elem = pciereg_findelem(papage);
  662         va = elem->vapage | (pa & PAGE_MASK);
  663 
  664         switch (bytes) {
  665         case 4:
  666                 data = *(volatile uint32_t *)(va);
  667                 break;
  668         case 2:
  669                 data = *(volatile uint16_t *)(va);
  670                 break;
  671         case 1:
  672                 data = *(volatile uint8_t *)(va);
  673                 break;
  674         }
  675 
  676         critical_exit();
  677         return (data);
  678 }
  679 
  680 static void
  681 pciereg_cfgwrite(int bus, unsigned slot, unsigned func, unsigned reg, int data,
  682     unsigned bytes)
  683 {
  684         struct pcie_cfg_elem *elem;
  685         volatile vm_offset_t va;
  686         vm_paddr_t pa, papage;
  687 
  688         if (bus < pcie_minbus || bus > pcie_maxbus || slot > PCI_SLOTMAX ||
  689             func > PCI_FUNCMAX || reg > PCIE_REGMAX)
  690                 return;
  691 
  692         critical_enter();
  693         pa = PCIE_PADDR(pcie_base, reg, bus, slot, func);
  694         papage = pa & ~PAGE_MASK;
  695         elem = pciereg_findelem(papage);
  696         va = elem->vapage | (pa & PAGE_MASK);
  697 
  698         switch (bytes) {
  699         case 4:
  700                 *(volatile uint32_t *)(va) = data;
  701                 break;
  702         case 2:
  703                 *(volatile uint16_t *)(va) = data;
  704                 break;
  705         case 1:
  706                 *(volatile uint8_t *)(va) = data;
  707                 break;
  708         }
  709 
  710         critical_exit();
  711 }

Cache object: b98609dbb53f07078fd2581825f23c04


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