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> 37e300f53cSWarner Losh #include <dev/pci/pcivar.h> 38e300f53cSWarner Losh #include <dev/pci/pcireg.h> 3912a02d6eSMike Smith #include <machine/pci_cfgreg.h> 40300451c4SMike Smith #include <machine/pc/bios.h> 41300451c4SMike Smith 428ff25e97SJohn Baldwin #define PRVERB(a) do { \ 438ff25e97SJohn Baldwin if (bootverbose) \ 448ff25e97SJohn Baldwin printf a ; \ 458ff25e97SJohn Baldwin } while(0) 46d626906bSWarner Losh 475bec6157SStefan Eßer static int cfgmech; 485bec6157SStefan Eßer static int devmax; 49300451c4SMike Smith 5012a02d6eSMike Smith static int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes); 5112a02d6eSMike Smith static void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes); 52300451c4SMike Smith static int pcireg_cfgopen(void); 53300451c4SMike Smith 54af3d516fSPeter Wemm static struct mtx pcicfg_mtx; 55af3d516fSPeter Wemm 568ce1ab3aSWarner Losh /* 578ce1ab3aSWarner Losh * Some BIOS writers seem to want to ignore the spec and put 588ce1ab3aSWarner Losh * 0 in the intline rather than 255 to indicate none. Some use 598ce1ab3aSWarner Losh * numbers in the range 128-254 to indicate something strange and 608ce1ab3aSWarner Losh * apparently undocumented anywhere. Assume these are completely bogus 618ce1ab3aSWarner Losh * and map them to 255, which means "none". 628ce1ab3aSWarner Losh */ 635908d366SStefan Farfeleder static __inline int 648ce1ab3aSWarner Losh pci_i386_map_intline(int line) 658ce1ab3aSWarner Losh { 668ce1ab3aSWarner Losh if (line == 0 || line >= 128) 67e300f53cSWarner Losh return (PCI_INVALID_IRQ); 688ce1ab3aSWarner Losh return (line); 698ce1ab3aSWarner Losh } 708ce1ab3aSWarner Losh 71d626906bSWarner Losh static u_int16_t 72d626906bSWarner Losh pcibios_get_version(void) 73d626906bSWarner Losh { 74d626906bSWarner Losh struct bios_regs args; 75d626906bSWarner Losh 765264a94fSJohn Baldwin if (PCIbios.ventry == 0) { 77d626906bSWarner Losh PRVERB(("pcibios: No call entry point\n")); 78d626906bSWarner Losh return (0); 79d626906bSWarner Losh } 80d626906bSWarner Losh args.eax = PCIBIOS_BIOS_PRESENT; 81d626906bSWarner Losh if (bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL))) { 82d626906bSWarner Losh PRVERB(("pcibios: BIOS_PRESENT call failed\n")); 83d626906bSWarner Losh return (0); 84d626906bSWarner Losh } 85d626906bSWarner Losh if (args.edx != 0x20494350) { 86d626906bSWarner Losh PRVERB(("pcibios: BIOS_PRESENT didn't return 'PCI ' in edx\n")); 87d626906bSWarner Losh return (0); 88d626906bSWarner Losh } 89d626906bSWarner Losh return (args.ebx & 0xffff); 90d626906bSWarner Losh } 91d626906bSWarner Losh 9212a02d6eSMike Smith /* 9312a02d6eSMike Smith * Initialise access to PCI configuration space 9412a02d6eSMike Smith */ 9512a02d6eSMike Smith int 9612a02d6eSMike Smith pci_cfgregopen(void) 9721c3015aSDoug Rabson { 9812a02d6eSMike Smith static int opened = 0; 99af3d516fSPeter Wemm u_int16_t v; 10021c3015aSDoug Rabson 10112a02d6eSMike Smith if (opened) 10212a02d6eSMike Smith return(1); 103300451c4SMike Smith 104af3d516fSPeter Wemm if (pcireg_cfgopen() == 0) 105300451c4SMike Smith return(0); 10654c9005fSWarner Losh 107af3d516fSPeter Wemm v = pcibios_get_version(); 108af3d516fSPeter Wemm if (v > 0) 10939981fedSJohn Baldwin PRVERB(("pcibios: BIOS version %x.%02x\n", (v & 0xff00) >> 8, 11039981fedSJohn Baldwin v & 0xff)); 111af3d516fSPeter Wemm mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN); 11212a02d6eSMike Smith opened = 1; 11377fa00faSJohn Baldwin 11477fa00faSJohn Baldwin /* $PIR requires PCI BIOS 2.10 or greater. */ 11577fa00faSJohn Baldwin if (v >= 0x0210) 11677fa00faSJohn Baldwin pci_pir_open(); 117300451c4SMike Smith return(1); 118300451c4SMike Smith } 119300451c4SMike Smith 12012a02d6eSMike Smith /* 12112a02d6eSMike Smith * Read configuration space register 12212a02d6eSMike Smith */ 123bb0d0a8eSMike Smith u_int32_t 124bb0d0a8eSMike Smith pci_cfgregread(int bus, int slot, int func, int reg, int bytes) 125bb0d0a8eSMike Smith { 126e300f53cSWarner Losh uint32_t line; 127e300f53cSWarner Losh 128bb0d0a8eSMike Smith /* 129d5ccecfaSWarner Losh * Some BIOS writers seem to want to ignore the spec and put 130d5ccecfaSWarner Losh * 0 in the intline rather than 255 to indicate none. The rest of 131d5ccecfaSWarner Losh * the code uses 255 as an invalid IRQ. 132d5ccecfaSWarner Losh */ 133d5ccecfaSWarner Losh if (reg == PCIR_INTLINE && bytes == 1) { 134af3d516fSPeter Wemm line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1); 1356f92bdd0SJohn Baldwin return (pci_i386_map_intline(line)); 136d5ccecfaSWarner Losh } 137af3d516fSPeter Wemm return (pcireg_cfgread(bus, slot, func, reg, bytes)); 138bb0d0a8eSMike Smith } 139bb0d0a8eSMike Smith 14012a02d6eSMike Smith /* 14112a02d6eSMike Smith * Write configuration space register 14212a02d6eSMike Smith */ 14312a02d6eSMike Smith void 14412a02d6eSMike Smith pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes) 14512a02d6eSMike Smith { 146af3d516fSPeter Wemm 147cb8e4332SPoul-Henning Kamp pcireg_cfgwrite(bus, slot, func, reg, data, bytes); 14812a02d6eSMike Smith } 14912a02d6eSMike Smith 15012a02d6eSMike Smith /* 15112a02d6eSMike Smith * Configuration space access using direct register operations 15212a02d6eSMike Smith */ 153ac19f918SStefan Eßer 1545bec6157SStefan Eßer /* enable configuration space accesses and return data port address */ 155a3adc4f8SStefan Eßer static int 1565bec6157SStefan Eßer pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) 1575bec6157SStefan Eßer { 1585bec6157SStefan Eßer int dataport = 0; 1595bec6157SStefan Eßer 1605bec6157SStefan Eßer if (bus <= PCI_BUSMAX 1615bec6157SStefan Eßer && slot < devmax 1625bec6157SStefan Eßer && func <= PCI_FUNCMAX 1635bec6157SStefan Eßer && reg <= PCI_REGMAX 1645bec6157SStefan Eßer && bytes != 3 1655bec6157SStefan Eßer && (unsigned) bytes <= 4 1665bec6157SStefan Eßer && (reg & (bytes - 1)) == 0) { 1675bec6157SStefan Eßer switch (cfgmech) { 1685bec6157SStefan Eßer case 1: 169b3daa02eSStefan Eßer outl(CONF1_ADDR_PORT, (1 << 31) 170b3daa02eSStefan Eßer | (bus << 16) | (slot << 11) 171b3daa02eSStefan Eßer | (func << 8) | (reg & ~0x03)); 172b3daa02eSStefan Eßer dataport = CONF1_DATA_PORT + (reg & 0x03); 1735bec6157SStefan Eßer break; 1745bec6157SStefan Eßer case 2: 1755bec6157SStefan Eßer outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); 1765bec6157SStefan Eßer outb(CONF2_FORWARD_PORT, bus); 1775bec6157SStefan Eßer dataport = 0xc000 | (slot << 8) | reg; 1785bec6157SStefan Eßer break; 1795bec6157SStefan Eßer } 1805bec6157SStefan Eßer } 1815bec6157SStefan Eßer return (dataport); 1825bec6157SStefan Eßer } 1835bec6157SStefan Eßer 1845bec6157SStefan Eßer /* disable configuration space accesses */ 1855bec6157SStefan Eßer static void 1865bec6157SStefan Eßer pci_cfgdisable(void) 1875bec6157SStefan Eßer { 1885bec6157SStefan Eßer switch (cfgmech) { 1895bec6157SStefan Eßer case 1: 1905bec6157SStefan Eßer outl(CONF1_ADDR_PORT, 0); 1915bec6157SStefan Eßer break; 1925bec6157SStefan Eßer case 2: 1935bec6157SStefan Eßer outb(CONF2_ENABLE_PORT, 0); 1945bec6157SStefan Eßer outb(CONF2_FORWARD_PORT, 0); 1955bec6157SStefan Eßer break; 1965bec6157SStefan Eßer } 1975bec6157SStefan Eßer } 1985bec6157SStefan Eßer 199300451c4SMike Smith static int 20021c3015aSDoug Rabson pcireg_cfgread(int bus, int slot, int func, int reg, int bytes) 2015bec6157SStefan Eßer { 2025bec6157SStefan Eßer int data = -1; 2035bec6157SStefan Eßer int port; 2045bec6157SStefan Eßer 205af3d516fSPeter Wemm mtx_lock_spin(&pcicfg_mtx); 20621c3015aSDoug Rabson port = pci_cfgenable(bus, slot, func, reg, bytes); 2075bec6157SStefan Eßer if (port != 0) { 2085bec6157SStefan Eßer switch (bytes) { 2095bec6157SStefan Eßer case 1: 2105bec6157SStefan Eßer data = inb(port); 2115bec6157SStefan Eßer break; 2125bec6157SStefan Eßer case 2: 2135bec6157SStefan Eßer data = inw(port); 2145bec6157SStefan Eßer break; 2155bec6157SStefan Eßer case 4: 2165bec6157SStefan Eßer data = inl(port); 2175bec6157SStefan Eßer break; 2185bec6157SStefan Eßer } 2195bec6157SStefan Eßer pci_cfgdisable(); 2205bec6157SStefan Eßer } 221af3d516fSPeter Wemm mtx_unlock_spin(&pcicfg_mtx); 2225bec6157SStefan Eßer return (data); 2235bec6157SStefan Eßer } 2245bec6157SStefan Eßer 225300451c4SMike Smith static void 22621c3015aSDoug Rabson pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 2275bec6157SStefan Eßer { 2285bec6157SStefan Eßer int port; 2295bec6157SStefan Eßer 230af3d516fSPeter Wemm mtx_lock_spin(&pcicfg_mtx); 23121c3015aSDoug Rabson port = pci_cfgenable(bus, slot, func, reg, bytes); 2325bec6157SStefan Eßer if (port != 0) { 2335bec6157SStefan Eßer switch (bytes) { 2345bec6157SStefan Eßer case 1: 2355bec6157SStefan Eßer outb(port, data); 2365bec6157SStefan Eßer break; 2375bec6157SStefan Eßer case 2: 2385bec6157SStefan Eßer outw(port, data); 2395bec6157SStefan Eßer break; 2405bec6157SStefan Eßer case 4: 2415bec6157SStefan Eßer outl(port, data); 2425bec6157SStefan Eßer break; 2435bec6157SStefan Eßer } 2445bec6157SStefan Eßer pci_cfgdisable(); 2455bec6157SStefan Eßer } 246af3d516fSPeter Wemm mtx_unlock_spin(&pcicfg_mtx); 2475bec6157SStefan Eßer } 2485bec6157SStefan Eßer 24912a02d6eSMike Smith /* check whether the configuration mechanism has been correctly identified */ 2505bec6157SStefan Eßer static int 2515bec6157SStefan Eßer pci_cfgcheck(int maxdev) 252a3adc4f8SStefan Eßer { 253984de797SWarner Losh uint32_t id, class; 254984de797SWarner Losh uint8_t header; 255984de797SWarner Losh uint8_t device; 256af3d516fSPeter Wemm int port; 257a3adc4f8SStefan Eßer 2585bec6157SStefan Eßer if (bootverbose) 2595bec6157SStefan Eßer printf("pci_cfgcheck:\tdevice "); 26077b57314SStefan Eßer 2615bec6157SStefan Eßer for (device = 0; device < maxdev; device++) { 262c7483249SStefan Eßer if (bootverbose) 263c7483249SStefan Eßer printf("%d ", device); 2645bec6157SStefan Eßer 265af3d516fSPeter Wemm port = pci_cfgenable(0, device, 0, 0, 4); 266af3d516fSPeter Wemm id = inl(port); 267984de797SWarner Losh if (id == 0 || id == 0xffffffff) 26881cf5d7aSStefan Eßer continue; 26981cf5d7aSStefan Eßer 270af3d516fSPeter Wemm port = pci_cfgenable(0, device, 0, 8, 4); 271af3d516fSPeter Wemm class = inl(port) >> 8; 27281cf5d7aSStefan Eßer if (bootverbose) 2735bec6157SStefan Eßer printf("[class=%06x] ", class); 2748277ac25SStefan Eßer if (class == 0 || (class & 0xf870ff) != 0) 27581cf5d7aSStefan Eßer continue; 27681cf5d7aSStefan Eßer 277af3d516fSPeter Wemm port = pci_cfgenable(0, device, 0, 14, 1); 278af3d516fSPeter Wemm header = inb(port); 27981cf5d7aSStefan Eßer if (bootverbose) 2805bec6157SStefan Eßer printf("[hdr=%02x] ", header); 2815bec6157SStefan Eßer if ((header & 0x7e) != 0) 28281cf5d7aSStefan Eßer continue; 28381cf5d7aSStefan Eßer 2845bec6157SStefan Eßer if (bootverbose) 2855bec6157SStefan Eßer printf("is there (id=%08x)\n", id); 2865bec6157SStefan Eßer 2875bec6157SStefan Eßer pci_cfgdisable(); 2885bec6157SStefan Eßer return (1); 289a3adc4f8SStefan Eßer } 290c7483249SStefan Eßer if (bootverbose) 291c7483249SStefan Eßer printf("-- nothing found\n"); 2925bec6157SStefan Eßer 2935bec6157SStefan Eßer pci_cfgdisable(); 2945bec6157SStefan Eßer return (0); 295a3adc4f8SStefan Eßer } 296d7ea35fcSStefan Eßer 2978dc26439SPeter Wemm static int 298300451c4SMike Smith pcireg_cfgopen(void) 299ac19f918SStefan Eßer { 300984de797SWarner Losh uint32_t mode1res, oldval1; 301984de797SWarner Losh uint8_t mode2res, oldval2; 3020847c06dSStefan Eßer 303287911bdSStefan Eßer oldval1 = inl(CONF1_ADDR_PORT); 304a3adc4f8SStefan Eßer 30577b57314SStefan Eßer if (bootverbose) { 306984de797SWarner Losh printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n", 3075bec6157SStefan Eßer oldval1); 308a3adc4f8SStefan Eßer } 309a3adc4f8SStefan Eßer 3100e2f699bSStefan Eßer if ((oldval1 & CONF1_ENABLE_MSK) == 0) { 311287911bdSStefan Eßer 3125bec6157SStefan Eßer cfgmech = 1; 3135bec6157SStefan Eßer devmax = 32; 31477b57314SStefan Eßer 31577b57314SStefan Eßer outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 31621e25fa6SJohn Baldwin DELAY(1); 31777b57314SStefan Eßer mode1res = inl(CONF1_ADDR_PORT); 318287911bdSStefan Eßer outl(CONF1_ADDR_PORT, oldval1); 31977b57314SStefan Eßer 32077b57314SStefan Eßer if (bootverbose) 321984de797SWarner Losh printf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n", 32277b57314SStefan Eßer mode1res, CONF1_ENABLE_CHK); 32377b57314SStefan Eßer 32477b57314SStefan Eßer if (mode1res) { 3255bec6157SStefan Eßer if (pci_cfgcheck(32)) 3265bec6157SStefan Eßer return (cfgmech); 3275bec6157SStefan Eßer } 32877b57314SStefan Eßer 32977b57314SStefan Eßer outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); 33077b57314SStefan Eßer mode1res = inl(CONF1_ADDR_PORT); 331287911bdSStefan Eßer outl(CONF1_ADDR_PORT, oldval1); 33277b57314SStefan Eßer 33377b57314SStefan Eßer if (bootverbose) 334984de797SWarner Losh printf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n", 33577b57314SStefan Eßer mode1res, CONF1_ENABLE_CHK1); 33677b57314SStefan Eßer 337c7483249SStefan Eßer if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { 3385bec6157SStefan Eßer if (pci_cfgcheck(32)) 3395bec6157SStefan Eßer return (cfgmech); 340287911bdSStefan Eßer } 3415bec6157SStefan Eßer } 34277b57314SStefan Eßer 343287911bdSStefan Eßer oldval2 = inb(CONF2_ENABLE_PORT); 344287911bdSStefan Eßer 345287911bdSStefan Eßer if (bootverbose) { 3465bec6157SStefan Eßer printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", 3475bec6157SStefan Eßer oldval2); 348287911bdSStefan Eßer } 349287911bdSStefan Eßer 350287911bdSStefan Eßer if ((oldval2 & 0xf0) == 0) { 351c7483249SStefan Eßer 3525bec6157SStefan Eßer cfgmech = 2; 3535bec6157SStefan Eßer devmax = 16; 35477b57314SStefan Eßer 355287911bdSStefan Eßer outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); 356287911bdSStefan Eßer mode2res = inb(CONF2_ENABLE_PORT); 357287911bdSStefan Eßer outb(CONF2_ENABLE_PORT, oldval2); 358287911bdSStefan Eßer 359287911bdSStefan Eßer if (bootverbose) 3605bec6157SStefan Eßer printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", 361287911bdSStefan Eßer mode2res, CONF2_ENABLE_CHK); 362287911bdSStefan Eßer 363287911bdSStefan Eßer if (mode2res == CONF2_ENABLE_RES) { 364287911bdSStefan Eßer if (bootverbose) 3655bec6157SStefan Eßer printf("pci_open(2a):\tnow trying mechanism 2\n"); 366287911bdSStefan Eßer 3675bec6157SStefan Eßer if (pci_cfgcheck(16)) 3685bec6157SStefan Eßer return (cfgmech); 369287911bdSStefan Eßer } 370287911bdSStefan Eßer } 37177b57314SStefan Eßer 3725bec6157SStefan Eßer cfgmech = 0; 3735bec6157SStefan Eßer devmax = 0; 3745bec6157SStefan Eßer return (cfgmech); 375ac19f918SStefan Eßer } 3768dc26439SPeter Wemm 377