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/amd64/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  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
    5  * Copyright (c) 2000, Michael Smith <msmith@freebsd.org>
    6  * Copyright (c) 2000, BSDi
    7  * All rights reserved.
    8  *
    9  * Redistribution and use in source and binary forms, with or without
   10  * modification, are permitted provided that the following conditions
   11  * are met:
   12  * 1. Redistributions of source code must retain the above copyright
   13  *    notice unmodified, this list of conditions, and the following
   14  *    disclaimer.
   15  * 2. Redistributions in binary form must reproduce the above copyright
   16  *    notice, this list of conditions and the following disclaimer in the
   17  *    documentation and/or other materials provided with the distribution.
   18  *
   19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   29  */
   30 
   31 #include <sys/cdefs.h>
   32 __FBSDID("$FreeBSD$");
   33 
   34 #include <sys/param.h>
   35 #include <sys/systm.h>
   36 #include <sys/bus.h>
   37 #include <sys/lock.h>
   38 #include <sys/kernel.h>
   39 #include <sys/mutex.h>
   40 #include <sys/sysctl.h>
   41 #include <dev/pci/pcivar.h>
   42 #include <dev/pci/pcireg.h>
   43 #include <vm/vm.h>
   44 #include <vm/pmap.h>
   45 #include <machine/pci_cfgreg.h>
   46 
   47 static uint32_t pci_docfgregread(int bus, int slot, int func, int reg,
   48                     int bytes);
   49 static int      pciereg_cfgread(int bus, unsigned slot, unsigned func,
   50                     unsigned reg, unsigned bytes);
   51 static void     pciereg_cfgwrite(int bus, unsigned slot, unsigned func,
   52                     unsigned reg, int data, unsigned bytes);
   53 static int      pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
   54 static void     pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
   55 
   56 SYSCTL_DECL(_hw_pci);
   57 
   58 /*
   59  * For amd64 we assume that type 1 I/O port-based access always works.
   60  * If an ACPI MCFG table exists, pcie_cfgregopen() will be called to
   61  * switch to memory-mapped access.
   62  */
   63 int cfgmech = CFGMECH_1;
   64 
   65 static vm_offset_t pcie_base;
   66 static int pcie_minbus, pcie_maxbus;
   67 static uint32_t pcie_badslots;
   68 static struct mtx pcicfg_mtx;
   69 MTX_SYSINIT(pcicfg_mtx, &pcicfg_mtx, "pcicfg_mtx", MTX_SPIN);
   70 static int mcfg_enable = 1;
   71 SYSCTL_INT(_hw_pci, OID_AUTO, mcfg, CTLFLAG_RDTUN, &mcfg_enable, 0,
   72     "Enable support for PCI-e memory mapped config access");
   73 
   74 int
   75 pci_cfgregopen(void)
   76 {
   77 
   78         return (1);
   79 }
   80 
   81 static uint32_t
   82 pci_docfgregread(int bus, int slot, int func, int reg, int bytes)
   83 {
   84 
   85         if (cfgmech == CFGMECH_PCIE &&
   86             (bus >= pcie_minbus && bus <= pcie_maxbus) &&
   87             (bus != 0 || !(1 << slot & pcie_badslots)))
   88                 return (pciereg_cfgread(bus, slot, func, reg, bytes));
   89         else
   90                 return (pcireg_cfgread(bus, slot, func, reg, bytes));
   91 }
   92 
   93 /* 
   94  * Read configuration space register
   95  */
   96 u_int32_t
   97 pci_cfgregread(int bus, int slot, int func, int reg, int bytes)
   98 {
   99         uint32_t line;
  100 
  101         /*
  102          * Some BIOS writers seem to want to ignore the spec and put
  103          * 0 in the intline rather than 255 to indicate none.  Some use
  104          * numbers in the range 128-254 to indicate something strange and
  105          * apparently undocumented anywhere.  Assume these are completely bogus
  106          * and map them to 255, which the rest of the PCI code recognizes as
  107          * as an invalid IRQ.
  108          */
  109         if (reg == PCIR_INTLINE && bytes == 1) {
  110                 line = pci_docfgregread(bus, slot, func, PCIR_INTLINE, 1);
  111                 if (line == 0 || line >= 128)
  112                         line = PCI_INVALID_IRQ;
  113                 return (line);
  114         }
  115         return (pci_docfgregread(bus, slot, func, reg, bytes));
  116 }
  117 
  118 /* 
  119  * Write configuration space register 
  120  */
  121 void
  122 pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
  123 {
  124 
  125         if (cfgmech == CFGMECH_PCIE &&
  126             (bus >= pcie_minbus && bus <= pcie_maxbus) &&
  127             (bus != 0 || !(1 << slot & pcie_badslots)))
  128                 pciereg_cfgwrite(bus, slot, func, reg, data, bytes);
  129         else
  130                 pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
  131 }
  132 
  133 /* 
  134  * Configuration space access using direct register operations
  135  */
  136 
  137 /* enable configuration space accesses and return data port address */
  138 static int
  139 pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
  140 {
  141         int dataport = 0;
  142 
  143         if (bus <= PCI_BUSMAX && slot <= PCI_SLOTMAX && func <= PCI_FUNCMAX &&
  144             (unsigned)reg <= PCI_REGMAX && bytes != 3 &&
  145             (unsigned)bytes <= 4 && (reg & (bytes - 1)) == 0) {
  146                 outl(CONF1_ADDR_PORT, (1U << 31) | (bus << 16) | (slot << 11) 
  147                     | (func << 8) | (reg & ~0x03));
  148                 dataport = CONF1_DATA_PORT + (reg & 0x03);
  149         }
  150         return (dataport);
  151 }
  152 
  153 /* disable configuration space accesses */
  154 static void
  155 pci_cfgdisable(void)
  156 {
  157 
  158         /*
  159          * Do nothing.  Writing a 0 to the address port can apparently
  160          * confuse some bridges and cause spurious access failures.
  161          */
  162 }
  163 
  164 static int
  165 pcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
  166 {
  167         int data = -1;
  168         int port;
  169 
  170         mtx_lock_spin(&pcicfg_mtx);
  171         port = pci_cfgenable(bus, slot, func, reg, bytes);
  172         if (port != 0) {
  173                 switch (bytes) {
  174                 case 1:
  175                         data = inb(port);
  176                         break;
  177                 case 2:
  178                         data = inw(port);
  179                         break;
  180                 case 4:
  181                         data = inl(port);
  182                         break;
  183                 }
  184                 pci_cfgdisable();
  185         }
  186         mtx_unlock_spin(&pcicfg_mtx);
  187         return (data);
  188 }
  189 
  190 static void
  191 pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
  192 {
  193         int port;
  194 
  195         mtx_lock_spin(&pcicfg_mtx);
  196         port = pci_cfgenable(bus, slot, func, reg, bytes);
  197         if (port != 0) {
  198                 switch (bytes) {
  199                 case 1:
  200                         outb(port, data);
  201                         break;
  202                 case 2:
  203                         outw(port, data);
  204                         break;
  205                 case 4:
  206                         outl(port, data);
  207                         break;
  208                 }
  209                 pci_cfgdisable();
  210         }
  211         mtx_unlock_spin(&pcicfg_mtx);
  212 }
  213 
  214 int
  215 pcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus)
  216 {
  217         uint32_t val1, val2;
  218         int slot;
  219 
  220         if (!mcfg_enable)
  221                 return (0);
  222 
  223         if (minbus != 0)
  224                 return (0);
  225 
  226         if (bootverbose)
  227                 printf("PCIe: Memory Mapped configuration base @ 0x%lx\n",
  228                     base);
  229 
  230         /* XXX: We should make sure this really fits into the direct map. */
  231         pcie_base = (vm_offset_t)pmap_mapdev_pciecfg(base, (maxbus + 1) << 20);
  232         pcie_minbus = minbus;
  233         pcie_maxbus = maxbus;
  234         cfgmech = CFGMECH_PCIE;
  235 
  236         /*
  237          * On some AMD systems, some of the devices on bus 0 are
  238          * inaccessible using memory-mapped PCI config access.  Walk
  239          * bus 0 looking for such devices.  For these devices, we will
  240          * fall back to using type 1 config access instead.
  241          */
  242         if (pci_cfgregopen() != 0) {
  243                 for (slot = 0; slot <= PCI_SLOTMAX; slot++) {
  244                         val1 = pcireg_cfgread(0, slot, 0, 0, 4);
  245                         if (val1 == 0xffffffff)
  246                                 continue;
  247 
  248                         val2 = pciereg_cfgread(0, slot, 0, 0, 4);
  249                         if (val2 != val1)
  250                                 pcie_badslots |= (1 << slot);
  251                 }
  252         }
  253 
  254         return (1);
  255 }
  256 
  257 #define PCIE_VADDR(base, reg, bus, slot, func)  \
  258         ((base)                         +       \
  259         ((((bus) & 0xff) << 20)         |       \
  260         (((slot) & 0x1f) << 15)         |       \
  261         (((func) & 0x7) << 12)          |       \
  262         ((reg) & 0xfff)))
  263 
  264 /*
  265  * AMD BIOS And Kernel Developer's Guides for CPU families starting with 10h
  266  * have a requirement that all accesses to the memory mapped PCI configuration
  267  * space are done using AX class of registers.
  268  * Since other vendors do not currently have any contradicting requirements
  269  * the AMD access pattern is applied universally.
  270  */
  271 
  272 static int
  273 pciereg_cfgread(int bus, unsigned slot, unsigned func, unsigned reg,
  274     unsigned bytes)
  275 {
  276         vm_offset_t va;
  277         int data = -1;
  278 
  279         if (bus < pcie_minbus || bus > pcie_maxbus || slot > PCI_SLOTMAX ||
  280             func > PCI_FUNCMAX || reg > PCIE_REGMAX)
  281                 return (-1);
  282 
  283         va = PCIE_VADDR(pcie_base, reg, bus, slot, func);
  284 
  285         switch (bytes) {
  286         case 4:
  287                 __asm("movl %1, %0" : "=a" (data)
  288                     : "m" (*(volatile uint32_t *)va));
  289                 break;
  290         case 2:
  291                 __asm("movzwl %1, %0" : "=a" (data)
  292                     : "m" (*(volatile uint16_t *)va));
  293                 break;
  294         case 1:
  295                 __asm("movzbl %1, %0" : "=a" (data)
  296                     : "m" (*(volatile uint8_t *)va));
  297                 break;
  298         }
  299 
  300         return (data);
  301 }
  302 
  303 static void
  304 pciereg_cfgwrite(int bus, unsigned slot, unsigned func, unsigned reg, int data,
  305     unsigned bytes)
  306 {
  307         vm_offset_t va;
  308 
  309         if (bus < pcie_minbus || bus > pcie_maxbus || slot > PCI_SLOTMAX ||
  310             func > PCI_FUNCMAX || reg > PCIE_REGMAX)
  311                 return;
  312 
  313         va = PCIE_VADDR(pcie_base, reg, bus, slot, func);
  314 
  315         switch (bytes) {
  316         case 4:
  317                 __asm("movl %1, %0" : "=m" (*(volatile uint32_t *)va)
  318                     : "a" (data));
  319                 break;
  320         case 2:
  321                 __asm("movw %1, %0" : "=m" (*(volatile uint16_t *)va)
  322                     : "a" ((uint16_t)data));
  323                 break;
  324         case 1:
  325                 __asm("movb %1, %0" : "=m" (*(volatile uint8_t *)va)
  326                     : "a" ((uint8_t)data));
  327                 break;
  328         }
  329 }

Cache object: 2b1ff8a9974f2f2633287bb16aa0fd19


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