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> /* XXX trim includes */ 33 #include <sys/systm.h> 34 #include <sys/bus.h> 35 #include <sys/kernel.h> 36 #include <sys/module.h> 37 #include <sys/malloc.h> 38 #include <sys/lock.h> 39 #include <sys/mutex.h> 40 #include <vm/vm.h> 41 #include <vm/pmap.h> 42 #include <machine/md_var.h> 43 #include <dev/pci/pcivar.h> 44 #include <dev/pci/pcireg.h> 45 #include <isa/isavar.h> 46 #include <machine/pci_cfgreg.h> 47 48 #include "pcib_if.h" 49 50 static int cfgmech; 51 static int devmax; 52 53 static int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes); 54 static void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes); 55 static int pcireg_cfgopen(void); 56 57 static struct mtx pcicfg_mtx; 58 59 /* 60 * Initialise access to PCI configuration space 61 */ 62 int 63 pci_cfgregopen(void) 64 { 65 static int opened = 0; 66 67 if (opened) 68 return (1); 69 if (pcireg_cfgopen() == 0) 70 return (0); 71 mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN); 72 opened = 1; 73 return (1); 74 } 75 76 /* 77 * Read configuration space register 78 */ 79 u_int32_t 80 pci_cfgregread(int bus, int slot, int func, int reg, int bytes) 81 { 82 uint32_t line; 83 84 /* 85 * Some BIOS writers seem to want to ignore the spec and put 86 * 0 in the intline rather than 255 to indicate none. Some use 87 * numbers in the range 128-254 to indicate something strange and 88 * apparently undocumented anywhere. Assume these are completely bogus 89 * and map them to 255, which the rest of the PCI code recognizes as 90 * as an invalid IRQ. 91 */ 92 if (reg == PCIR_INTLINE && bytes == 1) { 93 line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1); 94 if (line == 0 || line >= 128) 95 line = PCI_INVALID_IRQ; 96 return (line); 97 } 98 return (pcireg_cfgread(bus, slot, func, reg, bytes)); 99 } 100 101 /* 102 * Write configuration space register 103 */ 104 void 105 pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes) 106 { 107 108 pcireg_cfgwrite(bus, slot, func, reg, data, bytes); 109 } 110 111 /* 112 * Route a PCI interrupt 113 */ 114 int 115 pci_cfgintr(int bus, int device, int pin, int oldirq) 116 { 117 118 printf("pci_cfgintr: can't route an interrupt to %d:%d INT%c without ACPI\n", bus, 119 device, 'A' + pin - 1); 120 return (PCI_INVALID_IRQ); 121 } 122 123 /* 124 * Configuration space access using direct register operations 125 */ 126 127 /* enable configuration space accesses and return data port address */ 128 static int 129 pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) 130 { 131 int dataport = 0; 132 133 if (bus <= PCI_BUSMAX 134 && slot < devmax 135 && func <= PCI_FUNCMAX 136 && reg <= PCI_REGMAX 137 && bytes != 3 138 && (unsigned) bytes <= 4 139 && (reg & (bytes - 1)) == 0) { 140 switch (cfgmech) { 141 case 1: 142 outl(CONF1_ADDR_PORT, (1 << 31) 143 | (bus << 16) | (slot << 11) 144 | (func << 8) | (reg & ~0x03)); 145 dataport = CONF1_DATA_PORT + (reg & 0x03); 146 break; 147 case 2: 148 outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); 149 outb(CONF2_FORWARD_PORT, bus); 150 dataport = 0xc000 | (slot << 8) | reg; 151 break; 152 } 153 } 154 return (dataport); 155 } 156 157 /* disable configuration space accesses */ 158 static void 159 pci_cfgdisable(void) 160 { 161 switch (cfgmech) { 162 case 1: 163 outl(CONF1_ADDR_PORT, 0); 164 break; 165 case 2: 166 outb(CONF2_ENABLE_PORT, 0); 167 outb(CONF2_FORWARD_PORT, 0); 168 break; 169 } 170 } 171 172 static int 173 pcireg_cfgread(int bus, int slot, int func, int reg, int bytes) 174 { 175 int data = -1; 176 int port; 177 178 mtx_lock_spin(&pcicfg_mtx); 179 port = pci_cfgenable(bus, slot, func, reg, bytes); 180 if (port != 0) { 181 switch (bytes) { 182 case 1: 183 data = inb(port); 184 break; 185 case 2: 186 data = inw(port); 187 break; 188 case 4: 189 data = inl(port); 190 break; 191 } 192 pci_cfgdisable(); 193 } 194 mtx_unlock_spin(&pcicfg_mtx); 195 return (data); 196 } 197 198 static void 199 pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 200 { 201 int port; 202 203 mtx_lock_spin(&pcicfg_mtx); 204 port = pci_cfgenable(bus, slot, func, reg, bytes); 205 if (port != 0) { 206 switch (bytes) { 207 case 1: 208 outb(port, data); 209 break; 210 case 2: 211 outw(port, data); 212 break; 213 case 4: 214 outl(port, data); 215 break; 216 } 217 pci_cfgdisable(); 218 } 219 mtx_unlock_spin(&pcicfg_mtx); 220 } 221 222 /* check whether the configuration mechanism has been correctly identified */ 223 static int 224 pci_cfgcheck(int maxdev) 225 { 226 uint32_t id, class; 227 uint8_t header; 228 uint8_t device; 229 int port; 230 231 if (bootverbose) 232 printf("pci_cfgcheck:\tdevice "); 233 234 for (device = 0; device < maxdev; device++) { 235 if (bootverbose) 236 printf("%d ", device); 237 238 port = pci_cfgenable(0, device, 0, 0, 4); 239 id = inl(port); 240 if (id == 0 || id == 0xffffffff) 241 continue; 242 243 port = pci_cfgenable(0, device, 0, 8, 4); 244 class = inl(port) >> 8; 245 if (bootverbose) 246 printf("[class=%06x] ", class); 247 if (class == 0 || (class & 0xf870ff) != 0) 248 continue; 249 250 port = pci_cfgenable(0, device, 0, 14, 1); 251 header = inb(port); 252 if (bootverbose) 253 printf("[hdr=%02x] ", header); 254 if ((header & 0x7e) != 0) 255 continue; 256 257 if (bootverbose) 258 printf("is there (id=%08x)\n", id); 259 260 pci_cfgdisable(); 261 return (1); 262 } 263 if (bootverbose) 264 printf("-- nothing found\n"); 265 266 pci_cfgdisable(); 267 return (0); 268 } 269 270 static int 271 pcireg_cfgopen(void) 272 { 273 uint32_t mode1res, oldval1; 274 uint8_t mode2res, oldval2; 275 276 oldval1 = inl(CONF1_ADDR_PORT); 277 278 if (bootverbose) { 279 printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n", 280 oldval1); 281 } 282 283 if ((oldval1 & CONF1_ENABLE_MSK) == 0) { 284 285 cfgmech = 1; 286 devmax = 32; 287 288 outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 289 outb(CONF1_ADDR_PORT + 3, 0); 290 mode1res = inl(CONF1_ADDR_PORT); 291 outl(CONF1_ADDR_PORT, oldval1); 292 293 if (bootverbose) 294 printf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n", 295 mode1res, CONF1_ENABLE_CHK); 296 297 if (mode1res) { 298 if (pci_cfgcheck(32)) 299 return (cfgmech); 300 } 301 302 outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); 303 mode1res = inl(CONF1_ADDR_PORT); 304 outl(CONF1_ADDR_PORT, oldval1); 305 306 if (bootverbose) 307 printf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n", 308 mode1res, CONF1_ENABLE_CHK1); 309 310 if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { 311 if (pci_cfgcheck(32)) 312 return (cfgmech); 313 } 314 } 315 316 oldval2 = inb(CONF2_ENABLE_PORT); 317 318 if (bootverbose) { 319 printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", 320 oldval2); 321 } 322 323 if ((oldval2 & 0xf0) == 0) { 324 325 cfgmech = 2; 326 devmax = 16; 327 328 outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); 329 mode2res = inb(CONF2_ENABLE_PORT); 330 outb(CONF2_ENABLE_PORT, oldval2); 331 332 if (bootverbose) 333 printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", 334 mode2res, CONF2_ENABLE_CHK); 335 336 if (mode2res == CONF2_ENABLE_RES) { 337 if (bootverbose) 338 printf("pci_open(2a):\tnow trying mechanism 2\n"); 339 340 if (pci_cfgcheck(16)) 341 return (cfgmech); 342 } 343 } 344 345 cfgmech = 0; 346 devmax = 0; 347 return (cfgmech); 348 } 349