pci_cfgreg.c revision 6281
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 63extern int printf(); 64 65static 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 101static int 102pcibus_mode (void); 103 104static pcici_t 105pcibus_tag (u_char bus, u_char device, u_char func); 106 107static u_long 108pcibus_read (pcici_t tag, u_long reg); 109 110static void 111pcibus_write (pcici_t tag, u_long reg, u_long data); 112 113static int 114pcibus_regint (pcici_t tag, int(*func)(), void* arg, unsigned* maskptr); 115 116struct 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 129DATA_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 182static int 183pcibus_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 232static pcici_t 233pcibus_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 267static u_long 268pcibus_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 316static void 317pcibus_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 */ 364typedef 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 */ 371typedef 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 385static int 386register_intr __P((int intr, int device_id, unsigned int flags, 387 inthand2_t *handler, unsigned int * mptr, int unit)); 388extern unsigned intr_mask[ICU_LEN]; 389 390#endif /* !__FreeBSD2__ */ 391static unsigned int pci_int_mask [16]; 392 393int 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