10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 51756Sszhou * Common Development and Distribution License (the "License"). 61756Sszhou * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*9149SJudy.Chen@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate /* 270Sstevel@tonic-gate * ISA bus nexus driver 280Sstevel@tonic-gate */ 290Sstevel@tonic-gate 300Sstevel@tonic-gate #include <sys/types.h> 310Sstevel@tonic-gate #include <sys/cmn_err.h> 320Sstevel@tonic-gate #include <sys/conf.h> 330Sstevel@tonic-gate #include <sys/modctl.h> 340Sstevel@tonic-gate #include <sys/autoconf.h> 350Sstevel@tonic-gate #include <sys/errno.h> 360Sstevel@tonic-gate #include <sys/debug.h> 370Sstevel@tonic-gate #include <sys/kmem.h> 382007Sml40262 #include <sys/psm.h> 390Sstevel@tonic-gate #include <sys/ddidmareq.h> 400Sstevel@tonic-gate #include <sys/ddi_impldefs.h> 410Sstevel@tonic-gate #include <sys/dma_engine.h> 420Sstevel@tonic-gate #include <sys/ddi.h> 430Sstevel@tonic-gate #include <sys/sunddi.h> 440Sstevel@tonic-gate #include <sys/sunndi.h> 450Sstevel@tonic-gate #include <sys/acpi/acpi_enum.h> 46*9149SJudy.Chen@Sun.COM #include <sys/mach_intr.h> 47*9149SJudy.Chen@Sun.COM #include <sys/pci.h> 48*9149SJudy.Chen@Sun.COM #include <sys/note.h> 495084Sjohnlev #if defined(__xpv) 505084Sjohnlev #include <sys/hypervisor.h> 515084Sjohnlev #include <sys/evtchn_impl.h> 525084Sjohnlev #endif 535084Sjohnlev 54*9149SJudy.Chen@Sun.COM extern int pseudo_isa; 550Sstevel@tonic-gate extern int isa_resource_setup(void); 56*9149SJudy.Chen@Sun.COM extern int (*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *, 57*9149SJudy.Chen@Sun.COM psm_intr_op_t, int *); 58*9149SJudy.Chen@Sun.COM extern void pci_remove_isa_resources(int, uint32_t, uint32_t); 590Sstevel@tonic-gate static char USED_RESOURCES[] = "used-resources"; 600Sstevel@tonic-gate static void isa_alloc_nodes(dev_info_t *); 612007Sml40262 static void enumerate_BIOS_serial(dev_info_t *); 62*9149SJudy.Chen@Sun.COM static void isa_postattach(dev_info_t *); 632007Sml40262 64*9149SJudy.Chen@Sun.COM /* 65*9149SJudy.Chen@Sun.COM * The following typedef is used to represent an entry in the "ranges" 66*9149SJudy.Chen@Sun.COM * property of a pci-isa bridge device node. 67*9149SJudy.Chen@Sun.COM */ 68*9149SJudy.Chen@Sun.COM typedef struct { 69*9149SJudy.Chen@Sun.COM uint32_t child_high; 70*9149SJudy.Chen@Sun.COM uint32_t child_low; 71*9149SJudy.Chen@Sun.COM uint32_t parent_high; 72*9149SJudy.Chen@Sun.COM uint32_t parent_mid; 73*9149SJudy.Chen@Sun.COM uint32_t parent_low; 74*9149SJudy.Chen@Sun.COM uint32_t size; 75*9149SJudy.Chen@Sun.COM } pib_ranges_t; 76*9149SJudy.Chen@Sun.COM 77*9149SJudy.Chen@Sun.COM typedef struct { 78*9149SJudy.Chen@Sun.COM uint32_t base; 79*9149SJudy.Chen@Sun.COM uint32_t len; 80*9149SJudy.Chen@Sun.COM } used_ranges_t; 81*9149SJudy.Chen@Sun.COM 82*9149SJudy.Chen@Sun.COM #define USED_CELL_SIZE 2 /* 1 byte addr, 1 byte size */ 83*9149SJudy.Chen@Sun.COM #define ISA_ADDR_IO 1 /* IO address space */ 84*9149SJudy.Chen@Sun.COM #define ISA_ADDR_MEM 0 /* memory adress space */ 852007Sml40262 #define BIOS_DATA_AREA 0x400 860Sstevel@tonic-gate /* 870Sstevel@tonic-gate * #define ISA_DEBUG 1 880Sstevel@tonic-gate */ 890Sstevel@tonic-gate 900Sstevel@tonic-gate /* 910Sstevel@tonic-gate * Local data 920Sstevel@tonic-gate */ 930Sstevel@tonic-gate static ddi_dma_lim_t ISA_dma_limits = { 940Sstevel@tonic-gate 0, /* address low */ 950Sstevel@tonic-gate 0x00ffffff, /* address high */ 960Sstevel@tonic-gate 0, /* counter max */ 970Sstevel@tonic-gate 1, /* burstsize */ 980Sstevel@tonic-gate DMA_UNIT_8, /* minimum xfer */ 990Sstevel@tonic-gate 0, /* dma speed */ 1000Sstevel@tonic-gate (uint_t)DMALIM_VER0, /* version */ 1010Sstevel@tonic-gate 0x0000ffff, /* address register */ 1020Sstevel@tonic-gate 0x0000ffff, /* counter register */ 1030Sstevel@tonic-gate 1, /* sector size */ 1040Sstevel@tonic-gate 0x00000001, /* scatter/gather list length */ 1050Sstevel@tonic-gate (uint_t)0xffffffff /* request size */ 1060Sstevel@tonic-gate }; 1070Sstevel@tonic-gate 1080Sstevel@tonic-gate static ddi_dma_attr_t ISA_dma_attr = { 1090Sstevel@tonic-gate DMA_ATTR_V0, 1100Sstevel@tonic-gate (unsigned long long)0, 1110Sstevel@tonic-gate (unsigned long long)0x00ffffff, 1120Sstevel@tonic-gate 0x0000ffff, 1130Sstevel@tonic-gate 1, 1140Sstevel@tonic-gate 1, 1150Sstevel@tonic-gate 1, 1160Sstevel@tonic-gate (unsigned long long)0xffffffff, 1170Sstevel@tonic-gate (unsigned long long)0x0000ffff, 1180Sstevel@tonic-gate 1, 1190Sstevel@tonic-gate 1, 1200Sstevel@tonic-gate 0 1210Sstevel@tonic-gate }; 1220Sstevel@tonic-gate 1230Sstevel@tonic-gate 1240Sstevel@tonic-gate /* 1250Sstevel@tonic-gate * Config information 1260Sstevel@tonic-gate */ 1270Sstevel@tonic-gate 1280Sstevel@tonic-gate static int 129*9149SJudy.Chen@Sun.COM isa_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 130*9149SJudy.Chen@Sun.COM off_t offset, off_t len, caddr_t *vaddrp); 131*9149SJudy.Chen@Sun.COM 132*9149SJudy.Chen@Sun.COM static int 1330Sstevel@tonic-gate isa_dma_allochdl(dev_info_t *, dev_info_t *, ddi_dma_attr_t *, 1340Sstevel@tonic-gate int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *); 1350Sstevel@tonic-gate 1360Sstevel@tonic-gate static int 1370Sstevel@tonic-gate isa_dma_mctl(dev_info_t *, dev_info_t *, ddi_dma_handle_t, enum ddi_dma_ctlops, 1380Sstevel@tonic-gate off_t *, size_t *, caddr_t *, uint_t); 1390Sstevel@tonic-gate 1400Sstevel@tonic-gate static int 1410Sstevel@tonic-gate isa_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, void *); 1420Sstevel@tonic-gate 143*9149SJudy.Chen@Sun.COM static int 144*9149SJudy.Chen@Sun.COM isa_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op, 145*9149SJudy.Chen@Sun.COM ddi_intr_handle_impl_t *hdlp, void *result); 146*9149SJudy.Chen@Sun.COM 1470Sstevel@tonic-gate struct bus_ops isa_bus_ops = { 1480Sstevel@tonic-gate BUSO_REV, 149*9149SJudy.Chen@Sun.COM isa_bus_map, 1500Sstevel@tonic-gate NULL, 1510Sstevel@tonic-gate NULL, 1520Sstevel@tonic-gate NULL, 1530Sstevel@tonic-gate i_ddi_map_fault, 1540Sstevel@tonic-gate ddi_dma_map, 1550Sstevel@tonic-gate isa_dma_allochdl, 1560Sstevel@tonic-gate ddi_dma_freehdl, 1570Sstevel@tonic-gate ddi_dma_bindhdl, 1580Sstevel@tonic-gate ddi_dma_unbindhdl, 1590Sstevel@tonic-gate ddi_dma_flush, 1600Sstevel@tonic-gate ddi_dma_win, 1610Sstevel@tonic-gate isa_dma_mctl, 1620Sstevel@tonic-gate isa_ctlops, 1630Sstevel@tonic-gate ddi_bus_prop_op, 1640Sstevel@tonic-gate NULL, /* (*bus_get_eventcookie)(); */ 1650Sstevel@tonic-gate NULL, /* (*bus_add_eventcall)(); */ 1660Sstevel@tonic-gate NULL, /* (*bus_remove_eventcall)(); */ 1670Sstevel@tonic-gate NULL, /* (*bus_post_event)(); */ 1680Sstevel@tonic-gate NULL, /* (*bus_intr_ctl)(); */ 1690Sstevel@tonic-gate NULL, /* (*bus_config)(); */ 1700Sstevel@tonic-gate NULL, /* (*bus_unconfig)(); */ 1710Sstevel@tonic-gate NULL, /* (*bus_fm_init)(); */ 1720Sstevel@tonic-gate NULL, /* (*bus_fm_fini)(); */ 1730Sstevel@tonic-gate NULL, /* (*bus_fm_access_enter)(); */ 1740Sstevel@tonic-gate NULL, /* (*bus_fm_access_exit)(); */ 1750Sstevel@tonic-gate NULL, /* (*bus_power)(); */ 176*9149SJudy.Chen@Sun.COM isa_intr_ops /* (*bus_intr_op)(); */ 1770Sstevel@tonic-gate }; 1780Sstevel@tonic-gate 1790Sstevel@tonic-gate 1800Sstevel@tonic-gate static int isa_attach(dev_info_t *devi, ddi_attach_cmd_t cmd); 1810Sstevel@tonic-gate 1820Sstevel@tonic-gate /* 1830Sstevel@tonic-gate * Internal isa ctlops support routines 1840Sstevel@tonic-gate */ 1850Sstevel@tonic-gate static int isa_initchild(dev_info_t *child); 1860Sstevel@tonic-gate 1870Sstevel@tonic-gate struct dev_ops isa_ops = { 1880Sstevel@tonic-gate DEVO_REV, /* devo_rev, */ 1890Sstevel@tonic-gate 0, /* refcnt */ 1900Sstevel@tonic-gate ddi_no_info, /* info */ 1910Sstevel@tonic-gate nulldev, /* identify */ 1920Sstevel@tonic-gate nulldev, /* probe */ 1930Sstevel@tonic-gate isa_attach, /* attach */ 194*9149SJudy.Chen@Sun.COM nulldev, /* detach */ 1950Sstevel@tonic-gate nodev, /* reset */ 1960Sstevel@tonic-gate (struct cb_ops *)0, /* driver operations */ 1977656SSherry.Moore@Sun.COM &isa_bus_ops, /* bus operations */ 1987656SSherry.Moore@Sun.COM NULL, /* power */ 1997656SSherry.Moore@Sun.COM ddi_quiesce_not_needed, /* quiesce */ 2000Sstevel@tonic-gate }; 2010Sstevel@tonic-gate 2020Sstevel@tonic-gate /* 2030Sstevel@tonic-gate * Module linkage information for the kernel. 2040Sstevel@tonic-gate */ 2050Sstevel@tonic-gate 2060Sstevel@tonic-gate static struct modldrv modldrv = { 2070Sstevel@tonic-gate &mod_driverops, /* Type of module. This is ISA bus driver */ 2087542SRichard.Bean@Sun.COM "isa nexus driver for 'ISA'", 2090Sstevel@tonic-gate &isa_ops, /* driver ops */ 2100Sstevel@tonic-gate }; 2110Sstevel@tonic-gate 2120Sstevel@tonic-gate static struct modlinkage modlinkage = { 2130Sstevel@tonic-gate MODREV_1, 2140Sstevel@tonic-gate &modldrv, 2150Sstevel@tonic-gate NULL 2160Sstevel@tonic-gate }; 2170Sstevel@tonic-gate 2180Sstevel@tonic-gate int 2190Sstevel@tonic-gate _init(void) 2200Sstevel@tonic-gate { 2210Sstevel@tonic-gate return (mod_install(&modlinkage)); 2220Sstevel@tonic-gate } 2230Sstevel@tonic-gate 2240Sstevel@tonic-gate int 2250Sstevel@tonic-gate _fini(void) 2260Sstevel@tonic-gate { 2270Sstevel@tonic-gate return (mod_remove(&modlinkage)); 2280Sstevel@tonic-gate } 2290Sstevel@tonic-gate 2300Sstevel@tonic-gate int 2310Sstevel@tonic-gate _info(struct modinfo *modinfop) 2320Sstevel@tonic-gate { 2330Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 2340Sstevel@tonic-gate } 2350Sstevel@tonic-gate 2360Sstevel@tonic-gate static int 2370Sstevel@tonic-gate isa_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 2380Sstevel@tonic-gate { 2390Sstevel@tonic-gate int rval; 2400Sstevel@tonic-gate 2415084Sjohnlev #if defined(__xpv) 2425084Sjohnlev /* 2435084Sjohnlev * don't allow isa to attach in domU. this can happen if someone sets 2445084Sjohnlev * the console wrong, etc. ISA devices assume the H/W is there and 2455084Sjohnlev * will cause the domU to panic. 2465084Sjohnlev */ 2475084Sjohnlev if (!DOMAIN_IS_INITDOMAIN(xen_info)) { 2485084Sjohnlev return (DDI_FAILURE); 2495084Sjohnlev } 2505084Sjohnlev #endif 2515084Sjohnlev 252*9149SJudy.Chen@Sun.COM switch (cmd) { 253*9149SJudy.Chen@Sun.COM case DDI_ATTACH: 254*9149SJudy.Chen@Sun.COM break; 255*9149SJudy.Chen@Sun.COM case DDI_RESUME: 256*9149SJudy.Chen@Sun.COM return (DDI_SUCCESS); 257*9149SJudy.Chen@Sun.COM default: 2580Sstevel@tonic-gate return (DDI_FAILURE); 259*9149SJudy.Chen@Sun.COM } 2600Sstevel@tonic-gate 2610Sstevel@tonic-gate if ((rval = i_dmae_init(devi)) == DDI_SUCCESS) { 2620Sstevel@tonic-gate ddi_report_dev(devi); 2630Sstevel@tonic-gate /* 2640Sstevel@tonic-gate * Enumerate children -- invoking ACPICA 2650Sstevel@tonic-gate * This is normally in bus_config(), but we need this 2660Sstevel@tonic-gate * to happen earlier to boot. 2670Sstevel@tonic-gate */ 2680Sstevel@tonic-gate isa_alloc_nodes(devi); 2690Sstevel@tonic-gate } 270*9149SJudy.Chen@Sun.COM 271*9149SJudy.Chen@Sun.COM if (!pseudo_isa) 272*9149SJudy.Chen@Sun.COM isa_postattach(devi); 273*9149SJudy.Chen@Sun.COM 2740Sstevel@tonic-gate return (rval); 2750Sstevel@tonic-gate } 2760Sstevel@tonic-gate 277*9149SJudy.Chen@Sun.COM #define SET_RNGS(rng_p, used_p, ctyp, ptyp) do { \ 278*9149SJudy.Chen@Sun.COM (rng_p)->child_high = (ctyp); \ 279*9149SJudy.Chen@Sun.COM (rng_p)->child_low = (rng_p)->parent_low = (used_p)->base; \ 280*9149SJudy.Chen@Sun.COM (rng_p)->parent_high = (ptyp); \ 281*9149SJudy.Chen@Sun.COM (rng_p)->parent_mid = 0; \ 282*9149SJudy.Chen@Sun.COM (rng_p)->size = (used_p)->len; \ 283*9149SJudy.Chen@Sun.COM _NOTE(CONSTCOND) } while (0) 284*9149SJudy.Chen@Sun.COM static uint_t 285*9149SJudy.Chen@Sun.COM isa_used_to_ranges(int ctype, int *array, uint_t size, pib_ranges_t *ranges) 286*9149SJudy.Chen@Sun.COM { 287*9149SJudy.Chen@Sun.COM used_ranges_t *used_p; 288*9149SJudy.Chen@Sun.COM pib_ranges_t *rng_p = ranges; 289*9149SJudy.Chen@Sun.COM uint_t i, ptype; 290*9149SJudy.Chen@Sun.COM 291*9149SJudy.Chen@Sun.COM ptype = (ctype == ISA_ADDR_IO) ? PCI_ADDR_IO : PCI_ADDR_MEM32; 292*9149SJudy.Chen@Sun.COM ptype |= PCI_REG_REL_M; 293*9149SJudy.Chen@Sun.COM size /= USED_CELL_SIZE; 294*9149SJudy.Chen@Sun.COM used_p = (used_ranges_t *)array; 295*9149SJudy.Chen@Sun.COM SET_RNGS(rng_p, used_p, ctype, ptype); 296*9149SJudy.Chen@Sun.COM for (i = 1, used_p++; i < size; i++, used_p++) { 297*9149SJudy.Chen@Sun.COM /* merge ranges record if applicable */ 298*9149SJudy.Chen@Sun.COM if (rng_p->child_low + rng_p->size == used_p->base) 299*9149SJudy.Chen@Sun.COM rng_p->size += used_p->len; 300*9149SJudy.Chen@Sun.COM else { 301*9149SJudy.Chen@Sun.COM rng_p++; 302*9149SJudy.Chen@Sun.COM SET_RNGS(rng_p, used_p, ctype, ptype); 303*9149SJudy.Chen@Sun.COM } 304*9149SJudy.Chen@Sun.COM } 305*9149SJudy.Chen@Sun.COM return (rng_p - ranges + 1); 306*9149SJudy.Chen@Sun.COM } 307*9149SJudy.Chen@Sun.COM 308*9149SJudy.Chen@Sun.COM void 309*9149SJudy.Chen@Sun.COM isa_remove_res_from_pci(int type, int *array, uint_t size) 310*9149SJudy.Chen@Sun.COM { 311*9149SJudy.Chen@Sun.COM int i; 312*9149SJudy.Chen@Sun.COM used_ranges_t *used_p; 313*9149SJudy.Chen@Sun.COM 314*9149SJudy.Chen@Sun.COM size /= USED_CELL_SIZE; 315*9149SJudy.Chen@Sun.COM used_p = (used_ranges_t *)array; 316*9149SJudy.Chen@Sun.COM for (i = 0; i < size; i++, used_p++) 317*9149SJudy.Chen@Sun.COM pci_remove_isa_resources(type, used_p->base, used_p->len); 318*9149SJudy.Chen@Sun.COM } 319*9149SJudy.Chen@Sun.COM 320*9149SJudy.Chen@Sun.COM static void 321*9149SJudy.Chen@Sun.COM isa_postattach(dev_info_t *dip) 322*9149SJudy.Chen@Sun.COM { 323*9149SJudy.Chen@Sun.COM dev_info_t *used; 324*9149SJudy.Chen@Sun.COM int *ioarray, *memarray, status; 325*9149SJudy.Chen@Sun.COM uint_t nio = 0, nmem = 0, nrng = 0, n; 326*9149SJudy.Chen@Sun.COM pib_ranges_t *ranges; 327*9149SJudy.Chen@Sun.COM 328*9149SJudy.Chen@Sun.COM used = ddi_find_devinfo("used-resources", -1, 0); 329*9149SJudy.Chen@Sun.COM if (used == NULL) { 330*9149SJudy.Chen@Sun.COM cmn_err(CE_WARN, "Failed to find used-resources <%s>\n", 331*9149SJudy.Chen@Sun.COM ddi_get_name(dip)); 332*9149SJudy.Chen@Sun.COM return; 333*9149SJudy.Chen@Sun.COM } 334*9149SJudy.Chen@Sun.COM status = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, used, 335*9149SJudy.Chen@Sun.COM DDI_PROP_DONTPASS, "io-space", &ioarray, &nio); 336*9149SJudy.Chen@Sun.COM if (status != DDI_PROP_SUCCESS && status != DDI_PROP_NOT_FOUND) { 337*9149SJudy.Chen@Sun.COM cmn_err(CE_WARN, "io-space property failure for %s (%x)\n", 338*9149SJudy.Chen@Sun.COM ddi_get_name(used), status); 339*9149SJudy.Chen@Sun.COM return; 340*9149SJudy.Chen@Sun.COM } 341*9149SJudy.Chen@Sun.COM status = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, used, 342*9149SJudy.Chen@Sun.COM DDI_PROP_DONTPASS, "device-memory", &memarray, &nmem); 343*9149SJudy.Chen@Sun.COM if (status != DDI_PROP_SUCCESS && status != DDI_PROP_NOT_FOUND) { 344*9149SJudy.Chen@Sun.COM cmn_err(CE_WARN, "device-memory property failure for %s (%x)\n", 345*9149SJudy.Chen@Sun.COM ddi_get_name(used), status); 346*9149SJudy.Chen@Sun.COM return; 347*9149SJudy.Chen@Sun.COM } 348*9149SJudy.Chen@Sun.COM n = (nio + nmem) / USED_CELL_SIZE; 349*9149SJudy.Chen@Sun.COM ranges = (pib_ranges_t *)kmem_zalloc(sizeof (pib_ranges_t) * n, 350*9149SJudy.Chen@Sun.COM KM_SLEEP); 351*9149SJudy.Chen@Sun.COM 352*9149SJudy.Chen@Sun.COM if (nio != 0) { 353*9149SJudy.Chen@Sun.COM nrng = isa_used_to_ranges(ISA_ADDR_IO, ioarray, nio, ranges); 354*9149SJudy.Chen@Sun.COM isa_remove_res_from_pci(ISA_ADDR_IO, ioarray, nio); 355*9149SJudy.Chen@Sun.COM ddi_prop_free(ioarray); 356*9149SJudy.Chen@Sun.COM } 357*9149SJudy.Chen@Sun.COM if (nmem != 0) { 358*9149SJudy.Chen@Sun.COM nrng += isa_used_to_ranges(ISA_ADDR_MEM, memarray, nmem, 359*9149SJudy.Chen@Sun.COM ranges + nrng); 360*9149SJudy.Chen@Sun.COM isa_remove_res_from_pci(ISA_ADDR_MEM, memarray, nmem); 361*9149SJudy.Chen@Sun.COM ddi_prop_free(memarray); 362*9149SJudy.Chen@Sun.COM } 363*9149SJudy.Chen@Sun.COM 364*9149SJudy.Chen@Sun.COM (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, "ranges", 365*9149SJudy.Chen@Sun.COM (int *)ranges, nrng * sizeof (pib_ranges_t) / sizeof (int)); 366*9149SJudy.Chen@Sun.COM kmem_free(ranges, sizeof (pib_ranges_t) * n); 367*9149SJudy.Chen@Sun.COM } 368*9149SJudy.Chen@Sun.COM 369*9149SJudy.Chen@Sun.COM /*ARGSUSED*/ 370*9149SJudy.Chen@Sun.COM static int 371*9149SJudy.Chen@Sun.COM isa_apply_range(dev_info_t *dip, struct regspec *isa_reg_p, 372*9149SJudy.Chen@Sun.COM pci_regspec_t *pci_reg_p) 373*9149SJudy.Chen@Sun.COM { 374*9149SJudy.Chen@Sun.COM pib_ranges_t *ranges, *rng_p; 375*9149SJudy.Chen@Sun.COM int len, i, offset, nrange; 376*9149SJudy.Chen@Sun.COM static char out_of_range[] = 377*9149SJudy.Chen@Sun.COM "Out of range register specification from device node <%s>"; 378*9149SJudy.Chen@Sun.COM 379*9149SJudy.Chen@Sun.COM if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 380*9149SJudy.Chen@Sun.COM "ranges", (caddr_t)&ranges, &len) != DDI_SUCCESS) { 381*9149SJudy.Chen@Sun.COM cmn_err(CE_WARN, "Can't get %s ranges property", 382*9149SJudy.Chen@Sun.COM ddi_get_name(dip)); 383*9149SJudy.Chen@Sun.COM return (DDI_ME_REGSPEC_RANGE); 384*9149SJudy.Chen@Sun.COM } 385*9149SJudy.Chen@Sun.COM nrange = len / sizeof (pib_ranges_t); 386*9149SJudy.Chen@Sun.COM rng_p = ranges; 387*9149SJudy.Chen@Sun.COM for (i = 0; i < nrange; i++, rng_p++) { 388*9149SJudy.Chen@Sun.COM /* Check for correct space */ 389*9149SJudy.Chen@Sun.COM if (isa_reg_p->regspec_bustype != rng_p->child_high) 390*9149SJudy.Chen@Sun.COM continue; 391*9149SJudy.Chen@Sun.COM 392*9149SJudy.Chen@Sun.COM /* Detect whether request entirely fits within a range */ 393*9149SJudy.Chen@Sun.COM if (isa_reg_p->regspec_addr < rng_p->child_low) 394*9149SJudy.Chen@Sun.COM continue; 395*9149SJudy.Chen@Sun.COM if ((isa_reg_p->regspec_addr + isa_reg_p->regspec_size) > 396*9149SJudy.Chen@Sun.COM (rng_p->child_low + rng_p->size)) 397*9149SJudy.Chen@Sun.COM continue; 398*9149SJudy.Chen@Sun.COM 399*9149SJudy.Chen@Sun.COM offset = isa_reg_p->regspec_addr - rng_p->child_low; 400*9149SJudy.Chen@Sun.COM 401*9149SJudy.Chen@Sun.COM pci_reg_p->pci_phys_hi = rng_p->parent_high; 402*9149SJudy.Chen@Sun.COM pci_reg_p->pci_phys_mid = 0; 403*9149SJudy.Chen@Sun.COM pci_reg_p->pci_phys_low = rng_p->parent_low + offset; 404*9149SJudy.Chen@Sun.COM pci_reg_p->pci_size_hi = 0; 405*9149SJudy.Chen@Sun.COM pci_reg_p->pci_size_low = isa_reg_p->regspec_size; 406*9149SJudy.Chen@Sun.COM 407*9149SJudy.Chen@Sun.COM break; 408*9149SJudy.Chen@Sun.COM } 409*9149SJudy.Chen@Sun.COM kmem_free(ranges, len); 410*9149SJudy.Chen@Sun.COM if (i == nrange) { 411*9149SJudy.Chen@Sun.COM cmn_err(CE_WARN, out_of_range, ddi_get_name(dip)); 412*9149SJudy.Chen@Sun.COM return (DDI_ME_REGSPEC_RANGE); 413*9149SJudy.Chen@Sun.COM } 414*9149SJudy.Chen@Sun.COM 415*9149SJudy.Chen@Sun.COM return (DDI_SUCCESS); 416*9149SJudy.Chen@Sun.COM } 417*9149SJudy.Chen@Sun.COM 418*9149SJudy.Chen@Sun.COM static int 419*9149SJudy.Chen@Sun.COM isa_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 420*9149SJudy.Chen@Sun.COM off_t offset, off_t len, caddr_t *vaddrp) 421*9149SJudy.Chen@Sun.COM { 422*9149SJudy.Chen@Sun.COM struct regspec tmp_reg, *rp; 423*9149SJudy.Chen@Sun.COM pci_regspec_t vreg; 424*9149SJudy.Chen@Sun.COM ddi_map_req_t mr = *mp; /* Get private copy of request */ 425*9149SJudy.Chen@Sun.COM int error; 426*9149SJudy.Chen@Sun.COM 427*9149SJudy.Chen@Sun.COM if (pseudo_isa) 428*9149SJudy.Chen@Sun.COM return (i_ddi_bus_map(dip, rdip, mp, offset, len, vaddrp)); 429*9149SJudy.Chen@Sun.COM 430*9149SJudy.Chen@Sun.COM mp = &mr; 431*9149SJudy.Chen@Sun.COM 432*9149SJudy.Chen@Sun.COM /* 433*9149SJudy.Chen@Sun.COM * First, if given an rnumber, convert it to a regspec... 434*9149SJudy.Chen@Sun.COM */ 435*9149SJudy.Chen@Sun.COM if (mp->map_type == DDI_MT_RNUMBER) { 436*9149SJudy.Chen@Sun.COM 437*9149SJudy.Chen@Sun.COM int rnumber = mp->map_obj.rnumber; 438*9149SJudy.Chen@Sun.COM 439*9149SJudy.Chen@Sun.COM rp = i_ddi_rnumber_to_regspec(rdip, rnumber); 440*9149SJudy.Chen@Sun.COM if (rp == (struct regspec *)0) 441*9149SJudy.Chen@Sun.COM return (DDI_ME_RNUMBER_RANGE); 442*9149SJudy.Chen@Sun.COM 443*9149SJudy.Chen@Sun.COM /* 444*9149SJudy.Chen@Sun.COM * Convert the given ddi_map_req_t from rnumber to regspec... 445*9149SJudy.Chen@Sun.COM */ 446*9149SJudy.Chen@Sun.COM mp->map_type = DDI_MT_REGSPEC; 447*9149SJudy.Chen@Sun.COM mp->map_obj.rp = rp; 448*9149SJudy.Chen@Sun.COM } 449*9149SJudy.Chen@Sun.COM 450*9149SJudy.Chen@Sun.COM /* 451*9149SJudy.Chen@Sun.COM * Adjust offset and length correspnding to called values... 452*9149SJudy.Chen@Sun.COM * XXX: A non-zero length means override the one in the regspec. 453*9149SJudy.Chen@Sun.COM * XXX: (Regardless of what's in the parent's range) 454*9149SJudy.Chen@Sun.COM */ 455*9149SJudy.Chen@Sun.COM 456*9149SJudy.Chen@Sun.COM tmp_reg = *(mp->map_obj.rp); /* Preserve underlying data */ 457*9149SJudy.Chen@Sun.COM rp = mp->map_obj.rp = &tmp_reg; /* Use tmp_reg in request */ 458*9149SJudy.Chen@Sun.COM 459*9149SJudy.Chen@Sun.COM rp->regspec_addr += (uint_t)offset; 460*9149SJudy.Chen@Sun.COM if (len != 0) 461*9149SJudy.Chen@Sun.COM rp->regspec_size = (uint_t)len; 462*9149SJudy.Chen@Sun.COM 463*9149SJudy.Chen@Sun.COM if ((error = isa_apply_range(dip, rp, &vreg)) != 0) 464*9149SJudy.Chen@Sun.COM return (error); 465*9149SJudy.Chen@Sun.COM mp->map_obj.rp = (struct regspec *)&vreg; 466*9149SJudy.Chen@Sun.COM 467*9149SJudy.Chen@Sun.COM /* 468*9149SJudy.Chen@Sun.COM * Call my parents bus_map function with modified values... 469*9149SJudy.Chen@Sun.COM */ 470*9149SJudy.Chen@Sun.COM 471*9149SJudy.Chen@Sun.COM return (ddi_map(dip, mp, (off_t)0, (off_t)0, vaddrp)); 472*9149SJudy.Chen@Sun.COM } 473*9149SJudy.Chen@Sun.COM 4740Sstevel@tonic-gate static int 4750Sstevel@tonic-gate isa_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_attr_t *dma_attr, 4760Sstevel@tonic-gate int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *handlep) 4770Sstevel@tonic-gate { 4780Sstevel@tonic-gate ddi_dma_attr_merge(dma_attr, &ISA_dma_attr); 4790Sstevel@tonic-gate return (ddi_dma_allochdl(dip, rdip, dma_attr, waitfp, arg, handlep)); 4800Sstevel@tonic-gate } 4810Sstevel@tonic-gate 4820Sstevel@tonic-gate static int 4830Sstevel@tonic-gate isa_dma_mctl(dev_info_t *dip, dev_info_t *rdip, 4840Sstevel@tonic-gate ddi_dma_handle_t handle, enum ddi_dma_ctlops request, 4850Sstevel@tonic-gate off_t *offp, size_t *lenp, caddr_t *objp, uint_t flags) 4860Sstevel@tonic-gate { 4870Sstevel@tonic-gate int rval; 4880Sstevel@tonic-gate ddi_dma_lim_t defalt; 4890Sstevel@tonic-gate int arg = (int)(uintptr_t)objp; 4900Sstevel@tonic-gate 4910Sstevel@tonic-gate switch (request) { 4920Sstevel@tonic-gate 4930Sstevel@tonic-gate case DDI_DMA_E_PROG: 4940Sstevel@tonic-gate return (i_dmae_prog(rdip, (struct ddi_dmae_req *)offp, 4950Sstevel@tonic-gate (ddi_dma_cookie_t *)lenp, arg)); 4960Sstevel@tonic-gate 4970Sstevel@tonic-gate case DDI_DMA_E_ACQUIRE: 4980Sstevel@tonic-gate return (i_dmae_acquire(rdip, arg, (int(*)(caddr_t))offp, 4990Sstevel@tonic-gate (caddr_t)lenp)); 5000Sstevel@tonic-gate 5010Sstevel@tonic-gate case DDI_DMA_E_FREE: 5020Sstevel@tonic-gate return (i_dmae_free(rdip, arg)); 5030Sstevel@tonic-gate 5040Sstevel@tonic-gate case DDI_DMA_E_STOP: 5050Sstevel@tonic-gate i_dmae_stop(rdip, arg); 5060Sstevel@tonic-gate return (DDI_SUCCESS); 5070Sstevel@tonic-gate 5080Sstevel@tonic-gate case DDI_DMA_E_ENABLE: 5090Sstevel@tonic-gate i_dmae_enable(rdip, arg); 5100Sstevel@tonic-gate return (DDI_SUCCESS); 5110Sstevel@tonic-gate 5120Sstevel@tonic-gate case DDI_DMA_E_DISABLE: 5130Sstevel@tonic-gate i_dmae_disable(rdip, arg); 5140Sstevel@tonic-gate return (DDI_SUCCESS); 5150Sstevel@tonic-gate 5160Sstevel@tonic-gate case DDI_DMA_E_GETCNT: 5170Sstevel@tonic-gate i_dmae_get_chan_stat(rdip, arg, NULL, (int *)lenp); 5180Sstevel@tonic-gate return (DDI_SUCCESS); 5190Sstevel@tonic-gate 5200Sstevel@tonic-gate case DDI_DMA_E_SWSETUP: 5210Sstevel@tonic-gate return (i_dmae_swsetup(rdip, (struct ddi_dmae_req *)offp, 5220Sstevel@tonic-gate (ddi_dma_cookie_t *)lenp, arg)); 5230Sstevel@tonic-gate 5240Sstevel@tonic-gate case DDI_DMA_E_SWSTART: 5250Sstevel@tonic-gate i_dmae_swstart(rdip, arg); 5260Sstevel@tonic-gate return (DDI_SUCCESS); 5270Sstevel@tonic-gate 5280Sstevel@tonic-gate case DDI_DMA_E_GETLIM: 5290Sstevel@tonic-gate bcopy(&ISA_dma_limits, objp, sizeof (ddi_dma_lim_t)); 5300Sstevel@tonic-gate return (DDI_SUCCESS); 5310Sstevel@tonic-gate 5320Sstevel@tonic-gate case DDI_DMA_E_GETATTR: 5330Sstevel@tonic-gate bcopy(&ISA_dma_attr, objp, sizeof (ddi_dma_attr_t)); 5340Sstevel@tonic-gate return (DDI_SUCCESS); 5350Sstevel@tonic-gate 5360Sstevel@tonic-gate case DDI_DMA_E_1STPTY: 5370Sstevel@tonic-gate { 5380Sstevel@tonic-gate struct ddi_dmae_req req1stpty = 5390Sstevel@tonic-gate { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 5400Sstevel@tonic-gate if (arg == 0) { 5410Sstevel@tonic-gate req1stpty.der_command = DMAE_CMD_TRAN; 5420Sstevel@tonic-gate req1stpty.der_trans = DMAE_TRANS_DMND; 5430Sstevel@tonic-gate } else { 5440Sstevel@tonic-gate req1stpty.der_trans = DMAE_TRANS_CSCD; 5450Sstevel@tonic-gate } 5460Sstevel@tonic-gate return (i_dmae_prog(rdip, &req1stpty, NULL, arg)); 5470Sstevel@tonic-gate } 5480Sstevel@tonic-gate 5490Sstevel@tonic-gate case DDI_DMA_IOPB_ALLOC: /* get contiguous DMA-able memory */ 5500Sstevel@tonic-gate case DDI_DMA_SMEM_ALLOC: 5510Sstevel@tonic-gate if (!offp) { 5520Sstevel@tonic-gate defalt = ISA_dma_limits; 5530Sstevel@tonic-gate offp = (off_t *)&defalt; 5540Sstevel@tonic-gate } 5550Sstevel@tonic-gate /*FALLTHROUGH*/ 5560Sstevel@tonic-gate default: 5570Sstevel@tonic-gate rval = ddi_dma_mctl(dip, rdip, handle, request, offp, 5580Sstevel@tonic-gate lenp, objp, flags); 5590Sstevel@tonic-gate } 5600Sstevel@tonic-gate return (rval); 5610Sstevel@tonic-gate } 5620Sstevel@tonic-gate 5630Sstevel@tonic-gate /* 5640Sstevel@tonic-gate * Check if driver should be treated as an old pre 2.6 driver 5650Sstevel@tonic-gate */ 5660Sstevel@tonic-gate static int 5670Sstevel@tonic-gate old_driver(dev_info_t *dip) 5680Sstevel@tonic-gate { 5690Sstevel@tonic-gate extern int ignore_hardware_nodes; /* force flag from ddi_impl.c */ 5700Sstevel@tonic-gate 5710Sstevel@tonic-gate if (ndi_dev_is_persistent_node(dip)) { 5720Sstevel@tonic-gate if (ignore_hardware_nodes) 5730Sstevel@tonic-gate return (1); 5740Sstevel@tonic-gate if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 5750Sstevel@tonic-gate "ignore-hardware-nodes", -1) != -1) 5760Sstevel@tonic-gate return (1); 5770Sstevel@tonic-gate } 5780Sstevel@tonic-gate return (0); 5790Sstevel@tonic-gate } 5800Sstevel@tonic-gate 5810Sstevel@tonic-gate typedef struct { 5820Sstevel@tonic-gate uint32_t phys_hi; 5830Sstevel@tonic-gate uint32_t phys_lo; 5840Sstevel@tonic-gate uint32_t size; 5850Sstevel@tonic-gate } isa_regs_t; 5860Sstevel@tonic-gate 5870Sstevel@tonic-gate /* 5880Sstevel@tonic-gate * Return non-zero if device in tree is a PnP isa device 5890Sstevel@tonic-gate */ 5900Sstevel@tonic-gate static int 5910Sstevel@tonic-gate is_pnpisa(dev_info_t *dip) 5920Sstevel@tonic-gate { 5930Sstevel@tonic-gate isa_regs_t *isa_regs; 5940Sstevel@tonic-gate int proplen, pnpisa; 5950Sstevel@tonic-gate 5960Sstevel@tonic-gate if (ndi_dev_is_persistent_node(dip) == 0) 5970Sstevel@tonic-gate return (0); 5980Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg", 5995084Sjohnlev (caddr_t)&isa_regs, &proplen) != DDI_PROP_SUCCESS) { 6000Sstevel@tonic-gate return (0); 6010Sstevel@tonic-gate } 6020Sstevel@tonic-gate pnpisa = isa_regs[0].phys_hi & 0x80000000; 6030Sstevel@tonic-gate /* 6040Sstevel@tonic-gate * free the memory allocated by ddi_getlongprop(). 6050Sstevel@tonic-gate */ 6060Sstevel@tonic-gate kmem_free(isa_regs, proplen); 6070Sstevel@tonic-gate if (pnpisa) 6080Sstevel@tonic-gate return (1); 6090Sstevel@tonic-gate else 6100Sstevel@tonic-gate return (0); 6110Sstevel@tonic-gate } 6120Sstevel@tonic-gate 6130Sstevel@tonic-gate /*ARGSUSED*/ 6140Sstevel@tonic-gate static int 6150Sstevel@tonic-gate isa_ctlops(dev_info_t *dip, dev_info_t *rdip, 6160Sstevel@tonic-gate ddi_ctl_enum_t ctlop, void *arg, void *result) 6170Sstevel@tonic-gate { 618*9149SJudy.Chen@Sun.COM int rn; 619*9149SJudy.Chen@Sun.COM struct ddi_parent_private_data *pdp; 620*9149SJudy.Chen@Sun.COM 6210Sstevel@tonic-gate switch (ctlop) { 6220Sstevel@tonic-gate case DDI_CTLOPS_REPORTDEV: 6230Sstevel@tonic-gate if (rdip == (dev_info_t *)0) 6240Sstevel@tonic-gate return (DDI_FAILURE); 6250Sstevel@tonic-gate cmn_err(CE_CONT, "?ISA-device: %s%d\n", 6260Sstevel@tonic-gate ddi_driver_name(rdip), ddi_get_instance(rdip)); 6270Sstevel@tonic-gate return (DDI_SUCCESS); 6280Sstevel@tonic-gate 6290Sstevel@tonic-gate case DDI_CTLOPS_INITCHILD: 6300Sstevel@tonic-gate /* 6310Sstevel@tonic-gate * older drivers aren't expecting the "standard" device 6320Sstevel@tonic-gate * node format used by the hardware nodes. these drivers 6330Sstevel@tonic-gate * only expect their own properties set in their driver.conf 6340Sstevel@tonic-gate * files. so they tell us not to call them with hardware 6350Sstevel@tonic-gate * nodes by setting the property "ignore-hardware-nodes". 6360Sstevel@tonic-gate */ 6370Sstevel@tonic-gate if (old_driver((dev_info_t *)arg)) { 6380Sstevel@tonic-gate return (DDI_NOT_WELL_FORMED); 6390Sstevel@tonic-gate } 6400Sstevel@tonic-gate 6410Sstevel@tonic-gate return (isa_initchild((dev_info_t *)arg)); 6420Sstevel@tonic-gate 6430Sstevel@tonic-gate case DDI_CTLOPS_UNINITCHILD: 6440Sstevel@tonic-gate impl_ddi_sunbus_removechild((dev_info_t *)arg); 6450Sstevel@tonic-gate return (DDI_SUCCESS); 6460Sstevel@tonic-gate 6470Sstevel@tonic-gate case DDI_CTLOPS_SIDDEV: 6480Sstevel@tonic-gate /* 6490Sstevel@tonic-gate * All ISA devices need to do confirming probes 6500Sstevel@tonic-gate * unless they are PnP ISA. 6510Sstevel@tonic-gate */ 6520Sstevel@tonic-gate if (is_pnpisa(dip)) 6530Sstevel@tonic-gate return (DDI_SUCCESS); 6540Sstevel@tonic-gate else 6550Sstevel@tonic-gate return (DDI_FAILURE); 6560Sstevel@tonic-gate 657*9149SJudy.Chen@Sun.COM case DDI_CTLOPS_REGSIZE: 658*9149SJudy.Chen@Sun.COM case DDI_CTLOPS_NREGS: 659*9149SJudy.Chen@Sun.COM if (rdip == (dev_info_t *)0) 660*9149SJudy.Chen@Sun.COM return (DDI_FAILURE); 661*9149SJudy.Chen@Sun.COM 662*9149SJudy.Chen@Sun.COM if ((pdp = ddi_get_parent_data(rdip)) == NULL) 663*9149SJudy.Chen@Sun.COM return (DDI_FAILURE); 664*9149SJudy.Chen@Sun.COM 665*9149SJudy.Chen@Sun.COM if (ctlop == DDI_CTLOPS_NREGS) { 666*9149SJudy.Chen@Sun.COM *(int *)result = pdp->par_nreg; 667*9149SJudy.Chen@Sun.COM } else { 668*9149SJudy.Chen@Sun.COM rn = *(int *)arg; 669*9149SJudy.Chen@Sun.COM if (rn >= pdp->par_nreg) 670*9149SJudy.Chen@Sun.COM return (DDI_FAILURE); 671*9149SJudy.Chen@Sun.COM *(off_t *)result = (off_t)pdp->par_reg[rn].regspec_size; 672*9149SJudy.Chen@Sun.COM } 673*9149SJudy.Chen@Sun.COM return (DDI_SUCCESS); 674*9149SJudy.Chen@Sun.COM 675*9149SJudy.Chen@Sun.COM case DDI_CTLOPS_ATTACH: 676*9149SJudy.Chen@Sun.COM case DDI_CTLOPS_DETACH: 677*9149SJudy.Chen@Sun.COM case DDI_CTLOPS_PEEK: 678*9149SJudy.Chen@Sun.COM case DDI_CTLOPS_POKE: 679*9149SJudy.Chen@Sun.COM return (DDI_FAILURE); 680*9149SJudy.Chen@Sun.COM 6810Sstevel@tonic-gate default: 6820Sstevel@tonic-gate return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 6830Sstevel@tonic-gate } 6840Sstevel@tonic-gate } 6850Sstevel@tonic-gate 686*9149SJudy.Chen@Sun.COM static struct intrspec * 687*9149SJudy.Chen@Sun.COM isa_get_ispec(dev_info_t *rdip, int inum) 688*9149SJudy.Chen@Sun.COM { 689*9149SJudy.Chen@Sun.COM struct ddi_parent_private_data *pdp = ddi_get_parent_data(rdip); 690*9149SJudy.Chen@Sun.COM 691*9149SJudy.Chen@Sun.COM /* Validate the interrupt number */ 692*9149SJudy.Chen@Sun.COM if (inum >= pdp->par_nintr) 693*9149SJudy.Chen@Sun.COM return (NULL); 694*9149SJudy.Chen@Sun.COM 695*9149SJudy.Chen@Sun.COM /* Get the interrupt structure pointer and return that */ 696*9149SJudy.Chen@Sun.COM return ((struct intrspec *)&pdp->par_intr[inum]); 697*9149SJudy.Chen@Sun.COM } 698*9149SJudy.Chen@Sun.COM 699*9149SJudy.Chen@Sun.COM static int 700*9149SJudy.Chen@Sun.COM isa_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op, 701*9149SJudy.Chen@Sun.COM ddi_intr_handle_impl_t *hdlp, void *result) 702*9149SJudy.Chen@Sun.COM { 703*9149SJudy.Chen@Sun.COM struct intrspec *ispec; 704*9149SJudy.Chen@Sun.COM 705*9149SJudy.Chen@Sun.COM if (pseudo_isa) 706*9149SJudy.Chen@Sun.COM return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result)); 707*9149SJudy.Chen@Sun.COM 708*9149SJudy.Chen@Sun.COM 709*9149SJudy.Chen@Sun.COM /* Process the interrupt operation */ 710*9149SJudy.Chen@Sun.COM switch (intr_op) { 711*9149SJudy.Chen@Sun.COM case DDI_INTROP_GETCAP: 712*9149SJudy.Chen@Sun.COM /* First check with pcplusmp */ 713*9149SJudy.Chen@Sun.COM if (psm_intr_ops == NULL) 714*9149SJudy.Chen@Sun.COM return (DDI_FAILURE); 715*9149SJudy.Chen@Sun.COM 716*9149SJudy.Chen@Sun.COM if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_GET_CAP, result)) { 717*9149SJudy.Chen@Sun.COM *(int *)result = 0; 718*9149SJudy.Chen@Sun.COM return (DDI_FAILURE); 719*9149SJudy.Chen@Sun.COM } 720*9149SJudy.Chen@Sun.COM break; 721*9149SJudy.Chen@Sun.COM case DDI_INTROP_SETCAP: 722*9149SJudy.Chen@Sun.COM if (psm_intr_ops == NULL) 723*9149SJudy.Chen@Sun.COM return (DDI_FAILURE); 724*9149SJudy.Chen@Sun.COM 725*9149SJudy.Chen@Sun.COM if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_CAP, result)) 726*9149SJudy.Chen@Sun.COM return (DDI_FAILURE); 727*9149SJudy.Chen@Sun.COM break; 728*9149SJudy.Chen@Sun.COM case DDI_INTROP_ALLOC: 729*9149SJudy.Chen@Sun.COM if ((ispec = isa_get_ispec(rdip, hdlp->ih_inum)) == NULL) 730*9149SJudy.Chen@Sun.COM return (DDI_FAILURE); 731*9149SJudy.Chen@Sun.COM hdlp->ih_pri = ispec->intrspec_pri; 732*9149SJudy.Chen@Sun.COM *(int *)result = hdlp->ih_scratch1; 733*9149SJudy.Chen@Sun.COM break; 734*9149SJudy.Chen@Sun.COM case DDI_INTROP_FREE: 735*9149SJudy.Chen@Sun.COM break; 736*9149SJudy.Chen@Sun.COM case DDI_INTROP_GETPRI: 737*9149SJudy.Chen@Sun.COM if ((ispec = isa_get_ispec(rdip, hdlp->ih_inum)) == NULL) 738*9149SJudy.Chen@Sun.COM return (DDI_FAILURE); 739*9149SJudy.Chen@Sun.COM *(int *)result = ispec->intrspec_pri; 740*9149SJudy.Chen@Sun.COM break; 741*9149SJudy.Chen@Sun.COM case DDI_INTROP_SETPRI: 742*9149SJudy.Chen@Sun.COM /* Validate the interrupt priority passed to us */ 743*9149SJudy.Chen@Sun.COM if (*(int *)result > LOCK_LEVEL) 744*9149SJudy.Chen@Sun.COM return (DDI_FAILURE); 745*9149SJudy.Chen@Sun.COM 746*9149SJudy.Chen@Sun.COM /* Ensure that PSM is all initialized and ispec is ok */ 747*9149SJudy.Chen@Sun.COM if ((psm_intr_ops == NULL) || 748*9149SJudy.Chen@Sun.COM ((ispec = isa_get_ispec(rdip, hdlp->ih_inum)) == NULL)) 749*9149SJudy.Chen@Sun.COM return (DDI_FAILURE); 750*9149SJudy.Chen@Sun.COM 751*9149SJudy.Chen@Sun.COM /* update the ispec with the new priority */ 752*9149SJudy.Chen@Sun.COM ispec->intrspec_pri = *(int *)result; 753*9149SJudy.Chen@Sun.COM break; 754*9149SJudy.Chen@Sun.COM case DDI_INTROP_ADDISR: 755*9149SJudy.Chen@Sun.COM if ((ispec = isa_get_ispec(rdip, hdlp->ih_inum)) == NULL) 756*9149SJudy.Chen@Sun.COM return (DDI_FAILURE); 757*9149SJudy.Chen@Sun.COM ispec->intrspec_func = hdlp->ih_cb_func; 758*9149SJudy.Chen@Sun.COM break; 759*9149SJudy.Chen@Sun.COM case DDI_INTROP_REMISR: 760*9149SJudy.Chen@Sun.COM if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 761*9149SJudy.Chen@Sun.COM return (DDI_FAILURE); 762*9149SJudy.Chen@Sun.COM if ((ispec = isa_get_ispec(rdip, hdlp->ih_inum)) == NULL) 763*9149SJudy.Chen@Sun.COM return (DDI_FAILURE); 764*9149SJudy.Chen@Sun.COM ispec->intrspec_func = (uint_t (*)()) 0; 765*9149SJudy.Chen@Sun.COM break; 766*9149SJudy.Chen@Sun.COM case DDI_INTROP_ENABLE: 767*9149SJudy.Chen@Sun.COM if ((ispec = isa_get_ispec(rdip, hdlp->ih_inum)) == NULL) 768*9149SJudy.Chen@Sun.COM return (DDI_FAILURE); 769*9149SJudy.Chen@Sun.COM 770*9149SJudy.Chen@Sun.COM /* Call psmi to translate irq with the dip */ 771*9149SJudy.Chen@Sun.COM if (psm_intr_ops == NULL) 772*9149SJudy.Chen@Sun.COM return (DDI_FAILURE); 773*9149SJudy.Chen@Sun.COM 774*9149SJudy.Chen@Sun.COM ((ihdl_plat_t *)hdlp->ih_private)->ip_ispecp = ispec; 775*9149SJudy.Chen@Sun.COM (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, 776*9149SJudy.Chen@Sun.COM (int *)&hdlp->ih_vector); 777*9149SJudy.Chen@Sun.COM 778*9149SJudy.Chen@Sun.COM /* Add the interrupt handler */ 779*9149SJudy.Chen@Sun.COM if (!add_avintr((void *)hdlp, ispec->intrspec_pri, 780*9149SJudy.Chen@Sun.COM hdlp->ih_cb_func, DEVI(rdip)->devi_name, hdlp->ih_vector, 781*9149SJudy.Chen@Sun.COM hdlp->ih_cb_arg1, hdlp->ih_cb_arg2, NULL, rdip)) 782*9149SJudy.Chen@Sun.COM return (DDI_FAILURE); 783*9149SJudy.Chen@Sun.COM break; 784*9149SJudy.Chen@Sun.COM case DDI_INTROP_DISABLE: 785*9149SJudy.Chen@Sun.COM if ((ispec = isa_get_ispec(rdip, hdlp->ih_inum)) == NULL) 786*9149SJudy.Chen@Sun.COM return (DDI_FAILURE); 787*9149SJudy.Chen@Sun.COM 788*9149SJudy.Chen@Sun.COM /* Call psm_ops() to translate irq with the dip */ 789*9149SJudy.Chen@Sun.COM if (psm_intr_ops == NULL) 790*9149SJudy.Chen@Sun.COM return (DDI_FAILURE); 791*9149SJudy.Chen@Sun.COM 792*9149SJudy.Chen@Sun.COM ((ihdl_plat_t *)hdlp->ih_private)->ip_ispecp = ispec; 793*9149SJudy.Chen@Sun.COM (void) (*psm_intr_ops)(rdip, hdlp, 794*9149SJudy.Chen@Sun.COM PSM_INTR_OP_XLATE_VECTOR, (int *)&hdlp->ih_vector); 795*9149SJudy.Chen@Sun.COM 796*9149SJudy.Chen@Sun.COM /* Remove the interrupt handler */ 797*9149SJudy.Chen@Sun.COM rem_avintr((void *)hdlp, ispec->intrspec_pri, 798*9149SJudy.Chen@Sun.COM hdlp->ih_cb_func, hdlp->ih_vector); 799*9149SJudy.Chen@Sun.COM break; 800*9149SJudy.Chen@Sun.COM case DDI_INTROP_SETMASK: 801*9149SJudy.Chen@Sun.COM if (psm_intr_ops == NULL) 802*9149SJudy.Chen@Sun.COM return (DDI_FAILURE); 803*9149SJudy.Chen@Sun.COM 804*9149SJudy.Chen@Sun.COM if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_MASK, NULL)) 805*9149SJudy.Chen@Sun.COM return (DDI_FAILURE); 806*9149SJudy.Chen@Sun.COM break; 807*9149SJudy.Chen@Sun.COM case DDI_INTROP_CLRMASK: 808*9149SJudy.Chen@Sun.COM if (psm_intr_ops == NULL) 809*9149SJudy.Chen@Sun.COM return (DDI_FAILURE); 810*9149SJudy.Chen@Sun.COM 811*9149SJudy.Chen@Sun.COM if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_CLEAR_MASK, NULL)) 812*9149SJudy.Chen@Sun.COM return (DDI_FAILURE); 813*9149SJudy.Chen@Sun.COM break; 814*9149SJudy.Chen@Sun.COM case DDI_INTROP_GETPENDING: 815*9149SJudy.Chen@Sun.COM if (psm_intr_ops == NULL) 816*9149SJudy.Chen@Sun.COM return (DDI_FAILURE); 817*9149SJudy.Chen@Sun.COM 818*9149SJudy.Chen@Sun.COM if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_GET_PENDING, 819*9149SJudy.Chen@Sun.COM result)) { 820*9149SJudy.Chen@Sun.COM *(int *)result = 0; 821*9149SJudy.Chen@Sun.COM return (DDI_FAILURE); 822*9149SJudy.Chen@Sun.COM } 823*9149SJudy.Chen@Sun.COM break; 824*9149SJudy.Chen@Sun.COM case DDI_INTROP_NAVAIL: 825*9149SJudy.Chen@Sun.COM case DDI_INTROP_NINTRS: 826*9149SJudy.Chen@Sun.COM *(int *)result = i_ddi_get_intx_nintrs(rdip); 827*9149SJudy.Chen@Sun.COM if (*(int *)result == 0) { 828*9149SJudy.Chen@Sun.COM return (DDI_FAILURE); 829*9149SJudy.Chen@Sun.COM } 830*9149SJudy.Chen@Sun.COM break; 831*9149SJudy.Chen@Sun.COM case DDI_INTROP_SUPPORTED_TYPES: 832*9149SJudy.Chen@Sun.COM *(int *)result = DDI_INTR_TYPE_FIXED; /* Always ... */ 833*9149SJudy.Chen@Sun.COM break; 834*9149SJudy.Chen@Sun.COM default: 835*9149SJudy.Chen@Sun.COM return (DDI_FAILURE); 836*9149SJudy.Chen@Sun.COM } 837*9149SJudy.Chen@Sun.COM 838*9149SJudy.Chen@Sun.COM return (DDI_SUCCESS); 839*9149SJudy.Chen@Sun.COM } 840*9149SJudy.Chen@Sun.COM 8410Sstevel@tonic-gate static void 8420Sstevel@tonic-gate isa_vendor(uint32_t id, char *vendor) 8430Sstevel@tonic-gate { 8440Sstevel@tonic-gate vendor[0] = '@' + ((id >> 26) & 0x1f); 8450Sstevel@tonic-gate vendor[1] = '@' + ((id >> 21) & 0x1f); 8460Sstevel@tonic-gate vendor[2] = '@' + ((id >> 16) & 0x1f); 8470Sstevel@tonic-gate vendor[3] = 0; 8480Sstevel@tonic-gate } 8490Sstevel@tonic-gate 8500Sstevel@tonic-gate /* 8510Sstevel@tonic-gate * Name a child 8520Sstevel@tonic-gate */ 8530Sstevel@tonic-gate static int 8540Sstevel@tonic-gate isa_name_child(dev_info_t *child, char *name, int namelen) 8550Sstevel@tonic-gate { 8560Sstevel@tonic-gate char vendor[8]; 8570Sstevel@tonic-gate int device; 8580Sstevel@tonic-gate uint32_t serial; 8590Sstevel@tonic-gate int func; 8600Sstevel@tonic-gate int bustype; 8610Sstevel@tonic-gate uint32_t base; 8620Sstevel@tonic-gate int proplen; 8630Sstevel@tonic-gate int pnpisa = 0; 8640Sstevel@tonic-gate isa_regs_t *isa_regs; 8650Sstevel@tonic-gate 8660Sstevel@tonic-gate void make_ddi_ppd(dev_info_t *, struct ddi_parent_private_data **); 8670Sstevel@tonic-gate 8680Sstevel@tonic-gate /* 8690Sstevel@tonic-gate * older drivers aren't expecting the "standard" device 8700Sstevel@tonic-gate * node format used by the hardware nodes. these drivers 8710Sstevel@tonic-gate * only expect their own properties set in their driver.conf 8720Sstevel@tonic-gate * files. so they tell us not to call them with hardware 8730Sstevel@tonic-gate * nodes by setting the property "ignore-hardware-nodes". 8740Sstevel@tonic-gate */ 8750Sstevel@tonic-gate if (old_driver(child)) 8760Sstevel@tonic-gate return (DDI_FAILURE); 8770Sstevel@tonic-gate 8780Sstevel@tonic-gate /* 8790Sstevel@tonic-gate * Fill in parent-private data 8800Sstevel@tonic-gate */ 8810Sstevel@tonic-gate if (ddi_get_parent_data(child) == NULL) { 8820Sstevel@tonic-gate struct ddi_parent_private_data *pdptr; 8830Sstevel@tonic-gate make_ddi_ppd(child, &pdptr); 8840Sstevel@tonic-gate ddi_set_parent_data(child, pdptr); 8850Sstevel@tonic-gate } 8860Sstevel@tonic-gate 8870Sstevel@tonic-gate if (ndi_dev_is_persistent_node(child) == 0) { 8880Sstevel@tonic-gate /* 8890Sstevel@tonic-gate * For .conf nodes, generate name from parent private data 8900Sstevel@tonic-gate */ 8910Sstevel@tonic-gate name[0] = '\0'; 8920Sstevel@tonic-gate if (sparc_pd_getnreg(child) > 0) { 8930Sstevel@tonic-gate (void) snprintf(name, namelen, "%x,%x", 8940Sstevel@tonic-gate (uint_t)sparc_pd_getreg(child, 0)->regspec_bustype, 8950Sstevel@tonic-gate (uint_t)sparc_pd_getreg(child, 0)->regspec_addr); 8960Sstevel@tonic-gate } 8970Sstevel@tonic-gate return (DDI_SUCCESS); 8980Sstevel@tonic-gate } 8990Sstevel@tonic-gate 9000Sstevel@tonic-gate /* 9010Sstevel@tonic-gate * For hw nodes, look up "reg" property 9020Sstevel@tonic-gate */ 9030Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg", 9040Sstevel@tonic-gate (caddr_t)&isa_regs, &proplen) != DDI_PROP_SUCCESS) { 9050Sstevel@tonic-gate return (DDI_FAILURE); 9060Sstevel@tonic-gate } 9070Sstevel@tonic-gate 9080Sstevel@tonic-gate /* 9090Sstevel@tonic-gate * extract the device identifications 9100Sstevel@tonic-gate */ 9110Sstevel@tonic-gate pnpisa = isa_regs[0].phys_hi & 0x80000000; 9120Sstevel@tonic-gate if (pnpisa) { 9130Sstevel@tonic-gate isa_vendor(isa_regs[0].phys_hi, vendor); 9140Sstevel@tonic-gate device = isa_regs[0].phys_hi & 0xffff; 9150Sstevel@tonic-gate serial = isa_regs[0].phys_lo; 9160Sstevel@tonic-gate func = (isa_regs[0].size >> 24) & 0xff; 9170Sstevel@tonic-gate if (func != 0) 9180Sstevel@tonic-gate (void) snprintf(name, namelen, "pnp%s,%04x,%x,%x", 9190Sstevel@tonic-gate vendor, device, serial, func); 9200Sstevel@tonic-gate else 9210Sstevel@tonic-gate (void) snprintf(name, namelen, "pnp%s,%04x,%x", 9220Sstevel@tonic-gate vendor, device, serial); 9230Sstevel@tonic-gate } else { 9240Sstevel@tonic-gate bustype = isa_regs[0].phys_hi; 9250Sstevel@tonic-gate base = isa_regs[0].phys_lo; 9260Sstevel@tonic-gate (void) sprintf(name, "%x,%x", bustype, base); 9270Sstevel@tonic-gate } 9280Sstevel@tonic-gate 9290Sstevel@tonic-gate /* 9300Sstevel@tonic-gate * free the memory allocated by ddi_getlongprop(). 9310Sstevel@tonic-gate */ 9320Sstevel@tonic-gate kmem_free(isa_regs, proplen); 9330Sstevel@tonic-gate 9340Sstevel@tonic-gate return (DDI_SUCCESS); 9350Sstevel@tonic-gate } 9360Sstevel@tonic-gate 9370Sstevel@tonic-gate static int 9380Sstevel@tonic-gate isa_initchild(dev_info_t *child) 9390Sstevel@tonic-gate { 9400Sstevel@tonic-gate char name[80]; 9410Sstevel@tonic-gate 9420Sstevel@tonic-gate if (isa_name_child(child, name, 80) != DDI_SUCCESS) 9430Sstevel@tonic-gate return (DDI_FAILURE); 9440Sstevel@tonic-gate ddi_set_name_addr(child, name); 9450Sstevel@tonic-gate 9460Sstevel@tonic-gate if (ndi_dev_is_persistent_node(child) != 0) 9470Sstevel@tonic-gate return (DDI_SUCCESS); 9480Sstevel@tonic-gate 9490Sstevel@tonic-gate /* 9500Sstevel@tonic-gate * This is a .conf node, try merge properties onto a 9510Sstevel@tonic-gate * hw node with the same name. 9520Sstevel@tonic-gate */ 9530Sstevel@tonic-gate if (ndi_merge_node(child, isa_name_child) == DDI_SUCCESS) { 9540Sstevel@tonic-gate /* 9550Sstevel@tonic-gate * Return failure to remove node 9560Sstevel@tonic-gate */ 9570Sstevel@tonic-gate impl_ddi_sunbus_removechild(child); 9580Sstevel@tonic-gate return (DDI_FAILURE); 9590Sstevel@tonic-gate } 9600Sstevel@tonic-gate /* 9610Sstevel@tonic-gate * Cannot merge node, permit pseudo children 9620Sstevel@tonic-gate */ 9630Sstevel@tonic-gate return (DDI_SUCCESS); 9640Sstevel@tonic-gate } 9650Sstevel@tonic-gate 9660Sstevel@tonic-gate /* 9670Sstevel@tonic-gate * called when ACPI enumeration is not used 9680Sstevel@tonic-gate */ 9690Sstevel@tonic-gate static void 9700Sstevel@tonic-gate add_known_used_resources(void) 9710Sstevel@tonic-gate { 9720Sstevel@tonic-gate /* needs to be in increasing order */ 9730Sstevel@tonic-gate int intr[] = {0x1, 0x3, 0x4, 0x6, 0x7, 0xc}; 9740Sstevel@tonic-gate int dma[] = {0x2}; 9750Sstevel@tonic-gate int io[] = {0x60, 0x1, 0x64, 0x1, 0x2f8, 0x8, 0x378, 0x8, 0x3f0, 0x10, 9760Sstevel@tonic-gate 0x778, 0x4}; 9770Sstevel@tonic-gate dev_info_t *usedrdip; 9780Sstevel@tonic-gate 9790Sstevel@tonic-gate usedrdip = ddi_find_devinfo(USED_RESOURCES, -1, 0); 9800Sstevel@tonic-gate 9810Sstevel@tonic-gate if (usedrdip == NULL) { 9820Sstevel@tonic-gate (void) ndi_devi_alloc_sleep(ddi_root_node(), USED_RESOURCES, 983789Sahrens (pnode_t)DEVI_SID_NODEID, &usedrdip); 9840Sstevel@tonic-gate } 9850Sstevel@tonic-gate 9860Sstevel@tonic-gate (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip, 9870Sstevel@tonic-gate "interrupts", (int *)intr, (int)(sizeof (intr) / sizeof (int))); 9880Sstevel@tonic-gate (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip, 9890Sstevel@tonic-gate "io-space", (int *)io, (int)(sizeof (io) / sizeof (int))); 9900Sstevel@tonic-gate (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip, 9910Sstevel@tonic-gate "dma-channels", (int *)dma, (int)(sizeof (dma) / sizeof (int))); 9920Sstevel@tonic-gate (void) ndi_devi_bind_driver(usedrdip, 0); 9930Sstevel@tonic-gate 9940Sstevel@tonic-gate } 9950Sstevel@tonic-gate 9960Sstevel@tonic-gate static void 9970Sstevel@tonic-gate isa_alloc_nodes(dev_info_t *isa_dip) 9980Sstevel@tonic-gate { 9990Sstevel@tonic-gate static int alloced = 0; 10000Sstevel@tonic-gate int circ, i; 10010Sstevel@tonic-gate dev_info_t *xdip; 10020Sstevel@tonic-gate 10030Sstevel@tonic-gate /* hard coded isa stuff */ 10040Sstevel@tonic-gate struct regspec asy_regs[] = { 10050Sstevel@tonic-gate {1, 0x3f8, 0x8}, 10060Sstevel@tonic-gate {1, 0x2f8, 0x8} 10070Sstevel@tonic-gate }; 10080Sstevel@tonic-gate int asy_intrs[] = {0x4, 0x3}; 10090Sstevel@tonic-gate 10100Sstevel@tonic-gate struct regspec i8042_regs[] = { 10110Sstevel@tonic-gate {1, 0x60, 0x1}, 10120Sstevel@tonic-gate {1, 0x64, 0x1} 10130Sstevel@tonic-gate }; 10140Sstevel@tonic-gate int i8042_intrs[] = {0x1, 0xc}; 10150Sstevel@tonic-gate char *acpi_prop; 10160Sstevel@tonic-gate int acpi_enum = 1; /* ACPI is default to be on */ 10170Sstevel@tonic-gate 10180Sstevel@tonic-gate if (alloced) 10190Sstevel@tonic-gate return; 10200Sstevel@tonic-gate 10210Sstevel@tonic-gate ndi_devi_enter(isa_dip, &circ); 10220Sstevel@tonic-gate if (alloced) { /* just in case we are multi-threaded */ 10230Sstevel@tonic-gate ndi_devi_exit(isa_dip, circ); 10240Sstevel@tonic-gate return; 10250Sstevel@tonic-gate } 10260Sstevel@tonic-gate alloced = 1; 10270Sstevel@tonic-gate 10280Sstevel@tonic-gate if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(), 10290Sstevel@tonic-gate DDI_PROP_DONTPASS, "acpi-enum", &acpi_prop) == DDI_PROP_SUCCESS) { 10300Sstevel@tonic-gate acpi_enum = strcmp("off", acpi_prop); 10310Sstevel@tonic-gate ddi_prop_free(acpi_prop); 10320Sstevel@tonic-gate } 10330Sstevel@tonic-gate 10340Sstevel@tonic-gate if (acpi_enum) { 10350Sstevel@tonic-gate if (acpi_isa_device_enum(isa_dip)) { 10360Sstevel@tonic-gate ndi_devi_exit(isa_dip, circ); 10370Sstevel@tonic-gate if (isa_resource_setup() != NDI_SUCCESS) { 10380Sstevel@tonic-gate cmn_err(CE_WARN, "isa nexus: isa " 10390Sstevel@tonic-gate "resource setup failed"); 10400Sstevel@tonic-gate } 10412007Sml40262 10422007Sml40262 /* serial ports? */ 10432007Sml40262 enumerate_BIOS_serial(isa_dip); 10440Sstevel@tonic-gate return; 10450Sstevel@tonic-gate } 10460Sstevel@tonic-gate cmn_err(CE_NOTE, "!Solaris did not detect ACPI BIOS"); 10470Sstevel@tonic-gate } 10480Sstevel@tonic-gate cmn_err(CE_NOTE, "!ACPI is off"); 10490Sstevel@tonic-gate 10500Sstevel@tonic-gate /* serial ports */ 10510Sstevel@tonic-gate for (i = 0; i < 2; i++) { 10525084Sjohnlev #if defined(__xpv) 10535084Sjohnlev /* 10545084Sjohnlev * the hypervisor may be reserving the serial ports for console 10555084Sjohnlev * and/or debug use. Probe the irqs to see if they are 10565084Sjohnlev * available. 10575084Sjohnlev */ 10585084Sjohnlev if (ec_probe_pirq(asy_intrs[i]) == 0) 10595084Sjohnlev continue; /* in use */ 10605084Sjohnlev #endif 10610Sstevel@tonic-gate ndi_devi_alloc_sleep(isa_dip, "asy", 1062789Sahrens (pnode_t)DEVI_SID_NODEID, &xdip); 10630Sstevel@tonic-gate (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip, 10640Sstevel@tonic-gate "reg", (int *)&asy_regs[i], 3); 10650Sstevel@tonic-gate (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, 10660Sstevel@tonic-gate "interrupts", asy_intrs[i]); 10670Sstevel@tonic-gate (void) ndi_devi_bind_driver(xdip, 0); 10680Sstevel@tonic-gate } 10690Sstevel@tonic-gate 10700Sstevel@tonic-gate /* i8042 node */ 10710Sstevel@tonic-gate ndi_devi_alloc_sleep(isa_dip, "i8042", 1072789Sahrens (pnode_t)DEVI_SID_NODEID, &xdip); 10730Sstevel@tonic-gate (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip, 10740Sstevel@tonic-gate "reg", (int *)i8042_regs, 6); 10750Sstevel@tonic-gate (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip, 10760Sstevel@tonic-gate "interrupts", (int *)i8042_intrs, 2); 10770Sstevel@tonic-gate (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 10780Sstevel@tonic-gate "unit-address", "1,60"); 10790Sstevel@tonic-gate (void) ndi_devi_bind_driver(xdip, 0); 10800Sstevel@tonic-gate 10810Sstevel@tonic-gate add_known_used_resources(); 10820Sstevel@tonic-gate 10830Sstevel@tonic-gate ndi_devi_exit(isa_dip, circ); 10840Sstevel@tonic-gate 10850Sstevel@tonic-gate } 10862007Sml40262 10872007Sml40262 /* 10882007Sml40262 * On some machines, serial port 2 isn't listed in the ACPI table. 10892007Sml40262 * This function goes through the BIOS data area and makes sure all 10902007Sml40262 * the serial ports there are in the dev_info tree. If any are missing, 10912007Sml40262 * this function will add them. 10922007Sml40262 */ 10932007Sml40262 10942007Sml40262 static int num_BIOS_serial = 2; /* number of BIOS serial ports to look at */ 10952007Sml40262 10962007Sml40262 static void 10972007Sml40262 enumerate_BIOS_serial(dev_info_t *isa_dip) 10982007Sml40262 { 10992007Sml40262 ushort_t *bios_data; 11002007Sml40262 int i; 11012007Sml40262 dev_info_t *xdip; 11022007Sml40262 int found; 11032007Sml40262 int ret; 11042007Sml40262 struct regspec *tmpregs; 11052007Sml40262 int tmpregs_len; 11062007Sml40262 static struct regspec tmp_asy_regs[] = { 11072007Sml40262 {1, 0x3f8, 0x8}, 11082007Sml40262 }; 11092007Sml40262 static int default_asy_intrs[] = { 4, 3, 4, 3 }; 11102007Sml40262 static size_t size = 4; 11112007Sml40262 11122007Sml40262 /* 11132007Sml40262 * The first four 2-byte quantities of the BIOS data area contain 11142007Sml40262 * the base I/O addresses of the first four serial ports. 11152007Sml40262 */ 11162007Sml40262 bios_data = (ushort_t *)psm_map_new((paddr_t)BIOS_DATA_AREA, size, 11175084Sjohnlev PSM_PROT_READ); 11182007Sml40262 for (i = 0; i < num_BIOS_serial; i++) { 11192007Sml40262 if (bios_data[i] == 0) { 11202007Sml40262 /* no COM[i]: port */ 11212007Sml40262 continue; 11222007Sml40262 } 11232007Sml40262 11242007Sml40262 /* Look for it in the dev_info tree */ 11252007Sml40262 found = 0; 11262007Sml40262 for (xdip = ddi_get_child(isa_dip); xdip != NULL; 11272007Sml40262 xdip = ddi_get_next_sibling(xdip)) { 11282007Sml40262 if (strncmp(ddi_node_name(xdip), "asy", 3) != 0) { 11292007Sml40262 /* skip non asy */ 11302007Sml40262 continue; 11312007Sml40262 } 11322007Sml40262 11332007Sml40262 /* Match by addr */ 11342007Sml40262 ret = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, xdip, 11355084Sjohnlev DDI_PROP_DONTPASS, "reg", (int **)&tmpregs, 11365084Sjohnlev (uint_t *)&tmpregs_len); 11372007Sml40262 if (ret != DDI_PROP_SUCCESS) { 11382007Sml40262 /* error */ 11392007Sml40262 continue; 11402007Sml40262 } 11412007Sml40262 11422007Sml40262 if (tmpregs->regspec_addr == bios_data[i]) 11432007Sml40262 found = 1; 11442007Sml40262 /* 11452007Sml40262 * Free the memory allocated by 11462007Sml40262 * ddi_prop_lookup_int_array(). 11472007Sml40262 */ 11482007Sml40262 ddi_prop_free(tmpregs); 11495084Sjohnlev 11502007Sml40262 } 11512007Sml40262 11522007Sml40262 /* If not found, then add it */ 11532007Sml40262 if (!found) { 11542007Sml40262 ndi_devi_alloc_sleep(isa_dip, "asy", 11552007Sml40262 (pnode_t)DEVI_SID_NODEID, &xdip); 11562007Sml40262 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 11572007Sml40262 "compatible", "PNP0500"); 11582007Sml40262 /* This should be gotten from master file: */ 11592007Sml40262 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 11602007Sml40262 "model", "Standard PC COM port"); 11612007Sml40262 tmp_asy_regs[0].regspec_addr = bios_data[i]; 11622007Sml40262 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip, 11632007Sml40262 "reg", (int *)&tmp_asy_regs[0], 3); 11642007Sml40262 (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, 11652007Sml40262 "interrupts", default_asy_intrs[i]); 11662007Sml40262 (void) ndi_devi_bind_driver(xdip, 0); 11672007Sml40262 } 11682007Sml40262 } 11695084Sjohnlev #if defined(__xpv) 11705084Sjohnlev /* 11715084Sjohnlev * Check each serial port to see if it is in use by the hypervisor. 11725084Sjohnlev * If it is in use, then remove the node from the device tree. 11735084Sjohnlev */ 11745084Sjohnlev i = 0; 11755084Sjohnlev for (xdip = ddi_get_child(isa_dip); xdip != NULL; ) { 11765084Sjohnlev int asy_intr; 11775084Sjohnlev dev_info_t *curdip; 11785084Sjohnlev 11795084Sjohnlev curdip = xdip; 11805084Sjohnlev xdip = ddi_get_next_sibling(xdip); 11815084Sjohnlev if (strncmp(ddi_node_name(curdip), "asy", 3) != 0) { 11825084Sjohnlev /* skip non asy */ 11835084Sjohnlev continue; 11845084Sjohnlev } 11855084Sjohnlev /* 11865084Sjohnlev * Check if the hypervisor is using the serial port by probing 11875084Sjohnlev * the irq and if it is using it remove the node 11885084Sjohnlev * from the device tree 11895084Sjohnlev */ 11905084Sjohnlev asy_intr = ddi_prop_get_int(DDI_DEV_T_ANY, curdip, 11915084Sjohnlev DDI_PROP_DONTPASS, "interrupts", -1); 11925084Sjohnlev if (asy_intr == -1) { 11935084Sjohnlev /* error */ 11945084Sjohnlev continue; 11955084Sjohnlev } 11965084Sjohnlev 11975084Sjohnlev if (ec_probe_pirq(asy_intr)) { 11985084Sjohnlev continue; 11995084Sjohnlev } 12005084Sjohnlev ret = ndi_devi_free(curdip); 12015084Sjohnlev if (ret != DDI_SUCCESS) 12025084Sjohnlev cmn_err(CE_WARN, 12035084Sjohnlev "could not remove asy%d node", i); 12045084Sjohnlev else 12055084Sjohnlev cmn_err(CE_NOTE, "!asy%d unavailable, reserved" 12065084Sjohnlev " to hypervisor", i); 12075084Sjohnlev i++; 12085084Sjohnlev } 12095084Sjohnlev #endif /* __xpv */ 12102007Sml40262 12112007Sml40262 psm_unmap((caddr_t)bios_data, size); 12122007Sml40262 } 1213