pci_cfgreg.c revision 21438
1/************************************************************************** 2** 3** $Id: pcibus.c,v 1.27 1996/10/30 22:38:55 asami Exp $ 4** 5** pci bus subroutines for i386 architecture. 6** 7** FreeBSD 8** 9**------------------------------------------------------------------------- 10** 11** Copyright (c) 1994 Wolfgang Stanglmeier. All rights reserved. 12** 13** Redistribution and use in source and binary forms, with or without 14** modification, are permitted provided that the following conditions 15** are met: 16** 1. Redistributions of source code must retain the above copyright 17** notice, this list of conditions and the following disclaimer. 18** 2. Redistributions in binary form must reproduce the above copyright 19** notice, this list of conditions and the following disclaimer in the 20** documentation and/or other materials provided with the distribution. 21** 3. The name of the author may not be used to endorse or promote products 22** derived from this software without specific prior written permission. 23** 24** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 25** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 26** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 27** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 28** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 29** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 33** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34** 35*************************************************************************** 36*/ 37 38#include "vector.h" 39 40#include <sys/param.h> 41#include <sys/systm.h> 42#include <sys/kernel.h> 43 44#include <i386/isa/icu.h> 45#include <i386/isa/isa_device.h> 46 47#include <pci/pcivar.h> 48#include <pci/pcireg.h> 49#include <pci/pcibus.h> 50 51/*----------------------------------------------------------------- 52** 53** The following functions are provided by the pci bios. 54** They are used only by the pci configuration. 55** 56** pcibus_setup(): 57** Probes for a pci system. 58** Sets pci_maxdevice and pci_mechanism. 59** 60** pcibus_tag(): 61** Creates a handle for pci configuration space access. 62** This handle is given to the read/write functions. 63** 64** pcibus_ftag(): 65** Creates a modified handle. 66** 67** pcibus_read(): 68** Read a long word from the pci configuration space. 69** Requires a tag (from pcitag) and the register 70** number (should be a long word alligned one). 71** 72** pcibus_write(): 73** Writes a long word to the pci configuration space. 74** Requires a tag (from pcitag), the register number 75** (should be a long word alligned one), and a value. 76** 77** pcibus_regirq(): 78** Register an interupt handler for a pci device. 79** Requires a tag (from pcitag), the register number 80** (should be a long word alligned one), and a value. 81** 82**----------------------------------------------------------------- 83*/ 84 85static int 86pcibus_check (void); 87 88static void 89pcibus_setup (void); 90 91static pcici_t 92pcibus_tag (u_char bus, u_char device, u_char func); 93 94static pcici_t 95pcibus_ftag (pcici_t tag, u_char func); 96 97static u_long 98pcibus_read (pcici_t tag, u_long reg); 99 100static void 101pcibus_write (pcici_t tag, u_long reg, u_long data); 102 103static int 104pcibus_ihandler_attach (int irq, inthand2_t *func, int arg, unsigned* maskptr); 105 106static int 107pcibus_ihandler_detach (int irq, inthand2_t *func); 108 109static int 110pcibus_imask_include (int irq, unsigned* maskptr); 111 112static int 113pcibus_imask_exclude (int irq, unsigned* maskptr); 114 115static struct pcibus i386pci = { 116 "pci", 117 pcibus_setup, 118 pcibus_tag, 119 pcibus_ftag, 120 pcibus_read, 121 pcibus_write, 122 ICU_LEN, 123 pcibus_ihandler_attach, 124 pcibus_ihandler_detach, 125 pcibus_imask_include, 126 pcibus_imask_exclude, 127}; 128 129/* 130** Announce structure to generic driver 131*/ 132 133DATA_SET (pcibus_set, i386pci); 134 135/*-------------------------------------------------------------------- 136** 137** Determine configuration mode 138** 139**-------------------------------------------------------------------- 140*/ 141 142 143#define CONF1_ADDR_PORT 0x0cf8 144#define CONF1_DATA_PORT 0x0cfc 145 146#define CONF1_ENABLE 0x80000000ul 147#define CONF1_ENABLE_CHK 0x80000000ul 148#define CONF1_ENABLE_MSK 0x7ff00000ul 149#define CONF1_ENABLE_CHK1 0xff000001ul 150#define CONF1_ENABLE_MSK1 0x80000001ul 151#define CONF1_ENABLE_RES1 0x80000000ul 152 153#define CONF2_ENABLE_PORT 0x0cf8 154#ifdef PC98 155#define CONF2_FORWARD_PORT 0x0cf9 156#else 157#define CONF2_FORWARD_PORT 0x0cfa 158#endif 159 160#define CONF2_ENABLE_CHK 0x0e 161#define CONF2_ENABLE_RES 0x0e 162 163static int 164pcibus_check (void) 165{ 166 u_char device; 167 168 if (bootverbose) printf ("pcibus_check:\tdevice "); 169 170 for (device = 0; device < pci_maxdevice; device++) { 171 unsigned long id; 172 if (bootverbose) 173 printf ("%d ", device); 174 id = pcibus_read (pcibus_tag (0,device,0), 0); 175 if (id && id != 0xfffffffful) { 176 if (bootverbose) printf ("is there (id=%08lx)\n", id); 177 return 1; 178 } 179 } 180 if (bootverbose) 181 printf ("-- nothing found\n"); 182 return 0; 183} 184 185static void 186pcibus_setup (void) 187{ 188 unsigned long mode1res,oldval1; 189 unsigned char mode2res,oldval2; 190 191 oldval1 = inl (CONF1_ADDR_PORT); 192 193 if (bootverbose) { 194 printf ("pcibus_setup(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n", oldval1); 195 } 196 197 /*--------------------------------------- 198 ** Assume configuration mechanism 1 for now ... 199 **--------------------------------------- 200 */ 201 202 if ((oldval1 & CONF1_ENABLE_MSK) == 0) { 203 204 pci_mechanism = 1; 205 pci_maxdevice = 32; 206 207 outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 208 outb (CONF1_ADDR_PORT +3, 0); 209 mode1res = inl (CONF1_ADDR_PORT); 210 outl (CONF1_ADDR_PORT, oldval1); 211 212 if (bootverbose) 213 printf ("pcibus_setup(1a):\tmode1res=0x%08lx (0x%08lx)\n", 214 mode1res, CONF1_ENABLE_CHK); 215 216 if (mode1res) { 217 if (pcibus_check()) 218 return; 219 }; 220 221 outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); 222 mode1res = inl(CONF1_ADDR_PORT); 223 outl (CONF1_ADDR_PORT, oldval1); 224 225 if (bootverbose) 226 printf ("pcibus_setup(1b):\tmode1res=0x%08lx (0x%08lx)\n", 227 mode1res, CONF1_ENABLE_CHK1); 228 229 if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { 230 if (pcibus_check()) 231 return; 232 }; 233 } 234 235 /*--------------------------------------- 236 ** Try configuration mechanism 2 ... 237 **--------------------------------------- 238 */ 239 240 oldval2 = inb (CONF2_ENABLE_PORT); 241 242 if (bootverbose) { 243 printf ("pcibus_setup(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", oldval2); 244 } 245 246 if ((oldval2 & 0xf0) == 0) { 247 248 pci_mechanism = 2; 249 pci_maxdevice = 16; 250 251 outb (CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); 252 mode2res = inb(CONF2_ENABLE_PORT); 253 outb (CONF2_ENABLE_PORT, oldval2); 254 255 if (bootverbose) 256 printf ("pcibus_setup(2a):\tmode2res=0x%02x (0x%02x)\n", 257 mode2res, CONF2_ENABLE_CHK); 258 259 if (mode2res == CONF2_ENABLE_RES) { 260 if (bootverbose) 261 printf ("pcibus_setup(2a):\tnow trying mechanism 2\n"); 262 263 if (pcibus_check()) 264 return; 265 } 266 } 267 268 /*--------------------------------------- 269 ** No PCI bus host bridge found 270 **--------------------------------------- 271 */ 272 273 pci_mechanism = 0; 274 pci_maxdevice = 0; 275} 276 277/*-------------------------------------------------------------------- 278** 279** Build a pcitag from bus, device and function number 280** 281**-------------------------------------------------------------------- 282*/ 283 284static pcici_t 285pcibus_tag (unsigned char bus, unsigned char device, unsigned char func) 286{ 287 pcici_t tag; 288 289 tag.cfg1 = 0; 290 if (func >= 8) return tag; 291 292 switch (pci_mechanism) { 293 294 case 1: 295 if (device < 32) { 296 tag.cfg1 = CONF1_ENABLE 297 | (((u_long) bus ) << 16ul) 298 | (((u_long) device) << 11ul) 299 | (((u_long) func ) << 8ul); 300 } 301 break; 302 case 2: 303 if (device < 16) { 304 tag.cfg2.port = 0xc000 | (device << 8ul); 305 tag.cfg2.enable = 0xf0 | (func << 1ul); 306 tag.cfg2.forward = bus; 307 } 308 break; 309 }; 310 return tag; 311} 312 313static pcici_t 314pcibus_ftag (pcici_t tag, u_char func) 315{ 316 switch (pci_mechanism) { 317 318 case 1: 319 tag.cfg1 &= ~0x700ul; 320 tag.cfg1 |= (((u_long) func) << 8ul); 321 break; 322 case 2: 323 tag.cfg2.enable = 0xf0 | (func << 1ul); 324 break; 325 }; 326 return tag; 327} 328 329/*-------------------------------------------------------------------- 330** 331** Read register from configuration space. 332** 333**-------------------------------------------------------------------- 334*/ 335 336static u_long 337pcibus_read (pcici_t tag, u_long reg) 338{ 339 u_long addr, data = 0; 340 341 if (!tag.cfg1) return (0xfffffffful); 342 343 switch (pci_mechanism) { 344 345 case 1: 346 addr = tag.cfg1 | (reg & 0xfc); 347#ifdef PCI_DEBUG 348 printf ("pci_conf_read(1): addr=%x ", addr); 349#endif 350 outl (CONF1_ADDR_PORT, addr); 351 data = inl (CONF1_DATA_PORT); 352 outl (CONF1_ADDR_PORT, 0 ); 353 break; 354 355 case 2: 356 addr = tag.cfg2.port | (reg & 0xfc); 357#ifdef PCI_DEBUG 358 printf ("pci_conf_read(2): addr=%x ", addr); 359#endif 360 outb (CONF2_ENABLE_PORT , tag.cfg2.enable ); 361 outb (CONF2_FORWARD_PORT, tag.cfg2.forward); 362 363 data = inl ((u_short) addr); 364 365 outb (CONF2_ENABLE_PORT, 0); 366 outb (CONF2_FORWARD_PORT, 0); 367 break; 368 }; 369 370#ifdef PCI_DEBUG 371 printf ("data=%x\n", data); 372#endif 373 374 return (data); 375} 376 377/*-------------------------------------------------------------------- 378** 379** Write register into configuration space. 380** 381**-------------------------------------------------------------------- 382*/ 383 384static void 385pcibus_write (pcici_t tag, u_long reg, u_long data) 386{ 387 u_long addr; 388 389 if (!tag.cfg1) return; 390 391 switch (pci_mechanism) { 392 393 case 1: 394 addr = tag.cfg1 | (reg & 0xfc); 395#ifdef PCI_DEBUG 396 printf ("pci_conf_write(1): addr=%x data=%x\n", 397 addr, data); 398#endif 399 outl (CONF1_ADDR_PORT, addr); 400 outl (CONF1_DATA_PORT, data); 401 outl (CONF1_ADDR_PORT, 0 ); 402 break; 403 404 case 2: 405 addr = tag.cfg2.port | (reg & 0xfc); 406#ifdef PCI_DEBUG 407 printf ("pci_conf_write(2): addr=%x data=%x\n", 408 addr, data); 409#endif 410 outb (CONF2_ENABLE_PORT, tag.cfg2.enable); 411 outb (CONF2_FORWARD_PORT, tag.cfg2.forward); 412 413 outl ((u_short) addr, data); 414 415 outb (CONF2_ENABLE_PORT, 0); 416 outb (CONF2_FORWARD_PORT, 0); 417 break; 418 }; 419} 420 421/*----------------------------------------------------------------------- 422** 423** Register an interupt handler for a pci device. 424** 425**----------------------------------------------------------------------- 426*/ 427 428static int 429pcibus_ihandler_attach (int irq, inthand2_t *func, int arg, unsigned * maskptr) 430{ 431 char buf[16]; 432 char *cp; 433 int free_id, id, result; 434 435 sprintf(buf, "pci irq%d", irq); 436 for (cp = intrnames, free_id = 0, id = 0; id < NR_DEVICES; id++) { 437 if (strcmp(cp, buf) == 0) 438 break; 439 if (free_id <= 0 && strcmp(cp, "pci irqnn") == 0) 440 free_id = id; 441 while (*cp++ != '\0') 442 ; 443 } 444 if (id == NR_DEVICES) { 445 id = free_id; 446 if (id == 0) { 447 /* 448 * All pci irq counters are in use, perhaps because 449 * config is old so there aren't any. Abuse the 450 * clk0 counter. 451 */ 452 printf ( 453 "pcibus_ihandler_attach: counting pci irq%d's as clk0 irqs\n", 454 irq); 455 } 456 } 457 result = register_intr( 458 irq, /* isa irq */ 459 id, /* device id */ 460 0, /* flags? */ 461 func, /* handler */ 462 maskptr, /* mask pointer */ 463 arg); /* handler arg */ 464 465 if (result) { 466 printf ("@@@ pcibus_ihandler_attach: result=%d\n", result); 467 return (result); 468 }; 469 update_intr_masks(); 470 471 INTREN ((1ul<<irq)); 472 return (0); 473} 474 475static int 476pcibus_ihandler_detach (int irq, inthand2_t *func) 477{ 478 int result; 479 480 INTRDIS ((1ul<<irq)); 481 482 result = unregister_intr (irq, func); 483 484 if (result) 485 printf ("@@@ pcibus_ihandler_detach: result=%d\n", result); 486 487 update_intr_masks(); 488 489 return (result); 490} 491 492static int 493pcibus_imask_include (int irq, unsigned* maskptr) 494{ 495 unsigned mask; 496 497 if (!maskptr) return (0); 498 499 mask = 1ul << irq; 500 501 if (*maskptr & mask) 502 return (-1); 503 504 INTRMASK (*maskptr, mask); 505 update_intr_masks(); 506 507 return (0); 508} 509 510static int 511pcibus_imask_exclude (int irq, unsigned* maskptr) 512{ 513 unsigned mask; 514 515 if (!maskptr) return (0); 516 517 mask = 1ul << irq; 518 519 if (! (*maskptr & mask)) 520 return (-1); 521 522 INTRUNMASK (*maskptr, mask); 523 update_intr_masks(); 524 525 return (0); 526} 527