1ac19f918SStefan Eßer /* 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 55bec6157SStefan Eßer * All rights reserved. 65bec6157SStefan Eßer * 75bec6157SStefan Eßer * Redistribution and use in source and binary forms, with or without 85bec6157SStefan Eßer * modification, are permitted provided that the following conditions 95bec6157SStefan Eßer * are met: 105bec6157SStefan Eßer * 1. Redistributions of source code must retain the above copyright 115bec6157SStefan Eßer * notice unmodified, this list of conditions, and the following 125bec6157SStefan Eßer * disclaimer. 135bec6157SStefan Eßer * 2. Redistributions in binary form must reproduce the above copyright 145bec6157SStefan Eßer * notice, this list of conditions and the following disclaimer in the 155bec6157SStefan Eßer * documentation and/or other materials provided with the distribution. 165bec6157SStefan Eßer * 175bec6157SStefan Eßer * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 185bec6157SStefan Eßer * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 195bec6157SStefan Eßer * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 205bec6157SStefan Eßer * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 215bec6157SStefan Eßer * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 225bec6157SStefan Eßer * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 235bec6157SStefan Eßer * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 245bec6157SStefan Eßer * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 255bec6157SStefan Eßer * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 265bec6157SStefan Eßer * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27ac19f918SStefan Eßer */ 28ac19f918SStefan Eßer 2971c5a901SDavid E. O'Brien #include <sys/cdefs.h> 3071c5a901SDavid E. O'Brien __FBSDID("$FreeBSD$"); 3171c5a901SDavid E. O'Brien 3277fa00faSJohn Baldwin #include <sys/param.h> 335bec6157SStefan Eßer #include <sys/systm.h> 348dc26439SPeter Wemm #include <sys/bus.h> 35af3d516fSPeter Wemm #include <sys/lock.h> 36af3d516fSPeter Wemm #include <sys/mutex.h> 37aa2ea232SScott Long #include <sys/malloc.h> 38aa2ea232SScott Long #include <sys/queue.h> 39e300f53cSWarner Losh #include <dev/pci/pcivar.h> 40e300f53cSWarner Losh #include <dev/pci/pcireg.h> 4112a02d6eSMike Smith #include <machine/pci_cfgreg.h> 42300451c4SMike Smith #include <machine/pc/bios.h> 43300451c4SMike Smith 44aa2ea232SScott Long #include <vm/vm.h> 45aa2ea232SScott Long #include <vm/vm_param.h> 46aa2ea232SScott Long #include <vm/vm_kern.h> 47aa2ea232SScott Long #include <vm/vm_extern.h> 48aa2ea232SScott Long #include <vm/pmap.h> 49aa2ea232SScott Long #include <machine/pmap.h> 50aa2ea232SScott Long 518ff25e97SJohn Baldwin #define PRVERB(a) do { \ 528ff25e97SJohn Baldwin if (bootverbose) \ 538ff25e97SJohn Baldwin printf a ; \ 548ff25e97SJohn Baldwin } while(0) 55d626906bSWarner Losh 56aa2ea232SScott Long #define PCIE_CACHE 8 57aa2ea232SScott Long struct pcie_cfg_elem { 58aa2ea232SScott Long TAILQ_ENTRY(pcie_cfg_elem) elem; 59aa2ea232SScott Long vm_offset_t vapage; 60aa2ea232SScott Long vm_paddr_t papage; 61aa2ea232SScott Long }; 62aa2ea232SScott Long 63aa2ea232SScott Long enum { 64aa2ea232SScott Long CFGMECH_NONE = 0, 65aa2ea232SScott Long CFGMECH_1, 66aa2ea232SScott Long CFGMECH_2, 67aa2ea232SScott Long CFGMECH_PCIE, 68aa2ea232SScott Long }; 69aa2ea232SScott Long 70aa2ea232SScott Long static TAILQ_HEAD(pcie_cfg_list, pcie_cfg_elem) pcie_list[MAXCPU]; 71aa2ea232SScott Long static uint32_t pciebar; 725bec6157SStefan Eßer static int cfgmech; 735bec6157SStefan Eßer static int devmax; 74aa2ea232SScott Long static struct mtx pcicfg_mtx; 75300451c4SMike Smith 7612a02d6eSMike Smith static int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes); 7712a02d6eSMike Smith static void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes); 78300451c4SMike Smith static int pcireg_cfgopen(void); 79300451c4SMike Smith 80aa2ea232SScott Long static int pciereg_cfgopen(void); 81aa2ea232SScott Long static int pciereg_cfgread(int bus, int slot, int func, int reg, 82aa2ea232SScott Long int bytes); 83aa2ea232SScott Long static void pciereg_cfgwrite(int bus, int slot, int func, int reg, 84aa2ea232SScott Long int data, int bytes); 85af3d516fSPeter Wemm 868ce1ab3aSWarner Losh /* 878ce1ab3aSWarner Losh * Some BIOS writers seem to want to ignore the spec and put 888ce1ab3aSWarner Losh * 0 in the intline rather than 255 to indicate none. Some use 898ce1ab3aSWarner Losh * numbers in the range 128-254 to indicate something strange and 908ce1ab3aSWarner Losh * apparently undocumented anywhere. Assume these are completely bogus 918ce1ab3aSWarner Losh * and map them to 255, which means "none". 928ce1ab3aSWarner Losh */ 935908d366SStefan Farfeleder static __inline int 948ce1ab3aSWarner Losh pci_i386_map_intline(int line) 958ce1ab3aSWarner Losh { 968ce1ab3aSWarner Losh if (line == 0 || line >= 128) 97e300f53cSWarner Losh return (PCI_INVALID_IRQ); 988ce1ab3aSWarner Losh return (line); 998ce1ab3aSWarner Losh } 1008ce1ab3aSWarner Losh 101d626906bSWarner Losh static u_int16_t 102d626906bSWarner Losh pcibios_get_version(void) 103d626906bSWarner Losh { 104d626906bSWarner Losh struct bios_regs args; 105d626906bSWarner Losh 1065264a94fSJohn Baldwin if (PCIbios.ventry == 0) { 107d626906bSWarner Losh PRVERB(("pcibios: No call entry point\n")); 108d626906bSWarner Losh return (0); 109d626906bSWarner Losh } 110d626906bSWarner Losh args.eax = PCIBIOS_BIOS_PRESENT; 111d626906bSWarner Losh if (bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL))) { 112d626906bSWarner Losh PRVERB(("pcibios: BIOS_PRESENT call failed\n")); 113d626906bSWarner Losh return (0); 114d626906bSWarner Losh } 115d626906bSWarner Losh if (args.edx != 0x20494350) { 116d626906bSWarner Losh PRVERB(("pcibios: BIOS_PRESENT didn't return 'PCI ' in edx\n")); 117d626906bSWarner Losh return (0); 118d626906bSWarner Losh } 119d626906bSWarner Losh return (args.ebx & 0xffff); 120d626906bSWarner Losh } 121d626906bSWarner Losh 12212a02d6eSMike Smith /* 12312a02d6eSMike Smith * Initialise access to PCI configuration space 12412a02d6eSMike Smith */ 12512a02d6eSMike Smith int 12612a02d6eSMike Smith pci_cfgregopen(void) 12721c3015aSDoug Rabson { 12812a02d6eSMike Smith static int opened = 0; 129aa2ea232SScott Long u_int16_t vid, did; 130af3d516fSPeter Wemm u_int16_t v; 13121c3015aSDoug Rabson 13212a02d6eSMike Smith if (opened) 13312a02d6eSMike Smith return(1); 134300451c4SMike Smith 135af3d516fSPeter Wemm if (pcireg_cfgopen() == 0) 136300451c4SMike Smith return(0); 13754c9005fSWarner Losh 138af3d516fSPeter Wemm v = pcibios_get_version(); 139af3d516fSPeter Wemm if (v > 0) 14039981fedSJohn Baldwin PRVERB(("pcibios: BIOS version %x.%02x\n", (v & 0xff00) >> 8, 14139981fedSJohn Baldwin v & 0xff)); 142af3d516fSPeter Wemm mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN); 14312a02d6eSMike Smith opened = 1; 14477fa00faSJohn Baldwin 14577fa00faSJohn Baldwin /* $PIR requires PCI BIOS 2.10 or greater. */ 14677fa00faSJohn Baldwin if (v >= 0x0210) 14777fa00faSJohn Baldwin pci_pir_open(); 148aa2ea232SScott Long 149aa2ea232SScott Long /* 150aa2ea232SScott Long * Grope around in the PCI config space to see if this is a 151aa2ea232SScott Long * chipset that is capable of doing memory-mapped config cycles. 152aa2ea232SScott Long * This also implies that it can do PCIe extended config cycles. 153aa2ea232SScott Long */ 154aa2ea232SScott Long 155aa2ea232SScott Long /* Check for the Intel 7520 and 925 chipsets */ 156aa2ea232SScott Long vid = pci_cfgregread(0, 0, 0, 0x0, 2); 157aa2ea232SScott Long did = pci_cfgregread(0, 0, 0, 0x2, 2); 158aa2ea232SScott Long if ((vid == 0x8086) && (did == 0x3590)) { 159aa2ea232SScott Long pciebar = pci_cfgregread(0, 0, 0, 0xce, 2) << 16; 160aa2ea232SScott Long pciereg_cfgopen(); 161aa2ea232SScott Long } else if ((vid == 0x8086) && (did == 0x2580)) { 162aa2ea232SScott Long pciebar = pci_cfgregread(0, 0, 0, 0x48, 4); 163aa2ea232SScott Long pciereg_cfgopen(); 164aa2ea232SScott Long } 165aa2ea232SScott Long 166300451c4SMike Smith return(1); 167300451c4SMike Smith } 168300451c4SMike Smith 16912a02d6eSMike Smith /* 17012a02d6eSMike Smith * Read configuration space register 17112a02d6eSMike Smith */ 172bb0d0a8eSMike Smith u_int32_t 173bb0d0a8eSMike Smith pci_cfgregread(int bus, int slot, int func, int reg, int bytes) 174bb0d0a8eSMike Smith { 175e300f53cSWarner Losh uint32_t line; 176e300f53cSWarner Losh 177bb0d0a8eSMike Smith /* 178d5ccecfaSWarner Losh * Some BIOS writers seem to want to ignore the spec and put 179d5ccecfaSWarner Losh * 0 in the intline rather than 255 to indicate none. The rest of 180d5ccecfaSWarner Losh * the code uses 255 as an invalid IRQ. 181d5ccecfaSWarner Losh */ 182d5ccecfaSWarner Losh if (reg == PCIR_INTLINE && bytes == 1) { 183af3d516fSPeter Wemm line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1); 1846f92bdd0SJohn Baldwin return (pci_i386_map_intline(line)); 185d5ccecfaSWarner Losh } 186af3d516fSPeter Wemm return (pcireg_cfgread(bus, slot, func, reg, bytes)); 187bb0d0a8eSMike Smith } 188bb0d0a8eSMike Smith 18912a02d6eSMike Smith /* 19012a02d6eSMike Smith * Write configuration space register 19112a02d6eSMike Smith */ 19212a02d6eSMike Smith void 19312a02d6eSMike Smith pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes) 19412a02d6eSMike Smith { 195af3d516fSPeter Wemm 196cb8e4332SPoul-Henning Kamp pcireg_cfgwrite(bus, slot, func, reg, data, bytes); 19712a02d6eSMike Smith } 19812a02d6eSMike Smith 19912a02d6eSMike Smith /* 20012a02d6eSMike Smith * Configuration space access using direct register operations 20112a02d6eSMike Smith */ 202ac19f918SStefan Eßer 2035bec6157SStefan Eßer /* enable configuration space accesses and return data port address */ 204a3adc4f8SStefan Eßer static int 2055bec6157SStefan Eßer pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) 2065bec6157SStefan Eßer { 2075bec6157SStefan Eßer int dataport = 0; 2085bec6157SStefan Eßer 2095bec6157SStefan Eßer if (bus <= PCI_BUSMAX 2105bec6157SStefan Eßer && slot < devmax 2115bec6157SStefan Eßer && func <= PCI_FUNCMAX 2125bec6157SStefan Eßer && reg <= PCI_REGMAX 2135bec6157SStefan Eßer && bytes != 3 2145bec6157SStefan Eßer && (unsigned) bytes <= 4 2155bec6157SStefan Eßer && (reg & (bytes - 1)) == 0) { 2165bec6157SStefan Eßer switch (cfgmech) { 217aa2ea232SScott Long case CFGMECH_1: 218b3daa02eSStefan Eßer outl(CONF1_ADDR_PORT, (1 << 31) 219b3daa02eSStefan Eßer | (bus << 16) | (slot << 11) 220b3daa02eSStefan Eßer | (func << 8) | (reg & ~0x03)); 221b3daa02eSStefan Eßer dataport = CONF1_DATA_PORT + (reg & 0x03); 2225bec6157SStefan Eßer break; 223aa2ea232SScott Long case CFGMECH_2: 2245bec6157SStefan Eßer outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); 2255bec6157SStefan Eßer outb(CONF2_FORWARD_PORT, bus); 2265bec6157SStefan Eßer dataport = 0xc000 | (slot << 8) | reg; 2275bec6157SStefan Eßer break; 2285bec6157SStefan Eßer } 2295bec6157SStefan Eßer } 2305bec6157SStefan Eßer return (dataport); 2315bec6157SStefan Eßer } 2325bec6157SStefan Eßer 2335bec6157SStefan Eßer /* disable configuration space accesses */ 2345bec6157SStefan Eßer static void 2355bec6157SStefan Eßer pci_cfgdisable(void) 2365bec6157SStefan Eßer { 2375bec6157SStefan Eßer switch (cfgmech) { 238aa2ea232SScott Long case CFGMECH_1: 2395bec6157SStefan Eßer outl(CONF1_ADDR_PORT, 0); 2405bec6157SStefan Eßer break; 241aa2ea232SScott Long case CFGMECH_2: 2425bec6157SStefan Eßer outb(CONF2_ENABLE_PORT, 0); 2435bec6157SStefan Eßer outb(CONF2_FORWARD_PORT, 0); 2445bec6157SStefan Eßer break; 2455bec6157SStefan Eßer } 2465bec6157SStefan Eßer } 2475bec6157SStefan Eßer 248300451c4SMike Smith static int 24921c3015aSDoug Rabson pcireg_cfgread(int bus, int slot, int func, int reg, int bytes) 2505bec6157SStefan Eßer { 2515bec6157SStefan Eßer int data = -1; 2525bec6157SStefan Eßer int port; 2535bec6157SStefan Eßer 254aa2ea232SScott Long if (cfgmech == CFGMECH_PCIE) { 255aa2ea232SScott Long data = pciereg_cfgread(bus, slot, func, reg, bytes); 256aa2ea232SScott Long return (data); 257aa2ea232SScott Long } 258aa2ea232SScott Long 259af3d516fSPeter Wemm mtx_lock_spin(&pcicfg_mtx); 26021c3015aSDoug Rabson port = pci_cfgenable(bus, slot, func, reg, bytes); 2615bec6157SStefan Eßer if (port != 0) { 2625bec6157SStefan Eßer switch (bytes) { 2635bec6157SStefan Eßer case 1: 2645bec6157SStefan Eßer data = inb(port); 2655bec6157SStefan Eßer break; 2665bec6157SStefan Eßer case 2: 2675bec6157SStefan Eßer data = inw(port); 2685bec6157SStefan Eßer break; 2695bec6157SStefan Eßer case 4: 2705bec6157SStefan Eßer data = inl(port); 2715bec6157SStefan Eßer break; 2725bec6157SStefan Eßer } 2735bec6157SStefan Eßer pci_cfgdisable(); 2745bec6157SStefan Eßer } 275af3d516fSPeter Wemm mtx_unlock_spin(&pcicfg_mtx); 2765bec6157SStefan Eßer return (data); 2775bec6157SStefan Eßer } 2785bec6157SStefan Eßer 279300451c4SMike Smith static void 28021c3015aSDoug Rabson pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 2815bec6157SStefan Eßer { 2825bec6157SStefan Eßer int port; 2835bec6157SStefan Eßer 284aa2ea232SScott Long if (cfgmech == CFGMECH_PCIE) { 285aa2ea232SScott Long pciereg_cfgwrite(bus, slot, func, reg, data, bytes); 286aa2ea232SScott Long return; 287aa2ea232SScott Long } 288aa2ea232SScott Long 289af3d516fSPeter Wemm mtx_lock_spin(&pcicfg_mtx); 29021c3015aSDoug Rabson port = pci_cfgenable(bus, slot, func, reg, bytes); 2915bec6157SStefan Eßer if (port != 0) { 2925bec6157SStefan Eßer switch (bytes) { 2935bec6157SStefan Eßer case 1: 2945bec6157SStefan Eßer outb(port, data); 2955bec6157SStefan Eßer break; 2965bec6157SStefan Eßer case 2: 2975bec6157SStefan Eßer outw(port, data); 2985bec6157SStefan Eßer break; 2995bec6157SStefan Eßer case 4: 3005bec6157SStefan Eßer outl(port, data); 3015bec6157SStefan Eßer break; 3025bec6157SStefan Eßer } 3035bec6157SStefan Eßer pci_cfgdisable(); 3045bec6157SStefan Eßer } 305af3d516fSPeter Wemm mtx_unlock_spin(&pcicfg_mtx); 3065bec6157SStefan Eßer } 3075bec6157SStefan Eßer 30812a02d6eSMike Smith /* check whether the configuration mechanism has been correctly identified */ 3095bec6157SStefan Eßer static int 3105bec6157SStefan Eßer pci_cfgcheck(int maxdev) 311a3adc4f8SStefan Eßer { 312984de797SWarner Losh uint32_t id, class; 313984de797SWarner Losh uint8_t header; 314984de797SWarner Losh uint8_t device; 315af3d516fSPeter Wemm int port; 316a3adc4f8SStefan Eßer 3175bec6157SStefan Eßer if (bootverbose) 3185bec6157SStefan Eßer printf("pci_cfgcheck:\tdevice "); 31977b57314SStefan Eßer 3205bec6157SStefan Eßer for (device = 0; device < maxdev; device++) { 321c7483249SStefan Eßer if (bootverbose) 322c7483249SStefan Eßer printf("%d ", device); 3235bec6157SStefan Eßer 324af3d516fSPeter Wemm port = pci_cfgenable(0, device, 0, 0, 4); 325af3d516fSPeter Wemm id = inl(port); 326984de797SWarner Losh if (id == 0 || id == 0xffffffff) 32781cf5d7aSStefan Eßer continue; 32881cf5d7aSStefan Eßer 329af3d516fSPeter Wemm port = pci_cfgenable(0, device, 0, 8, 4); 330af3d516fSPeter Wemm class = inl(port) >> 8; 33181cf5d7aSStefan Eßer if (bootverbose) 3325bec6157SStefan Eßer printf("[class=%06x] ", class); 3338277ac25SStefan Eßer if (class == 0 || (class & 0xf870ff) != 0) 33481cf5d7aSStefan Eßer continue; 33581cf5d7aSStefan Eßer 336af3d516fSPeter Wemm port = pci_cfgenable(0, device, 0, 14, 1); 337af3d516fSPeter Wemm header = inb(port); 33881cf5d7aSStefan Eßer if (bootverbose) 3395bec6157SStefan Eßer printf("[hdr=%02x] ", header); 3405bec6157SStefan Eßer if ((header & 0x7e) != 0) 34181cf5d7aSStefan Eßer continue; 34281cf5d7aSStefan Eßer 3435bec6157SStefan Eßer if (bootverbose) 3445bec6157SStefan Eßer printf("is there (id=%08x)\n", id); 3455bec6157SStefan Eßer 3465bec6157SStefan Eßer pci_cfgdisable(); 3475bec6157SStefan Eßer return (1); 348a3adc4f8SStefan Eßer } 349c7483249SStefan Eßer if (bootverbose) 350c7483249SStefan Eßer printf("-- nothing found\n"); 3515bec6157SStefan Eßer 3525bec6157SStefan Eßer pci_cfgdisable(); 3535bec6157SStefan Eßer return (0); 354a3adc4f8SStefan Eßer } 355d7ea35fcSStefan Eßer 3568dc26439SPeter Wemm static int 357300451c4SMike Smith pcireg_cfgopen(void) 358ac19f918SStefan Eßer { 359984de797SWarner Losh uint32_t mode1res, oldval1; 360984de797SWarner Losh uint8_t mode2res, oldval2; 3610847c06dSStefan Eßer 362287911bdSStefan Eßer oldval1 = inl(CONF1_ADDR_PORT); 363a3adc4f8SStefan Eßer 36477b57314SStefan Eßer if (bootverbose) { 365984de797SWarner Losh printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n", 3665bec6157SStefan Eßer oldval1); 367a3adc4f8SStefan Eßer } 368a3adc4f8SStefan Eßer 3690e2f699bSStefan Eßer if ((oldval1 & CONF1_ENABLE_MSK) == 0) { 370287911bdSStefan Eßer 371aa2ea232SScott Long cfgmech = CFGMECH_1; 3725bec6157SStefan Eßer devmax = 32; 37377b57314SStefan Eßer 37477b57314SStefan Eßer outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 37521e25fa6SJohn Baldwin DELAY(1); 37677b57314SStefan Eßer mode1res = inl(CONF1_ADDR_PORT); 377287911bdSStefan Eßer outl(CONF1_ADDR_PORT, oldval1); 37877b57314SStefan Eßer 37977b57314SStefan Eßer if (bootverbose) 380984de797SWarner Losh printf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n", 38177b57314SStefan Eßer mode1res, CONF1_ENABLE_CHK); 38277b57314SStefan Eßer 38377b57314SStefan Eßer if (mode1res) { 3845bec6157SStefan Eßer if (pci_cfgcheck(32)) 3855bec6157SStefan Eßer return (cfgmech); 3865bec6157SStefan Eßer } 38777b57314SStefan Eßer 38877b57314SStefan Eßer outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); 38977b57314SStefan Eßer mode1res = inl(CONF1_ADDR_PORT); 390287911bdSStefan Eßer outl(CONF1_ADDR_PORT, oldval1); 39177b57314SStefan Eßer 39277b57314SStefan Eßer if (bootverbose) 393984de797SWarner Losh printf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n", 39477b57314SStefan Eßer mode1res, CONF1_ENABLE_CHK1); 39577b57314SStefan Eßer 396c7483249SStefan Eßer if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { 3975bec6157SStefan Eßer if (pci_cfgcheck(32)) 3985bec6157SStefan Eßer return (cfgmech); 399287911bdSStefan Eßer } 4005bec6157SStefan Eßer } 40177b57314SStefan Eßer 402287911bdSStefan Eßer oldval2 = inb(CONF2_ENABLE_PORT); 403287911bdSStefan Eßer 404287911bdSStefan Eßer if (bootverbose) { 4055bec6157SStefan Eßer printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", 4065bec6157SStefan Eßer oldval2); 407287911bdSStefan Eßer } 408287911bdSStefan Eßer 409287911bdSStefan Eßer if ((oldval2 & 0xf0) == 0) { 410c7483249SStefan Eßer 411aa2ea232SScott Long cfgmech = CFGMECH_2; 4125bec6157SStefan Eßer devmax = 16; 41377b57314SStefan Eßer 414287911bdSStefan Eßer outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); 415287911bdSStefan Eßer mode2res = inb(CONF2_ENABLE_PORT); 416287911bdSStefan Eßer outb(CONF2_ENABLE_PORT, oldval2); 417287911bdSStefan Eßer 418287911bdSStefan Eßer if (bootverbose) 4195bec6157SStefan Eßer printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", 420287911bdSStefan Eßer mode2res, CONF2_ENABLE_CHK); 421287911bdSStefan Eßer 422287911bdSStefan Eßer if (mode2res == CONF2_ENABLE_RES) { 423287911bdSStefan Eßer if (bootverbose) 4245bec6157SStefan Eßer printf("pci_open(2a):\tnow trying mechanism 2\n"); 425287911bdSStefan Eßer 4265bec6157SStefan Eßer if (pci_cfgcheck(16)) 4275bec6157SStefan Eßer return (cfgmech); 428287911bdSStefan Eßer } 429287911bdSStefan Eßer } 43077b57314SStefan Eßer 431aa2ea232SScott Long cfgmech = CFGMECH_NONE; 4325bec6157SStefan Eßer devmax = 0; 4335bec6157SStefan Eßer return (cfgmech); 434ac19f918SStefan Eßer } 4358dc26439SPeter Wemm 436aa2ea232SScott Long static int 437aa2ea232SScott Long pciereg_cfgopen(void) 438aa2ea232SScott Long { 439aa2ea232SScott Long struct pcie_cfg_list *pcielist; 440aa2ea232SScott Long struct pcie_cfg_elem *pcie_array, *elem; 441aa2ea232SScott Long #ifdef SMP 442aa2ea232SScott Long struct pcpu *pc; 443aa2ea232SScott Long #endif 444aa2ea232SScott Long vm_offset_t va; 445aa2ea232SScott Long int i; 446aa2ea232SScott Long 447aa2ea232SScott Long if (bootverbose) 448aa2ea232SScott Long printf("Setting up PCIe mappings for BAR 0x%x\n", pciebar); 449aa2ea232SScott Long 450aa2ea232SScott Long #ifdef SMP 451aa2ea232SScott Long SLIST_FOREACH(pc, &cpuhead, pc_allcpu) 452aa2ea232SScott Long #endif 453aa2ea232SScott Long { 454aa2ea232SScott Long 455aa2ea232SScott Long pcie_array = malloc(sizeof(struct pcie_cfg_elem) * PCIE_CACHE, 456aa2ea232SScott Long M_DEVBUF, M_NOWAIT); 457aa2ea232SScott Long if (pcie_array == NULL) 458aa2ea232SScott Long return (0); 459aa2ea232SScott Long 460aa2ea232SScott Long va = kmem_alloc_nofault(kernel_map, PCIE_CACHE * PAGE_SIZE); 461aa2ea232SScott Long if (va == 0) { 462aa2ea232SScott Long free(pcie_array, M_DEVBUF); 463aa2ea232SScott Long return (0); 464aa2ea232SScott Long } 465aa2ea232SScott Long 466aa2ea232SScott Long #ifdef SMP 467aa2ea232SScott Long pcielist = &pcie_list[pc->pc_cpuid]; 468aa2ea232SScott Long #else 469aa2ea232SScott Long pcielist = &pcie_list[0]; 470aa2ea232SScott Long #endif 471aa2ea232SScott Long TAILQ_INIT(pcielist); 472aa2ea232SScott Long for (i = 0; i < PCIE_CACHE; i++) { 473aa2ea232SScott Long elem = &pcie_array[i]; 474aa2ea232SScott Long elem->vapage = va + (i * PAGE_SIZE); 475aa2ea232SScott Long elem->papage = 0; 476aa2ea232SScott Long TAILQ_INSERT_HEAD(pcielist, elem, elem); 477aa2ea232SScott Long } 478aa2ea232SScott Long } 479aa2ea232SScott Long 480aa2ea232SScott Long 481aa2ea232SScott Long cfgmech = CFGMECH_PCIE; 482aa2ea232SScott Long devmax = 32; 483aa2ea232SScott Long return (1); 484aa2ea232SScott Long } 485aa2ea232SScott Long 486aa2ea232SScott Long #define PCIE_PADDR(bar, reg, bus, slot, func) \ 487aa2ea232SScott Long ((bar) | \ 488aa2ea232SScott Long (((bus) & 0xff) << 20) | \ 489aa2ea232SScott Long (((slot) & 0x1f) << 15) | \ 490aa2ea232SScott Long (((func) & 0x7) << 12) | \ 491aa2ea232SScott Long ((reg) & 0xfff)) 492aa2ea232SScott Long 493aa2ea232SScott Long /* 494aa2ea232SScott Long * Find an element in the cache that matches the physical page desired, or 495aa2ea232SScott Long * create a new mapping from the least recently used element. 496aa2ea232SScott Long * A very simple LRU algorithm is used here, does it need to be more 497aa2ea232SScott Long * efficient? 498aa2ea232SScott Long */ 499aa2ea232SScott Long static __inline struct pcie_cfg_elem * 500aa2ea232SScott Long pciereg_findelem(vm_paddr_t papage) 501aa2ea232SScott Long { 502aa2ea232SScott Long struct pcie_cfg_list *pcielist; 503aa2ea232SScott Long struct pcie_cfg_elem *elem; 504aa2ea232SScott Long 505aa2ea232SScott Long critical_enter(); 506aa2ea232SScott Long pcielist = &pcie_list[PCPU_GET(cpuid)]; 507aa2ea232SScott Long TAILQ_FOREACH(elem, pcielist, elem) { 508aa2ea232SScott Long if (elem->papage == papage) 509aa2ea232SScott Long break; 510aa2ea232SScott Long } 511aa2ea232SScott Long 512aa2ea232SScott Long if (elem == NULL) { 513aa2ea232SScott Long elem = TAILQ_LAST(pcielist, pcie_cfg_list); 514aa2ea232SScott Long if (elem->papage != 0) { 515aa2ea232SScott Long pmap_kremove(elem->vapage); 516aa2ea232SScott Long invlpg(elem->vapage); 517aa2ea232SScott Long } 518aa2ea232SScott Long pmap_kenter(elem->vapage, papage); 519aa2ea232SScott Long elem->papage = papage; 520aa2ea232SScott Long } 521aa2ea232SScott Long 522aa2ea232SScott Long if (elem != TAILQ_FIRST(pcielist)) { 523aa2ea232SScott Long TAILQ_REMOVE(pcielist, elem, elem); 524aa2ea232SScott Long TAILQ_INSERT_HEAD(pcielist, elem, elem); 525aa2ea232SScott Long } 526aa2ea232SScott Long critical_exit(); 527aa2ea232SScott Long return (elem); 528aa2ea232SScott Long } 529aa2ea232SScott Long 530aa2ea232SScott Long static int 531aa2ea232SScott Long pciereg_cfgread(int bus, int slot, int func, int reg, int bytes) 532aa2ea232SScott Long { 533aa2ea232SScott Long struct pcie_cfg_elem *elem; 534aa2ea232SScott Long volatile vm_offset_t va; 535aa2ea232SScott Long vm_paddr_t pa, papage; 536aa2ea232SScott Long 537aa2ea232SScott Long pa = PCIE_PADDR(pciebar, reg, bus, slot, func); 538aa2ea232SScott Long papage = pa & ~PAGE_MASK; 539aa2ea232SScott Long elem = pciereg_findelem(papage); 540aa2ea232SScott Long va = elem->vapage | (pa & PAGE_MASK); 541aa2ea232SScott Long 542aa2ea232SScott Long switch (bytes) { 543aa2ea232SScott Long case 4: 544aa2ea232SScott Long return (*(volatile uint32_t *)(va)); 545aa2ea232SScott Long case 2: 546aa2ea232SScott Long return (*(volatile uint16_t *)(va)); 547aa2ea232SScott Long case 1: 548aa2ea232SScott Long return (*(volatile uint8_t *)(va)); 549aa2ea232SScott Long default: 550aa2ea232SScott Long panic("pciereg_cfgread: invalid width"); 551aa2ea232SScott Long } 552aa2ea232SScott Long } 553aa2ea232SScott Long 554aa2ea232SScott Long static void 555aa2ea232SScott Long pciereg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 556aa2ea232SScott Long { 557aa2ea232SScott Long struct pcie_cfg_elem *elem; 558aa2ea232SScott Long volatile vm_offset_t va; 559aa2ea232SScott Long vm_paddr_t pa, papage; 560aa2ea232SScott Long 561aa2ea232SScott Long pa = PCIE_PADDR(pciebar, reg, bus, slot, func); 562aa2ea232SScott Long papage = pa & ~PAGE_MASK; 563aa2ea232SScott Long elem = pciereg_findelem(papage); 564aa2ea232SScott Long va = elem->vapage | (pa & PAGE_MASK); 565aa2ea232SScott Long 566aa2ea232SScott Long switch (bytes) { 567aa2ea232SScott Long case 4: 568aa2ea232SScott Long *(volatile uint32_t *)(va) = data; 569aa2ea232SScott Long break; 570aa2ea232SScott Long case 2: 571aa2ea232SScott Long *(volatile uint16_t *)(va) = data; 572aa2ea232SScott Long break; 573aa2ea232SScott Long case 1: 574aa2ea232SScott Long *(volatile uint8_t *)(va) = data; 575aa2ea232SScott Long break; 576aa2ea232SScott Long default: 577aa2ea232SScott Long panic("pciereg_cfgwrite: invalid width"); 578aa2ea232SScott Long } 579aa2ea232SScott Long } 580