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 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: releng/11.0/sys/dev/mca/mca_bus.c 297000 2016-03-18 01:28:41Z jhibbits $"); 30 31/* 32 * References: 33 * The CMU Mach3 microkernel 34 * NetBSD MCA patches by Scott Telford 35 * Linux MCA code. 36 */ 37 38#include <sys/param.h> 39#include <sys/systm.h> 40#include <sys/queue.h> 41#include <sys/malloc.h> 42#include <sys/kernel.h> 43#include <sys/limits.h> 44#include <sys/module.h> 45#include <sys/bus.h> 46 47#include <machine/bus.h> 48#include <machine/resource.h> 49#include <sys/rman.h> 50 51#include <dev/mca/mca_busreg.h> 52#include <dev/mca/mca_busvar.h> 53 54#include <sys/interrupt.h> 55 56#define MAX_COL 79 57 58static void mca_reg_print (device_t, char *, char *, int *); 59 60struct mca_device { 61 struct resource_list rl; /* Resources */ 62 63 mca_id_t id; 64 u_int8_t slot; 65 u_int8_t enabled; 66 u_int8_t pos[8]; /* Programable Option Select Regs. */ 67}; 68 69/* Not supposed to use this function! */ 70void 71mca_pos_set (device_t dev, u_int8_t reg, u_int8_t data) 72{ 73 struct mca_device * m_dev = device_get_ivars(dev); 74 u_int8_t slot = mca_get_slot(dev); 75 76 if ((slot > MCA_MAX_ADAPTERS) || (reg > MCA_POS7)) 77 return; 78 79 /* Disable motherboard setup */ 80 outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS); 81 82 /* Select adapter setup regs */ 83 outb(MCA_ADAP_SETUP_REG, ((slot & 0x0f) | MCA_ADAP_SET)); 84 85 /* Write the register */ 86 outb(MCA_POS_REG(reg), data); 87 88 /* Disable adapter setup */ 89 outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS); 90 91 /* Update the IVAR copy */ 92 m_dev->pos[reg] = data; 93 94 return; 95} 96 97u_int8_t 98mca_pos_get (device_t dev, u_int8_t reg) 99{ 100 u_int8_t slot = mca_get_slot(dev); 101 u_int8_t data = 0; 102 103 if ((slot > MCA_MAX_ADAPTERS) || (reg > MCA_POS7)) 104 return (0); 105 106 /* Disable motherboard setup */ 107 outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS); 108 109 switch (slot) { 110 case MCA_MB_SCSI_SLOT: 111 112 /* Disable adapter setup */ 113 outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS); 114 115 /* Select motherboard video setup regs */ 116 outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_SCSI); 117 118 /* read the register */ 119 data = inb(MCA_POS_REG(reg)); 120 121 /* Disable motherboard setup */ 122 outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS); 123 124 break; 125 case MCA_MB_VIDEO_SLOT: 126 /* Disable adapter setup */ 127 outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS); 128 129 /* Select motherboard scsi setup regs */ 130 outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_VIDEO); 131 132 /* read the register */ 133 data = inb(MCA_POS_REG(reg)); 134 135 /* Disable motherboard setup */ 136 outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS); 137 break; 138 default: 139 140 /* Select adapter setup regs */ 141 outb(MCA_ADAP_SETUP_REG, 142 ((slot & 0x0f) | MCA_ADAP_SET)); 143 144 /* read the register */ 145 data = inb(MCA_POS_REG(reg)); 146 147 /* Disable adapter setup */ 148 outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS); 149 break; 150 } 151 152 return (data); 153} 154 155const char * 156mca_match_id (u_int16_t id, struct mca_ident *mca_devs) 157{ 158 struct mca_ident * m = mca_devs; 159 while(m->name != NULL) { 160 if (id == m->id) 161 return (m->name); 162 m++; 163 } 164 return (NULL); 165} 166 167u_int8_t 168mca_pos_read (device_t dev, u_int8_t reg) 169{ 170 struct mca_device * m_dev = device_get_ivars(dev); 171 172 if (reg > MCA_POS7) 173 return (0); 174 175 return (m_dev->pos[reg]); 176} 177 178void 179mca_add_irq (dev, irq) 180 device_t dev; 181 int irq; 182{ 183 struct mca_device * m_dev = device_get_ivars(dev); 184 int rid = 0; 185 186 while (resource_list_find(&(m_dev->rl), SYS_RES_IRQ, rid)) rid++; 187 resource_list_add(&(m_dev->rl), SYS_RES_IRQ, rid, irq, irq, 1); 188 189 return; 190} 191 192void 193mca_add_drq (dev, drq) 194 device_t dev; 195 int drq; 196{ 197 struct mca_device * m_dev = device_get_ivars(dev); 198 int rid = 0; 199 200 while (resource_list_find(&(m_dev->rl), SYS_RES_DRQ, rid)) rid++; 201 resource_list_add(&(m_dev->rl), SYS_RES_DRQ, rid, drq, drq, 1); 202 203 return; 204} 205 206void 207mca_add_mspace (dev, mbase, msize) 208 device_t dev; 209 u_long mbase; 210 u_long msize; 211{ 212 struct mca_device * m_dev = device_get_ivars(dev); 213 int rid = 0; 214 215 while (resource_list_find(&(m_dev->rl), SYS_RES_MEMORY, rid)) rid++; 216 resource_list_add(&(m_dev->rl), SYS_RES_MEMORY, rid, 217 mbase, (mbase + msize), msize); 218 219 return; 220} 221 222void 223mca_add_iospace (dev, iobase, iosize) 224 device_t dev; 225 u_long iobase; 226 u_long iosize; 227{ 228 struct mca_device * m_dev = device_get_ivars(dev); 229 int rid = 0; 230 231 while (resource_list_find(&(m_dev->rl), SYS_RES_IOPORT, rid)) rid++; 232 resource_list_add(&(m_dev->rl), SYS_RES_IOPORT, rid, 233 iobase, (iobase + iosize), iosize); 234 235 return; 236} 237 238static int 239mca_probe (device_t dev) 240{ 241 device_t child; 242 struct mca_device * m_dev = NULL; 243 int devices_found = 0; 244 u_int8_t slot; 245 u_int8_t reg; 246 247 device_set_desc(dev, "MCA bus"); 248 249 /* Disable adapter setup */ 250 outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS); 251 /* Disable motherboard setup */ 252 outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS); 253 254 if (bootverbose) { 255 printf("POS REG 00 01 02 03 04 05 06 07\n"); 256 printf("-----------------------------------\n"); 257 } 258 259 for (slot = 0; slot < MCA_MAX_SLOTS; slot++) { 260 261 if (!m_dev) { 262 m_dev = (struct mca_device *)malloc(sizeof(*m_dev), 263 M_DEVBUF, M_NOWAIT); 264 if (!m_dev) { 265 device_printf(dev, "cannot malloc mca_device"); 266 break; 267 } 268 } 269 bzero(m_dev, sizeof(*m_dev)); 270 271 /* Select adapter setup regs */ 272 outb(MCA_ADAP_SETUP_REG, ((slot & 0x0f) | MCA_ADAP_SET)); 273 274 /* Read the POS registers */ 275 for (reg = MCA_POS0; reg <= MCA_POS7; reg++) { 276 m_dev->pos[reg] = inb(MCA_POS_REG(reg)); 277 } 278 279 /* Disable adapter setup */ 280 outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS); 281 282 if (bootverbose) { 283 printf("mca slot %d:", slot + 1); 284 for (reg = MCA_POS0; reg <= MCA_POS7; reg++) { 285 printf(" %02x", m_dev->pos[reg]); 286 } 287 printf("\n"); 288 } 289 290 m_dev->id = (u_int16_t)m_dev->pos[MCA_POS0] | 291 ((u_int16_t)m_dev->pos[MCA_POS1] << 8); 292 293 if (m_dev->id == 0xffff) { 294 continue; 295 } 296 297 devices_found++; 298 299 m_dev->enabled = (m_dev->pos[MCA_POS2] & MCA_POS2_ENABLE); 300 m_dev->slot = slot; 301 302 resource_list_init(&(m_dev->rl)); 303 304 child = device_add_child(dev, NULL, -1); 305 device_set_ivars(child, m_dev); 306 307 m_dev = NULL; 308 } 309 310 if (m_dev) { 311 free(m_dev, M_DEVBUF); 312 } 313 314 return (devices_found ? 0 : ENXIO); 315} 316 317static void 318mca_reg_print (dev, string, separator, column) 319 device_t dev; 320 char * string; 321 char * separator; 322 int * column; 323{ 324 int length = strlen(string); 325 326 length += (separator ? 2 : 1); 327 328 if (((*column) + length) >= MAX_COL) { 329 printf("\n"); 330 (*column) = 0; 331 } else if ((*column) != 0) { 332 if (separator) { 333 printf("%c", *separator); 334 (*column)++; 335 } 336 printf(" "); 337 (*column)++; 338 } 339 340 if ((*column) == 0) { 341 (*column) += device_printf(dev, "%s", string); 342 } else { 343 (*column) += printf("%s", string); 344 } 345 346 return; 347} 348 349static int 350mca_print_child (device_t dev, device_t child) 351{ 352 char buf[MAX_COL+1]; 353 struct mca_device * m_dev = device_get_ivars(child); 354 int rid; 355 struct resource_list_entry * rle; 356 char separator = ','; 357 int column = 0; 358 int retval = 0; 359 360 if (device_get_desc(child)) { 361 snprintf(buf, sizeof(buf), "<%s>", device_get_desc(child)); 362 mca_reg_print(child, buf, NULL, &column); 363 } 364 365 rid = 0; 366 while ((rle = resource_list_find(&(m_dev->rl), SYS_RES_IOPORT, rid++))) { 367 if (rle->count == 1) { 368 snprintf(buf, sizeof(buf), "%s%jx", 369 ((rid == 1) ? "io 0x" : "0x"), 370 rle->start); 371 } else { 372 snprintf(buf, sizeof(buf), "%s%jx-0x%jx", 373 ((rid == 1) ? "io 0x" : "0x"), 374 rle->start, 375 (rle->start + rle->count)); 376 } 377 mca_reg_print(child, buf, 378 ((rid == 2) ? &separator : NULL), &column); 379 } 380 381 rid = 0; 382 while ((rle = resource_list_find(&(m_dev->rl), SYS_RES_MEMORY, rid++))) { 383 if (rle->count == 1) { 384 snprintf(buf, sizeof(buf), "%s%jx", 385 ((rid == 1) ? "mem 0x" : "0x"), 386 rle->start); 387 } else { 388 snprintf(buf, sizeof(buf), "%s%jx-0x%jx", 389 ((rid == 1) ? "mem 0x" : "0x"), 390 rle->start, 391 (rle->start + rle->count)); 392 } 393 mca_reg_print(child, buf, 394 ((rid == 2) ? &separator : NULL), &column); 395 } 396 397 rid = 0; 398 while ((rle = resource_list_find(&(m_dev->rl), SYS_RES_IRQ, rid++))) { 399 snprintf(buf, sizeof(buf), "irq %jd", rle->start); 400 mca_reg_print(child, buf, 401 ((rid == 1) ? &separator : NULL), &column); 402 } 403 404 rid = 0; 405 while ((rle = resource_list_find(&(m_dev->rl), SYS_RES_DRQ, rid++))) { 406 snprintf(buf, sizeof(buf), "drq %jx", rle->start); 407 mca_reg_print(child, buf, 408 ((rid == 1) ? &separator : NULL), &column); 409 } 410 411 snprintf(buf, sizeof(buf), "on %s id %04x slot %d\n", 412 device_get_nameunit(dev), 413 mca_get_id(child), mca_get_slot(child)+1); 414 mca_reg_print(child, buf, NULL, &column); 415 416 return (retval); 417} 418 419static void 420mca_probe_nomatch (device_t dev, device_t child) 421{ 422 mca_id_t mca_id = mca_get_id(child); 423 u_int8_t slot = mca_get_slot(child); 424 u_int8_t enabled = mca_get_enabled(child); 425 426 device_printf(dev, "unknown card (id 0x%04x, %s) at slot %d\n", 427 mca_id, 428 (enabled ? "enabled" : "disabled"), 429 slot + 1); 430 431 return; 432} 433 434static int 435mca_read_ivar (device_t dev, device_t child, int which, uintptr_t * result) 436{ 437 struct mca_device * m_dev = device_get_ivars(child); 438 439 switch (which) { 440 case MCA_IVAR_SLOT: 441 *result = m_dev->slot; 442 break; 443 case MCA_IVAR_ID: 444 *result = m_dev->id; 445 break; 446 case MCA_IVAR_ENABLED: 447 *result = m_dev->enabled; 448 break; 449 default: 450 return (ENOENT); 451 break; 452 } 453 454 return (0); 455} 456 457static struct resource * 458mca_alloc_resource (device_t dev, device_t child, int type, int *rid, 459 rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) 460{ 461 struct mca_device * m_dev = device_get_ivars(child); 462 struct resource_list_entry * rle; 463 int isdefault; 464 int passthrough; 465 466 isdefault = RMAN_IS_DEFAULT_RANGE(start, end); 467 passthrough = (device_get_parent(child) != dev); 468 469 if (!passthrough && !isdefault) { 470 rle = resource_list_find(&(m_dev->rl), type, *rid); 471 if (!rle) { 472 resource_list_add(&(m_dev->rl), type, *rid, 473 start, end, count); 474 } 475 } 476 477 if (type == SYS_RES_IRQ) { 478 flags |= RF_SHAREABLE; 479 } 480 481 return (resource_list_alloc(&(m_dev->rl), dev, child, type, rid, 482 start, end, count, flags)); 483} 484 485static struct resource_list * 486mca_get_resource_list (device_t dev, device_t child) 487{ 488 struct mca_device * m_dev = device_get_ivars(child); 489 struct resource_list * rl = &m_dev->rl; 490 491 if (!rl) 492 return (NULL); 493 494 return (rl); 495} 496 497static device_method_t mca_methods[] = { 498 /* Device interface */ 499 DEVMETHOD(device_probe, mca_probe), 500 DEVMETHOD(device_attach, bus_generic_attach), 501 DEVMETHOD(device_shutdown, bus_generic_shutdown), 502 DEVMETHOD(device_suspend, bus_generic_suspend), 503 DEVMETHOD(device_resume, bus_generic_resume), 504 505 /* Bus interface */ 506 DEVMETHOD(bus_print_child, mca_print_child), 507 DEVMETHOD(bus_probe_nomatch, mca_probe_nomatch), 508 DEVMETHOD(bus_read_ivar, mca_read_ivar), 509 DEVMETHOD(bus_write_ivar, bus_generic_write_ivar), 510 DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 511 DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 512 513 DEVMETHOD(bus_get_resource_list,mca_get_resource_list), 514 DEVMETHOD(bus_alloc_resource, mca_alloc_resource), 515 DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource), 516 DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), 517 DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), 518 DEVMETHOD(bus_delete_resource, bus_generic_rl_delete_resource), 519 DEVMETHOD(bus_activate_resource,bus_generic_activate_resource), 520 DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 521 522 DEVMETHOD_END 523}; 524 525static driver_t mca_driver = { 526 "mca", 527 mca_methods, 528 1, /* no softc */ 529}; 530 531static devclass_t mca_devclass; 532 533DRIVER_MODULE(mca, legacy, mca_driver, mca_devclass, 0, 0); 534