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]; 78aa2ea232SScott Long static uint32_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); 85300451c4SMike Smith static int pcireg_cfgopen(void); 86300451c4SMike Smith 87aa2ea232SScott Long static int pciereg_cfgopen(void); 88aa2ea232SScott Long static int pciereg_cfgread(int bus, int slot, int func, int reg, 89aa2ea232SScott Long int bytes); 90aa2ea232SScott Long static void pciereg_cfgwrite(int bus, int slot, int func, int reg, 91aa2ea232SScott Long int data, int bytes); 92af3d516fSPeter Wemm 938ce1ab3aSWarner Losh /* 948ce1ab3aSWarner Losh * Some BIOS writers seem to want to ignore the spec and put 958ce1ab3aSWarner Losh * 0 in the intline rather than 255 to indicate none. Some use 968ce1ab3aSWarner Losh * numbers in the range 128-254 to indicate something strange and 978ce1ab3aSWarner Losh * apparently undocumented anywhere. Assume these are completely bogus 988ce1ab3aSWarner Losh * and map them to 255, which means "none". 998ce1ab3aSWarner Losh */ 1005908d366SStefan Farfeleder static __inline int 1018ce1ab3aSWarner Losh pci_i386_map_intline(int line) 1028ce1ab3aSWarner Losh { 1038ce1ab3aSWarner Losh if (line == 0 || line >= 128) 104e300f53cSWarner Losh return (PCI_INVALID_IRQ); 1058ce1ab3aSWarner Losh return (line); 1068ce1ab3aSWarner Losh } 1078ce1ab3aSWarner Losh 108d626906bSWarner Losh static u_int16_t 109d626906bSWarner Losh pcibios_get_version(void) 110d626906bSWarner Losh { 111d626906bSWarner Losh struct bios_regs args; 112d626906bSWarner Losh 1135264a94fSJohn Baldwin if (PCIbios.ventry == 0) { 114d626906bSWarner Losh PRVERB(("pcibios: No call entry point\n")); 115d626906bSWarner Losh return (0); 116d626906bSWarner Losh } 117d626906bSWarner Losh args.eax = PCIBIOS_BIOS_PRESENT; 118d626906bSWarner Losh if (bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL))) { 119d626906bSWarner Losh PRVERB(("pcibios: BIOS_PRESENT call failed\n")); 120d626906bSWarner Losh return (0); 121d626906bSWarner Losh } 122d626906bSWarner Losh if (args.edx != 0x20494350) { 123d626906bSWarner Losh PRVERB(("pcibios: BIOS_PRESENT didn't return 'PCI ' in edx\n")); 124d626906bSWarner Losh return (0); 125d626906bSWarner Losh } 126d626906bSWarner Losh return (args.ebx & 0xffff); 127d626906bSWarner Losh } 128d626906bSWarner Losh 12912a02d6eSMike Smith /* 13012a02d6eSMike Smith * Initialise access to PCI configuration space 13112a02d6eSMike Smith */ 13212a02d6eSMike Smith int 13312a02d6eSMike Smith pci_cfgregopen(void) 13421c3015aSDoug Rabson { 13512a02d6eSMike Smith static int opened = 0; 136aa2ea232SScott Long u_int16_t vid, did; 137af3d516fSPeter Wemm u_int16_t v; 13821c3015aSDoug Rabson 13912a02d6eSMike Smith if (opened) 14012a02d6eSMike Smith return(1); 141300451c4SMike Smith 142af3d516fSPeter Wemm if (pcireg_cfgopen() == 0) 143300451c4SMike Smith return(0); 14454c9005fSWarner Losh 145af3d516fSPeter Wemm v = pcibios_get_version(); 146af3d516fSPeter Wemm if (v > 0) 14739981fedSJohn Baldwin PRVERB(("pcibios: BIOS version %x.%02x\n", (v & 0xff00) >> 8, 14839981fedSJohn Baldwin v & 0xff)); 149af3d516fSPeter Wemm mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN); 15012a02d6eSMike Smith opened = 1; 15177fa00faSJohn Baldwin 15277fa00faSJohn Baldwin /* $PIR requires PCI BIOS 2.10 or greater. */ 15377fa00faSJohn Baldwin if (v >= 0x0210) 15477fa00faSJohn Baldwin pci_pir_open(); 155aa2ea232SScott Long 156aa2ea232SScott Long /* 157aa2ea232SScott Long * Grope around in the PCI config space to see if this is a 158aa2ea232SScott Long * chipset that is capable of doing memory-mapped config cycles. 159aa2ea232SScott Long * This also implies that it can do PCIe extended config cycles. 160aa2ea232SScott Long */ 161aa2ea232SScott Long 16216f99fe1SCraig Rodrigues /* Check for supported chipsets */ 163d748ef47SJohn Baldwin vid = pci_cfgregread(0, 0, 0, PCIR_VENDOR, 2); 164d748ef47SJohn Baldwin did = pci_cfgregread(0, 0, 0, PCIR_DEVICE, 2); 16516f99fe1SCraig Rodrigues if (vid == 0x8086) { 16616f99fe1SCraig Rodrigues if (did == 0x3590 || did == 0x3592) { 16716f99fe1SCraig Rodrigues /* Intel 7520 or 7320 */ 168aa2ea232SScott Long pciebar = pci_cfgregread(0, 0, 0, 0xce, 2) << 16; 169aa2ea232SScott Long pciereg_cfgopen(); 17016f99fe1SCraig Rodrigues } else if (did == 0x2580 || did == 0x2584) { 17116f99fe1SCraig Rodrigues /* Intel 915 or 925 */ 172aa2ea232SScott Long pciebar = pci_cfgregread(0, 0, 0, 0x48, 4); 173aa2ea232SScott Long pciereg_cfgopen(); 174aa2ea232SScott Long } 17516f99fe1SCraig Rodrigues } 176aa2ea232SScott Long 177300451c4SMike Smith return(1); 178300451c4SMike Smith } 179300451c4SMike Smith 18012a02d6eSMike Smith /* 18112a02d6eSMike Smith * Read configuration space register 18212a02d6eSMike Smith */ 183bb0d0a8eSMike Smith u_int32_t 184bb0d0a8eSMike Smith pci_cfgregread(int bus, int slot, int func, int reg, int bytes) 185bb0d0a8eSMike Smith { 186e300f53cSWarner Losh uint32_t line; 187e300f53cSWarner Losh 188bb0d0a8eSMike Smith /* 189d5ccecfaSWarner Losh * Some BIOS writers seem to want to ignore the spec and put 190d5ccecfaSWarner Losh * 0 in the intline rather than 255 to indicate none. The rest of 191d5ccecfaSWarner Losh * the code uses 255 as an invalid IRQ. 192d5ccecfaSWarner Losh */ 193d5ccecfaSWarner Losh if (reg == PCIR_INTLINE && bytes == 1) { 194af3d516fSPeter Wemm line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1); 1956f92bdd0SJohn Baldwin return (pci_i386_map_intline(line)); 196d5ccecfaSWarner Losh } 197af3d516fSPeter Wemm return (pcireg_cfgread(bus, slot, func, reg, bytes)); 198bb0d0a8eSMike Smith } 199bb0d0a8eSMike Smith 20012a02d6eSMike Smith /* 20112a02d6eSMike Smith * Write configuration space register 20212a02d6eSMike Smith */ 20312a02d6eSMike Smith void 20412a02d6eSMike Smith pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes) 20512a02d6eSMike Smith { 206af3d516fSPeter Wemm 207cb8e4332SPoul-Henning Kamp pcireg_cfgwrite(bus, slot, func, reg, data, bytes); 20812a02d6eSMike Smith } 20912a02d6eSMike Smith 21012a02d6eSMike Smith /* 21112a02d6eSMike Smith * Configuration space access using direct register operations 21212a02d6eSMike Smith */ 213ac19f918SStefan Eßer 2145bec6157SStefan Eßer /* enable configuration space accesses and return data port address */ 215a3adc4f8SStefan Eßer static int 2165bec6157SStefan Eßer pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) 2175bec6157SStefan Eßer { 2185bec6157SStefan Eßer int dataport = 0; 2195bec6157SStefan Eßer 22051ef421dSWarner Losh #ifdef XBOX 22151ef421dSWarner Losh if (arch_i386_is_xbox) { 22251ef421dSWarner Losh /* 22351ef421dSWarner Losh * The Xbox MCPX chipset is a derivative of the nForce 1 22451ef421dSWarner Losh * chipset. It almost has the same bus layout; some devices 22551ef421dSWarner Losh * cannot be used, because they have been removed. 22651ef421dSWarner Losh */ 22751ef421dSWarner Losh 22851ef421dSWarner Losh /* 22951ef421dSWarner Losh * Devices 00:00.1 and 00:00.2 used to be memory controllers on 23051ef421dSWarner Losh * the nForce chipset, but on the Xbox, using them will lockup 23151ef421dSWarner Losh * the chipset. 23251ef421dSWarner Losh */ 23351ef421dSWarner Losh if (bus == 0 && slot == 0 && (func == 1 || func == 2)) 23451ef421dSWarner Losh return dataport; 23551ef421dSWarner Losh 23651ef421dSWarner Losh /* 23751ef421dSWarner Losh * Bus 1 only contains a VGA controller at 01:00.0. When you try 23851ef421dSWarner Losh * to probe beyond that device, you only get garbage, which 23951ef421dSWarner Losh * could cause lockups. 24051ef421dSWarner Losh */ 24151ef421dSWarner Losh if (bus == 1 && (slot != 0 || func != 0)) 24251ef421dSWarner Losh return dataport; 24351ef421dSWarner Losh 24451ef421dSWarner Losh /* 24551ef421dSWarner Losh * Bus 2 used to contain the AGP controller, but the Xbox MCPX 24651ef421dSWarner Losh * doesn't have one. Probing it can cause lockups. 24751ef421dSWarner Losh */ 24851ef421dSWarner Losh if (bus >= 2) 24951ef421dSWarner Losh return dataport; 25051ef421dSWarner Losh } 25151ef421dSWarner Losh #endif 25251ef421dSWarner Losh 2535bec6157SStefan Eßer if (bus <= PCI_BUSMAX 2545bec6157SStefan Eßer && slot < devmax 2555bec6157SStefan Eßer && func <= PCI_FUNCMAX 2565bec6157SStefan Eßer && reg <= PCI_REGMAX 2575bec6157SStefan Eßer && bytes != 3 2585bec6157SStefan Eßer && (unsigned) bytes <= 4 2595bec6157SStefan Eßer && (reg & (bytes - 1)) == 0) { 2605bec6157SStefan Eßer switch (cfgmech) { 261aa2ea232SScott Long case CFGMECH_1: 262b3daa02eSStefan Eßer outl(CONF1_ADDR_PORT, (1 << 31) 263b3daa02eSStefan Eßer | (bus << 16) | (slot << 11) 264b3daa02eSStefan Eßer | (func << 8) | (reg & ~0x03)); 265b3daa02eSStefan Eßer dataport = CONF1_DATA_PORT + (reg & 0x03); 2665bec6157SStefan Eßer break; 267aa2ea232SScott Long case CFGMECH_2: 2685bec6157SStefan Eßer outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); 2695bec6157SStefan Eßer outb(CONF2_FORWARD_PORT, bus); 2705bec6157SStefan Eßer dataport = 0xc000 | (slot << 8) | reg; 2715bec6157SStefan Eßer break; 2725bec6157SStefan Eßer } 2735bec6157SStefan Eßer } 2745bec6157SStefan Eßer return (dataport); 2755bec6157SStefan Eßer } 2765bec6157SStefan Eßer 2775bec6157SStefan Eßer /* disable configuration space accesses */ 2785bec6157SStefan Eßer static void 2795bec6157SStefan Eßer pci_cfgdisable(void) 2805bec6157SStefan Eßer { 2815bec6157SStefan Eßer switch (cfgmech) { 282aa2ea232SScott Long case CFGMECH_1: 2833f7f26e9SJohn Baldwin /* 2843f7f26e9SJohn Baldwin * Do nothing for the config mechanism 1 case. 2853f7f26e9SJohn Baldwin * Writing a 0 to the address port can apparently 2863f7f26e9SJohn Baldwin * confuse some bridges and cause spurious 2873f7f26e9SJohn Baldwin * access failures. 2883f7f26e9SJohn Baldwin */ 2895bec6157SStefan Eßer break; 290aa2ea232SScott Long case CFGMECH_2: 2915bec6157SStefan Eßer outb(CONF2_ENABLE_PORT, 0); 2925bec6157SStefan Eßer break; 2935bec6157SStefan Eßer } 2945bec6157SStefan Eßer } 2955bec6157SStefan Eßer 296300451c4SMike Smith static int 29721c3015aSDoug Rabson pcireg_cfgread(int bus, int slot, int func, int reg, int bytes) 2985bec6157SStefan Eßer { 2995bec6157SStefan Eßer int data = -1; 3005bec6157SStefan Eßer int port; 3015bec6157SStefan Eßer 302aa2ea232SScott Long if (cfgmech == CFGMECH_PCIE) { 303aa2ea232SScott Long data = pciereg_cfgread(bus, slot, func, reg, bytes); 304aa2ea232SScott Long return (data); 305aa2ea232SScott Long } 306aa2ea232SScott Long 307af3d516fSPeter Wemm mtx_lock_spin(&pcicfg_mtx); 30821c3015aSDoug Rabson port = pci_cfgenable(bus, slot, func, reg, bytes); 3095bec6157SStefan Eßer if (port != 0) { 3105bec6157SStefan Eßer switch (bytes) { 3115bec6157SStefan Eßer case 1: 3125bec6157SStefan Eßer data = inb(port); 3135bec6157SStefan Eßer break; 3145bec6157SStefan Eßer case 2: 3155bec6157SStefan Eßer data = inw(port); 3165bec6157SStefan Eßer break; 3175bec6157SStefan Eßer case 4: 3185bec6157SStefan Eßer data = inl(port); 3195bec6157SStefan Eßer break; 3205bec6157SStefan Eßer } 3215bec6157SStefan Eßer pci_cfgdisable(); 3225bec6157SStefan Eßer } 323af3d516fSPeter Wemm mtx_unlock_spin(&pcicfg_mtx); 3245bec6157SStefan Eßer return (data); 3255bec6157SStefan Eßer } 3265bec6157SStefan Eßer 327300451c4SMike Smith static void 32821c3015aSDoug Rabson pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 3295bec6157SStefan Eßer { 3305bec6157SStefan Eßer int port; 3315bec6157SStefan Eßer 332aa2ea232SScott Long if (cfgmech == CFGMECH_PCIE) { 333aa2ea232SScott Long pciereg_cfgwrite(bus, slot, func, reg, data, bytes); 334aa2ea232SScott Long return; 335aa2ea232SScott Long } 336aa2ea232SScott Long 337af3d516fSPeter Wemm mtx_lock_spin(&pcicfg_mtx); 33821c3015aSDoug Rabson port = pci_cfgenable(bus, slot, func, reg, bytes); 3395bec6157SStefan Eßer if (port != 0) { 3405bec6157SStefan Eßer switch (bytes) { 3415bec6157SStefan Eßer case 1: 3425bec6157SStefan Eßer outb(port, data); 3435bec6157SStefan Eßer break; 3445bec6157SStefan Eßer case 2: 3455bec6157SStefan Eßer outw(port, data); 3465bec6157SStefan Eßer break; 3475bec6157SStefan Eßer case 4: 3485bec6157SStefan Eßer outl(port, data); 3495bec6157SStefan Eßer break; 3505bec6157SStefan Eßer } 3515bec6157SStefan Eßer pci_cfgdisable(); 3525bec6157SStefan Eßer } 353af3d516fSPeter Wemm mtx_unlock_spin(&pcicfg_mtx); 3545bec6157SStefan Eßer } 3555bec6157SStefan Eßer 35612a02d6eSMike Smith /* check whether the configuration mechanism has been correctly identified */ 3575bec6157SStefan Eßer static int 3585bec6157SStefan Eßer pci_cfgcheck(int maxdev) 359a3adc4f8SStefan Eßer { 360984de797SWarner Losh uint32_t id, class; 361984de797SWarner Losh uint8_t header; 362984de797SWarner Losh uint8_t device; 363af3d516fSPeter Wemm int port; 364a3adc4f8SStefan Eßer 3655bec6157SStefan Eßer if (bootverbose) 3665bec6157SStefan Eßer printf("pci_cfgcheck:\tdevice "); 36777b57314SStefan Eßer 3685bec6157SStefan Eßer for (device = 0; device < maxdev; device++) { 369c7483249SStefan Eßer if (bootverbose) 370c7483249SStefan Eßer printf("%d ", device); 3715bec6157SStefan Eßer 372af3d516fSPeter Wemm port = pci_cfgenable(0, device, 0, 0, 4); 373af3d516fSPeter Wemm id = inl(port); 374984de797SWarner Losh if (id == 0 || id == 0xffffffff) 37581cf5d7aSStefan Eßer continue; 37681cf5d7aSStefan Eßer 377af3d516fSPeter Wemm port = pci_cfgenable(0, device, 0, 8, 4); 378af3d516fSPeter Wemm class = inl(port) >> 8; 37981cf5d7aSStefan Eßer if (bootverbose) 3805bec6157SStefan Eßer printf("[class=%06x] ", class); 3818277ac25SStefan Eßer if (class == 0 || (class & 0xf870ff) != 0) 38281cf5d7aSStefan Eßer continue; 38381cf5d7aSStefan Eßer 384af3d516fSPeter Wemm port = pci_cfgenable(0, device, 0, 14, 1); 385af3d516fSPeter Wemm header = inb(port); 38681cf5d7aSStefan Eßer if (bootverbose) 3875bec6157SStefan Eßer printf("[hdr=%02x] ", header); 3885bec6157SStefan Eßer if ((header & 0x7e) != 0) 38981cf5d7aSStefan Eßer continue; 39081cf5d7aSStefan Eßer 3915bec6157SStefan Eßer if (bootverbose) 3925bec6157SStefan Eßer printf("is there (id=%08x)\n", id); 3935bec6157SStefan Eßer 3945bec6157SStefan Eßer pci_cfgdisable(); 3955bec6157SStefan Eßer return (1); 396a3adc4f8SStefan Eßer } 397c7483249SStefan Eßer if (bootverbose) 398c7483249SStefan Eßer printf("-- nothing found\n"); 3995bec6157SStefan Eßer 4005bec6157SStefan Eßer pci_cfgdisable(); 4015bec6157SStefan Eßer return (0); 402a3adc4f8SStefan Eßer } 403d7ea35fcSStefan Eßer 4048dc26439SPeter Wemm static int 405300451c4SMike Smith pcireg_cfgopen(void) 406ac19f918SStefan Eßer { 407984de797SWarner Losh uint32_t mode1res, oldval1; 408984de797SWarner Losh uint8_t mode2res, oldval2; 4090847c06dSStefan Eßer 41098bbce55SJohn Baldwin /* Check for type #1 first. */ 411287911bdSStefan Eßer oldval1 = inl(CONF1_ADDR_PORT); 412a3adc4f8SStefan Eßer 41377b57314SStefan Eßer if (bootverbose) { 414984de797SWarner Losh printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n", 4155bec6157SStefan Eßer oldval1); 416a3adc4f8SStefan Eßer } 417a3adc4f8SStefan Eßer 418aa2ea232SScott Long cfgmech = CFGMECH_1; 4195bec6157SStefan Eßer devmax = 32; 42077b57314SStefan Eßer 42177b57314SStefan Eßer outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 42221e25fa6SJohn Baldwin DELAY(1); 42377b57314SStefan Eßer mode1res = inl(CONF1_ADDR_PORT); 424287911bdSStefan Eßer outl(CONF1_ADDR_PORT, oldval1); 42577b57314SStefan Eßer 42677b57314SStefan Eßer if (bootverbose) 42798bbce55SJohn Baldwin printf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n", mode1res, 42898bbce55SJohn Baldwin CONF1_ENABLE_CHK); 42977b57314SStefan Eßer 43077b57314SStefan Eßer if (mode1res) { 4315bec6157SStefan Eßer if (pci_cfgcheck(32)) 4325bec6157SStefan Eßer return (cfgmech); 4335bec6157SStefan Eßer } 43477b57314SStefan Eßer 43577b57314SStefan Eßer outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); 43677b57314SStefan Eßer mode1res = inl(CONF1_ADDR_PORT); 437287911bdSStefan Eßer outl(CONF1_ADDR_PORT, oldval1); 43877b57314SStefan Eßer 43977b57314SStefan Eßer if (bootverbose) 44098bbce55SJohn Baldwin printf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n", mode1res, 44198bbce55SJohn Baldwin CONF1_ENABLE_CHK1); 44277b57314SStefan Eßer 443c7483249SStefan Eßer if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { 4445bec6157SStefan Eßer if (pci_cfgcheck(32)) 4455bec6157SStefan Eßer return (cfgmech); 446287911bdSStefan Eßer } 44777b57314SStefan Eßer 44898bbce55SJohn Baldwin /* Type #1 didn't work, so try type #2. */ 449287911bdSStefan Eßer oldval2 = inb(CONF2_ENABLE_PORT); 450287911bdSStefan Eßer 451287911bdSStefan Eßer if (bootverbose) { 4525bec6157SStefan Eßer printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", 4535bec6157SStefan Eßer oldval2); 454287911bdSStefan Eßer } 455287911bdSStefan Eßer 456287911bdSStefan Eßer if ((oldval2 & 0xf0) == 0) { 457c7483249SStefan Eßer 458aa2ea232SScott Long cfgmech = CFGMECH_2; 4595bec6157SStefan Eßer devmax = 16; 46077b57314SStefan Eßer 461287911bdSStefan Eßer outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); 462287911bdSStefan Eßer mode2res = inb(CONF2_ENABLE_PORT); 463287911bdSStefan Eßer outb(CONF2_ENABLE_PORT, oldval2); 464287911bdSStefan Eßer 465287911bdSStefan Eßer if (bootverbose) 4665bec6157SStefan Eßer printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", 467287911bdSStefan Eßer mode2res, CONF2_ENABLE_CHK); 468287911bdSStefan Eßer 469287911bdSStefan Eßer if (mode2res == CONF2_ENABLE_RES) { 470287911bdSStefan Eßer if (bootverbose) 4715bec6157SStefan Eßer printf("pci_open(2a):\tnow trying mechanism 2\n"); 472287911bdSStefan Eßer 4735bec6157SStefan Eßer if (pci_cfgcheck(16)) 4745bec6157SStefan Eßer return (cfgmech); 475287911bdSStefan Eßer } 476287911bdSStefan Eßer } 47777b57314SStefan Eßer 47898bbce55SJohn Baldwin /* Nothing worked, so punt. */ 479aa2ea232SScott Long cfgmech = CFGMECH_NONE; 4805bec6157SStefan Eßer devmax = 0; 4815bec6157SStefan Eßer return (cfgmech); 482ac19f918SStefan Eßer } 4838dc26439SPeter Wemm 484aa2ea232SScott Long static int 485aa2ea232SScott Long pciereg_cfgopen(void) 486aa2ea232SScott Long { 487aa2ea232SScott Long struct pcie_cfg_list *pcielist; 488aa2ea232SScott Long struct pcie_cfg_elem *pcie_array, *elem; 489aa2ea232SScott Long #ifdef SMP 490aa2ea232SScott Long struct pcpu *pc; 491aa2ea232SScott Long #endif 492aa2ea232SScott Long vm_offset_t va; 493aa2ea232SScott Long int i; 494aa2ea232SScott Long 495aa2ea232SScott Long if (bootverbose) 496aa2ea232SScott Long printf("Setting up PCIe mappings for BAR 0x%x\n", pciebar); 497aa2ea232SScott Long 498aa2ea232SScott Long #ifdef SMP 499aa2ea232SScott Long SLIST_FOREACH(pc, &cpuhead, pc_allcpu) 500aa2ea232SScott Long #endif 501aa2ea232SScott Long { 502aa2ea232SScott Long 503aa2ea232SScott Long pcie_array = malloc(sizeof(struct pcie_cfg_elem) * PCIE_CACHE, 504aa2ea232SScott Long M_DEVBUF, M_NOWAIT); 505aa2ea232SScott Long if (pcie_array == NULL) 506aa2ea232SScott Long return (0); 507aa2ea232SScott Long 508aa2ea232SScott Long va = kmem_alloc_nofault(kernel_map, PCIE_CACHE * PAGE_SIZE); 509aa2ea232SScott Long if (va == 0) { 510aa2ea232SScott Long free(pcie_array, M_DEVBUF); 511aa2ea232SScott Long return (0); 512aa2ea232SScott Long } 513aa2ea232SScott Long 514aa2ea232SScott Long #ifdef SMP 515aa2ea232SScott Long pcielist = &pcie_list[pc->pc_cpuid]; 516aa2ea232SScott Long #else 517aa2ea232SScott Long pcielist = &pcie_list[0]; 518aa2ea232SScott Long #endif 519aa2ea232SScott Long TAILQ_INIT(pcielist); 520aa2ea232SScott Long for (i = 0; i < PCIE_CACHE; i++) { 521aa2ea232SScott Long elem = &pcie_array[i]; 522aa2ea232SScott Long elem->vapage = va + (i * PAGE_SIZE); 523aa2ea232SScott Long elem->papage = 0; 524aa2ea232SScott Long TAILQ_INSERT_HEAD(pcielist, elem, elem); 525aa2ea232SScott Long } 526aa2ea232SScott Long } 527aa2ea232SScott Long 528aa2ea232SScott Long 529aa2ea232SScott Long cfgmech = CFGMECH_PCIE; 530aa2ea232SScott Long devmax = 32; 531aa2ea232SScott Long return (1); 532aa2ea232SScott Long } 533aa2ea232SScott Long 534aa2ea232SScott Long #define PCIE_PADDR(bar, reg, bus, slot, func) \ 535aa2ea232SScott Long ((bar) | \ 536aa2ea232SScott Long (((bus) & 0xff) << 20) | \ 537aa2ea232SScott Long (((slot) & 0x1f) << 15) | \ 538aa2ea232SScott Long (((func) & 0x7) << 12) | \ 539aa2ea232SScott Long ((reg) & 0xfff)) 540aa2ea232SScott Long 541aa2ea232SScott Long /* 542aa2ea232SScott Long * Find an element in the cache that matches the physical page desired, or 543aa2ea232SScott Long * create a new mapping from the least recently used element. 544aa2ea232SScott Long * A very simple LRU algorithm is used here, does it need to be more 545aa2ea232SScott Long * efficient? 546aa2ea232SScott Long */ 547aa2ea232SScott Long static __inline struct pcie_cfg_elem * 548aa2ea232SScott Long pciereg_findelem(vm_paddr_t papage) 549aa2ea232SScott Long { 550aa2ea232SScott Long struct pcie_cfg_list *pcielist; 551aa2ea232SScott Long struct pcie_cfg_elem *elem; 552aa2ea232SScott Long 553aa2ea232SScott Long pcielist = &pcie_list[PCPU_GET(cpuid)]; 554aa2ea232SScott Long TAILQ_FOREACH(elem, pcielist, elem) { 555aa2ea232SScott Long if (elem->papage == papage) 556aa2ea232SScott Long break; 557aa2ea232SScott Long } 558aa2ea232SScott Long 559aa2ea232SScott Long if (elem == NULL) { 560aa2ea232SScott Long elem = TAILQ_LAST(pcielist, pcie_cfg_list); 561aa2ea232SScott Long if (elem->papage != 0) { 562aa2ea232SScott Long pmap_kremove(elem->vapage); 563aa2ea232SScott Long invlpg(elem->vapage); 564aa2ea232SScott Long } 565aa2ea232SScott Long pmap_kenter(elem->vapage, papage); 566aa2ea232SScott Long elem->papage = papage; 567aa2ea232SScott Long } 568aa2ea232SScott Long 569aa2ea232SScott Long if (elem != TAILQ_FIRST(pcielist)) { 570aa2ea232SScott Long TAILQ_REMOVE(pcielist, elem, elem); 571aa2ea232SScott Long TAILQ_INSERT_HEAD(pcielist, elem, elem); 572aa2ea232SScott Long } 573aa2ea232SScott Long return (elem); 574aa2ea232SScott Long } 575aa2ea232SScott Long 576aa2ea232SScott Long static int 577aa2ea232SScott Long pciereg_cfgread(int bus, int slot, int func, int reg, int bytes) 578aa2ea232SScott Long { 579aa2ea232SScott Long struct pcie_cfg_elem *elem; 580aa2ea232SScott Long volatile vm_offset_t va; 581aa2ea232SScott Long vm_paddr_t pa, papage; 582245e410bSScott Long int data; 583aa2ea232SScott Long 584245e410bSScott Long critical_enter(); 585aa2ea232SScott Long pa = PCIE_PADDR(pciebar, reg, bus, slot, func); 586aa2ea232SScott Long papage = pa & ~PAGE_MASK; 587aa2ea232SScott Long elem = pciereg_findelem(papage); 588aa2ea232SScott Long va = elem->vapage | (pa & PAGE_MASK); 589aa2ea232SScott Long 590aa2ea232SScott Long switch (bytes) { 591aa2ea232SScott Long case 4: 592245e410bSScott Long data = *(volatile uint32_t *)(va); 593245e410bSScott Long break; 594aa2ea232SScott Long case 2: 595245e410bSScott Long data = *(volatile uint16_t *)(va); 596245e410bSScott Long break; 597aa2ea232SScott Long case 1: 598245e410bSScott Long data = *(volatile uint8_t *)(va); 599245e410bSScott Long break; 600aa2ea232SScott Long default: 601aa2ea232SScott Long panic("pciereg_cfgread: invalid width"); 602aa2ea232SScott Long } 603245e410bSScott Long 604245e410bSScott Long critical_exit(); 605245e410bSScott Long return (data); 606aa2ea232SScott Long } 607aa2ea232SScott Long 608aa2ea232SScott Long static void 609aa2ea232SScott Long pciereg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 610aa2ea232SScott Long { 611aa2ea232SScott Long struct pcie_cfg_elem *elem; 612aa2ea232SScott Long volatile vm_offset_t va; 613aa2ea232SScott Long vm_paddr_t pa, papage; 614aa2ea232SScott Long 615245e410bSScott Long critical_enter(); 616aa2ea232SScott Long pa = PCIE_PADDR(pciebar, reg, bus, slot, func); 617aa2ea232SScott Long papage = pa & ~PAGE_MASK; 618aa2ea232SScott Long elem = pciereg_findelem(papage); 619aa2ea232SScott Long va = elem->vapage | (pa & PAGE_MASK); 620aa2ea232SScott Long 621aa2ea232SScott Long switch (bytes) { 622aa2ea232SScott Long case 4: 623aa2ea232SScott Long *(volatile uint32_t *)(va) = data; 624aa2ea232SScott Long break; 625aa2ea232SScott Long case 2: 626aa2ea232SScott Long *(volatile uint16_t *)(va) = data; 627aa2ea232SScott Long break; 628aa2ea232SScott Long case 1: 629aa2ea232SScott Long *(volatile uint8_t *)(va) = data; 630aa2ea232SScott Long break; 631aa2ea232SScott Long default: 632aa2ea232SScott Long panic("pciereg_cfgwrite: invalid width"); 633aa2ea232SScott Long } 634245e410bSScott Long 635245e410bSScott Long critical_exit(); 636aa2ea232SScott Long } 637