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> 393591fea8SJohn Baldwin #include <sys/kernel.h> 40af3d516fSPeter Wemm #include <sys/mutex.h> 41aa2ea232SScott Long #include <sys/malloc.h> 42aa2ea232SScott Long #include <sys/queue.h> 43d3da228fSJohn Baldwin #include <sys/sysctl.h> 44e300f53cSWarner Losh #include <dev/pci/pcivar.h> 45e300f53cSWarner Losh #include <dev/pci/pcireg.h> 4612a02d6eSMike Smith #include <machine/pci_cfgreg.h> 47300451c4SMike Smith #include <machine/pc/bios.h> 48300451c4SMike Smith 49aa2ea232SScott Long #include <vm/vm.h> 50aa2ea232SScott Long #include <vm/vm_param.h> 51aa2ea232SScott Long #include <vm/vm_kern.h> 52aa2ea232SScott Long #include <vm/vm_extern.h> 53aa2ea232SScott Long #include <vm/pmap.h> 54aa2ea232SScott Long #include <machine/pmap.h> 55aa2ea232SScott Long 5651ef421dSWarner Losh #ifdef XBOX 5751ef421dSWarner Losh #include <machine/xbox.h> 5851ef421dSWarner Losh #endif 5951ef421dSWarner Losh 608ff25e97SJohn Baldwin #define PRVERB(a) do { \ 618ff25e97SJohn Baldwin if (bootverbose) \ 628ff25e97SJohn Baldwin printf a ; \ 638ff25e97SJohn Baldwin } while(0) 64d626906bSWarner Losh 65aa2ea232SScott Long #define PCIE_CACHE 8 66aa2ea232SScott Long struct pcie_cfg_elem { 67aa2ea232SScott Long TAILQ_ENTRY(pcie_cfg_elem) elem; 68aa2ea232SScott Long vm_offset_t vapage; 69aa2ea232SScott Long vm_paddr_t papage; 70aa2ea232SScott Long }; 71aa2ea232SScott Long 72aa2ea232SScott Long enum { 73aa2ea232SScott Long CFGMECH_NONE = 0, 74aa2ea232SScott Long CFGMECH_1, 75aa2ea232SScott Long CFGMECH_2, 76aa2ea232SScott Long CFGMECH_PCIE, 77aa2ea232SScott Long }; 78aa2ea232SScott Long 79d3da228fSJohn Baldwin SYSCTL_DECL(_hw_pci); 80d3da228fSJohn Baldwin 81aa2ea232SScott Long static TAILQ_HEAD(pcie_cfg_list, pcie_cfg_elem) pcie_list[MAXCPU]; 82d320e05cSJohn Baldwin static uint64_t pcie_base; 83d320e05cSJohn Baldwin static int pcie_minbus, pcie_maxbus; 842d10570aSJohn Baldwin static uint32_t pcie_badslots; 855bec6157SStefan Eßer static int cfgmech; 865bec6157SStefan Eßer static int devmax; 87aa2ea232SScott Long static struct mtx pcicfg_mtx; 883591fea8SJohn Baldwin static int mcfg_enable = 1; 893591fea8SJohn Baldwin TUNABLE_INT("hw.pci.mcfg", &mcfg_enable); 90d3da228fSJohn Baldwin SYSCTL_INT(_hw_pci, OID_AUTO, mcfg, CTLFLAG_RDTUN, &mcfg_enable, 0, 91d3da228fSJohn Baldwin "Enable support for PCI-e memory mapped config access"); 92300451c4SMike Smith 932d10570aSJohn Baldwin static uint32_t pci_docfgregread(int bus, int slot, int func, int reg, 942d10570aSJohn Baldwin int bytes); 9512a02d6eSMike Smith static int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes); 9612a02d6eSMike Smith static void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes); 9793ee134aSKip Macy #ifndef XEN 98300451c4SMike Smith static int pcireg_cfgopen(void); 9993ee134aSKip Macy #endif 100d320e05cSJohn Baldwin static int pciereg_cfgread(int bus, unsigned slot, unsigned func, 101d320e05cSJohn Baldwin unsigned reg, unsigned bytes); 102d320e05cSJohn Baldwin static void pciereg_cfgwrite(int bus, unsigned slot, unsigned func, 103d320e05cSJohn Baldwin unsigned reg, int data, unsigned bytes); 104af3d516fSPeter Wemm 1058ce1ab3aSWarner Losh /* 1068ce1ab3aSWarner Losh * Some BIOS writers seem to want to ignore the spec and put 1078ce1ab3aSWarner Losh * 0 in the intline rather than 255 to indicate none. Some use 1088ce1ab3aSWarner Losh * numbers in the range 128-254 to indicate something strange and 1098ce1ab3aSWarner Losh * apparently undocumented anywhere. Assume these are completely bogus 1108ce1ab3aSWarner Losh * and map them to 255, which means "none". 1118ce1ab3aSWarner Losh */ 1125908d366SStefan Farfeleder static __inline int 1138ce1ab3aSWarner Losh pci_i386_map_intline(int line) 1148ce1ab3aSWarner Losh { 1158ce1ab3aSWarner Losh if (line == 0 || line >= 128) 116e300f53cSWarner Losh return (PCI_INVALID_IRQ); 1178ce1ab3aSWarner Losh return (line); 1188ce1ab3aSWarner Losh } 1198ce1ab3aSWarner Losh 12093ee134aSKip Macy #ifndef XEN 121d626906bSWarner Losh static u_int16_t 122d626906bSWarner Losh pcibios_get_version(void) 123d626906bSWarner Losh { 124d626906bSWarner Losh struct bios_regs args; 125d626906bSWarner Losh 1265264a94fSJohn Baldwin if (PCIbios.ventry == 0) { 127d626906bSWarner Losh PRVERB(("pcibios: No call entry point\n")); 128d626906bSWarner Losh return (0); 129d626906bSWarner Losh } 130d626906bSWarner Losh args.eax = PCIBIOS_BIOS_PRESENT; 131d626906bSWarner Losh if (bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL))) { 132d626906bSWarner Losh PRVERB(("pcibios: BIOS_PRESENT call failed\n")); 133d626906bSWarner Losh return (0); 134d626906bSWarner Losh } 135d626906bSWarner Losh if (args.edx != 0x20494350) { 136d626906bSWarner Losh PRVERB(("pcibios: BIOS_PRESENT didn't return 'PCI ' in edx\n")); 137d626906bSWarner Losh return (0); 138d626906bSWarner Losh } 139d626906bSWarner Losh return (args.ebx & 0xffff); 140d626906bSWarner Losh } 14193ee134aSKip Macy #endif 142d626906bSWarner Losh 14312a02d6eSMike Smith /* 14412a02d6eSMike Smith * Initialise access to PCI configuration space 14512a02d6eSMike Smith */ 14612a02d6eSMike Smith int 14712a02d6eSMike Smith pci_cfgregopen(void) 14821c3015aSDoug Rabson { 14993ee134aSKip Macy #ifdef XEN 15093ee134aSKip Macy return (0); 15193ee134aSKip Macy #else 15212a02d6eSMike Smith static int opened = 0; 153d320e05cSJohn Baldwin uint64_t pciebar; 154aa2ea232SScott Long u_int16_t vid, did; 155af3d516fSPeter Wemm u_int16_t v; 15621c3015aSDoug Rabson 15712a02d6eSMike Smith if (opened) 15812a02d6eSMike Smith return (1); 159300451c4SMike Smith 160d320e05cSJohn Baldwin if (cfgmech == CFGMECH_NONE && pcireg_cfgopen() == 0) 161300451c4SMike Smith return (0); 16254c9005fSWarner Losh 163af3d516fSPeter Wemm v = pcibios_get_version(); 164af3d516fSPeter Wemm if (v > 0) 16539981fedSJohn Baldwin PRVERB(("pcibios: BIOS version %x.%02x\n", (v & 0xff00) >> 8, 16639981fedSJohn Baldwin v & 0xff)); 167af3d516fSPeter Wemm mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN); 16812a02d6eSMike Smith opened = 1; 16977fa00faSJohn Baldwin 17077fa00faSJohn Baldwin /* $PIR requires PCI BIOS 2.10 or greater. */ 17177fa00faSJohn Baldwin if (v >= 0x0210) 17277fa00faSJohn Baldwin pci_pir_open(); 173aa2ea232SScott Long 174d320e05cSJohn Baldwin if (cfgmech == CFGMECH_PCIE) 175d320e05cSJohn Baldwin return (1); 176d320e05cSJohn Baldwin 177aa2ea232SScott Long /* 178aa2ea232SScott Long * Grope around in the PCI config space to see if this is a 179aa2ea232SScott Long * chipset that is capable of doing memory-mapped config cycles. 180aa2ea232SScott Long * This also implies that it can do PCIe extended config cycles. 181aa2ea232SScott Long */ 182aa2ea232SScott Long 18316f99fe1SCraig Rodrigues /* Check for supported chipsets */ 184d748ef47SJohn Baldwin vid = pci_cfgregread(0, 0, 0, PCIR_VENDOR, 2); 185d748ef47SJohn Baldwin did = pci_cfgregread(0, 0, 0, PCIR_DEVICE, 2); 1865326f077SJohn Baldwin switch (vid) { 1875326f077SJohn Baldwin case 0x8086: 1885326f077SJohn Baldwin switch (did) { 1895326f077SJohn Baldwin case 0x3590: 1905326f077SJohn Baldwin case 0x3592: 19116f99fe1SCraig Rodrigues /* Intel 7520 or 7320 */ 192aa2ea232SScott Long pciebar = pci_cfgregread(0, 0, 0, 0xce, 2) << 16; 193d320e05cSJohn Baldwin pcie_cfgregopen(pciebar, 0, 255); 1945326f077SJohn Baldwin break; 1955326f077SJohn Baldwin case 0x2580: 1965326f077SJohn Baldwin case 0x2584: 19734ce932fSJohn Baldwin case 0x2590: 19834ce932fSJohn Baldwin /* Intel 915, 925, or 915GM */ 199aa2ea232SScott Long pciebar = pci_cfgregread(0, 0, 0, 0x48, 4); 200d320e05cSJohn Baldwin pcie_cfgregopen(pciebar, 0, 255); 20134ce932fSJohn Baldwin break; 202aa2ea232SScott Long } 20316f99fe1SCraig Rodrigues } 204aa2ea232SScott Long 205300451c4SMike Smith return(1); 20693ee134aSKip Macy #endif 207300451c4SMike Smith } 208300451c4SMike Smith 2092d10570aSJohn Baldwin static uint32_t 2102d10570aSJohn Baldwin pci_docfgregread(int bus, int slot, int func, int reg, int bytes) 2112d10570aSJohn Baldwin { 2122d10570aSJohn Baldwin 2132d10570aSJohn Baldwin if (cfgmech == CFGMECH_PCIE && 2146cad8eb4SJohn Baldwin (bus >= pcie_minbus && bus <= pcie_maxbus) && 2152d10570aSJohn Baldwin (bus != 0 || !(1 << slot & pcie_badslots))) 2162d10570aSJohn Baldwin return (pciereg_cfgread(bus, slot, func, reg, bytes)); 2172d10570aSJohn Baldwin else 2182d10570aSJohn Baldwin return (pcireg_cfgread(bus, slot, func, reg, bytes)); 2192d10570aSJohn Baldwin } 2202d10570aSJohn Baldwin 22112a02d6eSMike Smith /* 22212a02d6eSMike Smith * Read configuration space register 22312a02d6eSMike Smith */ 224bb0d0a8eSMike Smith u_int32_t 225bb0d0a8eSMike Smith pci_cfgregread(int bus, int slot, int func, int reg, int bytes) 226bb0d0a8eSMike Smith { 227e300f53cSWarner Losh uint32_t line; 228e300f53cSWarner Losh 229bb0d0a8eSMike Smith /* 230d5ccecfaSWarner Losh * Some BIOS writers seem to want to ignore the spec and put 231d5ccecfaSWarner Losh * 0 in the intline rather than 255 to indicate none. The rest of 232d5ccecfaSWarner Losh * the code uses 255 as an invalid IRQ. 233d5ccecfaSWarner Losh */ 234d5ccecfaSWarner Losh if (reg == PCIR_INTLINE && bytes == 1) { 2352d10570aSJohn Baldwin line = pci_docfgregread(bus, slot, func, PCIR_INTLINE, 1); 2366f92bdd0SJohn Baldwin return (pci_i386_map_intline(line)); 237d5ccecfaSWarner Losh } 2382d10570aSJohn Baldwin return (pci_docfgregread(bus, slot, func, reg, bytes)); 239bb0d0a8eSMike Smith } 240bb0d0a8eSMike Smith 24112a02d6eSMike Smith /* 24212a02d6eSMike Smith * Write configuration space register 24312a02d6eSMike Smith */ 24412a02d6eSMike Smith void 24512a02d6eSMike Smith pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes) 24612a02d6eSMike Smith { 247af3d516fSPeter Wemm 2482d10570aSJohn Baldwin if (cfgmech == CFGMECH_PCIE && 2496cad8eb4SJohn Baldwin (bus >= pcie_minbus && bus <= pcie_maxbus) && 2502d10570aSJohn Baldwin (bus != 0 || !(1 << slot & pcie_badslots))) 2512d10570aSJohn Baldwin pciereg_cfgwrite(bus, slot, func, reg, data, bytes); 2522d10570aSJohn Baldwin else 253cb8e4332SPoul-Henning Kamp pcireg_cfgwrite(bus, slot, func, reg, data, bytes); 25412a02d6eSMike Smith } 25512a02d6eSMike Smith 25612a02d6eSMike Smith /* 25712a02d6eSMike Smith * Configuration space access using direct register operations 25812a02d6eSMike Smith */ 259ac19f918SStefan Eßer 2605bec6157SStefan Eßer /* enable configuration space accesses and return data port address */ 261a3adc4f8SStefan Eßer static int 2625bec6157SStefan Eßer pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) 2635bec6157SStefan Eßer { 2645bec6157SStefan Eßer int dataport = 0; 2655bec6157SStefan Eßer 26651ef421dSWarner Losh #ifdef XBOX 26751ef421dSWarner Losh if (arch_i386_is_xbox) { 26851ef421dSWarner Losh /* 26951ef421dSWarner Losh * The Xbox MCPX chipset is a derivative of the nForce 1 27051ef421dSWarner Losh * chipset. It almost has the same bus layout; some devices 27151ef421dSWarner Losh * cannot be used, because they have been removed. 27251ef421dSWarner Losh */ 27351ef421dSWarner Losh 27451ef421dSWarner Losh /* 27551ef421dSWarner Losh * Devices 00:00.1 and 00:00.2 used to be memory controllers on 27651ef421dSWarner Losh * the nForce chipset, but on the Xbox, using them will lockup 27751ef421dSWarner Losh * the chipset. 27851ef421dSWarner Losh */ 27951ef421dSWarner Losh if (bus == 0 && slot == 0 && (func == 1 || func == 2)) 28051ef421dSWarner Losh return dataport; 28151ef421dSWarner Losh 28251ef421dSWarner Losh /* 28351ef421dSWarner Losh * Bus 1 only contains a VGA controller at 01:00.0. When you try 28451ef421dSWarner Losh * to probe beyond that device, you only get garbage, which 28551ef421dSWarner Losh * could cause lockups. 28651ef421dSWarner Losh */ 28751ef421dSWarner Losh if (bus == 1 && (slot != 0 || func != 0)) 28851ef421dSWarner Losh return dataport; 28951ef421dSWarner Losh 29051ef421dSWarner Losh /* 29151ef421dSWarner Losh * Bus 2 used to contain the AGP controller, but the Xbox MCPX 29251ef421dSWarner Losh * doesn't have one. Probing it can cause lockups. 29351ef421dSWarner Losh */ 29451ef421dSWarner Losh if (bus >= 2) 29551ef421dSWarner Losh return dataport; 29651ef421dSWarner Losh } 29751ef421dSWarner Losh #endif 29851ef421dSWarner Losh 2995bec6157SStefan Eßer if (bus <= PCI_BUSMAX 3005bec6157SStefan Eßer && slot < devmax 3015bec6157SStefan Eßer && func <= PCI_FUNCMAX 3021e908511SAndriy Gapon && (unsigned)reg <= PCI_REGMAX 3035bec6157SStefan Eßer && bytes != 3 3045bec6157SStefan Eßer && (unsigned)bytes <= 4 3055bec6157SStefan Eßer && (reg & (bytes - 1)) == 0) { 3065bec6157SStefan Eßer switch (cfgmech) { 3072d10570aSJohn Baldwin case CFGMECH_PCIE: 308aa2ea232SScott Long case CFGMECH_1: 309b3daa02eSStefan Eßer outl(CONF1_ADDR_PORT, (1 << 31) 310b3daa02eSStefan Eßer | (bus << 16) | (slot << 11) 311b3daa02eSStefan Eßer | (func << 8) | (reg & ~0x03)); 312b3daa02eSStefan Eßer dataport = CONF1_DATA_PORT + (reg & 0x03); 3135bec6157SStefan Eßer break; 314aa2ea232SScott Long case CFGMECH_2: 3155bec6157SStefan Eßer outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); 3165bec6157SStefan Eßer outb(CONF2_FORWARD_PORT, bus); 3175bec6157SStefan Eßer dataport = 0xc000 | (slot << 8) | reg; 3185bec6157SStefan Eßer break; 3195bec6157SStefan Eßer } 3205bec6157SStefan Eßer } 3215bec6157SStefan Eßer return (dataport); 3225bec6157SStefan Eßer } 3235bec6157SStefan Eßer 3245bec6157SStefan Eßer /* disable configuration space accesses */ 3255bec6157SStefan Eßer static void 3265bec6157SStefan Eßer pci_cfgdisable(void) 3275bec6157SStefan Eßer { 3285bec6157SStefan Eßer switch (cfgmech) { 3292d10570aSJohn Baldwin case CFGMECH_PCIE: 330aa2ea232SScott Long case CFGMECH_1: 3313f7f26e9SJohn Baldwin /* 3323f7f26e9SJohn Baldwin * Do nothing for the config mechanism 1 case. 3333f7f26e9SJohn Baldwin * Writing a 0 to the address port can apparently 3343f7f26e9SJohn Baldwin * confuse some bridges and cause spurious 3353f7f26e9SJohn Baldwin * access failures. 3363f7f26e9SJohn Baldwin */ 3375bec6157SStefan Eßer break; 338aa2ea232SScott Long case CFGMECH_2: 3395bec6157SStefan Eßer outb(CONF2_ENABLE_PORT, 0); 3405bec6157SStefan Eßer break; 3415bec6157SStefan Eßer } 3425bec6157SStefan Eßer } 3435bec6157SStefan Eßer 344300451c4SMike Smith static int 34521c3015aSDoug Rabson pcireg_cfgread(int bus, int slot, int func, int reg, int bytes) 3465bec6157SStefan Eßer { 3475bec6157SStefan Eßer int data = -1; 3485bec6157SStefan Eßer int port; 3495bec6157SStefan Eßer 350af3d516fSPeter Wemm mtx_lock_spin(&pcicfg_mtx); 35121c3015aSDoug Rabson port = pci_cfgenable(bus, slot, func, reg, bytes); 3525bec6157SStefan Eßer if (port != 0) { 3535bec6157SStefan Eßer switch (bytes) { 3545bec6157SStefan Eßer case 1: 3555bec6157SStefan Eßer data = inb(port); 3565bec6157SStefan Eßer break; 3575bec6157SStefan Eßer case 2: 3585bec6157SStefan Eßer data = inw(port); 3595bec6157SStefan Eßer break; 3605bec6157SStefan Eßer case 4: 3615bec6157SStefan Eßer data = inl(port); 3625bec6157SStefan Eßer break; 3635bec6157SStefan Eßer } 3645bec6157SStefan Eßer pci_cfgdisable(); 3655bec6157SStefan Eßer } 366af3d516fSPeter Wemm mtx_unlock_spin(&pcicfg_mtx); 3675bec6157SStefan Eßer return (data); 3685bec6157SStefan Eßer } 3695bec6157SStefan Eßer 370300451c4SMike Smith static void 37121c3015aSDoug Rabson pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 3725bec6157SStefan Eßer { 3735bec6157SStefan Eßer int port; 3745bec6157SStefan Eßer 375af3d516fSPeter Wemm mtx_lock_spin(&pcicfg_mtx); 37621c3015aSDoug Rabson port = pci_cfgenable(bus, slot, func, reg, bytes); 3775bec6157SStefan Eßer if (port != 0) { 3785bec6157SStefan Eßer switch (bytes) { 3795bec6157SStefan Eßer case 1: 3805bec6157SStefan Eßer outb(port, data); 3815bec6157SStefan Eßer break; 3825bec6157SStefan Eßer case 2: 3835bec6157SStefan Eßer outw(port, data); 3845bec6157SStefan Eßer break; 3855bec6157SStefan Eßer case 4: 3865bec6157SStefan Eßer outl(port, data); 3875bec6157SStefan Eßer break; 3885bec6157SStefan Eßer } 3895bec6157SStefan Eßer pci_cfgdisable(); 3905bec6157SStefan Eßer } 391af3d516fSPeter Wemm mtx_unlock_spin(&pcicfg_mtx); 3925bec6157SStefan Eßer } 3935bec6157SStefan Eßer 39493ee134aSKip Macy #ifndef XEN 39512a02d6eSMike Smith /* check whether the configuration mechanism has been correctly identified */ 3965bec6157SStefan Eßer static int 3975bec6157SStefan Eßer pci_cfgcheck(int maxdev) 398a3adc4f8SStefan Eßer { 399984de797SWarner Losh uint32_t id, class; 400984de797SWarner Losh uint8_t header; 401984de797SWarner Losh uint8_t device; 402af3d516fSPeter Wemm int port; 403a3adc4f8SStefan Eßer 4045bec6157SStefan Eßer if (bootverbose) 4055bec6157SStefan Eßer printf("pci_cfgcheck:\tdevice "); 40677b57314SStefan Eßer 4075bec6157SStefan Eßer for (device = 0; device < maxdev; device++) { 408c7483249SStefan Eßer if (bootverbose) 409c7483249SStefan Eßer printf("%d ", device); 4105bec6157SStefan Eßer 411af3d516fSPeter Wemm port = pci_cfgenable(0, device, 0, 0, 4); 412af3d516fSPeter Wemm id = inl(port); 413984de797SWarner Losh if (id == 0 || id == 0xffffffff) 41481cf5d7aSStefan Eßer continue; 41581cf5d7aSStefan Eßer 416af3d516fSPeter Wemm port = pci_cfgenable(0, device, 0, 8, 4); 417af3d516fSPeter Wemm class = inl(port) >> 8; 41881cf5d7aSStefan Eßer if (bootverbose) 4195bec6157SStefan Eßer printf("[class=%06x] ", class); 4208277ac25SStefan Eßer if (class == 0 || (class & 0xf870ff) != 0) 42181cf5d7aSStefan Eßer continue; 42281cf5d7aSStefan Eßer 423af3d516fSPeter Wemm port = pci_cfgenable(0, device, 0, 14, 1); 424af3d516fSPeter Wemm header = inb(port); 42581cf5d7aSStefan Eßer if (bootverbose) 4265bec6157SStefan Eßer printf("[hdr=%02x] ", header); 4275bec6157SStefan Eßer if ((header & 0x7e) != 0) 42881cf5d7aSStefan Eßer continue; 42981cf5d7aSStefan Eßer 4305bec6157SStefan Eßer if (bootverbose) 4315bec6157SStefan Eßer printf("is there (id=%08x)\n", id); 4325bec6157SStefan Eßer 4335bec6157SStefan Eßer pci_cfgdisable(); 4345bec6157SStefan Eßer return (1); 435a3adc4f8SStefan Eßer } 436c7483249SStefan Eßer if (bootverbose) 437c7483249SStefan Eßer printf("-- nothing found\n"); 4385bec6157SStefan Eßer 4395bec6157SStefan Eßer pci_cfgdisable(); 4405bec6157SStefan Eßer return (0); 441a3adc4f8SStefan Eßer } 442d7ea35fcSStefan Eßer 4438dc26439SPeter Wemm static int 444300451c4SMike Smith pcireg_cfgopen(void) 445ac19f918SStefan Eßer { 446984de797SWarner Losh uint32_t mode1res, oldval1; 447984de797SWarner Losh uint8_t mode2res, oldval2; 4480847c06dSStefan Eßer 44998bbce55SJohn Baldwin /* Check for type #1 first. */ 450287911bdSStefan Eßer oldval1 = inl(CONF1_ADDR_PORT); 451a3adc4f8SStefan Eßer 45277b57314SStefan Eßer if (bootverbose) { 453984de797SWarner Losh printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n", 4545bec6157SStefan Eßer oldval1); 455a3adc4f8SStefan Eßer } 456a3adc4f8SStefan Eßer 457aa2ea232SScott Long cfgmech = CFGMECH_1; 4585bec6157SStefan Eßer devmax = 32; 45977b57314SStefan Eßer 46077b57314SStefan Eßer outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 46121e25fa6SJohn Baldwin DELAY(1); 46277b57314SStefan Eßer mode1res = inl(CONF1_ADDR_PORT); 463287911bdSStefan Eßer outl(CONF1_ADDR_PORT, oldval1); 46477b57314SStefan Eßer 46577b57314SStefan Eßer if (bootverbose) 46698bbce55SJohn Baldwin printf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n", mode1res, 46798bbce55SJohn Baldwin CONF1_ENABLE_CHK); 46877b57314SStefan Eßer 46977b57314SStefan Eßer if (mode1res) { 4705bec6157SStefan Eßer if (pci_cfgcheck(32)) 4715bec6157SStefan Eßer return (cfgmech); 4725bec6157SStefan Eßer } 47377b57314SStefan Eßer 47477b57314SStefan Eßer outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); 47577b57314SStefan Eßer mode1res = inl(CONF1_ADDR_PORT); 476287911bdSStefan Eßer outl(CONF1_ADDR_PORT, oldval1); 47777b57314SStefan Eßer 47877b57314SStefan Eßer if (bootverbose) 47998bbce55SJohn Baldwin printf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n", mode1res, 48098bbce55SJohn Baldwin CONF1_ENABLE_CHK1); 48177b57314SStefan Eßer 482c7483249SStefan Eßer if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { 4835bec6157SStefan Eßer if (pci_cfgcheck(32)) 4845bec6157SStefan Eßer return (cfgmech); 485287911bdSStefan Eßer } 48677b57314SStefan Eßer 48798bbce55SJohn Baldwin /* Type #1 didn't work, so try type #2. */ 488287911bdSStefan Eßer oldval2 = inb(CONF2_ENABLE_PORT); 489287911bdSStefan Eßer 490287911bdSStefan Eßer if (bootverbose) { 4915bec6157SStefan Eßer printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", 4925bec6157SStefan Eßer oldval2); 493287911bdSStefan Eßer } 494287911bdSStefan Eßer 495287911bdSStefan Eßer if ((oldval2 & 0xf0) == 0) { 496c7483249SStefan Eßer 497aa2ea232SScott Long cfgmech = CFGMECH_2; 4985bec6157SStefan Eßer devmax = 16; 49977b57314SStefan Eßer 500287911bdSStefan Eßer outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); 501287911bdSStefan Eßer mode2res = inb(CONF2_ENABLE_PORT); 502287911bdSStefan Eßer outb(CONF2_ENABLE_PORT, oldval2); 503287911bdSStefan Eßer 504287911bdSStefan Eßer if (bootverbose) 5055bec6157SStefan Eßer printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", 506287911bdSStefan Eßer mode2res, CONF2_ENABLE_CHK); 507287911bdSStefan Eßer 508287911bdSStefan Eßer if (mode2res == CONF2_ENABLE_RES) { 509287911bdSStefan Eßer if (bootverbose) 5105bec6157SStefan Eßer printf("pci_open(2a):\tnow trying mechanism 2\n"); 511287911bdSStefan Eßer 5125bec6157SStefan Eßer if (pci_cfgcheck(16)) 5135bec6157SStefan Eßer return (cfgmech); 514287911bdSStefan Eßer } 515287911bdSStefan Eßer } 51677b57314SStefan Eßer 51798bbce55SJohn Baldwin /* Nothing worked, so punt. */ 518aa2ea232SScott Long cfgmech = CFGMECH_NONE; 5195bec6157SStefan Eßer devmax = 0; 5205bec6157SStefan Eßer return (cfgmech); 521ac19f918SStefan Eßer } 5228dc26439SPeter Wemm 523d320e05cSJohn Baldwin int 524d320e05cSJohn Baldwin pcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus) 525aa2ea232SScott Long { 526aa2ea232SScott Long struct pcie_cfg_list *pcielist; 527aa2ea232SScott Long struct pcie_cfg_elem *pcie_array, *elem; 528aa2ea232SScott Long #ifdef SMP 529aa2ea232SScott Long struct pcpu *pc; 530aa2ea232SScott Long #endif 531aa2ea232SScott Long vm_offset_t va; 5322d10570aSJohn Baldwin uint32_t val1, val2; 5332d10570aSJohn Baldwin int i, slot; 534aa2ea232SScott Long 5353591fea8SJohn Baldwin if (!mcfg_enable) 5363591fea8SJohn Baldwin return (0); 5373591fea8SJohn Baldwin 538d320e05cSJohn Baldwin if (minbus != 0) 539d320e05cSJohn Baldwin return (0); 540d320e05cSJohn Baldwin 54134ce932fSJohn Baldwin #ifndef PAE 542d320e05cSJohn Baldwin if (base >= 0x100000000) { 543aa2ea232SScott Long if (bootverbose) 54434ce932fSJohn Baldwin printf( 54534ce932fSJohn Baldwin "PCI: Memory Mapped PCI configuration area base 0x%jx too high\n", 546d320e05cSJohn Baldwin (uintmax_t)base); 54734ce932fSJohn Baldwin return (0); 54834ce932fSJohn Baldwin } 54934ce932fSJohn Baldwin #endif 55034ce932fSJohn Baldwin 55134ce932fSJohn Baldwin if (bootverbose) 552d320e05cSJohn Baldwin printf("PCIe: Memory Mapped configuration base @ 0x%jx\n", 553d320e05cSJohn Baldwin (uintmax_t)base); 554aa2ea232SScott Long 555aa2ea232SScott Long #ifdef SMP 556aa2ea232SScott Long SLIST_FOREACH(pc, &cpuhead, pc_allcpu) 557aa2ea232SScott Long #endif 558aa2ea232SScott Long { 559aa2ea232SScott Long 560aa2ea232SScott Long pcie_array = malloc(sizeof(struct pcie_cfg_elem) * PCIE_CACHE, 561aa2ea232SScott Long M_DEVBUF, M_NOWAIT); 562aa2ea232SScott Long if (pcie_array == NULL) 563aa2ea232SScott Long return (0); 564aa2ea232SScott Long 565aa2ea232SScott Long va = kmem_alloc_nofault(kernel_map, PCIE_CACHE * PAGE_SIZE); 566aa2ea232SScott Long if (va == 0) { 567aa2ea232SScott Long free(pcie_array, M_DEVBUF); 568aa2ea232SScott Long return (0); 569aa2ea232SScott Long } 570aa2ea232SScott Long 571aa2ea232SScott Long #ifdef SMP 572aa2ea232SScott Long pcielist = &pcie_list[pc->pc_cpuid]; 573aa2ea232SScott Long #else 574aa2ea232SScott Long pcielist = &pcie_list[0]; 575aa2ea232SScott Long #endif 576aa2ea232SScott Long TAILQ_INIT(pcielist); 577aa2ea232SScott Long for (i = 0; i < PCIE_CACHE; i++) { 578aa2ea232SScott Long elem = &pcie_array[i]; 579aa2ea232SScott Long elem->vapage = va + (i * PAGE_SIZE); 580aa2ea232SScott Long elem->papage = 0; 581aa2ea232SScott Long TAILQ_INSERT_HEAD(pcielist, elem, elem); 582aa2ea232SScott Long } 583aa2ea232SScott Long } 584aa2ea232SScott Long 585d320e05cSJohn Baldwin pcie_base = base; 586d320e05cSJohn Baldwin pcie_minbus = minbus; 587d320e05cSJohn Baldwin pcie_maxbus = maxbus; 588aa2ea232SScott Long cfgmech = CFGMECH_PCIE; 589aa2ea232SScott Long devmax = 32; 5902d10570aSJohn Baldwin 5912d10570aSJohn Baldwin /* 5922d10570aSJohn Baldwin * On some AMD systems, some of the devices on bus 0 are 5932d10570aSJohn Baldwin * inaccessible using memory-mapped PCI config access. Walk 5942d10570aSJohn Baldwin * bus 0 looking for such devices. For these devices, we will 5952d10570aSJohn Baldwin * fall back to using type 1 config access instead. 5962d10570aSJohn Baldwin */ 5972d10570aSJohn Baldwin if (pci_cfgregopen() != 0) { 5981e908511SAndriy Gapon for (slot = 0; slot <= PCI_SLOTMAX; slot++) { 5992d10570aSJohn Baldwin val1 = pcireg_cfgread(0, slot, 0, 0, 4); 6002d10570aSJohn Baldwin if (val1 == 0xffffffff) 6012d10570aSJohn Baldwin continue; 6022d10570aSJohn Baldwin 6032d10570aSJohn Baldwin val2 = pciereg_cfgread(0, slot, 0, 0, 4); 6042d10570aSJohn Baldwin if (val2 != val1) 6052d10570aSJohn Baldwin pcie_badslots |= (1 << slot); 6062d10570aSJohn Baldwin } 6072d10570aSJohn Baldwin } 6082d10570aSJohn Baldwin 609aa2ea232SScott Long return (1); 610aa2ea232SScott Long } 61193ee134aSKip Macy #endif /* !XEN */ 612aa2ea232SScott Long 613aa2ea232SScott Long #define PCIE_PADDR(bar, reg, bus, slot, func) \ 614aa2ea232SScott Long ((bar) | \ 615aa2ea232SScott Long (((bus) & 0xff) << 20) | \ 616aa2ea232SScott Long (((slot) & 0x1f) << 15) | \ 617aa2ea232SScott Long (((func) & 0x7) << 12) | \ 618aa2ea232SScott Long ((reg) & 0xfff)) 619aa2ea232SScott Long 620aa2ea232SScott Long /* 621aa2ea232SScott Long * Find an element in the cache that matches the physical page desired, or 622aa2ea232SScott Long * create a new mapping from the least recently used element. 623aa2ea232SScott Long * A very simple LRU algorithm is used here, does it need to be more 624aa2ea232SScott Long * efficient? 625aa2ea232SScott Long */ 626aa2ea232SScott Long static __inline struct pcie_cfg_elem * 627aa2ea232SScott Long pciereg_findelem(vm_paddr_t papage) 628aa2ea232SScott Long { 629aa2ea232SScott Long struct pcie_cfg_list *pcielist; 630aa2ea232SScott Long struct pcie_cfg_elem *elem; 631aa2ea232SScott Long 632aa2ea232SScott Long pcielist = &pcie_list[PCPU_GET(cpuid)]; 633aa2ea232SScott Long TAILQ_FOREACH(elem, pcielist, elem) { 634aa2ea232SScott Long if (elem->papage == papage) 635aa2ea232SScott Long break; 636aa2ea232SScott Long } 637aa2ea232SScott Long 638aa2ea232SScott Long if (elem == NULL) { 639aa2ea232SScott Long elem = TAILQ_LAST(pcielist, pcie_cfg_list); 640aa2ea232SScott Long if (elem->papage != 0) { 641aa2ea232SScott Long pmap_kremove(elem->vapage); 642aa2ea232SScott Long invlpg(elem->vapage); 643aa2ea232SScott Long } 644aa2ea232SScott Long pmap_kenter(elem->vapage, papage); 645aa2ea232SScott Long elem->papage = papage; 646aa2ea232SScott Long } 647aa2ea232SScott Long 648aa2ea232SScott Long if (elem != TAILQ_FIRST(pcielist)) { 649aa2ea232SScott Long TAILQ_REMOVE(pcielist, elem, elem); 650aa2ea232SScott Long TAILQ_INSERT_HEAD(pcielist, elem, elem); 651aa2ea232SScott Long } 652aa2ea232SScott Long return (elem); 653aa2ea232SScott Long } 654aa2ea232SScott Long 655aa2ea232SScott Long static int 656d320e05cSJohn Baldwin pciereg_cfgread(int bus, unsigned slot, unsigned func, unsigned reg, 657d320e05cSJohn Baldwin unsigned bytes) 658aa2ea232SScott Long { 659aa2ea232SScott Long struct pcie_cfg_elem *elem; 660aa2ea232SScott Long volatile vm_offset_t va; 661aa2ea232SScott Long vm_paddr_t pa, papage; 662d320e05cSJohn Baldwin int data = -1; 663d320e05cSJohn Baldwin 6641e908511SAndriy Gapon if (bus < pcie_minbus || bus > pcie_maxbus || slot > PCI_SLOTMAX || 6651e908511SAndriy Gapon func > PCI_FUNCMAX || reg > PCIE_REGMAX) 666d320e05cSJohn Baldwin return (-1); 667aa2ea232SScott Long 668245e410bSScott Long critical_enter(); 669d320e05cSJohn Baldwin pa = PCIE_PADDR(pcie_base, reg, bus, slot, func); 670aa2ea232SScott Long papage = pa & ~PAGE_MASK; 671aa2ea232SScott Long elem = pciereg_findelem(papage); 672aa2ea232SScott Long va = elem->vapage | (pa & PAGE_MASK); 673aa2ea232SScott Long 674aa2ea232SScott Long switch (bytes) { 675aa2ea232SScott Long case 4: 676245e410bSScott Long data = *(volatile uint32_t *)(va); 677245e410bSScott Long break; 678aa2ea232SScott Long case 2: 679245e410bSScott Long data = *(volatile uint16_t *)(va); 680245e410bSScott Long break; 681aa2ea232SScott Long case 1: 682245e410bSScott Long data = *(volatile uint8_t *)(va); 683245e410bSScott Long break; 684aa2ea232SScott Long } 685245e410bSScott Long 686245e410bSScott Long critical_exit(); 687245e410bSScott Long return (data); 688aa2ea232SScott Long } 689aa2ea232SScott Long 690aa2ea232SScott Long static void 691d320e05cSJohn Baldwin pciereg_cfgwrite(int bus, unsigned slot, unsigned func, unsigned reg, int data, 692d320e05cSJohn Baldwin unsigned bytes) 693aa2ea232SScott Long { 694aa2ea232SScott Long struct pcie_cfg_elem *elem; 695aa2ea232SScott Long volatile vm_offset_t va; 696aa2ea232SScott Long vm_paddr_t pa, papage; 697aa2ea232SScott Long 6981e908511SAndriy Gapon if (bus < pcie_minbus || bus > pcie_maxbus || slot > PCI_SLOTMAX || 6991e908511SAndriy Gapon func > PCI_FUNCMAX || reg > PCIE_REGMAX) 700d320e05cSJohn Baldwin return; 701d320e05cSJohn Baldwin 702245e410bSScott Long critical_enter(); 703d320e05cSJohn Baldwin pa = PCIE_PADDR(pcie_base, reg, bus, slot, func); 704aa2ea232SScott Long papage = pa & ~PAGE_MASK; 705aa2ea232SScott Long elem = pciereg_findelem(papage); 706aa2ea232SScott Long va = elem->vapage | (pa & PAGE_MASK); 707aa2ea232SScott Long 708aa2ea232SScott Long switch (bytes) { 709aa2ea232SScott Long case 4: 710aa2ea232SScott Long *(volatile uint32_t *)(va) = data; 711aa2ea232SScott Long break; 712aa2ea232SScott Long case 2: 713aa2ea232SScott Long *(volatile uint16_t *)(va) = data; 714aa2ea232SScott Long break; 715aa2ea232SScott Long case 1: 716aa2ea232SScott Long *(volatile uint8_t *)(va) = data; 717aa2ea232SScott Long break; 718aa2ea232SScott Long } 719245e410bSScott Long 720245e410bSScott Long critical_exit(); 721aa2ea232SScott Long } 722