pci_cfgreg.c revision 66529
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 * $FreeBSD: head/sys/amd64/pci/pci_cfgreg.c 66529 2000-10-02 07:11:13Z msmith $ 29 * 30 */ 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 39#include <pci/pcivar.h> 40#include <pci/pcireg.h> 41#include <isa/isavar.h> 42#include <machine/nexusvar.h> 43#include <machine/pci_cfgreg.h> 44 45#include <machine/segments.h> 46#include <machine/pc/bios.h> 47 48#include "pcib_if.h" 49 50static int cfgmech; 51static int devmax; 52static int usebios; 53 54static int pcibios_cfgread(int bus, int slot, int func, int reg, int bytes); 55static void pcibios_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes); 56static int pcibios_cfgopen(void); 57static int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes); 58static void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes); 59static int pcireg_cfgopen(void); 60 61/* 62 * Initialise access to PCI configuration space 63 */ 64int 65pci_cfgregopen(void) 66{ 67 static int opened = 0; 68 69 if (opened) 70 return(1); 71 72 if (pcibios_cfgopen() != 0) { 73 usebios = 1; 74 } else if (pcireg_cfgopen() != 0) { 75 usebios = 0; 76 } else { 77 return(0); 78 } 79 opened = 1; 80 return(1); 81} 82 83/* 84 * Read configuration space register 85 */ 86u_int32_t 87pci_cfgregread(int bus, int slot, int func, int reg, int bytes) 88{ 89 return(usebios ? 90 pcibios_cfgread(bus, slot, func, reg, bytes) : 91 pcireg_cfgread(bus, slot, func, reg, bytes)); 92} 93 94/* 95 * Write configuration space register 96 */ 97void 98pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes) 99{ 100 return(usebios ? 101 pcibios_cfgwrite(bus, slot, func, reg, data, bytes) : 102 pcireg_cfgwrite(bus, slot, func, reg, data, bytes)); 103} 104 105/* 106 * Config space access using BIOS functions 107 */ 108static int 109pcibios_cfgread(int bus, int slot, int func, int reg, int bytes) 110{ 111 struct bios_regs args; 112 u_int mask; 113 114 switch(bytes) { 115 case 1: 116 args.eax = PCIBIOS_READ_CONFIG_BYTE; 117 mask = 0xff; 118 break; 119 case 2: 120 args.eax = PCIBIOS_READ_CONFIG_WORD; 121 mask = 0xffff; 122 break; 123 case 4: 124 args.eax = PCIBIOS_READ_CONFIG_DWORD; 125 mask = 0xffffffff; 126 break; 127 default: 128 return(-1); 129 } 130 args.ebx = (bus << 8) | (slot << 3) | func; 131 args.edi = reg; 132 bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL)); 133 /* check call results? */ 134 return(args.ecx & mask); 135} 136 137static void 138pcibios_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 139{ 140 struct bios_regs args; 141 142 switch(bytes) { 143 case 1: 144 args.eax = PCIBIOS_WRITE_CONFIG_BYTE; 145 break; 146 case 2: 147 args.eax = PCIBIOS_WRITE_CONFIG_WORD; 148 break; 149 case 4: 150 args.eax = PCIBIOS_WRITE_CONFIG_DWORD; 151 break; 152 default: 153 return; 154 } 155 args.ebx = (bus << 8) | (slot << 3) | func; 156 args.ecx = data; 157 args.edi = reg; 158 bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL)); 159} 160 161/* 162 * Determine whether there is a PCI BIOS present 163 */ 164static int 165pcibios_cfgopen(void) 166{ 167 /* check for a found entrypoint */ 168 return(PCIbios.entry != 0); 169} 170 171/* 172 * Configuration space access using direct register operations 173 */ 174 175/* enable configuration space accesses and return data port address */ 176static int 177pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) 178{ 179 int dataport = 0; 180 181 if (bus <= PCI_BUSMAX 182 && slot < devmax 183 && func <= PCI_FUNCMAX 184 && reg <= PCI_REGMAX 185 && bytes != 3 186 && (unsigned) bytes <= 4 187 && (reg & (bytes -1)) == 0) { 188 switch (cfgmech) { 189 case 1: 190 outl(CONF1_ADDR_PORT, (1 << 31) 191 | (bus << 16) | (slot << 11) 192 | (func << 8) | (reg & ~0x03)); 193 dataport = CONF1_DATA_PORT + (reg & 0x03); 194 break; 195 case 2: 196 outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); 197 outb(CONF2_FORWARD_PORT, bus); 198 dataport = 0xc000 | (slot << 8) | reg; 199 break; 200 } 201 } 202 return (dataport); 203} 204 205/* disable configuration space accesses */ 206static void 207pci_cfgdisable(void) 208{ 209 switch (cfgmech) { 210 case 1: 211 outl(CONF1_ADDR_PORT, 0); 212 break; 213 case 2: 214 outb(CONF2_ENABLE_PORT, 0); 215 outb(CONF2_FORWARD_PORT, 0); 216 break; 217 } 218} 219 220static int 221pcireg_cfgread(int bus, int slot, int func, int reg, int bytes) 222{ 223 int data = -1; 224 int port; 225 226 port = pci_cfgenable(bus, slot, func, reg, bytes); 227 228 if (port != 0) { 229 switch (bytes) { 230 case 1: 231 data = inb(port); 232 break; 233 case 2: 234 data = inw(port); 235 break; 236 case 4: 237 data = inl(port); 238 break; 239 } 240 pci_cfgdisable(); 241 } 242 return (data); 243} 244 245static void 246pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 247{ 248 int port; 249 250 port = pci_cfgenable(bus, slot, func, reg, bytes); 251 if (port != 0) { 252 switch (bytes) { 253 case 1: 254 outb(port, data); 255 break; 256 case 2: 257 outw(port, data); 258 break; 259 case 4: 260 outl(port, data); 261 break; 262 } 263 pci_cfgdisable(); 264 } 265} 266 267/* check whether the configuration mechanism has been correctly identified */ 268static int 269pci_cfgcheck(int maxdev) 270{ 271 u_char device; 272 273 if (bootverbose) 274 printf("pci_cfgcheck:\tdevice "); 275 276 for (device = 0; device < maxdev; device++) { 277 unsigned id, class, header; 278 if (bootverbose) 279 printf("%d ", device); 280 281 id = inl(pci_cfgenable(0, device, 0, 0, 4)); 282 if (id == 0 || id == -1) 283 continue; 284 285 class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8; 286 if (bootverbose) 287 printf("[class=%06x] ", class); 288 if (class == 0 || (class & 0xf870ff) != 0) 289 continue; 290 291 header = inb(pci_cfgenable(0, device, 0, 14, 1)); 292 if (bootverbose) 293 printf("[hdr=%02x] ", header); 294 if ((header & 0x7e) != 0) 295 continue; 296 297 if (bootverbose) 298 printf("is there (id=%08x)\n", id); 299 300 pci_cfgdisable(); 301 return (1); 302 } 303 if (bootverbose) 304 printf("-- nothing found\n"); 305 306 pci_cfgdisable(); 307 return (0); 308} 309 310static int 311pcireg_cfgopen(void) 312{ 313 unsigned long mode1res,oldval1; 314 unsigned char mode2res,oldval2; 315 316 oldval1 = inl(CONF1_ADDR_PORT); 317 318 if (bootverbose) { 319 printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n", 320 oldval1); 321 } 322 323 if ((oldval1 & CONF1_ENABLE_MSK) == 0) { 324 325 cfgmech = 1; 326 devmax = 32; 327 328 outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 329 outb(CONF1_ADDR_PORT +3, 0); 330 mode1res = inl(CONF1_ADDR_PORT); 331 outl(CONF1_ADDR_PORT, oldval1); 332 333 if (bootverbose) 334 printf("pci_open(1a):\tmode1res=0x%08lx (0x%08lx)\n", 335 mode1res, CONF1_ENABLE_CHK); 336 337 if (mode1res) { 338 if (pci_cfgcheck(32)) 339 return (cfgmech); 340 } 341 342 outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); 343 mode1res = inl(CONF1_ADDR_PORT); 344 outl(CONF1_ADDR_PORT, oldval1); 345 346 if (bootverbose) 347 printf("pci_open(1b):\tmode1res=0x%08lx (0x%08lx)\n", 348 mode1res, CONF1_ENABLE_CHK1); 349 350 if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { 351 if (pci_cfgcheck(32)) 352 return (cfgmech); 353 } 354 } 355 356 oldval2 = inb(CONF2_ENABLE_PORT); 357 358 if (bootverbose) { 359 printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", 360 oldval2); 361 } 362 363 if ((oldval2 & 0xf0) == 0) { 364 365 cfgmech = 2; 366 devmax = 16; 367 368 outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); 369 mode2res = inb(CONF2_ENABLE_PORT); 370 outb(CONF2_ENABLE_PORT, oldval2); 371 372 if (bootverbose) 373 printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", 374 mode2res, CONF2_ENABLE_CHK); 375 376 if (mode2res == CONF2_ENABLE_RES) { 377 if (bootverbose) 378 printf("pci_open(2a):\tnow trying mechanism 2\n"); 379 380 if (pci_cfgcheck(16)) 381 return (cfgmech); 382 } 383 } 384 385 cfgmech = 0; 386 devmax = 0; 387 return (cfgmech); 388} 389 390