186cb007fSWarner Losh /*- 25bec6157SStefan Eßer * Copyright (c) 1997, Stefan Esser <se@freebsd.org> 312a02d6eSMike Smith * Copyright (c) 2000, Michael Smith <msmith@freebsd.org> 412a02d6eSMike Smith * Copyright (c) 2000, BSDi 5568b7ee1SScott Long * Copyright (c) 2004, Scott Long <scottl@freebsd.org> 65bec6157SStefan Eßer * All rights reserved. 75bec6157SStefan Eßer * 85bec6157SStefan Eßer * Redistribution and use in source and binary forms, with or without 95bec6157SStefan Eßer * modification, are permitted provided that the following conditions 105bec6157SStefan Eßer * are met: 115bec6157SStefan Eßer * 1. Redistributions of source code must retain the above copyright 125bec6157SStefan Eßer * notice unmodified, this list of conditions, and the following 135bec6157SStefan Eßer * disclaimer. 145bec6157SStefan Eßer * 2. Redistributions in binary form must reproduce the above copyright 155bec6157SStefan Eßer * notice, this list of conditions and the following disclaimer in the 165bec6157SStefan Eßer * documentation and/or other materials provided with the distribution. 175bec6157SStefan Eßer * 185bec6157SStefan Eßer * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 195bec6157SStefan Eßer * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 205bec6157SStefan Eßer * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 215bec6157SStefan Eßer * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 225bec6157SStefan Eßer * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 235bec6157SStefan Eßer * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 245bec6157SStefan Eßer * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 255bec6157SStefan Eßer * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 265bec6157SStefan Eßer * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 275bec6157SStefan Eßer * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28ac19f918SStefan Eßer */ 29ac19f918SStefan Eßer 3071c5a901SDavid E. O'Brien #include <sys/cdefs.h> 3171c5a901SDavid E. O'Brien __FBSDID("$FreeBSD$"); 3271c5a901SDavid E. O'Brien 3351ef421dSWarner Losh #include "opt_xbox.h" 3451ef421dSWarner Losh 3577fa00faSJohn Baldwin #include <sys/param.h> 365bec6157SStefan Eßer #include <sys/systm.h> 378dc26439SPeter Wemm #include <sys/bus.h> 38af3d516fSPeter Wemm #include <sys/lock.h> 39af3d516fSPeter Wemm #include <sys/mutex.h> 40aa2ea232SScott Long #include <sys/malloc.h> 41aa2ea232SScott Long #include <sys/queue.h> 42e300f53cSWarner Losh #include <dev/pci/pcivar.h> 43e300f53cSWarner Losh #include <dev/pci/pcireg.h> 4412a02d6eSMike Smith #include <machine/pci_cfgreg.h> 45300451c4SMike Smith #include <machine/pc/bios.h> 46300451c4SMike Smith 47aa2ea232SScott Long #include <vm/vm.h> 48aa2ea232SScott Long #include <vm/vm_param.h> 49aa2ea232SScott Long #include <vm/vm_kern.h> 50aa2ea232SScott Long #include <vm/vm_extern.h> 51aa2ea232SScott Long #include <vm/pmap.h> 52aa2ea232SScott Long #include <machine/pmap.h> 53aa2ea232SScott Long 5451ef421dSWarner Losh #ifdef XBOX 5551ef421dSWarner Losh #include <machine/xbox.h> 5651ef421dSWarner Losh #endif 5751ef421dSWarner Losh 588ff25e97SJohn Baldwin #define PRVERB(a) do { \ 598ff25e97SJohn Baldwin if (bootverbose) \ 608ff25e97SJohn Baldwin printf a ; \ 618ff25e97SJohn Baldwin } while(0) 62d626906bSWarner Losh 63aa2ea232SScott Long #define PCIE_CACHE 8 64aa2ea232SScott Long struct pcie_cfg_elem { 65aa2ea232SScott Long TAILQ_ENTRY(pcie_cfg_elem) elem; 66aa2ea232SScott Long vm_offset_t vapage; 67aa2ea232SScott Long vm_paddr_t papage; 68aa2ea232SScott Long }; 69aa2ea232SScott Long 70aa2ea232SScott Long enum { 71aa2ea232SScott Long CFGMECH_NONE = 0, 72aa2ea232SScott Long CFGMECH_1, 73aa2ea232SScott Long CFGMECH_2, 74aa2ea232SScott Long CFGMECH_PCIE, 75aa2ea232SScott Long }; 76aa2ea232SScott Long 77aa2ea232SScott Long static TAILQ_HEAD(pcie_cfg_list, pcie_cfg_elem) pcie_list[MAXCPU]; 7834ce932fSJohn Baldwin static uint64_t pciebar; 795bec6157SStefan Eßer static int cfgmech; 805bec6157SStefan Eßer static int devmax; 81aa2ea232SScott Long static struct mtx pcicfg_mtx; 82300451c4SMike Smith 8312a02d6eSMike Smith static int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes); 8412a02d6eSMike Smith static void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes); 8593ee134aSKip Macy #ifndef XEN 86300451c4SMike Smith static int pcireg_cfgopen(void); 87300451c4SMike Smith 88aa2ea232SScott Long static int pciereg_cfgopen(void); 8993ee134aSKip Macy #endif 90aa2ea232SScott Long static int pciereg_cfgread(int bus, int slot, int func, int reg, 91aa2ea232SScott Long int bytes); 92aa2ea232SScott Long static void pciereg_cfgwrite(int bus, int slot, int func, int reg, 93aa2ea232SScott Long int data, int bytes); 94af3d516fSPeter Wemm 958ce1ab3aSWarner Losh /* 968ce1ab3aSWarner Losh * Some BIOS writers seem to want to ignore the spec and put 978ce1ab3aSWarner Losh * 0 in the intline rather than 255 to indicate none. Some use 988ce1ab3aSWarner Losh * numbers in the range 128-254 to indicate something strange and 998ce1ab3aSWarner Losh * apparently undocumented anywhere. Assume these are completely bogus 1008ce1ab3aSWarner Losh * and map them to 255, which means "none". 1018ce1ab3aSWarner Losh */ 1025908d366SStefan Farfeleder static __inline int 1038ce1ab3aSWarner Losh pci_i386_map_intline(int line) 1048ce1ab3aSWarner Losh { 1058ce1ab3aSWarner Losh if (line == 0 || line >= 128) 106e300f53cSWarner Losh return (PCI_INVALID_IRQ); 1078ce1ab3aSWarner Losh return (line); 1088ce1ab3aSWarner Losh } 1098ce1ab3aSWarner Losh 11093ee134aSKip Macy #ifndef XEN 111d626906bSWarner Losh static u_int16_t 112d626906bSWarner Losh pcibios_get_version(void) 113d626906bSWarner Losh { 114d626906bSWarner Losh struct bios_regs args; 115d626906bSWarner Losh 1165264a94fSJohn Baldwin if (PCIbios.ventry == 0) { 117d626906bSWarner Losh PRVERB(("pcibios: No call entry point\n")); 118d626906bSWarner Losh return (0); 119d626906bSWarner Losh } 120d626906bSWarner Losh args.eax = PCIBIOS_BIOS_PRESENT; 121d626906bSWarner Losh if (bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL))) { 122d626906bSWarner Losh PRVERB(("pcibios: BIOS_PRESENT call failed\n")); 123d626906bSWarner Losh return (0); 124d626906bSWarner Losh } 125d626906bSWarner Losh if (args.edx != 0x20494350) { 126d626906bSWarner Losh PRVERB(("pcibios: BIOS_PRESENT didn't return 'PCI ' in edx\n")); 127d626906bSWarner Losh return (0); 128d626906bSWarner Losh } 129d626906bSWarner Losh return (args.ebx & 0xffff); 130d626906bSWarner Losh } 13193ee134aSKip Macy #endif 132d626906bSWarner Losh 13312a02d6eSMike Smith /* 13412a02d6eSMike Smith * Initialise access to PCI configuration space 13512a02d6eSMike Smith */ 13612a02d6eSMike Smith int 13712a02d6eSMike Smith pci_cfgregopen(void) 13821c3015aSDoug Rabson { 13993ee134aSKip Macy #ifdef XEN 14093ee134aSKip Macy return (0); 14193ee134aSKip Macy #else 14212a02d6eSMike Smith static int opened = 0; 143aa2ea232SScott Long u_int16_t vid, did; 144af3d516fSPeter Wemm u_int16_t v; 14521c3015aSDoug Rabson 14612a02d6eSMike Smith if (opened) 14712a02d6eSMike Smith return(1); 148300451c4SMike Smith 149af3d516fSPeter Wemm if (pcireg_cfgopen() == 0) 150300451c4SMike Smith return(0); 15154c9005fSWarner Losh 152af3d516fSPeter Wemm v = pcibios_get_version(); 153af3d516fSPeter Wemm if (v > 0) 15439981fedSJohn Baldwin PRVERB(("pcibios: BIOS version %x.%02x\n", (v & 0xff00) >> 8, 15539981fedSJohn Baldwin v & 0xff)); 156af3d516fSPeter Wemm mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN); 15712a02d6eSMike Smith opened = 1; 15877fa00faSJohn Baldwin 15977fa00faSJohn Baldwin /* $PIR requires PCI BIOS 2.10 or greater. */ 16077fa00faSJohn Baldwin if (v >= 0x0210) 16177fa00faSJohn Baldwin pci_pir_open(); 162aa2ea232SScott Long 163aa2ea232SScott Long /* 164aa2ea232SScott Long * Grope around in the PCI config space to see if this is a 165aa2ea232SScott Long * chipset that is capable of doing memory-mapped config cycles. 166aa2ea232SScott Long * This also implies that it can do PCIe extended config cycles. 167aa2ea232SScott Long */ 168aa2ea232SScott Long 16916f99fe1SCraig Rodrigues /* Check for supported chipsets */ 170d748ef47SJohn Baldwin vid = pci_cfgregread(0, 0, 0, PCIR_VENDOR, 2); 171d748ef47SJohn Baldwin did = pci_cfgregread(0, 0, 0, PCIR_DEVICE, 2); 1725326f077SJohn Baldwin switch (vid) { 1735326f077SJohn Baldwin case 0x8086: 1745326f077SJohn Baldwin switch (did) { 1755326f077SJohn Baldwin case 0x3590: 1765326f077SJohn Baldwin case 0x3592: 17716f99fe1SCraig Rodrigues /* Intel 7520 or 7320 */ 178aa2ea232SScott Long pciebar = pci_cfgregread(0, 0, 0, 0xce, 2) << 16; 179aa2ea232SScott Long pciereg_cfgopen(); 1805326f077SJohn Baldwin break; 1815326f077SJohn Baldwin case 0x2580: 1825326f077SJohn Baldwin case 0x2584: 18334ce932fSJohn Baldwin case 0x2590: 18434ce932fSJohn Baldwin /* Intel 915, 925, or 915GM */ 185aa2ea232SScott Long pciebar = pci_cfgregread(0, 0, 0, 0x48, 4); 186aa2ea232SScott Long pciereg_cfgopen(); 1875326f077SJohn Baldwin break; 18834ce932fSJohn Baldwin case 0x25d0: 18934ce932fSJohn Baldwin case 0x25d4: 19034ce932fSJohn Baldwin case 0x25d8: 19134ce932fSJohn Baldwin /* Intel 5000Z/V/P */ 19234ce932fSJohn Baldwin pciebar = pci_cfgregread(0, 16, 0, 0x64, 4) << 16; 19334ce932fSJohn Baldwin pciereg_cfgopen(); 19434ce932fSJohn Baldwin break; 195aa2ea232SScott Long } 19616f99fe1SCraig Rodrigues } 197aa2ea232SScott Long 198300451c4SMike Smith return(1); 19993ee134aSKip Macy #endif 200300451c4SMike Smith } 201300451c4SMike Smith 20212a02d6eSMike Smith /* 20312a02d6eSMike Smith * Read configuration space register 20412a02d6eSMike Smith */ 205bb0d0a8eSMike Smith u_int32_t 206bb0d0a8eSMike Smith pci_cfgregread(int bus, int slot, int func, int reg, int bytes) 207bb0d0a8eSMike Smith { 208e300f53cSWarner Losh uint32_t line; 209e300f53cSWarner Losh 210bb0d0a8eSMike Smith /* 211d5ccecfaSWarner Losh * Some BIOS writers seem to want to ignore the spec and put 212d5ccecfaSWarner Losh * 0 in the intline rather than 255 to indicate none. The rest of 213d5ccecfaSWarner Losh * the code uses 255 as an invalid IRQ. 214d5ccecfaSWarner Losh */ 215d5ccecfaSWarner Losh if (reg == PCIR_INTLINE && bytes == 1) { 216af3d516fSPeter Wemm line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1); 2176f92bdd0SJohn Baldwin return (pci_i386_map_intline(line)); 218d5ccecfaSWarner Losh } 219af3d516fSPeter Wemm return (pcireg_cfgread(bus, slot, func, reg, bytes)); 220bb0d0a8eSMike Smith } 221bb0d0a8eSMike Smith 22212a02d6eSMike Smith /* 22312a02d6eSMike Smith * Write configuration space register 22412a02d6eSMike Smith */ 22512a02d6eSMike Smith void 22612a02d6eSMike Smith pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes) 22712a02d6eSMike Smith { 228af3d516fSPeter Wemm 229cb8e4332SPoul-Henning Kamp pcireg_cfgwrite(bus, slot, func, reg, data, bytes); 23012a02d6eSMike Smith } 23112a02d6eSMike Smith 23212a02d6eSMike Smith /* 23312a02d6eSMike Smith * Configuration space access using direct register operations 23412a02d6eSMike Smith */ 235ac19f918SStefan Eßer 2365bec6157SStefan Eßer /* enable configuration space accesses and return data port address */ 237a3adc4f8SStefan Eßer static int 2385bec6157SStefan Eßer pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) 2395bec6157SStefan Eßer { 2405bec6157SStefan Eßer int dataport = 0; 2415bec6157SStefan Eßer 24251ef421dSWarner Losh #ifdef XBOX 24351ef421dSWarner Losh if (arch_i386_is_xbox) { 24451ef421dSWarner Losh /* 24551ef421dSWarner Losh * The Xbox MCPX chipset is a derivative of the nForce 1 24651ef421dSWarner Losh * chipset. It almost has the same bus layout; some devices 24751ef421dSWarner Losh * cannot be used, because they have been removed. 24851ef421dSWarner Losh */ 24951ef421dSWarner Losh 25051ef421dSWarner Losh /* 25151ef421dSWarner Losh * Devices 00:00.1 and 00:00.2 used to be memory controllers on 25251ef421dSWarner Losh * the nForce chipset, but on the Xbox, using them will lockup 25351ef421dSWarner Losh * the chipset. 25451ef421dSWarner Losh */ 25551ef421dSWarner Losh if (bus == 0 && slot == 0 && (func == 1 || func == 2)) 25651ef421dSWarner Losh return dataport; 25751ef421dSWarner Losh 25851ef421dSWarner Losh /* 25951ef421dSWarner Losh * Bus 1 only contains a VGA controller at 01:00.0. When you try 26051ef421dSWarner Losh * to probe beyond that device, you only get garbage, which 26151ef421dSWarner Losh * could cause lockups. 26251ef421dSWarner Losh */ 26351ef421dSWarner Losh if (bus == 1 && (slot != 0 || func != 0)) 26451ef421dSWarner Losh return dataport; 26551ef421dSWarner Losh 26651ef421dSWarner Losh /* 26751ef421dSWarner Losh * Bus 2 used to contain the AGP controller, but the Xbox MCPX 26851ef421dSWarner Losh * doesn't have one. Probing it can cause lockups. 26951ef421dSWarner Losh */ 27051ef421dSWarner Losh if (bus >= 2) 27151ef421dSWarner Losh return dataport; 27251ef421dSWarner Losh } 27351ef421dSWarner Losh #endif 27451ef421dSWarner Losh 2755bec6157SStefan Eßer if (bus <= PCI_BUSMAX 2765bec6157SStefan Eßer && slot < devmax 2775bec6157SStefan Eßer && func <= PCI_FUNCMAX 2785bec6157SStefan Eßer && reg <= PCI_REGMAX 2795bec6157SStefan Eßer && bytes != 3 2805bec6157SStefan Eßer && (unsigned) bytes <= 4 2815bec6157SStefan Eßer && (reg & (bytes - 1)) == 0) { 2825bec6157SStefan Eßer switch (cfgmech) { 283aa2ea232SScott Long case CFGMECH_1: 284b3daa02eSStefan Eßer outl(CONF1_ADDR_PORT, (1 << 31) 285b3daa02eSStefan Eßer | (bus << 16) | (slot << 11) 286b3daa02eSStefan Eßer | (func << 8) | (reg & ~0x03)); 287b3daa02eSStefan Eßer dataport = CONF1_DATA_PORT + (reg & 0x03); 2885bec6157SStefan Eßer break; 289aa2ea232SScott Long case CFGMECH_2: 2905bec6157SStefan Eßer outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); 2915bec6157SStefan Eßer outb(CONF2_FORWARD_PORT, bus); 2925bec6157SStefan Eßer dataport = 0xc000 | (slot << 8) | reg; 2935bec6157SStefan Eßer break; 2945bec6157SStefan Eßer } 2955bec6157SStefan Eßer } 2965bec6157SStefan Eßer return (dataport); 2975bec6157SStefan Eßer } 2985bec6157SStefan Eßer 2995bec6157SStefan Eßer /* disable configuration space accesses */ 3005bec6157SStefan Eßer static void 3015bec6157SStefan Eßer pci_cfgdisable(void) 3025bec6157SStefan Eßer { 3035bec6157SStefan Eßer switch (cfgmech) { 304aa2ea232SScott Long case CFGMECH_1: 3053f7f26e9SJohn Baldwin /* 3063f7f26e9SJohn Baldwin * Do nothing for the config mechanism 1 case. 3073f7f26e9SJohn Baldwin * Writing a 0 to the address port can apparently 3083f7f26e9SJohn Baldwin * confuse some bridges and cause spurious 3093f7f26e9SJohn Baldwin * access failures. 3103f7f26e9SJohn Baldwin */ 3115bec6157SStefan Eßer break; 312aa2ea232SScott Long case CFGMECH_2: 3135bec6157SStefan Eßer outb(CONF2_ENABLE_PORT, 0); 3145bec6157SStefan Eßer break; 3155bec6157SStefan Eßer } 3165bec6157SStefan Eßer } 3175bec6157SStefan Eßer 318300451c4SMike Smith static int 31921c3015aSDoug Rabson pcireg_cfgread(int bus, int slot, int func, int reg, int bytes) 3205bec6157SStefan Eßer { 3215bec6157SStefan Eßer int data = -1; 3225bec6157SStefan Eßer int port; 3235bec6157SStefan Eßer 324aa2ea232SScott Long if (cfgmech == CFGMECH_PCIE) { 325aa2ea232SScott Long data = pciereg_cfgread(bus, slot, func, reg, bytes); 326aa2ea232SScott Long return (data); 327aa2ea232SScott Long } 328aa2ea232SScott Long 329af3d516fSPeter Wemm mtx_lock_spin(&pcicfg_mtx); 33021c3015aSDoug Rabson port = pci_cfgenable(bus, slot, func, reg, bytes); 3315bec6157SStefan Eßer if (port != 0) { 3325bec6157SStefan Eßer switch (bytes) { 3335bec6157SStefan Eßer case 1: 3345bec6157SStefan Eßer data = inb(port); 3355bec6157SStefan Eßer break; 3365bec6157SStefan Eßer case 2: 3375bec6157SStefan Eßer data = inw(port); 3385bec6157SStefan Eßer break; 3395bec6157SStefan Eßer case 4: 3405bec6157SStefan Eßer data = inl(port); 3415bec6157SStefan Eßer break; 3425bec6157SStefan Eßer } 3435bec6157SStefan Eßer pci_cfgdisable(); 3445bec6157SStefan Eßer } 345af3d516fSPeter Wemm mtx_unlock_spin(&pcicfg_mtx); 3465bec6157SStefan Eßer return (data); 3475bec6157SStefan Eßer } 3485bec6157SStefan Eßer 349300451c4SMike Smith static void 35021c3015aSDoug Rabson pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 3515bec6157SStefan Eßer { 3525bec6157SStefan Eßer int port; 3535bec6157SStefan Eßer 354aa2ea232SScott Long if (cfgmech == CFGMECH_PCIE) { 355aa2ea232SScott Long pciereg_cfgwrite(bus, slot, func, reg, data, bytes); 356aa2ea232SScott Long return; 357aa2ea232SScott Long } 358aa2ea232SScott Long 359af3d516fSPeter Wemm mtx_lock_spin(&pcicfg_mtx); 36021c3015aSDoug Rabson port = pci_cfgenable(bus, slot, func, reg, bytes); 3615bec6157SStefan Eßer if (port != 0) { 3625bec6157SStefan Eßer switch (bytes) { 3635bec6157SStefan Eßer case 1: 3645bec6157SStefan Eßer outb(port, data); 3655bec6157SStefan Eßer break; 3665bec6157SStefan Eßer case 2: 3675bec6157SStefan Eßer outw(port, data); 3685bec6157SStefan Eßer break; 3695bec6157SStefan Eßer case 4: 3705bec6157SStefan Eßer outl(port, data); 3715bec6157SStefan Eßer break; 3725bec6157SStefan Eßer } 3735bec6157SStefan Eßer pci_cfgdisable(); 3745bec6157SStefan Eßer } 375af3d516fSPeter Wemm mtx_unlock_spin(&pcicfg_mtx); 3765bec6157SStefan Eßer } 3775bec6157SStefan Eßer 37893ee134aSKip Macy #ifndef XEN 37912a02d6eSMike Smith /* check whether the configuration mechanism has been correctly identified */ 3805bec6157SStefan Eßer static int 3815bec6157SStefan Eßer pci_cfgcheck(int maxdev) 382a3adc4f8SStefan Eßer { 383984de797SWarner Losh uint32_t id, class; 384984de797SWarner Losh uint8_t header; 385984de797SWarner Losh uint8_t device; 386af3d516fSPeter Wemm int port; 387a3adc4f8SStefan Eßer 3885bec6157SStefan Eßer if (bootverbose) 3895bec6157SStefan Eßer printf("pci_cfgcheck:\tdevice "); 39077b57314SStefan Eßer 3915bec6157SStefan Eßer for (device = 0; device < maxdev; device++) { 392c7483249SStefan Eßer if (bootverbose) 393c7483249SStefan Eßer printf("%d ", device); 3945bec6157SStefan Eßer 395af3d516fSPeter Wemm port = pci_cfgenable(0, device, 0, 0, 4); 396af3d516fSPeter Wemm id = inl(port); 397984de797SWarner Losh if (id == 0 || id == 0xffffffff) 39881cf5d7aSStefan Eßer continue; 39981cf5d7aSStefan Eßer 400af3d516fSPeter Wemm port = pci_cfgenable(0, device, 0, 8, 4); 401af3d516fSPeter Wemm class = inl(port) >> 8; 40281cf5d7aSStefan Eßer if (bootverbose) 4035bec6157SStefan Eßer printf("[class=%06x] ", class); 4048277ac25SStefan Eßer if (class == 0 || (class & 0xf870ff) != 0) 40581cf5d7aSStefan Eßer continue; 40681cf5d7aSStefan Eßer 407af3d516fSPeter Wemm port = pci_cfgenable(0, device, 0, 14, 1); 408af3d516fSPeter Wemm header = inb(port); 40981cf5d7aSStefan Eßer if (bootverbose) 4105bec6157SStefan Eßer printf("[hdr=%02x] ", header); 4115bec6157SStefan Eßer if ((header & 0x7e) != 0) 41281cf5d7aSStefan Eßer continue; 41381cf5d7aSStefan Eßer 4145bec6157SStefan Eßer if (bootverbose) 4155bec6157SStefan Eßer printf("is there (id=%08x)\n", id); 4165bec6157SStefan Eßer 4175bec6157SStefan Eßer pci_cfgdisable(); 4185bec6157SStefan Eßer return (1); 419a3adc4f8SStefan Eßer } 420c7483249SStefan Eßer if (bootverbose) 421c7483249SStefan Eßer printf("-- nothing found\n"); 4225bec6157SStefan Eßer 4235bec6157SStefan Eßer pci_cfgdisable(); 4245bec6157SStefan Eßer return (0); 425a3adc4f8SStefan Eßer } 426d7ea35fcSStefan Eßer 4278dc26439SPeter Wemm static int 428300451c4SMike Smith pcireg_cfgopen(void) 429ac19f918SStefan Eßer { 430984de797SWarner Losh uint32_t mode1res, oldval1; 431984de797SWarner Losh uint8_t mode2res, oldval2; 4320847c06dSStefan Eßer 43398bbce55SJohn Baldwin /* Check for type #1 first. */ 434287911bdSStefan Eßer oldval1 = inl(CONF1_ADDR_PORT); 435a3adc4f8SStefan Eßer 43677b57314SStefan Eßer if (bootverbose) { 437984de797SWarner Losh printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n", 4385bec6157SStefan Eßer oldval1); 439a3adc4f8SStefan Eßer } 440a3adc4f8SStefan Eßer 441aa2ea232SScott Long cfgmech = CFGMECH_1; 4425bec6157SStefan Eßer devmax = 32; 44377b57314SStefan Eßer 44477b57314SStefan Eßer outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 44521e25fa6SJohn Baldwin DELAY(1); 44677b57314SStefan Eßer mode1res = inl(CONF1_ADDR_PORT); 447287911bdSStefan Eßer outl(CONF1_ADDR_PORT, oldval1); 44877b57314SStefan Eßer 44977b57314SStefan Eßer if (bootverbose) 45098bbce55SJohn Baldwin printf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n", mode1res, 45198bbce55SJohn Baldwin CONF1_ENABLE_CHK); 45277b57314SStefan Eßer 45377b57314SStefan Eßer if (mode1res) { 4545bec6157SStefan Eßer if (pci_cfgcheck(32)) 4555bec6157SStefan Eßer return (cfgmech); 4565bec6157SStefan Eßer } 45777b57314SStefan Eßer 45877b57314SStefan Eßer outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); 45977b57314SStefan Eßer mode1res = inl(CONF1_ADDR_PORT); 460287911bdSStefan Eßer outl(CONF1_ADDR_PORT, oldval1); 46177b57314SStefan Eßer 46277b57314SStefan Eßer if (bootverbose) 46398bbce55SJohn Baldwin printf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n", mode1res, 46498bbce55SJohn Baldwin CONF1_ENABLE_CHK1); 46577b57314SStefan Eßer 466c7483249SStefan Eßer if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { 4675bec6157SStefan Eßer if (pci_cfgcheck(32)) 4685bec6157SStefan Eßer return (cfgmech); 469287911bdSStefan Eßer } 47077b57314SStefan Eßer 47198bbce55SJohn Baldwin /* Type #1 didn't work, so try type #2. */ 472287911bdSStefan Eßer oldval2 = inb(CONF2_ENABLE_PORT); 473287911bdSStefan Eßer 474287911bdSStefan Eßer if (bootverbose) { 4755bec6157SStefan Eßer printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", 4765bec6157SStefan Eßer oldval2); 477287911bdSStefan Eßer } 478287911bdSStefan Eßer 479287911bdSStefan Eßer if ((oldval2 & 0xf0) == 0) { 480c7483249SStefan Eßer 481aa2ea232SScott Long cfgmech = CFGMECH_2; 4825bec6157SStefan Eßer devmax = 16; 48377b57314SStefan Eßer 484287911bdSStefan Eßer outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); 485287911bdSStefan Eßer mode2res = inb(CONF2_ENABLE_PORT); 486287911bdSStefan Eßer outb(CONF2_ENABLE_PORT, oldval2); 487287911bdSStefan Eßer 488287911bdSStefan Eßer if (bootverbose) 4895bec6157SStefan Eßer printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", 490287911bdSStefan Eßer mode2res, CONF2_ENABLE_CHK); 491287911bdSStefan Eßer 492287911bdSStefan Eßer if (mode2res == CONF2_ENABLE_RES) { 493287911bdSStefan Eßer if (bootverbose) 4945bec6157SStefan Eßer printf("pci_open(2a):\tnow trying mechanism 2\n"); 495287911bdSStefan Eßer 4965bec6157SStefan Eßer if (pci_cfgcheck(16)) 4975bec6157SStefan Eßer return (cfgmech); 498287911bdSStefan Eßer } 499287911bdSStefan Eßer } 50077b57314SStefan Eßer 50198bbce55SJohn Baldwin /* Nothing worked, so punt. */ 502aa2ea232SScott Long cfgmech = CFGMECH_NONE; 5035bec6157SStefan Eßer devmax = 0; 5045bec6157SStefan Eßer return (cfgmech); 505ac19f918SStefan Eßer } 5068dc26439SPeter Wemm 507aa2ea232SScott Long static int 508aa2ea232SScott Long pciereg_cfgopen(void) 509aa2ea232SScott Long { 510aa2ea232SScott Long struct pcie_cfg_list *pcielist; 511aa2ea232SScott Long struct pcie_cfg_elem *pcie_array, *elem; 512aa2ea232SScott Long #ifdef SMP 513aa2ea232SScott Long struct pcpu *pc; 514aa2ea232SScott Long #endif 515aa2ea232SScott Long vm_offset_t va; 516aa2ea232SScott Long int i; 517aa2ea232SScott Long 51834ce932fSJohn Baldwin #ifndef PAE 51934ce932fSJohn Baldwin if (pciebar >= 0x100000000) { 520aa2ea232SScott Long if (bootverbose) 52134ce932fSJohn Baldwin printf( 52234ce932fSJohn Baldwin "PCI: Memory Mapped PCI configuration area base 0x%jx too high\n", 52334ce932fSJohn Baldwin (uintmax_t)pciebar); 52434ce932fSJohn Baldwin pciebar = 0; 52534ce932fSJohn Baldwin return (0); 52634ce932fSJohn Baldwin } 52734ce932fSJohn Baldwin #endif 52834ce932fSJohn Baldwin 52934ce932fSJohn Baldwin if (bootverbose) 53034ce932fSJohn Baldwin printf("Setting up PCIe mappings for BAR 0x%jx\n", 53134ce932fSJohn Baldwin (uintmax_t)pciebar); 532aa2ea232SScott Long 533aa2ea232SScott Long #ifdef SMP 534aa2ea232SScott Long SLIST_FOREACH(pc, &cpuhead, pc_allcpu) 535aa2ea232SScott Long #endif 536aa2ea232SScott Long { 537aa2ea232SScott Long 538aa2ea232SScott Long pcie_array = malloc(sizeof(struct pcie_cfg_elem) * PCIE_CACHE, 539aa2ea232SScott Long M_DEVBUF, M_NOWAIT); 540aa2ea232SScott Long if (pcie_array == NULL) 541aa2ea232SScott Long return (0); 542aa2ea232SScott Long 543aa2ea232SScott Long va = kmem_alloc_nofault(kernel_map, PCIE_CACHE * PAGE_SIZE); 544aa2ea232SScott Long if (va == 0) { 545aa2ea232SScott Long free(pcie_array, M_DEVBUF); 546aa2ea232SScott Long return (0); 547aa2ea232SScott Long } 548aa2ea232SScott Long 549aa2ea232SScott Long #ifdef SMP 550aa2ea232SScott Long pcielist = &pcie_list[pc->pc_cpuid]; 551aa2ea232SScott Long #else 552aa2ea232SScott Long pcielist = &pcie_list[0]; 553aa2ea232SScott Long #endif 554aa2ea232SScott Long TAILQ_INIT(pcielist); 555aa2ea232SScott Long for (i = 0; i < PCIE_CACHE; i++) { 556aa2ea232SScott Long elem = &pcie_array[i]; 557aa2ea232SScott Long elem->vapage = va + (i * PAGE_SIZE); 558aa2ea232SScott Long elem->papage = 0; 559aa2ea232SScott Long TAILQ_INSERT_HEAD(pcielist, elem, elem); 560aa2ea232SScott Long } 561aa2ea232SScott Long } 562aa2ea232SScott Long 563aa2ea232SScott Long 564aa2ea232SScott Long cfgmech = CFGMECH_PCIE; 565aa2ea232SScott Long devmax = 32; 566aa2ea232SScott Long return (1); 567aa2ea232SScott Long } 56893ee134aSKip Macy #endif /* !XEN */ 569aa2ea232SScott Long 570aa2ea232SScott Long #define PCIE_PADDR(bar, reg, bus, slot, func) \ 571aa2ea232SScott Long ((bar) | \ 572aa2ea232SScott Long (((bus) & 0xff) << 20) | \ 573aa2ea232SScott Long (((slot) & 0x1f) << 15) | \ 574aa2ea232SScott Long (((func) & 0x7) << 12) | \ 575aa2ea232SScott Long ((reg) & 0xfff)) 576aa2ea232SScott Long 577aa2ea232SScott Long /* 578aa2ea232SScott Long * Find an element in the cache that matches the physical page desired, or 579aa2ea232SScott Long * create a new mapping from the least recently used element. 580aa2ea232SScott Long * A very simple LRU algorithm is used here, does it need to be more 581aa2ea232SScott Long * efficient? 582aa2ea232SScott Long */ 583aa2ea232SScott Long static __inline struct pcie_cfg_elem * 584aa2ea232SScott Long pciereg_findelem(vm_paddr_t papage) 585aa2ea232SScott Long { 586aa2ea232SScott Long struct pcie_cfg_list *pcielist; 587aa2ea232SScott Long struct pcie_cfg_elem *elem; 588aa2ea232SScott Long 589aa2ea232SScott Long pcielist = &pcie_list[PCPU_GET(cpuid)]; 590aa2ea232SScott Long TAILQ_FOREACH(elem, pcielist, elem) { 591aa2ea232SScott Long if (elem->papage == papage) 592aa2ea232SScott Long break; 593aa2ea232SScott Long } 594aa2ea232SScott Long 595aa2ea232SScott Long if (elem == NULL) { 596aa2ea232SScott Long elem = TAILQ_LAST(pcielist, pcie_cfg_list); 597aa2ea232SScott Long if (elem->papage != 0) { 598aa2ea232SScott Long pmap_kremove(elem->vapage); 599aa2ea232SScott Long invlpg(elem->vapage); 600aa2ea232SScott Long } 601aa2ea232SScott Long pmap_kenter(elem->vapage, papage); 602aa2ea232SScott Long elem->papage = papage; 603aa2ea232SScott Long } 604aa2ea232SScott Long 605aa2ea232SScott Long if (elem != TAILQ_FIRST(pcielist)) { 606aa2ea232SScott Long TAILQ_REMOVE(pcielist, elem, elem); 607aa2ea232SScott Long TAILQ_INSERT_HEAD(pcielist, elem, elem); 608aa2ea232SScott Long } 609aa2ea232SScott Long return (elem); 610aa2ea232SScott Long } 611aa2ea232SScott Long 612aa2ea232SScott Long static int 613aa2ea232SScott Long pciereg_cfgread(int bus, int slot, int func, int reg, int bytes) 614aa2ea232SScott Long { 615aa2ea232SScott Long struct pcie_cfg_elem *elem; 616aa2ea232SScott Long volatile vm_offset_t va; 617aa2ea232SScott Long vm_paddr_t pa, papage; 618245e410bSScott Long int data; 619aa2ea232SScott Long 620245e410bSScott Long critical_enter(); 621aa2ea232SScott Long pa = PCIE_PADDR(pciebar, reg, bus, slot, func); 622aa2ea232SScott Long papage = pa & ~PAGE_MASK; 623aa2ea232SScott Long elem = pciereg_findelem(papage); 624aa2ea232SScott Long va = elem->vapage | (pa & PAGE_MASK); 625aa2ea232SScott Long 626aa2ea232SScott Long switch (bytes) { 627aa2ea232SScott Long case 4: 628245e410bSScott Long data = *(volatile uint32_t *)(va); 629245e410bSScott Long break; 630aa2ea232SScott Long case 2: 631245e410bSScott Long data = *(volatile uint16_t *)(va); 632245e410bSScott Long break; 633aa2ea232SScott Long case 1: 634245e410bSScott Long data = *(volatile uint8_t *)(va); 635245e410bSScott Long break; 636aa2ea232SScott Long default: 637aa2ea232SScott Long panic("pciereg_cfgread: invalid width"); 638aa2ea232SScott Long } 639245e410bSScott Long 640245e410bSScott Long critical_exit(); 641245e410bSScott Long return (data); 642aa2ea232SScott Long } 643aa2ea232SScott Long 644aa2ea232SScott Long static void 645aa2ea232SScott Long pciereg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 646aa2ea232SScott Long { 647aa2ea232SScott Long struct pcie_cfg_elem *elem; 648aa2ea232SScott Long volatile vm_offset_t va; 649aa2ea232SScott Long vm_paddr_t pa, papage; 650aa2ea232SScott Long 651245e410bSScott Long critical_enter(); 652aa2ea232SScott Long pa = PCIE_PADDR(pciebar, reg, bus, slot, func); 653aa2ea232SScott Long papage = pa & ~PAGE_MASK; 654aa2ea232SScott Long elem = pciereg_findelem(papage); 655aa2ea232SScott Long va = elem->vapage | (pa & PAGE_MASK); 656aa2ea232SScott Long 657aa2ea232SScott Long switch (bytes) { 658aa2ea232SScott Long case 4: 659aa2ea232SScott Long *(volatile uint32_t *)(va) = data; 660aa2ea232SScott Long break; 661aa2ea232SScott Long case 2: 662aa2ea232SScott Long *(volatile uint16_t *)(va) = data; 663aa2ea232SScott Long break; 664aa2ea232SScott Long case 1: 665aa2ea232SScott Long *(volatile uint8_t *)(va) = data; 666aa2ea232SScott Long break; 667aa2ea232SScott Long default: 668aa2ea232SScott Long panic("pciereg_cfgwrite: invalid width"); 669aa2ea232SScott Long } 670245e410bSScott Long 671245e410bSScott Long critical_exit(); 672aa2ea232SScott Long } 673