pci_cfgreg.c revision 11544
1181155Sdas/************************************************************************** 2181155Sdas** 3181155Sdas** $Id: pcibus.c,v 1.17 1995/10/15 23:43:08 se Exp $ 4181155Sdas** 5181155Sdas** pci bus subroutines for i386 architecture. 6181155Sdas** 7181155Sdas** FreeBSD 8181155Sdas** 9181155Sdas**------------------------------------------------------------------------- 10181155Sdas** 11181155Sdas** Copyright (c) 1994 Wolfgang Stanglmeier. All rights reserved. 12181155Sdas** 13181155Sdas** Redistribution and use in source and binary forms, with or without 14181155Sdas** modification, are permitted provided that the following conditions 15181155Sdas** are met: 16181155Sdas** 1. Redistributions of source code must retain the above copyright 17181155Sdas** notice, this list of conditions and the following disclaimer. 18181155Sdas** 2. Redistributions in binary form must reproduce the above copyright 19181155Sdas** notice, this list of conditions and the following disclaimer in the 20181155Sdas** documentation and/or other materials provided with the distribution. 21181155Sdas** 3. The name of the author may not be used to endorse or promote products 22181155Sdas** derived from this software without specific prior written permission. 23181155Sdas** 24181155Sdas** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 25181155Sdas** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 26181155Sdas** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 27181155Sdas** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 28181155Sdas** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 29181155Sdas** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30181155Sdas** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31181155Sdas** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32181155Sdas** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 33181155Sdas** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34181155Sdas** 35181155Sdas*************************************************************************** 36181155Sdas*/ 37181155Sdas 38181155Sdas#include <sys/param.h> 39181155Sdas#include <sys/systm.h> 40181155Sdas#include <sys/kernel.h> 41181155Sdas 42181155Sdas#include <machine/cpu.h> /* bootverbose */ 43181155Sdas 44181155Sdas#include <i386/isa/icu.h> 45181155Sdas#include <i386/isa/isa.h> 46181155Sdas#include <i386/isa/isa_device.h> 47181155Sdas 48181155Sdas#include <pci/pcivar.h> 49181155Sdas#include <pci/pcireg.h> 50181155Sdas#include <pci/pcibus.h> 51181155Sdas 52181155Sdas/*----------------------------------------------------------------- 53181155Sdas** 54181155Sdas** The following functions are provided by the pci bios. 55181155Sdas** They are used only by the pci configuration. 56181155Sdas** 57181155Sdas** pcibus_setup(): 58181155Sdas** Probes for a pci system. 59181155Sdas** Sets pci_maxdevice and pci_mechanism. 60181155Sdas** 61181155Sdas** pcibus_tag(): 62181155Sdas** Creates a handle for pci configuration space access. 63181155Sdas** This handle is given to the read/write functions. 64181155Sdas** 65181155Sdas** pcibus_ftag(): 66181155Sdas** Creates a modified handle. 67181155Sdas** 68181155Sdas** pcibus_read(): 69181155Sdas** Read a long word from the pci configuration space. 70181155Sdas** Requires a tag (from pcitag) and the register 71181155Sdas** number (should be a long word alligned one). 72181155Sdas** 73181155Sdas** pcibus_write(): 74181155Sdas** Writes a long word to the pci configuration space. 75181155Sdas** Requires a tag (from pcitag), the register number 76181155Sdas** (should be a long word alligned one), and a value. 77181155Sdas** 78181155Sdas** pcibus_regirq(): 79181155Sdas** Register an interupt handler for a pci device. 80181155Sdas** Requires a tag (from pcitag), the register number 81181155Sdas** (should be a long word alligned one), and a value. 82181155Sdas** 83181155Sdas**----------------------------------------------------------------- 84181155Sdas*/ 85181155Sdas 86181155Sdasstatic int 87181155Sdaspcibus_check (void); 88181155Sdas 89181155Sdasstatic void 90181155Sdaspcibus_setup (void); 91181155Sdas 92181155Sdasstatic pcici_t 93181155Sdaspcibus_tag (u_char bus, u_char device, u_char func); 94181155Sdas 95181155Sdasstatic pcici_t 96181155Sdaspcibus_ftag (pcici_t tag, u_char func); 97181155Sdas 98181155Sdasstatic u_long 99181155Sdaspcibus_read (pcici_t tag, u_long reg); 100181155Sdas 101181155Sdasstatic void 102181155Sdaspcibus_write (pcici_t tag, u_long reg, u_long data); 103 104static int 105pcibus_ihandler_attach (int irq, void(*ihandler)(), int arg, unsigned* maskp); 106 107static int 108pcibus_ihandler_detach (int irq, void(*handler)()); 109 110static int 111pcibus_imask_include (int irq, unsigned* maskptr); 112 113static int 114pcibus_imask_exclude (int irq, unsigned* maskptr); 115 116struct pcibus i386pci = { 117 "pci", 118 pcibus_setup, 119 pcibus_tag, 120 pcibus_ftag, 121 pcibus_read, 122 pcibus_write, 123 ICU_LEN, 124 pcibus_ihandler_attach, 125 pcibus_ihandler_detach, 126 pcibus_imask_include, 127 pcibus_imask_exclude, 128}; 129 130/* 131** Announce structure to generic driver 132*/ 133 134DATA_SET (pcibus_set, i386pci); 135 136/*-------------------------------------------------------------------- 137** 138** Determine configuration mode 139** 140**-------------------------------------------------------------------- 141*/ 142 143 144#define CONF1_ADDR_PORT 0x0cf8 145#define CONF1_DATA_PORT 0x0cfc 146 147#define CONF1_ENABLE 0x80000000ul 148#define CONF1_ENABLE_CHK 0x80000000ul 149#define CONF1_ENABLE_MSK 0x00ff07fful 150#define CONF1_ENABLE_CHK1 0xff000001ul 151#define CONF1_ENABLE_MSK1 0x80000001ul 152#define CONF1_ENABLE_RES1 0x80000000ul 153 154#define CONF2_ENABLE_PORT 0x0cf8 155#define CONF2_FORWARD_PORT 0x0cfa 156 157#define CONF2_ENABLE_CHK 0x0e 158#define CONF2_ENABLE_RES 0x0e 159 160static int 161pcibus_check (void) 162{ 163 u_char device; 164 165 if (bootverbose) printf ("pcibus_check:\tdevice "); 166 167 for (device = 0; device < pci_maxdevice; device++) { 168 unsigned long id; 169 if (bootverbose) 170 printf ("%d ", device); 171 id = pcibus_read (pcibus_tag (0,device,0), 0); 172 if (id != 0xfffffffful) { 173 if (bootverbose) printf ("is there (id=%08lx)\n", id); 174 return 1; 175 } 176 } 177 if (bootverbose) 178 printf ("-- nothing found\n"); 179 return 0; 180} 181 182static void 183pcibus_setup (void) 184{ 185 unsigned long mode1res,oldval1; 186 unsigned char mode2res,oldval2; 187 188 oldval1 = inl (CONF1_ADDR_PORT); 189 190 if (bootverbose) { 191 printf ("pcibus_setup(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n", oldval1); 192 } 193 194 /*--------------------------------------- 195 ** Assume configuration mechanism 1 for now ... 196 **--------------------------------------- 197 */ 198 199 if ((oldval1 & CONF1_ENABLE_MSK) == 0) { 200 201 pci_mechanism = 1; 202 pci_maxdevice = 32; 203 204 outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 205 outb (CONF1_ADDR_PORT +3, 0); 206 mode1res = inl (CONF1_ADDR_PORT); 207 outl (CONF1_ADDR_PORT, oldval1); 208 209 if (bootverbose) 210 printf ("pcibus_setup(1a):\tmode1res=0x%08lx (0x%08lx)\n", 211 mode1res, CONF1_ENABLE_CHK); 212 213 if (mode1res) { 214 if (pcibus_check()) 215 return; 216 }; 217 218 outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); 219 mode1res = inl(CONF1_ADDR_PORT); 220 outl (CONF1_ADDR_PORT, oldval1); 221 222 if (bootverbose) 223 printf ("pcibus_setup(1b):\tmode1res=0x%08lx (0x%08lx)\n", 224 mode1res, CONF1_ENABLE_CHK1); 225 226 if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { 227 if (pcibus_check()) 228 return; 229 }; 230 } 231 232 /*--------------------------------------- 233 ** Try configuration mechanism 2 ... 234 **--------------------------------------- 235 */ 236 237 oldval2 = inb (CONF2_ENABLE_PORT); 238 239 if (bootverbose) { 240 printf ("pcibus_setup(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", oldval2); 241 } 242 243 if ((oldval2 & 0xf0) == 0) { 244 245 pci_mechanism = 2; 246 pci_maxdevice = 16; 247 248 outb (CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); 249 mode2res = inb(CONF2_ENABLE_PORT); 250 outb (CONF2_ENABLE_PORT, oldval2); 251 252 if (bootverbose) 253 printf ("pcibus_setup(2a):\tmode2res=0x%02x (0x%02x)\n", 254 mode2res, CONF2_ENABLE_CHK); 255 256 if (mode2res == CONF2_ENABLE_RES) { 257 if (bootverbose) 258 printf ("pcibus_setup(2a):\tnow trying mechanism 2\n"); 259 260 if (pcibus_check()) 261 return; 262 } 263 } 264 265 /*--------------------------------------- 266 ** No PCI bus host bridge found 267 **--------------------------------------- 268 */ 269 270 pci_mechanism = 0; 271 pci_maxdevice = 0; 272} 273 274/*-------------------------------------------------------------------- 275** 276** Build a pcitag from bus, device and function number 277** 278**-------------------------------------------------------------------- 279*/ 280 281static pcici_t 282pcibus_tag (unsigned char bus, unsigned char device, unsigned char func) 283{ 284 pcici_t tag; 285 286 tag.cfg1 = 0; 287 if (func >= 8) return tag; 288 289 switch (pci_mechanism) { 290 291 case 1: 292 if (device < 32) { 293 tag.cfg1 = CONF1_ENABLE 294 | (((u_long) bus ) << 16ul) 295 | (((u_long) device) << 11ul) 296 | (((u_long) func ) << 8ul); 297 } 298 break; 299 case 2: 300 if (device < 16) { 301 tag.cfg2.port = 0xc000 | (device << 8ul); 302 tag.cfg2.enable = 0xf0 | (func << 1ul); 303 tag.cfg2.forward = bus; 304 } 305 break; 306 }; 307 return tag; 308} 309 310static pcici_t 311pcibus_ftag (pcici_t tag, u_char func) 312{ 313 switch (pci_mechanism) { 314 315 case 1: 316 tag.cfg1 &= ~0x700ul; 317 tag.cfg1 |= (((u_long) func) << 8ul); 318 break; 319 case 2: 320 tag.cfg2.enable = 0xf0 | (func << 1ul); 321 break; 322 }; 323 return tag; 324} 325 326/*-------------------------------------------------------------------- 327** 328** Read register from configuration space. 329** 330**-------------------------------------------------------------------- 331*/ 332 333static u_long 334pcibus_read (pcici_t tag, u_long reg) 335{ 336 u_long addr, data = 0; 337 338 if (!tag.cfg1) return (0xfffffffful); 339 340 switch (pci_mechanism) { 341 342 case 1: 343 addr = tag.cfg1 | (reg & 0xfc); 344#ifdef PCI_DEBUG 345 printf ("pci_conf_read(1): addr=%x ", addr); 346#endif 347 outl (CONF1_ADDR_PORT, addr); 348 data = inl (CONF1_DATA_PORT); 349 outl (CONF1_ADDR_PORT, 0 ); 350 break; 351 352 case 2: 353 addr = tag.cfg2.port | (reg & 0xfc); 354#ifdef PCI_DEBUG 355 printf ("pci_conf_read(2): addr=%x ", addr); 356#endif 357 outb (CONF2_ENABLE_PORT , tag.cfg2.enable ); 358 outb (CONF2_FORWARD_PORT, tag.cfg2.forward); 359 360 data = inl ((u_short) addr); 361 362 outb (CONF2_ENABLE_PORT, 0); 363 outb (CONF2_FORWARD_PORT, 0); 364 break; 365 }; 366 367#ifdef PCI_DEBUG 368 printf ("data=%x\n", data); 369#endif 370 371 return (data); 372} 373 374/*-------------------------------------------------------------------- 375** 376** Write register into configuration space. 377** 378**-------------------------------------------------------------------- 379*/ 380 381static void 382pcibus_write (pcici_t tag, u_long reg, u_long data) 383{ 384 u_long addr; 385 386 if (!tag.cfg1) return; 387 388 switch (pci_mechanism) { 389 390 case 1: 391 addr = tag.cfg1 | (reg & 0xfc); 392#ifdef PCI_DEBUG 393 printf ("pci_conf_write(1): addr=%x data=%x\n", 394 addr, data); 395#endif 396 outl (CONF1_ADDR_PORT, addr); 397 outl (CONF1_DATA_PORT, data); 398 outl (CONF1_ADDR_PORT, 0 ); 399 break; 400 401 case 2: 402 addr = tag.cfg2.port | (reg & 0xfc); 403#ifdef PCI_DEBUG 404 printf ("pci_conf_write(2): addr=%x data=%x\n", 405 addr, data); 406#endif 407 outb (CONF2_ENABLE_PORT, tag.cfg2.enable); 408 outb (CONF2_FORWARD_PORT, tag.cfg2.forward); 409 410 outl ((u_short) addr, data); 411 412 outb (CONF2_ENABLE_PORT, 0); 413 outb (CONF2_FORWARD_PORT, 0); 414 break; 415 }; 416} 417 418/*----------------------------------------------------------------------- 419** 420** Register an interupt handler for a pci device. 421** 422**----------------------------------------------------------------------- 423*/ 424 425static int 426pcibus_ihandler_attach (int irq, void(*func)(), int arg, unsigned * maskptr) 427{ 428 int result; 429 result = register_intr( 430 irq, /* isa irq */ 431 0, /* deviced?? */ 432 0, /* flags? */ 433 (inthand2_t*) func, /* handler */ 434 maskptr, /* mask pointer */ 435 arg); /* handler arg */ 436 437 if (result) { 438 printf ("@@@ pcibus_ihandler_attach: result=%d\n", result); 439 return (result); 440 }; 441 update_intr_masks(); 442 443 INTREN ((1ul<<irq)); 444 return (0); 445} 446 447static int 448pcibus_ihandler_detach (int irq, void(*func)()) 449{ 450 int result; 451 452 INTRDIS ((1ul<<irq)); 453 454 result = unregister_intr (irq, (inthand2_t*) func); 455 456 if (result) 457 printf ("@@@ pcibus_ihandler_detach: result=%d\n", result); 458 459 update_intr_masks(); 460 461 return (result); 462} 463 464static int 465pcibus_imask_include (int irq, unsigned* maskptr) 466{ 467 unsigned mask; 468 469 if (!maskptr) return (0); 470 471 mask = 1ul << irq; 472 473 if (*maskptr & mask) 474 return (-1); 475 476 INTRMASK (*maskptr, mask); 477 update_intr_masks(); 478 479 return (0); 480} 481 482static int 483pcibus_imask_exclude (int irq, unsigned* maskptr) 484{ 485 unsigned mask; 486 487 if (!maskptr) return (0); 488 489 mask = 1ul << irq; 490 491 if (! (*maskptr & mask)) 492 return (-1); 493 494 *maskptr &= ~mask; 495 update_intr_masks(); 496 497 return (0); 498} 499