1 /*- 2 * Copyright (c) 1997, Stefan Esser <se@freebsd.org> 3 * Copyright (c) 2000, Michael Smith <msmith@freebsd.org> 4 * Copyright (c) 2000, BSDi 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice unmodified, this list of conditions, and the following 12 * disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/bus.h> 35 #include <sys/lock.h> 36 #include <sys/mutex.h> 37 #include <dev/pci/pcivar.h> 38 #include <dev/pci/pcireg.h> 39 #include <machine/pci_cfgreg.h> 40 41 static int cfgmech; 42 static int devmax; 43 44 static int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes); 45 static void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes); 46 static int pcireg_cfgopen(void); 47 48 static struct mtx pcicfg_mtx; 49 50 /* 51 * Initialise access to PCI configuration space 52 */ 53 int 54 pci_cfgregopen(void) 55 { 56 static int opened = 0; 57 58 if (opened) 59 return (1); 60 if (pcireg_cfgopen() == 0) 61 return (0); 62 mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN); 63 opened = 1; 64 return (1); 65 } 66 67 /* 68 * Read configuration space register 69 */ 70 u_int32_t 71 pci_cfgregread(int bus, int slot, int func, int reg, int bytes) 72 { 73 uint32_t line; 74 75 /* 76 * Some BIOS writers seem to want to ignore the spec and put 77 * 0 in the intline rather than 255 to indicate none. Some use 78 * numbers in the range 128-254 to indicate something strange and 79 * apparently undocumented anywhere. Assume these are completely bogus 80 * and map them to 255, which the rest of the PCI code recognizes as 81 * as an invalid IRQ. 82 */ 83 if (reg == PCIR_INTLINE && bytes == 1) { 84 line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1); 85 if (line == 0 || line >= 128) 86 line = PCI_INVALID_IRQ; 87 return (line); 88 } 89 return (pcireg_cfgread(bus, slot, func, reg, bytes)); 90 } 91 92 /* 93 * Write configuration space register 94 */ 95 void 96 pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes) 97 { 98 99 pcireg_cfgwrite(bus, slot, func, reg, data, bytes); 100 } 101 102 /* 103 * Configuration space access using direct register operations 104 */ 105 106 /* enable configuration space accesses and return data port address */ 107 static int 108 pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) 109 { 110 int dataport = 0; 111 112 if (bus <= PCI_BUSMAX 113 && slot < devmax 114 && func <= PCI_FUNCMAX 115 && reg <= PCI_REGMAX 116 && bytes != 3 117 && (unsigned) bytes <= 4 118 && (reg & (bytes - 1)) == 0) { 119 switch (cfgmech) { 120 case 1: 121 outl(CONF1_ADDR_PORT, (1 << 31) 122 | (bus << 16) | (slot << 11) 123 | (func << 8) | (reg & ~0x03)); 124 dataport = CONF1_DATA_PORT + (reg & 0x03); 125 break; 126 case 2: 127 outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); 128 outb(CONF2_FORWARD_PORT, bus); 129 dataport = 0xc000 | (slot << 8) | reg; 130 break; 131 } 132 } 133 return (dataport); 134 } 135 136 /* disable configuration space accesses */ 137 static void 138 pci_cfgdisable(void) 139 { 140 switch (cfgmech) { 141 case 1: 142 /* 143 * Do nothing for the config mechanism 1 case. 144 * Writing a 0 to the address port can apparently 145 * confuse some bridges and cause spurious 146 * access failures. 147 */ 148 break; 149 case 2: 150 outb(CONF2_ENABLE_PORT, 0); 151 break; 152 } 153 } 154 155 static int 156 pcireg_cfgread(int bus, int slot, int func, int reg, int bytes) 157 { 158 int data = -1; 159 int port; 160 161 mtx_lock_spin(&pcicfg_mtx); 162 port = pci_cfgenable(bus, slot, func, reg, bytes); 163 if (port != 0) { 164 switch (bytes) { 165 case 1: 166 data = inb(port); 167 break; 168 case 2: 169 data = inw(port); 170 break; 171 case 4: 172 data = inl(port); 173 break; 174 } 175 pci_cfgdisable(); 176 } 177 mtx_unlock_spin(&pcicfg_mtx); 178 return (data); 179 } 180 181 static void 182 pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 183 { 184 int port; 185 186 mtx_lock_spin(&pcicfg_mtx); 187 port = pci_cfgenable(bus, slot, func, reg, bytes); 188 if (port != 0) { 189 switch (bytes) { 190 case 1: 191 outb(port, data); 192 break; 193 case 2: 194 outw(port, data); 195 break; 196 case 4: 197 outl(port, data); 198 break; 199 } 200 pci_cfgdisable(); 201 } 202 mtx_unlock_spin(&pcicfg_mtx); 203 } 204 205 /* check whether the configuration mechanism has been correctly identified */ 206 static int 207 pci_cfgcheck(int maxdev) 208 { 209 uint32_t id, class; 210 uint8_t header; 211 uint8_t device; 212 int port; 213 214 if (bootverbose) 215 printf("pci_cfgcheck:\tdevice "); 216 217 for (device = 0; device < maxdev; device++) { 218 if (bootverbose) 219 printf("%d ", device); 220 221 port = pci_cfgenable(0, device, 0, 0, 4); 222 id = inl(port); 223 if (id == 0 || id == 0xffffffff) 224 continue; 225 226 port = pci_cfgenable(0, device, 0, 8, 4); 227 class = inl(port) >> 8; 228 if (bootverbose) 229 printf("[class=%06x] ", class); 230 if (class == 0 || (class & 0xf870ff) != 0) 231 continue; 232 233 port = pci_cfgenable(0, device, 0, 14, 1); 234 header = inb(port); 235 if (bootverbose) 236 printf("[hdr=%02x] ", header); 237 if ((header & 0x7e) != 0) 238 continue; 239 240 if (bootverbose) 241 printf("is there (id=%08x)\n", id); 242 243 pci_cfgdisable(); 244 return (1); 245 } 246 if (bootverbose) 247 printf("-- nothing found\n"); 248 249 pci_cfgdisable(); 250 return (0); 251 } 252 253 static int 254 pcireg_cfgopen(void) 255 { 256 uint32_t mode1res, oldval1; 257 uint8_t mode2res, oldval2; 258 259 oldval1 = inl(CONF1_ADDR_PORT); 260 261 if (bootverbose) { 262 printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n", 263 oldval1); 264 } 265 266 if ((oldval1 & CONF1_ENABLE_MSK) == 0) { 267 268 cfgmech = 1; 269 devmax = 32; 270 271 outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 272 DELAY(1); 273 mode1res = inl(CONF1_ADDR_PORT); 274 outl(CONF1_ADDR_PORT, oldval1); 275 276 if (bootverbose) 277 printf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n", 278 mode1res, CONF1_ENABLE_CHK); 279 280 if (mode1res) { 281 if (pci_cfgcheck(32)) 282 return (cfgmech); 283 } 284 285 outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); 286 mode1res = inl(CONF1_ADDR_PORT); 287 outl(CONF1_ADDR_PORT, oldval1); 288 289 if (bootverbose) 290 printf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n", 291 mode1res, CONF1_ENABLE_CHK1); 292 293 if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { 294 if (pci_cfgcheck(32)) 295 return (cfgmech); 296 } 297 } 298 299 oldval2 = inb(CONF2_ENABLE_PORT); 300 301 if (bootverbose) { 302 printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", 303 oldval2); 304 } 305 306 if ((oldval2 & 0xf0) == 0) { 307 308 cfgmech = 2; 309 devmax = 16; 310 311 outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); 312 mode2res = inb(CONF2_ENABLE_PORT); 313 outb(CONF2_ENABLE_PORT, oldval2); 314 315 if (bootverbose) 316 printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", 317 mode2res, CONF2_ENABLE_CHK); 318 319 if (mode2res == CONF2_ENABLE_RES) { 320 if (bootverbose) 321 printf("pci_open(2a):\tnow trying mechanism 2\n"); 322 323 if (pci_cfgcheck(16)) 324 return (cfgmech); 325 } 326 } 327 328 cfgmech = 0; 329 devmax = 0; 330 return (cfgmech); 331 } 332