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*11465SKerry.Shu@Sun.COM * Copyright 2010 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> 4110127SLipeng.Sang@Sun.COM #include <sys/ddi_subrdefs.h> 420Sstevel@tonic-gate #include <sys/dma_engine.h> 430Sstevel@tonic-gate #include <sys/ddi.h> 440Sstevel@tonic-gate #include <sys/sunddi.h> 450Sstevel@tonic-gate #include <sys/sunndi.h> 460Sstevel@tonic-gate #include <sys/acpi/acpi_enum.h> 479149SJudy.Chen@Sun.COM #include <sys/mach_intr.h> 489149SJudy.Chen@Sun.COM #include <sys/pci.h> 499149SJudy.Chen@Sun.COM #include <sys/note.h> 5010175SStuart.Maybee@Sun.COM #include <sys/boot_console.h> 515084Sjohnlev #if defined(__xpv) 525084Sjohnlev #include <sys/hypervisor.h> 535084Sjohnlev #include <sys/evtchn_impl.h> 5410175SStuart.Maybee@Sun.COM 5510175SStuart.Maybee@Sun.COM extern int console_hypervisor_device; 565084Sjohnlev #endif 575084Sjohnlev 589149SJudy.Chen@Sun.COM extern int pseudo_isa; 590Sstevel@tonic-gate extern int isa_resource_setup(void); 609149SJudy.Chen@Sun.COM extern int (*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *, 619149SJudy.Chen@Sun.COM psm_intr_op_t, int *); 629226SJudy.Chen@Sun.COM extern void pci_register_isa_resources(int, uint32_t, uint32_t); 630Sstevel@tonic-gate static char USED_RESOURCES[] = "used-resources"; 6410127SLipeng.Sang@Sun.COM static void isa_enumerate(int); 652007Sml40262 static void enumerate_BIOS_serial(dev_info_t *); 669226SJudy.Chen@Sun.COM static void adjust_prtsz(dev_info_t *isa_dip); 6710127SLipeng.Sang@Sun.COM static void isa_create_ranges_prop(dev_info_t *); 682007Sml40262 699149SJudy.Chen@Sun.COM /* 709149SJudy.Chen@Sun.COM * The following typedef is used to represent an entry in the "ranges" 719149SJudy.Chen@Sun.COM * property of a pci-isa bridge device node. 729149SJudy.Chen@Sun.COM */ 739149SJudy.Chen@Sun.COM typedef struct { 749149SJudy.Chen@Sun.COM uint32_t child_high; 759149SJudy.Chen@Sun.COM uint32_t child_low; 769149SJudy.Chen@Sun.COM uint32_t parent_high; 779149SJudy.Chen@Sun.COM uint32_t parent_mid; 789149SJudy.Chen@Sun.COM uint32_t parent_low; 799149SJudy.Chen@Sun.COM uint32_t size; 809149SJudy.Chen@Sun.COM } pib_ranges_t; 819149SJudy.Chen@Sun.COM 829149SJudy.Chen@Sun.COM typedef struct { 839149SJudy.Chen@Sun.COM uint32_t base; 849149SJudy.Chen@Sun.COM uint32_t len; 859149SJudy.Chen@Sun.COM } used_ranges_t; 869149SJudy.Chen@Sun.COM 879149SJudy.Chen@Sun.COM #define USED_CELL_SIZE 2 /* 1 byte addr, 1 byte size */ 889149SJudy.Chen@Sun.COM #define ISA_ADDR_IO 1 /* IO address space */ 899149SJudy.Chen@Sun.COM #define ISA_ADDR_MEM 0 /* memory adress space */ 902007Sml40262 #define BIOS_DATA_AREA 0x400 910Sstevel@tonic-gate /* 920Sstevel@tonic-gate * #define ISA_DEBUG 1 930Sstevel@tonic-gate */ 940Sstevel@tonic-gate 950Sstevel@tonic-gate /* 969226SJudy.Chen@Sun.COM * For serial ports not enumerated by ACPI, and parallel ports with 979226SJudy.Chen@Sun.COM * illegal size. Typically, a system can have as many as 4 serial 989226SJudy.Chen@Sun.COM * ports and 3 parallel ports. 999226SJudy.Chen@Sun.COM */ 1009226SJudy.Chen@Sun.COM #define MAX_EXTRA_RESOURCE 7 1019226SJudy.Chen@Sun.COM static struct regspec isa_extra_resource[MAX_EXTRA_RESOURCE]; 1029226SJudy.Chen@Sun.COM static int isa_extra_count = 0; 1039226SJudy.Chen@Sun.COM 1049226SJudy.Chen@Sun.COM /* 1050Sstevel@tonic-gate * Local data 1060Sstevel@tonic-gate */ 1070Sstevel@tonic-gate static ddi_dma_lim_t ISA_dma_limits = { 1080Sstevel@tonic-gate 0, /* address low */ 1090Sstevel@tonic-gate 0x00ffffff, /* address high */ 1100Sstevel@tonic-gate 0, /* counter max */ 1110Sstevel@tonic-gate 1, /* burstsize */ 1120Sstevel@tonic-gate DMA_UNIT_8, /* minimum xfer */ 1130Sstevel@tonic-gate 0, /* dma speed */ 1140Sstevel@tonic-gate (uint_t)DMALIM_VER0, /* version */ 1150Sstevel@tonic-gate 0x0000ffff, /* address register */ 1160Sstevel@tonic-gate 0x0000ffff, /* counter register */ 1170Sstevel@tonic-gate 1, /* sector size */ 1180Sstevel@tonic-gate 0x00000001, /* scatter/gather list length */ 1190Sstevel@tonic-gate (uint_t)0xffffffff /* request size */ 1200Sstevel@tonic-gate }; 1210Sstevel@tonic-gate 1220Sstevel@tonic-gate static ddi_dma_attr_t ISA_dma_attr = { 1230Sstevel@tonic-gate DMA_ATTR_V0, 1240Sstevel@tonic-gate (unsigned long long)0, 1250Sstevel@tonic-gate (unsigned long long)0x00ffffff, 1260Sstevel@tonic-gate 0x0000ffff, 1270Sstevel@tonic-gate 1, 1280Sstevel@tonic-gate 1, 1290Sstevel@tonic-gate 1, 1300Sstevel@tonic-gate (unsigned long long)0xffffffff, 1310Sstevel@tonic-gate (unsigned long long)0x0000ffff, 1320Sstevel@tonic-gate 1, 1330Sstevel@tonic-gate 1, 1340Sstevel@tonic-gate 0 1350Sstevel@tonic-gate }; 1360Sstevel@tonic-gate 1370Sstevel@tonic-gate 1380Sstevel@tonic-gate /* 1390Sstevel@tonic-gate * Config information 1400Sstevel@tonic-gate */ 1410Sstevel@tonic-gate 1420Sstevel@tonic-gate static int 1439149SJudy.Chen@Sun.COM isa_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 1449149SJudy.Chen@Sun.COM off_t offset, off_t len, caddr_t *vaddrp); 1459149SJudy.Chen@Sun.COM 1469149SJudy.Chen@Sun.COM static int 1470Sstevel@tonic-gate isa_dma_allochdl(dev_info_t *, dev_info_t *, ddi_dma_attr_t *, 1480Sstevel@tonic-gate int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *); 1490Sstevel@tonic-gate 1500Sstevel@tonic-gate static int 1510Sstevel@tonic-gate isa_dma_mctl(dev_info_t *, dev_info_t *, ddi_dma_handle_t, enum ddi_dma_ctlops, 1520Sstevel@tonic-gate off_t *, size_t *, caddr_t *, uint_t); 1530Sstevel@tonic-gate 1540Sstevel@tonic-gate static int 1550Sstevel@tonic-gate isa_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, void *); 1560Sstevel@tonic-gate 1579149SJudy.Chen@Sun.COM static int 1589149SJudy.Chen@Sun.COM isa_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op, 1599149SJudy.Chen@Sun.COM ddi_intr_handle_impl_t *hdlp, void *result); 1609149SJudy.Chen@Sun.COM 1610Sstevel@tonic-gate struct bus_ops isa_bus_ops = { 1620Sstevel@tonic-gate BUSO_REV, 1639149SJudy.Chen@Sun.COM isa_bus_map, 1640Sstevel@tonic-gate NULL, 1650Sstevel@tonic-gate NULL, 1660Sstevel@tonic-gate NULL, 1670Sstevel@tonic-gate i_ddi_map_fault, 1680Sstevel@tonic-gate ddi_dma_map, 1690Sstevel@tonic-gate isa_dma_allochdl, 1700Sstevel@tonic-gate ddi_dma_freehdl, 1710Sstevel@tonic-gate ddi_dma_bindhdl, 1720Sstevel@tonic-gate ddi_dma_unbindhdl, 1730Sstevel@tonic-gate ddi_dma_flush, 1740Sstevel@tonic-gate ddi_dma_win, 1750Sstevel@tonic-gate isa_dma_mctl, 1760Sstevel@tonic-gate isa_ctlops, 1770Sstevel@tonic-gate ddi_bus_prop_op, 1780Sstevel@tonic-gate NULL, /* (*bus_get_eventcookie)(); */ 1790Sstevel@tonic-gate NULL, /* (*bus_add_eventcall)(); */ 1800Sstevel@tonic-gate NULL, /* (*bus_remove_eventcall)(); */ 1810Sstevel@tonic-gate NULL, /* (*bus_post_event)(); */ 1820Sstevel@tonic-gate NULL, /* (*bus_intr_ctl)(); */ 1830Sstevel@tonic-gate NULL, /* (*bus_config)(); */ 1840Sstevel@tonic-gate NULL, /* (*bus_unconfig)(); */ 1850Sstevel@tonic-gate NULL, /* (*bus_fm_init)(); */ 1860Sstevel@tonic-gate NULL, /* (*bus_fm_fini)(); */ 1870Sstevel@tonic-gate NULL, /* (*bus_fm_access_enter)(); */ 1880Sstevel@tonic-gate NULL, /* (*bus_fm_access_exit)(); */ 1890Sstevel@tonic-gate NULL, /* (*bus_power)(); */ 1909149SJudy.Chen@Sun.COM isa_intr_ops /* (*bus_intr_op)(); */ 1910Sstevel@tonic-gate }; 1920Sstevel@tonic-gate 1930Sstevel@tonic-gate 1940Sstevel@tonic-gate static int isa_attach(dev_info_t *devi, ddi_attach_cmd_t cmd); 1950Sstevel@tonic-gate 1960Sstevel@tonic-gate /* 1970Sstevel@tonic-gate * Internal isa ctlops support routines 1980Sstevel@tonic-gate */ 1990Sstevel@tonic-gate static int isa_initchild(dev_info_t *child); 2000Sstevel@tonic-gate 2010Sstevel@tonic-gate struct dev_ops isa_ops = { 2020Sstevel@tonic-gate DEVO_REV, /* devo_rev, */ 2030Sstevel@tonic-gate 0, /* refcnt */ 2040Sstevel@tonic-gate ddi_no_info, /* info */ 2050Sstevel@tonic-gate nulldev, /* identify */ 2060Sstevel@tonic-gate nulldev, /* probe */ 2070Sstevel@tonic-gate isa_attach, /* attach */ 2089149SJudy.Chen@Sun.COM nulldev, /* detach */ 2090Sstevel@tonic-gate nodev, /* reset */ 2100Sstevel@tonic-gate (struct cb_ops *)0, /* driver operations */ 2117656SSherry.Moore@Sun.COM &isa_bus_ops, /* bus operations */ 2127656SSherry.Moore@Sun.COM NULL, /* power */ 2137656SSherry.Moore@Sun.COM ddi_quiesce_not_needed, /* quiesce */ 2140Sstevel@tonic-gate }; 2150Sstevel@tonic-gate 2160Sstevel@tonic-gate /* 2170Sstevel@tonic-gate * Module linkage information for the kernel. 2180Sstevel@tonic-gate */ 2190Sstevel@tonic-gate 2200Sstevel@tonic-gate static struct modldrv modldrv = { 2210Sstevel@tonic-gate &mod_driverops, /* Type of module. This is ISA bus driver */ 2227542SRichard.Bean@Sun.COM "isa nexus driver for 'ISA'", 2230Sstevel@tonic-gate &isa_ops, /* driver ops */ 2240Sstevel@tonic-gate }; 2250Sstevel@tonic-gate 2260Sstevel@tonic-gate static struct modlinkage modlinkage = { 2270Sstevel@tonic-gate MODREV_1, 2280Sstevel@tonic-gate &modldrv, 2290Sstevel@tonic-gate NULL 2300Sstevel@tonic-gate }; 2310Sstevel@tonic-gate 2320Sstevel@tonic-gate int 2330Sstevel@tonic-gate _init(void) 2340Sstevel@tonic-gate { 23510127SLipeng.Sang@Sun.COM int err; 23610127SLipeng.Sang@Sun.COM 23710127SLipeng.Sang@Sun.COM if ((err = mod_install(&modlinkage)) != 0) 23810127SLipeng.Sang@Sun.COM return (err); 23910127SLipeng.Sang@Sun.COM 24010127SLipeng.Sang@Sun.COM impl_bus_add_probe(isa_enumerate); 24110127SLipeng.Sang@Sun.COM return (0); 2420Sstevel@tonic-gate } 2430Sstevel@tonic-gate 2440Sstevel@tonic-gate int 2450Sstevel@tonic-gate _fini(void) 2460Sstevel@tonic-gate { 24710127SLipeng.Sang@Sun.COM int err; 24810127SLipeng.Sang@Sun.COM 24910127SLipeng.Sang@Sun.COM impl_bus_delete_probe(isa_enumerate); 25010127SLipeng.Sang@Sun.COM 25110127SLipeng.Sang@Sun.COM if ((err = mod_remove(&modlinkage)) != 0) 25210127SLipeng.Sang@Sun.COM return (err); 25310127SLipeng.Sang@Sun.COM 25410127SLipeng.Sang@Sun.COM return (0); 2550Sstevel@tonic-gate } 2560Sstevel@tonic-gate 2570Sstevel@tonic-gate int 2580Sstevel@tonic-gate _info(struct modinfo *modinfop) 2590Sstevel@tonic-gate { 2600Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 2610Sstevel@tonic-gate } 2620Sstevel@tonic-gate 2630Sstevel@tonic-gate static int 2640Sstevel@tonic-gate isa_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 2650Sstevel@tonic-gate { 2660Sstevel@tonic-gate int rval; 2670Sstevel@tonic-gate 2685084Sjohnlev #if defined(__xpv) 2695084Sjohnlev /* 2705084Sjohnlev * don't allow isa to attach in domU. this can happen if someone sets 2715084Sjohnlev * the console wrong, etc. ISA devices assume the H/W is there and 2725084Sjohnlev * will cause the domU to panic. 2735084Sjohnlev */ 2745084Sjohnlev if (!DOMAIN_IS_INITDOMAIN(xen_info)) { 2755084Sjohnlev return (DDI_FAILURE); 2765084Sjohnlev } 2775084Sjohnlev #endif 2785084Sjohnlev 2799149SJudy.Chen@Sun.COM switch (cmd) { 2809149SJudy.Chen@Sun.COM case DDI_ATTACH: 2819149SJudy.Chen@Sun.COM break; 2829149SJudy.Chen@Sun.COM case DDI_RESUME: 2839149SJudy.Chen@Sun.COM return (DDI_SUCCESS); 2849149SJudy.Chen@Sun.COM default: 2850Sstevel@tonic-gate return (DDI_FAILURE); 2869149SJudy.Chen@Sun.COM } 2870Sstevel@tonic-gate 28810127SLipeng.Sang@Sun.COM if ((rval = i_dmae_init(devi)) == DDI_SUCCESS) 2890Sstevel@tonic-gate ddi_report_dev(devi); 2909149SJudy.Chen@Sun.COM 2910Sstevel@tonic-gate return (rval); 2920Sstevel@tonic-gate } 2930Sstevel@tonic-gate 2949149SJudy.Chen@Sun.COM #define SET_RNGS(rng_p, used_p, ctyp, ptyp) do { \ 2959149SJudy.Chen@Sun.COM (rng_p)->child_high = (ctyp); \ 2969149SJudy.Chen@Sun.COM (rng_p)->child_low = (rng_p)->parent_low = (used_p)->base; \ 2979149SJudy.Chen@Sun.COM (rng_p)->parent_high = (ptyp); \ 2989149SJudy.Chen@Sun.COM (rng_p)->parent_mid = 0; \ 2999149SJudy.Chen@Sun.COM (rng_p)->size = (used_p)->len; \ 3009149SJudy.Chen@Sun.COM _NOTE(CONSTCOND) } while (0) 3019149SJudy.Chen@Sun.COM static uint_t 3029149SJudy.Chen@Sun.COM isa_used_to_ranges(int ctype, int *array, uint_t size, pib_ranges_t *ranges) 3039149SJudy.Chen@Sun.COM { 3049149SJudy.Chen@Sun.COM used_ranges_t *used_p; 3059149SJudy.Chen@Sun.COM pib_ranges_t *rng_p = ranges; 3069149SJudy.Chen@Sun.COM uint_t i, ptype; 3079149SJudy.Chen@Sun.COM 3089149SJudy.Chen@Sun.COM ptype = (ctype == ISA_ADDR_IO) ? PCI_ADDR_IO : PCI_ADDR_MEM32; 3099149SJudy.Chen@Sun.COM ptype |= PCI_REG_REL_M; 3109149SJudy.Chen@Sun.COM size /= USED_CELL_SIZE; 3119149SJudy.Chen@Sun.COM used_p = (used_ranges_t *)array; 3129149SJudy.Chen@Sun.COM SET_RNGS(rng_p, used_p, ctype, ptype); 3139149SJudy.Chen@Sun.COM for (i = 1, used_p++; i < size; i++, used_p++) { 3149149SJudy.Chen@Sun.COM /* merge ranges record if applicable */ 3159149SJudy.Chen@Sun.COM if (rng_p->child_low + rng_p->size == used_p->base) 3169149SJudy.Chen@Sun.COM rng_p->size += used_p->len; 3179149SJudy.Chen@Sun.COM else { 3189149SJudy.Chen@Sun.COM rng_p++; 3199149SJudy.Chen@Sun.COM SET_RNGS(rng_p, used_p, ctype, ptype); 3209149SJudy.Chen@Sun.COM } 3219149SJudy.Chen@Sun.COM } 3229149SJudy.Chen@Sun.COM return (rng_p - ranges + 1); 3239149SJudy.Chen@Sun.COM } 3249149SJudy.Chen@Sun.COM 3259149SJudy.Chen@Sun.COM void 3269149SJudy.Chen@Sun.COM isa_remove_res_from_pci(int type, int *array, uint_t size) 3279149SJudy.Chen@Sun.COM { 3289149SJudy.Chen@Sun.COM int i; 3299149SJudy.Chen@Sun.COM used_ranges_t *used_p; 3309149SJudy.Chen@Sun.COM 3319149SJudy.Chen@Sun.COM size /= USED_CELL_SIZE; 3329149SJudy.Chen@Sun.COM used_p = (used_ranges_t *)array; 3339149SJudy.Chen@Sun.COM for (i = 0; i < size; i++, used_p++) 3349226SJudy.Chen@Sun.COM pci_register_isa_resources(type, used_p->base, used_p->len); 3359149SJudy.Chen@Sun.COM } 3369149SJudy.Chen@Sun.COM 3379149SJudy.Chen@Sun.COM static void 33810127SLipeng.Sang@Sun.COM isa_create_ranges_prop(dev_info_t *dip) 3399149SJudy.Chen@Sun.COM { 3409149SJudy.Chen@Sun.COM dev_info_t *used; 3419149SJudy.Chen@Sun.COM int *ioarray, *memarray, status; 3429149SJudy.Chen@Sun.COM uint_t nio = 0, nmem = 0, nrng = 0, n; 3439149SJudy.Chen@Sun.COM pib_ranges_t *ranges; 3449149SJudy.Chen@Sun.COM 3459149SJudy.Chen@Sun.COM used = ddi_find_devinfo("used-resources", -1, 0); 3469149SJudy.Chen@Sun.COM if (used == NULL) { 3479149SJudy.Chen@Sun.COM cmn_err(CE_WARN, "Failed to find used-resources <%s>\n", 3489149SJudy.Chen@Sun.COM ddi_get_name(dip)); 3499149SJudy.Chen@Sun.COM return; 3509149SJudy.Chen@Sun.COM } 3519149SJudy.Chen@Sun.COM status = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, used, 3529149SJudy.Chen@Sun.COM DDI_PROP_DONTPASS, "io-space", &ioarray, &nio); 3539149SJudy.Chen@Sun.COM if (status != DDI_PROP_SUCCESS && status != DDI_PROP_NOT_FOUND) { 3549149SJudy.Chen@Sun.COM cmn_err(CE_WARN, "io-space property failure for %s (%x)\n", 3559149SJudy.Chen@Sun.COM ddi_get_name(used), status); 3569149SJudy.Chen@Sun.COM return; 3579149SJudy.Chen@Sun.COM } 3589149SJudy.Chen@Sun.COM status = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, used, 3599149SJudy.Chen@Sun.COM DDI_PROP_DONTPASS, "device-memory", &memarray, &nmem); 3609149SJudy.Chen@Sun.COM if (status != DDI_PROP_SUCCESS && status != DDI_PROP_NOT_FOUND) { 3619149SJudy.Chen@Sun.COM cmn_err(CE_WARN, "device-memory property failure for %s (%x)\n", 3629149SJudy.Chen@Sun.COM ddi_get_name(used), status); 3639149SJudy.Chen@Sun.COM return; 3649149SJudy.Chen@Sun.COM } 3659149SJudy.Chen@Sun.COM n = (nio + nmem) / USED_CELL_SIZE; 3669149SJudy.Chen@Sun.COM ranges = (pib_ranges_t *)kmem_zalloc(sizeof (pib_ranges_t) * n, 3679149SJudy.Chen@Sun.COM KM_SLEEP); 3689149SJudy.Chen@Sun.COM 3699149SJudy.Chen@Sun.COM if (nio != 0) { 3709149SJudy.Chen@Sun.COM nrng = isa_used_to_ranges(ISA_ADDR_IO, ioarray, nio, ranges); 3719149SJudy.Chen@Sun.COM isa_remove_res_from_pci(ISA_ADDR_IO, ioarray, nio); 3729149SJudy.Chen@Sun.COM ddi_prop_free(ioarray); 3739149SJudy.Chen@Sun.COM } 3749149SJudy.Chen@Sun.COM if (nmem != 0) { 3759149SJudy.Chen@Sun.COM nrng += isa_used_to_ranges(ISA_ADDR_MEM, memarray, nmem, 3769149SJudy.Chen@Sun.COM ranges + nrng); 3779149SJudy.Chen@Sun.COM isa_remove_res_from_pci(ISA_ADDR_MEM, memarray, nmem); 3789149SJudy.Chen@Sun.COM ddi_prop_free(memarray); 3799149SJudy.Chen@Sun.COM } 3809149SJudy.Chen@Sun.COM 3819226SJudy.Chen@Sun.COM if (!pseudo_isa) 3829226SJudy.Chen@Sun.COM (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, "ranges", 3839226SJudy.Chen@Sun.COM (int *)ranges, nrng * sizeof (pib_ranges_t) / sizeof (int)); 3849149SJudy.Chen@Sun.COM kmem_free(ranges, sizeof (pib_ranges_t) * n); 3859149SJudy.Chen@Sun.COM } 3869149SJudy.Chen@Sun.COM 3879149SJudy.Chen@Sun.COM /*ARGSUSED*/ 3889149SJudy.Chen@Sun.COM static int 3899149SJudy.Chen@Sun.COM isa_apply_range(dev_info_t *dip, struct regspec *isa_reg_p, 3909149SJudy.Chen@Sun.COM pci_regspec_t *pci_reg_p) 3919149SJudy.Chen@Sun.COM { 3929149SJudy.Chen@Sun.COM pib_ranges_t *ranges, *rng_p; 3939149SJudy.Chen@Sun.COM int len, i, offset, nrange; 3949149SJudy.Chen@Sun.COM 3959149SJudy.Chen@Sun.COM if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 3969149SJudy.Chen@Sun.COM "ranges", (caddr_t)&ranges, &len) != DDI_SUCCESS) { 3979149SJudy.Chen@Sun.COM cmn_err(CE_WARN, "Can't get %s ranges property", 3989149SJudy.Chen@Sun.COM ddi_get_name(dip)); 3999149SJudy.Chen@Sun.COM return (DDI_ME_REGSPEC_RANGE); 4009149SJudy.Chen@Sun.COM } 4019149SJudy.Chen@Sun.COM nrange = len / sizeof (pib_ranges_t); 4029149SJudy.Chen@Sun.COM rng_p = ranges; 4039149SJudy.Chen@Sun.COM for (i = 0; i < nrange; i++, rng_p++) { 4049149SJudy.Chen@Sun.COM /* Check for correct space */ 4059149SJudy.Chen@Sun.COM if (isa_reg_p->regspec_bustype != rng_p->child_high) 4069149SJudy.Chen@Sun.COM continue; 4079149SJudy.Chen@Sun.COM 4089149SJudy.Chen@Sun.COM /* Detect whether request entirely fits within a range */ 4099149SJudy.Chen@Sun.COM if (isa_reg_p->regspec_addr < rng_p->child_low) 4109149SJudy.Chen@Sun.COM continue; 4119481SJudy.Chen@Sun.COM if ((isa_reg_p->regspec_addr + isa_reg_p->regspec_size - 1) > 4129481SJudy.Chen@Sun.COM (rng_p->child_low + rng_p->size - 1)) 4139149SJudy.Chen@Sun.COM continue; 4149149SJudy.Chen@Sun.COM 4159149SJudy.Chen@Sun.COM offset = isa_reg_p->regspec_addr - rng_p->child_low; 4169149SJudy.Chen@Sun.COM 4179149SJudy.Chen@Sun.COM pci_reg_p->pci_phys_hi = rng_p->parent_high; 4189149SJudy.Chen@Sun.COM pci_reg_p->pci_phys_mid = 0; 4199149SJudy.Chen@Sun.COM pci_reg_p->pci_phys_low = rng_p->parent_low + offset; 4209149SJudy.Chen@Sun.COM pci_reg_p->pci_size_hi = 0; 4219149SJudy.Chen@Sun.COM pci_reg_p->pci_size_low = isa_reg_p->regspec_size; 4229149SJudy.Chen@Sun.COM 4239149SJudy.Chen@Sun.COM break; 4249149SJudy.Chen@Sun.COM } 4259149SJudy.Chen@Sun.COM kmem_free(ranges, len); 4269226SJudy.Chen@Sun.COM 4279226SJudy.Chen@Sun.COM if (i < nrange) 4289226SJudy.Chen@Sun.COM return (DDI_SUCCESS); 4299226SJudy.Chen@Sun.COM 4309226SJudy.Chen@Sun.COM /* 4319226SJudy.Chen@Sun.COM * Check extra resource range specially for serial and parallel 4329226SJudy.Chen@Sun.COM * devices, which are treated differently from all other ISA 4339226SJudy.Chen@Sun.COM * devices. On some machines, serial ports are not enumerated 4349226SJudy.Chen@Sun.COM * by ACPI but by BIOS, with io base addresses noted in legacy 4359226SJudy.Chen@Sun.COM * BIOS data area. Parallel port on some machines comes with 4369226SJudy.Chen@Sun.COM * illegal size. 4379226SJudy.Chen@Sun.COM */ 4389226SJudy.Chen@Sun.COM if (isa_reg_p->regspec_bustype != ISA_ADDR_IO) 4399226SJudy.Chen@Sun.COM goto out_of_range; 4409226SJudy.Chen@Sun.COM 4419226SJudy.Chen@Sun.COM for (i = 0; i < isa_extra_count; i++) { 4429226SJudy.Chen@Sun.COM struct regspec *reg_p = &isa_extra_resource[i]; 4439226SJudy.Chen@Sun.COM 4449226SJudy.Chen@Sun.COM if (isa_reg_p->regspec_addr < reg_p->regspec_addr) 4459226SJudy.Chen@Sun.COM continue; 4469226SJudy.Chen@Sun.COM if ((isa_reg_p->regspec_addr + isa_reg_p->regspec_size) > 4479226SJudy.Chen@Sun.COM (reg_p->regspec_addr + reg_p->regspec_size)) 4489226SJudy.Chen@Sun.COM continue; 4499226SJudy.Chen@Sun.COM 4509226SJudy.Chen@Sun.COM pci_reg_p->pci_phys_hi = PCI_ADDR_IO | PCI_REG_REL_M; 4519226SJudy.Chen@Sun.COM pci_reg_p->pci_phys_mid = 0; 4529226SJudy.Chen@Sun.COM pci_reg_p->pci_phys_low = isa_reg_p->regspec_addr; 4539226SJudy.Chen@Sun.COM pci_reg_p->pci_size_hi = 0; 4549226SJudy.Chen@Sun.COM pci_reg_p->pci_size_low = isa_reg_p->regspec_size; 4559226SJudy.Chen@Sun.COM break; 4569149SJudy.Chen@Sun.COM } 4579226SJudy.Chen@Sun.COM if (i < isa_extra_count) 4589226SJudy.Chen@Sun.COM return (DDI_SUCCESS); 4599149SJudy.Chen@Sun.COM 4609226SJudy.Chen@Sun.COM out_of_range: 4619226SJudy.Chen@Sun.COM cmn_err(CE_WARN, "isa_apply_range: Out of range base <0x%x>, size <%d>", 4629226SJudy.Chen@Sun.COM isa_reg_p->regspec_addr, isa_reg_p->regspec_size); 4639226SJudy.Chen@Sun.COM return (DDI_ME_REGSPEC_RANGE); 4649149SJudy.Chen@Sun.COM } 4659149SJudy.Chen@Sun.COM 4669149SJudy.Chen@Sun.COM static int 4679149SJudy.Chen@Sun.COM isa_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 4689149SJudy.Chen@Sun.COM off_t offset, off_t len, caddr_t *vaddrp) 4699149SJudy.Chen@Sun.COM { 4709149SJudy.Chen@Sun.COM struct regspec tmp_reg, *rp; 4719149SJudy.Chen@Sun.COM pci_regspec_t vreg; 4729149SJudy.Chen@Sun.COM ddi_map_req_t mr = *mp; /* Get private copy of request */ 4739149SJudy.Chen@Sun.COM int error; 4749149SJudy.Chen@Sun.COM 4759149SJudy.Chen@Sun.COM if (pseudo_isa) 4769149SJudy.Chen@Sun.COM return (i_ddi_bus_map(dip, rdip, mp, offset, len, vaddrp)); 4779149SJudy.Chen@Sun.COM 4789149SJudy.Chen@Sun.COM mp = &mr; 4799149SJudy.Chen@Sun.COM 4809149SJudy.Chen@Sun.COM /* 4819149SJudy.Chen@Sun.COM * First, if given an rnumber, convert it to a regspec... 4829149SJudy.Chen@Sun.COM */ 4839149SJudy.Chen@Sun.COM if (mp->map_type == DDI_MT_RNUMBER) { 4849149SJudy.Chen@Sun.COM 4859149SJudy.Chen@Sun.COM int rnumber = mp->map_obj.rnumber; 4869149SJudy.Chen@Sun.COM 4879149SJudy.Chen@Sun.COM rp = i_ddi_rnumber_to_regspec(rdip, rnumber); 4889149SJudy.Chen@Sun.COM if (rp == (struct regspec *)0) 4899149SJudy.Chen@Sun.COM return (DDI_ME_RNUMBER_RANGE); 4909149SJudy.Chen@Sun.COM 4919149SJudy.Chen@Sun.COM /* 4929149SJudy.Chen@Sun.COM * Convert the given ddi_map_req_t from rnumber to regspec... 4939149SJudy.Chen@Sun.COM */ 4949149SJudy.Chen@Sun.COM mp->map_type = DDI_MT_REGSPEC; 4959149SJudy.Chen@Sun.COM mp->map_obj.rp = rp; 4969149SJudy.Chen@Sun.COM } 4979149SJudy.Chen@Sun.COM 4989149SJudy.Chen@Sun.COM /* 4999149SJudy.Chen@Sun.COM * Adjust offset and length correspnding to called values... 5009149SJudy.Chen@Sun.COM * XXX: A non-zero length means override the one in the regspec. 5019149SJudy.Chen@Sun.COM * XXX: (Regardless of what's in the parent's range) 5029149SJudy.Chen@Sun.COM */ 5039149SJudy.Chen@Sun.COM 5049149SJudy.Chen@Sun.COM tmp_reg = *(mp->map_obj.rp); /* Preserve underlying data */ 5059149SJudy.Chen@Sun.COM rp = mp->map_obj.rp = &tmp_reg; /* Use tmp_reg in request */ 5069149SJudy.Chen@Sun.COM 5079149SJudy.Chen@Sun.COM rp->regspec_addr += (uint_t)offset; 5089149SJudy.Chen@Sun.COM if (len != 0) 5099149SJudy.Chen@Sun.COM rp->regspec_size = (uint_t)len; 5109149SJudy.Chen@Sun.COM 5119149SJudy.Chen@Sun.COM if ((error = isa_apply_range(dip, rp, &vreg)) != 0) 5129149SJudy.Chen@Sun.COM return (error); 5139149SJudy.Chen@Sun.COM mp->map_obj.rp = (struct regspec *)&vreg; 5149149SJudy.Chen@Sun.COM 5159149SJudy.Chen@Sun.COM /* 5169149SJudy.Chen@Sun.COM * Call my parents bus_map function with modified values... 5179149SJudy.Chen@Sun.COM */ 5189149SJudy.Chen@Sun.COM 5199149SJudy.Chen@Sun.COM return (ddi_map(dip, mp, (off_t)0, (off_t)0, vaddrp)); 5209149SJudy.Chen@Sun.COM } 5219149SJudy.Chen@Sun.COM 5220Sstevel@tonic-gate static int 5230Sstevel@tonic-gate isa_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_attr_t *dma_attr, 5240Sstevel@tonic-gate int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *handlep) 5250Sstevel@tonic-gate { 5260Sstevel@tonic-gate ddi_dma_attr_merge(dma_attr, &ISA_dma_attr); 5270Sstevel@tonic-gate return (ddi_dma_allochdl(dip, rdip, dma_attr, waitfp, arg, handlep)); 5280Sstevel@tonic-gate } 5290Sstevel@tonic-gate 5300Sstevel@tonic-gate static int 5310Sstevel@tonic-gate isa_dma_mctl(dev_info_t *dip, dev_info_t *rdip, 5320Sstevel@tonic-gate ddi_dma_handle_t handle, enum ddi_dma_ctlops request, 5330Sstevel@tonic-gate off_t *offp, size_t *lenp, caddr_t *objp, uint_t flags) 5340Sstevel@tonic-gate { 5350Sstevel@tonic-gate int rval; 5360Sstevel@tonic-gate ddi_dma_lim_t defalt; 5370Sstevel@tonic-gate int arg = (int)(uintptr_t)objp; 5380Sstevel@tonic-gate 5390Sstevel@tonic-gate switch (request) { 5400Sstevel@tonic-gate 5410Sstevel@tonic-gate case DDI_DMA_E_PROG: 5420Sstevel@tonic-gate return (i_dmae_prog(rdip, (struct ddi_dmae_req *)offp, 5430Sstevel@tonic-gate (ddi_dma_cookie_t *)lenp, arg)); 5440Sstevel@tonic-gate 5450Sstevel@tonic-gate case DDI_DMA_E_ACQUIRE: 5460Sstevel@tonic-gate return (i_dmae_acquire(rdip, arg, (int(*)(caddr_t))offp, 5470Sstevel@tonic-gate (caddr_t)lenp)); 5480Sstevel@tonic-gate 5490Sstevel@tonic-gate case DDI_DMA_E_FREE: 5500Sstevel@tonic-gate return (i_dmae_free(rdip, arg)); 5510Sstevel@tonic-gate 5520Sstevel@tonic-gate case DDI_DMA_E_STOP: 5530Sstevel@tonic-gate i_dmae_stop(rdip, arg); 5540Sstevel@tonic-gate return (DDI_SUCCESS); 5550Sstevel@tonic-gate 5560Sstevel@tonic-gate case DDI_DMA_E_ENABLE: 5570Sstevel@tonic-gate i_dmae_enable(rdip, arg); 5580Sstevel@tonic-gate return (DDI_SUCCESS); 5590Sstevel@tonic-gate 5600Sstevel@tonic-gate case DDI_DMA_E_DISABLE: 5610Sstevel@tonic-gate i_dmae_disable(rdip, arg); 5620Sstevel@tonic-gate return (DDI_SUCCESS); 5630Sstevel@tonic-gate 5640Sstevel@tonic-gate case DDI_DMA_E_GETCNT: 5650Sstevel@tonic-gate i_dmae_get_chan_stat(rdip, arg, NULL, (int *)lenp); 5660Sstevel@tonic-gate return (DDI_SUCCESS); 5670Sstevel@tonic-gate 5680Sstevel@tonic-gate case DDI_DMA_E_SWSETUP: 5690Sstevel@tonic-gate return (i_dmae_swsetup(rdip, (struct ddi_dmae_req *)offp, 5700Sstevel@tonic-gate (ddi_dma_cookie_t *)lenp, arg)); 5710Sstevel@tonic-gate 5720Sstevel@tonic-gate case DDI_DMA_E_SWSTART: 5730Sstevel@tonic-gate i_dmae_swstart(rdip, arg); 5740Sstevel@tonic-gate return (DDI_SUCCESS); 5750Sstevel@tonic-gate 5760Sstevel@tonic-gate case DDI_DMA_E_GETLIM: 5770Sstevel@tonic-gate bcopy(&ISA_dma_limits, objp, sizeof (ddi_dma_lim_t)); 5780Sstevel@tonic-gate return (DDI_SUCCESS); 5790Sstevel@tonic-gate 5800Sstevel@tonic-gate case DDI_DMA_E_GETATTR: 5810Sstevel@tonic-gate bcopy(&ISA_dma_attr, objp, sizeof (ddi_dma_attr_t)); 5820Sstevel@tonic-gate return (DDI_SUCCESS); 5830Sstevel@tonic-gate 5840Sstevel@tonic-gate case DDI_DMA_E_1STPTY: 5850Sstevel@tonic-gate { 5860Sstevel@tonic-gate struct ddi_dmae_req req1stpty = 5870Sstevel@tonic-gate { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 5880Sstevel@tonic-gate if (arg == 0) { 5890Sstevel@tonic-gate req1stpty.der_command = DMAE_CMD_TRAN; 5900Sstevel@tonic-gate req1stpty.der_trans = DMAE_TRANS_DMND; 5910Sstevel@tonic-gate } else { 5920Sstevel@tonic-gate req1stpty.der_trans = DMAE_TRANS_CSCD; 5930Sstevel@tonic-gate } 5940Sstevel@tonic-gate return (i_dmae_prog(rdip, &req1stpty, NULL, arg)); 5950Sstevel@tonic-gate } 5960Sstevel@tonic-gate 5970Sstevel@tonic-gate case DDI_DMA_IOPB_ALLOC: /* get contiguous DMA-able memory */ 5980Sstevel@tonic-gate case DDI_DMA_SMEM_ALLOC: 5990Sstevel@tonic-gate if (!offp) { 6000Sstevel@tonic-gate defalt = ISA_dma_limits; 6010Sstevel@tonic-gate offp = (off_t *)&defalt; 6020Sstevel@tonic-gate } 6030Sstevel@tonic-gate /*FALLTHROUGH*/ 6040Sstevel@tonic-gate default: 6050Sstevel@tonic-gate rval = ddi_dma_mctl(dip, rdip, handle, request, offp, 6060Sstevel@tonic-gate lenp, objp, flags); 6070Sstevel@tonic-gate } 6080Sstevel@tonic-gate return (rval); 6090Sstevel@tonic-gate } 6100Sstevel@tonic-gate 6110Sstevel@tonic-gate /* 6120Sstevel@tonic-gate * Check if driver should be treated as an old pre 2.6 driver 6130Sstevel@tonic-gate */ 6140Sstevel@tonic-gate static int 6150Sstevel@tonic-gate old_driver(dev_info_t *dip) 6160Sstevel@tonic-gate { 6170Sstevel@tonic-gate extern int ignore_hardware_nodes; /* force flag from ddi_impl.c */ 6180Sstevel@tonic-gate 6190Sstevel@tonic-gate if (ndi_dev_is_persistent_node(dip)) { 6200Sstevel@tonic-gate if (ignore_hardware_nodes) 6210Sstevel@tonic-gate return (1); 6220Sstevel@tonic-gate if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 6230Sstevel@tonic-gate "ignore-hardware-nodes", -1) != -1) 6240Sstevel@tonic-gate return (1); 6250Sstevel@tonic-gate } 6260Sstevel@tonic-gate return (0); 6270Sstevel@tonic-gate } 6280Sstevel@tonic-gate 6290Sstevel@tonic-gate typedef struct { 6300Sstevel@tonic-gate uint32_t phys_hi; 6310Sstevel@tonic-gate uint32_t phys_lo; 6320Sstevel@tonic-gate uint32_t size; 6330Sstevel@tonic-gate } isa_regs_t; 6340Sstevel@tonic-gate 6350Sstevel@tonic-gate /* 6360Sstevel@tonic-gate * Return non-zero if device in tree is a PnP isa device 6370Sstevel@tonic-gate */ 6380Sstevel@tonic-gate static int 6390Sstevel@tonic-gate is_pnpisa(dev_info_t *dip) 6400Sstevel@tonic-gate { 6410Sstevel@tonic-gate isa_regs_t *isa_regs; 6420Sstevel@tonic-gate int proplen, pnpisa; 6430Sstevel@tonic-gate 6440Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg", 6455084Sjohnlev (caddr_t)&isa_regs, &proplen) != DDI_PROP_SUCCESS) { 6460Sstevel@tonic-gate return (0); 6470Sstevel@tonic-gate } 6480Sstevel@tonic-gate pnpisa = isa_regs[0].phys_hi & 0x80000000; 6490Sstevel@tonic-gate /* 6500Sstevel@tonic-gate * free the memory allocated by ddi_getlongprop(). 6510Sstevel@tonic-gate */ 6520Sstevel@tonic-gate kmem_free(isa_regs, proplen); 6530Sstevel@tonic-gate if (pnpisa) 6540Sstevel@tonic-gate return (1); 6550Sstevel@tonic-gate else 6560Sstevel@tonic-gate return (0); 6570Sstevel@tonic-gate } 6580Sstevel@tonic-gate 6590Sstevel@tonic-gate /*ARGSUSED*/ 6600Sstevel@tonic-gate static int 6610Sstevel@tonic-gate isa_ctlops(dev_info_t *dip, dev_info_t *rdip, 6620Sstevel@tonic-gate ddi_ctl_enum_t ctlop, void *arg, void *result) 6630Sstevel@tonic-gate { 6649149SJudy.Chen@Sun.COM int rn; 6659149SJudy.Chen@Sun.COM struct ddi_parent_private_data *pdp; 6669149SJudy.Chen@Sun.COM 6670Sstevel@tonic-gate switch (ctlop) { 6680Sstevel@tonic-gate case DDI_CTLOPS_REPORTDEV: 6690Sstevel@tonic-gate if (rdip == (dev_info_t *)0) 6700Sstevel@tonic-gate return (DDI_FAILURE); 6710Sstevel@tonic-gate cmn_err(CE_CONT, "?ISA-device: %s%d\n", 6720Sstevel@tonic-gate ddi_driver_name(rdip), ddi_get_instance(rdip)); 6730Sstevel@tonic-gate return (DDI_SUCCESS); 6740Sstevel@tonic-gate 6750Sstevel@tonic-gate case DDI_CTLOPS_INITCHILD: 6760Sstevel@tonic-gate /* 6770Sstevel@tonic-gate * older drivers aren't expecting the "standard" device 6780Sstevel@tonic-gate * node format used by the hardware nodes. these drivers 6790Sstevel@tonic-gate * only expect their own properties set in their driver.conf 6800Sstevel@tonic-gate * files. so they tell us not to call them with hardware 6810Sstevel@tonic-gate * nodes by setting the property "ignore-hardware-nodes". 6820Sstevel@tonic-gate */ 6830Sstevel@tonic-gate if (old_driver((dev_info_t *)arg)) { 6840Sstevel@tonic-gate return (DDI_NOT_WELL_FORMED); 6850Sstevel@tonic-gate } 6860Sstevel@tonic-gate 6870Sstevel@tonic-gate return (isa_initchild((dev_info_t *)arg)); 6880Sstevel@tonic-gate 6890Sstevel@tonic-gate case DDI_CTLOPS_UNINITCHILD: 6900Sstevel@tonic-gate impl_ddi_sunbus_removechild((dev_info_t *)arg); 6910Sstevel@tonic-gate return (DDI_SUCCESS); 6920Sstevel@tonic-gate 6930Sstevel@tonic-gate case DDI_CTLOPS_SIDDEV: 69411175SKerry.Shu@Sun.COM if (ndi_dev_is_persistent_node(rdip)) 69511175SKerry.Shu@Sun.COM return (DDI_SUCCESS); 6960Sstevel@tonic-gate /* 6970Sstevel@tonic-gate * All ISA devices need to do confirming probes 6980Sstevel@tonic-gate * unless they are PnP ISA. 6990Sstevel@tonic-gate */ 70011175SKerry.Shu@Sun.COM if (is_pnpisa(rdip)) 7010Sstevel@tonic-gate return (DDI_SUCCESS); 7020Sstevel@tonic-gate else 7030Sstevel@tonic-gate return (DDI_FAILURE); 7040Sstevel@tonic-gate 7059149SJudy.Chen@Sun.COM case DDI_CTLOPS_REGSIZE: 7069149SJudy.Chen@Sun.COM case DDI_CTLOPS_NREGS: 7079149SJudy.Chen@Sun.COM if (rdip == (dev_info_t *)0) 7089149SJudy.Chen@Sun.COM return (DDI_FAILURE); 7099149SJudy.Chen@Sun.COM 7109149SJudy.Chen@Sun.COM if ((pdp = ddi_get_parent_data(rdip)) == NULL) 7119149SJudy.Chen@Sun.COM return (DDI_FAILURE); 7129149SJudy.Chen@Sun.COM 7139149SJudy.Chen@Sun.COM if (ctlop == DDI_CTLOPS_NREGS) { 7149149SJudy.Chen@Sun.COM *(int *)result = pdp->par_nreg; 7159149SJudy.Chen@Sun.COM } else { 7169149SJudy.Chen@Sun.COM rn = *(int *)arg; 7179149SJudy.Chen@Sun.COM if (rn >= pdp->par_nreg) 7189149SJudy.Chen@Sun.COM return (DDI_FAILURE); 7199149SJudy.Chen@Sun.COM *(off_t *)result = (off_t)pdp->par_reg[rn].regspec_size; 7209149SJudy.Chen@Sun.COM } 7219149SJudy.Chen@Sun.COM return (DDI_SUCCESS); 7229149SJudy.Chen@Sun.COM 7239149SJudy.Chen@Sun.COM case DDI_CTLOPS_ATTACH: 7249149SJudy.Chen@Sun.COM case DDI_CTLOPS_DETACH: 7259149SJudy.Chen@Sun.COM case DDI_CTLOPS_PEEK: 7269149SJudy.Chen@Sun.COM case DDI_CTLOPS_POKE: 7279149SJudy.Chen@Sun.COM return (DDI_FAILURE); 7289149SJudy.Chen@Sun.COM 7290Sstevel@tonic-gate default: 7300Sstevel@tonic-gate return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 7310Sstevel@tonic-gate } 7320Sstevel@tonic-gate } 7330Sstevel@tonic-gate 7349149SJudy.Chen@Sun.COM static struct intrspec * 7359149SJudy.Chen@Sun.COM isa_get_ispec(dev_info_t *rdip, int inum) 7369149SJudy.Chen@Sun.COM { 7379149SJudy.Chen@Sun.COM struct ddi_parent_private_data *pdp = ddi_get_parent_data(rdip); 7389149SJudy.Chen@Sun.COM 7399149SJudy.Chen@Sun.COM /* Validate the interrupt number */ 7409149SJudy.Chen@Sun.COM if (inum >= pdp->par_nintr) 7419149SJudy.Chen@Sun.COM return (NULL); 7429149SJudy.Chen@Sun.COM 7439149SJudy.Chen@Sun.COM /* Get the interrupt structure pointer and return that */ 7449149SJudy.Chen@Sun.COM return ((struct intrspec *)&pdp->par_intr[inum]); 7459149SJudy.Chen@Sun.COM } 7469149SJudy.Chen@Sun.COM 7479149SJudy.Chen@Sun.COM static int 7489149SJudy.Chen@Sun.COM isa_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op, 7499149SJudy.Chen@Sun.COM ddi_intr_handle_impl_t *hdlp, void *result) 7509149SJudy.Chen@Sun.COM { 7519149SJudy.Chen@Sun.COM struct intrspec *ispec; 7529149SJudy.Chen@Sun.COM 7539149SJudy.Chen@Sun.COM if (pseudo_isa) 7549149SJudy.Chen@Sun.COM return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result)); 7559149SJudy.Chen@Sun.COM 7569149SJudy.Chen@Sun.COM 7579149SJudy.Chen@Sun.COM /* Process the interrupt operation */ 7589149SJudy.Chen@Sun.COM switch (intr_op) { 7599149SJudy.Chen@Sun.COM case DDI_INTROP_GETCAP: 7609149SJudy.Chen@Sun.COM /* First check with pcplusmp */ 7619149SJudy.Chen@Sun.COM if (psm_intr_ops == NULL) 7629149SJudy.Chen@Sun.COM return (DDI_FAILURE); 7639149SJudy.Chen@Sun.COM 7649149SJudy.Chen@Sun.COM if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_GET_CAP, result)) { 7659149SJudy.Chen@Sun.COM *(int *)result = 0; 7669149SJudy.Chen@Sun.COM return (DDI_FAILURE); 7679149SJudy.Chen@Sun.COM } 7689149SJudy.Chen@Sun.COM break; 7699149SJudy.Chen@Sun.COM case DDI_INTROP_SETCAP: 7709149SJudy.Chen@Sun.COM if (psm_intr_ops == NULL) 7719149SJudy.Chen@Sun.COM return (DDI_FAILURE); 7729149SJudy.Chen@Sun.COM 7739149SJudy.Chen@Sun.COM if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_CAP, result)) 7749149SJudy.Chen@Sun.COM return (DDI_FAILURE); 7759149SJudy.Chen@Sun.COM break; 7769149SJudy.Chen@Sun.COM case DDI_INTROP_ALLOC: 7779149SJudy.Chen@Sun.COM if ((ispec = isa_get_ispec(rdip, hdlp->ih_inum)) == NULL) 7789149SJudy.Chen@Sun.COM return (DDI_FAILURE); 7799149SJudy.Chen@Sun.COM hdlp->ih_pri = ispec->intrspec_pri; 7809149SJudy.Chen@Sun.COM *(int *)result = hdlp->ih_scratch1; 7819149SJudy.Chen@Sun.COM break; 7829149SJudy.Chen@Sun.COM case DDI_INTROP_FREE: 7839149SJudy.Chen@Sun.COM break; 7849149SJudy.Chen@Sun.COM case DDI_INTROP_GETPRI: 7859149SJudy.Chen@Sun.COM if ((ispec = isa_get_ispec(rdip, hdlp->ih_inum)) == NULL) 7869149SJudy.Chen@Sun.COM return (DDI_FAILURE); 7879149SJudy.Chen@Sun.COM *(int *)result = ispec->intrspec_pri; 7889149SJudy.Chen@Sun.COM break; 7899149SJudy.Chen@Sun.COM case DDI_INTROP_SETPRI: 7909149SJudy.Chen@Sun.COM /* Validate the interrupt priority passed to us */ 7919149SJudy.Chen@Sun.COM if (*(int *)result > LOCK_LEVEL) 7929149SJudy.Chen@Sun.COM return (DDI_FAILURE); 7939149SJudy.Chen@Sun.COM 7949149SJudy.Chen@Sun.COM /* Ensure that PSM is all initialized and ispec is ok */ 7959149SJudy.Chen@Sun.COM if ((psm_intr_ops == NULL) || 7969149SJudy.Chen@Sun.COM ((ispec = isa_get_ispec(rdip, hdlp->ih_inum)) == NULL)) 7979149SJudy.Chen@Sun.COM return (DDI_FAILURE); 7989149SJudy.Chen@Sun.COM 7999149SJudy.Chen@Sun.COM /* update the ispec with the new priority */ 8009149SJudy.Chen@Sun.COM ispec->intrspec_pri = *(int *)result; 8019149SJudy.Chen@Sun.COM break; 8029149SJudy.Chen@Sun.COM case DDI_INTROP_ADDISR: 8039149SJudy.Chen@Sun.COM if ((ispec = isa_get_ispec(rdip, hdlp->ih_inum)) == NULL) 8049149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8059149SJudy.Chen@Sun.COM ispec->intrspec_func = hdlp->ih_cb_func; 8069149SJudy.Chen@Sun.COM break; 8079149SJudy.Chen@Sun.COM case DDI_INTROP_REMISR: 8089149SJudy.Chen@Sun.COM if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 8099149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8109149SJudy.Chen@Sun.COM if ((ispec = isa_get_ispec(rdip, hdlp->ih_inum)) == NULL) 8119149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8129149SJudy.Chen@Sun.COM ispec->intrspec_func = (uint_t (*)()) 0; 8139149SJudy.Chen@Sun.COM break; 8149149SJudy.Chen@Sun.COM case DDI_INTROP_ENABLE: 8159149SJudy.Chen@Sun.COM if ((ispec = isa_get_ispec(rdip, hdlp->ih_inum)) == NULL) 8169149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8179149SJudy.Chen@Sun.COM 8189149SJudy.Chen@Sun.COM /* Call psmi to translate irq with the dip */ 8199149SJudy.Chen@Sun.COM if (psm_intr_ops == NULL) 8209149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8219149SJudy.Chen@Sun.COM 82210175SStuart.Maybee@Sun.COM #if defined(__xpv) 82310175SStuart.Maybee@Sun.COM /* 82410175SStuart.Maybee@Sun.COM * if the hypervisor is using an isa serial port for the 82510175SStuart.Maybee@Sun.COM * console, make sure we don't try to use that interrupt as 82610175SStuart.Maybee@Sun.COM * it will cause us to panic when xen_bind_pirq() fails. 82710175SStuart.Maybee@Sun.COM */ 82810175SStuart.Maybee@Sun.COM if (((ispec->intrspec_vec == 4) && 82910175SStuart.Maybee@Sun.COM (console_hypervisor_device == CONS_TTYA)) || 83010175SStuart.Maybee@Sun.COM ((ispec->intrspec_vec == 3) && 83110175SStuart.Maybee@Sun.COM (console_hypervisor_device == CONS_TTYB))) { 83210175SStuart.Maybee@Sun.COM return (DDI_FAILURE); 83310175SStuart.Maybee@Sun.COM } 83410175SStuart.Maybee@Sun.COM #endif 8359149SJudy.Chen@Sun.COM ((ihdl_plat_t *)hdlp->ih_private)->ip_ispecp = ispec; 836*11465SKerry.Shu@Sun.COM if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, 837*11465SKerry.Shu@Sun.COM (int *)&hdlp->ih_vector) == PSM_FAILURE) 838*11465SKerry.Shu@Sun.COM return (DDI_FAILURE); 8399149SJudy.Chen@Sun.COM 8409149SJudy.Chen@Sun.COM /* Add the interrupt handler */ 8419149SJudy.Chen@Sun.COM if (!add_avintr((void *)hdlp, ispec->intrspec_pri, 8429149SJudy.Chen@Sun.COM hdlp->ih_cb_func, DEVI(rdip)->devi_name, hdlp->ih_vector, 8439149SJudy.Chen@Sun.COM hdlp->ih_cb_arg1, hdlp->ih_cb_arg2, NULL, rdip)) 8449149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8459149SJudy.Chen@Sun.COM break; 8469149SJudy.Chen@Sun.COM case DDI_INTROP_DISABLE: 8479149SJudy.Chen@Sun.COM if ((ispec = isa_get_ispec(rdip, hdlp->ih_inum)) == NULL) 8489149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8499149SJudy.Chen@Sun.COM 8509149SJudy.Chen@Sun.COM /* Call psm_ops() to translate irq with the dip */ 8519149SJudy.Chen@Sun.COM if (psm_intr_ops == NULL) 8529149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8539149SJudy.Chen@Sun.COM 8549149SJudy.Chen@Sun.COM ((ihdl_plat_t *)hdlp->ih_private)->ip_ispecp = ispec; 8559149SJudy.Chen@Sun.COM (void) (*psm_intr_ops)(rdip, hdlp, 8569149SJudy.Chen@Sun.COM PSM_INTR_OP_XLATE_VECTOR, (int *)&hdlp->ih_vector); 8579149SJudy.Chen@Sun.COM 8589149SJudy.Chen@Sun.COM /* Remove the interrupt handler */ 8599149SJudy.Chen@Sun.COM rem_avintr((void *)hdlp, ispec->intrspec_pri, 8609149SJudy.Chen@Sun.COM hdlp->ih_cb_func, hdlp->ih_vector); 8619149SJudy.Chen@Sun.COM break; 8629149SJudy.Chen@Sun.COM case DDI_INTROP_SETMASK: 8639149SJudy.Chen@Sun.COM if (psm_intr_ops == NULL) 8649149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8659149SJudy.Chen@Sun.COM 8669149SJudy.Chen@Sun.COM if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_MASK, NULL)) 8679149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8689149SJudy.Chen@Sun.COM break; 8699149SJudy.Chen@Sun.COM case DDI_INTROP_CLRMASK: 8709149SJudy.Chen@Sun.COM if (psm_intr_ops == NULL) 8719149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8729149SJudy.Chen@Sun.COM 8739149SJudy.Chen@Sun.COM if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_CLEAR_MASK, NULL)) 8749149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8759149SJudy.Chen@Sun.COM break; 8769149SJudy.Chen@Sun.COM case DDI_INTROP_GETPENDING: 8779149SJudy.Chen@Sun.COM if (psm_intr_ops == NULL) 8789149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8799149SJudy.Chen@Sun.COM 8809149SJudy.Chen@Sun.COM if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_GET_PENDING, 8819149SJudy.Chen@Sun.COM result)) { 8829149SJudy.Chen@Sun.COM *(int *)result = 0; 8839149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8849149SJudy.Chen@Sun.COM } 8859149SJudy.Chen@Sun.COM break; 8869149SJudy.Chen@Sun.COM case DDI_INTROP_NAVAIL: 8879149SJudy.Chen@Sun.COM case DDI_INTROP_NINTRS: 8889149SJudy.Chen@Sun.COM *(int *)result = i_ddi_get_intx_nintrs(rdip); 8899149SJudy.Chen@Sun.COM if (*(int *)result == 0) { 8909149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8919149SJudy.Chen@Sun.COM } 8929149SJudy.Chen@Sun.COM break; 8939149SJudy.Chen@Sun.COM case DDI_INTROP_SUPPORTED_TYPES: 8949149SJudy.Chen@Sun.COM *(int *)result = DDI_INTR_TYPE_FIXED; /* Always ... */ 8959149SJudy.Chen@Sun.COM break; 8969149SJudy.Chen@Sun.COM default: 8979149SJudy.Chen@Sun.COM return (DDI_FAILURE); 8989149SJudy.Chen@Sun.COM } 8999149SJudy.Chen@Sun.COM 9009149SJudy.Chen@Sun.COM return (DDI_SUCCESS); 9019149SJudy.Chen@Sun.COM } 9029149SJudy.Chen@Sun.COM 9030Sstevel@tonic-gate static void 9040Sstevel@tonic-gate isa_vendor(uint32_t id, char *vendor) 9050Sstevel@tonic-gate { 9060Sstevel@tonic-gate vendor[0] = '@' + ((id >> 26) & 0x1f); 9070Sstevel@tonic-gate vendor[1] = '@' + ((id >> 21) & 0x1f); 9080Sstevel@tonic-gate vendor[2] = '@' + ((id >> 16) & 0x1f); 9090Sstevel@tonic-gate vendor[3] = 0; 9100Sstevel@tonic-gate } 9110Sstevel@tonic-gate 9120Sstevel@tonic-gate /* 9130Sstevel@tonic-gate * Name a child 9140Sstevel@tonic-gate */ 9150Sstevel@tonic-gate static int 9160Sstevel@tonic-gate isa_name_child(dev_info_t *child, char *name, int namelen) 9170Sstevel@tonic-gate { 9180Sstevel@tonic-gate char vendor[8]; 9190Sstevel@tonic-gate int device; 9200Sstevel@tonic-gate uint32_t serial; 9210Sstevel@tonic-gate int func; 9220Sstevel@tonic-gate int bustype; 9230Sstevel@tonic-gate uint32_t base; 9240Sstevel@tonic-gate int proplen; 9250Sstevel@tonic-gate int pnpisa = 0; 9260Sstevel@tonic-gate isa_regs_t *isa_regs; 9270Sstevel@tonic-gate 9280Sstevel@tonic-gate void make_ddi_ppd(dev_info_t *, struct ddi_parent_private_data **); 9290Sstevel@tonic-gate 9300Sstevel@tonic-gate /* 9310Sstevel@tonic-gate * older drivers aren't expecting the "standard" device 9320Sstevel@tonic-gate * node format used by the hardware nodes. these drivers 9330Sstevel@tonic-gate * only expect their own properties set in their driver.conf 9340Sstevel@tonic-gate * files. so they tell us not to call them with hardware 9350Sstevel@tonic-gate * nodes by setting the property "ignore-hardware-nodes". 9360Sstevel@tonic-gate */ 9370Sstevel@tonic-gate if (old_driver(child)) 9380Sstevel@tonic-gate return (DDI_FAILURE); 9390Sstevel@tonic-gate 9400Sstevel@tonic-gate /* 9410Sstevel@tonic-gate * Fill in parent-private data 9420Sstevel@tonic-gate */ 9430Sstevel@tonic-gate if (ddi_get_parent_data(child) == NULL) { 9440Sstevel@tonic-gate struct ddi_parent_private_data *pdptr; 9450Sstevel@tonic-gate make_ddi_ppd(child, &pdptr); 9460Sstevel@tonic-gate ddi_set_parent_data(child, pdptr); 9470Sstevel@tonic-gate } 9480Sstevel@tonic-gate 9490Sstevel@tonic-gate if (ndi_dev_is_persistent_node(child) == 0) { 9500Sstevel@tonic-gate /* 9510Sstevel@tonic-gate * For .conf nodes, generate name from parent private data 9520Sstevel@tonic-gate */ 9530Sstevel@tonic-gate name[0] = '\0'; 9540Sstevel@tonic-gate if (sparc_pd_getnreg(child) > 0) { 9550Sstevel@tonic-gate (void) snprintf(name, namelen, "%x,%x", 9560Sstevel@tonic-gate (uint_t)sparc_pd_getreg(child, 0)->regspec_bustype, 9570Sstevel@tonic-gate (uint_t)sparc_pd_getreg(child, 0)->regspec_addr); 9580Sstevel@tonic-gate } 9590Sstevel@tonic-gate return (DDI_SUCCESS); 9600Sstevel@tonic-gate } 9610Sstevel@tonic-gate 9620Sstevel@tonic-gate /* 9630Sstevel@tonic-gate * For hw nodes, look up "reg" property 9640Sstevel@tonic-gate */ 9650Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg", 9660Sstevel@tonic-gate (caddr_t)&isa_regs, &proplen) != DDI_PROP_SUCCESS) { 9670Sstevel@tonic-gate return (DDI_FAILURE); 9680Sstevel@tonic-gate } 9690Sstevel@tonic-gate 9700Sstevel@tonic-gate /* 9710Sstevel@tonic-gate * extract the device identifications 9720Sstevel@tonic-gate */ 9730Sstevel@tonic-gate pnpisa = isa_regs[0].phys_hi & 0x80000000; 9740Sstevel@tonic-gate if (pnpisa) { 9750Sstevel@tonic-gate isa_vendor(isa_regs[0].phys_hi, vendor); 9760Sstevel@tonic-gate device = isa_regs[0].phys_hi & 0xffff; 9770Sstevel@tonic-gate serial = isa_regs[0].phys_lo; 9780Sstevel@tonic-gate func = (isa_regs[0].size >> 24) & 0xff; 9790Sstevel@tonic-gate if (func != 0) 9800Sstevel@tonic-gate (void) snprintf(name, namelen, "pnp%s,%04x,%x,%x", 9810Sstevel@tonic-gate vendor, device, serial, func); 9820Sstevel@tonic-gate else 9830Sstevel@tonic-gate (void) snprintf(name, namelen, "pnp%s,%04x,%x", 9840Sstevel@tonic-gate vendor, device, serial); 9850Sstevel@tonic-gate } else { 9860Sstevel@tonic-gate bustype = isa_regs[0].phys_hi; 9870Sstevel@tonic-gate base = isa_regs[0].phys_lo; 9880Sstevel@tonic-gate (void) sprintf(name, "%x,%x", bustype, base); 9890Sstevel@tonic-gate } 9900Sstevel@tonic-gate 9910Sstevel@tonic-gate /* 9920Sstevel@tonic-gate * free the memory allocated by ddi_getlongprop(). 9930Sstevel@tonic-gate */ 9940Sstevel@tonic-gate kmem_free(isa_regs, proplen); 9950Sstevel@tonic-gate 9960Sstevel@tonic-gate return (DDI_SUCCESS); 9970Sstevel@tonic-gate } 9980Sstevel@tonic-gate 9990Sstevel@tonic-gate static int 10000Sstevel@tonic-gate isa_initchild(dev_info_t *child) 10010Sstevel@tonic-gate { 10020Sstevel@tonic-gate char name[80]; 10030Sstevel@tonic-gate 10040Sstevel@tonic-gate if (isa_name_child(child, name, 80) != DDI_SUCCESS) 10050Sstevel@tonic-gate return (DDI_FAILURE); 10060Sstevel@tonic-gate ddi_set_name_addr(child, name); 10070Sstevel@tonic-gate 10080Sstevel@tonic-gate if (ndi_dev_is_persistent_node(child) != 0) 10090Sstevel@tonic-gate return (DDI_SUCCESS); 10100Sstevel@tonic-gate 10110Sstevel@tonic-gate /* 10120Sstevel@tonic-gate * This is a .conf node, try merge properties onto a 10130Sstevel@tonic-gate * hw node with the same name. 10140Sstevel@tonic-gate */ 10150Sstevel@tonic-gate if (ndi_merge_node(child, isa_name_child) == DDI_SUCCESS) { 10160Sstevel@tonic-gate /* 10170Sstevel@tonic-gate * Return failure to remove node 10180Sstevel@tonic-gate */ 10190Sstevel@tonic-gate impl_ddi_sunbus_removechild(child); 10200Sstevel@tonic-gate return (DDI_FAILURE); 10210Sstevel@tonic-gate } 10220Sstevel@tonic-gate /* 10230Sstevel@tonic-gate * Cannot merge node, permit pseudo children 10240Sstevel@tonic-gate */ 10250Sstevel@tonic-gate return (DDI_SUCCESS); 10260Sstevel@tonic-gate } 10270Sstevel@tonic-gate 10280Sstevel@tonic-gate /* 10290Sstevel@tonic-gate * called when ACPI enumeration is not used 10300Sstevel@tonic-gate */ 10310Sstevel@tonic-gate static void 10320Sstevel@tonic-gate add_known_used_resources(void) 10330Sstevel@tonic-gate { 10340Sstevel@tonic-gate /* needs to be in increasing order */ 10350Sstevel@tonic-gate int intr[] = {0x1, 0x3, 0x4, 0x6, 0x7, 0xc}; 10360Sstevel@tonic-gate int dma[] = {0x2}; 10370Sstevel@tonic-gate int io[] = {0x60, 0x1, 0x64, 0x1, 0x2f8, 0x8, 0x378, 0x8, 0x3f0, 0x10, 10380Sstevel@tonic-gate 0x778, 0x4}; 10390Sstevel@tonic-gate dev_info_t *usedrdip; 10400Sstevel@tonic-gate 10410Sstevel@tonic-gate usedrdip = ddi_find_devinfo(USED_RESOURCES, -1, 0); 10420Sstevel@tonic-gate 10430Sstevel@tonic-gate if (usedrdip == NULL) { 10440Sstevel@tonic-gate (void) ndi_devi_alloc_sleep(ddi_root_node(), USED_RESOURCES, 1045789Sahrens (pnode_t)DEVI_SID_NODEID, &usedrdip); 10460Sstevel@tonic-gate } 10470Sstevel@tonic-gate 10480Sstevel@tonic-gate (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip, 10490Sstevel@tonic-gate "interrupts", (int *)intr, (int)(sizeof (intr) / sizeof (int))); 10500Sstevel@tonic-gate (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip, 10510Sstevel@tonic-gate "io-space", (int *)io, (int)(sizeof (io) / sizeof (int))); 10520Sstevel@tonic-gate (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip, 10530Sstevel@tonic-gate "dma-channels", (int *)dma, (int)(sizeof (dma) / sizeof (int))); 10540Sstevel@tonic-gate (void) ndi_devi_bind_driver(usedrdip, 0); 10550Sstevel@tonic-gate 10560Sstevel@tonic-gate } 10570Sstevel@tonic-gate 10580Sstevel@tonic-gate static void 105910127SLipeng.Sang@Sun.COM isa_enumerate(int reprogram) 10600Sstevel@tonic-gate { 10610Sstevel@tonic-gate int circ, i; 10620Sstevel@tonic-gate dev_info_t *xdip; 106310127SLipeng.Sang@Sun.COM dev_info_t *isa_dip = ddi_find_devinfo("isa", -1, 0); 10640Sstevel@tonic-gate 10650Sstevel@tonic-gate /* hard coded isa stuff */ 10660Sstevel@tonic-gate struct regspec asy_regs[] = { 10670Sstevel@tonic-gate {1, 0x3f8, 0x8}, 10680Sstevel@tonic-gate {1, 0x2f8, 0x8} 10690Sstevel@tonic-gate }; 10700Sstevel@tonic-gate int asy_intrs[] = {0x4, 0x3}; 10710Sstevel@tonic-gate 10720Sstevel@tonic-gate struct regspec i8042_regs[] = { 10730Sstevel@tonic-gate {1, 0x60, 0x1}, 10740Sstevel@tonic-gate {1, 0x64, 0x1} 10750Sstevel@tonic-gate }; 10760Sstevel@tonic-gate int i8042_intrs[] = {0x1, 0xc}; 10770Sstevel@tonic-gate char *acpi_prop; 10780Sstevel@tonic-gate int acpi_enum = 1; /* ACPI is default to be on */ 10790Sstevel@tonic-gate 108010127SLipeng.Sang@Sun.COM if (reprogram || !isa_dip) 10810Sstevel@tonic-gate return; 10820Sstevel@tonic-gate 108310127SLipeng.Sang@Sun.COM bzero(isa_extra_resource, MAX_EXTRA_RESOURCE * sizeof (struct regspec)); 108410127SLipeng.Sang@Sun.COM 10850Sstevel@tonic-gate ndi_devi_enter(isa_dip, &circ); 10860Sstevel@tonic-gate 10870Sstevel@tonic-gate if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(), 10880Sstevel@tonic-gate DDI_PROP_DONTPASS, "acpi-enum", &acpi_prop) == DDI_PROP_SUCCESS) { 10890Sstevel@tonic-gate acpi_enum = strcmp("off", acpi_prop); 10900Sstevel@tonic-gate ddi_prop_free(acpi_prop); 10910Sstevel@tonic-gate } 10920Sstevel@tonic-gate 10930Sstevel@tonic-gate if (acpi_enum) { 10940Sstevel@tonic-gate if (acpi_isa_device_enum(isa_dip)) { 10950Sstevel@tonic-gate ndi_devi_exit(isa_dip, circ); 10960Sstevel@tonic-gate if (isa_resource_setup() != NDI_SUCCESS) { 10970Sstevel@tonic-gate cmn_err(CE_WARN, "isa nexus: isa " 10980Sstevel@tonic-gate "resource setup failed"); 10990Sstevel@tonic-gate } 11002007Sml40262 11012007Sml40262 /* serial ports? */ 11022007Sml40262 enumerate_BIOS_serial(isa_dip); 11039226SJudy.Chen@Sun.COM 11049226SJudy.Chen@Sun.COM /* adjust parallel port size */ 11059226SJudy.Chen@Sun.COM adjust_prtsz(isa_dip); 110610127SLipeng.Sang@Sun.COM 110710127SLipeng.Sang@Sun.COM isa_create_ranges_prop(isa_dip); 11080Sstevel@tonic-gate return; 11090Sstevel@tonic-gate } 11100Sstevel@tonic-gate cmn_err(CE_NOTE, "!Solaris did not detect ACPI BIOS"); 11110Sstevel@tonic-gate } 11120Sstevel@tonic-gate cmn_err(CE_NOTE, "!ACPI is off"); 11130Sstevel@tonic-gate 11140Sstevel@tonic-gate /* serial ports */ 11150Sstevel@tonic-gate for (i = 0; i < 2; i++) { 11165084Sjohnlev #if defined(__xpv) 111710175SStuart.Maybee@Sun.COM if ((i == 0 && console_hypervisor_device == CONS_TTYA) || 111810175SStuart.Maybee@Sun.COM (i == 1 && console_hypervisor_device == CONS_TTYB)) { 111910175SStuart.Maybee@Sun.COM continue; 112010175SStuart.Maybee@Sun.COM } 11215084Sjohnlev #endif 11220Sstevel@tonic-gate ndi_devi_alloc_sleep(isa_dip, "asy", 1123789Sahrens (pnode_t)DEVI_SID_NODEID, &xdip); 11240Sstevel@tonic-gate (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip, 11250Sstevel@tonic-gate "reg", (int *)&asy_regs[i], 3); 11260Sstevel@tonic-gate (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, 11270Sstevel@tonic-gate "interrupts", asy_intrs[i]); 11280Sstevel@tonic-gate (void) ndi_devi_bind_driver(xdip, 0); 11290Sstevel@tonic-gate } 11300Sstevel@tonic-gate 11310Sstevel@tonic-gate /* i8042 node */ 11320Sstevel@tonic-gate ndi_devi_alloc_sleep(isa_dip, "i8042", 1133789Sahrens (pnode_t)DEVI_SID_NODEID, &xdip); 11340Sstevel@tonic-gate (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip, 11350Sstevel@tonic-gate "reg", (int *)i8042_regs, 6); 11360Sstevel@tonic-gate (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip, 11370Sstevel@tonic-gate "interrupts", (int *)i8042_intrs, 2); 11380Sstevel@tonic-gate (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 11390Sstevel@tonic-gate "unit-address", "1,60"); 11400Sstevel@tonic-gate (void) ndi_devi_bind_driver(xdip, 0); 11410Sstevel@tonic-gate 11420Sstevel@tonic-gate add_known_used_resources(); 11430Sstevel@tonic-gate 11440Sstevel@tonic-gate ndi_devi_exit(isa_dip, circ); 11450Sstevel@tonic-gate 114610127SLipeng.Sang@Sun.COM isa_create_ranges_prop(isa_dip); 11470Sstevel@tonic-gate } 11482007Sml40262 11492007Sml40262 /* 11502007Sml40262 * On some machines, serial port 2 isn't listed in the ACPI table. 11512007Sml40262 * This function goes through the BIOS data area and makes sure all 11522007Sml40262 * the serial ports there are in the dev_info tree. If any are missing, 11532007Sml40262 * this function will add them. 11542007Sml40262 */ 11552007Sml40262 11562007Sml40262 static int num_BIOS_serial = 2; /* number of BIOS serial ports to look at */ 11572007Sml40262 11582007Sml40262 static void 11592007Sml40262 enumerate_BIOS_serial(dev_info_t *isa_dip) 11602007Sml40262 { 11612007Sml40262 ushort_t *bios_data; 11622007Sml40262 int i; 11632007Sml40262 dev_info_t *xdip; 11642007Sml40262 int found; 11652007Sml40262 int ret; 11662007Sml40262 struct regspec *tmpregs; 11672007Sml40262 int tmpregs_len; 11682007Sml40262 static struct regspec tmp_asy_regs[] = { 11692007Sml40262 {1, 0x3f8, 0x8}, 11702007Sml40262 }; 11712007Sml40262 static int default_asy_intrs[] = { 4, 3, 4, 3 }; 11722007Sml40262 static size_t size = 4; 11732007Sml40262 11742007Sml40262 /* 11752007Sml40262 * The first four 2-byte quantities of the BIOS data area contain 11762007Sml40262 * the base I/O addresses of the first four serial ports. 11772007Sml40262 */ 11782007Sml40262 bios_data = (ushort_t *)psm_map_new((paddr_t)BIOS_DATA_AREA, size, 11795084Sjohnlev PSM_PROT_READ); 11802007Sml40262 for (i = 0; i < num_BIOS_serial; i++) { 11812007Sml40262 if (bios_data[i] == 0) { 11822007Sml40262 /* no COM[i]: port */ 11832007Sml40262 continue; 11842007Sml40262 } 11852007Sml40262 11862007Sml40262 /* Look for it in the dev_info tree */ 11872007Sml40262 found = 0; 11882007Sml40262 for (xdip = ddi_get_child(isa_dip); xdip != NULL; 11892007Sml40262 xdip = ddi_get_next_sibling(xdip)) { 11902007Sml40262 if (strncmp(ddi_node_name(xdip), "asy", 3) != 0) { 11912007Sml40262 /* skip non asy */ 11922007Sml40262 continue; 11932007Sml40262 } 11942007Sml40262 11952007Sml40262 /* Match by addr */ 11962007Sml40262 ret = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, xdip, 11975084Sjohnlev DDI_PROP_DONTPASS, "reg", (int **)&tmpregs, 11985084Sjohnlev (uint_t *)&tmpregs_len); 11992007Sml40262 if (ret != DDI_PROP_SUCCESS) { 12002007Sml40262 /* error */ 12012007Sml40262 continue; 12022007Sml40262 } 12032007Sml40262 12042007Sml40262 if (tmpregs->regspec_addr == bios_data[i]) 12052007Sml40262 found = 1; 12062007Sml40262 /* 12072007Sml40262 * Free the memory allocated by 12082007Sml40262 * ddi_prop_lookup_int_array(). 12092007Sml40262 */ 12102007Sml40262 ddi_prop_free(tmpregs); 12115084Sjohnlev 12122007Sml40262 } 12132007Sml40262 12142007Sml40262 /* If not found, then add it */ 12152007Sml40262 if (!found) { 12162007Sml40262 ndi_devi_alloc_sleep(isa_dip, "asy", 12172007Sml40262 (pnode_t)DEVI_SID_NODEID, &xdip); 12182007Sml40262 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 12192007Sml40262 "compatible", "PNP0500"); 12202007Sml40262 /* This should be gotten from master file: */ 12212007Sml40262 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 12222007Sml40262 "model", "Standard PC COM port"); 12232007Sml40262 tmp_asy_regs[0].regspec_addr = bios_data[i]; 12242007Sml40262 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip, 12252007Sml40262 "reg", (int *)&tmp_asy_regs[0], 3); 12262007Sml40262 (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, 12272007Sml40262 "interrupts", default_asy_intrs[i]); 12282007Sml40262 (void) ndi_devi_bind_driver(xdip, 0); 12299226SJudy.Chen@Sun.COM 12309226SJudy.Chen@Sun.COM ASSERT(isa_extra_count < MAX_EXTRA_RESOURCE); 12319226SJudy.Chen@Sun.COM bcopy(tmp_asy_regs, 12329226SJudy.Chen@Sun.COM isa_extra_resource + isa_extra_count, 12339226SJudy.Chen@Sun.COM sizeof (struct regspec)); 12349226SJudy.Chen@Sun.COM isa_extra_count++; 12352007Sml40262 } 12362007Sml40262 } 123710175SStuart.Maybee@Sun.COM 12385084Sjohnlev /* 123910175SStuart.Maybee@Sun.COM * An asy node may have been attached via ACPI enumeration, or 124010175SStuart.Maybee@Sun.COM * directly from this file. Check each serial port to see if it 124110175SStuart.Maybee@Sun.COM * is in use by the hypervisor. If it is in use, then remove 124210175SStuart.Maybee@Sun.COM * the node from the device tree. 12435084Sjohnlev */ 124410175SStuart.Maybee@Sun.COM #if defined(__xpv) 12455084Sjohnlev i = 0; 124610175SStuart.Maybee@Sun.COM 12475084Sjohnlev for (xdip = ddi_get_child(isa_dip); xdip != NULL; ) { 12485084Sjohnlev dev_info_t *curdip; 12495084Sjohnlev 12505084Sjohnlev curdip = xdip; 12515084Sjohnlev xdip = ddi_get_next_sibling(xdip); 125210175SStuart.Maybee@Sun.COM 125310175SStuart.Maybee@Sun.COM if (strncmp(ddi_node_name(curdip), "asy", 3) != 0) 12545084Sjohnlev continue; 125510175SStuart.Maybee@Sun.COM 125610175SStuart.Maybee@Sun.COM if ((i == 0 && console_hypervisor_device == CONS_TTYA) || 125710175SStuart.Maybee@Sun.COM (i == 1 && console_hypervisor_device == CONS_TTYB)) { 125810175SStuart.Maybee@Sun.COM ret = ndi_devi_free(curdip); 125910175SStuart.Maybee@Sun.COM if (ret != DDI_SUCCESS) { 126010175SStuart.Maybee@Sun.COM cmn_err(CE_WARN, 126110175SStuart.Maybee@Sun.COM "could not remove asy%d node", i); 126210175SStuart.Maybee@Sun.COM } 126310175SStuart.Maybee@Sun.COM 126410175SStuart.Maybee@Sun.COM cmn_err(CE_NOTE, "!asy%d unavailable, reserved" 126510175SStuart.Maybee@Sun.COM " to hypervisor", i); 12665084Sjohnlev } 12675084Sjohnlev 12685084Sjohnlev i++; 12695084Sjohnlev } 127010175SStuart.Maybee@Sun.COM #endif 12712007Sml40262 12722007Sml40262 psm_unmap((caddr_t)bios_data, size); 12732007Sml40262 } 12749226SJudy.Chen@Sun.COM 12759226SJudy.Chen@Sun.COM /* 12769226SJudy.Chen@Sun.COM * Some machine comes with an illegal parallel port size of 3 12779226SJudy.Chen@Sun.COM * bytes in ACPI, even parallel port mode is ECP. 12789226SJudy.Chen@Sun.COM */ 12799226SJudy.Chen@Sun.COM #define DEFAULT_PRT_SIZE 8 12809226SJudy.Chen@Sun.COM static void 12819226SJudy.Chen@Sun.COM adjust_prtsz(dev_info_t *isa_dip) 12829226SJudy.Chen@Sun.COM { 12839226SJudy.Chen@Sun.COM dev_info_t *cdip; 12849226SJudy.Chen@Sun.COM struct regspec *regs_p, *extreg_p; 12859226SJudy.Chen@Sun.COM int regs_len, nreg, i; 12869226SJudy.Chen@Sun.COM char *name; 12879226SJudy.Chen@Sun.COM 12889226SJudy.Chen@Sun.COM for (cdip = ddi_get_child(isa_dip); cdip != NULL; 12899226SJudy.Chen@Sun.COM cdip = ddi_get_next_sibling(cdip)) { 12909226SJudy.Chen@Sun.COM name = ddi_node_name(cdip); 12919226SJudy.Chen@Sun.COM if ((strncmp(name, "lp", 2) != 0) || (strnlen(name, 3) != 2)) 12929226SJudy.Chen@Sun.COM continue; /* skip non parallel */ 12939226SJudy.Chen@Sun.COM 12949226SJudy.Chen@Sun.COM if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, cdip, 12959226SJudy.Chen@Sun.COM DDI_PROP_DONTPASS, "reg", (int **)®s_p, 12969226SJudy.Chen@Sun.COM (uint_t *)®s_len) != DDI_PROP_SUCCESS) 12979226SJudy.Chen@Sun.COM continue; 12989226SJudy.Chen@Sun.COM 12999226SJudy.Chen@Sun.COM nreg = regs_len / (sizeof (struct regspec) / sizeof (int)); 13009226SJudy.Chen@Sun.COM for (i = 0; i < nreg; i++) { 13019226SJudy.Chen@Sun.COM if (regs_p[i].regspec_size == DEFAULT_PRT_SIZE) 13029226SJudy.Chen@Sun.COM continue; 13039226SJudy.Chen@Sun.COM 13049226SJudy.Chen@Sun.COM ASSERT(isa_extra_count < MAX_EXTRA_RESOURCE); 13059226SJudy.Chen@Sun.COM extreg_p = &isa_extra_resource[isa_extra_count++]; 13069226SJudy.Chen@Sun.COM extreg_p->regspec_bustype = ISA_ADDR_IO; 13079226SJudy.Chen@Sun.COM extreg_p->regspec_addr = regs_p[i].regspec_addr; 13089226SJudy.Chen@Sun.COM extreg_p->regspec_size = DEFAULT_PRT_SIZE; 13099226SJudy.Chen@Sun.COM } 13109226SJudy.Chen@Sun.COM 13119226SJudy.Chen@Sun.COM ddi_prop_free(regs_p); 13129226SJudy.Chen@Sun.COM } 13139226SJudy.Chen@Sun.COM } 1314