1 /************************************************************************** 2 ** 3 ** $Id: pcibus.c,v 1.1 1995/02/01 23:06:58 se 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 #ifndef __FreeBSD2__ 39 #if __FreeBSD__ >= 2 40 #define __FreeBSD2__ 41 #endif 42 #endif 43 44 #ifdef __FreeBSD2__ 45 #define HAS_CPUFUNC_H 46 #endif 47 48 #include <types.h> 49 #include <param.h> 50 #include <kernel.h> 51 #include <i386/isa/isa.h> 52 #include <i386/isa/isa_device.h> 53 #include <i386/isa/icu.h> 54 55 #ifdef HAS_CPUFUNC_H 56 #include <i386/include/cpufunc.h> 57 #endif 58 59 #include <pci/pcivar.h> 60 #include <pci/pcireg.h> 61 #include <pci/pcibus.h> 62 63 extern int printf(); 64 65 static char pci_mode; 66 67 /*----------------------------------------------------------------- 68 ** 69 ** The following functions are provided by the pci bios. 70 ** They are used only by the pci configuration. 71 ** 72 ** pcibus_mode(): 73 ** Probes for a pci system. 74 ** Returns 1 or 2 for pci configuration mechanism. 75 ** Returns 0 if no pci system. 76 ** 77 ** pcibus_tag(): 78 ** Gets a handle for accessing the pci configuration 79 ** space. 80 ** This handle is given to the mapping functions (see 81 ** above) or to the read/write functions. 82 ** 83 ** pcibus_read(): 84 ** Read a long word from the pci configuration space. 85 ** Requires a tag (from pcitag) and the register 86 ** number (should be a long word alligned one). 87 ** 88 ** pcibus_write(): 89 ** Writes a long word to the pci configuration space. 90 ** Requires a tag (from pcitag), the register number 91 ** (should be a long word alligned one), and a value. 92 ** 93 ** pcibus_regirq(): 94 ** Register an interupt handler for a pci device. 95 ** Requires a tag (from pcitag), the register number 96 ** (should be a long word alligned one), and a value. 97 ** 98 **----------------------------------------------------------------- 99 */ 100 101 static int 102 pcibus_mode (void); 103 104 static pcici_t 105 pcibus_tag (u_char bus, u_char device, u_char func); 106 107 static u_long 108 pcibus_read (pcici_t tag, u_long reg); 109 110 static void 111 pcibus_write (pcici_t tag, u_long reg, u_long data); 112 113 static int 114 pcibus_regint (pcici_t tag, int(*func)(), void* arg, unsigned* maskptr); 115 116 struct pcibus i386pci = { 117 "pci", 118 pcibus_mode, 119 pcibus_tag, 120 pcibus_read, 121 pcibus_write, 122 pcibus_regint, 123 }; 124 125 /* 126 ** Announce structure to generic driver 127 */ 128 129 DATA_SET (pcibus_set, i386pci); 130 131 /*-------------------------------------------------------------------- 132 ** 133 ** Port access 134 ** 135 **-------------------------------------------------------------------- 136 */ 137 138 #ifndef HAS_CPUFUNC_H 139 140 #undef inl 141 #define inl(port) \ 142 ({ u_long data; \ 143 __asm __volatile("inl %1, %0": "=a" (data): "d" ((u_short)(port))); \ 144 data; }) 145 146 147 #undef outl 148 #define outl(port, data) \ 149 {__asm __volatile("outl %0, %1"::"a" ((u_long)(data)), "d" ((u_short)(port)));} 150 151 152 #undef inb 153 #define inb(port) \ 154 ({ u_char data; \ 155 __asm __volatile("inb %1, %0": "=a" (data): "d" ((u_short)(port))); \ 156 data; }) 157 158 159 #undef outb 160 #define outb(port, data) \ 161 {__asm __volatile("outb %0, %1"::"a" ((u_char)(data)), "d" ((u_short)(port)));} 162 163 #endif /* HAS_CPUFUNC_H */ 164 165 /*-------------------------------------------------------------------- 166 ** 167 ** Determine configuration mode 168 ** 169 **-------------------------------------------------------------------- 170 */ 171 172 173 #define CONF1_ENABLE 0x80000000ul 174 #define CONF1_ADDR_PORT 0x0cf8 175 #define CONF1_DATA_PORT 0x0cfc 176 177 178 #define CONF2_ENABLE_PORT 0x0cf8 179 #define CONF2_FORWARD_PORT 0x0cfa 180 181 182 static int 183 pcibus_mode (void) 184 { 185 #ifdef PCI_CONF_MODE 186 return (PCI_CONF_MODE) 187 #else /* PCI_CONF_MODE */ 188 u_long result, oldval; 189 190 /*--------------------------------------- 191 ** Configuration mode 2 ? 192 **--------------------------------------- 193 */ 194 195 outb (CONF2_ENABLE_PORT, 0); 196 outb (CONF2_FORWARD_PORT, 0); 197 if (!inb (CONF2_ENABLE_PORT) && !inb (CONF2_FORWARD_PORT)) { 198 pci_mode = 2; 199 return (2); 200 }; 201 202 /*--------------------------------------- 203 ** Configuration mode 1 ? 204 **--------------------------------------- 205 */ 206 207 oldval = inl (CONF1_ADDR_PORT); 208 outl (CONF1_ADDR_PORT, CONF1_ENABLE); 209 result = inl (CONF1_ADDR_PORT); 210 outl (CONF1_ADDR_PORT, oldval); 211 212 if (result == CONF1_ENABLE) { 213 pci_mode = 1; 214 return (1); 215 }; 216 217 /*--------------------------------------- 218 ** No PCI bus available. 219 **--------------------------------------- 220 */ 221 return (0); 222 #endif /* PCI_CONF_MODE */ 223 } 224 225 /*-------------------------------------------------------------------- 226 ** 227 ** Build a pcitag from bus, device and function number 228 ** 229 **-------------------------------------------------------------------- 230 */ 231 232 static pcici_t 233 pcibus_tag (unsigned char bus, unsigned char device, unsigned char func) 234 { 235 pcici_t tag; 236 237 tag.cfg1 = 0; 238 if (device >= 32) return tag; 239 if (func >= 8) return tag; 240 241 switch (pci_mode) { 242 243 case 1: 244 tag.cfg1 = CONF1_ENABLE 245 | (((u_long) bus ) << 16ul) 246 | (((u_long) device) << 11ul) 247 | (((u_long) func ) << 8ul); 248 break; 249 case 2: 250 if (device >= 16) break; 251 tag.cfg2.port = 0xc000 | (device << 8ul); 252 tag.cfg2.enable = 0xf1 | (func << 1ul); 253 tag.cfg2.forward = bus; 254 break; 255 }; 256 return tag; 257 } 258 259 /*-------------------------------------------------------------------- 260 ** 261 ** Read register from configuration space. 262 ** 263 **-------------------------------------------------------------------- 264 */ 265 266 267 static u_long 268 pcibus_read (pcici_t tag, u_long reg) 269 { 270 u_long addr, data = 0; 271 272 if (!tag.cfg1) return (0xfffffffful); 273 274 switch (pci_mode) { 275 276 case 1: 277 addr = tag.cfg1 | (reg & 0xfc); 278 #ifdef PCI_DEBUG 279 printf ("pci_conf_read(1): addr=%x ", addr); 280 #endif 281 outl (CONF1_ADDR_PORT, addr); 282 data = inl (CONF1_DATA_PORT); 283 outl (CONF1_ADDR_PORT, 0 ); 284 break; 285 286 case 2: 287 addr = tag.cfg2.port | (reg & 0xfc); 288 #ifdef PCI_DEBUG 289 printf ("pci_conf_read(2): addr=%x ", addr); 290 #endif 291 outb (CONF2_ENABLE_PORT , tag.cfg2.enable ); 292 outb (CONF2_FORWARD_PORT, tag.cfg2.forward); 293 294 data = inl ((u_short) addr); 295 296 outb (CONF2_ENABLE_PORT, 0); 297 outb (CONF2_FORWARD_PORT, 0); 298 break; 299 }; 300 301 #ifdef PCI_DEBUG 302 printf ("data=%x\n", data); 303 #endif 304 305 return (data); 306 } 307 308 /*-------------------------------------------------------------------- 309 ** 310 ** Write register into configuration space. 311 ** 312 **-------------------------------------------------------------------- 313 */ 314 315 316 static void 317 pcibus_write (pcici_t tag, u_long reg, u_long data) 318 { 319 u_long addr; 320 321 if (!tag.cfg1) return; 322 323 switch (pci_mode) { 324 325 case 1: 326 addr = tag.cfg1 | (reg & 0xfc); 327 #ifdef PCI_DEBUG 328 printf ("pci_conf_write(1): addr=%x data=%x\n", 329 addr, data); 330 #endif 331 outl (CONF1_ADDR_PORT, addr); 332 outl (CONF1_DATA_PORT, data); 333 outl (CONF1_ADDR_PORT, 0 ); 334 break; 335 336 case 2: 337 addr = tag.cfg2.port | (reg & 0xfc); 338 #ifdef PCI_DEBUG 339 printf ("pci_conf_write(2): addr=%x data=%x\n", 340 addr, data); 341 #endif 342 outb (CONF2_ENABLE_PORT, tag.cfg2.enable); 343 outb (CONF2_FORWARD_PORT, tag.cfg2.forward); 344 345 outl ((u_short) addr, data); 346 347 outb (CONF2_ENABLE_PORT, 0); 348 outb (CONF2_FORWARD_PORT, 0); 349 break; 350 }; 351 } 352 353 /*----------------------------------------------------------------------- 354 ** 355 ** Register an interupt handler for a pci device. 356 ** 357 **----------------------------------------------------------------------- 358 */ 359 360 #ifndef __FreeBSD2__ 361 /* 362 * Type of the first (asm) part of an interrupt handler. 363 */ 364 typedef void inthand_t __P((u_int cs, u_int ef, u_int esp, u_int ss)); 365 366 /* 367 * Usual type of the second (C) part of an interrupt handler. Some bogus 368 * ones need the arg to be the interrupt frame (and not a copy of it, which 369 * is all that is possible in C). 370 */ 371 typedef void inthand2_t __P((int unit)); 372 373 /* 374 ** XXX @FreeBSD2@ 375 ** 376 ** Unfortunately, the mptr argument is _no_ pointer in 2.0 FreeBSD. 377 ** We would prefer a pointer because it enables us to install 378 ** new interrupt handlers at any time. 379 ** (This is just going to be changed ... <se> :) 380 ** In 2.0 FreeBSD later installed interrupt handlers may change 381 ** the xyz_imask, but this would not be recognized by handlers 382 ** which are installed before. 383 */ 384 385 static int 386 register_intr __P((int intr, int device_id, unsigned int flags, 387 inthand2_t *handler, unsigned int * mptr, int unit)); 388 extern unsigned intr_mask[ICU_LEN]; 389 390 #endif /* !__FreeBSD2__ */ 391 static unsigned int pci_int_mask [16]; 392 393 int pcibus_regint (pcici_t tag, int(*func)(), void* arg, unsigned* maskptr) 394 { 395 int irq; 396 unsigned mask, oldmask; 397 398 irq = PCI_INTERRUPT_LINE_EXTRACT( 399 pci_conf_read (tag, PCI_INTERRUPT_REG)); 400 401 mask = 1ul << irq; 402 403 if (!maskptr) 404 maskptr = &pci_int_mask[irq]; 405 oldmask = *maskptr; 406 407 INTRMASK (*maskptr, mask); 408 409 register_intr( 410 irq, /* isa irq */ 411 0, /* deviced?? */ 412 0, /* flags? */ 413 (inthand2_t*) func, /* handler */ 414 maskptr, /* mask pointer */ 415 (int) arg); /* handler arg */ 416 417 #ifdef __FreeBSD2__ 418 /* 419 ** XXX See comment at beginning of file. 420 ** 421 ** Have to update all the interrupt masks ... Grrrrr!!! 422 */ 423 { 424 unsigned * mp = &intr_mask[0]; 425 /* 426 ** update the isa interrupt masks. 427 */ 428 for (mp=&intr_mask[0]; mp<&intr_mask[ICU_LEN]; mp++) 429 if ((~*mp & oldmask)==0) 430 *mp |= mask; 431 /* 432 ** update the pci interrupt masks. 433 */ 434 for (mp=&pci_int_mask[0]; mp<&pci_int_mask[16]; mp++) 435 if ((~*mp & oldmask)==0) 436 *mp |= mask; 437 }; 438 #endif 439 440 INTREN (mask); 441 442 return (1); 443 } 444