1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * ISA bus nexus driver 30 */ 31 32 #include <sys/types.h> 33 #include <sys/cmn_err.h> 34 #include <sys/conf.h> 35 #include <sys/modctl.h> 36 #include <sys/autoconf.h> 37 #include <sys/errno.h> 38 #include <sys/debug.h> 39 #include <sys/kmem.h> 40 #include <sys/ddidmareq.h> 41 #include <sys/ddi_impldefs.h> 42 #include <sys/dma_engine.h> 43 #include <sys/ddi.h> 44 #include <sys/sunddi.h> 45 #include <sys/sunndi.h> 46 #include <sys/acpi/acpi_enum.h> 47 48 extern int isa_resource_setup(void); 49 static char USED_RESOURCES[] = "used-resources"; 50 static void isa_alloc_nodes(dev_info_t *); 51 /* 52 * #define ISA_DEBUG 1 53 */ 54 55 /* 56 * Local data 57 */ 58 static ddi_dma_lim_t ISA_dma_limits = { 59 0, /* address low */ 60 0x00ffffff, /* address high */ 61 0, /* counter max */ 62 1, /* burstsize */ 63 DMA_UNIT_8, /* minimum xfer */ 64 0, /* dma speed */ 65 (uint_t)DMALIM_VER0, /* version */ 66 0x0000ffff, /* address register */ 67 0x0000ffff, /* counter register */ 68 1, /* sector size */ 69 0x00000001, /* scatter/gather list length */ 70 (uint_t)0xffffffff /* request size */ 71 }; 72 73 static ddi_dma_attr_t ISA_dma_attr = { 74 DMA_ATTR_V0, 75 (unsigned long long)0, 76 (unsigned long long)0x00ffffff, 77 0x0000ffff, 78 1, 79 1, 80 1, 81 (unsigned long long)0xffffffff, 82 (unsigned long long)0x0000ffff, 83 1, 84 1, 85 0 86 }; 87 88 89 /* 90 * Config information 91 */ 92 93 static int 94 isa_dma_allochdl(dev_info_t *, dev_info_t *, ddi_dma_attr_t *, 95 int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *); 96 97 static int 98 isa_dma_mctl(dev_info_t *, dev_info_t *, ddi_dma_handle_t, enum ddi_dma_ctlops, 99 off_t *, size_t *, caddr_t *, uint_t); 100 101 static int 102 isa_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, void *); 103 104 struct bus_ops isa_bus_ops = { 105 BUSO_REV, 106 i_ddi_bus_map, 107 NULL, 108 NULL, 109 NULL, 110 i_ddi_map_fault, 111 ddi_dma_map, 112 isa_dma_allochdl, 113 ddi_dma_freehdl, 114 ddi_dma_bindhdl, 115 ddi_dma_unbindhdl, 116 ddi_dma_flush, 117 ddi_dma_win, 118 isa_dma_mctl, 119 isa_ctlops, 120 ddi_bus_prop_op, 121 NULL, /* (*bus_get_eventcookie)(); */ 122 NULL, /* (*bus_add_eventcall)(); */ 123 NULL, /* (*bus_remove_eventcall)(); */ 124 NULL, /* (*bus_post_event)(); */ 125 NULL, /* (*bus_intr_ctl)(); */ 126 NULL, /* (*bus_config)(); */ 127 NULL, /* (*bus_unconfig)(); */ 128 NULL, /* (*bus_fm_init)(); */ 129 NULL, /* (*bus_fm_fini)(); */ 130 NULL, /* (*bus_fm_access_enter)(); */ 131 NULL, /* (*bus_fm_access_exit)(); */ 132 NULL, /* (*bus_power)(); */ 133 i_ddi_intr_ops /* (*bus_intr_op)(); */ 134 }; 135 136 137 static int isa_attach(dev_info_t *devi, ddi_attach_cmd_t cmd); 138 139 /* 140 * Internal isa ctlops support routines 141 */ 142 static int isa_initchild(dev_info_t *child); 143 144 struct dev_ops isa_ops = { 145 DEVO_REV, /* devo_rev, */ 146 0, /* refcnt */ 147 ddi_no_info, /* info */ 148 nulldev, /* identify */ 149 nulldev, /* probe */ 150 isa_attach, /* attach */ 151 nodev, /* detach */ 152 nodev, /* reset */ 153 (struct cb_ops *)0, /* driver operations */ 154 &isa_bus_ops /* bus operations */ 155 156 }; 157 158 /* 159 * Module linkage information for the kernel. 160 */ 161 162 static struct modldrv modldrv = { 163 &mod_driverops, /* Type of module. This is ISA bus driver */ 164 "isa nexus driver for 'ISA' %I%", 165 &isa_ops, /* driver ops */ 166 }; 167 168 static struct modlinkage modlinkage = { 169 MODREV_1, 170 &modldrv, 171 NULL 172 }; 173 174 int 175 _init(void) 176 { 177 return (mod_install(&modlinkage)); 178 } 179 180 int 181 _fini(void) 182 { 183 return (mod_remove(&modlinkage)); 184 } 185 186 int 187 _info(struct modinfo *modinfop) 188 { 189 return (mod_info(&modlinkage, modinfop)); 190 } 191 192 static int 193 isa_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 194 { 195 int rval; 196 197 if (cmd != DDI_ATTACH) 198 return (DDI_FAILURE); 199 200 if ((rval = i_dmae_init(devi)) == DDI_SUCCESS) { 201 ddi_report_dev(devi); 202 /* 203 * Enumerate children -- invoking ACPICA 204 * This is normally in bus_config(), but we need this 205 * to happen earlier to boot. 206 */ 207 isa_alloc_nodes(devi); 208 } 209 return (rval); 210 } 211 212 static int 213 isa_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_attr_t *dma_attr, 214 int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *handlep) 215 { 216 ddi_dma_attr_merge(dma_attr, &ISA_dma_attr); 217 return (ddi_dma_allochdl(dip, rdip, dma_attr, waitfp, arg, handlep)); 218 } 219 220 static int 221 isa_dma_mctl(dev_info_t *dip, dev_info_t *rdip, 222 ddi_dma_handle_t handle, enum ddi_dma_ctlops request, 223 off_t *offp, size_t *lenp, caddr_t *objp, uint_t flags) 224 { 225 int rval; 226 ddi_dma_lim_t defalt; 227 int arg = (int)(uintptr_t)objp; 228 229 switch (request) { 230 231 case DDI_DMA_E_PROG: 232 return (i_dmae_prog(rdip, (struct ddi_dmae_req *)offp, 233 (ddi_dma_cookie_t *)lenp, arg)); 234 235 case DDI_DMA_E_ACQUIRE: 236 return (i_dmae_acquire(rdip, arg, (int(*)(caddr_t))offp, 237 (caddr_t)lenp)); 238 239 case DDI_DMA_E_FREE: 240 return (i_dmae_free(rdip, arg)); 241 242 case DDI_DMA_E_STOP: 243 i_dmae_stop(rdip, arg); 244 return (DDI_SUCCESS); 245 246 case DDI_DMA_E_ENABLE: 247 i_dmae_enable(rdip, arg); 248 return (DDI_SUCCESS); 249 250 case DDI_DMA_E_DISABLE: 251 i_dmae_disable(rdip, arg); 252 return (DDI_SUCCESS); 253 254 case DDI_DMA_E_GETCNT: 255 i_dmae_get_chan_stat(rdip, arg, NULL, (int *)lenp); 256 return (DDI_SUCCESS); 257 258 case DDI_DMA_E_SWSETUP: 259 return (i_dmae_swsetup(rdip, (struct ddi_dmae_req *)offp, 260 (ddi_dma_cookie_t *)lenp, arg)); 261 262 case DDI_DMA_E_SWSTART: 263 i_dmae_swstart(rdip, arg); 264 return (DDI_SUCCESS); 265 266 case DDI_DMA_E_GETLIM: 267 bcopy(&ISA_dma_limits, objp, sizeof (ddi_dma_lim_t)); 268 return (DDI_SUCCESS); 269 270 case DDI_DMA_E_GETATTR: 271 bcopy(&ISA_dma_attr, objp, sizeof (ddi_dma_attr_t)); 272 return (DDI_SUCCESS); 273 274 case DDI_DMA_E_1STPTY: 275 { 276 struct ddi_dmae_req req1stpty = 277 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 278 if (arg == 0) { 279 req1stpty.der_command = DMAE_CMD_TRAN; 280 req1stpty.der_trans = DMAE_TRANS_DMND; 281 } else { 282 req1stpty.der_trans = DMAE_TRANS_CSCD; 283 } 284 return (i_dmae_prog(rdip, &req1stpty, NULL, arg)); 285 } 286 287 case DDI_DMA_IOPB_ALLOC: /* get contiguous DMA-able memory */ 288 case DDI_DMA_SMEM_ALLOC: 289 if (!offp) { 290 defalt = ISA_dma_limits; 291 offp = (off_t *)&defalt; 292 } 293 /*FALLTHROUGH*/ 294 default: 295 rval = ddi_dma_mctl(dip, rdip, handle, request, offp, 296 lenp, objp, flags); 297 } 298 return (rval); 299 } 300 301 /* 302 * Check if driver should be treated as an old pre 2.6 driver 303 */ 304 static int 305 old_driver(dev_info_t *dip) 306 { 307 extern int ignore_hardware_nodes; /* force flag from ddi_impl.c */ 308 309 if (ndi_dev_is_persistent_node(dip)) { 310 if (ignore_hardware_nodes) 311 return (1); 312 if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 313 "ignore-hardware-nodes", -1) != -1) 314 return (1); 315 } 316 return (0); 317 } 318 319 typedef struct { 320 uint32_t phys_hi; 321 uint32_t phys_lo; 322 uint32_t size; 323 } isa_regs_t; 324 325 /* 326 * Return non-zero if device in tree is a PnP isa device 327 */ 328 static int 329 is_pnpisa(dev_info_t *dip) 330 { 331 isa_regs_t *isa_regs; 332 int proplen, pnpisa; 333 334 if (ndi_dev_is_persistent_node(dip) == 0) 335 return (0); 336 if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg", 337 (caddr_t)&isa_regs, &proplen) != DDI_PROP_SUCCESS) { 338 return (0); 339 } 340 pnpisa = isa_regs[0].phys_hi & 0x80000000; 341 /* 342 * free the memory allocated by ddi_getlongprop(). 343 */ 344 kmem_free(isa_regs, proplen); 345 if (pnpisa) 346 return (1); 347 else 348 return (0); 349 } 350 351 /*ARGSUSED*/ 352 static int 353 isa_ctlops(dev_info_t *dip, dev_info_t *rdip, 354 ddi_ctl_enum_t ctlop, void *arg, void *result) 355 { 356 switch (ctlop) { 357 case DDI_CTLOPS_REPORTDEV: 358 if (rdip == (dev_info_t *)0) 359 return (DDI_FAILURE); 360 cmn_err(CE_CONT, "?ISA-device: %s%d\n", 361 ddi_driver_name(rdip), ddi_get_instance(rdip)); 362 return (DDI_SUCCESS); 363 364 case DDI_CTLOPS_INITCHILD: 365 /* 366 * older drivers aren't expecting the "standard" device 367 * node format used by the hardware nodes. these drivers 368 * only expect their own properties set in their driver.conf 369 * files. so they tell us not to call them with hardware 370 * nodes by setting the property "ignore-hardware-nodes". 371 */ 372 if (old_driver((dev_info_t *)arg)) { 373 return (DDI_NOT_WELL_FORMED); 374 } 375 376 return (isa_initchild((dev_info_t *)arg)); 377 378 case DDI_CTLOPS_UNINITCHILD: 379 impl_ddi_sunbus_removechild((dev_info_t *)arg); 380 return (DDI_SUCCESS); 381 382 case DDI_CTLOPS_SIDDEV: 383 /* 384 * All ISA devices need to do confirming probes 385 * unless they are PnP ISA. 386 */ 387 if (is_pnpisa(dip)) 388 return (DDI_SUCCESS); 389 else 390 return (DDI_FAILURE); 391 392 default: 393 return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 394 } 395 } 396 397 static void 398 isa_vendor(uint32_t id, char *vendor) 399 { 400 vendor[0] = '@' + ((id >> 26) & 0x1f); 401 vendor[1] = '@' + ((id >> 21) & 0x1f); 402 vendor[2] = '@' + ((id >> 16) & 0x1f); 403 vendor[3] = 0; 404 } 405 406 /* 407 * Name a child 408 */ 409 static int 410 isa_name_child(dev_info_t *child, char *name, int namelen) 411 { 412 char vendor[8]; 413 int device; 414 uint32_t serial; 415 int func; 416 int bustype; 417 uint32_t base; 418 int proplen; 419 int pnpisa = 0; 420 isa_regs_t *isa_regs; 421 422 void make_ddi_ppd(dev_info_t *, struct ddi_parent_private_data **); 423 424 /* 425 * older drivers aren't expecting the "standard" device 426 * node format used by the hardware nodes. these drivers 427 * only expect their own properties set in their driver.conf 428 * files. so they tell us not to call them with hardware 429 * nodes by setting the property "ignore-hardware-nodes". 430 */ 431 if (old_driver(child)) 432 return (DDI_FAILURE); 433 434 /* 435 * Fill in parent-private data 436 */ 437 if (ddi_get_parent_data(child) == NULL) { 438 struct ddi_parent_private_data *pdptr; 439 make_ddi_ppd(child, &pdptr); 440 ddi_set_parent_data(child, pdptr); 441 } 442 443 if (ndi_dev_is_persistent_node(child) == 0) { 444 /* 445 * For .conf nodes, generate name from parent private data 446 */ 447 name[0] = '\0'; 448 if (sparc_pd_getnreg(child) > 0) { 449 (void) snprintf(name, namelen, "%x,%x", 450 (uint_t)sparc_pd_getreg(child, 0)->regspec_bustype, 451 (uint_t)sparc_pd_getreg(child, 0)->regspec_addr); 452 } 453 return (DDI_SUCCESS); 454 } 455 456 /* 457 * For hw nodes, look up "reg" property 458 */ 459 if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg", 460 (caddr_t)&isa_regs, &proplen) != DDI_PROP_SUCCESS) { 461 return (DDI_FAILURE); 462 } 463 464 /* 465 * extract the device identifications 466 */ 467 pnpisa = isa_regs[0].phys_hi & 0x80000000; 468 if (pnpisa) { 469 isa_vendor(isa_regs[0].phys_hi, vendor); 470 device = isa_regs[0].phys_hi & 0xffff; 471 serial = isa_regs[0].phys_lo; 472 func = (isa_regs[0].size >> 24) & 0xff; 473 if (func != 0) 474 (void) snprintf(name, namelen, "pnp%s,%04x,%x,%x", 475 vendor, device, serial, func); 476 else 477 (void) snprintf(name, namelen, "pnp%s,%04x,%x", 478 vendor, device, serial); 479 } else { 480 bustype = isa_regs[0].phys_hi; 481 base = isa_regs[0].phys_lo; 482 (void) sprintf(name, "%x,%x", bustype, base); 483 } 484 485 /* 486 * free the memory allocated by ddi_getlongprop(). 487 */ 488 kmem_free(isa_regs, proplen); 489 490 return (DDI_SUCCESS); 491 } 492 493 static int 494 isa_initchild(dev_info_t *child) 495 { 496 char name[80]; 497 498 if (isa_name_child(child, name, 80) != DDI_SUCCESS) 499 return (DDI_FAILURE); 500 ddi_set_name_addr(child, name); 501 502 if (ndi_dev_is_persistent_node(child) != 0) 503 return (DDI_SUCCESS); 504 505 /* 506 * This is a .conf node, try merge properties onto a 507 * hw node with the same name. 508 */ 509 if (ndi_merge_node(child, isa_name_child) == DDI_SUCCESS) { 510 /* 511 * Return failure to remove node 512 */ 513 impl_ddi_sunbus_removechild(child); 514 return (DDI_FAILURE); 515 } 516 /* 517 * Cannot merge node, permit pseudo children 518 */ 519 return (DDI_SUCCESS); 520 } 521 522 /* 523 * called when ACPI enumeration is not used 524 */ 525 static void 526 add_known_used_resources(void) 527 { 528 /* needs to be in increasing order */ 529 int intr[] = {0x1, 0x3, 0x4, 0x6, 0x7, 0xc}; 530 int dma[] = {0x2}; 531 int io[] = {0x60, 0x1, 0x64, 0x1, 0x2f8, 0x8, 0x378, 0x8, 0x3f0, 0x10, 532 0x778, 0x4}; 533 dev_info_t *usedrdip; 534 535 usedrdip = ddi_find_devinfo(USED_RESOURCES, -1, 0); 536 537 if (usedrdip == NULL) { 538 (void) ndi_devi_alloc_sleep(ddi_root_node(), USED_RESOURCES, 539 (pnode_t)DEVI_SID_NODEID, &usedrdip); 540 } 541 542 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip, 543 "interrupts", (int *)intr, (int)(sizeof (intr) / sizeof (int))); 544 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip, 545 "io-space", (int *)io, (int)(sizeof (io) / sizeof (int))); 546 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip, 547 "dma-channels", (int *)dma, (int)(sizeof (dma) / sizeof (int))); 548 (void) ndi_devi_bind_driver(usedrdip, 0); 549 550 } 551 552 static void 553 isa_alloc_nodes(dev_info_t *isa_dip) 554 { 555 static int alloced = 0; 556 int circ, i; 557 dev_info_t *xdip; 558 559 /* hard coded isa stuff */ 560 struct regspec asy_regs[] = { 561 {1, 0x3f8, 0x8}, 562 {1, 0x2f8, 0x8} 563 }; 564 int asy_intrs[] = {0x4, 0x3}; 565 566 struct regspec i8042_regs[] = { 567 {1, 0x60, 0x1}, 568 {1, 0x64, 0x1} 569 }; 570 int i8042_intrs[] = {0x1, 0xc}; 571 char *acpi_prop; 572 int acpi_enum = 1; /* ACPI is default to be on */ 573 574 if (alloced) 575 return; 576 577 ndi_devi_enter(isa_dip, &circ); 578 if (alloced) { /* just in case we are multi-threaded */ 579 ndi_devi_exit(isa_dip, circ); 580 return; 581 } 582 alloced = 1; 583 584 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(), 585 DDI_PROP_DONTPASS, "acpi-enum", &acpi_prop) == DDI_PROP_SUCCESS) { 586 acpi_enum = strcmp("off", acpi_prop); 587 ddi_prop_free(acpi_prop); 588 } 589 590 if (acpi_enum) { 591 if (acpi_isa_device_enum(isa_dip)) { 592 ndi_devi_exit(isa_dip, circ); 593 if (isa_resource_setup() != NDI_SUCCESS) { 594 cmn_err(CE_WARN, "isa nexus: isa " 595 "resource setup failed"); 596 } 597 return; 598 } 599 cmn_err(CE_NOTE, "!Solaris did not detect ACPI BIOS"); 600 } 601 cmn_err(CE_NOTE, "!ACPI is off"); 602 603 /* serial ports */ 604 for (i = 0; i < 2; i++) { 605 ndi_devi_alloc_sleep(isa_dip, "asy", 606 (pnode_t)DEVI_SID_NODEID, &xdip); 607 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip, 608 "reg", (int *)&asy_regs[i], 3); 609 (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, 610 "interrupts", asy_intrs[i]); 611 (void) ndi_devi_bind_driver(xdip, 0); 612 } 613 614 /* i8042 node */ 615 ndi_devi_alloc_sleep(isa_dip, "i8042", 616 (pnode_t)DEVI_SID_NODEID, &xdip); 617 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip, 618 "reg", (int *)i8042_regs, 6); 619 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip, 620 "interrupts", (int *)i8042_intrs, 2); 621 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 622 "unit-address", "1,60"); 623 (void) ndi_devi_bind_driver(xdip, 0); 624 625 add_known_used_resources(); 626 627 ndi_devi_exit(isa_dip, circ); 628 629 } 630