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