mca_bus.c revision 55890
1/*- 2 * Copyright (c) 1999 Matthew N. Dodd <winter@jurai.net> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: head/sys/dev/mca/mca_bus.c 55890 2000-01-13 09:01:46Z mdodd $ 27 */ 28 29/* 30 * References: 31 * The CMU Mach3 microkernel 32 * NetBSD MCA patches by Scott Telford 33 * Linux MCA code. 34 */ 35 36#include <sys/param.h> 37#include <sys/systm.h> 38#include <sys/queue.h> 39#include <sys/malloc.h> 40#include <sys/kernel.h> 41#include <sys/module.h> 42#include <sys/bus.h> 43 44#include <machine/limits.h> 45#include <machine/bus.h> 46#include <machine/resource.h> 47#include <sys/rman.h> 48 49#include <dev/mca/mca_busreg.h> 50#include <dev/mca/mca_busvar.h> 51 52#include <sys/interrupt.h> 53 54#define MAX_COL 79 55 56static void mca_reg_print (device_t, char *, char *, int *); 57 58struct mca_device { 59 struct resource_list rl; /* Resources */ 60 61 mca_id_t id; 62 u_int8_t slot; 63 u_int8_t enabled; 64 u_int8_t pos[8]; /* Programable Option Select Regs. */ 65}; 66 67/* Not supposed to use this function! */ 68void 69mca_pos_set (dev, reg, data) 70 device_t dev; 71 u_int8_t reg; 72 u_int8_t data; 73{ 74 struct mca_device * m_dev = device_get_ivars(dev); 75 u_int8_t slot = mca_get_slot(dev); 76 77 if ((slot > MCA_MAX_ADAPTERS) || (reg > MCA_POS7)) 78 return; 79 80 /* Disable motherboard setup */ 81 outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS); 82 83 /* Select adapter setup regs */ 84 outb(MCA_ADAP_SETUP_REG, ((slot & 0x0f) | MCA_ADAP_SET)); 85 86 /* Write the register */ 87 outb(MCA_POS_REG(reg), data); 88 89 /* Disable adapter setup */ 90 outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS); 91 92 /* Update the IVAR copy */ 93 m_dev->pos[reg] = data; 94 95 return; 96} 97 98u_int8_t 99mca_pos_get (dev, reg) 100 device_t dev; 101 u_int8_t reg; 102{ 103 u_int8_t slot = mca_get_slot(dev); 104 u_int8_t data = 0; 105 106 if ((slot > MCA_MAX_ADAPTERS) || (reg > MCA_POS7)) 107 return (0); 108 109 /* Disable motherboard setup */ 110 outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS); 111 112 switch (slot) { 113 case MCA_MB_SCSI_SLOT: 114 115 /* Disable adapter setup */ 116 outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS); 117 118 /* Select motherboard video setup regs */ 119 outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_SCSI); 120 121 /* read the register */ 122 data = inb(MCA_POS_REG(reg)); 123 124 /* Disable motherboard setup */ 125 outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS); 126 127 break; 128 case MCA_MB_VIDEO_SLOT: 129 /* Disable adapter setup */ 130 outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS); 131 132 /* Select motherboard scsi setup regs */ 133 outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_VIDEO); 134 135 /* read the register */ 136 data = inb(MCA_POS_REG(reg)); 137 138 /* Disable motherboard setup */ 139 outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS); 140 break; 141 default: 142 143 /* Select adapter setup regs */ 144 outb(MCA_ADAP_SETUP_REG, 145 ((slot & 0x0f) | MCA_ADAP_SET)); 146 147 /* read the register */ 148 data = inb(MCA_POS_REG(reg)); 149 150 /* Disable adapter setup */ 151 outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS); 152 break; 153 } 154 155 return (data); 156} 157 158const char * 159mca_match_id (id, mca_devs) 160 u_int16_t id; 161 struct mca_ident * mca_devs; 162{ 163 struct mca_ident * m = mca_devs; 164 while(m->name != NULL) { 165 if (id == m->id) 166 return (m->name); 167 m++; 168 } 169 return (NULL); 170} 171 172u_int8_t 173mca_pos_read (dev, reg) 174 device_t dev; 175 u_int8_t reg; 176{ 177 struct mca_device * m_dev = device_get_ivars(dev); 178 179 if (reg > MCA_POS7) 180 return (0); 181 182 return (m_dev->pos[reg]); 183} 184 185void 186mca_add_irq (dev, irq) 187 device_t dev; 188 int irq; 189{ 190 struct mca_device * m_dev = device_get_ivars(dev); 191 int rid = 0; 192 193 while (resource_list_find(&(m_dev->rl), SYS_RES_IRQ, rid)) rid++; 194 resource_list_add(&(m_dev->rl), SYS_RES_IRQ, rid, irq, irq, 1); 195 196 return; 197} 198 199void 200mca_add_drq (dev, drq) 201 device_t dev; 202 int drq; 203{ 204 struct mca_device * m_dev = device_get_ivars(dev); 205 int rid = 0; 206 207 while (resource_list_find(&(m_dev->rl), SYS_RES_DRQ, rid)) rid++; 208 resource_list_add(&(m_dev->rl), SYS_RES_DRQ, rid, drq, drq, 1); 209 210 return; 211} 212 213void 214mca_add_mspace (dev, mbase, msize) 215 device_t dev; 216 u_long mbase; 217 u_long msize; 218{ 219 struct mca_device * m_dev = device_get_ivars(dev); 220 int rid = 0; 221 222 while (resource_list_find(&(m_dev->rl), SYS_RES_MEMORY, rid)) rid++; 223 resource_list_add(&(m_dev->rl), SYS_RES_MEMORY, rid, 224 mbase, (mbase + msize), msize); 225 226 return; 227} 228 229void 230mca_add_iospace (dev, iobase, iosize) 231 device_t dev; 232 u_long iobase; 233 u_long iosize; 234{ 235 struct mca_device * m_dev = device_get_ivars(dev); 236 int rid = 0; 237 238 while (resource_list_find(&(m_dev->rl), SYS_RES_IOPORT, rid)) rid++; 239 resource_list_add(&(m_dev->rl), SYS_RES_IOPORT, rid, 240 iobase, (iobase + iosize), iosize); 241 242 return; 243} 244 245static int 246mca_probe (device_t dev) 247{ 248 device_t child; 249 struct mca_device * m_dev = NULL; 250 int devices_found = 0; 251 u_int8_t slot; 252 u_int8_t reg; 253 254 device_set_desc(dev, "MCA bus"); 255 256 /* Disable adapter setup */ 257 outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS); 258 /* Disable motherboard setup */ 259 outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS); 260 261 if (bootverbose) { 262 printf("POS REG 00 01 02 03 04 05 06 07\n"); 263 printf("-----------------------------------\n"); 264 } 265 266 for (slot = 0; slot < MCA_MAX_SLOTS; slot++) { 267 268 if (!m_dev) { 269 m_dev = (struct mca_device *)malloc(sizeof(*m_dev), 270 M_DEVBUF, M_NOWAIT); 271 if (!m_dev) { 272 device_printf(dev, "cannot malloc mca_device"); 273 break; 274 } 275 } 276 bzero(m_dev, sizeof(*m_dev)); 277 278 /* Select adapter setup regs */ 279 outb(MCA_ADAP_SETUP_REG, ((slot & 0x0f) | MCA_ADAP_SET)); 280 281 /* Read the POS registers */ 282 for (reg = MCA_POS0; reg <= MCA_POS7; reg++) { 283 m_dev->pos[reg] = inb(MCA_POS_REG(reg)); 284 } 285 286 /* Disable adapter setup */ 287 outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS); 288 289 if (bootverbose) { 290 printf("mca slot %d:", slot + 1); 291 for (reg = MCA_POS0; reg <= MCA_POS7; reg++) { 292 printf(" %02x", m_dev->pos[reg]); 293 } 294 printf("\n"); 295 } 296 297 m_dev->id = (u_int16_t)m_dev->pos[MCA_POS0] | 298 ((u_int16_t)m_dev->pos[MCA_POS1] << 8); 299 300 if (m_dev->id == 0xffff) { 301 continue; 302 } 303 304 devices_found++; 305 306 m_dev->enabled = (m_dev->pos[MCA_POS2] & MCA_POS2_ENABLE); 307 m_dev->slot = slot; 308 309 resource_list_init(&(m_dev->rl)); 310 311 child = device_add_child(dev, NULL, -1); 312 device_set_ivars(child, m_dev); 313 314 m_dev = NULL; 315 } 316 317 if (m_dev) { 318 free(m_dev, M_DEVBUF); 319 } 320 321 return (devices_found ? 0 : ENXIO); 322} 323 324static void 325mca_reg_print (dev, string, separator, column) 326 device_t dev; 327 char * string; 328 char * separator; 329 int * column; 330{ 331 int length = strlen(string); 332 333 length += (separator ? 2 : 1); 334 335 if (((*column) + length) >= MAX_COL) { 336 printf("\n"); 337 (*column) = 0; 338 } else if ((*column) != 0) { 339 if (separator) { 340 printf("%c", *separator); 341 (*column)++; 342 } 343 printf(" "); 344 (*column)++; 345 } 346 347 if ((*column) == 0) { 348 (*column) += device_printf(dev, "%s", string); 349 } else { 350 (*column) += printf("%s", string); 351 } 352 353 return; 354} 355 356static int 357mca_print_child (device_t dev, device_t child) 358{ 359 char buf[MAX_COL+1]; 360 struct mca_device * m_dev = device_get_ivars(child); 361 int rid; 362 struct resource_list_entry * rle; 363 char separator = ','; 364 int column = 0; 365 int retval = 0; 366 367 if (device_get_desc(child)) { 368 snprintf(buf, sizeof(buf), "<%s>", device_get_desc(child)); 369 mca_reg_print(child, buf, NULL, &column); 370 } 371 372 rid = 0; 373 while ((rle = resource_list_find(&(m_dev->rl), SYS_RES_IOPORT, rid++))) { 374 if (rle->count == 1) { 375 snprintf(buf, sizeof(buf), "%s%lx", 376 ((rid == 1) ? "io 0x" : "0x"), 377 rle->start); 378 } else { 379 snprintf(buf, sizeof(buf), "%s%lx-0x%lx", 380 ((rid == 1) ? "io 0x" : "0x"), 381 rle->start, 382 (rle->start + rle->count)); 383 } 384 mca_reg_print(child, buf, 385 ((rid == 2) ? &separator : NULL), &column); 386 } 387 388 rid = 0; 389 while ((rle = resource_list_find(&(m_dev->rl), SYS_RES_MEMORY, rid++))) { 390 if (rle->count == 1) { 391 snprintf(buf, sizeof(buf), "%s%lx", 392 ((rid == 1) ? "mem 0x" : "0x"), 393 rle->start); 394 } else { 395 snprintf(buf, sizeof(buf), "%s%lx-0x%lx", 396 ((rid == 1) ? "mem 0x" : "0x"), 397 rle->start, 398 (rle->start + rle->count)); 399 } 400 mca_reg_print(child, buf, 401 ((rid == 2) ? &separator : NULL), &column); 402 } 403 404 rid = 0; 405 while ((rle = resource_list_find(&(m_dev->rl), SYS_RES_IRQ, rid++))) { 406 snprintf(buf, sizeof(buf), "irq %ld", rle->start); 407 mca_reg_print(child, buf, 408 ((rid == 1) ? &separator : NULL), &column); 409 } 410 411 rid = 0; 412 while ((rle = resource_list_find(&(m_dev->rl), SYS_RES_DRQ, rid++))) { 413 snprintf(buf, sizeof(buf), "drq %lx", rle->start); 414 mca_reg_print(child, buf, 415 ((rid == 1) ? &separator : NULL), &column); 416 } 417 418 snprintf(buf, sizeof(buf), "on %s id %04x slot %d\n", 419 device_get_nameunit(dev), 420 mca_get_id(child), mca_get_slot(child)+1); 421 mca_reg_print(child, buf, NULL, &column); 422 423 return (retval); 424} 425 426static void 427mca_probe_nomatch (device_t dev, device_t child) 428{ 429 mca_id_t mca_id = mca_get_id(child); 430 u_int8_t slot = mca_get_slot(child); 431 u_int8_t enabled = mca_get_enabled(child); 432 433 device_printf(dev, "unknown card (id 0x%04x, %s) at slot %d\n", 434 mca_id, 435 (enabled ? "enabled" : "disabled"), 436 slot + 1); 437 438 return; 439} 440 441static int 442mca_read_ivar (device_t dev, device_t child, int which, u_long * result) 443{ 444 struct mca_device * m_dev = device_get_ivars(child); 445 446 switch (which) { 447 case MCA_IVAR_SLOT: 448 *result = m_dev->slot; 449 break; 450 case MCA_IVAR_ID: 451 *result = m_dev->id; 452 break; 453 case MCA_IVAR_ENABLED: 454 *result = m_dev->enabled; 455 break; 456 default: 457 return (ENOENT); 458 break; 459 } 460 461 return (0); 462} 463 464static int 465mca_write_ivar(device_t dev, device_t child, int which, uintptr_t value) 466{ 467 return (EINVAL); 468} 469 470static struct resource * 471mca_alloc_resource (device_t dev, device_t child, int type, int *rid, 472 u_long start, u_long end, u_long count, u_int flags) 473{ 474 struct mca_device * m_dev = device_get_ivars(child); 475 struct resource_list_entry * rle; 476 int isdefault; 477 int passthrough; 478 479 isdefault = (start == 0UL && end == ~0UL); 480 passthrough = (device_get_parent(child) != dev); 481 482 if (!passthrough && !isdefault) { 483 rle = resource_list_find(&(m_dev->rl), type, *rid); 484 if (!rle) { 485 resource_list_add(&(m_dev->rl), type, *rid, 486 start, end, count); 487 } 488 } 489 490 return (resource_list_alloc(&(m_dev->rl), dev, child, type, rid, 491 start, end, count, flags)); 492} 493 494static int 495mca_release_resource (device_t dev, device_t child, int type, int rid, 496 struct resource * r) 497{ 498 struct mca_device * m_dev = device_get_ivars(child); 499 500 return (resource_list_release(&(m_dev->rl), dev, child, type, rid, r)); 501} 502 503static int 504mca_get_resource(device_t dev, device_t child, int type, int rid, 505 u_long *startp, u_long *countp) 506{ 507 struct mca_device * m_dev = device_get_ivars(child); 508 struct resource_list * rl = &(m_dev->rl); 509 struct resource_list_entry * rle; 510 511 rle = resource_list_find(rl, type, rid); 512 if (!rle) 513 return ENOENT; 514 515 *startp = rle->start; 516 *countp = rle->count; 517 518 return (0); 519} 520 521static int 522mca_set_resource(device_t dev, device_t child, int type, int rid, 523 u_long start, u_long count) 524{ 525 struct mca_device * m_dev = device_get_ivars(child); 526 struct resource_list * rl = &(m_dev->rl); 527 528 resource_list_add(rl, type, rid, start, start + count - 1, count); 529 return (0); 530} 531 532static void 533mca_delete_resource(device_t dev, device_t child, int type, int rid) 534{ 535 struct mca_device * m_dev = device_get_ivars(child); 536 struct resource_list * rl = &(m_dev->rl); 537 538 resource_list_delete(rl, type, rid); 539} 540 541static device_method_t mca_methods[] = { 542 /* Device interface */ 543 DEVMETHOD(device_probe, mca_probe), 544 DEVMETHOD(device_attach, bus_generic_attach), 545 DEVMETHOD(device_shutdown, bus_generic_shutdown), 546 DEVMETHOD(device_suspend, bus_generic_suspend), 547 DEVMETHOD(device_resume, bus_generic_resume), 548 549 /* Bus interface */ 550 DEVMETHOD(bus_print_child, mca_print_child), 551 DEVMETHOD(bus_probe_nomatch, mca_probe_nomatch), 552 DEVMETHOD(bus_read_ivar, mca_read_ivar), 553 DEVMETHOD(bus_write_ivar, mca_write_ivar), 554 DEVMETHOD(bus_driver_added, bus_generic_driver_added), 555 DEVMETHOD(bus_alloc_resource, mca_alloc_resource), 556 DEVMETHOD(bus_release_resource, mca_release_resource), 557 DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 558 DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 559 DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 560 DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 561 562 DEVMETHOD(bus_set_resource, mca_set_resource), 563 DEVMETHOD(bus_get_resource, mca_get_resource), 564 DEVMETHOD(bus_delete_resource, mca_delete_resource), 565 566 { 0, 0 } 567}; 568 569static driver_t mca_driver = { 570 "mca", 571 mca_methods, 572 1, /* no softc */ 573}; 574 575static devclass_t mca_devclass; 576 577DRIVER_MODULE(mca, nexus, mca_driver, mca_devclass, 0, 0); 578