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 /* 229149SJudy.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> 469149SJudy.Chen@Sun.COM #include <sys/mach_intr.h> 479149SJudy.Chen@Sun.COM #include <sys/pci.h> 489149SJudy.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 549149SJudy.Chen@Sun.COM extern int pseudo_isa; 550Sstevel@tonic-gate extern int isa_resource_setup(void); 569149SJudy.Chen@Sun.COM extern int (*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *, 579149SJudy.Chen@Sun.COM psm_intr_op_t, int *); 58*9226SJudy.Chen@Sun.COM extern void pci_register_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*9226SJudy.Chen@Sun.COM static void adjust_prtsz(dev_info_t *isa_dip); 639149SJudy.Chen@Sun.COM static void isa_postattach(dev_info_t *); 642007Sml40262 659149SJudy.Chen@Sun.COM /* 669149SJudy.Chen@Sun.COM * The following typedef is used to represent an entry in the "ranges" 679149SJudy.Chen@Sun.COM * property of a pci-isa bridge device node. 689149SJudy.Chen@Sun.COM */ 699149SJudy.Chen@Sun.COM typedef struct { 709149SJudy.Chen@Sun.COM uint32_t child_high; 719149SJudy.Chen@Sun.COM uint32_t child_low; 729149SJudy.Chen@Sun.COM uint32_t parent_high; 739149SJudy.Chen@Sun.COM uint32_t parent_mid; 749149SJudy.Chen@Sun.COM uint32_t parent_low; 759149SJudy.Chen@Sun.COM uint32_t size; 769149SJudy.Chen@Sun.COM } pib_ranges_t; 779149SJudy.Chen@Sun.COM 789149SJudy.Chen@Sun.COM typedef struct { 799149SJudy.Chen@Sun.COM uint32_t base; 809149SJudy.Chen@Sun.COM uint32_t len; 819149SJudy.Chen@Sun.COM } used_ranges_t; 829149SJudy.Chen@Sun.COM 839149SJudy.Chen@Sun.COM #define USED_CELL_SIZE 2 /* 1 byte addr, 1 byte size */ 849149SJudy.Chen@Sun.COM #define ISA_ADDR_IO 1 /* IO address space */ 859149SJudy.Chen@Sun.COM #define ISA_ADDR_MEM 0 /* memory adress space */ 862007Sml40262 #define BIOS_DATA_AREA 0x400 870Sstevel@tonic-gate /* 880Sstevel@tonic-gate * #define ISA_DEBUG 1 890Sstevel@tonic-gate */ 900Sstevel@tonic-gate 910Sstevel@tonic-gate /* 92*9226SJudy.Chen@Sun.COM * For serial ports not enumerated by ACPI, and parallel ports with 93*9226SJudy.Chen@Sun.COM * illegal size. Typically, a system can have as many as 4 serial 94*9226SJudy.Chen@Sun.COM * ports and 3 parallel ports. 95*9226SJudy.Chen@Sun.COM */ 96*9226SJudy.Chen@Sun.COM #define MAX_EXTRA_RESOURCE 7 97*9226SJudy.Chen@Sun.COM static struct regspec isa_extra_resource[MAX_EXTRA_RESOURCE]; 98*9226SJudy.Chen@Sun.COM static int isa_extra_count = 0; 99*9226SJudy.Chen@Sun.COM 100*9226SJudy.Chen@Sun.COM /* 1010Sstevel@tonic-gate * Local data 1020Sstevel@tonic-gate */ 1030Sstevel@tonic-gate static ddi_dma_lim_t ISA_dma_limits = { 1040Sstevel@tonic-gate 0, /* address low */ 1050Sstevel@tonic-gate 0x00ffffff, /* address high */ 1060Sstevel@tonic-gate 0, /* counter max */ 1070Sstevel@tonic-gate 1, /* burstsize */ 1080Sstevel@tonic-gate DMA_UNIT_8, /* minimum xfer */ 1090Sstevel@tonic-gate 0, /* dma speed */ 1100Sstevel@tonic-gate (uint_t)DMALIM_VER0, /* version */ 1110Sstevel@tonic-gate 0x0000ffff, /* address register */ 1120Sstevel@tonic-gate 0x0000ffff, /* counter register */ 1130Sstevel@tonic-gate 1, /* sector size */ 1140Sstevel@tonic-gate 0x00000001, /* scatter/gather list length */ 1150Sstevel@tonic-gate (uint_t)0xffffffff /* request size */ 1160Sstevel@tonic-gate }; 1170Sstevel@tonic-gate 1180Sstevel@tonic-gate static ddi_dma_attr_t ISA_dma_attr = { 1190Sstevel@tonic-gate DMA_ATTR_V0, 1200Sstevel@tonic-gate (unsigned long long)0, 1210Sstevel@tonic-gate (unsigned long long)0x00ffffff, 1220Sstevel@tonic-gate 0x0000ffff, 1230Sstevel@tonic-gate 1, 1240Sstevel@tonic-gate 1, 1250Sstevel@tonic-gate 1, 1260Sstevel@tonic-gate (unsigned long long)0xffffffff, 1270Sstevel@tonic-gate (unsigned long long)0x0000ffff, 1280Sstevel@tonic-gate 1, 1290Sstevel@tonic-gate 1, 1300Sstevel@tonic-gate 0 1310Sstevel@tonic-gate }; 1320Sstevel@tonic-gate 1330Sstevel@tonic-gate 1340Sstevel@tonic-gate /* 1350Sstevel@tonic-gate * Config information 1360Sstevel@tonic-gate */ 1370Sstevel@tonic-gate 1380Sstevel@tonic-gate static int 1399149SJudy.Chen@Sun.COM isa_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 1409149SJudy.Chen@Sun.COM off_t offset, off_t len, caddr_t *vaddrp); 1419149SJudy.Chen@Sun.COM 1429149SJudy.Chen@Sun.COM static int 1430Sstevel@tonic-gate isa_dma_allochdl(dev_info_t *, dev_info_t *, ddi_dma_attr_t *, 1440Sstevel@tonic-gate int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *); 1450Sstevel@tonic-gate 1460Sstevel@tonic-gate static int 1470Sstevel@tonic-gate isa_dma_mctl(dev_info_t *, dev_info_t *, ddi_dma_handle_t, enum ddi_dma_ctlops, 1480Sstevel@tonic-gate off_t *, size_t *, caddr_t *, uint_t); 1490Sstevel@tonic-gate 1500Sstevel@tonic-gate static int 1510Sstevel@tonic-gate isa_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, void *); 1520Sstevel@tonic-gate 1539149SJudy.Chen@Sun.COM static int 1549149SJudy.Chen@Sun.COM isa_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op, 1559149SJudy.Chen@Sun.COM ddi_intr_handle_impl_t *hdlp, void *result); 1569149SJudy.Chen@Sun.COM 1570Sstevel@tonic-gate struct bus_ops isa_bus_ops = { 1580Sstevel@tonic-gate BUSO_REV, 1599149SJudy.Chen@Sun.COM isa_bus_map, 1600Sstevel@tonic-gate NULL, 1610Sstevel@tonic-gate NULL, 1620Sstevel@tonic-gate NULL, 1630Sstevel@tonic-gate i_ddi_map_fault, 1640Sstevel@tonic-gate ddi_dma_map, 1650Sstevel@tonic-gate isa_dma_allochdl, 1660Sstevel@tonic-gate ddi_dma_freehdl, 1670Sstevel@tonic-gate ddi_dma_bindhdl, 1680Sstevel@tonic-gate ddi_dma_unbindhdl, 1690Sstevel@tonic-gate ddi_dma_flush, 1700Sstevel@tonic-gate ddi_dma_win, 1710Sstevel@tonic-gate isa_dma_mctl, 1720Sstevel@tonic-gate isa_ctlops, 1730Sstevel@tonic-gate ddi_bus_prop_op, 1740Sstevel@tonic-gate NULL, /* (*bus_get_eventcookie)(); */ 1750Sstevel@tonic-gate NULL, /* (*bus_add_eventcall)(); */ 1760Sstevel@tonic-gate NULL, /* (*bus_remove_eventcall)(); */ 1770Sstevel@tonic-gate NULL, /* (*bus_post_event)(); */ 1780Sstevel@tonic-gate NULL, /* (*bus_intr_ctl)(); */ 1790Sstevel@tonic-gate NULL, /* (*bus_config)(); */ 1800Sstevel@tonic-gate NULL, /* (*bus_unconfig)(); */ 1810Sstevel@tonic-gate NULL, /* (*bus_fm_init)(); */ 1820Sstevel@tonic-gate NULL, /* (*bus_fm_fini)(); */ 1830Sstevel@tonic-gate NULL, /* (*bus_fm_access_enter)(); */ 1840Sstevel@tonic-gate NULL, /* (*bus_fm_access_exit)(); */ 1850Sstevel@tonic-gate NULL, /* (*bus_power)(); */ 1869149SJudy.Chen@Sun.COM isa_intr_ops /* (*bus_intr_op)(); */ 1870Sstevel@tonic-gate }; 1880Sstevel@tonic-gate 1890Sstevel@tonic-gate 1900Sstevel@tonic-gate static int isa_attach(dev_info_t *devi, ddi_attach_cmd_t cmd); 1910Sstevel@tonic-gate 1920Sstevel@tonic-gate /* 1930Sstevel@tonic-gate * Internal isa ctlops support routines 1940Sstevel@tonic-gate */ 1950Sstevel@tonic-gate static int isa_initchild(dev_info_t *child); 1960Sstevel@tonic-gate 1970Sstevel@tonic-gate struct dev_ops isa_ops = { 1980Sstevel@tonic-gate DEVO_REV, /* devo_rev, */ 1990Sstevel@tonic-gate 0, /* refcnt */ 2000Sstevel@tonic-gate ddi_no_info, /* info */ 2010Sstevel@tonic-gate nulldev, /* identify */ 2020Sstevel@tonic-gate nulldev, /* probe */ 2030Sstevel@tonic-gate isa_attach, /* attach */ 2049149SJudy.Chen@Sun.COM nulldev, /* detach */ 2050Sstevel@tonic-gate nodev, /* reset */ 2060Sstevel@tonic-gate (struct cb_ops *)0, /* driver operations */ 2077656SSherry.Moore@Sun.COM &isa_bus_ops, /* bus operations */ 2087656SSherry.Moore@Sun.COM NULL, /* power */ 2097656SSherry.Moore@Sun.COM ddi_quiesce_not_needed, /* quiesce */ 2100Sstevel@tonic-gate }; 2110Sstevel@tonic-gate 2120Sstevel@tonic-gate /* 2130Sstevel@tonic-gate * Module linkage information for the kernel. 2140Sstevel@tonic-gate */ 2150Sstevel@tonic-gate 2160Sstevel@tonic-gate static struct modldrv modldrv = { 2170Sstevel@tonic-gate &mod_driverops, /* Type of module. This is ISA bus driver */ 2187542SRichard.Bean@Sun.COM "isa nexus driver for 'ISA'", 2190Sstevel@tonic-gate &isa_ops, /* driver ops */ 2200Sstevel@tonic-gate }; 2210Sstevel@tonic-gate 2220Sstevel@tonic-gate static struct modlinkage modlinkage = { 2230Sstevel@tonic-gate MODREV_1, 2240Sstevel@tonic-gate &modldrv, 2250Sstevel@tonic-gate NULL 2260Sstevel@tonic-gate }; 2270Sstevel@tonic-gate 2280Sstevel@tonic-gate int 2290Sstevel@tonic-gate _init(void) 2300Sstevel@tonic-gate { 2310Sstevel@tonic-gate return (mod_install(&modlinkage)); 2320Sstevel@tonic-gate } 2330Sstevel@tonic-gate 2340Sstevel@tonic-gate int 2350Sstevel@tonic-gate _fini(void) 2360Sstevel@tonic-gate { 2370Sstevel@tonic-gate return (mod_remove(&modlinkage)); 2380Sstevel@tonic-gate } 2390Sstevel@tonic-gate 2400Sstevel@tonic-gate int 2410Sstevel@tonic-gate _info(struct modinfo *modinfop) 2420Sstevel@tonic-gate { 2430Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 2440Sstevel@tonic-gate } 2450Sstevel@tonic-gate 2460Sstevel@tonic-gate static int 2470Sstevel@tonic-gate isa_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 2480Sstevel@tonic-gate { 2490Sstevel@tonic-gate int rval; 2500Sstevel@tonic-gate 2515084Sjohnlev #if defined(__xpv) 2525084Sjohnlev /* 2535084Sjohnlev * don't allow isa to attach in domU. this can happen if someone sets 2545084Sjohnlev * the console wrong, etc. ISA devices assume the H/W is there and 2555084Sjohnlev * will cause the domU to panic. 2565084Sjohnlev */ 2575084Sjohnlev if (!DOMAIN_IS_INITDOMAIN(xen_info)) { 2585084Sjohnlev return (DDI_FAILURE); 2595084Sjohnlev } 2605084Sjohnlev #endif 2615084Sjohnlev 2629149SJudy.Chen@Sun.COM switch (cmd) { 2639149SJudy.Chen@Sun.COM case DDI_ATTACH: 2649149SJudy.Chen@Sun.COM break; 2659149SJudy.Chen@Sun.COM case DDI_RESUME: 2669149SJudy.Chen@Sun.COM return (DDI_SUCCESS); 2679149SJudy.Chen@Sun.COM default: 2680Sstevel@tonic-gate return (DDI_FAILURE); 2699149SJudy.Chen@Sun.COM } 2700Sstevel@tonic-gate 271*9226SJudy.Chen@Sun.COM bzero(isa_extra_resource, MAX_EXTRA_RESOURCE * sizeof (struct regspec)); 272*9226SJudy.Chen@Sun.COM 2730Sstevel@tonic-gate if ((rval = i_dmae_init(devi)) == DDI_SUCCESS) { 2740Sstevel@tonic-gate ddi_report_dev(devi); 2750Sstevel@tonic-gate /* 2760Sstevel@tonic-gate * Enumerate children -- invoking ACPICA 2770Sstevel@tonic-gate * This is normally in bus_config(), but we need this 2780Sstevel@tonic-gate * to happen earlier to boot. 2790Sstevel@tonic-gate */ 2800Sstevel@tonic-gate isa_alloc_nodes(devi); 2810Sstevel@tonic-gate } 2829149SJudy.Chen@Sun.COM 283*9226SJudy.Chen@Sun.COM isa_postattach(devi); 2849149SJudy.Chen@Sun.COM 2850Sstevel@tonic-gate return (rval); 2860Sstevel@tonic-gate } 2870Sstevel@tonic-gate 2889149SJudy.Chen@Sun.COM #define SET_RNGS(rng_p, used_p, ctyp, ptyp) do { \ 2899149SJudy.Chen@Sun.COM (rng_p)->child_high = (ctyp); \ 2909149SJudy.Chen@Sun.COM (rng_p)->child_low = (rng_p)->parent_low = (used_p)->base; \ 2919149SJudy.Chen@Sun.COM (rng_p)->parent_high = (ptyp); \ 2929149SJudy.Chen@Sun.COM (rng_p)->parent_mid = 0; \ 2939149SJudy.Chen@Sun.COM (rng_p)->size = (used_p)->len; \ 2949149SJudy.Chen@Sun.COM _NOTE(CONSTCOND) } while (0) 2959149SJudy.Chen@Sun.COM static uint_t 2969149SJudy.Chen@Sun.COM isa_used_to_ranges(int ctype, int *array, uint_t size, pib_ranges_t *ranges) 2979149SJudy.Chen@Sun.COM { 2989149SJudy.Chen@Sun.COM used_ranges_t *used_p; 2999149SJudy.Chen@Sun.COM pib_ranges_t *rng_p = ranges; 3009149SJudy.Chen@Sun.COM uint_t i, ptype; 3019149SJudy.Chen@Sun.COM 3029149SJudy.Chen@Sun.COM ptype = (ctype == ISA_ADDR_IO) ? PCI_ADDR_IO : PCI_ADDR_MEM32; 3039149SJudy.Chen@Sun.COM ptype |= PCI_REG_REL_M; 3049149SJudy.Chen@Sun.COM size /= USED_CELL_SIZE; 3059149SJudy.Chen@Sun.COM used_p = (used_ranges_t *)array; 3069149SJudy.Chen@Sun.COM SET_RNGS(rng_p, used_p, ctype, ptype); 3079149SJudy.Chen@Sun.COM for (i = 1, used_p++; i < size; i++, used_p++) { 3089149SJudy.Chen@Sun.COM /* merge ranges record if applicable */ 3099149SJudy.Chen@Sun.COM if (rng_p->child_low + rng_p->size == used_p->base) 3109149SJudy.Chen@Sun.COM rng_p->size += used_p->len; 3119149SJudy.Chen@Sun.COM else { 3129149SJudy.Chen@Sun.COM rng_p++; 3139149SJudy.Chen@Sun.COM SET_RNGS(rng_p, used_p, ctype, ptype); 3149149SJudy.Chen@Sun.COM } 3159149SJudy.Chen@Sun.COM } 3169149SJudy.Chen@Sun.COM return (rng_p - ranges + 1); 3179149SJudy.Chen@Sun.COM } 3189149SJudy.Chen@Sun.COM 3199149SJudy.Chen@Sun.COM void 3209149SJudy.Chen@Sun.COM isa_remove_res_from_pci(int type, int *array, uint_t size) 3219149SJudy.Chen@Sun.COM { 3229149SJudy.Chen@Sun.COM int i; 3239149SJudy.Chen@Sun.COM used_ranges_t *used_p; 3249149SJudy.Chen@Sun.COM 3259149SJudy.Chen@Sun.COM size /= USED_CELL_SIZE; 3269149SJudy.Chen@Sun.COM used_p = (used_ranges_t *)array; 3279149SJudy.Chen@Sun.COM for (i = 0; i < size; i++, used_p++) 328*9226SJudy.Chen@Sun.COM pci_register_isa_resources(type, used_p->base, used_p->len); 3299149SJudy.Chen@Sun.COM } 3309149SJudy.Chen@Sun.COM 3319149SJudy.Chen@Sun.COM static void 3329149SJudy.Chen@Sun.COM isa_postattach(dev_info_t *dip) 3339149SJudy.Chen@Sun.COM { 3349149SJudy.Chen@Sun.COM dev_info_t *used; 3359149SJudy.Chen@Sun.COM int *ioarray, *memarray, status; 3369149SJudy.Chen@Sun.COM uint_t nio = 0, nmem = 0, nrng = 0, n; 3379149SJudy.Chen@Sun.COM pib_ranges_t *ranges; 3389149SJudy.Chen@Sun.COM 3399149SJudy.Chen@Sun.COM used = ddi_find_devinfo("used-resources", -1, 0); 3409149SJudy.Chen@Sun.COM if (used == NULL) { 3419149SJudy.Chen@Sun.COM cmn_err(CE_WARN, "Failed to find used-resources <%s>\n", 3429149SJudy.Chen@Sun.COM ddi_get_name(dip)); 3439149SJudy.Chen@Sun.COM return; 3449149SJudy.Chen@Sun.COM } 3459149SJudy.Chen@Sun.COM status = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, used, 3469149SJudy.Chen@Sun.COM DDI_PROP_DONTPASS, "io-space", &ioarray, &nio); 3479149SJudy.Chen@Sun.COM if (status != DDI_PROP_SUCCESS && status != DDI_PROP_NOT_FOUND) { 3489149SJudy.Chen@Sun.COM cmn_err(CE_WARN, "io-space property failure for %s (%x)\n", 3499149SJudy.Chen@Sun.COM ddi_get_name(used), status); 3509149SJudy.Chen@Sun.COM return; 3519149SJudy.Chen@Sun.COM } 3529149SJudy.Chen@Sun.COM status = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, used, 3539149SJudy.Chen@Sun.COM DDI_PROP_DONTPASS, "device-memory", &memarray, &nmem); 3549149SJudy.Chen@Sun.COM if (status != DDI_PROP_SUCCESS && status != DDI_PROP_NOT_FOUND) { 3559149SJudy.Chen@Sun.COM cmn_err(CE_WARN, "device-memory property failure for %s (%x)\n", 3569149SJudy.Chen@Sun.COM ddi_get_name(used), status); 3579149SJudy.Chen@Sun.COM return; 3589149SJudy.Chen@Sun.COM } 3599149SJudy.Chen@Sun.COM n = (nio + nmem) / USED_CELL_SIZE; 3609149SJudy.Chen@Sun.COM ranges = (pib_ranges_t *)kmem_zalloc(sizeof (pib_ranges_t) * n, 3619149SJudy.Chen@Sun.COM KM_SLEEP); 3629149SJudy.Chen@Sun.COM 3639149SJudy.Chen@Sun.COM if (nio != 0) { 3649149SJudy.Chen@Sun.COM nrng = isa_used_to_ranges(ISA_ADDR_IO, ioarray, nio, ranges); 3659149SJudy.Chen@Sun.COM isa_remove_res_from_pci(ISA_ADDR_IO, ioarray, nio); 3669149SJudy.Chen@Sun.COM ddi_prop_free(ioarray); 3679149SJudy.Chen@Sun.COM } 3689149SJudy.Chen@Sun.COM if (nmem != 0) { 3699149SJudy.Chen@Sun.COM nrng += isa_used_to_ranges(ISA_ADDR_MEM, memarray, nmem, 3709149SJudy.Chen@Sun.COM ranges + nrng); 3719149SJudy.Chen@Sun.COM isa_remove_res_from_pci(ISA_ADDR_MEM, memarray, nmem); 3729149SJudy.Chen@Sun.COM ddi_prop_free(memarray); 3739149SJudy.Chen@Sun.COM } 3749149SJudy.Chen@Sun.COM 375*9226SJudy.Chen@Sun.COM if (!pseudo_isa) 376*9226SJudy.Chen@Sun.COM (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, "ranges", 377*9226SJudy.Chen@Sun.COM (int *)ranges, nrng * sizeof (pib_ranges_t) / sizeof (int)); 3789149SJudy.Chen@Sun.COM kmem_free(ranges, sizeof (pib_ranges_t) * n); 3799149SJudy.Chen@Sun.COM } 3809149SJudy.Chen@Sun.COM 3819149SJudy.Chen@Sun.COM /*ARGSUSED*/ 3829149SJudy.Chen@Sun.COM static int 3839149SJudy.Chen@Sun.COM isa_apply_range(dev_info_t *dip, struct regspec *isa_reg_p, 3849149SJudy.Chen@Sun.COM pci_regspec_t *pci_reg_p) 3859149SJudy.Chen@Sun.COM { 3869149SJudy.Chen@Sun.COM pib_ranges_t *ranges, *rng_p; 3879149SJudy.Chen@Sun.COM int len, i, offset, nrange; 3889149SJudy.Chen@Sun.COM 3899149SJudy.Chen@Sun.COM if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 3909149SJudy.Chen@Sun.COM "ranges", (caddr_t)&ranges, &len) != DDI_SUCCESS) { 3919149SJudy.Chen@Sun.COM cmn_err(CE_WARN, "Can't get %s ranges property", 3929149SJudy.Chen@Sun.COM ddi_get_name(dip)); 3939149SJudy.Chen@Sun.COM return (DDI_ME_REGSPEC_RANGE); 3949149SJudy.Chen@Sun.COM } 3959149SJudy.Chen@Sun.COM nrange = len / sizeof (pib_ranges_t); 3969149SJudy.Chen@Sun.COM rng_p = ranges; 3979149SJudy.Chen@Sun.COM for (i = 0; i < nrange; i++, rng_p++) { 3989149SJudy.Chen@Sun.COM /* Check for correct space */ 3999149SJudy.Chen@Sun.COM if (isa_reg_p->regspec_bustype != rng_p->child_high) 4009149SJudy.Chen@Sun.COM continue; 4019149SJudy.Chen@Sun.COM 4029149SJudy.Chen@Sun.COM /* Detect whether request entirely fits within a range */ 4039149SJudy.Chen@Sun.COM if (isa_reg_p->regspec_addr < rng_p->child_low) 4049149SJudy.Chen@Sun.COM continue; 4059149SJudy.Chen@Sun.COM if ((isa_reg_p->regspec_addr + isa_reg_p->regspec_size) > 4069149SJudy.Chen@Sun.COM (rng_p->child_low + rng_p->size)) 4079149SJudy.Chen@Sun.COM continue; 4089149SJudy.Chen@Sun.COM 4099149SJudy.Chen@Sun.COM offset = isa_reg_p->regspec_addr - rng_p->child_low; 4109149SJudy.Chen@Sun.COM 4119149SJudy.Chen@Sun.COM pci_reg_p->pci_phys_hi = rng_p->parent_high; 4129149SJudy.Chen@Sun.COM pci_reg_p->pci_phys_mid = 0; 4139149SJudy.Chen@Sun.COM pci_reg_p->pci_phys_low = rng_p->parent_low + offset; 4149149SJudy.Chen@Sun.COM pci_reg_p->pci_size_hi = 0; 4159149SJudy.Chen@Sun.COM pci_reg_p->pci_size_low = isa_reg_p->regspec_size; 4169149SJudy.Chen@Sun.COM 4179149SJudy.Chen@Sun.COM break; 4189149SJudy.Chen@Sun.COM } 4199149SJudy.Chen@Sun.COM kmem_free(ranges, len); 420*9226SJudy.Chen@Sun.COM 421*9226SJudy.Chen@Sun.COM if (i < nrange) 422*9226SJudy.Chen@Sun.COM return (DDI_SUCCESS); 423*9226SJudy.Chen@Sun.COM 424*9226SJudy.Chen@Sun.COM /* 425*9226SJudy.Chen@Sun.COM * Check extra resource range specially for serial and parallel 426*9226SJudy.Chen@Sun.COM * devices, which are treated differently from all other ISA 427*9226SJudy.Chen@Sun.COM * devices. On some machines, serial ports are not enumerated 428*9226SJudy.Chen@Sun.COM * by ACPI but by BIOS, with io base addresses noted in legacy 429*9226SJudy.Chen@Sun.COM * BIOS data area. Parallel port on some machines comes with 430*9226SJudy.Chen@Sun.COM * illegal size. 431*9226SJudy.Chen@Sun.COM */ 432*9226SJudy.Chen@Sun.COM if (isa_reg_p->regspec_bustype != ISA_ADDR_IO) 433*9226SJudy.Chen@Sun.COM goto out_of_range; 434*9226SJudy.Chen@Sun.COM 435*9226SJudy.Chen@Sun.COM for (i = 0; i < isa_extra_count; i++) { 436*9226SJudy.Chen@Sun.COM struct regspec *reg_p = &isa_extra_resource[i]; 437*9226SJudy.Chen@Sun.COM 438*9226SJudy.Chen@Sun.COM if (isa_reg_p->regspec_addr < reg_p->regspec_addr) 439*9226SJudy.Chen@Sun.COM continue; 440*9226SJudy.Chen@Sun.COM if ((isa_reg_p->regspec_addr + isa_reg_p->regspec_size) > 441*9226SJudy.Chen@Sun.COM (reg_p->regspec_addr + reg_p->regspec_size)) 442*9226SJudy.Chen@Sun.COM continue; 443*9226SJudy.Chen@Sun.COM 444*9226SJudy.Chen@Sun.COM pci_reg_p->pci_phys_hi = PCI_ADDR_IO | PCI_REG_REL_M; 445*9226SJudy.Chen@Sun.COM pci_reg_p->pci_phys_mid = 0; 446*9226SJudy.Chen@Sun.COM pci_reg_p->pci_phys_low = isa_reg_p->regspec_addr; 447*9226SJudy.Chen@Sun.COM pci_reg_p->pci_size_hi = 0; 448*9226SJudy.Chen@Sun.COM pci_reg_p->pci_size_low = isa_reg_p->regspec_size; 449*9226SJudy.Chen@Sun.COM break; 4509149SJudy.Chen@Sun.COM } 451*9226SJudy.Chen@Sun.COM if (i < isa_extra_count) 452*9226SJudy.Chen@Sun.COM return (DDI_SUCCESS); 4539149SJudy.Chen@Sun.COM 454*9226SJudy.Chen@Sun.COM out_of_range: 455*9226SJudy.Chen@Sun.COM cmn_err(CE_WARN, "isa_apply_range: Out of range base <0x%x>, size <%d>", 456*9226SJudy.Chen@Sun.COM isa_reg_p->regspec_addr, isa_reg_p->regspec_size); 457*9226SJudy.Chen@Sun.COM return (DDI_ME_REGSPEC_RANGE); 4589149SJudy.Chen@Sun.COM } 4599149SJudy.Chen@Sun.COM 4609149SJudy.Chen@Sun.COM static int 4619149SJudy.Chen@Sun.COM isa_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 4629149SJudy.Chen@Sun.COM off_t offset, off_t len, caddr_t *vaddrp) 4639149SJudy.Chen@Sun.COM { 4649149SJudy.Chen@Sun.COM struct regspec tmp_reg, *rp; 4659149SJudy.Chen@Sun.COM pci_regspec_t vreg; 4669149SJudy.Chen@Sun.COM ddi_map_req_t mr = *mp; /* Get private copy of request */ 4679149SJudy.Chen@Sun.COM int error; 4689149SJudy.Chen@Sun.COM 4699149SJudy.Chen@Sun.COM if (pseudo_isa) 4709149SJudy.Chen@Sun.COM return (i_ddi_bus_map(dip, rdip, mp, offset, len, vaddrp)); 4719149SJudy.Chen@Sun.COM 4729149SJudy.Chen@Sun.COM mp = &mr; 4739149SJudy.Chen@Sun.COM 4749149SJudy.Chen@Sun.COM /* 4759149SJudy.Chen@Sun.COM * First, if given an rnumber, convert it to a regspec... 4769149SJudy.Chen@Sun.COM */ 4779149SJudy.Chen@Sun.COM if (mp->map_type == DDI_MT_RNUMBER) { 4789149SJudy.Chen@Sun.COM 4799149SJudy.Chen@Sun.COM int rnumber = mp->map_obj.rnumber; 4809149SJudy.Chen@Sun.COM 4819149SJudy.Chen@Sun.COM rp = i_ddi_rnumber_to_regspec(rdip, rnumber); 4829149SJudy.Chen@Sun.COM if (rp == (struct regspec *)0) 4839149SJudy.Chen@Sun.COM return (DDI_ME_RNUMBER_RANGE); 4849149SJudy.Chen@Sun.COM 4859149SJudy.Chen@Sun.COM /* 4869149SJudy.Chen@Sun.COM * Convert the given ddi_map_req_t from rnumber to regspec... 4879149SJudy.Chen@Sun.COM */ 4889149SJudy.Chen@Sun.COM mp->map_type = DDI_MT_REGSPEC; 4899149SJudy.Chen@Sun.COM mp->map_obj.rp = rp; 4909149SJudy.Chen@Sun.COM } 4919149SJudy.Chen@Sun.COM 4929149SJudy.Chen@Sun.COM /* 4939149SJudy.Chen@Sun.COM * Adjust offset and length correspnding to called values... 4949149SJudy.Chen@Sun.COM * XXX: A non-zero length means override the one in the regspec. 4959149SJudy.Chen@Sun.COM * XXX: (Regardless of what's in the parent's range) 4969149SJudy.Chen@Sun.COM */ 4979149SJudy.Chen@Sun.COM 4989149SJudy.Chen@Sun.COM tmp_reg = *(mp->map_obj.rp); /* Preserve underlying data */ 4999149SJudy.Chen@Sun.COM rp = mp->map_obj.rp = &tmp_reg; /* Use tmp_reg in request */ 5009149SJudy.Chen@Sun.COM 5019149SJudy.Chen@Sun.COM rp->regspec_addr += (uint_t)offset; 5029149SJudy.Chen@Sun.COM if (len != 0) 5039149SJudy.Chen@Sun.COM rp->regspec_size = (uint_t)len; 5049149SJudy.Chen@Sun.COM 5059149SJudy.Chen@Sun.COM if ((error = isa_apply_range(dip, rp, &vreg)) != 0) 5069149SJudy.Chen@Sun.COM return (error); 5079149SJudy.Chen@Sun.COM mp->map_obj.rp = (struct regspec *)&vreg; 5089149SJudy.Chen@Sun.COM 5099149SJudy.Chen@Sun.COM /* 5109149SJudy.Chen@Sun.COM * Call my parents bus_map function with modified values... 5119149SJudy.Chen@Sun.COM */ 5129149SJudy.Chen@Sun.COM 5139149SJudy.Chen@Sun.COM return (ddi_map(dip, mp, (off_t)0, (off_t)0, vaddrp)); 5149149SJudy.Chen@Sun.COM } 5159149SJudy.Chen@Sun.COM 5160Sstevel@tonic-gate static int 5170Sstevel@tonic-gate isa_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_attr_t *dma_attr, 5180Sstevel@tonic-gate int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *handlep) 5190Sstevel@tonic-gate { 5200Sstevel@tonic-gate ddi_dma_attr_merge(dma_attr, &ISA_dma_attr); 5210Sstevel@tonic-gate return (ddi_dma_allochdl(dip, rdip, dma_attr, waitfp, arg, handlep)); 5220Sstevel@tonic-gate } 5230Sstevel@tonic-gate 5240Sstevel@tonic-gate static int 5250Sstevel@tonic-gate isa_dma_mctl(dev_info_t *dip, dev_info_t *rdip, 5260Sstevel@tonic-gate ddi_dma_handle_t handle, enum ddi_dma_ctlops request, 5270Sstevel@tonic-gate off_t *offp, size_t *lenp, caddr_t *objp, uint_t flags) 5280Sstevel@tonic-gate { 5290Sstevel@tonic-gate int rval; 5300Sstevel@tonic-gate ddi_dma_lim_t defalt; 5310Sstevel@tonic-gate int arg = (int)(uintptr_t)objp; 5320Sstevel@tonic-gate 5330Sstevel@tonic-gate switch (request) { 5340Sstevel@tonic-gate 5350Sstevel@tonic-gate case DDI_DMA_E_PROG: 5360Sstevel@tonic-gate return (i_dmae_prog(rdip, (struct ddi_dmae_req *)offp, 5370Sstevel@tonic-gate (ddi_dma_cookie_t *)lenp, arg)); 5380Sstevel@tonic-gate 5390Sstevel@tonic-gate case DDI_DMA_E_ACQUIRE: 5400Sstevel@tonic-gate return (i_dmae_acquire(rdip, arg, (int(*)(caddr_t))offp, 5410Sstevel@tonic-gate (caddr_t)lenp)); 5420Sstevel@tonic-gate 5430Sstevel@tonic-gate case DDI_DMA_E_FREE: 5440Sstevel@tonic-gate return (i_dmae_free(rdip, arg)); 5450Sstevel@tonic-gate 5460Sstevel@tonic-gate case DDI_DMA_E_STOP: 5470Sstevel@tonic-gate i_dmae_stop(rdip, arg); 5480Sstevel@tonic-gate return (DDI_SUCCESS); 5490Sstevel@tonic-gate 5500Sstevel@tonic-gate case DDI_DMA_E_ENABLE: 5510Sstevel@tonic-gate i_dmae_enable(rdip, arg); 5520Sstevel@tonic-gate return (DDI_SUCCESS); 5530Sstevel@tonic-gate 5540Sstevel@tonic-gate case DDI_DMA_E_DISABLE: 5550Sstevel@tonic-gate i_dmae_disable(rdip, arg); 5560Sstevel@tonic-gate return (DDI_SUCCESS); 5570Sstevel@tonic-gate 5580Sstevel@tonic-gate case DDI_DMA_E_GETCNT: 5590Sstevel@tonic-gate i_dmae_get_chan_stat(rdip, arg, NULL, (int *)lenp); 5600Sstevel@tonic-gate return (DDI_SUCCESS); 5610Sstevel@tonic-gate 5620Sstevel@tonic-gate case DDI_DMA_E_SWSETUP: 5630Sstevel@tonic-gate return (i_dmae_swsetup(rdip, (struct ddi_dmae_req *)offp, 5640Sstevel@tonic-gate (ddi_dma_cookie_t *)lenp, arg)); 5650Sstevel@tonic-gate 5660Sstevel@tonic-gate case DDI_DMA_E_SWSTART: 5670Sstevel@tonic-gate i_dmae_swstart(rdip, arg); 5680Sstevel@tonic-gate return (DDI_SUCCESS); 5690Sstevel@tonic-gate 5700Sstevel@tonic-gate case DDI_DMA_E_GETLIM: 5710Sstevel@tonic-gate bcopy(&ISA_dma_limits, objp, sizeof (ddi_dma_lim_t)); 5720Sstevel@tonic-gate return (DDI_SUCCESS); 5730Sstevel@tonic-gate 5740Sstevel@tonic-gate case DDI_DMA_E_GETATTR: 5750Sstevel@tonic-gate bcopy(&ISA_dma_attr, objp, sizeof (ddi_dma_attr_t)); 5760Sstevel@tonic-gate return (DDI_SUCCESS); 5770Sstevel@tonic-gate 5780Sstevel@tonic-gate case DDI_DMA_E_1STPTY: 5790Sstevel@tonic-gate { 5800Sstevel@tonic-gate struct ddi_dmae_req req1stpty = 5810Sstevel@tonic-gate { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 5820Sstevel@tonic-gate if (arg == 0) { 5830Sstevel@tonic-gate req1stpty.der_command = DMAE_CMD_TRAN; 5840Sstevel@tonic-gate req1stpty.der_trans = DMAE_TRANS_DMND; 5850Sstevel@tonic-gate } else { 5860Sstevel@tonic-gate req1stpty.der_trans = DMAE_TRANS_CSCD; 5870Sstevel@tonic-gate } 5880Sstevel@tonic-gate return (i_dmae_prog(rdip, &req1stpty, NULL, arg)); 5890Sstevel@tonic-gate } 5900Sstevel@tonic-gate 5910Sstevel@tonic-gate case DDI_DMA_IOPB_ALLOC: /* get contiguous DMA-able memory */ 5920Sstevel@tonic-gate case DDI_DMA_SMEM_ALLOC: 5930Sstevel@tonic-gate if (!offp) { 5940Sstevel@tonic-gate defalt = ISA_dma_limits; 5950Sstevel@tonic-gate offp = (off_t *)&defalt; 5960Sstevel@tonic-gate } 5970Sstevel@tonic-gate /*FALLTHROUGH*/ 5980Sstevel@tonic-gate default: 5990Sstevel@tonic-gate rval = ddi_dma_mctl(dip, rdip, handle, request, offp, 6000Sstevel@tonic-gate lenp, objp, flags); 6010Sstevel@tonic-gate } 6020Sstevel@tonic-gate return (rval); 6030Sstevel@tonic-gate } 6040Sstevel@tonic-gate 6050Sstevel@tonic-gate /* 6060Sstevel@tonic-gate * Check if driver should be treated as an old pre 2.6 driver 6070Sstevel@tonic-gate */ 6080Sstevel@tonic-gate static int 6090Sstevel@tonic-gate old_driver(dev_info_t *dip) 6100Sstevel@tonic-gate { 6110Sstevel@tonic-gate extern int ignore_hardware_nodes; /* force flag from ddi_impl.c */ 6120Sstevel@tonic-gate 6130Sstevel@tonic-gate if (ndi_dev_is_persistent_node(dip)) { 6140Sstevel@tonic-gate if (ignore_hardware_nodes) 6150Sstevel@tonic-gate return (1); 6160Sstevel@tonic-gate if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 6170Sstevel@tonic-gate "ignore-hardware-nodes", -1) != -1) 6180Sstevel@tonic-gate return (1); 6190Sstevel@tonic-gate } 6200Sstevel@tonic-gate return (0); 6210Sstevel@tonic-gate } 6220Sstevel@tonic-gate 6230Sstevel@tonic-gate typedef struct { 6240Sstevel@tonic-gate uint32_t phys_hi; 6250Sstevel@tonic-gate uint32_t phys_lo; 6260Sstevel@tonic-gate uint32_t size; 6270Sstevel@tonic-gate } isa_regs_t; 6280Sstevel@tonic-gate 6290Sstevel@tonic-gate /* 6300Sstevel@tonic-gate * Return non-zero if device in tree is a PnP isa device 6310Sstevel@tonic-gate */ 6320Sstevel@tonic-gate static int 6330Sstevel@tonic-gate is_pnpisa(dev_info_t *dip) 6340Sstevel@tonic-gate { 6350Sstevel@tonic-gate isa_regs_t *isa_regs; 6360Sstevel@tonic-gate int proplen, pnpisa; 6370Sstevel@tonic-gate 6380Sstevel@tonic-gate if (ndi_dev_is_persistent_node(dip) == 0) 6390Sstevel@tonic-gate return (0); 6400Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg", 6415084Sjohnlev (caddr_t)&isa_regs, &proplen) != DDI_PROP_SUCCESS) { 6420Sstevel@tonic-gate return (0); 6430Sstevel@tonic-gate } 6440Sstevel@tonic-gate pnpisa = isa_regs[0].phys_hi & 0x80000000; 6450Sstevel@tonic-gate /* 6460Sstevel@tonic-gate * free the memory allocated by ddi_getlongprop(). 6470Sstevel@tonic-gate */ 6480Sstevel@tonic-gate kmem_free(isa_regs, proplen); 6490Sstevel@tonic-gate if (pnpisa) 6500Sstevel@tonic-gate return (1); 6510Sstevel@tonic-gate else 6520Sstevel@tonic-gate return (0); 6530Sstevel@tonic-gate } 6540Sstevel@tonic-gate 6550Sstevel@tonic-gate /*ARGSUSED*/ 6560Sstevel@tonic-gate static int 6570Sstevel@tonic-gate isa_ctlops(dev_info_t *dip, dev_info_t *rdip, 6580Sstevel@tonic-gate ddi_ctl_enum_t ctlop, void *arg, void *result) 6590Sstevel@tonic-gate { 6609149SJudy.Chen@Sun.COM int rn; 6619149SJudy.Chen@Sun.COM struct ddi_parent_private_data *pdp; 6629149SJudy.Chen@Sun.COM 6630Sstevel@tonic-gate switch (ctlop) { 6640Sstevel@tonic-gate case DDI_CTLOPS_REPORTDEV: 6650Sstevel@tonic-gate if (rdip == (dev_info_t *)0) 6660Sstevel@tonic-gate return (DDI_FAILURE); 6670Sstevel@tonic-gate cmn_err(CE_CONT, "?ISA-device: %s%d\n", 6680Sstevel@tonic-gate ddi_driver_name(rdip), ddi_get_instance(rdip)); 6690Sstevel@tonic-gate return (DDI_SUCCESS); 6700Sstevel@tonic-gate 6710Sstevel@tonic-gate case DDI_CTLOPS_INITCHILD: 6720Sstevel@tonic-gate /* 6730Sstevel@tonic-gate * older drivers aren't expecting the "standard" device 6740Sstevel@tonic-gate * node format used by the hardware nodes. these drivers 6750Sstevel@tonic-gate * only expect their own properties set in their driver.conf 6760Sstevel@tonic-gate * files. so they tell us not to call them with hardware 6770Sstevel@tonic-gate * nodes by setting the property "ignore-hardware-nodes". 6780Sstevel@tonic-gate */ 6790Sstevel@tonic-gate if (old_driver((dev_info_t *)arg)) { 6800Sstevel@tonic-gate return (DDI_NOT_WELL_FORMED); 6810Sstevel@tonic-gate } 6820Sstevel@tonic-gate 6830Sstevel@tonic-gate return (isa_initchild((dev_info_t *)arg)); 6840Sstevel@tonic-gate 6850Sstevel@tonic-gate case DDI_CTLOPS_UNINITCHILD: 6860Sstevel@tonic-gate impl_ddi_sunbus_removechild((dev_info_t *)arg); 6870Sstevel@tonic-gate return (DDI_SUCCESS); 6880Sstevel@tonic-gate 6890Sstevel@tonic-gate case DDI_CTLOPS_SIDDEV: 6900Sstevel@tonic-gate /* 6910Sstevel@tonic-gate * All ISA devices need to do confirming probes 6920Sstevel@tonic-gate * unless they are PnP ISA. 6930Sstevel@tonic-gate */ 6940Sstevel@tonic-gate if (is_pnpisa(dip)) 6950Sstevel@tonic-gate return (DDI_SUCCESS); 6960Sstevel@tonic-gate else 6970Sstevel@tonic-gate return (DDI_FAILURE); 6980Sstevel@tonic-gate 6999149SJudy.Chen@Sun.COM case DDI_CTLOPS_REGSIZE: 7009149SJudy.Chen@Sun.COM case DDI_CTLOPS_NREGS: 7019149SJudy.Chen@Sun.COM if (rdip == (dev_info_t *)0) 7029149SJudy.Chen@Sun.COM return (DDI_FAILURE); 7039149SJudy.Chen@Sun.COM 7049149SJudy.Chen@Sun.COM if ((pdp = ddi_get_parent_data(rdip)) == NULL) 7059149SJudy.Chen@Sun.COM return (DDI_FAILURE); 7069149SJudy.Chen@Sun.COM 7079149SJudy.Chen@Sun.COM if (ctlop == DDI_CTLOPS_NREGS) { 7089149SJudy.Chen@Sun.COM *(int *)result = pdp->par_nreg; 7099149SJudy.Chen@Sun.COM } else { 7109149SJudy.Chen@Sun.COM rn = *(int *)arg; 7119149SJudy.Chen@Sun.COM if (rn >= pdp->par_nreg) 7129149SJudy.Chen@Sun.COM return (DDI_FAILURE); 7139149SJudy.Chen@Sun.COM *(off_t *)result = (off_t)pdp->par_reg[rn].regspec_size; 7149149SJudy.Chen@Sun.COM } 7159149SJudy.Chen@Sun.COM return (DDI_SUCCESS); 7169149SJudy.Chen@Sun.COM 7179149SJudy.Chen@Sun.COM case DDI_CTLOPS_ATTACH: 7189149SJudy.Chen@Sun.COM case DDI_CTLOPS_DETACH: 7199149SJudy.Chen@Sun.COM case DDI_CTLOPS_PEEK: 7209149SJudy.Chen@Sun.COM case DDI_CTLOPS_POKE: 7219149SJudy.Chen@Sun.COM return (DDI_FAILURE); 7229149SJudy.Chen@Sun.COM 7230Sstevel@tonic-gate default: 7240Sstevel@tonic-gate return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 7250Sstevel@tonic-gate } 7260Sstevel@tonic-gate } 7270Sstevel@tonic-gate 7289149SJudy.Chen@Sun.COM static struct intrspec * 7299149SJudy.Chen@Sun.COM isa_get_ispec(dev_info_t *rdip, int inum) 7309149SJudy.Chen@Sun.COM { 7319149SJudy.Chen@Sun.COM struct ddi_parent_private_data *pdp = ddi_get_parent_data(rdip); 7329149SJudy.Chen@Sun.COM 7339149SJudy.Chen@Sun.COM /* Validate the interrupt number */ 7349149SJudy.Chen@Sun.COM if (inum >= pdp->par_nintr) 7359149SJudy.Chen@Sun.COM return (NULL); 7369149SJudy.Chen@Sun.COM 7379149SJudy.Chen@Sun.COM /* Get the interrupt structure pointer and return that */ 7389149SJudy.Chen@Sun.COM return ((struct intrspec *)&pdp->par_intr[inum]); 7399149SJudy.Chen@Sun.COM } 7409149SJudy.Chen@Sun.COM 7419149SJudy.Chen@Sun.COM static int 7429149SJudy.Chen@Sun.COM isa_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op, 7439149SJudy.Chen@Sun.COM ddi_intr_handle_impl_t *hdlp, void *result) 7449149SJudy.Chen@Sun.COM { 7459149SJudy.Chen@Sun.COM struct intrspec *ispec; 7469149SJudy.Chen@Sun.COM 7479149SJudy.Chen@Sun.COM if (pseudo_isa) 7489149SJudy.Chen@Sun.COM return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result)); 7499149SJudy.Chen@Sun.COM 7509149SJudy.Chen@Sun.COM 7519149SJudy.Chen@Sun.COM /* Process the interrupt operation */ 7529149SJudy.Chen@Sun.COM switch (intr_op) { 7539149SJudy.Chen@Sun.COM case DDI_INTROP_GETCAP: 7549149SJudy.Chen@Sun.COM /* First check with pcplusmp */ 7559149SJudy.Chen@Sun.COM if (psm_intr_ops == NULL) 7569149SJudy.Chen@Sun.COM return (DDI_FAILURE); 7579149SJudy.Chen@Sun.COM 7589149SJudy.Chen@Sun.COM if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_GET_CAP, result)) { 7599149SJudy.Chen@Sun.COM *(int *)result = 0; 7609149SJudy.Chen@Sun.COM return (DDI_FAILURE); 7619149SJudy.Chen@Sun.COM } 7629149SJudy.Chen@Sun.COM break; 7639149SJudy.Chen@Sun.COM case DDI_INTROP_SETCAP: 7649149SJudy.Chen@Sun.COM if (psm_intr_ops == NULL) 7659149SJudy.Chen@Sun.COM return (DDI_FAILURE); 7669149SJudy.Chen@Sun.COM 7679149SJudy.Chen@Sun.COM if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_CAP, result)) 7689149SJudy.Chen@Sun.COM return (DDI_FAILURE); 7699149SJudy.Chen@Sun.COM break; 7709149SJudy.Chen@Sun.COM case DDI_INTROP_ALLOC: 7719149SJudy.Chen@Sun.COM if ((ispec = isa_get_ispec(rdip, hdlp->ih_inum)) == NULL) 7729149SJudy.Chen@Sun.COM return (DDI_FAILURE); 7739149SJudy.Chen@Sun.COM hdlp->ih_pri = ispec->intrspec_pri; 7749149SJudy.Chen@Sun.COM *(int *)result = hdlp->ih_scratch1; 7759149SJudy.Chen@Sun.COM break; 7769149SJudy.Chen@Sun.COM case DDI_INTROP_FREE: 7779149SJudy.Chen@Sun.COM break; 7789149SJudy.Chen@Sun.COM case DDI_INTROP_GETPRI: 7799149SJudy.Chen@Sun.COM if ((ispec = isa_get_ispec(rdip, hdlp->ih_inum)) == NULL) 7809149SJudy.Chen@Sun.COM return (DDI_FAILURE); 7819149SJudy.Chen@Sun.COM *(int *)result = ispec->intrspec_pri; 7829149SJudy.Chen@Sun.COM break; 7839149SJudy.Chen@Sun.COM case DDI_INTROP_SETPRI: 7849149SJudy.Chen@Sun.COM /* Validate the interrupt priority passed to us */ 7859149SJudy.Chen@Sun.COM if (*(int *)result > LOCK_LEVEL) 7869149SJudy.Chen@Sun.COM return (DDI_FAILURE); 7879149SJudy.Chen@Sun.COM 7889149SJudy.Chen@Sun.COM /* Ensure that PSM is all initialized and ispec is ok */ 7899149SJudy.Chen@Sun.COM if ((psm_intr_ops == NULL) || 7909149SJudy.Chen@Sun.COM ((ispec = isa_get_ispec(rdip, hdlp->ih_inum)) == NULL)) 7919149SJudy.Chen@Sun.COM return (DDI_FAILURE); 7929149SJudy.Chen@Sun.COM 7939149SJudy.Chen@Sun.COM /* update the ispec with the new priority */ 7949149SJudy.Chen@Sun.COM ispec->intrspec_pri = *(int *)result; 7959149SJudy.Chen@Sun.COM break; 7969149SJudy.Chen@Sun.COM case DDI_INTROP_ADDISR: 7979149SJudy.Chen@Sun.COM if ((ispec = isa_get_ispec(rdip, hdlp->ih_inum)) == NULL) 7989149SJudy.Chen@Sun.COM return (DDI_FAILURE); 7999149SJudy.Chen@Sun.COM ispec->intrspec_func = hdlp->ih_cb_func; 8009149SJudy.Chen@Sun.COM break; 8019149SJudy.Chen@Sun.COM case DDI_INTROP_REMISR: 8029149SJudy.Chen@Sun.COM if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 8039149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8049149SJudy.Chen@Sun.COM if ((ispec = isa_get_ispec(rdip, hdlp->ih_inum)) == NULL) 8059149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8069149SJudy.Chen@Sun.COM ispec->intrspec_func = (uint_t (*)()) 0; 8079149SJudy.Chen@Sun.COM break; 8089149SJudy.Chen@Sun.COM case DDI_INTROP_ENABLE: 8099149SJudy.Chen@Sun.COM if ((ispec = isa_get_ispec(rdip, hdlp->ih_inum)) == NULL) 8109149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8119149SJudy.Chen@Sun.COM 8129149SJudy.Chen@Sun.COM /* Call psmi to translate irq with the dip */ 8139149SJudy.Chen@Sun.COM if (psm_intr_ops == NULL) 8149149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8159149SJudy.Chen@Sun.COM 8169149SJudy.Chen@Sun.COM ((ihdl_plat_t *)hdlp->ih_private)->ip_ispecp = ispec; 8179149SJudy.Chen@Sun.COM (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, 8189149SJudy.Chen@Sun.COM (int *)&hdlp->ih_vector); 8199149SJudy.Chen@Sun.COM 8209149SJudy.Chen@Sun.COM /* Add the interrupt handler */ 8219149SJudy.Chen@Sun.COM if (!add_avintr((void *)hdlp, ispec->intrspec_pri, 8229149SJudy.Chen@Sun.COM hdlp->ih_cb_func, DEVI(rdip)->devi_name, hdlp->ih_vector, 8239149SJudy.Chen@Sun.COM hdlp->ih_cb_arg1, hdlp->ih_cb_arg2, NULL, rdip)) 8249149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8259149SJudy.Chen@Sun.COM break; 8269149SJudy.Chen@Sun.COM case DDI_INTROP_DISABLE: 8279149SJudy.Chen@Sun.COM if ((ispec = isa_get_ispec(rdip, hdlp->ih_inum)) == NULL) 8289149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8299149SJudy.Chen@Sun.COM 8309149SJudy.Chen@Sun.COM /* Call psm_ops() to translate irq with the dip */ 8319149SJudy.Chen@Sun.COM if (psm_intr_ops == NULL) 8329149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8339149SJudy.Chen@Sun.COM 8349149SJudy.Chen@Sun.COM ((ihdl_plat_t *)hdlp->ih_private)->ip_ispecp = ispec; 8359149SJudy.Chen@Sun.COM (void) (*psm_intr_ops)(rdip, hdlp, 8369149SJudy.Chen@Sun.COM PSM_INTR_OP_XLATE_VECTOR, (int *)&hdlp->ih_vector); 8379149SJudy.Chen@Sun.COM 8389149SJudy.Chen@Sun.COM /* Remove the interrupt handler */ 8399149SJudy.Chen@Sun.COM rem_avintr((void *)hdlp, ispec->intrspec_pri, 8409149SJudy.Chen@Sun.COM hdlp->ih_cb_func, hdlp->ih_vector); 8419149SJudy.Chen@Sun.COM break; 8429149SJudy.Chen@Sun.COM case DDI_INTROP_SETMASK: 8439149SJudy.Chen@Sun.COM if (psm_intr_ops == NULL) 8449149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8459149SJudy.Chen@Sun.COM 8469149SJudy.Chen@Sun.COM if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_MASK, NULL)) 8479149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8489149SJudy.Chen@Sun.COM break; 8499149SJudy.Chen@Sun.COM case DDI_INTROP_CLRMASK: 8509149SJudy.Chen@Sun.COM if (psm_intr_ops == NULL) 8519149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8529149SJudy.Chen@Sun.COM 8539149SJudy.Chen@Sun.COM if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_CLEAR_MASK, NULL)) 8549149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8559149SJudy.Chen@Sun.COM break; 8569149SJudy.Chen@Sun.COM case DDI_INTROP_GETPENDING: 8579149SJudy.Chen@Sun.COM if (psm_intr_ops == NULL) 8589149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8599149SJudy.Chen@Sun.COM 8609149SJudy.Chen@Sun.COM if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_GET_PENDING, 8619149SJudy.Chen@Sun.COM result)) { 8629149SJudy.Chen@Sun.COM *(int *)result = 0; 8639149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8649149SJudy.Chen@Sun.COM } 8659149SJudy.Chen@Sun.COM break; 8669149SJudy.Chen@Sun.COM case DDI_INTROP_NAVAIL: 8679149SJudy.Chen@Sun.COM case DDI_INTROP_NINTRS: 8689149SJudy.Chen@Sun.COM *(int *)result = i_ddi_get_intx_nintrs(rdip); 8699149SJudy.Chen@Sun.COM if (*(int *)result == 0) { 8709149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8719149SJudy.Chen@Sun.COM } 8729149SJudy.Chen@Sun.COM break; 8739149SJudy.Chen@Sun.COM case DDI_INTROP_SUPPORTED_TYPES: 8749149SJudy.Chen@Sun.COM *(int *)result = DDI_INTR_TYPE_FIXED; /* Always ... */ 8759149SJudy.Chen@Sun.COM break; 8769149SJudy.Chen@Sun.COM default: 8779149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8789149SJudy.Chen@Sun.COM } 8799149SJudy.Chen@Sun.COM 8809149SJudy.Chen@Sun.COM return (DDI_SUCCESS); 8819149SJudy.Chen@Sun.COM } 8829149SJudy.Chen@Sun.COM 8830Sstevel@tonic-gate static void 8840Sstevel@tonic-gate isa_vendor(uint32_t id, char *vendor) 8850Sstevel@tonic-gate { 8860Sstevel@tonic-gate vendor[0] = '@' + ((id >> 26) & 0x1f); 8870Sstevel@tonic-gate vendor[1] = '@' + ((id >> 21) & 0x1f); 8880Sstevel@tonic-gate vendor[2] = '@' + ((id >> 16) & 0x1f); 8890Sstevel@tonic-gate vendor[3] = 0; 8900Sstevel@tonic-gate } 8910Sstevel@tonic-gate 8920Sstevel@tonic-gate /* 8930Sstevel@tonic-gate * Name a child 8940Sstevel@tonic-gate */ 8950Sstevel@tonic-gate static int 8960Sstevel@tonic-gate isa_name_child(dev_info_t *child, char *name, int namelen) 8970Sstevel@tonic-gate { 8980Sstevel@tonic-gate char vendor[8]; 8990Sstevel@tonic-gate int device; 9000Sstevel@tonic-gate uint32_t serial; 9010Sstevel@tonic-gate int func; 9020Sstevel@tonic-gate int bustype; 9030Sstevel@tonic-gate uint32_t base; 9040Sstevel@tonic-gate int proplen; 9050Sstevel@tonic-gate int pnpisa = 0; 9060Sstevel@tonic-gate isa_regs_t *isa_regs; 9070Sstevel@tonic-gate 9080Sstevel@tonic-gate void make_ddi_ppd(dev_info_t *, struct ddi_parent_private_data **); 9090Sstevel@tonic-gate 9100Sstevel@tonic-gate /* 9110Sstevel@tonic-gate * older drivers aren't expecting the "standard" device 9120Sstevel@tonic-gate * node format used by the hardware nodes. these drivers 9130Sstevel@tonic-gate * only expect their own properties set in their driver.conf 9140Sstevel@tonic-gate * files. so they tell us not to call them with hardware 9150Sstevel@tonic-gate * nodes by setting the property "ignore-hardware-nodes". 9160Sstevel@tonic-gate */ 9170Sstevel@tonic-gate if (old_driver(child)) 9180Sstevel@tonic-gate return (DDI_FAILURE); 9190Sstevel@tonic-gate 9200Sstevel@tonic-gate /* 9210Sstevel@tonic-gate * Fill in parent-private data 9220Sstevel@tonic-gate */ 9230Sstevel@tonic-gate if (ddi_get_parent_data(child) == NULL) { 9240Sstevel@tonic-gate struct ddi_parent_private_data *pdptr; 9250Sstevel@tonic-gate make_ddi_ppd(child, &pdptr); 9260Sstevel@tonic-gate ddi_set_parent_data(child, pdptr); 9270Sstevel@tonic-gate } 9280Sstevel@tonic-gate 9290Sstevel@tonic-gate if (ndi_dev_is_persistent_node(child) == 0) { 9300Sstevel@tonic-gate /* 9310Sstevel@tonic-gate * For .conf nodes, generate name from parent private data 9320Sstevel@tonic-gate */ 9330Sstevel@tonic-gate name[0] = '\0'; 9340Sstevel@tonic-gate if (sparc_pd_getnreg(child) > 0) { 9350Sstevel@tonic-gate (void) snprintf(name, namelen, "%x,%x", 9360Sstevel@tonic-gate (uint_t)sparc_pd_getreg(child, 0)->regspec_bustype, 9370Sstevel@tonic-gate (uint_t)sparc_pd_getreg(child, 0)->regspec_addr); 9380Sstevel@tonic-gate } 9390Sstevel@tonic-gate return (DDI_SUCCESS); 9400Sstevel@tonic-gate } 9410Sstevel@tonic-gate 9420Sstevel@tonic-gate /* 9430Sstevel@tonic-gate * For hw nodes, look up "reg" property 9440Sstevel@tonic-gate */ 9450Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg", 9460Sstevel@tonic-gate (caddr_t)&isa_regs, &proplen) != DDI_PROP_SUCCESS) { 9470Sstevel@tonic-gate return (DDI_FAILURE); 9480Sstevel@tonic-gate } 9490Sstevel@tonic-gate 9500Sstevel@tonic-gate /* 9510Sstevel@tonic-gate * extract the device identifications 9520Sstevel@tonic-gate */ 9530Sstevel@tonic-gate pnpisa = isa_regs[0].phys_hi & 0x80000000; 9540Sstevel@tonic-gate if (pnpisa) { 9550Sstevel@tonic-gate isa_vendor(isa_regs[0].phys_hi, vendor); 9560Sstevel@tonic-gate device = isa_regs[0].phys_hi & 0xffff; 9570Sstevel@tonic-gate serial = isa_regs[0].phys_lo; 9580Sstevel@tonic-gate func = (isa_regs[0].size >> 24) & 0xff; 9590Sstevel@tonic-gate if (func != 0) 9600Sstevel@tonic-gate (void) snprintf(name, namelen, "pnp%s,%04x,%x,%x", 9610Sstevel@tonic-gate vendor, device, serial, func); 9620Sstevel@tonic-gate else 9630Sstevel@tonic-gate (void) snprintf(name, namelen, "pnp%s,%04x,%x", 9640Sstevel@tonic-gate vendor, device, serial); 9650Sstevel@tonic-gate } else { 9660Sstevel@tonic-gate bustype = isa_regs[0].phys_hi; 9670Sstevel@tonic-gate base = isa_regs[0].phys_lo; 9680Sstevel@tonic-gate (void) sprintf(name, "%x,%x", bustype, base); 9690Sstevel@tonic-gate } 9700Sstevel@tonic-gate 9710Sstevel@tonic-gate /* 9720Sstevel@tonic-gate * free the memory allocated by ddi_getlongprop(). 9730Sstevel@tonic-gate */ 9740Sstevel@tonic-gate kmem_free(isa_regs, proplen); 9750Sstevel@tonic-gate 9760Sstevel@tonic-gate return (DDI_SUCCESS); 9770Sstevel@tonic-gate } 9780Sstevel@tonic-gate 9790Sstevel@tonic-gate static int 9800Sstevel@tonic-gate isa_initchild(dev_info_t *child) 9810Sstevel@tonic-gate { 9820Sstevel@tonic-gate char name[80]; 9830Sstevel@tonic-gate 9840Sstevel@tonic-gate if (isa_name_child(child, name, 80) != DDI_SUCCESS) 9850Sstevel@tonic-gate return (DDI_FAILURE); 9860Sstevel@tonic-gate ddi_set_name_addr(child, name); 9870Sstevel@tonic-gate 9880Sstevel@tonic-gate if (ndi_dev_is_persistent_node(child) != 0) 9890Sstevel@tonic-gate return (DDI_SUCCESS); 9900Sstevel@tonic-gate 9910Sstevel@tonic-gate /* 9920Sstevel@tonic-gate * This is a .conf node, try merge properties onto a 9930Sstevel@tonic-gate * hw node with the same name. 9940Sstevel@tonic-gate */ 9950Sstevel@tonic-gate if (ndi_merge_node(child, isa_name_child) == DDI_SUCCESS) { 9960Sstevel@tonic-gate /* 9970Sstevel@tonic-gate * Return failure to remove node 9980Sstevel@tonic-gate */ 9990Sstevel@tonic-gate impl_ddi_sunbus_removechild(child); 10000Sstevel@tonic-gate return (DDI_FAILURE); 10010Sstevel@tonic-gate } 10020Sstevel@tonic-gate /* 10030Sstevel@tonic-gate * Cannot merge node, permit pseudo children 10040Sstevel@tonic-gate */ 10050Sstevel@tonic-gate return (DDI_SUCCESS); 10060Sstevel@tonic-gate } 10070Sstevel@tonic-gate 10080Sstevel@tonic-gate /* 10090Sstevel@tonic-gate * called when ACPI enumeration is not used 10100Sstevel@tonic-gate */ 10110Sstevel@tonic-gate static void 10120Sstevel@tonic-gate add_known_used_resources(void) 10130Sstevel@tonic-gate { 10140Sstevel@tonic-gate /* needs to be in increasing order */ 10150Sstevel@tonic-gate int intr[] = {0x1, 0x3, 0x4, 0x6, 0x7, 0xc}; 10160Sstevel@tonic-gate int dma[] = {0x2}; 10170Sstevel@tonic-gate int io[] = {0x60, 0x1, 0x64, 0x1, 0x2f8, 0x8, 0x378, 0x8, 0x3f0, 0x10, 10180Sstevel@tonic-gate 0x778, 0x4}; 10190Sstevel@tonic-gate dev_info_t *usedrdip; 10200Sstevel@tonic-gate 10210Sstevel@tonic-gate usedrdip = ddi_find_devinfo(USED_RESOURCES, -1, 0); 10220Sstevel@tonic-gate 10230Sstevel@tonic-gate if (usedrdip == NULL) { 10240Sstevel@tonic-gate (void) ndi_devi_alloc_sleep(ddi_root_node(), USED_RESOURCES, 1025789Sahrens (pnode_t)DEVI_SID_NODEID, &usedrdip); 10260Sstevel@tonic-gate } 10270Sstevel@tonic-gate 10280Sstevel@tonic-gate (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip, 10290Sstevel@tonic-gate "interrupts", (int *)intr, (int)(sizeof (intr) / sizeof (int))); 10300Sstevel@tonic-gate (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip, 10310Sstevel@tonic-gate "io-space", (int *)io, (int)(sizeof (io) / sizeof (int))); 10320Sstevel@tonic-gate (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip, 10330Sstevel@tonic-gate "dma-channels", (int *)dma, (int)(sizeof (dma) / sizeof (int))); 10340Sstevel@tonic-gate (void) ndi_devi_bind_driver(usedrdip, 0); 10350Sstevel@tonic-gate 10360Sstevel@tonic-gate } 10370Sstevel@tonic-gate 10380Sstevel@tonic-gate static void 10390Sstevel@tonic-gate isa_alloc_nodes(dev_info_t *isa_dip) 10400Sstevel@tonic-gate { 10410Sstevel@tonic-gate static int alloced = 0; 10420Sstevel@tonic-gate int circ, i; 10430Sstevel@tonic-gate dev_info_t *xdip; 10440Sstevel@tonic-gate 10450Sstevel@tonic-gate /* hard coded isa stuff */ 10460Sstevel@tonic-gate struct regspec asy_regs[] = { 10470Sstevel@tonic-gate {1, 0x3f8, 0x8}, 10480Sstevel@tonic-gate {1, 0x2f8, 0x8} 10490Sstevel@tonic-gate }; 10500Sstevel@tonic-gate int asy_intrs[] = {0x4, 0x3}; 10510Sstevel@tonic-gate 10520Sstevel@tonic-gate struct regspec i8042_regs[] = { 10530Sstevel@tonic-gate {1, 0x60, 0x1}, 10540Sstevel@tonic-gate {1, 0x64, 0x1} 10550Sstevel@tonic-gate }; 10560Sstevel@tonic-gate int i8042_intrs[] = {0x1, 0xc}; 10570Sstevel@tonic-gate char *acpi_prop; 10580Sstevel@tonic-gate int acpi_enum = 1; /* ACPI is default to be on */ 10590Sstevel@tonic-gate 10600Sstevel@tonic-gate if (alloced) 10610Sstevel@tonic-gate return; 10620Sstevel@tonic-gate 10630Sstevel@tonic-gate ndi_devi_enter(isa_dip, &circ); 10640Sstevel@tonic-gate if (alloced) { /* just in case we are multi-threaded */ 10650Sstevel@tonic-gate ndi_devi_exit(isa_dip, circ); 10660Sstevel@tonic-gate return; 10670Sstevel@tonic-gate } 10680Sstevel@tonic-gate alloced = 1; 10690Sstevel@tonic-gate 10700Sstevel@tonic-gate if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(), 10710Sstevel@tonic-gate DDI_PROP_DONTPASS, "acpi-enum", &acpi_prop) == DDI_PROP_SUCCESS) { 10720Sstevel@tonic-gate acpi_enum = strcmp("off", acpi_prop); 10730Sstevel@tonic-gate ddi_prop_free(acpi_prop); 10740Sstevel@tonic-gate } 10750Sstevel@tonic-gate 10760Sstevel@tonic-gate if (acpi_enum) { 10770Sstevel@tonic-gate if (acpi_isa_device_enum(isa_dip)) { 10780Sstevel@tonic-gate ndi_devi_exit(isa_dip, circ); 10790Sstevel@tonic-gate if (isa_resource_setup() != NDI_SUCCESS) { 10800Sstevel@tonic-gate cmn_err(CE_WARN, "isa nexus: isa " 10810Sstevel@tonic-gate "resource setup failed"); 10820Sstevel@tonic-gate } 10832007Sml40262 10842007Sml40262 /* serial ports? */ 10852007Sml40262 enumerate_BIOS_serial(isa_dip); 1086*9226SJudy.Chen@Sun.COM 1087*9226SJudy.Chen@Sun.COM /* adjust parallel port size */ 1088*9226SJudy.Chen@Sun.COM adjust_prtsz(isa_dip); 10890Sstevel@tonic-gate return; 10900Sstevel@tonic-gate } 10910Sstevel@tonic-gate cmn_err(CE_NOTE, "!Solaris did not detect ACPI BIOS"); 10920Sstevel@tonic-gate } 10930Sstevel@tonic-gate cmn_err(CE_NOTE, "!ACPI is off"); 10940Sstevel@tonic-gate 10950Sstevel@tonic-gate /* serial ports */ 10960Sstevel@tonic-gate for (i = 0; i < 2; i++) { 10975084Sjohnlev #if defined(__xpv) 10985084Sjohnlev /* 10995084Sjohnlev * the hypervisor may be reserving the serial ports for console 11005084Sjohnlev * and/or debug use. Probe the irqs to see if they are 11015084Sjohnlev * available. 11025084Sjohnlev */ 11035084Sjohnlev if (ec_probe_pirq(asy_intrs[i]) == 0) 11045084Sjohnlev continue; /* in use */ 11055084Sjohnlev #endif 11060Sstevel@tonic-gate ndi_devi_alloc_sleep(isa_dip, "asy", 1107789Sahrens (pnode_t)DEVI_SID_NODEID, &xdip); 11080Sstevel@tonic-gate (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip, 11090Sstevel@tonic-gate "reg", (int *)&asy_regs[i], 3); 11100Sstevel@tonic-gate (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, 11110Sstevel@tonic-gate "interrupts", asy_intrs[i]); 11120Sstevel@tonic-gate (void) ndi_devi_bind_driver(xdip, 0); 11130Sstevel@tonic-gate } 11140Sstevel@tonic-gate 11150Sstevel@tonic-gate /* i8042 node */ 11160Sstevel@tonic-gate ndi_devi_alloc_sleep(isa_dip, "i8042", 1117789Sahrens (pnode_t)DEVI_SID_NODEID, &xdip); 11180Sstevel@tonic-gate (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip, 11190Sstevel@tonic-gate "reg", (int *)i8042_regs, 6); 11200Sstevel@tonic-gate (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip, 11210Sstevel@tonic-gate "interrupts", (int *)i8042_intrs, 2); 11220Sstevel@tonic-gate (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 11230Sstevel@tonic-gate "unit-address", "1,60"); 11240Sstevel@tonic-gate (void) ndi_devi_bind_driver(xdip, 0); 11250Sstevel@tonic-gate 11260Sstevel@tonic-gate add_known_used_resources(); 11270Sstevel@tonic-gate 11280Sstevel@tonic-gate ndi_devi_exit(isa_dip, circ); 11290Sstevel@tonic-gate 11300Sstevel@tonic-gate } 11312007Sml40262 11322007Sml40262 /* 11332007Sml40262 * On some machines, serial port 2 isn't listed in the ACPI table. 11342007Sml40262 * This function goes through the BIOS data area and makes sure all 11352007Sml40262 * the serial ports there are in the dev_info tree. If any are missing, 11362007Sml40262 * this function will add them. 11372007Sml40262 */ 11382007Sml40262 11392007Sml40262 static int num_BIOS_serial = 2; /* number of BIOS serial ports to look at */ 11402007Sml40262 11412007Sml40262 static void 11422007Sml40262 enumerate_BIOS_serial(dev_info_t *isa_dip) 11432007Sml40262 { 11442007Sml40262 ushort_t *bios_data; 11452007Sml40262 int i; 11462007Sml40262 dev_info_t *xdip; 11472007Sml40262 int found; 11482007Sml40262 int ret; 11492007Sml40262 struct regspec *tmpregs; 11502007Sml40262 int tmpregs_len; 11512007Sml40262 static struct regspec tmp_asy_regs[] = { 11522007Sml40262 {1, 0x3f8, 0x8}, 11532007Sml40262 }; 11542007Sml40262 static int default_asy_intrs[] = { 4, 3, 4, 3 }; 11552007Sml40262 static size_t size = 4; 11562007Sml40262 11572007Sml40262 /* 11582007Sml40262 * The first four 2-byte quantities of the BIOS data area contain 11592007Sml40262 * the base I/O addresses of the first four serial ports. 11602007Sml40262 */ 11612007Sml40262 bios_data = (ushort_t *)psm_map_new((paddr_t)BIOS_DATA_AREA, size, 11625084Sjohnlev PSM_PROT_READ); 11632007Sml40262 for (i = 0; i < num_BIOS_serial; i++) { 11642007Sml40262 if (bios_data[i] == 0) { 11652007Sml40262 /* no COM[i]: port */ 11662007Sml40262 continue; 11672007Sml40262 } 11682007Sml40262 11692007Sml40262 /* Look for it in the dev_info tree */ 11702007Sml40262 found = 0; 11712007Sml40262 for (xdip = ddi_get_child(isa_dip); xdip != NULL; 11722007Sml40262 xdip = ddi_get_next_sibling(xdip)) { 11732007Sml40262 if (strncmp(ddi_node_name(xdip), "asy", 3) != 0) { 11742007Sml40262 /* skip non asy */ 11752007Sml40262 continue; 11762007Sml40262 } 11772007Sml40262 11782007Sml40262 /* Match by addr */ 11792007Sml40262 ret = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, xdip, 11805084Sjohnlev DDI_PROP_DONTPASS, "reg", (int **)&tmpregs, 11815084Sjohnlev (uint_t *)&tmpregs_len); 11822007Sml40262 if (ret != DDI_PROP_SUCCESS) { 11832007Sml40262 /* error */ 11842007Sml40262 continue; 11852007Sml40262 } 11862007Sml40262 11872007Sml40262 if (tmpregs->regspec_addr == bios_data[i]) 11882007Sml40262 found = 1; 11892007Sml40262 /* 11902007Sml40262 * Free the memory allocated by 11912007Sml40262 * ddi_prop_lookup_int_array(). 11922007Sml40262 */ 11932007Sml40262 ddi_prop_free(tmpregs); 11945084Sjohnlev 11952007Sml40262 } 11962007Sml40262 11972007Sml40262 /* If not found, then add it */ 11982007Sml40262 if (!found) { 11992007Sml40262 ndi_devi_alloc_sleep(isa_dip, "asy", 12002007Sml40262 (pnode_t)DEVI_SID_NODEID, &xdip); 12012007Sml40262 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 12022007Sml40262 "compatible", "PNP0500"); 12032007Sml40262 /* This should be gotten from master file: */ 12042007Sml40262 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 12052007Sml40262 "model", "Standard PC COM port"); 12062007Sml40262 tmp_asy_regs[0].regspec_addr = bios_data[i]; 12072007Sml40262 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip, 12082007Sml40262 "reg", (int *)&tmp_asy_regs[0], 3); 12092007Sml40262 (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, 12102007Sml40262 "interrupts", default_asy_intrs[i]); 12112007Sml40262 (void) ndi_devi_bind_driver(xdip, 0); 1212*9226SJudy.Chen@Sun.COM 1213*9226SJudy.Chen@Sun.COM ASSERT(isa_extra_count < MAX_EXTRA_RESOURCE); 1214*9226SJudy.Chen@Sun.COM bcopy(tmp_asy_regs, 1215*9226SJudy.Chen@Sun.COM isa_extra_resource + isa_extra_count, 1216*9226SJudy.Chen@Sun.COM sizeof (struct regspec)); 1217*9226SJudy.Chen@Sun.COM isa_extra_count++; 12182007Sml40262 } 12192007Sml40262 } 12205084Sjohnlev #if defined(__xpv) 12215084Sjohnlev /* 12225084Sjohnlev * Check each serial port to see if it is in use by the hypervisor. 12235084Sjohnlev * If it is in use, then remove the node from the device tree. 12245084Sjohnlev */ 12255084Sjohnlev i = 0; 12265084Sjohnlev for (xdip = ddi_get_child(isa_dip); xdip != NULL; ) { 12275084Sjohnlev int asy_intr; 12285084Sjohnlev dev_info_t *curdip; 12295084Sjohnlev 12305084Sjohnlev curdip = xdip; 12315084Sjohnlev xdip = ddi_get_next_sibling(xdip); 12325084Sjohnlev if (strncmp(ddi_node_name(curdip), "asy", 3) != 0) { 12335084Sjohnlev /* skip non asy */ 12345084Sjohnlev continue; 12355084Sjohnlev } 12365084Sjohnlev /* 12375084Sjohnlev * Check if the hypervisor is using the serial port by probing 12385084Sjohnlev * the irq and if it is using it remove the node 12395084Sjohnlev * from the device tree 12405084Sjohnlev */ 12415084Sjohnlev asy_intr = ddi_prop_get_int(DDI_DEV_T_ANY, curdip, 12425084Sjohnlev DDI_PROP_DONTPASS, "interrupts", -1); 12435084Sjohnlev if (asy_intr == -1) { 12445084Sjohnlev /* error */ 12455084Sjohnlev continue; 12465084Sjohnlev } 12475084Sjohnlev 12485084Sjohnlev if (ec_probe_pirq(asy_intr)) { 12495084Sjohnlev continue; 12505084Sjohnlev } 12515084Sjohnlev ret = ndi_devi_free(curdip); 12525084Sjohnlev if (ret != DDI_SUCCESS) 12535084Sjohnlev cmn_err(CE_WARN, 12545084Sjohnlev "could not remove asy%d node", i); 12555084Sjohnlev else 12565084Sjohnlev cmn_err(CE_NOTE, "!asy%d unavailable, reserved" 12575084Sjohnlev " to hypervisor", i); 12585084Sjohnlev i++; 12595084Sjohnlev } 12605084Sjohnlev #endif /* __xpv */ 12612007Sml40262 12622007Sml40262 psm_unmap((caddr_t)bios_data, size); 12632007Sml40262 } 1264*9226SJudy.Chen@Sun.COM 1265*9226SJudy.Chen@Sun.COM /* 1266*9226SJudy.Chen@Sun.COM * Some machine comes with an illegal parallel port size of 3 1267*9226SJudy.Chen@Sun.COM * bytes in ACPI, even parallel port mode is ECP. 1268*9226SJudy.Chen@Sun.COM */ 1269*9226SJudy.Chen@Sun.COM #define DEFAULT_PRT_SIZE 8 1270*9226SJudy.Chen@Sun.COM static void 1271*9226SJudy.Chen@Sun.COM adjust_prtsz(dev_info_t *isa_dip) 1272*9226SJudy.Chen@Sun.COM { 1273*9226SJudy.Chen@Sun.COM dev_info_t *cdip; 1274*9226SJudy.Chen@Sun.COM struct regspec *regs_p, *extreg_p; 1275*9226SJudy.Chen@Sun.COM int regs_len, nreg, i; 1276*9226SJudy.Chen@Sun.COM char *name; 1277*9226SJudy.Chen@Sun.COM 1278*9226SJudy.Chen@Sun.COM for (cdip = ddi_get_child(isa_dip); cdip != NULL; 1279*9226SJudy.Chen@Sun.COM cdip = ddi_get_next_sibling(cdip)) { 1280*9226SJudy.Chen@Sun.COM name = ddi_node_name(cdip); 1281*9226SJudy.Chen@Sun.COM if ((strncmp(name, "lp", 2) != 0) || (strnlen(name, 3) != 2)) 1282*9226SJudy.Chen@Sun.COM continue; /* skip non parallel */ 1283*9226SJudy.Chen@Sun.COM 1284*9226SJudy.Chen@Sun.COM if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, cdip, 1285*9226SJudy.Chen@Sun.COM DDI_PROP_DONTPASS, "reg", (int **)®s_p, 1286*9226SJudy.Chen@Sun.COM (uint_t *)®s_len) != DDI_PROP_SUCCESS) 1287*9226SJudy.Chen@Sun.COM continue; 1288*9226SJudy.Chen@Sun.COM 1289*9226SJudy.Chen@Sun.COM nreg = regs_len / (sizeof (struct regspec) / sizeof (int)); 1290*9226SJudy.Chen@Sun.COM for (i = 0; i < nreg; i++) { 1291*9226SJudy.Chen@Sun.COM if (regs_p[i].regspec_size == DEFAULT_PRT_SIZE) 1292*9226SJudy.Chen@Sun.COM continue; 1293*9226SJudy.Chen@Sun.COM 1294*9226SJudy.Chen@Sun.COM ASSERT(isa_extra_count < MAX_EXTRA_RESOURCE); 1295*9226SJudy.Chen@Sun.COM extreg_p = &isa_extra_resource[isa_extra_count++]; 1296*9226SJudy.Chen@Sun.COM extreg_p->regspec_bustype = ISA_ADDR_IO; 1297*9226SJudy.Chen@Sun.COM extreg_p->regspec_addr = regs_p[i].regspec_addr; 1298*9226SJudy.Chen@Sun.COM extreg_p->regspec_size = DEFAULT_PRT_SIZE; 1299*9226SJudy.Chen@Sun.COM } 1300*9226SJudy.Chen@Sun.COM 1301*9226SJudy.Chen@Sun.COM ddi_prop_free(regs_p); 1302*9226SJudy.Chen@Sun.COM } 1303*9226SJudy.Chen@Sun.COM } 1304