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 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 3377fa00faSJohn Baldwin #include <sys/param.h> 345bec6157SStefan Eßer #include <sys/systm.h> 358dc26439SPeter Wemm #include <sys/bus.h> 36af3d516fSPeter Wemm #include <sys/lock.h> 37af3d516fSPeter Wemm #include <sys/mutex.h> 38aa2ea232SScott Long #include <sys/malloc.h> 39aa2ea232SScott Long #include <sys/queue.h> 40e300f53cSWarner Losh #include <dev/pci/pcivar.h> 41e300f53cSWarner Losh #include <dev/pci/pcireg.h> 4212a02d6eSMike Smith #include <machine/pci_cfgreg.h> 43300451c4SMike Smith #include <machine/pc/bios.h> 44300451c4SMike Smith 45aa2ea232SScott Long #include <vm/vm.h> 46aa2ea232SScott Long #include <vm/vm_param.h> 47aa2ea232SScott Long #include <vm/vm_kern.h> 48aa2ea232SScott Long #include <vm/vm_extern.h> 49aa2ea232SScott Long #include <vm/pmap.h> 50aa2ea232SScott Long #include <machine/pmap.h> 51aa2ea232SScott Long 528ff25e97SJohn Baldwin #define PRVERB(a) do { \ 538ff25e97SJohn Baldwin if (bootverbose) \ 548ff25e97SJohn Baldwin printf a ; \ 558ff25e97SJohn Baldwin } while(0) 56d626906bSWarner Losh 57aa2ea232SScott Long #define PCIE_CACHE 8 58aa2ea232SScott Long struct pcie_cfg_elem { 59aa2ea232SScott Long TAILQ_ENTRY(pcie_cfg_elem) elem; 60aa2ea232SScott Long vm_offset_t vapage; 61aa2ea232SScott Long vm_paddr_t papage; 62aa2ea232SScott Long }; 63aa2ea232SScott Long 64aa2ea232SScott Long enum { 65aa2ea232SScott Long CFGMECH_NONE = 0, 66aa2ea232SScott Long CFGMECH_1, 67aa2ea232SScott Long CFGMECH_2, 68aa2ea232SScott Long CFGMECH_PCIE, 69aa2ea232SScott Long }; 70aa2ea232SScott Long 71aa2ea232SScott Long static TAILQ_HEAD(pcie_cfg_list, pcie_cfg_elem) pcie_list[MAXCPU]; 72aa2ea232SScott Long static uint32_t pciebar; 735bec6157SStefan Eßer static int cfgmech; 745bec6157SStefan Eßer static int devmax; 75aa2ea232SScott Long static struct mtx pcicfg_mtx; 76300451c4SMike Smith 7712a02d6eSMike Smith static int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes); 7812a02d6eSMike Smith static void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes); 79300451c4SMike Smith static int pcireg_cfgopen(void); 80300451c4SMike Smith 81aa2ea232SScott Long static int pciereg_cfgopen(void); 82aa2ea232SScott Long static int pciereg_cfgread(int bus, int slot, int func, int reg, 83aa2ea232SScott Long int bytes); 84aa2ea232SScott Long static void pciereg_cfgwrite(int bus, int slot, int func, int reg, 85aa2ea232SScott Long int data, int bytes); 86af3d516fSPeter Wemm 878ce1ab3aSWarner Losh /* 888ce1ab3aSWarner Losh * Some BIOS writers seem to want to ignore the spec and put 898ce1ab3aSWarner Losh * 0 in the intline rather than 255 to indicate none. Some use 908ce1ab3aSWarner Losh * numbers in the range 128-254 to indicate something strange and 918ce1ab3aSWarner Losh * apparently undocumented anywhere. Assume these are completely bogus 928ce1ab3aSWarner Losh * and map them to 255, which means "none". 938ce1ab3aSWarner Losh */ 945908d366SStefan Farfeleder static __inline int 958ce1ab3aSWarner Losh pci_i386_map_intline(int line) 968ce1ab3aSWarner Losh { 978ce1ab3aSWarner Losh if (line == 0 || line >= 128) 98e300f53cSWarner Losh return (PCI_INVALID_IRQ); 998ce1ab3aSWarner Losh return (line); 1008ce1ab3aSWarner Losh } 1018ce1ab3aSWarner Losh 102d626906bSWarner Losh static u_int16_t 103d626906bSWarner Losh pcibios_get_version(void) 104d626906bSWarner Losh { 105d626906bSWarner Losh struct bios_regs args; 106d626906bSWarner Losh 1075264a94fSJohn Baldwin if (PCIbios.ventry == 0) { 108d626906bSWarner Losh PRVERB(("pcibios: No call entry point\n")); 109d626906bSWarner Losh return (0); 110d626906bSWarner Losh } 111d626906bSWarner Losh args.eax = PCIBIOS_BIOS_PRESENT; 112d626906bSWarner Losh if (bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL))) { 113d626906bSWarner Losh PRVERB(("pcibios: BIOS_PRESENT call failed\n")); 114d626906bSWarner Losh return (0); 115d626906bSWarner Losh } 116d626906bSWarner Losh if (args.edx != 0x20494350) { 117d626906bSWarner Losh PRVERB(("pcibios: BIOS_PRESENT didn't return 'PCI ' in edx\n")); 118d626906bSWarner Losh return (0); 119d626906bSWarner Losh } 120d626906bSWarner Losh return (args.ebx & 0xffff); 121d626906bSWarner Losh } 122d626906bSWarner Losh 12312a02d6eSMike Smith /* 12412a02d6eSMike Smith * Initialise access to PCI configuration space 12512a02d6eSMike Smith */ 12612a02d6eSMike Smith int 12712a02d6eSMike Smith pci_cfgregopen(void) 12821c3015aSDoug Rabson { 12912a02d6eSMike Smith static int opened = 0; 130aa2ea232SScott Long u_int16_t vid, did; 131af3d516fSPeter Wemm u_int16_t v; 13221c3015aSDoug Rabson 13312a02d6eSMike Smith if (opened) 13412a02d6eSMike Smith return(1); 135300451c4SMike Smith 136af3d516fSPeter Wemm if (pcireg_cfgopen() == 0) 137300451c4SMike Smith return(0); 13854c9005fSWarner Losh 139af3d516fSPeter Wemm v = pcibios_get_version(); 140af3d516fSPeter Wemm if (v > 0) 14139981fedSJohn Baldwin PRVERB(("pcibios: BIOS version %x.%02x\n", (v & 0xff00) >> 8, 14239981fedSJohn Baldwin v & 0xff)); 143af3d516fSPeter Wemm mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN); 14412a02d6eSMike Smith opened = 1; 14577fa00faSJohn Baldwin 14677fa00faSJohn Baldwin /* $PIR requires PCI BIOS 2.10 or greater. */ 14777fa00faSJohn Baldwin if (v >= 0x0210) 14877fa00faSJohn Baldwin pci_pir_open(); 149aa2ea232SScott Long 150aa2ea232SScott Long /* 151aa2ea232SScott Long * Grope around in the PCI config space to see if this is a 152aa2ea232SScott Long * chipset that is capable of doing memory-mapped config cycles. 153aa2ea232SScott Long * This also implies that it can do PCIe extended config cycles. 154aa2ea232SScott Long */ 155aa2ea232SScott Long 156aa2ea232SScott Long /* Check for the Intel 7520 and 925 chipsets */ 157aa2ea232SScott Long vid = pci_cfgregread(0, 0, 0, 0x0, 2); 158aa2ea232SScott Long did = pci_cfgregread(0, 0, 0, 0x2, 2); 159aa2ea232SScott Long if ((vid == 0x8086) && (did == 0x3590)) { 160aa2ea232SScott Long pciebar = pci_cfgregread(0, 0, 0, 0xce, 2) << 16; 161aa2ea232SScott Long pciereg_cfgopen(); 162aa2ea232SScott Long } else if ((vid == 0x8086) && (did == 0x2580)) { 163aa2ea232SScott Long pciebar = pci_cfgregread(0, 0, 0, 0x48, 4); 164aa2ea232SScott Long pciereg_cfgopen(); 165aa2ea232SScott Long } 166aa2ea232SScott Long 167300451c4SMike Smith return(1); 168300451c4SMike Smith } 169300451c4SMike Smith 17012a02d6eSMike Smith /* 17112a02d6eSMike Smith * Read configuration space register 17212a02d6eSMike Smith */ 173bb0d0a8eSMike Smith u_int32_t 174bb0d0a8eSMike Smith pci_cfgregread(int bus, int slot, int func, int reg, int bytes) 175bb0d0a8eSMike Smith { 176e300f53cSWarner Losh uint32_t line; 177e300f53cSWarner Losh 178bb0d0a8eSMike Smith /* 179d5ccecfaSWarner Losh * Some BIOS writers seem to want to ignore the spec and put 180d5ccecfaSWarner Losh * 0 in the intline rather than 255 to indicate none. The rest of 181d5ccecfaSWarner Losh * the code uses 255 as an invalid IRQ. 182d5ccecfaSWarner Losh */ 183d5ccecfaSWarner Losh if (reg == PCIR_INTLINE && bytes == 1) { 184af3d516fSPeter Wemm line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1); 1856f92bdd0SJohn Baldwin return (pci_i386_map_intline(line)); 186d5ccecfaSWarner Losh } 187af3d516fSPeter Wemm return (pcireg_cfgread(bus, slot, func, reg, bytes)); 188bb0d0a8eSMike Smith } 189bb0d0a8eSMike Smith 19012a02d6eSMike Smith /* 19112a02d6eSMike Smith * Write configuration space register 19212a02d6eSMike Smith */ 19312a02d6eSMike Smith void 19412a02d6eSMike Smith pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes) 19512a02d6eSMike Smith { 196af3d516fSPeter Wemm 197cb8e4332SPoul-Henning Kamp pcireg_cfgwrite(bus, slot, func, reg, data, bytes); 19812a02d6eSMike Smith } 19912a02d6eSMike Smith 20012a02d6eSMike Smith /* 20112a02d6eSMike Smith * Configuration space access using direct register operations 20212a02d6eSMike Smith */ 203ac19f918SStefan Eßer 2045bec6157SStefan Eßer /* enable configuration space accesses and return data port address */ 205a3adc4f8SStefan Eßer static int 2065bec6157SStefan Eßer pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) 2075bec6157SStefan Eßer { 2085bec6157SStefan Eßer int dataport = 0; 2095bec6157SStefan Eßer 2105bec6157SStefan Eßer if (bus <= PCI_BUSMAX 2115bec6157SStefan Eßer && slot < devmax 2125bec6157SStefan Eßer && func <= PCI_FUNCMAX 2135bec6157SStefan Eßer && reg <= PCI_REGMAX 2145bec6157SStefan Eßer && bytes != 3 2155bec6157SStefan Eßer && (unsigned) bytes <= 4 2165bec6157SStefan Eßer && (reg & (bytes - 1)) == 0) { 2175bec6157SStefan Eßer switch (cfgmech) { 218aa2ea232SScott Long case CFGMECH_1: 219b3daa02eSStefan Eßer outl(CONF1_ADDR_PORT, (1 << 31) 220b3daa02eSStefan Eßer | (bus << 16) | (slot << 11) 221b3daa02eSStefan Eßer | (func << 8) | (reg & ~0x03)); 222b3daa02eSStefan Eßer dataport = CONF1_DATA_PORT + (reg & 0x03); 2235bec6157SStefan Eßer break; 224aa2ea232SScott Long case CFGMECH_2: 2255bec6157SStefan Eßer outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); 2265bec6157SStefan Eßer outb(CONF2_FORWARD_PORT, bus); 2275bec6157SStefan Eßer dataport = 0xc000 | (slot << 8) | reg; 2285bec6157SStefan Eßer break; 2295bec6157SStefan Eßer } 2305bec6157SStefan Eßer } 2315bec6157SStefan Eßer return (dataport); 2325bec6157SStefan Eßer } 2335bec6157SStefan Eßer 2345bec6157SStefan Eßer /* disable configuration space accesses */ 2355bec6157SStefan Eßer static void 2365bec6157SStefan Eßer pci_cfgdisable(void) 2375bec6157SStefan Eßer { 2385bec6157SStefan Eßer switch (cfgmech) { 239aa2ea232SScott Long case CFGMECH_1: 2405bec6157SStefan Eßer outl(CONF1_ADDR_PORT, 0); 2415bec6157SStefan Eßer break; 242aa2ea232SScott Long case CFGMECH_2: 2435bec6157SStefan Eßer outb(CONF2_ENABLE_PORT, 0); 2445bec6157SStefan Eßer outb(CONF2_FORWARD_PORT, 0); 2455bec6157SStefan Eßer break; 2465bec6157SStefan Eßer } 2475bec6157SStefan Eßer } 2485bec6157SStefan Eßer 249300451c4SMike Smith static int 25021c3015aSDoug Rabson pcireg_cfgread(int bus, int slot, int func, int reg, int bytes) 2515bec6157SStefan Eßer { 2525bec6157SStefan Eßer int data = -1; 2535bec6157SStefan Eßer int port; 2545bec6157SStefan Eßer 255aa2ea232SScott Long if (cfgmech == CFGMECH_PCIE) { 256aa2ea232SScott Long data = pciereg_cfgread(bus, slot, func, reg, bytes); 257aa2ea232SScott Long return (data); 258aa2ea232SScott Long } 259aa2ea232SScott Long 260af3d516fSPeter Wemm mtx_lock_spin(&pcicfg_mtx); 26121c3015aSDoug Rabson port = pci_cfgenable(bus, slot, func, reg, bytes); 2625bec6157SStefan Eßer if (port != 0) { 2635bec6157SStefan Eßer switch (bytes) { 2645bec6157SStefan Eßer case 1: 2655bec6157SStefan Eßer data = inb(port); 2665bec6157SStefan Eßer break; 2675bec6157SStefan Eßer case 2: 2685bec6157SStefan Eßer data = inw(port); 2695bec6157SStefan Eßer break; 2705bec6157SStefan Eßer case 4: 2715bec6157SStefan Eßer data = inl(port); 2725bec6157SStefan Eßer break; 2735bec6157SStefan Eßer } 2745bec6157SStefan Eßer pci_cfgdisable(); 2755bec6157SStefan Eßer } 276af3d516fSPeter Wemm mtx_unlock_spin(&pcicfg_mtx); 2775bec6157SStefan Eßer return (data); 2785bec6157SStefan Eßer } 2795bec6157SStefan Eßer 280300451c4SMike Smith static void 28121c3015aSDoug Rabson pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 2825bec6157SStefan Eßer { 2835bec6157SStefan Eßer int port; 2845bec6157SStefan Eßer 285aa2ea232SScott Long if (cfgmech == CFGMECH_PCIE) { 286aa2ea232SScott Long pciereg_cfgwrite(bus, slot, func, reg, data, bytes); 287aa2ea232SScott Long return; 288aa2ea232SScott Long } 289aa2ea232SScott Long 290af3d516fSPeter Wemm mtx_lock_spin(&pcicfg_mtx); 29121c3015aSDoug Rabson port = pci_cfgenable(bus, slot, func, reg, bytes); 2925bec6157SStefan Eßer if (port != 0) { 2935bec6157SStefan Eßer switch (bytes) { 2945bec6157SStefan Eßer case 1: 2955bec6157SStefan Eßer outb(port, data); 2965bec6157SStefan Eßer break; 2975bec6157SStefan Eßer case 2: 2985bec6157SStefan Eßer outw(port, data); 2995bec6157SStefan Eßer break; 3005bec6157SStefan Eßer case 4: 3015bec6157SStefan Eßer outl(port, data); 3025bec6157SStefan Eßer break; 3035bec6157SStefan Eßer } 3045bec6157SStefan Eßer pci_cfgdisable(); 3055bec6157SStefan Eßer } 306af3d516fSPeter Wemm mtx_unlock_spin(&pcicfg_mtx); 3075bec6157SStefan Eßer } 3085bec6157SStefan Eßer 30912a02d6eSMike Smith /* check whether the configuration mechanism has been correctly identified */ 3105bec6157SStefan Eßer static int 3115bec6157SStefan Eßer pci_cfgcheck(int maxdev) 312a3adc4f8SStefan Eßer { 313984de797SWarner Losh uint32_t id, class; 314984de797SWarner Losh uint8_t header; 315984de797SWarner Losh uint8_t device; 316af3d516fSPeter Wemm int port; 317a3adc4f8SStefan Eßer 3185bec6157SStefan Eßer if (bootverbose) 3195bec6157SStefan Eßer printf("pci_cfgcheck:\tdevice "); 32077b57314SStefan Eßer 3215bec6157SStefan Eßer for (device = 0; device < maxdev; device++) { 322c7483249SStefan Eßer if (bootverbose) 323c7483249SStefan Eßer printf("%d ", device); 3245bec6157SStefan Eßer 325af3d516fSPeter Wemm port = pci_cfgenable(0, device, 0, 0, 4); 326af3d516fSPeter Wemm id = inl(port); 327984de797SWarner Losh if (id == 0 || id == 0xffffffff) 32881cf5d7aSStefan Eßer continue; 32981cf5d7aSStefan Eßer 330af3d516fSPeter Wemm port = pci_cfgenable(0, device, 0, 8, 4); 331af3d516fSPeter Wemm class = inl(port) >> 8; 33281cf5d7aSStefan Eßer if (bootverbose) 3335bec6157SStefan Eßer printf("[class=%06x] ", class); 3348277ac25SStefan Eßer if (class == 0 || (class & 0xf870ff) != 0) 33581cf5d7aSStefan Eßer continue; 33681cf5d7aSStefan Eßer 337af3d516fSPeter Wemm port = pci_cfgenable(0, device, 0, 14, 1); 338af3d516fSPeter Wemm header = inb(port); 33981cf5d7aSStefan Eßer if (bootverbose) 3405bec6157SStefan Eßer printf("[hdr=%02x] ", header); 3415bec6157SStefan Eßer if ((header & 0x7e) != 0) 34281cf5d7aSStefan Eßer continue; 34381cf5d7aSStefan Eßer 3445bec6157SStefan Eßer if (bootverbose) 3455bec6157SStefan Eßer printf("is there (id=%08x)\n", id); 3465bec6157SStefan Eßer 3475bec6157SStefan Eßer pci_cfgdisable(); 3485bec6157SStefan Eßer return (1); 349a3adc4f8SStefan Eßer } 350c7483249SStefan Eßer if (bootverbose) 351c7483249SStefan Eßer printf("-- nothing found\n"); 3525bec6157SStefan Eßer 3535bec6157SStefan Eßer pci_cfgdisable(); 3545bec6157SStefan Eßer return (0); 355a3adc4f8SStefan Eßer } 356d7ea35fcSStefan Eßer 3578dc26439SPeter Wemm static int 358300451c4SMike Smith pcireg_cfgopen(void) 359ac19f918SStefan Eßer { 360984de797SWarner Losh uint32_t mode1res, oldval1; 361984de797SWarner Losh uint8_t mode2res, oldval2; 3620847c06dSStefan Eßer 363287911bdSStefan Eßer oldval1 = inl(CONF1_ADDR_PORT); 364a3adc4f8SStefan Eßer 36577b57314SStefan Eßer if (bootverbose) { 366984de797SWarner Losh printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n", 3675bec6157SStefan Eßer oldval1); 368a3adc4f8SStefan Eßer } 369a3adc4f8SStefan Eßer 3700e2f699bSStefan Eßer if ((oldval1 & CONF1_ENABLE_MSK) == 0) { 371287911bdSStefan Eßer 372aa2ea232SScott Long cfgmech = CFGMECH_1; 3735bec6157SStefan Eßer devmax = 32; 37477b57314SStefan Eßer 37577b57314SStefan Eßer outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 37621e25fa6SJohn Baldwin DELAY(1); 37777b57314SStefan Eßer mode1res = inl(CONF1_ADDR_PORT); 378287911bdSStefan Eßer outl(CONF1_ADDR_PORT, oldval1); 37977b57314SStefan Eßer 38077b57314SStefan Eßer if (bootverbose) 381984de797SWarner Losh printf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n", 38277b57314SStefan Eßer mode1res, CONF1_ENABLE_CHK); 38377b57314SStefan Eßer 38477b57314SStefan Eßer if (mode1res) { 3855bec6157SStefan Eßer if (pci_cfgcheck(32)) 3865bec6157SStefan Eßer return (cfgmech); 3875bec6157SStefan Eßer } 38877b57314SStefan Eßer 38977b57314SStefan Eßer outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); 39077b57314SStefan Eßer mode1res = inl(CONF1_ADDR_PORT); 391287911bdSStefan Eßer outl(CONF1_ADDR_PORT, oldval1); 39277b57314SStefan Eßer 39377b57314SStefan Eßer if (bootverbose) 394984de797SWarner Losh printf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n", 39577b57314SStefan Eßer mode1res, CONF1_ENABLE_CHK1); 39677b57314SStefan Eßer 397c7483249SStefan Eßer if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { 3985bec6157SStefan Eßer if (pci_cfgcheck(32)) 3995bec6157SStefan Eßer return (cfgmech); 400287911bdSStefan Eßer } 4015bec6157SStefan Eßer } 40277b57314SStefan Eßer 403287911bdSStefan Eßer oldval2 = inb(CONF2_ENABLE_PORT); 404287911bdSStefan Eßer 405287911bdSStefan Eßer if (bootverbose) { 4065bec6157SStefan Eßer printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", 4075bec6157SStefan Eßer oldval2); 408287911bdSStefan Eßer } 409287911bdSStefan Eßer 410287911bdSStefan Eßer if ((oldval2 & 0xf0) == 0) { 411c7483249SStefan Eßer 412aa2ea232SScott Long cfgmech = CFGMECH_2; 4135bec6157SStefan Eßer devmax = 16; 41477b57314SStefan Eßer 415287911bdSStefan Eßer outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); 416287911bdSStefan Eßer mode2res = inb(CONF2_ENABLE_PORT); 417287911bdSStefan Eßer outb(CONF2_ENABLE_PORT, oldval2); 418287911bdSStefan Eßer 419287911bdSStefan Eßer if (bootverbose) 4205bec6157SStefan Eßer printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", 421287911bdSStefan Eßer mode2res, CONF2_ENABLE_CHK); 422287911bdSStefan Eßer 423287911bdSStefan Eßer if (mode2res == CONF2_ENABLE_RES) { 424287911bdSStefan Eßer if (bootverbose) 4255bec6157SStefan Eßer printf("pci_open(2a):\tnow trying mechanism 2\n"); 426287911bdSStefan Eßer 4275bec6157SStefan Eßer if (pci_cfgcheck(16)) 4285bec6157SStefan Eßer return (cfgmech); 429287911bdSStefan Eßer } 430287911bdSStefan Eßer } 43177b57314SStefan Eßer 432aa2ea232SScott Long cfgmech = CFGMECH_NONE; 4335bec6157SStefan Eßer devmax = 0; 4345bec6157SStefan Eßer return (cfgmech); 435ac19f918SStefan Eßer } 4368dc26439SPeter Wemm 437aa2ea232SScott Long static int 438aa2ea232SScott Long pciereg_cfgopen(void) 439aa2ea232SScott Long { 440aa2ea232SScott Long struct pcie_cfg_list *pcielist; 441aa2ea232SScott Long struct pcie_cfg_elem *pcie_array, *elem; 442aa2ea232SScott Long #ifdef SMP 443aa2ea232SScott Long struct pcpu *pc; 444aa2ea232SScott Long #endif 445aa2ea232SScott Long vm_offset_t va; 446aa2ea232SScott Long int i; 447aa2ea232SScott Long 448aa2ea232SScott Long if (bootverbose) 449aa2ea232SScott Long printf("Setting up PCIe mappings for BAR 0x%x\n", pciebar); 450aa2ea232SScott Long 451aa2ea232SScott Long #ifdef SMP 452aa2ea232SScott Long SLIST_FOREACH(pc, &cpuhead, pc_allcpu) 453aa2ea232SScott Long #endif 454aa2ea232SScott Long { 455aa2ea232SScott Long 456aa2ea232SScott Long pcie_array = malloc(sizeof(struct pcie_cfg_elem) * PCIE_CACHE, 457aa2ea232SScott Long M_DEVBUF, M_NOWAIT); 458aa2ea232SScott Long if (pcie_array == NULL) 459aa2ea232SScott Long return (0); 460aa2ea232SScott Long 461aa2ea232SScott Long va = kmem_alloc_nofault(kernel_map, PCIE_CACHE * PAGE_SIZE); 462aa2ea232SScott Long if (va == 0) { 463aa2ea232SScott Long free(pcie_array, M_DEVBUF); 464aa2ea232SScott Long return (0); 465aa2ea232SScott Long } 466aa2ea232SScott Long 467aa2ea232SScott Long #ifdef SMP 468aa2ea232SScott Long pcielist = &pcie_list[pc->pc_cpuid]; 469aa2ea232SScott Long #else 470aa2ea232SScott Long pcielist = &pcie_list[0]; 471aa2ea232SScott Long #endif 472aa2ea232SScott Long TAILQ_INIT(pcielist); 473aa2ea232SScott Long for (i = 0; i < PCIE_CACHE; i++) { 474aa2ea232SScott Long elem = &pcie_array[i]; 475aa2ea232SScott Long elem->vapage = va + (i * PAGE_SIZE); 476aa2ea232SScott Long elem->papage = 0; 477aa2ea232SScott Long TAILQ_INSERT_HEAD(pcielist, elem, elem); 478aa2ea232SScott Long } 479aa2ea232SScott Long } 480aa2ea232SScott Long 481aa2ea232SScott Long 482aa2ea232SScott Long cfgmech = CFGMECH_PCIE; 483aa2ea232SScott Long devmax = 32; 484aa2ea232SScott Long return (1); 485aa2ea232SScott Long } 486aa2ea232SScott Long 487aa2ea232SScott Long #define PCIE_PADDR(bar, reg, bus, slot, func) \ 488aa2ea232SScott Long ((bar) | \ 489aa2ea232SScott Long (((bus) & 0xff) << 20) | \ 490aa2ea232SScott Long (((slot) & 0x1f) << 15) | \ 491aa2ea232SScott Long (((func) & 0x7) << 12) | \ 492aa2ea232SScott Long ((reg) & 0xfff)) 493aa2ea232SScott Long 494aa2ea232SScott Long /* 495aa2ea232SScott Long * Find an element in the cache that matches the physical page desired, or 496aa2ea232SScott Long * create a new mapping from the least recently used element. 497aa2ea232SScott Long * A very simple LRU algorithm is used here, does it need to be more 498aa2ea232SScott Long * efficient? 499aa2ea232SScott Long */ 500aa2ea232SScott Long static __inline struct pcie_cfg_elem * 501aa2ea232SScott Long pciereg_findelem(vm_paddr_t papage) 502aa2ea232SScott Long { 503aa2ea232SScott Long struct pcie_cfg_list *pcielist; 504aa2ea232SScott Long struct pcie_cfg_elem *elem; 505aa2ea232SScott Long 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; 536245e410bSScott Long int data; 537aa2ea232SScott Long 538245e410bSScott Long critical_enter(); 539aa2ea232SScott Long pa = PCIE_PADDR(pciebar, reg, bus, slot, func); 540aa2ea232SScott Long papage = pa & ~PAGE_MASK; 541aa2ea232SScott Long elem = pciereg_findelem(papage); 542aa2ea232SScott Long va = elem->vapage | (pa & PAGE_MASK); 543aa2ea232SScott Long 544aa2ea232SScott Long switch (bytes) { 545aa2ea232SScott Long case 4: 546245e410bSScott Long data = *(volatile uint32_t *)(va); 547245e410bSScott Long break; 548aa2ea232SScott Long case 2: 549245e410bSScott Long data = *(volatile uint16_t *)(va); 550245e410bSScott Long break; 551aa2ea232SScott Long case 1: 552245e410bSScott Long data = *(volatile uint8_t *)(va); 553245e410bSScott Long break; 554aa2ea232SScott Long default: 555aa2ea232SScott Long panic("pciereg_cfgread: invalid width"); 556aa2ea232SScott Long } 557245e410bSScott Long 558245e410bSScott Long critical_exit(); 559245e410bSScott Long return (data); 560aa2ea232SScott Long } 561aa2ea232SScott Long 562aa2ea232SScott Long static void 563aa2ea232SScott Long pciereg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 564aa2ea232SScott Long { 565aa2ea232SScott Long struct pcie_cfg_elem *elem; 566aa2ea232SScott Long volatile vm_offset_t va; 567aa2ea232SScott Long vm_paddr_t pa, papage; 568aa2ea232SScott Long 569245e410bSScott Long critical_enter(); 570aa2ea232SScott Long pa = PCIE_PADDR(pciebar, reg, bus, slot, func); 571aa2ea232SScott Long papage = pa & ~PAGE_MASK; 572aa2ea232SScott Long elem = pciereg_findelem(papage); 573aa2ea232SScott Long va = elem->vapage | (pa & PAGE_MASK); 574aa2ea232SScott Long 575aa2ea232SScott Long switch (bytes) { 576aa2ea232SScott Long case 4: 577aa2ea232SScott Long *(volatile uint32_t *)(va) = data; 578aa2ea232SScott Long break; 579aa2ea232SScott Long case 2: 580aa2ea232SScott Long *(volatile uint16_t *)(va) = data; 581aa2ea232SScott Long break; 582aa2ea232SScott Long case 1: 583aa2ea232SScott Long *(volatile uint8_t *)(va) = data; 584aa2ea232SScott Long break; 585aa2ea232SScott Long default: 586aa2ea232SScott Long panic("pciereg_cfgwrite: invalid width"); 587aa2ea232SScott Long } 588245e410bSScott Long 589245e410bSScott Long critical_exit(); 590aa2ea232SScott Long } 591