xref: /onnv-gate/usr/src/uts/sun4/io/ebus.c (revision 506)
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
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
220Sstevel@tonic-gate /*
230Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
280Sstevel@tonic-gate 
290Sstevel@tonic-gate #include <sys/types.h>
300Sstevel@tonic-gate #include <sys/conf.h>
310Sstevel@tonic-gate #include <sys/ddi.h>
320Sstevel@tonic-gate #include <sys/sunddi.h>
330Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
340Sstevel@tonic-gate #include <sys/ddi_subrdefs.h>
350Sstevel@tonic-gate #include <sys/pci.h>
360Sstevel@tonic-gate #include <sys/autoconf.h>
370Sstevel@tonic-gate #include <sys/cmn_err.h>
380Sstevel@tonic-gate #include <sys/errno.h>
390Sstevel@tonic-gate #include <sys/kmem.h>
400Sstevel@tonic-gate #include <sys/debug.h>
410Sstevel@tonic-gate #include <sys/sysmacros.h>
420Sstevel@tonic-gate #include <sys/ebus.h>
430Sstevel@tonic-gate #include <sys/open.h>
440Sstevel@tonic-gate #include <sys/stat.h>
450Sstevel@tonic-gate #include <sys/file.h>
460Sstevel@tonic-gate #include <sys/sunndi.h>
470Sstevel@tonic-gate 
480Sstevel@tonic-gate #ifdef DEBUG
490Sstevel@tonic-gate uint64_t ebus_debug_flags = 0;
500Sstevel@tonic-gate #endif
510Sstevel@tonic-gate 
520Sstevel@tonic-gate /*
530Sstevel@tonic-gate  * The values of the following variables are used to initialize
540Sstevel@tonic-gate  * the cache line size and latency timer registers in the ebus
550Sstevel@tonic-gate  * configuration header.  Variables are used instead of constants
560Sstevel@tonic-gate  * to allow tuning from the /etc/system file.
570Sstevel@tonic-gate  */
580Sstevel@tonic-gate static uint8_t ebus_cache_line_size = 0x10;	/* 64 bytes */
590Sstevel@tonic-gate static uint8_t ebus_latency_timer = 0x40;	/* 64 PCI cycles */
600Sstevel@tonic-gate 
610Sstevel@tonic-gate /*
620Sstevel@tonic-gate  * function prototypes for bus ops routines:
630Sstevel@tonic-gate  */
640Sstevel@tonic-gate static int
650Sstevel@tonic-gate ebus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
660Sstevel@tonic-gate 	off_t offset, off_t len, caddr_t *addrp);
670Sstevel@tonic-gate static int
680Sstevel@tonic-gate ebus_ctlops(dev_info_t *dip, dev_info_t *rdip,
690Sstevel@tonic-gate 	ddi_ctl_enum_t op, void *arg, void *result);
700Sstevel@tonic-gate static int
710Sstevel@tonic-gate ebus_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
720Sstevel@tonic-gate     ddi_intr_handle_impl_t *hdlp, void *result);
730Sstevel@tonic-gate 
740Sstevel@tonic-gate /*
750Sstevel@tonic-gate  * function prototypes for dev ops routines:
760Sstevel@tonic-gate  */
770Sstevel@tonic-gate static int ebus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
780Sstevel@tonic-gate static int ebus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
790Sstevel@tonic-gate static int ebus_info(dev_info_t *dip, ddi_info_cmd_t infocmd,
800Sstevel@tonic-gate 	void *arg, void **result);
810Sstevel@tonic-gate 
820Sstevel@tonic-gate /*
830Sstevel@tonic-gate  * general function prototypes:
840Sstevel@tonic-gate  */
850Sstevel@tonic-gate static int ebus_config(ebus_devstate_t *ebus_p);
860Sstevel@tonic-gate static int ebus_apply_range(ebus_devstate_t *ebus_p, dev_info_t *rdip,
870Sstevel@tonic-gate     ebus_regspec_t *ebus_rp, pci_regspec_t *rp);
880Sstevel@tonic-gate static int febus_apply_range(ebus_devstate_t *ebus_p, dev_info_t *rdip,
890Sstevel@tonic-gate     ebus_regspec_t *ebus_rp, struct regspec *rp);
900Sstevel@tonic-gate int get_ranges_prop(ebus_devstate_t *ebus_p);
910Sstevel@tonic-gate 
920Sstevel@tonic-gate #define	getprop(dip, name, addr, intp)		\
93*506Scth 		ddi_getlongprop(DDI_DEV_T_ANY, (dip), DDI_PROP_DONTPASS, \
940Sstevel@tonic-gate 				(name), (caddr_t)(addr), (intp))
950Sstevel@tonic-gate 
960Sstevel@tonic-gate static int ebus_open(dev_t *devp, int flags, int otyp, cred_t *credp);
970Sstevel@tonic-gate static int ebus_close(dev_t dev, int flags, int otyp, cred_t *credp);
980Sstevel@tonic-gate static int ebus_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
990Sstevel@tonic-gate 						cred_t *credp, int *rvalp);
1000Sstevel@tonic-gate struct cb_ops ebus_cb_ops = {
1010Sstevel@tonic-gate 	ebus_open,			/* open */
1020Sstevel@tonic-gate 	ebus_close,			/* close */
1030Sstevel@tonic-gate 	nodev,				/* strategy */
1040Sstevel@tonic-gate 	nodev,				/* print */
1050Sstevel@tonic-gate 	nodev,				/* dump */
1060Sstevel@tonic-gate 	nodev,				/* read */
1070Sstevel@tonic-gate 	nodev,				/* write */
1080Sstevel@tonic-gate 	ebus_ioctl,			/* ioctl */
1090Sstevel@tonic-gate 	nodev,				/* devmap */
1100Sstevel@tonic-gate 	nodev,				/* mmap */
1110Sstevel@tonic-gate 	nodev,				/* segmap */
1120Sstevel@tonic-gate 	nochpoll,			/* poll */
1130Sstevel@tonic-gate 	ddi_prop_op,			/* cb_prop_op */
1140Sstevel@tonic-gate 	NULL,				/* streamtab */
1150Sstevel@tonic-gate 	D_NEW | D_MP | D_HOTPLUG,	/* Driver compatibility flag */
1160Sstevel@tonic-gate 	CB_REV,				/* rev */
1170Sstevel@tonic-gate 	nodev,				/* int (*cb_aread)() */
1180Sstevel@tonic-gate 	nodev				/* int (*cb_awrite)() */
1190Sstevel@tonic-gate };
1200Sstevel@tonic-gate 
1210Sstevel@tonic-gate /*
1220Sstevel@tonic-gate  * bus ops and dev ops structures:
1230Sstevel@tonic-gate  */
1240Sstevel@tonic-gate static struct bus_ops ebus_bus_ops = {
1250Sstevel@tonic-gate 	BUSO_REV,
1260Sstevel@tonic-gate 	ebus_map,
1270Sstevel@tonic-gate 	NULL,
1280Sstevel@tonic-gate 	NULL,
1290Sstevel@tonic-gate 	NULL,
1300Sstevel@tonic-gate 	i_ddi_map_fault,
1310Sstevel@tonic-gate 	ddi_dma_map,
1320Sstevel@tonic-gate 	ddi_dma_allochdl,
1330Sstevel@tonic-gate 	ddi_dma_freehdl,
1340Sstevel@tonic-gate 	ddi_dma_bindhdl,
1350Sstevel@tonic-gate 	ddi_dma_unbindhdl,
1360Sstevel@tonic-gate 	ddi_dma_flush,
1370Sstevel@tonic-gate 	ddi_dma_win,
1380Sstevel@tonic-gate 	ddi_dma_mctl,
1390Sstevel@tonic-gate 	ebus_ctlops,
1400Sstevel@tonic-gate 	ddi_bus_prop_op,
1410Sstevel@tonic-gate 	ndi_busop_get_eventcookie,
1420Sstevel@tonic-gate 	ndi_busop_add_eventcall,
1430Sstevel@tonic-gate 	ndi_busop_remove_eventcall,
1440Sstevel@tonic-gate 	ndi_post_event,
1450Sstevel@tonic-gate 	0,
1460Sstevel@tonic-gate 	0,
1470Sstevel@tonic-gate 	0,
1480Sstevel@tonic-gate 	0,
1490Sstevel@tonic-gate 	0,
1500Sstevel@tonic-gate 	0,
1510Sstevel@tonic-gate 	0,
1520Sstevel@tonic-gate 	0,
1530Sstevel@tonic-gate 	ebus_intr_ops
1540Sstevel@tonic-gate };
1550Sstevel@tonic-gate 
1560Sstevel@tonic-gate static struct dev_ops ebus_ops = {
1570Sstevel@tonic-gate 	DEVO_REV,
1580Sstevel@tonic-gate 	0,
1590Sstevel@tonic-gate 	ebus_info,
1600Sstevel@tonic-gate 	nulldev,
1610Sstevel@tonic-gate 	nulldev,
1620Sstevel@tonic-gate 	ebus_attach,
1630Sstevel@tonic-gate 	ebus_detach,
1640Sstevel@tonic-gate 	nodev,
1650Sstevel@tonic-gate 	&ebus_cb_ops,
1660Sstevel@tonic-gate 	&ebus_bus_ops
1670Sstevel@tonic-gate };
1680Sstevel@tonic-gate 
1690Sstevel@tonic-gate /*
1700Sstevel@tonic-gate  * module definitions:
1710Sstevel@tonic-gate  */
1720Sstevel@tonic-gate #include <sys/modctl.h>
1730Sstevel@tonic-gate extern struct mod_ops mod_driverops;
1740Sstevel@tonic-gate 
1750Sstevel@tonic-gate static struct modldrv modldrv = {
1760Sstevel@tonic-gate 	&mod_driverops, 	/* Type of module.  This one is a driver */
1770Sstevel@tonic-gate 	"ebus nexus driver %I%", /* Name of module. */
1780Sstevel@tonic-gate 	&ebus_ops,		/* driver ops */
1790Sstevel@tonic-gate };
1800Sstevel@tonic-gate 
1810Sstevel@tonic-gate static struct modlinkage modlinkage = {
1820Sstevel@tonic-gate 	MODREV_1, (void *)&modldrv, NULL
1830Sstevel@tonic-gate };
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate /*
1860Sstevel@tonic-gate  * driver global data:
1870Sstevel@tonic-gate  */
1880Sstevel@tonic-gate static void *per_ebus_state;		/* per-ebus soft state pointer */
1890Sstevel@tonic-gate 
1900Sstevel@tonic-gate 
1910Sstevel@tonic-gate int
1920Sstevel@tonic-gate _init(void)
1930Sstevel@tonic-gate {
1940Sstevel@tonic-gate 	int e;
1950Sstevel@tonic-gate 
1960Sstevel@tonic-gate 	/*
1970Sstevel@tonic-gate 	 * Initialize per-ebus soft state pointer.
1980Sstevel@tonic-gate 	 */
1990Sstevel@tonic-gate 	e = ddi_soft_state_init(&per_ebus_state, sizeof (ebus_devstate_t), 1);
2000Sstevel@tonic-gate 	if (e != 0)
2010Sstevel@tonic-gate 		return (e);
2020Sstevel@tonic-gate 
2030Sstevel@tonic-gate 	/*
2040Sstevel@tonic-gate 	 * Install the module.
2050Sstevel@tonic-gate 	 */
2060Sstevel@tonic-gate 	e = mod_install(&modlinkage);
2070Sstevel@tonic-gate 	if (e != 0)
2080Sstevel@tonic-gate 		ddi_soft_state_fini(&per_ebus_state);
2090Sstevel@tonic-gate 	return (e);
2100Sstevel@tonic-gate }
2110Sstevel@tonic-gate 
2120Sstevel@tonic-gate int
2130Sstevel@tonic-gate _fini(void)
2140Sstevel@tonic-gate {
2150Sstevel@tonic-gate 	int e;
2160Sstevel@tonic-gate 
2170Sstevel@tonic-gate 	/*
2180Sstevel@tonic-gate 	 * Remove the module.
2190Sstevel@tonic-gate 	 */
2200Sstevel@tonic-gate 	e = mod_remove(&modlinkage);
2210Sstevel@tonic-gate 	if (e != 0)
2220Sstevel@tonic-gate 		return (e);
2230Sstevel@tonic-gate 
2240Sstevel@tonic-gate 	/*
2250Sstevel@tonic-gate 	 * Free the soft state info.
2260Sstevel@tonic-gate 	 */
2270Sstevel@tonic-gate 	ddi_soft_state_fini(&per_ebus_state);
2280Sstevel@tonic-gate 	return (e);
2290Sstevel@tonic-gate }
2300Sstevel@tonic-gate 
2310Sstevel@tonic-gate int
2320Sstevel@tonic-gate _info(struct modinfo *modinfop)
2330Sstevel@tonic-gate {
2340Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
2350Sstevel@tonic-gate }
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate /* device driver entry points */
2380Sstevel@tonic-gate 
2390Sstevel@tonic-gate /*ARGSUSED*/
2400Sstevel@tonic-gate static int
2410Sstevel@tonic-gate ebus_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
2420Sstevel@tonic-gate {
2430Sstevel@tonic-gate 	ebus_devstate_t *ebus_p;	/* per ebus state pointer */
2440Sstevel@tonic-gate 	int instance;
2450Sstevel@tonic-gate 
2460Sstevel@tonic-gate 	instance = getminor((dev_t)arg);
2470Sstevel@tonic-gate 	ebus_p = get_ebus_soft_state(instance);
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate 	switch (infocmd) {
2500Sstevel@tonic-gate 	default:
2510Sstevel@tonic-gate 		return (DDI_FAILURE);
2520Sstevel@tonic-gate 
2530Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
2540Sstevel@tonic-gate 		*result = (void *)instance;
2550Sstevel@tonic-gate 		return (DDI_SUCCESS);
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
2580Sstevel@tonic-gate 		if (ebus_p == NULL)
2590Sstevel@tonic-gate 			return (DDI_FAILURE);
2600Sstevel@tonic-gate 		*result = (void *)ebus_p->dip;
2610Sstevel@tonic-gate 		return (DDI_SUCCESS);
2620Sstevel@tonic-gate 	}
2630Sstevel@tonic-gate }
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate /*
2660Sstevel@tonic-gate  * attach entry point:
2670Sstevel@tonic-gate  *
2680Sstevel@tonic-gate  * normal attach:
2690Sstevel@tonic-gate  *
2700Sstevel@tonic-gate  *	create soft state structure (dip, reg, nreg and state fields)
2710Sstevel@tonic-gate  *	map in configuration header
2720Sstevel@tonic-gate  *	make sure device is properly configured
2730Sstevel@tonic-gate  *	report device
2740Sstevel@tonic-gate  */
2750Sstevel@tonic-gate static int
2760Sstevel@tonic-gate ebus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2770Sstevel@tonic-gate {
2780Sstevel@tonic-gate 	ebus_devstate_t *ebus_p;	/* per ebus state pointer */
2790Sstevel@tonic-gate 	int instance;
2800Sstevel@tonic-gate 
2810Sstevel@tonic-gate 	DBG1(D_ATTACH, NULL, "dip=%p\n", dip);
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 	switch (cmd) {
2840Sstevel@tonic-gate 	case DDI_ATTACH:
2850Sstevel@tonic-gate 
2860Sstevel@tonic-gate 		/*
2870Sstevel@tonic-gate 		 * Allocate soft state for this instance.
2880Sstevel@tonic-gate 		 */
2890Sstevel@tonic-gate 		instance = ddi_get_instance(dip);
2900Sstevel@tonic-gate 		if (ddi_soft_state_zalloc(per_ebus_state, instance)
2910Sstevel@tonic-gate 				!= DDI_SUCCESS) {
2920Sstevel@tonic-gate 			DBG(D_ATTACH, NULL, "failed to alloc soft state\n");
2930Sstevel@tonic-gate 			return (DDI_FAILURE);
2940Sstevel@tonic-gate 		}
2950Sstevel@tonic-gate 		ebus_p = get_ebus_soft_state(instance);
2960Sstevel@tonic-gate 		ebus_p->dip = dip;
2970Sstevel@tonic-gate 		mutex_init(&ebus_p->ebus_mutex, NULL, MUTEX_DRIVER, NULL);
2980Sstevel@tonic-gate 		ebus_p->ebus_soft_state = EBUS_SOFT_STATE_CLOSED;
2990Sstevel@tonic-gate 
3000Sstevel@tonic-gate 		/* Set ebus type field based on ddi name info */
3010Sstevel@tonic-gate 		if (strcmp(ddi_get_name(dip), "jbus-ebus") == 0) {
3020Sstevel@tonic-gate 			ebus_p->type = FEBUS_TYPE;
3030Sstevel@tonic-gate 		} else {
3040Sstevel@tonic-gate 			ebus_p->type = EBUS_TYPE;
3050Sstevel@tonic-gate 		}
3060Sstevel@tonic-gate 
3070Sstevel@tonic-gate 		(void) ddi_prop_create(DDI_DEV_T_NONE, dip,
3080Sstevel@tonic-gate 			DDI_PROP_CANSLEEP, "no-dma-interrupt-sync", NULL, 0);
3090Sstevel@tonic-gate 		/* Get our ranges property for mapping child registers. */
3100Sstevel@tonic-gate 		if (get_ranges_prop(ebus_p) != DDI_SUCCESS) {
3110Sstevel@tonic-gate 			mutex_destroy(&ebus_p->ebus_mutex);
3120Sstevel@tonic-gate 			free_ebus_soft_state(instance);
3130Sstevel@tonic-gate 			return (DDI_FAILURE);
3140Sstevel@tonic-gate 		}
3150Sstevel@tonic-gate 
3160Sstevel@tonic-gate 		/*
3170Sstevel@tonic-gate 		 * create minor node for devctl interfaces
3180Sstevel@tonic-gate 		 */
3190Sstevel@tonic-gate 		if (ddi_create_minor_node(dip, "devctl", S_IFCHR, instance,
3200Sstevel@tonic-gate 		    DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
3210Sstevel@tonic-gate 			mutex_destroy(&ebus_p->ebus_mutex);
3220Sstevel@tonic-gate 			free_ebus_soft_state(instance);
3230Sstevel@tonic-gate 			return (DDI_FAILURE);
3240Sstevel@tonic-gate 		}
3250Sstevel@tonic-gate 		/*
3260Sstevel@tonic-gate 		 * Make sure the master enable and memory access enable
3270Sstevel@tonic-gate 		 * bits are set in the config command register.
3280Sstevel@tonic-gate 		 */
3290Sstevel@tonic-gate 		if (ebus_p->type == EBUS_TYPE) {
3300Sstevel@tonic-gate 			if (!ebus_config(ebus_p)) {
3310Sstevel@tonic-gate 				ddi_remove_minor_node(dip, "devctl");
3320Sstevel@tonic-gate 				mutex_destroy(&ebus_p->ebus_mutex);
3330Sstevel@tonic-gate 				free_ebus_soft_state(instance);
3340Sstevel@tonic-gate 				return (DDI_FAILURE);
3350Sstevel@tonic-gate 			}
3360Sstevel@tonic-gate 		}
3370Sstevel@tonic-gate 
3380Sstevel@tonic-gate 		/*
3390Sstevel@tonic-gate 		 * Make the pci_report_pmcap() call only for RIO
3400Sstevel@tonic-gate 		 * implementations.
3410Sstevel@tonic-gate 		 */
3420Sstevel@tonic-gate 		if (IS_RIO(dip)) {
3430Sstevel@tonic-gate 			(void) pci_report_pmcap(dip, PCI_PM_IDLESPEED,
3440Sstevel@tonic-gate 			    (void *)EBUS_4MHZ);
3450Sstevel@tonic-gate 		}
3460Sstevel@tonic-gate 
3470Sstevel@tonic-gate 		/*
3480Sstevel@tonic-gate 		 * Make the state as attached and report the device.
3490Sstevel@tonic-gate 		 */
3500Sstevel@tonic-gate 		ebus_p->state = ATTACHED;
3510Sstevel@tonic-gate 		ddi_report_dev(dip);
3520Sstevel@tonic-gate 		DBG(D_ATTACH, ebus_p, "returning\n");
3530Sstevel@tonic-gate 
3540Sstevel@tonic-gate 		return (DDI_SUCCESS);
3550Sstevel@tonic-gate 
3560Sstevel@tonic-gate 	case DDI_RESUME:
3570Sstevel@tonic-gate 
3580Sstevel@tonic-gate 		instance = ddi_get_instance(dip);
3590Sstevel@tonic-gate 		ebus_p = get_ebus_soft_state(instance);
3600Sstevel@tonic-gate 
3610Sstevel@tonic-gate 		/*
3620Sstevel@tonic-gate 		 * Make sure the master enable and memory access enable
3630Sstevel@tonic-gate 		 * bits are set in the config command register.
3640Sstevel@tonic-gate 		 */
3650Sstevel@tonic-gate 		if (ebus_p->type == EBUS_TYPE) {
3660Sstevel@tonic-gate 			if (!ebus_config(ebus_p)) {
3670Sstevel@tonic-gate 				free_ebus_soft_state(instance);
3680Sstevel@tonic-gate 				return (DDI_FAILURE);
3690Sstevel@tonic-gate 			}
3700Sstevel@tonic-gate 		}
3710Sstevel@tonic-gate 
3720Sstevel@tonic-gate 		ebus_p->state = RESUMED;
3730Sstevel@tonic-gate 		return (DDI_SUCCESS);
3740Sstevel@tonic-gate 	}
3750Sstevel@tonic-gate 	return (DDI_FAILURE);
3760Sstevel@tonic-gate }
3770Sstevel@tonic-gate 
3780Sstevel@tonic-gate /*
3790Sstevel@tonic-gate  * detach entry point:
3800Sstevel@tonic-gate  */
3810Sstevel@tonic-gate static int
3820Sstevel@tonic-gate ebus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
3830Sstevel@tonic-gate {
3840Sstevel@tonic-gate 	int instance = ddi_get_instance(dip);
3850Sstevel@tonic-gate 	ebus_devstate_t *ebus_p = get_ebus_soft_state(instance);
3860Sstevel@tonic-gate 
3870Sstevel@tonic-gate 	switch (cmd) {
3880Sstevel@tonic-gate 	case DDI_DETACH:
3890Sstevel@tonic-gate 		DBG1(D_DETACH, ebus_p, "DDI_DETACH dip=%p\n", dip);
3900Sstevel@tonic-gate 
3910Sstevel@tonic-gate 		switch (ebus_p->type) {
3920Sstevel@tonic-gate 		case EBUS_TYPE:
3930Sstevel@tonic-gate 			kmem_free(ebus_p->rangespec.rangep, ebus_p->range_cnt *
3940Sstevel@tonic-gate 				sizeof (struct ebus_pci_rangespec));
3950Sstevel@tonic-gate 			break;
3960Sstevel@tonic-gate 		case FEBUS_TYPE:
3970Sstevel@tonic-gate 			kmem_free(ebus_p->rangespec.ferangep,
3980Sstevel@tonic-gate 				ebus_p->range_cnt *
3990Sstevel@tonic-gate 				sizeof (struct febus_rangespec));
4000Sstevel@tonic-gate 			break;
4010Sstevel@tonic-gate 		default:
4020Sstevel@tonic-gate 			DBG(D_ATTACH, NULL, "failed to recognize ebus type\n");
4030Sstevel@tonic-gate 			return (DDI_FAILURE);
4040Sstevel@tonic-gate 		}
4050Sstevel@tonic-gate 
4060Sstevel@tonic-gate 		ddi_remove_minor_node(dip, "devctl");
4070Sstevel@tonic-gate 		mutex_destroy(&ebus_p->ebus_mutex);
4080Sstevel@tonic-gate 		free_ebus_soft_state(instance);
4090Sstevel@tonic-gate 		return (DDI_SUCCESS);
4100Sstevel@tonic-gate 
4110Sstevel@tonic-gate 	case DDI_SUSPEND:
4120Sstevel@tonic-gate 		DBG1(D_DETACH, ebus_p, "DDI_SUSPEND dip=%p\n", dip);
4130Sstevel@tonic-gate 		ebus_p->state = SUSPENDED;
4140Sstevel@tonic-gate 		return (DDI_SUCCESS);
4150Sstevel@tonic-gate 	}
4160Sstevel@tonic-gate 	DBG(D_ATTACH, NULL, "failed to recognize ebus detach command\n");
4170Sstevel@tonic-gate 	return (DDI_FAILURE);
4180Sstevel@tonic-gate }
4190Sstevel@tonic-gate 
4200Sstevel@tonic-gate 
4210Sstevel@tonic-gate int
4220Sstevel@tonic-gate get_ranges_prop(ebus_devstate_t *ebus_p)
4230Sstevel@tonic-gate {
4240Sstevel@tonic-gate 	int nrange, range_len;
4250Sstevel@tonic-gate 	struct ebus_pci_rangespec *rangep;
4260Sstevel@tonic-gate 	struct febus_rangespec *ferangep;
4270Sstevel@tonic-gate 
4280Sstevel@tonic-gate 	switch (ebus_p->type) {
4290Sstevel@tonic-gate 	case EBUS_TYPE:
4300Sstevel@tonic-gate 		if (ddi_getlongprop(DDI_DEV_T_ANY,
4310Sstevel@tonic-gate 			ebus_p->dip, DDI_PROP_DONTPASS,
4320Sstevel@tonic-gate 			"ranges", (caddr_t)&rangep,
4330Sstevel@tonic-gate 			&range_len) != DDI_SUCCESS) {
4340Sstevel@tonic-gate 				cmn_err(CE_WARN, "Can't get %s ranges property",
4350Sstevel@tonic-gate 					ddi_get_name(ebus_p->dip));
4360Sstevel@tonic-gate 				return (DDI_ME_REGSPEC_RANGE);
4370Sstevel@tonic-gate 		}
4380Sstevel@tonic-gate 
4390Sstevel@tonic-gate 		nrange = range_len / sizeof (struct ebus_pci_rangespec);
4400Sstevel@tonic-gate 
4410Sstevel@tonic-gate 		if (nrange == 0)  {
4420Sstevel@tonic-gate 			kmem_free(rangep, range_len);
4430Sstevel@tonic-gate 			DBG(D_ATTACH, NULL, "range is equal to zero\n");
4440Sstevel@tonic-gate 			return (DDI_FAILURE);
4450Sstevel@tonic-gate 		}
4460Sstevel@tonic-gate 
4470Sstevel@tonic-gate #ifdef DEBUG
4480Sstevel@tonic-gate 		{
4490Sstevel@tonic-gate 			int i;
4500Sstevel@tonic-gate 
4510Sstevel@tonic-gate 			for (i = 0; i < nrange; i++) {
4520Sstevel@tonic-gate 				DBG5(D_MAP, ebus_p,
4530Sstevel@tonic-gate 					"ebus range addr 0x%x.0x%x PCI range "
4540Sstevel@tonic-gate 					"addr 0x%x.0x%x.0x%x ",
4550Sstevel@tonic-gate 					rangep[i].ebus_phys_hi,
4560Sstevel@tonic-gate 					rangep[i].ebus_phys_low,
4570Sstevel@tonic-gate 					rangep[i].pci_phys_hi,
4580Sstevel@tonic-gate 					rangep[i].pci_phys_mid,
4590Sstevel@tonic-gate 					rangep[i].pci_phys_low);
4600Sstevel@tonic-gate 				DBG1(D_MAP, ebus_p,
4610Sstevel@tonic-gate 					"Size 0x%x\n", rangep[i].rng_size);
4620Sstevel@tonic-gate 			}
4630Sstevel@tonic-gate 		}
4640Sstevel@tonic-gate #endif /* DEBUG */
4650Sstevel@tonic-gate 
4660Sstevel@tonic-gate 		ebus_p->rangespec.rangep = rangep;
4670Sstevel@tonic-gate 		ebus_p->range_cnt = nrange;
4680Sstevel@tonic-gate 		return (DDI_SUCCESS);
4690Sstevel@tonic-gate 
4700Sstevel@tonic-gate 	case FEBUS_TYPE:
4710Sstevel@tonic-gate 		if (ddi_getlongprop(DDI_DEV_T_ANY, ebus_p->dip,
4720Sstevel@tonic-gate 			DDI_PROP_DONTPASS, "ranges",
4730Sstevel@tonic-gate 			(caddr_t)&ferangep, &range_len) != DDI_SUCCESS) {
4740Sstevel@tonic-gate 			cmn_err(CE_WARN, "Can't get %s ranges property",
4750Sstevel@tonic-gate 				ddi_get_name(ebus_p->dip));
4760Sstevel@tonic-gate 				return (DDI_ME_REGSPEC_RANGE);
4770Sstevel@tonic-gate 		}
4780Sstevel@tonic-gate 
4790Sstevel@tonic-gate 		nrange = range_len / sizeof (struct febus_rangespec);
4800Sstevel@tonic-gate 
4810Sstevel@tonic-gate 		if (nrange == 0)  {
4820Sstevel@tonic-gate 			kmem_free(ferangep, range_len);
4830Sstevel@tonic-gate 			return (DDI_FAILURE);
4840Sstevel@tonic-gate 		}
4850Sstevel@tonic-gate 
4860Sstevel@tonic-gate #ifdef	DEBUG
4870Sstevel@tonic-gate 		{
4880Sstevel@tonic-gate 			int i;
4890Sstevel@tonic-gate 
4900Sstevel@tonic-gate 			for (i = 0; i < nrange; i++) {
4910Sstevel@tonic-gate 				DBG4(D_MAP, ebus_p,
4920Sstevel@tonic-gate 					"ebus range addr 0x%x.0x%x"
4930Sstevel@tonic-gate 					" Parent range "
4940Sstevel@tonic-gate 					"addr 0x%x.0x%x ",
4950Sstevel@tonic-gate 					ferangep[i].febus_phys_hi,
4960Sstevel@tonic-gate 					ferangep[i].febus_phys_low,
4970Sstevel@tonic-gate 					ferangep[i].parent_phys_hi,
4980Sstevel@tonic-gate 					ferangep[i].parent_phys_low);
4990Sstevel@tonic-gate 				DBG1(D_MAP, ebus_p, "Size 0x%x\n",
5000Sstevel@tonic-gate 					ferangep[i].rng_size);
5010Sstevel@tonic-gate 			}
5020Sstevel@tonic-gate 		}
5030Sstevel@tonic-gate #endif /* DEBUG */
5040Sstevel@tonic-gate 		ebus_p->rangespec.ferangep = ferangep;
5050Sstevel@tonic-gate 		ebus_p->range_cnt = nrange;
5060Sstevel@tonic-gate 		return (DDI_SUCCESS);
5070Sstevel@tonic-gate 
5080Sstevel@tonic-gate 	default:
5090Sstevel@tonic-gate 		DBG(D_MAP, NULL, "failed to recognize ebus type\n");
5100Sstevel@tonic-gate 		return (DDI_FAILURE);
5110Sstevel@tonic-gate 	}
5120Sstevel@tonic-gate }
5130Sstevel@tonic-gate 
5140Sstevel@tonic-gate /* bus driver entry points */
5150Sstevel@tonic-gate 
5160Sstevel@tonic-gate /*
5170Sstevel@tonic-gate  * bus map entry point:
5180Sstevel@tonic-gate  *
5190Sstevel@tonic-gate  * 	if map request is for an rnumber
5200Sstevel@tonic-gate  *		get the corresponding regspec from device node
5210Sstevel@tonic-gate  * 	build a new regspec in our parent's format
5220Sstevel@tonic-gate  *	build a new map_req with the new regspec
5230Sstevel@tonic-gate  *	call up the tree to complete the mapping
5240Sstevel@tonic-gate  */
5250Sstevel@tonic-gate static int
5260Sstevel@tonic-gate ebus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
5270Sstevel@tonic-gate 	off_t off, off_t len, caddr_t *addrp)
5280Sstevel@tonic-gate {
5290Sstevel@tonic-gate 	ebus_devstate_t *ebus_p = get_ebus_soft_state(ddi_get_instance(dip));
5300Sstevel@tonic-gate 	ebus_regspec_t *ebus_rp, *ebus_regs;
5310Sstevel@tonic-gate 	struct regspec reg;
5320Sstevel@tonic-gate 	pci_regspec_t pci_reg;
5330Sstevel@tonic-gate 	ddi_map_req_t p_map_request;
5340Sstevel@tonic-gate 	int rnumber, i, n;
5350Sstevel@tonic-gate 	int rval = DDI_SUCCESS;
5360Sstevel@tonic-gate 
5370Sstevel@tonic-gate 	/*
5380Sstevel@tonic-gate 	 * Handle the mapping according to its type.
5390Sstevel@tonic-gate 	 */
5400Sstevel@tonic-gate 	DBG4(D_MAP, ebus_p, "rdip=%s%d: off=%x len=%x\n",
5410Sstevel@tonic-gate 	    ddi_get_name(rdip), ddi_get_instance(rdip), off, len);
5420Sstevel@tonic-gate 	switch (mp->map_type) {
5430Sstevel@tonic-gate 	case DDI_MT_REGSPEC:
5440Sstevel@tonic-gate 
5450Sstevel@tonic-gate 		/*
5460Sstevel@tonic-gate 		 * We assume the register specification is in ebus format.
5470Sstevel@tonic-gate 		 * We must convert it into a PCI format regspec and pass
5480Sstevel@tonic-gate 		 * the request to our parent.
5490Sstevel@tonic-gate 		 */
5500Sstevel@tonic-gate 		DBG3(D_MAP, ebus_p, "rdip=%s%d: REGSPEC - handlep=%p\n",
5510Sstevel@tonic-gate 			ddi_get_name(rdip), ddi_get_instance(rdip),
5520Sstevel@tonic-gate 			mp->map_handlep);
5530Sstevel@tonic-gate 		ebus_rp = (ebus_regspec_t *)mp->map_obj.rp;
5540Sstevel@tonic-gate 		break;
5550Sstevel@tonic-gate 
5560Sstevel@tonic-gate 	case DDI_MT_RNUMBER:
5570Sstevel@tonic-gate 
5580Sstevel@tonic-gate 		/*
5590Sstevel@tonic-gate 		 * Get the "reg" property from the device node and convert
5600Sstevel@tonic-gate 		 * it to our parent's format.
5610Sstevel@tonic-gate 		 */
5620Sstevel@tonic-gate 		rnumber = mp->map_obj.rnumber;
5630Sstevel@tonic-gate 		DBG4(D_MAP, ebus_p, "rdip=%s%d: rnumber=%x handlep=%p\n",
5640Sstevel@tonic-gate 			ddi_get_name(rdip), ddi_get_instance(rdip),
5650Sstevel@tonic-gate 			rnumber, mp->map_handlep);
5660Sstevel@tonic-gate 
5670Sstevel@tonic-gate 		if (getprop(rdip, "reg", &ebus_regs, &i) != DDI_SUCCESS) {
5680Sstevel@tonic-gate 			DBG(D_MAP, ebus_p, "can't get reg property\n");
5690Sstevel@tonic-gate 			return (DDI_ME_RNUMBER_RANGE);
5700Sstevel@tonic-gate 		}
5710Sstevel@tonic-gate 		n = i / sizeof (ebus_regspec_t);
5720Sstevel@tonic-gate 
5730Sstevel@tonic-gate 		if (rnumber < 0 || rnumber >= n) {
5740Sstevel@tonic-gate 			DBG(D_MAP, ebus_p, "rnumber out of range\n");
5750Sstevel@tonic-gate 			return (DDI_ME_RNUMBER_RANGE);
5760Sstevel@tonic-gate 		}
5770Sstevel@tonic-gate 		ebus_rp = &ebus_regs[rnumber];
5780Sstevel@tonic-gate 		break;
5790Sstevel@tonic-gate 
5800Sstevel@tonic-gate 	default:
5810Sstevel@tonic-gate 		return (DDI_ME_INVAL);
5820Sstevel@tonic-gate 
5830Sstevel@tonic-gate 	}
5840Sstevel@tonic-gate 
5850Sstevel@tonic-gate 	/* Adjust our reg property with offset and length */
5860Sstevel@tonic-gate 	ebus_rp->addr_low += off;
5870Sstevel@tonic-gate 	if (len)
5880Sstevel@tonic-gate 		ebus_rp->size = len;
5890Sstevel@tonic-gate 
5900Sstevel@tonic-gate 	/*
5910Sstevel@tonic-gate 	 * Now we have a copy the "reg" entry we're attempting to map.
5920Sstevel@tonic-gate 	 * Translate this into our parents PCI address using the ranges
5930Sstevel@tonic-gate 	 * property.
5940Sstevel@tonic-gate 	 */
5950Sstevel@tonic-gate 	switch (ebus_p->type) {
5960Sstevel@tonic-gate 	case EBUS_TYPE:
5970Sstevel@tonic-gate 		rval = ebus_apply_range(ebus_p, rdip, ebus_rp, &pci_reg);
5980Sstevel@tonic-gate 		break;
5990Sstevel@tonic-gate 	case FEBUS_TYPE:
6000Sstevel@tonic-gate 		rval = febus_apply_range(ebus_p, rdip, ebus_rp, &reg);
6010Sstevel@tonic-gate 		break;
6020Sstevel@tonic-gate 	default:
6030Sstevel@tonic-gate 		DBG(D_MAP, NULL, "failed to recognize ebus type\n");
6040Sstevel@tonic-gate 		rval = DDI_FAILURE;
6050Sstevel@tonic-gate 	}
6060Sstevel@tonic-gate 
6070Sstevel@tonic-gate 	if (mp->map_type == DDI_MT_RNUMBER)
6080Sstevel@tonic-gate 		kmem_free(ebus_regs, i);
6090Sstevel@tonic-gate 
6100Sstevel@tonic-gate 	if (rval != DDI_SUCCESS)
6110Sstevel@tonic-gate 		return (rval);
6120Sstevel@tonic-gate 
6130Sstevel@tonic-gate #ifdef DEBUG
6140Sstevel@tonic-gate 	switch (ebus_p->type) {
6150Sstevel@tonic-gate 	case EBUS_TYPE:
6160Sstevel@tonic-gate 		DBG5(D_MAP, ebus_p, "(%x,%x,%x)(%x,%x)\n",
6170Sstevel@tonic-gate 			pci_reg.pci_phys_hi,
6180Sstevel@tonic-gate 			pci_reg.pci_phys_mid,
6190Sstevel@tonic-gate 			pci_reg.pci_phys_low,
6200Sstevel@tonic-gate 			pci_reg.pci_size_hi,
6210Sstevel@tonic-gate 			pci_reg.pci_size_low);
6220Sstevel@tonic-gate 		break;
6230Sstevel@tonic-gate 
6240Sstevel@tonic-gate 	case FEBUS_TYPE:
6250Sstevel@tonic-gate 		DBG3(D_MAP, ebus_p, "%x,%x,%x\n",
6260Sstevel@tonic-gate 			reg.regspec_bustype,
6270Sstevel@tonic-gate 			reg.regspec_addr,
6280Sstevel@tonic-gate 			reg.regspec_size);
6290Sstevel@tonic-gate 		break;
6300Sstevel@tonic-gate 	}
6310Sstevel@tonic-gate #endif
6320Sstevel@tonic-gate 
6330Sstevel@tonic-gate 	p_map_request = *mp;
6340Sstevel@tonic-gate 	p_map_request.map_type = DDI_MT_REGSPEC;
6350Sstevel@tonic-gate 
6360Sstevel@tonic-gate 	switch (ebus_p->type) {
6370Sstevel@tonic-gate 	case EBUS_TYPE:
6380Sstevel@tonic-gate 		p_map_request.map_obj.rp = (struct regspec *)&pci_reg;
6390Sstevel@tonic-gate 		break;
6400Sstevel@tonic-gate 	case FEBUS_TYPE:
6410Sstevel@tonic-gate 		p_map_request.map_obj.rp = &reg;
6420Sstevel@tonic-gate 		break;
6430Sstevel@tonic-gate 	default:
6440Sstevel@tonic-gate 		DBG(D_MAP, NULL, "failed to recognize ebus type\n");
6450Sstevel@tonic-gate 		return (DDI_FAILURE);
6460Sstevel@tonic-gate 	}
6470Sstevel@tonic-gate 
6480Sstevel@tonic-gate 	rval = ddi_map(dip, &p_map_request, 0, 0, addrp);
6490Sstevel@tonic-gate 	DBG1(D_MAP, ebus_p, "parent returned %x\n", rval);
6500Sstevel@tonic-gate 	return (rval);
6510Sstevel@tonic-gate }
6520Sstevel@tonic-gate 
6530Sstevel@tonic-gate 
6540Sstevel@tonic-gate static int
6550Sstevel@tonic-gate ebus_apply_range(ebus_devstate_t *ebus_p, dev_info_t *rdip,
6560Sstevel@tonic-gate     ebus_regspec_t *ebus_rp, pci_regspec_t *rp)
6570Sstevel@tonic-gate {
6580Sstevel@tonic-gate 	int b;
6590Sstevel@tonic-gate 	int rval = DDI_SUCCESS;
6600Sstevel@tonic-gate 	struct ebus_pci_rangespec *rangep = ebus_p->rangespec.rangep;
6610Sstevel@tonic-gate 	int nrange = ebus_p->range_cnt;
6620Sstevel@tonic-gate 	static char out_of_range[] =
6630Sstevel@tonic-gate 	    "Out of range register specification from device node <%s>";
6640Sstevel@tonic-gate 
6650Sstevel@tonic-gate 	DBG3(D_MAP, ebus_p, "Range Matching Addr 0x%x.%x size 0x%x\n",
6660Sstevel@tonic-gate 	    ebus_rp->addr_hi, ebus_rp->addr_low, ebus_rp->size);
6670Sstevel@tonic-gate 
6680Sstevel@tonic-gate 	for (b = 0; b < nrange; ++b, ++rangep) {
6690Sstevel@tonic-gate 
6700Sstevel@tonic-gate 		/* Check for the correct space */
6710Sstevel@tonic-gate 		if (ebus_rp->addr_hi == rangep->ebus_phys_hi)
6720Sstevel@tonic-gate 			/* See if we fit in this range */
6730Sstevel@tonic-gate 			if ((ebus_rp->addr_low >=
6740Sstevel@tonic-gate 			    rangep->ebus_phys_low) &&
6750Sstevel@tonic-gate 			    ((ebus_rp->addr_low + ebus_rp->size - 1)
6760Sstevel@tonic-gate 				<= (rangep->ebus_phys_low +
6770Sstevel@tonic-gate 				    rangep->rng_size - 1))) {
6780Sstevel@tonic-gate 				uint_t addr_offset = ebus_rp->addr_low -
6790Sstevel@tonic-gate 				    rangep->ebus_phys_low;
6800Sstevel@tonic-gate 				/*
6810Sstevel@tonic-gate 				 * Use the range entry to translate
6820Sstevel@tonic-gate 				 * the EBUS physical address into the
6830Sstevel@tonic-gate 				 * parents PCI space.
6840Sstevel@tonic-gate 				 */
6850Sstevel@tonic-gate 				rp->pci_phys_hi =
6860Sstevel@tonic-gate 				rangep->pci_phys_hi;
6870Sstevel@tonic-gate 				rp->pci_phys_mid = rangep->pci_phys_mid;
6880Sstevel@tonic-gate 				rp->pci_phys_low =
6890Sstevel@tonic-gate 					rangep->pci_phys_low + addr_offset;
6900Sstevel@tonic-gate 				rp->pci_size_hi = 0;
6910Sstevel@tonic-gate 				rp->pci_size_low =
6920Sstevel@tonic-gate 					min(ebus_rp->size, (rangep->rng_size -
6930Sstevel@tonic-gate 					addr_offset));
6940Sstevel@tonic-gate 
6950Sstevel@tonic-gate 				DBG2(D_MAP, ebus_p, "Child hi0x%x lo0x%x ",
6960Sstevel@tonic-gate 					rangep->ebus_phys_hi,
6970Sstevel@tonic-gate 					rangep->ebus_phys_low);
6980Sstevel@tonic-gate 				DBG4(D_MAP, ebus_p, "Parent hi0x%x "
6990Sstevel@tonic-gate 					"mid0x%x lo0x%x size 0x%x\n",
7000Sstevel@tonic-gate 					rangep->pci_phys_hi,
7010Sstevel@tonic-gate 					rangep->pci_phys_mid,
7020Sstevel@tonic-gate 					rangep->pci_phys_low,
7030Sstevel@tonic-gate 					rangep->rng_size);
7040Sstevel@tonic-gate 
7050Sstevel@tonic-gate 				break;
7060Sstevel@tonic-gate 			}
7070Sstevel@tonic-gate 	}
7080Sstevel@tonic-gate 
7090Sstevel@tonic-gate 	if (b == nrange)  {
7100Sstevel@tonic-gate 		cmn_err(CE_WARN, out_of_range, ddi_get_name(rdip));
7110Sstevel@tonic-gate 		return (DDI_ME_REGSPEC_RANGE);
7120Sstevel@tonic-gate 	}
7130Sstevel@tonic-gate 
7140Sstevel@tonic-gate 	return (rval);
7150Sstevel@tonic-gate }
7160Sstevel@tonic-gate 
7170Sstevel@tonic-gate static int
7180Sstevel@tonic-gate febus_apply_range(ebus_devstate_t *ebus_p, dev_info_t *rdip,
7190Sstevel@tonic-gate 		ebus_regspec_t *ebus_rp, struct regspec *rp) {
7200Sstevel@tonic-gate 	int b;
7210Sstevel@tonic-gate 	int rval = DDI_SUCCESS;
7220Sstevel@tonic-gate 	struct febus_rangespec *rangep = ebus_p->rangespec.ferangep;
7230Sstevel@tonic-gate 	int nrange = ebus_p->range_cnt;
7240Sstevel@tonic-gate 	static char out_of_range[] =
7250Sstevel@tonic-gate 		"Out of range register specification from device node <%s>";
7260Sstevel@tonic-gate 
7270Sstevel@tonic-gate 	DBG3(D_MAP, ebus_p, "Range Matching Addr 0x%x.%x size 0x%x\n",
7280Sstevel@tonic-gate 	ebus_rp->addr_hi, ebus_rp->addr_low, ebus_rp->size);
7290Sstevel@tonic-gate 
7300Sstevel@tonic-gate 	for (b = 0; b < nrange; ++b, ++rangep) {
7310Sstevel@tonic-gate 		/* Check for the correct space */
7320Sstevel@tonic-gate 		if (ebus_rp->addr_hi == rangep->febus_phys_hi)
7330Sstevel@tonic-gate 			/* See if we fit in this range */
7340Sstevel@tonic-gate 			if ((ebus_rp->addr_low >=
7350Sstevel@tonic-gate 				rangep->febus_phys_low) &&
7360Sstevel@tonic-gate 				((ebus_rp->addr_low + ebus_rp->size - 1)
7370Sstevel@tonic-gate 				<= (rangep->febus_phys_low +
7380Sstevel@tonic-gate 				rangep->rng_size - 1))) {
7390Sstevel@tonic-gate 					uint_t addr_offset = ebus_rp->addr_low -
7400Sstevel@tonic-gate 					rangep->febus_phys_low;
7410Sstevel@tonic-gate 
7420Sstevel@tonic-gate 				/*
7430Sstevel@tonic-gate 				 * Use the range entry to translate
7440Sstevel@tonic-gate 				 * the FEBUS physical address into the
7450Sstevel@tonic-gate 				 * parents space.
7460Sstevel@tonic-gate 				 */
7470Sstevel@tonic-gate 				rp->regspec_bustype =
7480Sstevel@tonic-gate 					rangep->parent_phys_hi;
7490Sstevel@tonic-gate 				rp->regspec_addr =
7500Sstevel@tonic-gate 				rangep->parent_phys_low + addr_offset;
7510Sstevel@tonic-gate 				rp->regspec_size =
7520Sstevel@tonic-gate 					min(ebus_rp->size, (rangep->rng_size -
7530Sstevel@tonic-gate 					addr_offset));
7540Sstevel@tonic-gate 
7550Sstevel@tonic-gate 				DBG2(D_MAP, ebus_p, "Child hi0x%x lo0x%x ",
7560Sstevel@tonic-gate 					rangep->febus_phys_hi,
7570Sstevel@tonic-gate 					rangep->febus_phys_low);
7580Sstevel@tonic-gate 				DBG3(D_MAP, ebus_p, "Parent hi0x%x "
7590Sstevel@tonic-gate 					"lo0x%x size 0x%x\n",
7600Sstevel@tonic-gate 					rangep->parent_phys_hi,
7610Sstevel@tonic-gate 					rangep->parent_phys_low,
7620Sstevel@tonic-gate 					rangep->rng_size);
7630Sstevel@tonic-gate 
7640Sstevel@tonic-gate 				break;
7650Sstevel@tonic-gate 			}
7660Sstevel@tonic-gate 	}
7670Sstevel@tonic-gate 
7680Sstevel@tonic-gate 	if (b == nrange)  {
7690Sstevel@tonic-gate 		cmn_err(CE_WARN, out_of_range, ddi_get_name(rdip));
7700Sstevel@tonic-gate 		return (DDI_ME_REGSPEC_RANGE);
7710Sstevel@tonic-gate 	}
7720Sstevel@tonic-gate 
7730Sstevel@tonic-gate 	return (rval);
7740Sstevel@tonic-gate }
7750Sstevel@tonic-gate 
7760Sstevel@tonic-gate 
7770Sstevel@tonic-gate static int
7780Sstevel@tonic-gate ebus_name_child(dev_info_t *child, char *name, int namelen)
7790Sstevel@tonic-gate {
7800Sstevel@tonic-gate 	ebus_regspec_t *ebus_rp;
7810Sstevel@tonic-gate 	int reglen;
7820Sstevel@tonic-gate 
7830Sstevel@tonic-gate 	/*
7840Sstevel@tonic-gate 	 * Get the address portion of the node name based on the
7850Sstevel@tonic-gate 	 * address/offset.
7860Sstevel@tonic-gate 	 */
787*506Scth 	if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
7880Sstevel@tonic-gate 	    "reg", (caddr_t)&ebus_rp, &reglen) != DDI_SUCCESS) {
7890Sstevel@tonic-gate 		return (DDI_FAILURE);
7900Sstevel@tonic-gate 	}
7910Sstevel@tonic-gate 
7920Sstevel@tonic-gate 	(void) snprintf(name, namelen, "%x,%x", ebus_rp->addr_hi,
7930Sstevel@tonic-gate 	    ebus_rp->addr_low);
7940Sstevel@tonic-gate 	kmem_free(ebus_rp, reglen);
7950Sstevel@tonic-gate 
7960Sstevel@tonic-gate 	return (DDI_SUCCESS);
7970Sstevel@tonic-gate }
7980Sstevel@tonic-gate 
7990Sstevel@tonic-gate /*
8000Sstevel@tonic-gate  * control ops entry point:
8010Sstevel@tonic-gate  *
8020Sstevel@tonic-gate  * Requests handled completely:
8030Sstevel@tonic-gate  *	DDI_CTLOPS_INITCHILD
8040Sstevel@tonic-gate  *	DDI_CTLOPS_UNINITCHILD
8050Sstevel@tonic-gate  *	DDI_CTLOPS_REPORTDEV
8060Sstevel@tonic-gate  *	DDI_CTLOPS_REGSIZE
8070Sstevel@tonic-gate  *	DDI_CTLOPS_NREGS
8080Sstevel@tonic-gate  *
8090Sstevel@tonic-gate  * All others passed to parent.
8100Sstevel@tonic-gate  */
8110Sstevel@tonic-gate static int
8120Sstevel@tonic-gate ebus_ctlops(dev_info_t *dip, dev_info_t *rdip,
8130Sstevel@tonic-gate 	ddi_ctl_enum_t op, void *arg, void *result)
8140Sstevel@tonic-gate {
8150Sstevel@tonic-gate #ifdef DEBUG
8160Sstevel@tonic-gate 	ebus_devstate_t *ebus_p = get_ebus_soft_state(ddi_get_instance(dip));
8170Sstevel@tonic-gate #endif
8180Sstevel@tonic-gate 	ebus_regspec_t *ebus_rp;
8190Sstevel@tonic-gate 	int i, n;
8200Sstevel@tonic-gate 	char name[10];
8210Sstevel@tonic-gate 
8220Sstevel@tonic-gate 	switch (op) {
8230Sstevel@tonic-gate 	case DDI_CTLOPS_INITCHILD: {
8240Sstevel@tonic-gate 		dev_info_t *child = (dev_info_t *)arg;
8250Sstevel@tonic-gate 		/*
8260Sstevel@tonic-gate 		 * Set the address portion of the node name based on the
8270Sstevel@tonic-gate 		 * address/offset.
8280Sstevel@tonic-gate 		 */
8290Sstevel@tonic-gate 		DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_INITCHILD: rdip=%s%d\n",
8300Sstevel@tonic-gate 		    ddi_get_name(child), ddi_get_instance(child));
8310Sstevel@tonic-gate 
8320Sstevel@tonic-gate 		if (ebus_name_child(child, name, 10) != DDI_SUCCESS) {
8330Sstevel@tonic-gate 			DBG(D_CTLOPS, ebus_p, "can't name child\n");
8340Sstevel@tonic-gate 			return (DDI_FAILURE);
8350Sstevel@tonic-gate 		}
8360Sstevel@tonic-gate 
8370Sstevel@tonic-gate 		ddi_set_name_addr(child, name);
8380Sstevel@tonic-gate 		ddi_set_parent_data(child, NULL);
8390Sstevel@tonic-gate 		return (DDI_SUCCESS);
8400Sstevel@tonic-gate 	}
8410Sstevel@tonic-gate 
8420Sstevel@tonic-gate 	case DDI_CTLOPS_UNINITCHILD:
8430Sstevel@tonic-gate 		DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_UNINITCHILD: rdip=%s%d\n",
8440Sstevel@tonic-gate 			ddi_get_name((dev_info_t *)arg),
8450Sstevel@tonic-gate 			ddi_get_instance((dev_info_t *)arg));
8460Sstevel@tonic-gate 		ddi_set_name_addr((dev_info_t *)arg, NULL);
8470Sstevel@tonic-gate 		ddi_remove_minor_node((dev_info_t *)arg, NULL);
8480Sstevel@tonic-gate 		impl_rem_dev_props((dev_info_t *)arg);
8490Sstevel@tonic-gate 		return (DDI_SUCCESS);
8500Sstevel@tonic-gate 
8510Sstevel@tonic-gate 	case DDI_CTLOPS_REPORTDEV:
8520Sstevel@tonic-gate 
8530Sstevel@tonic-gate 		DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_REPORTDEV: rdip=%s%d\n",
8540Sstevel@tonic-gate 			ddi_get_name(rdip), ddi_get_instance(rdip));
8550Sstevel@tonic-gate 		cmn_err(CE_CONT, "?%s%d at %s%d: offset %s\n",
8560Sstevel@tonic-gate 			ddi_driver_name(rdip), ddi_get_instance(rdip),
8570Sstevel@tonic-gate 			ddi_driver_name(dip), ddi_get_instance(dip),
8580Sstevel@tonic-gate 			ddi_get_name_addr(rdip));
8590Sstevel@tonic-gate 		return (DDI_SUCCESS);
8600Sstevel@tonic-gate 
8610Sstevel@tonic-gate 	case DDI_CTLOPS_REGSIZE:
8620Sstevel@tonic-gate 
8630Sstevel@tonic-gate 		DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_REGSIZE: rdip=%s%d\n",
8640Sstevel@tonic-gate 			ddi_get_name(rdip), ddi_get_instance(rdip));
8650Sstevel@tonic-gate 		if (getprop(rdip, "reg", &ebus_rp, &i) != DDI_SUCCESS) {
8660Sstevel@tonic-gate 			DBG(D_CTLOPS, ebus_p, "can't get reg property\n");
8670Sstevel@tonic-gate 			return (DDI_FAILURE);
8680Sstevel@tonic-gate 		}
8690Sstevel@tonic-gate 		n = i / sizeof (ebus_regspec_t);
8700Sstevel@tonic-gate 		if (*(int *)arg < 0 || *(int *)arg >= n) {
8710Sstevel@tonic-gate 			DBG(D_MAP, ebus_p, "rnumber out of range\n");
8720Sstevel@tonic-gate 			kmem_free(ebus_rp, i);
8730Sstevel@tonic-gate 			return (DDI_FAILURE);
8740Sstevel@tonic-gate 		}
8750Sstevel@tonic-gate 		*((off_t *)result) = ebus_rp[*(int *)arg].size;
8760Sstevel@tonic-gate 		kmem_free(ebus_rp, i);
8770Sstevel@tonic-gate 		return (DDI_SUCCESS);
8780Sstevel@tonic-gate 
8790Sstevel@tonic-gate 	case DDI_CTLOPS_NREGS:
8800Sstevel@tonic-gate 
8810Sstevel@tonic-gate 		DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_NREGS: rdip=%s%d\n",
8820Sstevel@tonic-gate 			ddi_get_name(rdip), ddi_get_instance(rdip));
8830Sstevel@tonic-gate 		if (getprop(rdip, "reg", &ebus_rp, &i) != DDI_SUCCESS) {
8840Sstevel@tonic-gate 			DBG(D_CTLOPS, ebus_p, "can't get reg property\n");
8850Sstevel@tonic-gate 			return (DDI_FAILURE);
8860Sstevel@tonic-gate 		}
8870Sstevel@tonic-gate 		*((uint_t *)result) = i / sizeof (ebus_regspec_t);
8880Sstevel@tonic-gate 		kmem_free(ebus_rp, i);
8890Sstevel@tonic-gate 		return (DDI_SUCCESS);
8900Sstevel@tonic-gate 	}
8910Sstevel@tonic-gate 
8920Sstevel@tonic-gate 	/*
8930Sstevel@tonic-gate 	 * Now pass the request up to our parent.
8940Sstevel@tonic-gate 	 */
8950Sstevel@tonic-gate 	DBG2(D_CTLOPS, ebus_p, "passing request to parent: rdip=%s%d\n",
8960Sstevel@tonic-gate 		ddi_get_name(rdip), ddi_get_instance(rdip));
8970Sstevel@tonic-gate 	return (ddi_ctlops(dip, rdip, op, arg, result));
8980Sstevel@tonic-gate }
8990Sstevel@tonic-gate 
9000Sstevel@tonic-gate struct ebus_string_to_pil {
9010Sstevel@tonic-gate 	int8_t *string;
9020Sstevel@tonic-gate 	uint32_t pil;
9030Sstevel@tonic-gate };
9040Sstevel@tonic-gate 
9050Sstevel@tonic-gate static struct ebus_string_to_pil ebus_name_to_pil[] = {{"SUNW,CS4231", 9},
9060Sstevel@tonic-gate 							{"audio", 9},
9070Sstevel@tonic-gate 							{"fdthree", 8},
9080Sstevel@tonic-gate 							{"floppy", 8},
9090Sstevel@tonic-gate 							{"ecpp", 3},
9100Sstevel@tonic-gate 							{"parallel", 3},
9110Sstevel@tonic-gate 							{"su", 12},
9120Sstevel@tonic-gate 							{"se", 12},
9130Sstevel@tonic-gate 							{"serial", 12},
9140Sstevel@tonic-gate 							{"power", 14}};
9150Sstevel@tonic-gate 
9160Sstevel@tonic-gate static struct ebus_string_to_pil ebus_device_type_to_pil[] = {{"serial", 12},
9170Sstevel@tonic-gate 								{"block", 8}};
9180Sstevel@tonic-gate 
9190Sstevel@tonic-gate static int
9200Sstevel@tonic-gate ebus_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
9210Sstevel@tonic-gate     ddi_intr_handle_impl_t *hdlp, void *result)
9220Sstevel@tonic-gate {
9230Sstevel@tonic-gate #ifdef DEBUG
9240Sstevel@tonic-gate 	ebus_devstate_t *ebus_p = get_ebus_soft_state(ddi_get_instance(dip));
9250Sstevel@tonic-gate #endif
9260Sstevel@tonic-gate 	ddi_ispec_t		*ip = (ddi_ispec_t *)hdlp->ih_private;
9270Sstevel@tonic-gate 	int32_t			i, max_children, max_device_types, len;
9280Sstevel@tonic-gate 	char			*name_p, *device_type_p;
9290Sstevel@tonic-gate 
9300Sstevel@tonic-gate 	DBG1(D_INTR, ebus_p, "ip 0x%p\n", ip);
9310Sstevel@tonic-gate 
9320Sstevel@tonic-gate 	/*
9330Sstevel@tonic-gate 	 * NOTE: These ops below will never be supported in this nexus
9340Sstevel@tonic-gate 	 * driver, hence they always return immediately.
9350Sstevel@tonic-gate 	 */
9360Sstevel@tonic-gate 	switch (intr_op) {
9370Sstevel@tonic-gate 	case DDI_INTROP_GETCAP:
9380Sstevel@tonic-gate 		*(int *)result = 0;
9390Sstevel@tonic-gate 		return (DDI_SUCCESS);
9400Sstevel@tonic-gate 	case DDI_INTROP_SETCAP:
9410Sstevel@tonic-gate 	case DDI_INTROP_SETMASK:
9420Sstevel@tonic-gate 	case DDI_INTROP_CLRMASK:
9430Sstevel@tonic-gate 	case DDI_INTROP_GETPENDING:
9440Sstevel@tonic-gate 		return (DDI_ENOTSUP);
9450Sstevel@tonic-gate 	default:
9460Sstevel@tonic-gate 		break;
9470Sstevel@tonic-gate 	}
9480Sstevel@tonic-gate 
9490Sstevel@tonic-gate 	if ((intr_op == DDI_INTROP_SUPPORTED_TYPES) || ip->is_pil)
9500Sstevel@tonic-gate 		goto done;
9510Sstevel@tonic-gate 
9520Sstevel@tonic-gate 	/*
9530Sstevel@tonic-gate 	 * This is a hack to set the PIL for the devices under ebus.
9540Sstevel@tonic-gate 	 * We first look up a device by it's specific name, if we can't
9550Sstevel@tonic-gate 	 * match the name, we try and match it's device_type property.
9560Sstevel@tonic-gate 	 * Lastly we default a PIL level of 1.
9570Sstevel@tonic-gate 	 */
9580Sstevel@tonic-gate 	name_p = ddi_node_name(rdip);
9590Sstevel@tonic-gate 	max_children = sizeof (ebus_name_to_pil) /
9600Sstevel@tonic-gate 	    sizeof (struct ebus_string_to_pil);
9610Sstevel@tonic-gate 
9620Sstevel@tonic-gate 	for (i = 0; i < max_children; i++) {
9630Sstevel@tonic-gate 		if (strcmp(ebus_name_to_pil[i].string, name_p) == 0) {
9640Sstevel@tonic-gate 			DBG2(D_INTR, ebus_p, "child name %s; match PIL %d\n",
9650Sstevel@tonic-gate 			    ebus_name_to_pil[i].string,
9660Sstevel@tonic-gate 			    ebus_name_to_pil[i].pil);
9670Sstevel@tonic-gate 
9680Sstevel@tonic-gate 			ip->is_pil = ebus_name_to_pil[i].pil;
9690Sstevel@tonic-gate 			goto done;
9700Sstevel@tonic-gate 		}
9710Sstevel@tonic-gate 	}
9720Sstevel@tonic-gate 
973*506Scth 	if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
9740Sstevel@tonic-gate 	    "device_type", (caddr_t)&device_type_p, &len) == DDI_SUCCESS) {
9750Sstevel@tonic-gate 
9760Sstevel@tonic-gate 		max_device_types = sizeof (ebus_device_type_to_pil) /
9770Sstevel@tonic-gate 		    sizeof (struct ebus_string_to_pil);
9780Sstevel@tonic-gate 
9790Sstevel@tonic-gate 		for (i = 0; i < max_device_types; i++) {
9800Sstevel@tonic-gate 			if (strcmp(ebus_device_type_to_pil[i].string,
9810Sstevel@tonic-gate 			    device_type_p) == 0) {
9820Sstevel@tonic-gate 				DBG2(D_INTR, ebus_p, "Device type %s; match "
9830Sstevel@tonic-gate 				    "PIL %d\n", ebus_device_type_to_pil[i].
9840Sstevel@tonic-gate 				    string, ebus_device_type_to_pil[i].pil);
9850Sstevel@tonic-gate 
9860Sstevel@tonic-gate 				ip->is_pil = ebus_device_type_to_pil[i].pil;
9870Sstevel@tonic-gate 				break;
9880Sstevel@tonic-gate 			}
9890Sstevel@tonic-gate 		}
9900Sstevel@tonic-gate 
9910Sstevel@tonic-gate 		kmem_free(device_type_p, len);
9920Sstevel@tonic-gate 	}
9930Sstevel@tonic-gate 
9940Sstevel@tonic-gate 	/*
9950Sstevel@tonic-gate 	 * If we get here, we need to set a default value
9960Sstevel@tonic-gate 	 * for the PIL.
9970Sstevel@tonic-gate 	 */
9980Sstevel@tonic-gate 	if (ip->is_pil == 0) {
9990Sstevel@tonic-gate 		ip->is_pil = 1;
10000Sstevel@tonic-gate 
10010Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s%d assigning default interrupt level %d "
10020Sstevel@tonic-gate 		    "for device %s%d", ddi_get_name(dip), ddi_get_instance(dip),
10030Sstevel@tonic-gate 		    ip->is_pil, ddi_get_name(rdip), ddi_get_instance(rdip));
10040Sstevel@tonic-gate 	}
10050Sstevel@tonic-gate 
10060Sstevel@tonic-gate done:
10070Sstevel@tonic-gate 	/* Pass up the request to our parent. */
10080Sstevel@tonic-gate 	return (i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result));
10090Sstevel@tonic-gate }
10100Sstevel@tonic-gate 
10110Sstevel@tonic-gate 
10120Sstevel@tonic-gate static int
10130Sstevel@tonic-gate ebus_config(ebus_devstate_t *ebus_p)
10140Sstevel@tonic-gate {
10150Sstevel@tonic-gate 	ddi_acc_handle_t conf_handle;
10160Sstevel@tonic-gate 	uint16_t comm;
10170Sstevel@tonic-gate 
10180Sstevel@tonic-gate 	/*
10190Sstevel@tonic-gate 	 * Make sure the master enable and memory access enable
10200Sstevel@tonic-gate 	 * bits are set in the config command register.
10210Sstevel@tonic-gate 	 */
10220Sstevel@tonic-gate 	if (pci_config_setup(ebus_p->dip, &conf_handle) != DDI_SUCCESS)
10230Sstevel@tonic-gate 		return (0);
10240Sstevel@tonic-gate 
10250Sstevel@tonic-gate 	comm = pci_config_get16(conf_handle, PCI_CONF_COMM),
10260Sstevel@tonic-gate #ifdef DEBUG
10270Sstevel@tonic-gate 	    DBG1(D_MAP, ebus_p, "command register was 0x%x\n", comm);
10280Sstevel@tonic-gate #endif
10290Sstevel@tonic-gate 	comm |= (PCI_COMM_ME|PCI_COMM_MAE|PCI_COMM_SERR_ENABLE|
10300Sstevel@tonic-gate 	    PCI_COMM_PARITY_DETECT);
10310Sstevel@tonic-gate 	pci_config_put16(conf_handle, PCI_CONF_COMM, comm),
10320Sstevel@tonic-gate #ifdef DEBUG
10330Sstevel@tonic-gate 	    DBG1(D_MAP, ebus_p, "command register is now 0x%x\n", comm);
10340Sstevel@tonic-gate #endif
10350Sstevel@tonic-gate 	pci_config_put8(conf_handle, PCI_CONF_CACHE_LINESZ,
10360Sstevel@tonic-gate 	    (uchar_t)ebus_cache_line_size);
10370Sstevel@tonic-gate 	pci_config_put8(conf_handle, PCI_CONF_LATENCY_TIMER,
10380Sstevel@tonic-gate 	    (uchar_t)ebus_latency_timer);
10390Sstevel@tonic-gate 	pci_config_teardown(&conf_handle);
10400Sstevel@tonic-gate 	return (1);
10410Sstevel@tonic-gate }
10420Sstevel@tonic-gate 
10430Sstevel@tonic-gate #ifdef DEBUG
10440Sstevel@tonic-gate extern void prom_printf(const char *, ...);
10450Sstevel@tonic-gate 
10460Sstevel@tonic-gate static void
10470Sstevel@tonic-gate ebus_debug(uint_t flag, ebus_devstate_t *ebus_p, char *fmt,
10480Sstevel@tonic-gate 	uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5)
10490Sstevel@tonic-gate {
10500Sstevel@tonic-gate 	char *s;
10510Sstevel@tonic-gate 
10520Sstevel@tonic-gate 	if (ebus_debug_flags & flag) {
10530Sstevel@tonic-gate 		switch (flag) {
10540Sstevel@tonic-gate 		case D_ATTACH:
10550Sstevel@tonic-gate 			s = "attach"; break;
10560Sstevel@tonic-gate 		case D_DETACH:
10570Sstevel@tonic-gate 			s = "detach"; break;
10580Sstevel@tonic-gate 		case D_MAP:
10590Sstevel@tonic-gate 			s = "map"; break;
10600Sstevel@tonic-gate 		case D_CTLOPS:
10610Sstevel@tonic-gate 			s = "ctlops"; break;
10620Sstevel@tonic-gate 		case D_INTR:
10630Sstevel@tonic-gate 			s = "intr"; break;
10640Sstevel@tonic-gate 		}
10650Sstevel@tonic-gate 		if (ebus_p)
10660Sstevel@tonic-gate 			cmn_err(CE_CONT, "%s%d: %s: ",
10670Sstevel@tonic-gate 				ddi_get_name(ebus_p->dip),
10680Sstevel@tonic-gate 				ddi_get_instance(ebus_p->dip), s);
10690Sstevel@tonic-gate 		else
10700Sstevel@tonic-gate 			cmn_err(CE_CONT, "ebus: ");
10710Sstevel@tonic-gate 		cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5);
10720Sstevel@tonic-gate 	}
10730Sstevel@tonic-gate }
10740Sstevel@tonic-gate #endif
10750Sstevel@tonic-gate 
10760Sstevel@tonic-gate /* ARGSUSED3 */
10770Sstevel@tonic-gate static int
10780Sstevel@tonic-gate ebus_open(dev_t *devp, int flags, int otyp, cred_t *credp)
10790Sstevel@tonic-gate {
10800Sstevel@tonic-gate 	ebus_devstate_t *ebus_p;
10810Sstevel@tonic-gate 
10820Sstevel@tonic-gate 	/*
10830Sstevel@tonic-gate 	 * Make sure the open is for the right file type.
10840Sstevel@tonic-gate 	 */
10850Sstevel@tonic-gate 	if (otyp != OTYP_CHR)
10860Sstevel@tonic-gate 		return (EINVAL);
10870Sstevel@tonic-gate 
10880Sstevel@tonic-gate 	/*
10890Sstevel@tonic-gate 	 * Get the soft state structure for the device.
10900Sstevel@tonic-gate 	 */
10910Sstevel@tonic-gate 	ebus_p = get_ebus_soft_state(getminor(*devp));
10920Sstevel@tonic-gate 	if (ebus_p == NULL)
10930Sstevel@tonic-gate 		return (ENXIO);
10940Sstevel@tonic-gate 
10950Sstevel@tonic-gate 	/*
10960Sstevel@tonic-gate 	 * Handle the open by tracking the device state.
10970Sstevel@tonic-gate 	 */
10980Sstevel@tonic-gate 	mutex_enter(&ebus_p->ebus_mutex);
10990Sstevel@tonic-gate 	if (flags & FEXCL) {
11000Sstevel@tonic-gate 		if (ebus_p->ebus_soft_state != EBUS_SOFT_STATE_CLOSED) {
11010Sstevel@tonic-gate 			mutex_exit(&ebus_p->ebus_mutex);
11020Sstevel@tonic-gate 			return (EBUSY);
11030Sstevel@tonic-gate 		}
11040Sstevel@tonic-gate 		ebus_p->ebus_soft_state = EBUS_SOFT_STATE_OPEN_EXCL;
11050Sstevel@tonic-gate 	} else {
11060Sstevel@tonic-gate 		if (ebus_p->ebus_soft_state == EBUS_SOFT_STATE_OPEN_EXCL) {
11070Sstevel@tonic-gate 			mutex_exit(&ebus_p->ebus_mutex);
11080Sstevel@tonic-gate 			return (EBUSY);
11090Sstevel@tonic-gate 		}
11100Sstevel@tonic-gate 		ebus_p->ebus_soft_state = EBUS_SOFT_STATE_OPEN;
11110Sstevel@tonic-gate 	}
11120Sstevel@tonic-gate 	mutex_exit(&ebus_p->ebus_mutex);
11130Sstevel@tonic-gate 	return (0);
11140Sstevel@tonic-gate }
11150Sstevel@tonic-gate 
11160Sstevel@tonic-gate 
11170Sstevel@tonic-gate /* ARGSUSED */
11180Sstevel@tonic-gate static int
11190Sstevel@tonic-gate ebus_close(dev_t dev, int flags, int otyp, cred_t *credp)
11200Sstevel@tonic-gate {
11210Sstevel@tonic-gate 	ebus_devstate_t *ebus_p;
11220Sstevel@tonic-gate 
11230Sstevel@tonic-gate 	if (otyp != OTYP_CHR)
11240Sstevel@tonic-gate 		return (EINVAL);
11250Sstevel@tonic-gate 
11260Sstevel@tonic-gate 	ebus_p = get_ebus_soft_state(getminor(dev));
11270Sstevel@tonic-gate 	if (ebus_p == NULL)
11280Sstevel@tonic-gate 		return (ENXIO);
11290Sstevel@tonic-gate 
11300Sstevel@tonic-gate 	mutex_enter(&ebus_p->ebus_mutex);
11310Sstevel@tonic-gate 	ebus_p->ebus_soft_state = EBUS_SOFT_STATE_CLOSED;
11320Sstevel@tonic-gate 	mutex_exit(&ebus_p->ebus_mutex);
11330Sstevel@tonic-gate 	return (0);
11340Sstevel@tonic-gate }
11350Sstevel@tonic-gate 
11360Sstevel@tonic-gate 
11370Sstevel@tonic-gate /*
11380Sstevel@tonic-gate  * ebus_ioctl: devctl hotplug controls
11390Sstevel@tonic-gate  */
11400Sstevel@tonic-gate /* ARGSUSED */
11410Sstevel@tonic-gate static int
11420Sstevel@tonic-gate ebus_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
11430Sstevel@tonic-gate 	int *rvalp)
11440Sstevel@tonic-gate {
11450Sstevel@tonic-gate 	ebus_devstate_t *ebus_p;
11460Sstevel@tonic-gate 	dev_info_t *self;
11470Sstevel@tonic-gate 	struct devctl_iocdata *dcp;
11480Sstevel@tonic-gate 	uint_t bus_state;
11490Sstevel@tonic-gate 	int rv = 0;
11500Sstevel@tonic-gate 
11510Sstevel@tonic-gate 	ebus_p = get_ebus_soft_state(getminor(dev));
11520Sstevel@tonic-gate 	if (ebus_p == NULL)
11530Sstevel@tonic-gate 		return (ENXIO);
11540Sstevel@tonic-gate 
11550Sstevel@tonic-gate 	self = ebus_p->dip;
11560Sstevel@tonic-gate 
11570Sstevel@tonic-gate 	/*
11580Sstevel@tonic-gate 	 * We can use the generic implementation for these ioctls
11590Sstevel@tonic-gate 	 */
11600Sstevel@tonic-gate 	switch (cmd) {
11610Sstevel@tonic-gate 	case DEVCTL_DEVICE_GETSTATE:
11620Sstevel@tonic-gate 	case DEVCTL_DEVICE_ONLINE:
11630Sstevel@tonic-gate 	case DEVCTL_DEVICE_OFFLINE:
11640Sstevel@tonic-gate 	case DEVCTL_BUS_GETSTATE:
11650Sstevel@tonic-gate 		return (ndi_devctl_ioctl(self, cmd, arg, mode, 0));
11660Sstevel@tonic-gate 	}
11670Sstevel@tonic-gate 
11680Sstevel@tonic-gate 	/*
11690Sstevel@tonic-gate 	 * read devctl ioctl data
11700Sstevel@tonic-gate 	 */
11710Sstevel@tonic-gate 	if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)
11720Sstevel@tonic-gate 		return (EFAULT);
11730Sstevel@tonic-gate 
11740Sstevel@tonic-gate 	switch (cmd) {
11750Sstevel@tonic-gate 
11760Sstevel@tonic-gate 	case DEVCTL_DEVICE_RESET:
11770Sstevel@tonic-gate 		rv = ENOTSUP;
11780Sstevel@tonic-gate 		break;
11790Sstevel@tonic-gate 
11800Sstevel@tonic-gate 	case DEVCTL_BUS_QUIESCE:
11810Sstevel@tonic-gate 		if (ndi_get_bus_state(self, &bus_state) == NDI_SUCCESS)
11820Sstevel@tonic-gate 			if (bus_state == BUS_QUIESCED)
11830Sstevel@tonic-gate 				break;
11840Sstevel@tonic-gate 		(void) ndi_set_bus_state(self, BUS_QUIESCED);
11850Sstevel@tonic-gate 		break;
11860Sstevel@tonic-gate 
11870Sstevel@tonic-gate 	case DEVCTL_BUS_UNQUIESCE:
11880Sstevel@tonic-gate 		if (ndi_get_bus_state(self, &bus_state) == NDI_SUCCESS)
11890Sstevel@tonic-gate 			if (bus_state == BUS_ACTIVE)
11900Sstevel@tonic-gate 				break;
11910Sstevel@tonic-gate 		(void) ndi_set_bus_state(self, BUS_ACTIVE);
11920Sstevel@tonic-gate 		break;
11930Sstevel@tonic-gate 
11940Sstevel@tonic-gate 	case DEVCTL_BUS_RESET:
11950Sstevel@tonic-gate 		rv = ENOTSUP;
11960Sstevel@tonic-gate 		break;
11970Sstevel@tonic-gate 
11980Sstevel@tonic-gate 	case DEVCTL_BUS_RESETALL:
11990Sstevel@tonic-gate 		rv = ENOTSUP;
12000Sstevel@tonic-gate 		break;
12010Sstevel@tonic-gate 
12020Sstevel@tonic-gate 	default:
12030Sstevel@tonic-gate 		rv = ENOTTY;
12040Sstevel@tonic-gate 	}
12050Sstevel@tonic-gate 
12060Sstevel@tonic-gate 	ndi_dc_freehdl(dcp);
12070Sstevel@tonic-gate 	return (rv);
12080Sstevel@tonic-gate }
1209