1139790Simp/*- 226159Sse * Copyright (c) 1997, Stefan Esser <se@freebsd.org> 366529Smsmith * Copyright (c) 2000, Michael Smith <msmith@freebsd.org> 466529Smsmith * Copyright (c) 2000, BSDi 5138468Sscottl * Copyright (c) 2004, Scott Long <scottl@freebsd.org> 626159Sse * All rights reserved. 726159Sse * 826159Sse * Redistribution and use in source and binary forms, with or without 926159Sse * modification, are permitted provided that the following conditions 1026159Sse * are met: 1126159Sse * 1. Redistributions of source code must retain the above copyright 1226159Sse * notice unmodified, this list of conditions, and the following 1326159Sse * disclaimer. 1426159Sse * 2. Redistributions in binary form must reproduce the above copyright 1526159Sse * notice, this list of conditions and the following disclaimer in the 1626159Sse * documentation and/or other materials provided with the distribution. 1726159Sse * 1826159Sse * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1926159Sse * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2026159Sse * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2126159Sse * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2226159Sse * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2326159Sse * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2426159Sse * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2526159Sse * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2626159Sse * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2726159Sse * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2826159Sse */ 296104Sse 30115706Sobrien#include <sys/cdefs.h> 31115706Sobrien__FBSDID("$FreeBSD$"); 32115706Sobrien 33152219Simp#include "opt_xbox.h" 34152219Simp 35125981Sjhb#include <sys/param.h> 366734Sbde#include <sys/systm.h> 3747307Speter#include <sys/bus.h> 38111068Speter#include <sys/lock.h> 39182947Sjhb#include <sys/kernel.h> 40111068Speter#include <sys/mutex.h> 41138429Sscottl#include <sys/malloc.h> 42138429Sscottl#include <sys/queue.h> 43192342Sjhb#include <sys/sysctl.h> 44100435Simp#include <dev/pci/pcivar.h> 45100435Simp#include <dev/pci/pcireg.h> 4666529Smsmith#include <machine/pci_cfgreg.h> 4759294Smsmith#include <machine/pc/bios.h> 4859294Smsmith 49138429Sscottl#include <vm/vm.h> 50138429Sscottl#include <vm/vm_param.h> 51138429Sscottl#include <vm/vm_kern.h> 52138429Sscottl#include <vm/vm_extern.h> 53138429Sscottl#include <vm/pmap.h> 54138429Sscottl#include <machine/pmap.h> 55138429Sscottl 56152219Simp#ifdef XBOX 57152219Simp#include <machine/xbox.h> 58152219Simp#endif 59152219Simp 60103868Sjhb#define PRVERB(a) do { \ 61103868Sjhb if (bootverbose) \ 62103868Sjhb printf a ; \ 63103868Sjhb} while(0) 6482441Simp 65138429Sscottl#define PCIE_CACHE 8 66138429Sscottlstruct pcie_cfg_elem { 67138429Sscottl TAILQ_ENTRY(pcie_cfg_elem) elem; 68138429Sscottl vm_offset_t vapage; 69138429Sscottl vm_paddr_t papage; 70138429Sscottl}; 71138429Sscottl 72138429Sscottlenum { 73138429Sscottl CFGMECH_NONE = 0, 74138429Sscottl CFGMECH_1, 75138429Sscottl CFGMECH_2, 76138429Sscottl CFGMECH_PCIE, 77138429Sscottl}; 78138429Sscottl 79192342SjhbSYSCTL_DECL(_hw_pci); 80192342Sjhb 81138429Sscottlstatic TAILQ_HEAD(pcie_cfg_list, pcie_cfg_elem) pcie_list[MAXCPU]; 82181987Sjhbstatic uint64_t pcie_base; 83181987Sjhbstatic int pcie_minbus, pcie_maxbus; 84182910Sjhbstatic uint32_t pcie_badslots; 8526159Ssestatic int cfgmech; 8626159Ssestatic int devmax; 87138429Sscottlstatic struct mtx pcicfg_mtx; 88182947Sjhbstatic int mcfg_enable = 1; 89182947SjhbTUNABLE_INT("hw.pci.mcfg", &mcfg_enable); 90192342SjhbSYSCTL_INT(_hw_pci, OID_AUTO, mcfg, CTLFLAG_RDTUN, &mcfg_enable, 0, 91192342Sjhb "Enable support for PCI-e memory mapped config access"); 926104Sse 93182910Sjhbstatic uint32_t pci_docfgregread(int bus, int slot, int func, int reg, 94182910Sjhb int bytes); 9566529Smsmithstatic int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes); 9666529Smsmithstatic void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes); 97181775Skmacy#ifndef XEN 9859294Smsmithstatic int pcireg_cfgopen(void); 99181775Skmacy#endif 100181987Sjhbstatic int pciereg_cfgread(int bus, unsigned slot, unsigned func, 101181987Sjhb unsigned reg, unsigned bytes); 102181987Sjhbstatic void pciereg_cfgwrite(int bus, unsigned slot, unsigned func, 103181987Sjhb unsigned reg, int data, unsigned bytes); 104111068Speter 10597694Simp/* 10697694Simp * Some BIOS writers seem to want to ignore the spec and put 10797694Simp * 0 in the intline rather than 255 to indicate none. Some use 10897694Simp * numbers in the range 128-254 to indicate something strange and 10997694Simp * apparently undocumented anywhere. Assume these are completely bogus 11097694Simp * and map them to 255, which means "none". 11197694Simp */ 112131575Sstefanfstatic __inline int 11397694Simppci_i386_map_intline(int line) 11497694Simp{ 115100435Simp if (line == 0 || line >= 128) 116100435Simp return (PCI_INVALID_IRQ); 117100435Simp return (line); 11897694Simp} 11997694Simp 120181775Skmacy#ifndef XEN 12182441Simpstatic u_int16_t 12282441Simppcibios_get_version(void) 12382441Simp{ 124100435Simp struct bios_regs args; 12582441Simp 126102976Sjhb if (PCIbios.ventry == 0) { 127100435Simp PRVERB(("pcibios: No call entry point\n")); 128100435Simp return (0); 129100435Simp } 130100435Simp args.eax = PCIBIOS_BIOS_PRESENT; 131100435Simp if (bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL))) { 132100435Simp PRVERB(("pcibios: BIOS_PRESENT call failed\n")); 133100435Simp return (0); 134100435Simp } 135100435Simp if (args.edx != 0x20494350) { 136100435Simp PRVERB(("pcibios: BIOS_PRESENT didn't return 'PCI ' in edx\n")); 137100435Simp return (0); 138100435Simp } 139100435Simp return (args.ebx & 0xffff); 14082441Simp} 141181775Skmacy#endif 14282441Simp 14366529Smsmith/* 14466529Smsmith * Initialise access to PCI configuration space 14566529Smsmith */ 14666529Smsmithint 14766529Smsmithpci_cfgregopen(void) 14859294Smsmith{ 149181775Skmacy#ifdef XEN 150181775Skmacy return (0); 151181775Skmacy#else 152100435Simp static int opened = 0; 153181987Sjhb uint64_t pciebar; 154138429Sscottl u_int16_t vid, did; 155111068Speter u_int16_t v; 15665176Sdfr 157100435Simp if (opened) 158181987Sjhb return (1); 15966529Smsmith 160181987Sjhb if (cfgmech == CFGMECH_NONE && pcireg_cfgopen() == 0) 161181987Sjhb return (0); 16267185Simp 163111068Speter v = pcibios_get_version(); 164111068Speter if (v > 0) 165131398Sjhb PRVERB(("pcibios: BIOS version %x.%02x\n", (v & 0xff00) >> 8, 166131398Sjhb v & 0xff)); 167111068Speter mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN); 168100435Simp opened = 1; 169125981Sjhb 170125981Sjhb /* $PIR requires PCI BIOS 2.10 or greater. */ 171125981Sjhb if (v >= 0x0210) 172125981Sjhb pci_pir_open(); 173138429Sscottl 174181987Sjhb if (cfgmech == CFGMECH_PCIE) 175181987Sjhb return (1); 176181987Sjhb 177138429Sscottl /* 178138429Sscottl * Grope around in the PCI config space to see if this is a 179138429Sscottl * chipset that is capable of doing memory-mapped config cycles. 180138429Sscottl * This also implies that it can do PCIe extended config cycles. 181138429Sscottl */ 182138429Sscottl 183153243Srodrigc /* Check for supported chipsets */ 184165126Sjhb vid = pci_cfgregread(0, 0, 0, PCIR_VENDOR, 2); 185165126Sjhb did = pci_cfgregread(0, 0, 0, PCIR_DEVICE, 2); 186181931Sjhb switch (vid) { 187181931Sjhb case 0x8086: 188181931Sjhb switch (did) { 189181931Sjhb case 0x3590: 190181931Sjhb case 0x3592: 191153243Srodrigc /* Intel 7520 or 7320 */ 192153243Srodrigc pciebar = pci_cfgregread(0, 0, 0, 0xce, 2) << 16; 193181987Sjhb pcie_cfgregopen(pciebar, 0, 255); 194181931Sjhb break; 195181931Sjhb case 0x2580: 196181931Sjhb case 0x2584: 197181933Sjhb case 0x2590: 198181933Sjhb /* Intel 915, 925, or 915GM */ 199153243Srodrigc pciebar = pci_cfgregread(0, 0, 0, 0x48, 4); 200181987Sjhb pcie_cfgregopen(pciebar, 0, 255); 201181931Sjhb break; 202153243Srodrigc } 203138429Sscottl } 204138429Sscottl 205100435Simp return(1); 206181775Skmacy#endif 20759294Smsmith} 20859294Smsmith 209182910Sjhbstatic uint32_t 210182910Sjhbpci_docfgregread(int bus, int slot, int func, int reg, int bytes) 211182910Sjhb{ 212182910Sjhb 213182910Sjhb if (cfgmech == CFGMECH_PCIE && 214190386Sjhb (bus >= pcie_minbus && bus <= pcie_maxbus) && 215182910Sjhb (bus != 0 || !(1 << slot & pcie_badslots))) 216182910Sjhb return (pciereg_cfgread(bus, slot, func, reg, bytes)); 217182910Sjhb else 218182910Sjhb return (pcireg_cfgread(bus, slot, func, reg, bytes)); 219182910Sjhb} 220182910Sjhb 22166529Smsmith/* 22269783Smsmith * Read configuration space register 22366529Smsmith */ 22469783Smsmithu_int32_t 22569783Smsmithpci_cfgregread(int bus, int slot, int func, int reg, int bytes) 22669783Smsmith{ 227100435Simp uint32_t line; 22869783Smsmith 229100435Simp /* 230100435Simp * Some BIOS writers seem to want to ignore the spec and put 231100435Simp * 0 in the intline rather than 255 to indicate none. The rest of 232100435Simp * the code uses 255 as an invalid IRQ. 233100435Simp */ 234100435Simp if (reg == PCIR_INTLINE && bytes == 1) { 235182910Sjhb line = pci_docfgregread(bus, slot, func, PCIR_INTLINE, 1); 236121986Sjhb return (pci_i386_map_intline(line)); 237100435Simp } 238182910Sjhb return (pci_docfgregread(bus, slot, func, reg, bytes)); 23969783Smsmith} 24069783Smsmith 24166529Smsmith/* 24266529Smsmith * Write configuration space register 24366529Smsmith */ 24466529Smsmithvoid 24566529Smsmithpci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes) 24659294Smsmith{ 247111068Speter 248182910Sjhb if (cfgmech == CFGMECH_PCIE && 249190386Sjhb (bus >= pcie_minbus && bus <= pcie_maxbus) && 250182910Sjhb (bus != 0 || !(1 << slot & pcie_badslots))) 251182910Sjhb pciereg_cfgwrite(bus, slot, func, reg, data, bytes); 252182910Sjhb else 253182910Sjhb pcireg_cfgwrite(bus, slot, func, reg, data, bytes); 25459294Smsmith} 25559294Smsmith 25666529Smsmith/* 25766529Smsmith * Configuration space access using direct register operations 25866529Smsmith */ 25959294Smsmith 26026159Sse/* enable configuration space accesses and return data port address */ 26110887Ssestatic int 26226159Ssepci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) 26326159Sse{ 264100435Simp int dataport = 0; 26510887Sse 266152219Simp#ifdef XBOX 267152219Simp if (arch_i386_is_xbox) { 268152219Simp /* 269152219Simp * The Xbox MCPX chipset is a derivative of the nForce 1 270152219Simp * chipset. It almost has the same bus layout; some devices 271152219Simp * cannot be used, because they have been removed. 272152219Simp */ 273152219Simp 274152219Simp /* 275152219Simp * Devices 00:00.1 and 00:00.2 used to be memory controllers on 276152219Simp * the nForce chipset, but on the Xbox, using them will lockup 277152219Simp * the chipset. 278152219Simp */ 279152219Simp if (bus == 0 && slot == 0 && (func == 1 || func == 2)) 280152219Simp return dataport; 281152219Simp 282152219Simp /* 283152219Simp * Bus 1 only contains a VGA controller at 01:00.0. When you try 284152219Simp * to probe beyond that device, you only get garbage, which 285152219Simp * could cause lockups. 286152219Simp */ 287152219Simp if (bus == 1 && (slot != 0 || func != 0)) 288152219Simp return dataport; 289152219Simp 290152219Simp /* 291152219Simp * Bus 2 used to contain the AGP controller, but the Xbox MCPX 292152219Simp * doesn't have one. Probing it can cause lockups. 293152219Simp */ 294152219Simp if (bus >= 2) 295152219Simp return dataport; 296152219Simp } 297152219Simp#endif 298152219Simp 299100435Simp if (bus <= PCI_BUSMAX 300100435Simp && slot < devmax 301100435Simp && func <= PCI_FUNCMAX 302197450Savg && (unsigned)reg <= PCI_REGMAX 303100435Simp && bytes != 3 304197450Savg && (unsigned)bytes <= 4 305106901Simp && (reg & (bytes - 1)) == 0) { 306100435Simp switch (cfgmech) { 307182910Sjhb case CFGMECH_PCIE: 308138429Sscottl case CFGMECH_1: 309261455Seadler outl(CONF1_ADDR_PORT, (1U << 31) 310100435Simp | (bus << 16) | (slot << 11) 311100435Simp | (func << 8) | (reg & ~0x03)); 312100435Simp dataport = CONF1_DATA_PORT + (reg & 0x03); 313100435Simp break; 314138429Sscottl case CFGMECH_2: 315100435Simp outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); 316100435Simp outb(CONF2_FORWARD_PORT, bus); 317100435Simp dataport = 0xc000 | (slot << 8) | reg; 318100435Simp break; 319100435Simp } 32026159Sse } 321100435Simp return (dataport); 32226159Sse} 3236104Sse 32426159Sse/* disable configuration space accesses */ 3256104Ssestatic void 32626159Ssepci_cfgdisable(void) 32726159Sse{ 328100435Simp switch (cfgmech) { 329182910Sjhb case CFGMECH_PCIE: 330138429Sscottl case CFGMECH_1: 331174052Sjhb /* 332174052Sjhb * Do nothing for the config mechanism 1 case. 333174052Sjhb * Writing a 0 to the address port can apparently 334174052Sjhb * confuse some bridges and cause spurious 335174052Sjhb * access failures. 336174052Sjhb */ 337152075Speter break; 338138429Sscottl case CFGMECH_2: 339100435Simp outb(CONF2_ENABLE_PORT, 0); 340100435Simp break; 341100435Simp } 34226159Sse} 3436104Sse 34459294Smsmithstatic int 34565176Sdfrpcireg_cfgread(int bus, int slot, int func, int reg, int bytes) 34626159Sse{ 347100435Simp int data = -1; 348100435Simp int port; 3497234Sse 350111068Speter mtx_lock_spin(&pcicfg_mtx); 351100435Simp port = pci_cfgenable(bus, slot, func, reg, bytes); 352100435Simp if (port != 0) { 353100435Simp switch (bytes) { 354100435Simp case 1: 355100435Simp data = inb(port); 356100435Simp break; 357100435Simp case 2: 358100435Simp data = inw(port); 359100435Simp break; 360100435Simp case 4: 361100435Simp data = inl(port); 362100435Simp break; 363100435Simp } 364100435Simp pci_cfgdisable(); 36526159Sse } 366111068Speter mtx_unlock_spin(&pcicfg_mtx); 367100435Simp return (data); 36826159Sse} 3697234Sse 37059294Smsmithstatic void 37165176Sdfrpcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 37226159Sse{ 373100435Simp int port; 3746104Sse 375111068Speter mtx_lock_spin(&pcicfg_mtx); 376100435Simp port = pci_cfgenable(bus, slot, func, reg, bytes); 377100435Simp if (port != 0) { 378100435Simp switch (bytes) { 379100435Simp case 1: 380100435Simp outb(port, data); 381100435Simp break; 382100435Simp case 2: 383100435Simp outw(port, data); 384100435Simp break; 385100435Simp case 4: 386100435Simp outl(port, data); 387100435Simp break; 388100435Simp } 389100435Simp pci_cfgdisable(); 39026159Sse } 391111068Speter mtx_unlock_spin(&pcicfg_mtx); 39226159Sse} 3936104Sse 394181775Skmacy#ifndef XEN 39566529Smsmith/* check whether the configuration mechanism has been correctly identified */ 39610887Ssestatic int 39726159Ssepci_cfgcheck(int maxdev) 39810887Sse{ 399106357Simp uint32_t id, class; 400106357Simp uint8_t header; 401106357Simp uint8_t device; 402111068Speter int port; 40310735Sse 40426159Sse if (bootverbose) 405100435Simp printf("pci_cfgcheck:\tdevice "); 40610960Sse 407100435Simp for (device = 0; device < maxdev; device++) { 408100435Simp if (bootverbose) 409100435Simp printf("%d ", device); 41026159Sse 411111068Speter port = pci_cfgenable(0, device, 0, 0, 4); 412111068Speter id = inl(port); 413106357Simp if (id == 0 || id == 0xffffffff) 414100435Simp continue; 41523415Sse 416111068Speter port = pci_cfgenable(0, device, 0, 8, 4); 417111068Speter class = inl(port) >> 8; 418100435Simp if (bootverbose) 419100435Simp printf("[class=%06x] ", class); 420100435Simp if (class == 0 || (class & 0xf870ff) != 0) 421100435Simp continue; 422100435Simp 423111068Speter port = pci_cfgenable(0, device, 0, 14, 1); 424111068Speter header = inb(port); 425106357Simp if (bootverbose) 426100435Simp printf("[hdr=%02x] ", header); 427100435Simp if ((header & 0x7e) != 0) 428100435Simp continue; 429100435Simp 430100435Simp if (bootverbose) 431100435Simp printf("is there (id=%08x)\n", id); 432100435Simp 433100435Simp pci_cfgdisable(); 434100435Simp return (1); 435100435Simp } 43666529Smsmith if (bootverbose) 437100435Simp printf("-- nothing found\n"); 43823415Sse 43966529Smsmith pci_cfgdisable(); 440100435Simp return (0); 44110887Sse} 44210887Sse 44347307Speterstatic int 44459294Smsmithpcireg_cfgopen(void) 4456104Sse{ 446106357Simp uint32_t mode1res, oldval1; 447106357Simp uint8_t mode2res, oldval2; 4486104Sse 449174050Sjhb /* Check for type #1 first. */ 450100435Simp oldval1 = inl(CONF1_ADDR_PORT); 45110960Sse 452100435Simp if (bootverbose) { 453106357Simp printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n", 454100435Simp oldval1); 455100435Simp } 45610960Sse 457174050Sjhb cfgmech = CFGMECH_1; 458174050Sjhb devmax = 32; 45910960Sse 460174050Sjhb outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 461174050Sjhb DELAY(1); 462174050Sjhb mode1res = inl(CONF1_ADDR_PORT); 463174050Sjhb outl(CONF1_ADDR_PORT, oldval1); 46410960Sse 465174050Sjhb if (bootverbose) 466174050Sjhb printf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n", mode1res, 467174050Sjhb CONF1_ENABLE_CHK); 46810960Sse 469174050Sjhb if (mode1res) { 470174050Sjhb if (pci_cfgcheck(32)) 471174050Sjhb return (cfgmech); 472174050Sjhb } 4736104Sse 474174050Sjhb outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); 475174050Sjhb mode1res = inl(CONF1_ADDR_PORT); 476174050Sjhb outl(CONF1_ADDR_PORT, oldval1); 47710807Sse 478174050Sjhb if (bootverbose) 479174050Sjhb printf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n", mode1res, 480174050Sjhb CONF1_ENABLE_CHK1); 48110887Sse 482174050Sjhb if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { 483174050Sjhb if (pci_cfgcheck(32)) 484174050Sjhb return (cfgmech); 48511524Sse } 48611524Sse 487174050Sjhb /* Type #1 didn't work, so try type #2. */ 488100435Simp oldval2 = inb(CONF2_ENABLE_PORT); 48947307Speter 490100435Simp if (bootverbose) { 491100435Simp printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", 492100435Simp oldval2); 493100435Simp } 49447307Speter 495100435Simp if ((oldval2 & 0xf0) == 0) { 49648832Smsmith 497138429Sscottl cfgmech = CFGMECH_2; 498100435Simp devmax = 16; 49948832Smsmith 500100435Simp outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); 501100435Simp mode2res = inb(CONF2_ENABLE_PORT); 502100435Simp outb(CONF2_ENABLE_PORT, oldval2); 50348832Smsmith 504100435Simp if (bootverbose) 505100435Simp printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", 506100435Simp mode2res, CONF2_ENABLE_CHK); 50752480Salc 508100435Simp if (mode2res == CONF2_ENABLE_RES) { 509100435Simp if (bootverbose) 510100435Simp printf("pci_open(2a):\tnow trying mechanism 2\n"); 51149404Speter 512100435Simp if (pci_cfgcheck(16)) 513100435Simp return (cfgmech); 514100435Simp } 51548832Smsmith } 51648832Smsmith 517174050Sjhb /* Nothing worked, so punt. */ 518138429Sscottl cfgmech = CFGMECH_NONE; 519100435Simp devmax = 0; 520100435Simp return (cfgmech); 52148832Smsmith} 52248832Smsmith 523181987Sjhbint 524181987Sjhbpcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus) 525138429Sscottl{ 526138429Sscottl struct pcie_cfg_list *pcielist; 527138429Sscottl struct pcie_cfg_elem *pcie_array, *elem; 528138429Sscottl#ifdef SMP 529138429Sscottl struct pcpu *pc; 530138429Sscottl#endif 531138429Sscottl vm_offset_t va; 532182910Sjhb uint32_t val1, val2; 533182910Sjhb int i, slot; 534138429Sscottl 535182947Sjhb if (!mcfg_enable) 536182947Sjhb return (0); 537182947Sjhb 538181987Sjhb if (minbus != 0) 539181987Sjhb return (0); 540181987Sjhb 541181933Sjhb#ifndef PAE 542181987Sjhb if (base >= 0x100000000) { 543181933Sjhb if (bootverbose) 544181933Sjhb printf( 545181933Sjhb "PCI: Memory Mapped PCI configuration area base 0x%jx too high\n", 546181987Sjhb (uintmax_t)base); 547181933Sjhb return (0); 548181933Sjhb } 549181933Sjhb#endif 550181933Sjhb 551138429Sscottl if (bootverbose) 552181987Sjhb printf("PCIe: Memory Mapped configuration base @ 0x%jx\n", 553181987Sjhb (uintmax_t)base); 554138429Sscottl 555138429Sscottl#ifdef SMP 556222531Snwhitehorn STAILQ_FOREACH(pc, &cpuhead, pc_allcpu) 557138429Sscottl#endif 558138429Sscottl { 559138429Sscottl 560138429Sscottl pcie_array = malloc(sizeof(struct pcie_cfg_elem) * PCIE_CACHE, 561138429Sscottl M_DEVBUF, M_NOWAIT); 562138429Sscottl if (pcie_array == NULL) 563138429Sscottl return (0); 564138429Sscottl 565254025Sjeff va = kva_alloc(PCIE_CACHE * PAGE_SIZE); 566138429Sscottl if (va == 0) { 567138429Sscottl free(pcie_array, M_DEVBUF); 568138429Sscottl return (0); 569138429Sscottl } 570138429Sscottl 571138429Sscottl#ifdef SMP 572138429Sscottl pcielist = &pcie_list[pc->pc_cpuid]; 573138429Sscottl#else 574138429Sscottl pcielist = &pcie_list[0]; 575138429Sscottl#endif 576138429Sscottl TAILQ_INIT(pcielist); 577138429Sscottl for (i = 0; i < PCIE_CACHE; i++) { 578138429Sscottl elem = &pcie_array[i]; 579138429Sscottl elem->vapage = va + (i * PAGE_SIZE); 580138429Sscottl elem->papage = 0; 581138429Sscottl TAILQ_INSERT_HEAD(pcielist, elem, elem); 582138429Sscottl } 583138429Sscottl } 584138429Sscottl 585181987Sjhb pcie_base = base; 586181987Sjhb pcie_minbus = minbus; 587181987Sjhb pcie_maxbus = maxbus; 588138429Sscottl cfgmech = CFGMECH_PCIE; 589138429Sscottl devmax = 32; 590182910Sjhb 591182910Sjhb /* 592182910Sjhb * On some AMD systems, some of the devices on bus 0 are 593182910Sjhb * inaccessible using memory-mapped PCI config access. Walk 594182910Sjhb * bus 0 looking for such devices. For these devices, we will 595182910Sjhb * fall back to using type 1 config access instead. 596182910Sjhb */ 597182910Sjhb if (pci_cfgregopen() != 0) { 598197450Savg for (slot = 0; slot <= PCI_SLOTMAX; slot++) { 599182910Sjhb val1 = pcireg_cfgread(0, slot, 0, 0, 4); 600182910Sjhb if (val1 == 0xffffffff) 601182910Sjhb continue; 602182910Sjhb 603182910Sjhb val2 = pciereg_cfgread(0, slot, 0, 0, 4); 604182910Sjhb if (val2 != val1) 605182910Sjhb pcie_badslots |= (1 << slot); 606182910Sjhb } 607182910Sjhb } 608182910Sjhb 609138429Sscottl return (1); 610138429Sscottl} 611181775Skmacy#endif /* !XEN */ 612138429Sscottl 613243737Sjkim#define PCIE_PADDR(base, reg, bus, slot, func) \ 614243737Sjkim ((base) + \ 615243737Sjkim ((((bus) & 0xff) << 20) | \ 616138429Sscottl (((slot) & 0x1f) << 15) | \ 617138429Sscottl (((func) & 0x7) << 12) | \ 618243737Sjkim ((reg) & 0xfff))) 619138429Sscottl 620243737Sjkimstatic __inline vm_offset_t 621243737Sjkimpciereg_findaddr(int bus, unsigned slot, unsigned func, unsigned reg) 622138429Sscottl{ 623138429Sscottl struct pcie_cfg_list *pcielist; 624138429Sscottl struct pcie_cfg_elem *elem; 625243737Sjkim vm_paddr_t pa, papage; 626138429Sscottl 627243737Sjkim pa = PCIE_PADDR(pcie_base, reg, bus, slot, func); 628243737Sjkim papage = pa & ~PAGE_MASK; 629243737Sjkim 630243737Sjkim /* 631243737Sjkim * Find an element in the cache that matches the physical page desired, 632243737Sjkim * or create a new mapping from the least recently used element. 633243737Sjkim * A very simple LRU algorithm is used here, does it need to be more 634243737Sjkim * efficient? 635243737Sjkim */ 636138429Sscottl pcielist = &pcie_list[PCPU_GET(cpuid)]; 637138429Sscottl TAILQ_FOREACH(elem, pcielist, elem) { 638138429Sscottl if (elem->papage == papage) 639138429Sscottl break; 640138429Sscottl } 641138429Sscottl 642138429Sscottl if (elem == NULL) { 643138429Sscottl elem = TAILQ_LAST(pcielist, pcie_cfg_list); 644138429Sscottl if (elem->papage != 0) { 645138429Sscottl pmap_kremove(elem->vapage); 646138429Sscottl invlpg(elem->vapage); 647138429Sscottl } 648138429Sscottl pmap_kenter(elem->vapage, papage); 649138429Sscottl elem->papage = papage; 650138429Sscottl } 651138429Sscottl 652138429Sscottl if (elem != TAILQ_FIRST(pcielist)) { 653138429Sscottl TAILQ_REMOVE(pcielist, elem, elem); 654138429Sscottl TAILQ_INSERT_HEAD(pcielist, elem, elem); 655138429Sscottl } 656243737Sjkim return (elem->vapage | (pa & PAGE_MASK)); 657138429Sscottl} 658138429Sscottl 659241540Savg/* 660241540Savg * AMD BIOS And Kernel Developer's Guides for CPU families starting with 10h 661241540Savg * have a requirement that all accesses to the memory mapped PCI configuration 662241540Savg * space are done using AX class of registers. 663241540Savg * Since other vendors do not currently have any contradicting requirements 664241540Savg * the AMD access pattern is applied universally. 665241540Savg */ 666241540Savg 667138429Sscottlstatic int 668181987Sjhbpciereg_cfgread(int bus, unsigned slot, unsigned func, unsigned reg, 669181987Sjhb unsigned bytes) 670138429Sscottl{ 671243712Sjkim vm_offset_t va; 672181987Sjhb int data = -1; 673138429Sscottl 674197450Savg if (bus < pcie_minbus || bus > pcie_maxbus || slot > PCI_SLOTMAX || 675197450Savg func > PCI_FUNCMAX || reg > PCIE_REGMAX) 676181987Sjhb return (-1); 677181987Sjhb 678138662Sscottl critical_enter(); 679243737Sjkim va = pciereg_findaddr(bus, slot, func, reg); 680138429Sscottl 681138429Sscottl switch (bytes) { 682138429Sscottl case 4: 683243712Sjkim __asm("movl %1, %0" : "=a" (data) 684243712Sjkim : "m" (*(volatile uint32_t *)va)); 685138662Sscottl break; 686138429Sscottl case 2: 687243712Sjkim __asm("movzwl %1, %0" : "=a" (data) 688243712Sjkim : "m" (*(volatile uint16_t *)va)); 689138662Sscottl break; 690138429Sscottl case 1: 691243712Sjkim __asm("movzbl %1, %0" : "=a" (data) 692243712Sjkim : "m" (*(volatile uint8_t *)va)); 693138662Sscottl break; 694138429Sscottl } 695138662Sscottl 696138662Sscottl critical_exit(); 697138662Sscottl return (data); 698138429Sscottl} 699138429Sscottl 700138429Sscottlstatic void 701181987Sjhbpciereg_cfgwrite(int bus, unsigned slot, unsigned func, unsigned reg, int data, 702181987Sjhb unsigned bytes) 703138429Sscottl{ 704243712Sjkim vm_offset_t va; 705138429Sscottl 706197450Savg if (bus < pcie_minbus || bus > pcie_maxbus || slot > PCI_SLOTMAX || 707197450Savg func > PCI_FUNCMAX || reg > PCIE_REGMAX) 708181987Sjhb return; 709181987Sjhb 710138662Sscottl critical_enter(); 711243737Sjkim va = pciereg_findaddr(bus, slot, func, reg); 712138429Sscottl 713138429Sscottl switch (bytes) { 714138429Sscottl case 4: 715243712Sjkim __asm("movl %1, %0" : "=m" (*(volatile uint32_t *)va) 716241540Savg : "a" (data)); 717138429Sscottl break; 718138429Sscottl case 2: 719243712Sjkim __asm("movw %1, %0" : "=m" (*(volatile uint16_t *)va) 720243685Sjkim : "a" ((uint16_t)data)); 721138429Sscottl break; 722138429Sscottl case 1: 723243712Sjkim __asm("movb %1, %0" : "=m" (*(volatile uint8_t *)va) 724243685Sjkim : "a" ((uint8_t)data)); 725138429Sscottl break; 726138429Sscottl } 727138662Sscottl 728138662Sscottl critical_exit(); 729138429Sscottl} 730