pci_cfgreg.c revision 192342
1130803Smarcel/*- 2130803Smarcel * Copyright (c) 1997, Stefan Esser <se@freebsd.org> 3130803Smarcel * Copyright (c) 2000, Michael Smith <msmith@freebsd.org> 4130803Smarcel * Copyright (c) 2000, BSDi 5130803Smarcel * All rights reserved. 6130803Smarcel * 7130803Smarcel * Redistribution and use in source and binary forms, with or without 8130803Smarcel * modification, are permitted provided that the following conditions 9130803Smarcel * are met: 10130803Smarcel * 1. Redistributions of source code must retain the above copyright 11130803Smarcel * notice unmodified, this list of conditions, and the following 12130803Smarcel * disclaimer. 13130803Smarcel * 2. Redistributions in binary form must reproduce the above copyright 14130803Smarcel * notice, this list of conditions and the following disclaimer in the 15130803Smarcel * documentation and/or other materials provided with the distribution. 16130803Smarcel * 17130803Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18130803Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19130803Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20130803Smarcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21130803Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22130803Smarcel * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23130803Smarcel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24130803Smarcel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25130803Smarcel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26130803Smarcel * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27130803Smarcel */ 28130803Smarcel 29130803Smarcel#include <sys/cdefs.h> 30130803Smarcel__FBSDID("$FreeBSD: head/sys/amd64/pci/pci_cfgreg.c 192342 2009-05-18 21:47:32Z jhb $"); 31130803Smarcel 32130803Smarcel#include <sys/param.h> 33130803Smarcel#include <sys/systm.h> 34130803Smarcel#include <sys/bus.h> 35130803Smarcel#include <sys/lock.h> 36130803Smarcel#include <sys/kernel.h> 37130803Smarcel#include <sys/mutex.h> 38130803Smarcel#include <sys/sysctl.h> 39130803Smarcel#include <dev/pci/pcivar.h> 40130803Smarcel#include <dev/pci/pcireg.h> 41130803Smarcel#include <vm/vm.h> 42130803Smarcel#include <vm/pmap.h> 43130803Smarcel#include <machine/pci_cfgreg.h> 44130803Smarcel 45130803Smarcelenum { 46130803Smarcel CFGMECH_NONE = 0, 47130803Smarcel CFGMECH_1, 48130803Smarcel CFGMECH_PCIE, 49130803Smarcel}; 50130803Smarcel 51130803Smarcelstatic uint32_t pci_docfgregread(int bus, int slot, int func, int reg, 52130803Smarcel int bytes); 53130803Smarcelstatic int pciereg_cfgread(int bus, unsigned slot, unsigned func, 54130803Smarcel unsigned reg, unsigned bytes); 55130803Smarcelstatic void pciereg_cfgwrite(int bus, unsigned slot, unsigned func, 56130803Smarcel unsigned reg, int data, unsigned bytes); 57130803Smarcelstatic int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes); 58130803Smarcelstatic void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes); 59130803Smarcel 60130803SmarcelSYSCTL_DECL(_hw_pci); 61130803Smarcel 62130803Smarcelstatic int cfgmech; 63130803Smarcelstatic vm_offset_t pcie_base; 64130803Smarcelstatic int pcie_minbus, pcie_maxbus; 65130803Smarcelstatic uint32_t pcie_badslots; 66130803Smarcelstatic struct mtx pcicfg_mtx; 67130803Smarcelstatic int mcfg_enable = 1; 68130803SmarcelTUNABLE_INT("hw.pci.mcfg", &mcfg_enable); 69130803SmarcelSYSCTL_INT(_hw_pci, OID_AUTO, mcfg, CTLFLAG_RDTUN, &mcfg_enable, 0, 70130803Smarcel "Enable support for PCI-e memory mapped config access"); 71130803Smarcel 72130803Smarcel/* 73130803Smarcel * Initialise access to PCI configuration space 74130803Smarcel */ 75130803Smarcelint 76130803Smarcelpci_cfgregopen(void) 77130803Smarcel{ 78130803Smarcel static int once = 0; 79130803Smarcel uint64_t pciebar; 80130803Smarcel uint16_t did, vid; 81130803Smarcel 82130803Smarcel if (!once) { 83130803Smarcel mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN); 84130803Smarcel once = 1; 85130803Smarcel } 86130803Smarcel 87130803Smarcel if (cfgmech != CFGMECH_NONE) 88130803Smarcel return (1); 89130803Smarcel cfgmech = CFGMECH_1; 90130803Smarcel 91130803Smarcel /* 92130803Smarcel * Grope around in the PCI config space to see if this is a 93130803Smarcel * chipset that is capable of doing memory-mapped config cycles. 94130803Smarcel * This also implies that it can do PCIe extended config cycles. 95130803Smarcel */ 96130803Smarcel 97130803Smarcel /* Check for supported chipsets */ 98130803Smarcel vid = pci_cfgregread(0, 0, 0, PCIR_VENDOR, 2); 99130803Smarcel did = pci_cfgregread(0, 0, 0, PCIR_DEVICE, 2); 100130803Smarcel switch (vid) { 101130803Smarcel case 0x8086: 102130803Smarcel switch (did) { 103130803Smarcel case 0x3590: 104130803Smarcel case 0x3592: 105130803Smarcel /* Intel 7520 or 7320 */ 106130803Smarcel pciebar = pci_cfgregread(0, 0, 0, 0xce, 2) << 16; 107130803Smarcel pcie_cfgregopen(pciebar, 0, 255); 108130803Smarcel break; 109130803Smarcel case 0x2580: 110130803Smarcel case 0x2584: 111130803Smarcel case 0x2590: 112130803Smarcel /* Intel 915, 925, or 915GM */ 113130803Smarcel pciebar = pci_cfgregread(0, 0, 0, 0x48, 4); 114130803Smarcel pcie_cfgregopen(pciebar, 0, 255); 115130803Smarcel break; 116130803Smarcel } 117130803Smarcel } 118130803Smarcel 119130803Smarcel return (1); 120130803Smarcel} 121130803Smarcel 122130803Smarcelstatic uint32_t 123130803Smarcelpci_docfgregread(int bus, int slot, int func, int reg, int bytes) 124130803Smarcel{ 125130803Smarcel 126130803Smarcel if (cfgmech == CFGMECH_PCIE && 127130803Smarcel (bus >= pcie_minbus && bus <= pcie_maxbus) && 128130803Smarcel (bus != 0 || !(1 << slot & pcie_badslots))) 129130803Smarcel return (pciereg_cfgread(bus, slot, func, reg, bytes)); 130130803Smarcel else 131130803Smarcel return (pcireg_cfgread(bus, slot, func, reg, bytes)); 132130803Smarcel} 133130803Smarcel 134130803Smarcel/* 135130803Smarcel * Read configuration space register 136130803Smarcel */ 137130803Smarcelu_int32_t 138130803Smarcelpci_cfgregread(int bus, int slot, int func, int reg, int bytes) 139130803Smarcel{ 140130803Smarcel uint32_t line; 141130803Smarcel 142130803Smarcel /* 143130803Smarcel * Some BIOS writers seem to want to ignore the spec and put 144130803Smarcel * 0 in the intline rather than 255 to indicate none. Some use 145130803Smarcel * numbers in the range 128-254 to indicate something strange and 146130803Smarcel * apparently undocumented anywhere. Assume these are completely bogus 147130803Smarcel * and map them to 255, which the rest of the PCI code recognizes as 148130803Smarcel * as an invalid IRQ. 149130803Smarcel */ 150130803Smarcel if (reg == PCIR_INTLINE && bytes == 1) { 151130803Smarcel line = pci_docfgregread(bus, slot, func, PCIR_INTLINE, 1); 152130803Smarcel if (line == 0 || line >= 128) 153130803Smarcel line = PCI_INVALID_IRQ; 154130803Smarcel return (line); 155130803Smarcel } 156130803Smarcel return (pci_docfgregread(bus, slot, func, reg, bytes)); 157130803Smarcel} 158130803Smarcel 159130803Smarcel/* 160130803Smarcel * Write configuration space register 161130803Smarcel */ 162130803Smarcelvoid 163130803Smarcelpci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes) 164130803Smarcel{ 165130803Smarcel 166130803Smarcel if (cfgmech == CFGMECH_PCIE && 167130803Smarcel (bus >= pcie_minbus && bus <= pcie_maxbus) && 168130803Smarcel (bus != 0 || !(1 << slot & pcie_badslots))) 169130803Smarcel pciereg_cfgwrite(bus, slot, func, reg, data, bytes); 170130803Smarcel else 171130803Smarcel pcireg_cfgwrite(bus, slot, func, reg, data, bytes); 172130803Smarcel} 173130803Smarcel 174130803Smarcel/* 175130803Smarcel * Configuration space access using direct register operations 176130803Smarcel */ 177130803Smarcel 178130803Smarcel/* enable configuration space accesses and return data port address */ 179130803Smarcelstatic int 180130803Smarcelpci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) 181130803Smarcel{ 182130803Smarcel int dataport = 0; 183130803Smarcel 184130803Smarcel if (bus <= PCI_BUSMAX && slot < 32 && func <= PCI_FUNCMAX && 185130803Smarcel reg <= PCI_REGMAX && bytes != 3 && (unsigned) bytes <= 4 && 186130803Smarcel (reg & (bytes - 1)) == 0) { 187130803Smarcel outl(CONF1_ADDR_PORT, (1 << 31) | (bus << 16) | (slot << 11) 188130803Smarcel | (func << 8) | (reg & ~0x03)); 189130803Smarcel dataport = CONF1_DATA_PORT + (reg & 0x03); 190130803Smarcel } 191130803Smarcel return (dataport); 192130803Smarcel} 193130803Smarcel 194130803Smarcel/* disable configuration space accesses */ 195130803Smarcelstatic void 196130803Smarcelpci_cfgdisable(void) 197130803Smarcel{ 198130803Smarcel 199130803Smarcel /* 200130803Smarcel * Do nothing. Writing a 0 to the address port can apparently 201130803Smarcel * confuse some bridges and cause spurious access failures. 202130803Smarcel */ 203130803Smarcel} 204130803Smarcel 205130803Smarcelstatic int 206130803Smarcelpcireg_cfgread(int bus, int slot, int func, int reg, int bytes) 207130803Smarcel{ 208130803Smarcel int data = -1; 209130803Smarcel int port; 210130803Smarcel 211130803Smarcel mtx_lock_spin(&pcicfg_mtx); 212130803Smarcel port = pci_cfgenable(bus, slot, func, reg, bytes); 213130803Smarcel if (port != 0) { 214130803Smarcel switch (bytes) { 215130803Smarcel case 1: 216130803Smarcel data = inb(port); 217130803Smarcel break; 218130803Smarcel case 2: 219130803Smarcel data = inw(port); 220130803Smarcel break; 221130803Smarcel case 4: 222130803Smarcel data = inl(port); 223130803Smarcel break; 224130803Smarcel } 225130803Smarcel pci_cfgdisable(); 226130803Smarcel } 227130803Smarcel mtx_unlock_spin(&pcicfg_mtx); 228130803Smarcel return (data); 229130803Smarcel} 230130803Smarcel 231130803Smarcelstatic void 232130803Smarcelpcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 233130803Smarcel{ 234130803Smarcel int port; 235130803Smarcel 236130803Smarcel mtx_lock_spin(&pcicfg_mtx); 237130803Smarcel port = pci_cfgenable(bus, slot, func, reg, bytes); 238130803Smarcel if (port != 0) { 239130803Smarcel switch (bytes) { 240130803Smarcel case 1: 241130803Smarcel outb(port, data); 242130803Smarcel break; 243130803Smarcel case 2: 244130803Smarcel outw(port, data); 245130803Smarcel break; 246130803Smarcel case 4: 247130803Smarcel outl(port, data); 248130803Smarcel break; 249130803Smarcel } 250130803Smarcel pci_cfgdisable(); 251130803Smarcel } 252130803Smarcel mtx_unlock_spin(&pcicfg_mtx); 253130803Smarcel} 254130803Smarcel 255130803Smarcelint 256130803Smarcelpcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus) 257130803Smarcel{ 258130803Smarcel uint32_t val1, val2; 259130803Smarcel int slot; 260130803Smarcel 261130803Smarcel if (!mcfg_enable) 262130803Smarcel return (0); 263130803Smarcel 264130803Smarcel if (minbus != 0) 265130803Smarcel return (0); 266130803Smarcel 267130803Smarcel if (bootverbose) 268130803Smarcel printf("PCIe: Memory Mapped configuration base @ 0x%lx\n", 269130803Smarcel base); 270130803Smarcel 271130803Smarcel /* XXX: We should make sure this really fits into the direct map. */ 272130803Smarcel pcie_base = (vm_offset_t)pmap_mapdev(base, (maxbus + 1) << 20); 273130803Smarcel pcie_minbus = minbus; 274130803Smarcel pcie_maxbus = maxbus; 275130803Smarcel cfgmech = CFGMECH_PCIE; 276130803Smarcel 277130803Smarcel /* 278130803Smarcel * On some AMD systems, some of the devices on bus 0 are 279130803Smarcel * inaccessible using memory-mapped PCI config access. Walk 280130803Smarcel * bus 0 looking for such devices. For these devices, we will 281130803Smarcel * fall back to using type 1 config access instead. 282130803Smarcel */ 283130803Smarcel if (pci_cfgregopen() != 0) { 284130803Smarcel for (slot = 0; slot < 32; slot++) { 285130803Smarcel val1 = pcireg_cfgread(0, slot, 0, 0, 4); 286130803Smarcel if (val1 == 0xffffffff) 287130803Smarcel continue; 288130803Smarcel 289130803Smarcel val2 = pciereg_cfgread(0, slot, 0, 0, 4); 290130803Smarcel if (val2 != val1) 291130803Smarcel pcie_badslots |= (1 << slot); 292130803Smarcel } 293130803Smarcel } 294130803Smarcel 295130803Smarcel return (1); 296130803Smarcel} 297130803Smarcel 298130803Smarcel#define PCIE_VADDR(base, reg, bus, slot, func) \ 299130803Smarcel ((base) + \ 300130803Smarcel ((((bus) & 0xff) << 20) | \ 301130803Smarcel (((slot) & 0x1f) << 15) | \ 302130803Smarcel (((func) & 0x7) << 12) | \ 303130803Smarcel ((reg) & 0xfff))) 304130803Smarcel 305130803Smarcelstatic int 306130803Smarcelpciereg_cfgread(int bus, unsigned slot, unsigned func, unsigned reg, 307130803Smarcel unsigned bytes) 308130803Smarcel{ 309130803Smarcel volatile vm_offset_t va; 310130803Smarcel int data = -1; 311130803Smarcel 312130803Smarcel if (bus < pcie_minbus || bus > pcie_maxbus || slot >= 32 || 313130803Smarcel func > PCI_FUNCMAX || reg >= 0x1000) 314130803Smarcel return (-1); 315130803Smarcel 316130803Smarcel va = PCIE_VADDR(pcie_base, reg, bus, slot, func); 317130803Smarcel 318130803Smarcel switch (bytes) { 319130803Smarcel case 4: 320130803Smarcel data = *(volatile uint32_t *)(va); 321130803Smarcel break; 322130803Smarcel case 2: 323130803Smarcel data = *(volatile uint16_t *)(va); 324130803Smarcel break; 325130803Smarcel case 1: 326130803Smarcel data = *(volatile uint8_t *)(va); 327130803Smarcel break; 328130803Smarcel } 329130803Smarcel 330130803Smarcel return (data); 331130803Smarcel} 332130803Smarcel 333130803Smarcelstatic void 334130803Smarcelpciereg_cfgwrite(int bus, unsigned slot, unsigned func, unsigned reg, int data, 335130803Smarcel unsigned bytes) 336130803Smarcel{ 337130803Smarcel volatile vm_offset_t va; 338130803Smarcel 339130803Smarcel if (bus < pcie_minbus || bus > pcie_maxbus || slot >= 32 || 340130803Smarcel func > PCI_FUNCMAX || reg >= 0x1000) 341130803Smarcel return; 342130803Smarcel 343130803Smarcel va = PCIE_VADDR(pcie_base, reg, bus, slot, func); 344130803Smarcel 345130803Smarcel switch (bytes) { 346130803Smarcel case 4: 347130803Smarcel *(volatile uint32_t *)(va) = data; 348130803Smarcel break; 349130803Smarcel case 2: 350130803Smarcel *(volatile uint16_t *)(va) = data; 351130803Smarcel break; 352130803Smarcel case 1: 353130803Smarcel *(volatile uint8_t *)(va) = data; 354130803Smarcel break; 355130803Smarcel } 356130803Smarcel} 357130803Smarcel