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 outl(CONF1_ADDR_PORT, 0); 143 break; 144 case 2: 145 outb(CONF2_ENABLE_PORT, 0); 146 outb(CONF2_FORWARD_PORT, 0); 147 break; 148 } 149 } 150 151 static int 152 pcireg_cfgread(int bus, int slot, int func, int reg, int bytes) 153 { 154 int data = -1; 155 int port; 156 157 mtx_lock_spin(&pcicfg_mtx); 158 port = pci_cfgenable(bus, slot, func, reg, bytes); 159 if (port != 0) { 160 switch (bytes) { 161 case 1: 162 data = inb(port); 163 break; 164 case 2: 165 data = inw(port); 166 break; 167 case 4: 168 data = inl(port); 169 break; 170 } 171 pci_cfgdisable(); 172 } 173 mtx_unlock_spin(&pcicfg_mtx); 174 return (data); 175 } 176 177 static void 178 pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 179 { 180 int port; 181 182 mtx_lock_spin(&pcicfg_mtx); 183 port = pci_cfgenable(bus, slot, func, reg, bytes); 184 if (port != 0) { 185 switch (bytes) { 186 case 1: 187 outb(port, data); 188 break; 189 case 2: 190 outw(port, data); 191 break; 192 case 4: 193 outl(port, data); 194 break; 195 } 196 pci_cfgdisable(); 197 } 198 mtx_unlock_spin(&pcicfg_mtx); 199 } 200 201 /* check whether the configuration mechanism has been correctly identified */ 202 static int 203 pci_cfgcheck(int maxdev) 204 { 205 uint32_t id, class; 206 uint8_t header; 207 uint8_t device; 208 int port; 209 210 if (bootverbose) 211 printf("pci_cfgcheck:\tdevice "); 212 213 for (device = 0; device < maxdev; device++) { 214 if (bootverbose) 215 printf("%d ", device); 216 217 port = pci_cfgenable(0, device, 0, 0, 4); 218 id = inl(port); 219 if (id == 0 || id == 0xffffffff) 220 continue; 221 222 port = pci_cfgenable(0, device, 0, 8, 4); 223 class = inl(port) >> 8; 224 if (bootverbose) 225 printf("[class=%06x] ", class); 226 if (class == 0 || (class & 0xf870ff) != 0) 227 continue; 228 229 port = pci_cfgenable(0, device, 0, 14, 1); 230 header = inb(port); 231 if (bootverbose) 232 printf("[hdr=%02x] ", header); 233 if ((header & 0x7e) != 0) 234 continue; 235 236 if (bootverbose) 237 printf("is there (id=%08x)\n", id); 238 239 pci_cfgdisable(); 240 return (1); 241 } 242 if (bootverbose) 243 printf("-- nothing found\n"); 244 245 pci_cfgdisable(); 246 return (0); 247 } 248 249 static int 250 pcireg_cfgopen(void) 251 { 252 uint32_t mode1res, oldval1; 253 uint8_t mode2res, oldval2; 254 255 oldval1 = inl(CONF1_ADDR_PORT); 256 257 if (bootverbose) { 258 printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n", 259 oldval1); 260 } 261 262 if ((oldval1 & CONF1_ENABLE_MSK) == 0) { 263 264 cfgmech = 1; 265 devmax = 32; 266 267 outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 268 DELAY(1); 269 mode1res = inl(CONF1_ADDR_PORT); 270 outl(CONF1_ADDR_PORT, oldval1); 271 272 if (bootverbose) 273 printf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n", 274 mode1res, CONF1_ENABLE_CHK); 275 276 if (mode1res) { 277 if (pci_cfgcheck(32)) 278 return (cfgmech); 279 } 280 281 outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); 282 mode1res = inl(CONF1_ADDR_PORT); 283 outl(CONF1_ADDR_PORT, oldval1); 284 285 if (bootverbose) 286 printf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n", 287 mode1res, CONF1_ENABLE_CHK1); 288 289 if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { 290 if (pci_cfgcheck(32)) 291 return (cfgmech); 292 } 293 } 294 295 oldval2 = inb(CONF2_ENABLE_PORT); 296 297 if (bootverbose) { 298 printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", 299 oldval2); 300 } 301 302 if ((oldval2 & 0xf0) == 0) { 303 304 cfgmech = 2; 305 devmax = 16; 306 307 outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); 308 mode2res = inb(CONF2_ENABLE_PORT); 309 outb(CONF2_ENABLE_PORT, oldval2); 310 311 if (bootverbose) 312 printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", 313 mode2res, CONF2_ENABLE_CHK); 314 315 if (mode2res == CONF2_ENABLE_RES) { 316 if (bootverbose) 317 printf("pci_open(2a):\tnow trying mechanism 2\n"); 318 319 if (pci_cfgcheck(16)) 320 return (cfgmech); 321 } 322 } 323 324 cfgmech = 0; 325 devmax = 0; 326 return (cfgmech); 327 } 328