xref: /titanic_50/usr/src/uts/sun4u/io/sbbc.c (revision a195726fa33097e56cf1c25c31feddb827e140f0)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
237c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate /*
307c478bd9Sstevel@tonic-gate  * Starcat PCI SBBC Device Nexus Driver that provides interfaces into
317c478bd9Sstevel@tonic-gate  * Console Bus, I2C, Error/Intr. EPLD, IOSRAM, and JTAG.
327c478bd9Sstevel@tonic-gate  */
337c478bd9Sstevel@tonic-gate 
347c478bd9Sstevel@tonic-gate #include <sys/types.h>
357c478bd9Sstevel@tonic-gate 
367c478bd9Sstevel@tonic-gate #include <sys/conf.h>		/* req. by dev_ops flags MTSAFE etc. */
377c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
387c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
397c478bd9Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
407c478bd9Sstevel@tonic-gate #include <sys/ddi_subrdefs.h>
417c478bd9Sstevel@tonic-gate #include <sys/pci.h>
427c478bd9Sstevel@tonic-gate #include <sys/pci/pci_nexus.h>
437c478bd9Sstevel@tonic-gate #include <sys/autoconf.h>
447c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
457c478bd9Sstevel@tonic-gate #include <sys/param.h>
467c478bd9Sstevel@tonic-gate #include <sys/errno.h>
477c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
487c478bd9Sstevel@tonic-gate #include <sys/debug.h>
497c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
507c478bd9Sstevel@tonic-gate #include <sys/machsystm.h>
517c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
527c478bd9Sstevel@tonic-gate #include <sys/stat.h>
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate 
557c478bd9Sstevel@tonic-gate #include <sys/sbbcreg.h>	/* hw description */
567c478bd9Sstevel@tonic-gate #include <sys/sbbcvar.h>	/* driver description */
577c478bd9Sstevel@tonic-gate #include <sys/sbbcio.h>		/* ioctl description */
587c478bd9Sstevel@tonic-gate 
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate #define	getprop(dip, name, addr, intp)		\
61a3282898Scth 		ddi_getlongprop(DDI_DEV_T_ANY, (dip), DDI_PROP_DONTPASS, \
627c478bd9Sstevel@tonic-gate 				(name), (caddr_t)(addr), (intp))
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate /* driver entry point fn definitions */
657c478bd9Sstevel@tonic-gate static int sbbc_open(dev_t *, int, int, cred_t *);
667c478bd9Sstevel@tonic-gate static int sbbc_close(dev_t, int, int, cred_t *);
677c478bd9Sstevel@tonic-gate static int sbbc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate /* configuration entry point fn definitions */
707c478bd9Sstevel@tonic-gate static int sbbc_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
717c478bd9Sstevel@tonic-gate static int sbbc_attach(dev_info_t *, ddi_attach_cmd_t);
727c478bd9Sstevel@tonic-gate static int sbbc_detach(dev_info_t *, ddi_detach_cmd_t);
737c478bd9Sstevel@tonic-gate 
747c478bd9Sstevel@tonic-gate /* local utility routines */
757c478bd9Sstevel@tonic-gate /*
767c478bd9Sstevel@tonic-gate  * NOTE - sbbc_offset_valid contains detailed address information taken from
777c478bd9Sstevel@tonic-gate  * the Serengeti Architecture Programmer's Reference Manual.  If any
787c478bd9Sstevel@tonic-gate  * changes are made to the SBBC registers, this routine may need to be
797c478bd9Sstevel@tonic-gate  * updated.
807c478bd9Sstevel@tonic-gate  */
817c478bd9Sstevel@tonic-gate static int sbbc_offset_valid(uint32_t offset);
827c478bd9Sstevel@tonic-gate 
837c478bd9Sstevel@tonic-gate /*
847c478bd9Sstevel@tonic-gate  * function prototypes for bus ops routines:
857c478bd9Sstevel@tonic-gate  */
867c478bd9Sstevel@tonic-gate static int sbbc_busmap(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
877c478bd9Sstevel@tonic-gate 	off_t offset, off_t len, caddr_t *addrp);
887c478bd9Sstevel@tonic-gate static int sbbc_ctlops(dev_info_t *dip, dev_info_t *rdip,
897c478bd9Sstevel@tonic-gate 	ddi_ctl_enum_t op, void *arg, void *result);
907c478bd9Sstevel@tonic-gate 
917c478bd9Sstevel@tonic-gate static int sbbc_intr_ops(dev_info_t *dip, dev_info_t *rdip,
927c478bd9Sstevel@tonic-gate 	ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
937c478bd9Sstevel@tonic-gate static int sbbc_add_intr_impl(dev_info_t *dip, dev_info_t *rdip,
947c478bd9Sstevel@tonic-gate 	ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
957c478bd9Sstevel@tonic-gate static int sbbc_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip,
967c478bd9Sstevel@tonic-gate 	ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
977c478bd9Sstevel@tonic-gate static int sbbc_update_intr_state(dev_info_t *dip, dev_info_t *rdip,
987c478bd9Sstevel@tonic-gate 	ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
997c478bd9Sstevel@tonic-gate 
1007c478bd9Sstevel@tonic-gate static int sbbc_apply_range(struct sbbcsoft *sbbc_p, dev_info_t *rdip,
1017c478bd9Sstevel@tonic-gate     sbbc_child_regspec_t *child_rp, pci_regspec_t *rp);
1027c478bd9Sstevel@tonic-gate 
1037c478bd9Sstevel@tonic-gate static int sbbc_init(struct sbbcsoft *);
1047c478bd9Sstevel@tonic-gate 
1057c478bd9Sstevel@tonic-gate static uint_t sbbc_intr_wrapper(caddr_t arg);
1067c478bd9Sstevel@tonic-gate 
1077c478bd9Sstevel@tonic-gate static int sbbc_get_ranges(struct sbbcsoft *);
1087c478bd9Sstevel@tonic-gate static int sbbc_config4pci(struct sbbcsoft *);
1097c478bd9Sstevel@tonic-gate static int sbbc_initchild(dev_info_t *, dev_info_t *, dev_info_t *);
1107c478bd9Sstevel@tonic-gate static int sbbc_uninitchild(dev_info_t *, dev_info_t *);
1117c478bd9Sstevel@tonic-gate static void sbbc_remove_reg_maps(struct sbbcsoft *);
1127c478bd9Sstevel@tonic-gate 
1137c478bd9Sstevel@tonic-gate /* debugging functions */
1147c478bd9Sstevel@tonic-gate #ifdef DEBUG
1157c478bd9Sstevel@tonic-gate uint32_t sbbc_dbg_flags = 0x0;
1167c478bd9Sstevel@tonic-gate static void sbbc_dbg(uint32_t flag, dev_info_t *dip, char *fmt,
1177c478bd9Sstevel@tonic-gate 	uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5);
1187c478bd9Sstevel@tonic-gate #endif
1197c478bd9Sstevel@tonic-gate 
1207c478bd9Sstevel@tonic-gate /*
1217c478bd9Sstevel@tonic-gate  * For tracing, allocate space for the trace buffer
1227c478bd9Sstevel@tonic-gate  */
1237c478bd9Sstevel@tonic-gate #if defined(SBBC_TRACE)
1247c478bd9Sstevel@tonic-gate struct sbbctrace sbbctrace_buffer[NSBBCTRACE+1];
1257c478bd9Sstevel@tonic-gate struct sbbctrace *sbbctrace_ptr;
1267c478bd9Sstevel@tonic-gate int sbbctrace_count;
1277c478bd9Sstevel@tonic-gate #endif
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate /*
1307c478bd9Sstevel@tonic-gate  * Local declarations and variables
1317c478bd9Sstevel@tonic-gate  */
1327c478bd9Sstevel@tonic-gate 
1337c478bd9Sstevel@tonic-gate static void *sbbcsoft_statep;
1347c478bd9Sstevel@tonic-gate int sbbc_scmode = FALSE;
1357c478bd9Sstevel@tonic-gate 
1367c478bd9Sstevel@tonic-gate /*
1377c478bd9Sstevel@tonic-gate  * ops stuff.
1387c478bd9Sstevel@tonic-gate  */
1397c478bd9Sstevel@tonic-gate static struct bus_ops sbbc_bus_ops = {
1407c478bd9Sstevel@tonic-gate 	BUSO_REV,
1417c478bd9Sstevel@tonic-gate 	sbbc_busmap,
1427c478bd9Sstevel@tonic-gate 	0,
1437c478bd9Sstevel@tonic-gate 	0,
1447c478bd9Sstevel@tonic-gate 	0,
1457c478bd9Sstevel@tonic-gate 	NULL, 			/* (*bus_map_fault)() */
1467c478bd9Sstevel@tonic-gate 	ddi_no_dma_map,
1477c478bd9Sstevel@tonic-gate 	ddi_no_dma_allochdl,
1487c478bd9Sstevel@tonic-gate 	ddi_no_dma_freehdl, 	/* (*bus_dma_freehdl)() */
1497c478bd9Sstevel@tonic-gate 	ddi_no_dma_bindhdl, 	/* (*bus_dma_bindhdl)() */
1507c478bd9Sstevel@tonic-gate 	ddi_no_dma_unbindhdl, 	/* (*bus_dma_unbindhdl)() */
1517c478bd9Sstevel@tonic-gate 	ddi_no_dma_flush, 	/* (*bus_dma_flush)() */
1527c478bd9Sstevel@tonic-gate 	ddi_no_dma_win, 	/* (*bus_dma_win)() */
1537c478bd9Sstevel@tonic-gate 	ddi_no_dma_mctl, 	/* (*bus_dma_ctl)() */
1547c478bd9Sstevel@tonic-gate 	sbbc_ctlops,
1557c478bd9Sstevel@tonic-gate 	ddi_bus_prop_op,
1567c478bd9Sstevel@tonic-gate 	0,			/* (*bus_get_eventcookie)();	*/
1577c478bd9Sstevel@tonic-gate 	0,			/* (*bus_add_eventcall)();	*/
1587c478bd9Sstevel@tonic-gate 	0,			/* (*bus_remove_eventcall)();	*/
1597c478bd9Sstevel@tonic-gate 	0,			/* (*bus_post_event)();		*/
1607c478bd9Sstevel@tonic-gate 	0,			/* (*bus_intr_ctl)();	*/
1617c478bd9Sstevel@tonic-gate 	0,			/* (*bus_config)();	*/
1627c478bd9Sstevel@tonic-gate 	0,			/* (*bus_unconfig)();	*/
1637c478bd9Sstevel@tonic-gate 	0,			/* (*bus_fm_init)();	*/
1647c478bd9Sstevel@tonic-gate 	0,			/* (*bus_fm_fini)();	*/
1657c478bd9Sstevel@tonic-gate 	0,			/* (*bus_fm_access_enter)();	*/
1667c478bd9Sstevel@tonic-gate 	0,			/* (*bus_fm_access_exit)();	*/
1677c478bd9Sstevel@tonic-gate 	0,			/* (*bus_power)();	*/
1687c478bd9Sstevel@tonic-gate 	sbbc_intr_ops		/* (*bus_intr_op)();	*/
1697c478bd9Sstevel@tonic-gate };
1707c478bd9Sstevel@tonic-gate 
1717c478bd9Sstevel@tonic-gate /*
1727c478bd9Sstevel@tonic-gate  * cb_ops
1737c478bd9Sstevel@tonic-gate  */
1747c478bd9Sstevel@tonic-gate static struct cb_ops sbbc_cb_ops = {
1757c478bd9Sstevel@tonic-gate 	sbbc_open,		/* cb_open */
1767c478bd9Sstevel@tonic-gate 	sbbc_close,		/* cb_close */
1777c478bd9Sstevel@tonic-gate 	nodev,			/* cb_strategy */
1787c478bd9Sstevel@tonic-gate 	nodev,			/* cb_print */
1797c478bd9Sstevel@tonic-gate 	nodev,			/* cb_dump */
1807c478bd9Sstevel@tonic-gate 	nodev,			/* cb_read */
1817c478bd9Sstevel@tonic-gate 	nodev,			/* cb_write */
1827c478bd9Sstevel@tonic-gate 	sbbc_ioctl,		/* cb_ioctl */
1837c478bd9Sstevel@tonic-gate 	nodev,			/* cb_devmap */
1847c478bd9Sstevel@tonic-gate 	nodev,			/* cb_mmap */
1857c478bd9Sstevel@tonic-gate 	nodev,			/* cb_segmap */
1867c478bd9Sstevel@tonic-gate 	nochpoll,		/* cb_chpoll */
1877c478bd9Sstevel@tonic-gate 	ddi_prop_op,		/* cb_prop_op */
1887c478bd9Sstevel@tonic-gate 	NULL,			/* cb_stream */
1897c478bd9Sstevel@tonic-gate 	(int)(D_NEW | D_MP)	/* cb_flag */
1907c478bd9Sstevel@tonic-gate };
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate /*
1937c478bd9Sstevel@tonic-gate  * Declare ops vectors for auto configuration.
1947c478bd9Sstevel@tonic-gate  */
1957c478bd9Sstevel@tonic-gate struct dev_ops  sbbc_ops = {
1967c478bd9Sstevel@tonic-gate 	DEVO_REV,		/* devo_rev */
1977c478bd9Sstevel@tonic-gate 	0,			/* devo_refcnt */
1987c478bd9Sstevel@tonic-gate 	sbbc_getinfo,		/* devo_getinfo */
1997c478bd9Sstevel@tonic-gate 	nulldev,		/* devo_identify */
2007c478bd9Sstevel@tonic-gate 	nulldev,		/* devo_probe */
2017c478bd9Sstevel@tonic-gate 	sbbc_attach,		/* devo_attach */
2027c478bd9Sstevel@tonic-gate 	sbbc_detach,		/* devo_detach */
2037c478bd9Sstevel@tonic-gate 	nodev,			/* devo_reset */
2047c478bd9Sstevel@tonic-gate 	&sbbc_cb_ops,		/* devo_cb_ops */
2057c478bd9Sstevel@tonic-gate 	&sbbc_bus_ops,		/* devo_bus_ops */
2067c478bd9Sstevel@tonic-gate 	nulldev			/* devo_power */
2077c478bd9Sstevel@tonic-gate };
2087c478bd9Sstevel@tonic-gate 
2097c478bd9Sstevel@tonic-gate /*
2107c478bd9Sstevel@tonic-gate  * Loadable module support.
2117c478bd9Sstevel@tonic-gate  */
2127c478bd9Sstevel@tonic-gate extern struct mod_ops mod_driverops;
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate static struct modldrv sbbcmodldrv = {
2157c478bd9Sstevel@tonic-gate 	&mod_driverops,		/* type of module - driver */
2167c478bd9Sstevel@tonic-gate 	"PCI Sbbc Nexus Driver v%I%",
2177c478bd9Sstevel@tonic-gate 	&sbbc_ops,
2187c478bd9Sstevel@tonic-gate };
2197c478bd9Sstevel@tonic-gate 
2207c478bd9Sstevel@tonic-gate static struct modlinkage sbbcmodlinkage = {
2217c478bd9Sstevel@tonic-gate 	MODREV_1,
2227c478bd9Sstevel@tonic-gate 	&sbbcmodldrv,
2237c478bd9Sstevel@tonic-gate 	NULL
2247c478bd9Sstevel@tonic-gate };
2257c478bd9Sstevel@tonic-gate 
2267c478bd9Sstevel@tonic-gate int
2277c478bd9Sstevel@tonic-gate _init(void)
2287c478bd9Sstevel@tonic-gate {
2297c478bd9Sstevel@tonic-gate 	int    error;
2307c478bd9Sstevel@tonic-gate 
2317c478bd9Sstevel@tonic-gate 	if ((error = ddi_soft_state_init(&sbbcsoft_statep,
2327c478bd9Sstevel@tonic-gate 		    sizeof (struct sbbcsoft), 1)) != 0)
2337c478bd9Sstevel@tonic-gate 		return (error);
2347c478bd9Sstevel@tonic-gate 	if ((error = mod_install(&sbbcmodlinkage)) != 0)
2357c478bd9Sstevel@tonic-gate 		ddi_soft_state_fini(&sbbcsoft_statep);
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate 	return (error);
2387c478bd9Sstevel@tonic-gate }
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate int
2417c478bd9Sstevel@tonic-gate _fini(void)
2427c478bd9Sstevel@tonic-gate {
2437c478bd9Sstevel@tonic-gate 	int    error;
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate 	if ((error = mod_remove(&sbbcmodlinkage)) == 0)
2467c478bd9Sstevel@tonic-gate 		ddi_soft_state_fini(&sbbcsoft_statep);
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate 	return (error);
2497c478bd9Sstevel@tonic-gate }
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate int
2527c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
2537c478bd9Sstevel@tonic-gate {
2547c478bd9Sstevel@tonic-gate 	return (mod_info(&sbbcmodlinkage, modinfop));
2557c478bd9Sstevel@tonic-gate }
2567c478bd9Sstevel@tonic-gate 
2577c478bd9Sstevel@tonic-gate static int
2587c478bd9Sstevel@tonic-gate sbbc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2597c478bd9Sstevel@tonic-gate {
2607c478bd9Sstevel@tonic-gate 	int	instance;
2617c478bd9Sstevel@tonic-gate 	char	name[32];
2627c478bd9Sstevel@tonic-gate 	struct	sbbcsoft *sbbcsoftp;
2637c478bd9Sstevel@tonic-gate 	struct ddi_device_acc_attr attr;
2647c478bd9Sstevel@tonic-gate 	uint32_t sbbc_id_reg = 0;
2657c478bd9Sstevel@tonic-gate 	uint16_t sbbc_id_reg_partid;
2667c478bd9Sstevel@tonic-gate 	uint16_t sbbc_id_reg_manfid;
2677c478bd9Sstevel@tonic-gate 
2687c478bd9Sstevel@tonic-gate 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
2697c478bd9Sstevel@tonic-gate 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
2707c478bd9Sstevel@tonic-gate 	attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
2717c478bd9Sstevel@tonic-gate 
2727c478bd9Sstevel@tonic-gate 	/* initialize tracing */
2737c478bd9Sstevel@tonic-gate 	SBBCTRACEINIT();
2747c478bd9Sstevel@tonic-gate 
2757c478bd9Sstevel@tonic-gate 	SBBC_DBG0(SBBC_DBG_ATTACH, dip, "Attaching\n");
2767c478bd9Sstevel@tonic-gate 
2777c478bd9Sstevel@tonic-gate 	instance = ddi_get_instance(dip);
2787c478bd9Sstevel@tonic-gate 	switch (cmd) {
2797c478bd9Sstevel@tonic-gate 	case DDI_ATTACH:
2807c478bd9Sstevel@tonic-gate 		break;
2817c478bd9Sstevel@tonic-gate 	case DDI_RESUME:
2827c478bd9Sstevel@tonic-gate 		if (!(sbbcsoftp =
2837c478bd9Sstevel@tonic-gate 		    ddi_get_soft_state(sbbcsoft_statep, instance))) {
2847c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN,
2857c478bd9Sstevel@tonic-gate 	    "sbbc_attach:resume: unable to acquire sbbcsoftp for instance %d",
2867c478bd9Sstevel@tonic-gate 			    instance);
2877c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
2887c478bd9Sstevel@tonic-gate 		}
2897c478bd9Sstevel@tonic-gate 		mutex_enter(&sbbcsoftp->umutex);
2907c478bd9Sstevel@tonic-gate 		if (!sbbcsoftp->suspended) {
2917c478bd9Sstevel@tonic-gate 			mutex_exit(&sbbcsoftp->umutex);
2927c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
2937c478bd9Sstevel@tonic-gate 		}
2947c478bd9Sstevel@tonic-gate 		sbbcsoftp->suspended = 0;
2957c478bd9Sstevel@tonic-gate 		mutex_exit(&sbbcsoftp->umutex);
2967c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
2977c478bd9Sstevel@tonic-gate 
2987c478bd9Sstevel@tonic-gate 	default:
2997c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
3007c478bd9Sstevel@tonic-gate 	}
3017c478bd9Sstevel@tonic-gate 
3027c478bd9Sstevel@tonic-gate 	if (ddi_soft_state_zalloc(sbbcsoft_statep, instance) != 0) {
3037c478bd9Sstevel@tonic-gate 	    cmn_err(CE_WARN,
3047c478bd9Sstevel@tonic-gate 	    "sbbc_attach: Unable to allocate statep for instance %d",
3057c478bd9Sstevel@tonic-gate 				    instance);
3067c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
3077c478bd9Sstevel@tonic-gate 	}
3087c478bd9Sstevel@tonic-gate 
3097c478bd9Sstevel@tonic-gate 	sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance);
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate 	if (sbbcsoftp == NULL) {
3127c478bd9Sstevel@tonic-gate 	    cmn_err(CE_WARN,
3137c478bd9Sstevel@tonic-gate 	    "sbbc_attach: Unable to acquire sbbcsoftp for instance %d",
3147c478bd9Sstevel@tonic-gate 					    instance);
3157c478bd9Sstevel@tonic-gate 		ddi_soft_state_free(sbbcsoft_statep, instance);
3167c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
3177c478bd9Sstevel@tonic-gate 	}
3187c478bd9Sstevel@tonic-gate 
3197c478bd9Sstevel@tonic-gate 	sbbcsoftp->instance = instance;
3207c478bd9Sstevel@tonic-gate 	sbbcsoftp->dip = dip;
3217c478bd9Sstevel@tonic-gate 	sbbcsoftp->oflag = FALSE;
3227c478bd9Sstevel@tonic-gate 
3237c478bd9Sstevel@tonic-gate 	/*
3247c478bd9Sstevel@tonic-gate 	 * Read our ranges property from OBP to map children space.
3257c478bd9Sstevel@tonic-gate 	 * And setup the internal structure for a later use when
3267c478bd9Sstevel@tonic-gate 	 * a child gets initialized.
3277c478bd9Sstevel@tonic-gate 	 */
3287c478bd9Sstevel@tonic-gate 	if (sbbc_get_ranges(sbbcsoftp)) {
3297c478bd9Sstevel@tonic-gate 	    cmn_err(CE_WARN,
3307c478bd9Sstevel@tonic-gate 	    "sbbc_attach: Unable to read sbbc ranges from OBP %d", instance);
3317c478bd9Sstevel@tonic-gate 	    ddi_soft_state_free(sbbcsoft_statep, instance);
3327c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
3337c478bd9Sstevel@tonic-gate 	}
3347c478bd9Sstevel@tonic-gate 
3357c478bd9Sstevel@tonic-gate 	if (sbbc_config4pci(sbbcsoftp)) {
3367c478bd9Sstevel@tonic-gate 	    cmn_err(CE_WARN,
3377c478bd9Sstevel@tonic-gate 	    "sbbc_attach: Unable to configure sbbc on PCI %d", instance);
3387c478bd9Sstevel@tonic-gate 	    kmem_free(sbbcsoftp->rangep, sbbcsoftp->range_len);
3397c478bd9Sstevel@tonic-gate 	    ddi_soft_state_free(sbbcsoft_statep, instance);
3407c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
3417c478bd9Sstevel@tonic-gate 	}
3427c478bd9Sstevel@tonic-gate 
3437c478bd9Sstevel@tonic-gate 	/*
3447c478bd9Sstevel@tonic-gate 	 * Map SBBC's internal registers used by hardware access daemon.
3457c478bd9Sstevel@tonic-gate 	 */
3467c478bd9Sstevel@tonic-gate 
3477c478bd9Sstevel@tonic-gate 	/* map the whole thing since OBP does not map individual devices */
3487c478bd9Sstevel@tonic-gate 	if (ddi_regs_map_setup(dip, 1, (caddr_t *)&sbbcsoftp->pci_sbbc_map,
3497c478bd9Sstevel@tonic-gate 	    0, 0, &attr, &sbbcsoftp->pci_sbbc_map_handle) != DDI_SUCCESS) {
3507c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "(%d):sbbc_attach failed to map sbbc_reg",
3517c478bd9Sstevel@tonic-gate 			instance);
3527c478bd9Sstevel@tonic-gate 		goto failed;
3537c478bd9Sstevel@tonic-gate 	}
3547c478bd9Sstevel@tonic-gate 
3557c478bd9Sstevel@tonic-gate 	sbbc_id_reg = ddi_get32(sbbcsoftp->pci_sbbc_map_handle,
3567c478bd9Sstevel@tonic-gate 		    (uint32_t *)
3577c478bd9Sstevel@tonic-gate 		    &sbbcsoftp->pci_sbbc_map->sbbc_internal_regs.device_conf);
3587c478bd9Sstevel@tonic-gate 
3597c478bd9Sstevel@tonic-gate 	if (sbbc_id_reg & SBBC_SC_MODE) {
3607c478bd9Sstevel@tonic-gate 
3617c478bd9Sstevel@tonic-gate 		SBBC_DBG5(SBBC_DBG_ATTACH, dip,
3627c478bd9Sstevel@tonic-gate 	"Mapped sbbc %llx, regs %llx, eregs %llx, sram %llx, consbus %llx\n",
3637c478bd9Sstevel@tonic-gate 		    sbbcsoftp->pci_sbbc_map,
3647c478bd9Sstevel@tonic-gate 		    &sbbcsoftp->pci_sbbc_map->sbbc_internal_regs,
3657c478bd9Sstevel@tonic-gate 		    &sbbcsoftp->pci_sbbc_map->echip_regs,
3667c478bd9Sstevel@tonic-gate 		    &sbbcsoftp->pci_sbbc_map->sram[0],
3677c478bd9Sstevel@tonic-gate 		    &sbbcsoftp->pci_sbbc_map->consbus);
3687c478bd9Sstevel@tonic-gate 
3697c478bd9Sstevel@tonic-gate 		sbbc_id_reg = ddi_get32(sbbcsoftp->pci_sbbc_map_handle,
3707c478bd9Sstevel@tonic-gate 			    (uint32_t *)
3717c478bd9Sstevel@tonic-gate 		    &sbbcsoftp->pci_sbbc_map->sbbc_internal_regs.devid);
3727c478bd9Sstevel@tonic-gate 		sbbc_id_reg_partid = ((sbbc_id_reg << 4) >> 16);
3737c478bd9Sstevel@tonic-gate 		sbbc_id_reg_manfid = ((sbbc_id_reg << 20) >> 21);
3747c478bd9Sstevel@tonic-gate 		SBBC_DBG4(SBBC_DBG_ATTACH, dip,
3757c478bd9Sstevel@tonic-gate 		    "FOUND SBBC(%d) Version %x, Partid %x, Manfid %x\n",
3767c478bd9Sstevel@tonic-gate 		    instance, (sbbc_id_reg >> 28), sbbc_id_reg_partid,
3777c478bd9Sstevel@tonic-gate 		    sbbc_id_reg_manfid);
3787c478bd9Sstevel@tonic-gate 
3797c478bd9Sstevel@tonic-gate 		sbbc_scmode = TRUE;
3807c478bd9Sstevel@tonic-gate 		SBBC_DBG1(SBBC_DBG_ATTACH, dip,
3817c478bd9Sstevel@tonic-gate 	    "SBBC(%d) nexus running in System Controller Mode.\n",
3827c478bd9Sstevel@tonic-gate 		    instance);
3837c478bd9Sstevel@tonic-gate 
3847c478bd9Sstevel@tonic-gate 		/*
3857c478bd9Sstevel@tonic-gate 		 * There will be only one SBBC instance on SC and no
3867c478bd9Sstevel@tonic-gate 		 * chosen node stuff to deal with :-)
3877c478bd9Sstevel@tonic-gate 		 */
3887c478bd9Sstevel@tonic-gate 
3897c478bd9Sstevel@tonic-gate 	} else {
3907c478bd9Sstevel@tonic-gate 		/* The code below needs to be common with SC mode above */
3917c478bd9Sstevel@tonic-gate 
3927c478bd9Sstevel@tonic-gate 		SBBC_DBG4(SBBC_DBG_ATTACH, dip,
3937c478bd9Sstevel@tonic-gate 	"Mapped sbbc %llx, regs %llx, eregs %llx, sram %llx\n",
3947c478bd9Sstevel@tonic-gate 		    sbbcsoftp->pci_sbbc_map,
3957c478bd9Sstevel@tonic-gate 		    &sbbcsoftp->pci_sbbc_map->sbbc_internal_regs,
3967c478bd9Sstevel@tonic-gate 		    &sbbcsoftp->pci_sbbc_map->echip_regs,
3977c478bd9Sstevel@tonic-gate 		    &sbbcsoftp->pci_sbbc_map->sram[0]);
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate 		sbbc_id_reg = ddi_get32(sbbcsoftp->pci_sbbc_map_handle,
4007c478bd9Sstevel@tonic-gate 			    (uint32_t *)
4017c478bd9Sstevel@tonic-gate 		    &sbbcsoftp->pci_sbbc_map->sbbc_internal_regs.devid);
4027c478bd9Sstevel@tonic-gate 		sbbc_id_reg_partid = ((sbbc_id_reg << 4) >> 16);
4037c478bd9Sstevel@tonic-gate 		sbbc_id_reg_manfid = ((sbbc_id_reg << 20) >> 21);
4047c478bd9Sstevel@tonic-gate 		SBBC_DBG4(SBBC_DBG_ATTACH, dip,
4057c478bd9Sstevel@tonic-gate 		    "FOUND SBBC(%d) Version %x, Partid %x, Manfid %x\n",
4067c478bd9Sstevel@tonic-gate 		    instance, (sbbc_id_reg >> 28), sbbc_id_reg_partid,
4077c478bd9Sstevel@tonic-gate 		    sbbc_id_reg_manfid);
4087c478bd9Sstevel@tonic-gate 
4097c478bd9Sstevel@tonic-gate 		sbbc_scmode = FALSE;
4107c478bd9Sstevel@tonic-gate 		SBBC_DBG1(SBBC_DBG_ATTACH, dip,
4117c478bd9Sstevel@tonic-gate 	    "SBBC(%d) nexus running in Domain Mode.\n",
4127c478bd9Sstevel@tonic-gate 		    instance);
4137c478bd9Sstevel@tonic-gate 
4147c478bd9Sstevel@tonic-gate 		/*
4157c478bd9Sstevel@tonic-gate 		 * There will be only one SBBC instance on SC and no
4167c478bd9Sstevel@tonic-gate 		 * chosen node stuff to deal with :-)
4177c478bd9Sstevel@tonic-gate 		 */
4187c478bd9Sstevel@tonic-gate 
4197c478bd9Sstevel@tonic-gate 	}
4207c478bd9Sstevel@tonic-gate 
4217c478bd9Sstevel@tonic-gate 	mutex_init(&sbbcsoftp->umutex, NULL, MUTEX_DRIVER, (void *)NULL);
4227c478bd9Sstevel@tonic-gate 	mutex_init(&sbbcsoftp->sbbc_intr_mutex, NULL,
4237c478bd9Sstevel@tonic-gate 	    MUTEX_DRIVER, (void *)NULL);
4247c478bd9Sstevel@tonic-gate 
4257c478bd9Sstevel@tonic-gate 	/* initialize sbbc */
4267c478bd9Sstevel@tonic-gate 	if (!sbbc_init(sbbcsoftp)) {
4277c478bd9Sstevel@tonic-gate 		goto remlock;
4287c478bd9Sstevel@tonic-gate 	}
4297c478bd9Sstevel@tonic-gate 
4307c478bd9Sstevel@tonic-gate 	(void) sprintf(name, "sbbc%d", instance);
4317c478bd9Sstevel@tonic-gate 
4327c478bd9Sstevel@tonic-gate 	if (ddi_create_minor_node(dip, name, S_IFCHR, instance, NULL,
4337c478bd9Sstevel@tonic-gate 	    NULL) == DDI_FAILURE) {
4347c478bd9Sstevel@tonic-gate 		ddi_remove_minor_node(dip, NULL);
4357c478bd9Sstevel@tonic-gate 		goto remlock;
4367c478bd9Sstevel@tonic-gate 	}
4377c478bd9Sstevel@tonic-gate 
4387c478bd9Sstevel@tonic-gate 	ddi_report_dev(dip);
4397c478bd9Sstevel@tonic-gate 
4407c478bd9Sstevel@tonic-gate 	SBBC_DBG0(SBBC_DBG_ATTACH, dip, "Attached successfully\n");
4417c478bd9Sstevel@tonic-gate 
4427c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
4437c478bd9Sstevel@tonic-gate 
4447c478bd9Sstevel@tonic-gate remlock:
4457c478bd9Sstevel@tonic-gate 	mutex_destroy(&sbbcsoftp->sbbc_intr_mutex);
4467c478bd9Sstevel@tonic-gate 	mutex_destroy(&sbbcsoftp->umutex);
4477c478bd9Sstevel@tonic-gate 
4487c478bd9Sstevel@tonic-gate failed:
4497c478bd9Sstevel@tonic-gate 	sbbc_remove_reg_maps(sbbcsoftp);
4507c478bd9Sstevel@tonic-gate 	kmem_free(sbbcsoftp->rangep, sbbcsoftp->range_len);
4517c478bd9Sstevel@tonic-gate 	ddi_soft_state_free(sbbcsoft_statep, instance);
4527c478bd9Sstevel@tonic-gate 
4537c478bd9Sstevel@tonic-gate 	SBBC_DBG0(SBBC_DBG_ATTACH, dip, "Attach failed\n");
4547c478bd9Sstevel@tonic-gate 
4557c478bd9Sstevel@tonic-gate 	return (DDI_FAILURE);
4567c478bd9Sstevel@tonic-gate }
4577c478bd9Sstevel@tonic-gate 
4587c478bd9Sstevel@tonic-gate static int
4597c478bd9Sstevel@tonic-gate sbbc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
4607c478bd9Sstevel@tonic-gate {
4617c478bd9Sstevel@tonic-gate 	int		instance;
4627c478bd9Sstevel@tonic-gate 	struct sbbcsoft *sbbcsoftp;
4637c478bd9Sstevel@tonic-gate 
4647c478bd9Sstevel@tonic-gate 	SBBCTRACE(sbbc_detach, 'DETA', dip);
4657c478bd9Sstevel@tonic-gate 
4667c478bd9Sstevel@tonic-gate 	instance = ddi_get_instance(dip);
4677c478bd9Sstevel@tonic-gate 
4687c478bd9Sstevel@tonic-gate 	switch (cmd) {
4697c478bd9Sstevel@tonic-gate 	case DDI_DETACH:
4707c478bd9Sstevel@tonic-gate 		break;
4717c478bd9Sstevel@tonic-gate 
4727c478bd9Sstevel@tonic-gate 	case DDI_SUSPEND:
4737c478bd9Sstevel@tonic-gate 		if (!(sbbcsoftp =
4747c478bd9Sstevel@tonic-gate 		    ddi_get_soft_state(sbbcsoft_statep, instance))) {
4757c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN,
4767c478bd9Sstevel@tonic-gate 			    "sbbc_detach: unable to get softstate %p",
4777c478bd9Sstevel@tonic-gate 			    (void *)sbbcsoftp);
4787c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
4797c478bd9Sstevel@tonic-gate 		}
4807c478bd9Sstevel@tonic-gate 		mutex_enter(&sbbcsoftp->umutex);
4817c478bd9Sstevel@tonic-gate 		if (sbbcsoftp->suspended) {
4827c478bd9Sstevel@tonic-gate 		    mutex_exit(&sbbcsoftp->umutex);
4837c478bd9Sstevel@tonic-gate 		    return (DDI_FAILURE);
4847c478bd9Sstevel@tonic-gate 		}
4857c478bd9Sstevel@tonic-gate 		sbbcsoftp->suspended = 1;
4867c478bd9Sstevel@tonic-gate 		mutex_exit(&sbbcsoftp->umutex);
4877c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
4887c478bd9Sstevel@tonic-gate 
4897c478bd9Sstevel@tonic-gate 	default:
4907c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
4917c478bd9Sstevel@tonic-gate 	}
4927c478bd9Sstevel@tonic-gate 
4937c478bd9Sstevel@tonic-gate 	if (!(sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance))) {
4947c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "sbbc_detach: unable to get softstate %p",
4957c478bd9Sstevel@tonic-gate 		    (void *)sbbcsoftp);
4967c478bd9Sstevel@tonic-gate 	    return (DDI_FAILURE);
4977c478bd9Sstevel@tonic-gate 	}
4987c478bd9Sstevel@tonic-gate 
4997c478bd9Sstevel@tonic-gate 	ddi_remove_minor_node(dip, NULL);
5007c478bd9Sstevel@tonic-gate 
5017c478bd9Sstevel@tonic-gate 	mutex_destroy(&sbbcsoftp->sbbc_intr_mutex);
5027c478bd9Sstevel@tonic-gate 	mutex_destroy(&sbbcsoftp->umutex);
5037c478bd9Sstevel@tonic-gate 
5047c478bd9Sstevel@tonic-gate 	sbbc_remove_reg_maps(sbbcsoftp);
5057c478bd9Sstevel@tonic-gate 	kmem_free(sbbcsoftp->rangep, sbbcsoftp->range_len);
5067c478bd9Sstevel@tonic-gate 
5077c478bd9Sstevel@tonic-gate 	ddi_soft_state_free(sbbcsoft_statep, instance);
5087c478bd9Sstevel@tonic-gate 
5097c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
5107c478bd9Sstevel@tonic-gate 
5117c478bd9Sstevel@tonic-gate }
5127c478bd9Sstevel@tonic-gate 
5137c478bd9Sstevel@tonic-gate 
5147c478bd9Sstevel@tonic-gate /*
5157c478bd9Sstevel@tonic-gate  * Translate child's address into parents.
5167c478bd9Sstevel@tonic-gate  */
5177c478bd9Sstevel@tonic-gate static int
5187c478bd9Sstevel@tonic-gate sbbc_busmap(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
5197c478bd9Sstevel@tonic-gate 	    off_t off, off_t len, caddr_t *addrp)
5207c478bd9Sstevel@tonic-gate {
5217c478bd9Sstevel@tonic-gate 	struct sbbcsoft *sbbcsoftp;
5227c478bd9Sstevel@tonic-gate 	sbbc_child_regspec_t *child_rp, *child_regs;
5237c478bd9Sstevel@tonic-gate 	pci_regspec_t pci_reg;
5247c478bd9Sstevel@tonic-gate 	ddi_map_req_t p_map_request;
5257c478bd9Sstevel@tonic-gate 	int rnumber, i, n;
5267c478bd9Sstevel@tonic-gate 	int rval = DDI_SUCCESS;
5277c478bd9Sstevel@tonic-gate 	int instance;
5287c478bd9Sstevel@tonic-gate 
5297c478bd9Sstevel@tonic-gate 	SBBC_DBG4(SBBC_DBG_BUSMAP, dip,
5307c478bd9Sstevel@tonic-gate 	    "mapping child %s, type %llx, off %llx, len %llx\n",
5317c478bd9Sstevel@tonic-gate 	    ddi_driver_name(rdip), mp->map_type, off, len);
5327c478bd9Sstevel@tonic-gate 
5337c478bd9Sstevel@tonic-gate 	SBBCTRACE(sbbc_busmap, 'BMAP', mp);
5347c478bd9Sstevel@tonic-gate 
5357c478bd9Sstevel@tonic-gate 	/*
5367c478bd9Sstevel@tonic-gate 	 * Handle the mapping according to its type.
5377c478bd9Sstevel@tonic-gate 	 */
5387c478bd9Sstevel@tonic-gate 	instance = ddi_get_instance(dip);
5397c478bd9Sstevel@tonic-gate 	if (!(sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance)))
5407c478bd9Sstevel@tonic-gate 	    return (DDI_FAILURE);
5417c478bd9Sstevel@tonic-gate 
5427c478bd9Sstevel@tonic-gate 	switch (mp->map_type) {
5437c478bd9Sstevel@tonic-gate 	case DDI_MT_REGSPEC:
5447c478bd9Sstevel@tonic-gate 
5457c478bd9Sstevel@tonic-gate 		/*
5467c478bd9Sstevel@tonic-gate 		 * We assume the register specification is in sbbc format.
5477c478bd9Sstevel@tonic-gate 		 * We must convert it into a PCI format regspec and pass
5487c478bd9Sstevel@tonic-gate 		 * the request to our parent.
5497c478bd9Sstevel@tonic-gate 		 */
5507c478bd9Sstevel@tonic-gate 		child_rp = (sbbc_child_regspec_t *)mp->map_obj.rp;
5517c478bd9Sstevel@tonic-gate 		break;
5527c478bd9Sstevel@tonic-gate 
5537c478bd9Sstevel@tonic-gate 	case DDI_MT_RNUMBER:
5547c478bd9Sstevel@tonic-gate 
5557c478bd9Sstevel@tonic-gate 		/*
5567c478bd9Sstevel@tonic-gate 		 * map_type 0
5577c478bd9Sstevel@tonic-gate 		 * Get the "reg" property from the device node and convert
5587c478bd9Sstevel@tonic-gate 		 * it to our parent's format.
5597c478bd9Sstevel@tonic-gate 		 */
5607c478bd9Sstevel@tonic-gate 		rnumber = mp->map_obj.rnumber;
5617c478bd9Sstevel@tonic-gate 
5627c478bd9Sstevel@tonic-gate 		/* get the requester's reg property */
563a3282898Scth 		if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
5647c478bd9Sstevel@tonic-gate 		    "reg", (caddr_t)&child_regs, &i) != DDI_SUCCESS) {
5657c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN,
5667c478bd9Sstevel@tonic-gate 			    "SBBC: couldn't get %s ranges property %d",
5677c478bd9Sstevel@tonic-gate 			    ddi_get_name(sbbcsoftp->dip), instance);
5687c478bd9Sstevel@tonic-gate 			return (DDI_ME_RNUMBER_RANGE);
5697c478bd9Sstevel@tonic-gate 		}
5707c478bd9Sstevel@tonic-gate 		n = i / sizeof (sbbc_child_regspec_t);
5717c478bd9Sstevel@tonic-gate 
5727c478bd9Sstevel@tonic-gate 		if (rnumber < 0 || rnumber >= n) {
5737c478bd9Sstevel@tonic-gate 			kmem_free(child_regs, i);
5747c478bd9Sstevel@tonic-gate 			return (DDI_ME_RNUMBER_RANGE);
5757c478bd9Sstevel@tonic-gate 		}
5767c478bd9Sstevel@tonic-gate 		child_rp = &child_regs[rnumber];
5777c478bd9Sstevel@tonic-gate 		break;
5787c478bd9Sstevel@tonic-gate 
5797c478bd9Sstevel@tonic-gate 	default:
5807c478bd9Sstevel@tonic-gate 		return (DDI_ME_INVAL);
5817c478bd9Sstevel@tonic-gate 
5827c478bd9Sstevel@tonic-gate 	}
5837c478bd9Sstevel@tonic-gate 
5847c478bd9Sstevel@tonic-gate 	/* Adjust our reg property with offset and length */
5857c478bd9Sstevel@tonic-gate 	child_rp->addr_low += off;
5867c478bd9Sstevel@tonic-gate 
5877c478bd9Sstevel@tonic-gate 	if (len)
5887c478bd9Sstevel@tonic-gate 		child_rp->size = len;
5897c478bd9Sstevel@tonic-gate 
5907c478bd9Sstevel@tonic-gate 	/*
5917c478bd9Sstevel@tonic-gate 	 * Combine this reg prop. into our parents PCI address using the ranges
5927c478bd9Sstevel@tonic-gate 	 * property.
5937c478bd9Sstevel@tonic-gate 	 */
5947c478bd9Sstevel@tonic-gate 	rval = sbbc_apply_range(sbbcsoftp, rdip, child_rp, &pci_reg);
5957c478bd9Sstevel@tonic-gate 
5967c478bd9Sstevel@tonic-gate 	if (mp->map_type == DDI_MT_RNUMBER)
5977c478bd9Sstevel@tonic-gate 		kmem_free(child_regs, i);
5987c478bd9Sstevel@tonic-gate 
5997c478bd9Sstevel@tonic-gate 	if (rval != DDI_SUCCESS)
6007c478bd9Sstevel@tonic-gate 		return (rval);
6017c478bd9Sstevel@tonic-gate 
6027c478bd9Sstevel@tonic-gate 	p_map_request = *mp;
6037c478bd9Sstevel@tonic-gate 	p_map_request.map_type = DDI_MT_REGSPEC;
6047c478bd9Sstevel@tonic-gate 	p_map_request.map_obj.rp = (struct regspec *)&pci_reg;
6057c478bd9Sstevel@tonic-gate 
6067c478bd9Sstevel@tonic-gate 	/* Send it to PCI nexus to map into the PCI space */
6077c478bd9Sstevel@tonic-gate 	rval = ddi_map(dip, &p_map_request, 0, 0, addrp);
6087c478bd9Sstevel@tonic-gate 
6097c478bd9Sstevel@tonic-gate 	return (rval);
6107c478bd9Sstevel@tonic-gate 
6117c478bd9Sstevel@tonic-gate }
6127c478bd9Sstevel@tonic-gate 
6137c478bd9Sstevel@tonic-gate 
6147c478bd9Sstevel@tonic-gate /* new intr_ops structure */
6157c478bd9Sstevel@tonic-gate static int
6167c478bd9Sstevel@tonic-gate sbbc_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
6177c478bd9Sstevel@tonic-gate     ddi_intr_handle_impl_t *hdlp, void *result)
6187c478bd9Sstevel@tonic-gate {
6197c478bd9Sstevel@tonic-gate 	int	ret = DDI_SUCCESS;
6207c478bd9Sstevel@tonic-gate 
6217c478bd9Sstevel@tonic-gate 	switch (intr_op) {
6227c478bd9Sstevel@tonic-gate 	case DDI_INTROP_GETCAP:
623*a195726fSgovinda 		*(int *)result = DDI_INTR_FLAG_LEVEL;
6247c478bd9Sstevel@tonic-gate 		break;
6257c478bd9Sstevel@tonic-gate 	case DDI_INTROP_ALLOC:
6267c478bd9Sstevel@tonic-gate 		*(int *)result = hdlp->ih_scratch1;
6277c478bd9Sstevel@tonic-gate 		break;
6287c478bd9Sstevel@tonic-gate 	case DDI_INTROP_FREE:
6297c478bd9Sstevel@tonic-gate 		break;
6307c478bd9Sstevel@tonic-gate 	case DDI_INTROP_GETPRI:
631*a195726fSgovinda 		if (hdlp->ih_pri == 0) {
632*a195726fSgovinda 			hdlp->ih_pri = 0x1;
6337c478bd9Sstevel@tonic-gate 
6347c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s%d assigning default interrupt "
635*a195726fSgovinda 			    "level %d for device %s%d", ddi_driver_name(dip),
636*a195726fSgovinda 			    ddi_get_instance(dip), hdlp->ih_pri,
637*a195726fSgovinda 			    ddi_driver_name(rdip), ddi_get_instance(rdip));
6387c478bd9Sstevel@tonic-gate 		}
6397c478bd9Sstevel@tonic-gate 
640*a195726fSgovinda 		*(int *)result = hdlp->ih_pri;
641*a195726fSgovinda 
6427c478bd9Sstevel@tonic-gate 		break;
6437c478bd9Sstevel@tonic-gate 	case DDI_INTROP_ADDISR:
6447c478bd9Sstevel@tonic-gate 		ret = sbbc_add_intr_impl(dip, rdip, intr_op, hdlp, result);
6457c478bd9Sstevel@tonic-gate 		break;
6467c478bd9Sstevel@tonic-gate 	case DDI_INTROP_REMISR:
6477c478bd9Sstevel@tonic-gate 		ret = sbbc_remove_intr_impl(dip, rdip, intr_op, hdlp, result);
6487c478bd9Sstevel@tonic-gate 		break;
6497c478bd9Sstevel@tonic-gate 	case DDI_INTROP_ENABLE:
6507c478bd9Sstevel@tonic-gate 		ret = sbbc_update_intr_state(dip, rdip, intr_op, hdlp, &result);
6517c478bd9Sstevel@tonic-gate 		break;
6527c478bd9Sstevel@tonic-gate 	case DDI_INTROP_DISABLE:
6537c478bd9Sstevel@tonic-gate 		ret = sbbc_update_intr_state(dip, rdip, intr_op, hdlp, &result);
6547c478bd9Sstevel@tonic-gate 		break;
6557c478bd9Sstevel@tonic-gate 	case DDI_INTROP_NINTRS:
6567c478bd9Sstevel@tonic-gate 	case DDI_INTROP_NAVAIL:
6577c478bd9Sstevel@tonic-gate 		*(int *)result = i_ddi_get_nintrs(rdip);
6587c478bd9Sstevel@tonic-gate 		break;
6597c478bd9Sstevel@tonic-gate 	case DDI_INTROP_SUPPORTED_TYPES:
6607c478bd9Sstevel@tonic-gate 		/* PCI nexus driver supports only fixed interrupts */
6617c478bd9Sstevel@tonic-gate 		*(int *)result = i_ddi_get_nintrs(rdip) ?
6627c478bd9Sstevel@tonic-gate 		    DDI_INTR_TYPE_FIXED : 0;
6637c478bd9Sstevel@tonic-gate 		break;
6647c478bd9Sstevel@tonic-gate 	default:
6657c478bd9Sstevel@tonic-gate 		ret = DDI_ENOTSUP;
6667c478bd9Sstevel@tonic-gate 		break;
6677c478bd9Sstevel@tonic-gate 	}
6687c478bd9Sstevel@tonic-gate 
6697c478bd9Sstevel@tonic-gate 	return (ret);
6707c478bd9Sstevel@tonic-gate }
6717c478bd9Sstevel@tonic-gate 
6727c478bd9Sstevel@tonic-gate 
6737c478bd9Sstevel@tonic-gate static int
6747c478bd9Sstevel@tonic-gate sbbc_add_intr_impl(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
6757c478bd9Sstevel@tonic-gate     ddi_intr_handle_impl_t *hdlp, void *result)
6767c478bd9Sstevel@tonic-gate {
6777c478bd9Sstevel@tonic-gate 	sbbcsoft_t *sbbcsoftp;
6787c478bd9Sstevel@tonic-gate 	sbbc_child_intr_t *childintr;
6797c478bd9Sstevel@tonic-gate 	int instance, i, rval = DDI_SUCCESS;
6807c478bd9Sstevel@tonic-gate 
6817c478bd9Sstevel@tonic-gate 	SBBC_DBG2(SBBC_DBG_INTR, dip,
6827c478bd9Sstevel@tonic-gate 	    "add: rdip 0x%llx hdlp 0x%llx\n", rdip, hdlp);
6837c478bd9Sstevel@tonic-gate 
6847c478bd9Sstevel@tonic-gate 	/* insert the sbbc isr wrapper instead */
6857c478bd9Sstevel@tonic-gate 	instance = ddi_get_instance(dip);
6867c478bd9Sstevel@tonic-gate 	if (!(sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance)))
6877c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
6887c478bd9Sstevel@tonic-gate 
6897c478bd9Sstevel@tonic-gate 	childintr = kmem_zalloc(sizeof (struct sbbc_child_intr), KM_SLEEP);
6907c478bd9Sstevel@tonic-gate 
6917c478bd9Sstevel@tonic-gate 	childintr->name = ddi_get_name(rdip);
6927c478bd9Sstevel@tonic-gate 	childintr->inum = hdlp->ih_inum;
6937c478bd9Sstevel@tonic-gate 	childintr->intr_handler = hdlp->ih_cb_func;
6947c478bd9Sstevel@tonic-gate 	childintr->arg1 = hdlp->ih_cb_arg1;
6957c478bd9Sstevel@tonic-gate 	childintr->arg2 = hdlp->ih_cb_arg2;
6967c478bd9Sstevel@tonic-gate 	childintr->status = SBBC_INTR_STATE_DISABLE;
6977c478bd9Sstevel@tonic-gate 
6987c478bd9Sstevel@tonic-gate 	for (i = 0; i < MAX_SBBC_DEVICES; i++) {
6997c478bd9Sstevel@tonic-gate 		if (sbbcsoftp->child_intr[i] == 0) {
7007c478bd9Sstevel@tonic-gate 			sbbcsoftp->child_intr[i] = childintr;
7017c478bd9Sstevel@tonic-gate 			break;
7027c478bd9Sstevel@tonic-gate 		}
7037c478bd9Sstevel@tonic-gate 	}
7047c478bd9Sstevel@tonic-gate 
7057c478bd9Sstevel@tonic-gate 	DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp,
7067c478bd9Sstevel@tonic-gate 	    (ddi_intr_handler_t *)sbbc_intr_wrapper,
7077c478bd9Sstevel@tonic-gate 	    (caddr_t)sbbcsoftp, NULL);
7087c478bd9Sstevel@tonic-gate 
7097c478bd9Sstevel@tonic-gate 	if ((rval = i_ddi_intr_ops(dip, rdip, intr_op,
7107c478bd9Sstevel@tonic-gate 	    hdlp, result)) != DDI_SUCCESS) {
7117c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "sbbc%d: failed to add intr for %s",
7127c478bd9Sstevel@tonic-gate 		    instance, ddi_get_name(rdip));
7137c478bd9Sstevel@tonic-gate 		kmem_free(childintr, sizeof (struct sbbc_child_intr));
7147c478bd9Sstevel@tonic-gate 		sbbcsoftp->child_intr[i] = NULL;
7157c478bd9Sstevel@tonic-gate 	}
7167c478bd9Sstevel@tonic-gate 
7177c478bd9Sstevel@tonic-gate 	/*
7187c478bd9Sstevel@tonic-gate 	 * Restore original interrupt handler
7197c478bd9Sstevel@tonic-gate 	 * and arguments in interrupt handle.
7207c478bd9Sstevel@tonic-gate 	 */
7217c478bd9Sstevel@tonic-gate 	DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, childintr->intr_handler,
7227c478bd9Sstevel@tonic-gate 	    childintr->arg1, childintr->arg2);
7237c478bd9Sstevel@tonic-gate 
7247c478bd9Sstevel@tonic-gate 	return (rval);
7257c478bd9Sstevel@tonic-gate }
7267c478bd9Sstevel@tonic-gate 
7277c478bd9Sstevel@tonic-gate static int
7287c478bd9Sstevel@tonic-gate sbbc_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
7297c478bd9Sstevel@tonic-gate     ddi_intr_handle_impl_t *hdlp, void *result)
7307c478bd9Sstevel@tonic-gate {
7317c478bd9Sstevel@tonic-gate 	sbbcsoft_t *sbbcsoftp;
7327c478bd9Sstevel@tonic-gate 	sbbc_child_intr_t *childintr;
7337c478bd9Sstevel@tonic-gate 	int instance, i, rval = DDI_SUCCESS;
7347c478bd9Sstevel@tonic-gate 
7357c478bd9Sstevel@tonic-gate 	SBBC_DBG2(SBBC_DBG_INTR, dip,
7367c478bd9Sstevel@tonic-gate 	    "remove: rdip 0x%llx hdlp 0x%llx\n", rdip, hdlp);
7377c478bd9Sstevel@tonic-gate 
7387c478bd9Sstevel@tonic-gate 	instance = ddi_get_instance(dip);
7397c478bd9Sstevel@tonic-gate 	if (!(sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance)))
7407c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
7417c478bd9Sstevel@tonic-gate 
7427c478bd9Sstevel@tonic-gate 	/* remove the sbbc isr wrapper instead */
7437c478bd9Sstevel@tonic-gate 	for (i = 0; i < MAX_SBBC_DEVICES; i++) {
7447c478bd9Sstevel@tonic-gate 		if (sbbcsoftp->child_intr[i]) {
7457c478bd9Sstevel@tonic-gate 			childintr = sbbcsoftp->child_intr[i];
7467c478bd9Sstevel@tonic-gate 			if (childintr->status == SBBC_INTR_STATE_DISABLE &&
7477c478bd9Sstevel@tonic-gate 			    childintr->name == ddi_get_name(rdip)) {
7487c478bd9Sstevel@tonic-gate 				/* put back child's inum */
7497c478bd9Sstevel@tonic-gate 				hdlp->ih_inum = childintr->inum;
7507c478bd9Sstevel@tonic-gate 				break;
7517c478bd9Sstevel@tonic-gate 			}
7527c478bd9Sstevel@tonic-gate 		}
7537c478bd9Sstevel@tonic-gate 	}
7547c478bd9Sstevel@tonic-gate 
7557c478bd9Sstevel@tonic-gate 	if (i >= MAX_SBBC_DEVICES) {
7567c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "sbbc%d:obound failed to remove intr for %s",
7577c478bd9Sstevel@tonic-gate 		    instance, ddi_get_name(rdip));
7587c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
7597c478bd9Sstevel@tonic-gate 	}
7607c478bd9Sstevel@tonic-gate 
7617c478bd9Sstevel@tonic-gate 	if ((rval = i_ddi_intr_ops(dip, rdip, intr_op,
7627c478bd9Sstevel@tonic-gate 	    hdlp, result)) != DDI_SUCCESS) {
7637c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "sbbc%d: failed to remove intr for %s",
7647c478bd9Sstevel@tonic-gate 		    instance, ddi_get_name(rdip));
7657c478bd9Sstevel@tonic-gate 		return (rval);
7667c478bd9Sstevel@tonic-gate 	}
7677c478bd9Sstevel@tonic-gate 
7687c478bd9Sstevel@tonic-gate 	kmem_free(childintr, sizeof (struct sbbc_child_intr));
7697c478bd9Sstevel@tonic-gate 	sbbcsoftp->child_intr[i] = NULL;
7707c478bd9Sstevel@tonic-gate 
7717c478bd9Sstevel@tonic-gate 	return (rval);
7727c478bd9Sstevel@tonic-gate }
7737c478bd9Sstevel@tonic-gate 
7747c478bd9Sstevel@tonic-gate 
7757c478bd9Sstevel@tonic-gate static int
7767c478bd9Sstevel@tonic-gate sbbc_update_intr_state(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
7777c478bd9Sstevel@tonic-gate     ddi_intr_handle_impl_t *hdlp, void *result)
7787c478bd9Sstevel@tonic-gate {
7797c478bd9Sstevel@tonic-gate 	sbbcsoft_t		*sbbcsoftp;
7807c478bd9Sstevel@tonic-gate 	sbbc_child_intr_t	*childintr;
7817c478bd9Sstevel@tonic-gate 	int			instance, i;
7827c478bd9Sstevel@tonic-gate 	int			ret = DDI_SUCCESS;
7837c478bd9Sstevel@tonic-gate 
7847c478bd9Sstevel@tonic-gate 	SBBC_DBG2(SBBC_DBG_INTR, dip, "sbbc_update_intr_state: "
7857c478bd9Sstevel@tonic-gate 	    "rdip 0x%llx hdlp 0x%llx state 0x%x\n", rdip, hdlp);
7867c478bd9Sstevel@tonic-gate 
7877c478bd9Sstevel@tonic-gate 	instance = ddi_get_instance(dip);
7887c478bd9Sstevel@tonic-gate 	if (!(sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance)))
7897c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
7907c478bd9Sstevel@tonic-gate 
7917c478bd9Sstevel@tonic-gate 	for (i = 0; i < MAX_SBBC_DEVICES; i++) {
7927c478bd9Sstevel@tonic-gate 		if (sbbcsoftp->child_intr[i]) {
7937c478bd9Sstevel@tonic-gate 			childintr = sbbcsoftp->child_intr[i];
7947c478bd9Sstevel@tonic-gate 			if (childintr->name == ddi_get_name(rdip))
7957c478bd9Sstevel@tonic-gate 				break;
7967c478bd9Sstevel@tonic-gate 		}
7977c478bd9Sstevel@tonic-gate 	}
7987c478bd9Sstevel@tonic-gate 
7997c478bd9Sstevel@tonic-gate 	if (i >= MAX_SBBC_DEVICES) {
8007c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "sbbc%d: failed to update intr state for %s",
8017c478bd9Sstevel@tonic-gate 		    instance, ddi_get_name(rdip));
8027c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
8037c478bd9Sstevel@tonic-gate 	}
8047c478bd9Sstevel@tonic-gate 
8057c478bd9Sstevel@tonic-gate 	if ((ret = i_ddi_intr_ops(dip, rdip, intr_op,
8067c478bd9Sstevel@tonic-gate 	    hdlp, result)) != DDI_SUCCESS) {
8077c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "sbbc%d: failed to update intr state for %s",
8087c478bd9Sstevel@tonic-gate 		    instance, ddi_get_name(rdip));
8097c478bd9Sstevel@tonic-gate 		return (ret);
8107c478bd9Sstevel@tonic-gate 	}
8117c478bd9Sstevel@tonic-gate 
8127c478bd9Sstevel@tonic-gate 	/* Update the interrupt state */
8137c478bd9Sstevel@tonic-gate 	childintr->status = (intr_op == DDI_INTROP_ENABLE) ?
8147c478bd9Sstevel@tonic-gate 	    SBBC_INTR_STATE_ENABLE : SBBC_INTR_STATE_DISABLE;
8157c478bd9Sstevel@tonic-gate 
8167c478bd9Sstevel@tonic-gate 	return (ret);
8177c478bd9Sstevel@tonic-gate }
8187c478bd9Sstevel@tonic-gate 
8197c478bd9Sstevel@tonic-gate 
8207c478bd9Sstevel@tonic-gate /*
8217c478bd9Sstevel@tonic-gate  * This entry point is called before a child's probe or attach is called.
8227c478bd9Sstevel@tonic-gate  * The arg pointer points to child's dev_info_t structure.
8237c478bd9Sstevel@tonic-gate  */
8247c478bd9Sstevel@tonic-gate static int
8257c478bd9Sstevel@tonic-gate sbbc_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op,
8267c478bd9Sstevel@tonic-gate 	    void *arg, void *result)
8277c478bd9Sstevel@tonic-gate {
8287c478bd9Sstevel@tonic-gate 	sbbc_child_regspec_t *child_rp;
8297c478bd9Sstevel@tonic-gate 	int i, n;
8307c478bd9Sstevel@tonic-gate 
8317c478bd9Sstevel@tonic-gate 	SBBC_DBG3(SBBC_DBG_CTLOPS, dip,
8327c478bd9Sstevel@tonic-gate 	    "Initializing %s, arg %x, op %x\n",
8337c478bd9Sstevel@tonic-gate 	    ddi_driver_name(rdip), arg, op);
8347c478bd9Sstevel@tonic-gate 
8357c478bd9Sstevel@tonic-gate 	SBBCTRACE(sbbc_ctlops, 'CTLO', arg);
8367c478bd9Sstevel@tonic-gate 
8377c478bd9Sstevel@tonic-gate 	switch (op) {
8387c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_INITCHILD: {
8397c478bd9Sstevel@tonic-gate 		return (sbbc_initchild(dip, rdip, (dev_info_t *)arg));
8407c478bd9Sstevel@tonic-gate 	}
8417c478bd9Sstevel@tonic-gate 
8427c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_UNINITCHILD: {
8437c478bd9Sstevel@tonic-gate 		return (sbbc_uninitchild(rdip, (dev_info_t *)arg));
8447c478bd9Sstevel@tonic-gate 	}
8457c478bd9Sstevel@tonic-gate 
8467c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_REPORTDEV:
8477c478bd9Sstevel@tonic-gate 
8487c478bd9Sstevel@tonic-gate 		cmn_err(CE_CONT, "?%s%d at %s%d: offset %s\n",
8497c478bd9Sstevel@tonic-gate 		    ddi_driver_name(rdip), ddi_get_instance(rdip),
8507c478bd9Sstevel@tonic-gate 		    ddi_driver_name(dip), ddi_get_instance(dip),
8517c478bd9Sstevel@tonic-gate 		    ddi_get_name_addr(rdip));
8527c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
8537c478bd9Sstevel@tonic-gate 
8547c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_REGSIZE:
8557c478bd9Sstevel@tonic-gate 
8567c478bd9Sstevel@tonic-gate 		if (getprop(rdip, "reg", &child_rp, &i) != DDI_SUCCESS) {
8577c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
8587c478bd9Sstevel@tonic-gate 		}
8597c478bd9Sstevel@tonic-gate 		n = i / sizeof (sbbc_child_regspec_t);
8607c478bd9Sstevel@tonic-gate 		if (*(int *)arg < 0 || *(int *)arg >= n) {
8617c478bd9Sstevel@tonic-gate 			kmem_free(child_rp, i);
8627c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
8637c478bd9Sstevel@tonic-gate 		}
8647c478bd9Sstevel@tonic-gate 		*((off_t *)result) = child_rp[*(int *)arg].size;
8657c478bd9Sstevel@tonic-gate 		kmem_free(child_rp, i);
8667c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
8677c478bd9Sstevel@tonic-gate 
8687c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_NREGS:
8697c478bd9Sstevel@tonic-gate 
8707c478bd9Sstevel@tonic-gate 		if (getprop(rdip, "reg", &child_rp, &i) != DDI_SUCCESS) {
8717c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
8727c478bd9Sstevel@tonic-gate 		}
8737c478bd9Sstevel@tonic-gate 		*((uint_t *)result) = i / sizeof (sbbc_child_regspec_t);
8747c478bd9Sstevel@tonic-gate 		kmem_free(child_rp, i);
8757c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
8767c478bd9Sstevel@tonic-gate 	}
8777c478bd9Sstevel@tonic-gate 
8787c478bd9Sstevel@tonic-gate 	/*
8797c478bd9Sstevel@tonic-gate 	 * Now pass the request up to our parent.
8807c478bd9Sstevel@tonic-gate 	 */
8817c478bd9Sstevel@tonic-gate 	SBBC_DBG0(SBBC_DBG_CTLOPS, dip, "Calling ddi_ctlops\n");
8827c478bd9Sstevel@tonic-gate 
8837c478bd9Sstevel@tonic-gate 	return (ddi_ctlops(dip, rdip, op, arg, result));
8847c478bd9Sstevel@tonic-gate }
8857c478bd9Sstevel@tonic-gate 
8867c478bd9Sstevel@tonic-gate 
8877c478bd9Sstevel@tonic-gate /*
8887c478bd9Sstevel@tonic-gate  * The following routine uses ranges property, that was read earlier, and
8897c478bd9Sstevel@tonic-gate  * takes child's reg property, and computes the complete address and size
8907c478bd9Sstevel@tonic-gate  * for the PCI parent to map.
8917c478bd9Sstevel@tonic-gate  */
8927c478bd9Sstevel@tonic-gate static int
8937c478bd9Sstevel@tonic-gate sbbc_apply_range(struct sbbcsoft *sbbc_p, dev_info_t *rdip,
8947c478bd9Sstevel@tonic-gate     sbbc_child_regspec_t *child_rp, pci_regspec_t *rp)
8957c478bd9Sstevel@tonic-gate {
8967c478bd9Sstevel@tonic-gate 	int b;
8977c478bd9Sstevel@tonic-gate 	int rval = DDI_SUCCESS;
8987c478bd9Sstevel@tonic-gate 	struct sbbc_pci_rangespec *rangep = sbbc_p->rangep;
8997c478bd9Sstevel@tonic-gate 	int nrange = sbbc_p->range_cnt;
9007c478bd9Sstevel@tonic-gate 
9017c478bd9Sstevel@tonic-gate 	SBBC_DBG4(SBBC_DBG_MAPRANGES, rdip,
9027c478bd9Sstevel@tonic-gate 	    "Applying ranges for %s, rangep %llx, child_rp %llx, range %x\n",
9037c478bd9Sstevel@tonic-gate 	    ddi_driver_name(rdip), sbbc_p->rangep, child_rp, nrange);
9047c478bd9Sstevel@tonic-gate 
9057c478bd9Sstevel@tonic-gate 	SBBCTRACE(sbbc_apply_range, 'APPL', sbbc_p);
9067c478bd9Sstevel@tonic-gate 
9077c478bd9Sstevel@tonic-gate 	for (b = 0; b < nrange; ++b, ++rangep) {
9087c478bd9Sstevel@tonic-gate 
9097c478bd9Sstevel@tonic-gate 		/* Make sure the correct range is being mapped */
9107c478bd9Sstevel@tonic-gate 		if (child_rp->addr_hi == rangep->sbbc_phys_hi)
9117c478bd9Sstevel@tonic-gate 			/* See if we fit in this range */
9127c478bd9Sstevel@tonic-gate 			if ((child_rp->addr_low >=
9137c478bd9Sstevel@tonic-gate 			    rangep->sbbc_phys_low) &&
9147c478bd9Sstevel@tonic-gate 			    ((child_rp->addr_low + child_rp->size - 1)
9157c478bd9Sstevel@tonic-gate 				<= (rangep->sbbc_phys_low +
9167c478bd9Sstevel@tonic-gate 				    rangep->rng_size - 1))) {
9177c478bd9Sstevel@tonic-gate 				uint_t addr_offset = child_rp->addr_low -
9187c478bd9Sstevel@tonic-gate 				    rangep->sbbc_phys_low;
9197c478bd9Sstevel@tonic-gate 				/*
9207c478bd9Sstevel@tonic-gate 				 * Use the range entry to translate
9217c478bd9Sstevel@tonic-gate 				 * the SBBC physical address into the
9227c478bd9Sstevel@tonic-gate 				 * parents PCI space.
9237c478bd9Sstevel@tonic-gate 				 */
9247c478bd9Sstevel@tonic-gate 				rp->pci_phys_hi =
9257c478bd9Sstevel@tonic-gate 				    rangep->pci_phys_hi;
9267c478bd9Sstevel@tonic-gate 				rp->pci_phys_mid = rangep->pci_phys_mid;
9277c478bd9Sstevel@tonic-gate 				rp->pci_phys_low =
9287c478bd9Sstevel@tonic-gate 				    rangep->pci_phys_low + addr_offset;
9297c478bd9Sstevel@tonic-gate 				rp->pci_size_hi = 0;
9307c478bd9Sstevel@tonic-gate 				rp->pci_size_low =
9317c478bd9Sstevel@tonic-gate 				    min(child_rp->size, (rangep->rng_size -
9327c478bd9Sstevel@tonic-gate 					addr_offset));
9337c478bd9Sstevel@tonic-gate 
9347c478bd9Sstevel@tonic-gate 				break;
9357c478bd9Sstevel@tonic-gate 			}
9367c478bd9Sstevel@tonic-gate 	}
9377c478bd9Sstevel@tonic-gate 
9387c478bd9Sstevel@tonic-gate 	if (b == nrange)  {
9397c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "out_of_range %s", ddi_get_name(rdip));
9407c478bd9Sstevel@tonic-gate 		return (DDI_ME_REGSPEC_RANGE);
9417c478bd9Sstevel@tonic-gate 	}
9427c478bd9Sstevel@tonic-gate 
9437c478bd9Sstevel@tonic-gate 	return (rval);
9447c478bd9Sstevel@tonic-gate }
9457c478bd9Sstevel@tonic-gate 
9467c478bd9Sstevel@tonic-gate 
9477c478bd9Sstevel@tonic-gate /*
9487c478bd9Sstevel@tonic-gate  * The following routine reads sbbc's ranges property from OBP and sets up
9497c478bd9Sstevel@tonic-gate  * its soft structure with it.
9507c478bd9Sstevel@tonic-gate  */
9517c478bd9Sstevel@tonic-gate static int
9527c478bd9Sstevel@tonic-gate sbbc_get_ranges(struct sbbcsoft *sbbcsoftp)
9537c478bd9Sstevel@tonic-gate {
9547c478bd9Sstevel@tonic-gate 	struct sbbc_pci_rangespec *rangep;
9557c478bd9Sstevel@tonic-gate 	int range_len, nrange;
9567c478bd9Sstevel@tonic-gate 
957a3282898Scth 	if (ddi_getlongprop(DDI_DEV_T_ANY, sbbcsoftp->dip, DDI_PROP_DONTPASS,
9587c478bd9Sstevel@tonic-gate 	    "ranges", (caddr_t)&rangep, &range_len) != DDI_SUCCESS) {
9597c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "SBBC: couldn't get %s ranges property %d",
9607c478bd9Sstevel@tonic-gate 		    ddi_get_name(sbbcsoftp->dip), sbbcsoftp->instance);
9617c478bd9Sstevel@tonic-gate 		return (DDI_ME_REGSPEC_RANGE);
9627c478bd9Sstevel@tonic-gate 	}
9637c478bd9Sstevel@tonic-gate 
9647c478bd9Sstevel@tonic-gate 	nrange = range_len / sizeof (struct sbbc_pci_rangespec);
9657c478bd9Sstevel@tonic-gate 
9667c478bd9Sstevel@tonic-gate 	if (!nrange) {
9677c478bd9Sstevel@tonic-gate 		kmem_free(rangep, range_len);
9687c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
9697c478bd9Sstevel@tonic-gate 	}
9707c478bd9Sstevel@tonic-gate 
9717c478bd9Sstevel@tonic-gate 	/* setup the soft structure with ranges info. */
9727c478bd9Sstevel@tonic-gate 	sbbcsoftp->rangep = rangep;
9737c478bd9Sstevel@tonic-gate 	sbbcsoftp->range_cnt = nrange;
9747c478bd9Sstevel@tonic-gate 	sbbcsoftp->range_len = range_len;
9757c478bd9Sstevel@tonic-gate 
9767c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
9777c478bd9Sstevel@tonic-gate }
9787c478bd9Sstevel@tonic-gate 
9797c478bd9Sstevel@tonic-gate 
9807c478bd9Sstevel@tonic-gate /*
9817c478bd9Sstevel@tonic-gate  * Configure the SBBC for PCI
9827c478bd9Sstevel@tonic-gate  */
9837c478bd9Sstevel@tonic-gate static int
9847c478bd9Sstevel@tonic-gate sbbc_config4pci(struct sbbcsoft *sbbcsoftp)
9857c478bd9Sstevel@tonic-gate {
9867c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t conf_handle;
9877c478bd9Sstevel@tonic-gate 	uint16_t comm, vendid, devid, stat;
9887c478bd9Sstevel@tonic-gate 	uint8_t revid;
9897c478bd9Sstevel@tonic-gate 
9907c478bd9Sstevel@tonic-gate #ifdef DEBUG
9917c478bd9Sstevel@tonic-gate 	if (sbbc_dbg_flags & SBBC_DBG_PCICONF) {
9927c478bd9Sstevel@tonic-gate 		cmn_err(CE_CONT,
9937c478bd9Sstevel@tonic-gate 		    "sbbc_config4pci: sbbcsoftp %p\n", (void *)sbbcsoftp);
9947c478bd9Sstevel@tonic-gate 	}
9957c478bd9Sstevel@tonic-gate #endif
9967c478bd9Sstevel@tonic-gate 	if (pci_config_setup(sbbcsoftp->dip, &conf_handle) != DDI_SUCCESS)
9977c478bd9Sstevel@tonic-gate 		return (1);
9987c478bd9Sstevel@tonic-gate 
9997c478bd9Sstevel@tonic-gate 	vendid = pci_config_get16(conf_handle, PCI_CONF_VENID);
10007c478bd9Sstevel@tonic-gate 	devid = pci_config_get16(conf_handle, PCI_CONF_DEVID);
10017c478bd9Sstevel@tonic-gate 	comm = pci_config_get16(conf_handle, PCI_CONF_COMM);
10027c478bd9Sstevel@tonic-gate 	stat = pci_config_get16(conf_handle, PCI_CONF_STAT);
10037c478bd9Sstevel@tonic-gate 	revid = pci_config_get8(conf_handle, PCI_CONF_REVID);
10047c478bd9Sstevel@tonic-gate 
10057c478bd9Sstevel@tonic-gate #ifdef DEBUG
10067c478bd9Sstevel@tonic-gate 	if (sbbc_dbg_flags & SBBC_DBG_PCICONF) {
10077c478bd9Sstevel@tonic-gate 		cmn_err(CE_CONT,
10087c478bd9Sstevel@tonic-gate 		    "SBBC vendid %x, devid %x, comm %x, stat %x, revid %x\n",
10097c478bd9Sstevel@tonic-gate 		    vendid, devid, comm, stat, revid);
10107c478bd9Sstevel@tonic-gate 	}
10117c478bd9Sstevel@tonic-gate #endif
10127c478bd9Sstevel@tonic-gate 	comm = (PCI_COMM_ME | PCI_COMM_MAE | PCI_COMM_SERR_ENABLE |
10137c478bd9Sstevel@tonic-gate 		    PCI_COMM_PARITY_DETECT);
10147c478bd9Sstevel@tonic-gate 
10157c478bd9Sstevel@tonic-gate 	pci_config_put16(conf_handle, PCI_CONF_COMM, comm);
10167c478bd9Sstevel@tonic-gate 
10177c478bd9Sstevel@tonic-gate 	comm = pci_config_get16(conf_handle, PCI_CONF_COMM);
10187c478bd9Sstevel@tonic-gate 
10197c478bd9Sstevel@tonic-gate #ifdef DEBUG
10207c478bd9Sstevel@tonic-gate 	if (sbbc_dbg_flags & SBBC_DBG_PCICONF) {
10217c478bd9Sstevel@tonic-gate 		cmn_err(CE_CONT, "comm %x\n", comm);
10227c478bd9Sstevel@tonic-gate 	}
10237c478bd9Sstevel@tonic-gate #endif
10247c478bd9Sstevel@tonic-gate 	pci_config_teardown(&conf_handle);
10257c478bd9Sstevel@tonic-gate 
10267c478bd9Sstevel@tonic-gate 	return (0);
10277c478bd9Sstevel@tonic-gate }
10287c478bd9Sstevel@tonic-gate 
10297c478bd9Sstevel@tonic-gate 
10307c478bd9Sstevel@tonic-gate /* ARGSUSED0 */
10317c478bd9Sstevel@tonic-gate int
10327c478bd9Sstevel@tonic-gate sbbc_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
10337c478bd9Sstevel@tonic-gate {
10347c478bd9Sstevel@tonic-gate 	dev_t	dev = (dev_t)arg;
10357c478bd9Sstevel@tonic-gate 	struct sbbcsoft *sbbcsoftp;
10367c478bd9Sstevel@tonic-gate 	int	instance, ret;
10377c478bd9Sstevel@tonic-gate 
10387c478bd9Sstevel@tonic-gate 	instance = getminor(dev);
10397c478bd9Sstevel@tonic-gate 
10407c478bd9Sstevel@tonic-gate 	SBBCTRACE(sbbc_getinfo, 'GINF', instance);
10417c478bd9Sstevel@tonic-gate 
10427c478bd9Sstevel@tonic-gate 	switch (infocmd) {
10437c478bd9Sstevel@tonic-gate 		case DDI_INFO_DEVT2DEVINFO:
10447c478bd9Sstevel@tonic-gate 			sbbcsoftp = (struct sbbcsoft *)
10457c478bd9Sstevel@tonic-gate 				ddi_get_soft_state(sbbcsoft_statep, instance);
10467c478bd9Sstevel@tonic-gate 			*result = sbbcsoftp->dip;
10477c478bd9Sstevel@tonic-gate 			ret = DDI_SUCCESS;
10487c478bd9Sstevel@tonic-gate 			break;
10497c478bd9Sstevel@tonic-gate 		case DDI_INFO_DEVT2INSTANCE:
10507c478bd9Sstevel@tonic-gate 			*result = (void *)instance;
10517c478bd9Sstevel@tonic-gate 			ret = DDI_SUCCESS;
10527c478bd9Sstevel@tonic-gate 			break;
10537c478bd9Sstevel@tonic-gate 		default:
10547c478bd9Sstevel@tonic-gate 			ret = DDI_FAILURE;
10557c478bd9Sstevel@tonic-gate 			break;
10567c478bd9Sstevel@tonic-gate 	}
10577c478bd9Sstevel@tonic-gate 
10587c478bd9Sstevel@tonic-gate 	return (ret);
10597c478bd9Sstevel@tonic-gate }
10607c478bd9Sstevel@tonic-gate 
10617c478bd9Sstevel@tonic-gate /*ARGSUSED1*/
10627c478bd9Sstevel@tonic-gate static int
10637c478bd9Sstevel@tonic-gate sbbc_open(dev_t *dev, int flag, int otype, cred_t *credp)
10647c478bd9Sstevel@tonic-gate {
10657c478bd9Sstevel@tonic-gate 	struct sbbcsoft *sbbcsoftp;
10667c478bd9Sstevel@tonic-gate 	int		instance;
10677c478bd9Sstevel@tonic-gate 
10687c478bd9Sstevel@tonic-gate 	/* check privilege of caller process */
10697c478bd9Sstevel@tonic-gate 	if (drv_priv(credp)) {
10707c478bd9Sstevel@tonic-gate 		return (EPERM);
10717c478bd9Sstevel@tonic-gate 	}
10727c478bd9Sstevel@tonic-gate 
10737c478bd9Sstevel@tonic-gate 	instance = getminor(*dev);
10747c478bd9Sstevel@tonic-gate 	if (instance < 0)
10757c478bd9Sstevel@tonic-gate 		return (ENXIO);
10767c478bd9Sstevel@tonic-gate 	sbbcsoftp = (struct sbbcsoft *)ddi_get_soft_state(sbbcsoft_statep,
10777c478bd9Sstevel@tonic-gate 							    instance);
10787c478bd9Sstevel@tonic-gate 	SBBCTRACE(sbbc_open, 'OPEN', sbbcsoftp);
10797c478bd9Sstevel@tonic-gate 
10807c478bd9Sstevel@tonic-gate 	if (sbbcsoftp == NULL)
10817c478bd9Sstevel@tonic-gate 		return (ENXIO);
10827c478bd9Sstevel@tonic-gate 
10837c478bd9Sstevel@tonic-gate 	mutex_enter(&sbbcsoftp->umutex);
10847c478bd9Sstevel@tonic-gate 
10857c478bd9Sstevel@tonic-gate 	/* check for exclusive access */
10867c478bd9Sstevel@tonic-gate 	if ((sbbcsoftp->oflag == TRUE)) {
10877c478bd9Sstevel@tonic-gate 		mutex_exit(&sbbcsoftp->umutex);
10887c478bd9Sstevel@tonic-gate 		return (EBUSY);
10897c478bd9Sstevel@tonic-gate 	}
10907c478bd9Sstevel@tonic-gate 	sbbcsoftp->oflag = TRUE;
10917c478bd9Sstevel@tonic-gate 
10927c478bd9Sstevel@tonic-gate 	mutex_exit(&sbbcsoftp->umutex);
10937c478bd9Sstevel@tonic-gate 
10947c478bd9Sstevel@tonic-gate 	return (0);
10957c478bd9Sstevel@tonic-gate }
10967c478bd9Sstevel@tonic-gate 
10977c478bd9Sstevel@tonic-gate /*ARGSUSED1*/
10987c478bd9Sstevel@tonic-gate static int
10997c478bd9Sstevel@tonic-gate sbbc_close(dev_t dev, int flag, int otype, cred_t *credp)
11007c478bd9Sstevel@tonic-gate {
11017c478bd9Sstevel@tonic-gate 	struct sbbcsoft *sbbcsoftp;
11027c478bd9Sstevel@tonic-gate 	int		instance;
11037c478bd9Sstevel@tonic-gate 
11047c478bd9Sstevel@tonic-gate 	instance = getminor(dev);
11057c478bd9Sstevel@tonic-gate 	if (instance < 0)
11067c478bd9Sstevel@tonic-gate 		return (ENXIO);
11077c478bd9Sstevel@tonic-gate 	sbbcsoftp = (struct sbbcsoft *)ddi_get_soft_state(sbbcsoft_statep,
11087c478bd9Sstevel@tonic-gate 							    instance);
11097c478bd9Sstevel@tonic-gate 	/* wait till all output activity has ceased */
11107c478bd9Sstevel@tonic-gate 
11117c478bd9Sstevel@tonic-gate 	mutex_enter(&sbbcsoftp->umutex);
11127c478bd9Sstevel@tonic-gate 
11137c478bd9Sstevel@tonic-gate 	SBBCTRACE(sbbc_close, 'CLOS', sbbcsoftp);
11147c478bd9Sstevel@tonic-gate 
11157c478bd9Sstevel@tonic-gate 	sbbcsoftp->oflag = FALSE;
11167c478bd9Sstevel@tonic-gate 
11177c478bd9Sstevel@tonic-gate 	mutex_exit(&sbbcsoftp->umutex);
11187c478bd9Sstevel@tonic-gate 
11197c478bd9Sstevel@tonic-gate 	return (0);
11207c478bd9Sstevel@tonic-gate }
11217c478bd9Sstevel@tonic-gate 
11227c478bd9Sstevel@tonic-gate /*ARGSUSED2*/
11237c478bd9Sstevel@tonic-gate static int
11247c478bd9Sstevel@tonic-gate sbbc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
11257c478bd9Sstevel@tonic-gate 		int *rvalp)
11267c478bd9Sstevel@tonic-gate {
11277c478bd9Sstevel@tonic-gate 	struct sbbcsoft *sbbcsoftp;
11287c478bd9Sstevel@tonic-gate 
11297c478bd9Sstevel@tonic-gate 	SBBCTRACE(sbbc_ioctl, 'IOCT', arg);
11307c478bd9Sstevel@tonic-gate 
11317c478bd9Sstevel@tonic-gate 	sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, getminor(dev));
11327c478bd9Sstevel@tonic-gate 
11337c478bd9Sstevel@tonic-gate 	if (sbbcsoftp == NULL) {
11347c478bd9Sstevel@tonic-gate 		return (ENXIO);
11357c478bd9Sstevel@tonic-gate 	}
11367c478bd9Sstevel@tonic-gate 
11377c478bd9Sstevel@tonic-gate 	switch (cmd) {
11387c478bd9Sstevel@tonic-gate 	case SBBC_SBBCREG_WR:
11397c478bd9Sstevel@tonic-gate 		{
11407c478bd9Sstevel@tonic-gate 		struct ssc_sbbc_regio sbbcregs;
11417c478bd9Sstevel@tonic-gate 		uint64_t offset;
11427c478bd9Sstevel@tonic-gate 
11437c478bd9Sstevel@tonic-gate 		if (arg == NULL) {
11447c478bd9Sstevel@tonic-gate 			return (ENXIO);
11457c478bd9Sstevel@tonic-gate 		}
11467c478bd9Sstevel@tonic-gate 
11477c478bd9Sstevel@tonic-gate 		if (ddi_copyin((caddr_t)arg, (caddr_t)&sbbcregs,
11487c478bd9Sstevel@tonic-gate 				    sizeof (struct ssc_sbbc_regio), mode)) {
11497c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "sbbc_ioctl: copyin failed arg %p",
11507c478bd9Sstevel@tonic-gate 			    (void *)arg);
11517c478bd9Sstevel@tonic-gate 			return (EFAULT);
11527c478bd9Sstevel@tonic-gate 		}
11537c478bd9Sstevel@tonic-gate 
11547c478bd9Sstevel@tonic-gate 		/*
11557c478bd9Sstevel@tonic-gate 		 * Bug #4287186: SBBC driver on cp1500 doesn't check length for
11567c478bd9Sstevel@tonic-gate 		 *		reads or writes
11577c478bd9Sstevel@tonic-gate 		 * Note that I've also added a check to make sure the offset is
11587c478bd9Sstevel@tonic-gate 		 * valid, since misaligned (i.e. not on 16-byte boundary)
11597c478bd9Sstevel@tonic-gate 		 * accesses or accesses to "Reserved" register offsets are
11607c478bd9Sstevel@tonic-gate 		 * treated as unmapped by the SBBC.
11617c478bd9Sstevel@tonic-gate 		 */
11627c478bd9Sstevel@tonic-gate 		if ((sbbcregs.len != 4) ||
11637c478bd9Sstevel@tonic-gate 		    !sbbc_offset_valid(sbbcregs.offset)) {
11647c478bd9Sstevel@tonic-gate 			return (EINVAL);
11657c478bd9Sstevel@tonic-gate 		}
11667c478bd9Sstevel@tonic-gate 
11677c478bd9Sstevel@tonic-gate 		offset = (uint64_t)&sbbcsoftp->pci_sbbc_map->sbbc_internal_regs;
11687c478bd9Sstevel@tonic-gate 		offset += sbbcregs.offset;
11697c478bd9Sstevel@tonic-gate 		ddi_put32(sbbcsoftp->pci_sbbc_map_handle, (uint32_t *)offset,
11707c478bd9Sstevel@tonic-gate 			    sbbcregs.value);
11717c478bd9Sstevel@tonic-gate 		}
11727c478bd9Sstevel@tonic-gate 		break;
11737c478bd9Sstevel@tonic-gate 	case SBBC_SBBCREG_RD:
11747c478bd9Sstevel@tonic-gate 		{
11757c478bd9Sstevel@tonic-gate 		struct ssc_sbbc_regio sbbcregs;
11767c478bd9Sstevel@tonic-gate 		uint64_t offset;
11777c478bd9Sstevel@tonic-gate 
11787c478bd9Sstevel@tonic-gate 		if (arg == NULL) {
11797c478bd9Sstevel@tonic-gate 			return (ENXIO);
11807c478bd9Sstevel@tonic-gate 		}
11817c478bd9Sstevel@tonic-gate 
11827c478bd9Sstevel@tonic-gate 		if (ddi_copyin((caddr_t)arg, (caddr_t)&sbbcregs,
11837c478bd9Sstevel@tonic-gate 				    sizeof (struct ssc_sbbc_regio), mode)) {
11847c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "sbbc_ioctl: copyin failed arg %p",
11857c478bd9Sstevel@tonic-gate 			    (void *)arg);
11867c478bd9Sstevel@tonic-gate 			return (EFAULT);
11877c478bd9Sstevel@tonic-gate 		}
11887c478bd9Sstevel@tonic-gate 
11897c478bd9Sstevel@tonic-gate 		/*
11907c478bd9Sstevel@tonic-gate 		 * Bug #4287186: SBBC driver on cp1500 doesn't check length for
11917c478bd9Sstevel@tonic-gate 		 *		reads or writes
11927c478bd9Sstevel@tonic-gate 		 * Note that I've also added a check to make sure the offset is
11937c478bd9Sstevel@tonic-gate 		 * valid, since misaligned (i.e. not on 16-byte boundary)
11947c478bd9Sstevel@tonic-gate 		 * accesses or accesses to "Reserved" register offsets are
11957c478bd9Sstevel@tonic-gate 		 * treated as unmapped by the SBBC.
11967c478bd9Sstevel@tonic-gate 		 */
11977c478bd9Sstevel@tonic-gate 		if ((sbbcregs.len != 4) ||
11987c478bd9Sstevel@tonic-gate 		    !sbbc_offset_valid(sbbcregs.offset)) {
11997c478bd9Sstevel@tonic-gate 			return (EINVAL);
12007c478bd9Sstevel@tonic-gate 		}
12017c478bd9Sstevel@tonic-gate 
12027c478bd9Sstevel@tonic-gate 		offset = (uint64_t)&sbbcsoftp->pci_sbbc_map->sbbc_internal_regs;
12037c478bd9Sstevel@tonic-gate 		offset += sbbcregs.offset;
12047c478bd9Sstevel@tonic-gate 
12057c478bd9Sstevel@tonic-gate 		sbbcregs.value = ddi_get32(sbbcsoftp->pci_sbbc_map_handle,
12067c478bd9Sstevel@tonic-gate 						    (uint32_t *)offset);
12077c478bd9Sstevel@tonic-gate 
12087c478bd9Sstevel@tonic-gate 		if (ddi_copyout((caddr_t)&sbbcregs.value,
12097c478bd9Sstevel@tonic-gate 	    &((struct ssc_sbbc_regio *)arg)->value, sbbcregs.len, mode)) {
12107c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "sbbc_ioctl:copyout failed arg %p",
12117c478bd9Sstevel@tonic-gate 			    (void *)arg);
12127c478bd9Sstevel@tonic-gate 			return (EFAULT);
12137c478bd9Sstevel@tonic-gate 		}
12147c478bd9Sstevel@tonic-gate 		}
12157c478bd9Sstevel@tonic-gate 		break;
12167c478bd9Sstevel@tonic-gate 	default:
12177c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "sbbc_ioctl:Illegal command 0x%08x", cmd);
12187c478bd9Sstevel@tonic-gate 		return (ENOTTY);
12197c478bd9Sstevel@tonic-gate 	}
12207c478bd9Sstevel@tonic-gate 
12217c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
12227c478bd9Sstevel@tonic-gate }
12237c478bd9Sstevel@tonic-gate 
12247c478bd9Sstevel@tonic-gate static void
12257c478bd9Sstevel@tonic-gate sbbc_remove_reg_maps(struct sbbcsoft *sbbcsoftp)
12267c478bd9Sstevel@tonic-gate {
12277c478bd9Sstevel@tonic-gate 	SBBCTRACE(sbbc_remove_reg_maps, 'RMAP', sbbcsoftp);
12287c478bd9Sstevel@tonic-gate 	if (sbbcsoftp->pci_sbbc_map_handle)
12297c478bd9Sstevel@tonic-gate 		ddi_regs_map_free(&sbbcsoftp->pci_sbbc_map_handle);
12307c478bd9Sstevel@tonic-gate 
12317c478bd9Sstevel@tonic-gate 	/* need to unmap other registers as well */
12327c478bd9Sstevel@tonic-gate }
12337c478bd9Sstevel@tonic-gate 
12347c478bd9Sstevel@tonic-gate 
12357c478bd9Sstevel@tonic-gate static int
12367c478bd9Sstevel@tonic-gate sbbc_init(struct sbbcsoft *sbbcsoftp)
12377c478bd9Sstevel@tonic-gate {
12387c478bd9Sstevel@tonic-gate 
12397c478bd9Sstevel@tonic-gate 	/*
12407c478bd9Sstevel@tonic-gate 	 * setup the regs. and mask all the interrupts
12417c478bd9Sstevel@tonic-gate 	 * till we are ready.
12427c478bd9Sstevel@tonic-gate 	 */
12437c478bd9Sstevel@tonic-gate 	ddi_put32(sbbcsoftp->pci_sbbc_map_handle,
12447c478bd9Sstevel@tonic-gate 	    &sbbcsoftp->pci_sbbc_map->sbbc_internal_regs.sys_intr_enable,
12457c478bd9Sstevel@tonic-gate 	    0x00000000);
12467c478bd9Sstevel@tonic-gate 
12477c478bd9Sstevel@tonic-gate 	return (1);
12487c478bd9Sstevel@tonic-gate }
12497c478bd9Sstevel@tonic-gate 
12507c478bd9Sstevel@tonic-gate /*
12517c478bd9Sstevel@tonic-gate  * The following routine is a generic routine to initialize any child of
12527c478bd9Sstevel@tonic-gate  * sbbc nexus driver information into parent private data structure.
12537c478bd9Sstevel@tonic-gate  */
12547c478bd9Sstevel@tonic-gate /* ARGSUSED0 */
12557c478bd9Sstevel@tonic-gate static int
12567c478bd9Sstevel@tonic-gate sbbc_initchild(dev_info_t *dip, dev_info_t *rdip, dev_info_t *child)
12577c478bd9Sstevel@tonic-gate {
12587c478bd9Sstevel@tonic-gate 	sbbc_child_regspec_t *child_rp;
12597c478bd9Sstevel@tonic-gate 	int reglen, slot;
12607c478bd9Sstevel@tonic-gate 	char name[10];
12617c478bd9Sstevel@tonic-gate 
12627c478bd9Sstevel@tonic-gate 	SBBC_DBG1(SBBC_DBG_INITCHILD, dip, "Initializing %s\n",
12637c478bd9Sstevel@tonic-gate 	    ddi_driver_name(rdip));
12647c478bd9Sstevel@tonic-gate 
12657c478bd9Sstevel@tonic-gate 	/*
12667c478bd9Sstevel@tonic-gate 	 * Initialize a child
12677c478bd9Sstevel@tonic-gate 	 * Set the address portion of the node name based on the
12687c478bd9Sstevel@tonic-gate 	 * address/offset.
12697c478bd9Sstevel@tonic-gate 	 */
1270a3282898Scth 	if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
12717c478bd9Sstevel@tonic-gate 	    "reg", (caddr_t)&child_rp, &reglen) != DDI_SUCCESS) {
12727c478bd9Sstevel@tonic-gate 		if (strcmp(ddi_node_name(child), "hotplug-controller") == 0) {
12737c478bd9Sstevel@tonic-gate 			slot = 1;
12747c478bd9Sstevel@tonic-gate 			(void) sprintf(name, "%x", slot);
12757c478bd9Sstevel@tonic-gate 			ddi_set_name_addr(child, name);
12767c478bd9Sstevel@tonic-gate 			return (DDI_SUCCESS);
12777c478bd9Sstevel@tonic-gate 		}
12787c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
12797c478bd9Sstevel@tonic-gate 	}
12807c478bd9Sstevel@tonic-gate 
12817c478bd9Sstevel@tonic-gate 	SBBC_DBG3(SBBC_DBG_INITCHILD, dip, "hi 0x%x, low 0x%x, size 0x%x\n",
12827c478bd9Sstevel@tonic-gate 	    child_rp->addr_hi, child_rp->addr_low, child_rp->size);
12837c478bd9Sstevel@tonic-gate 
12847c478bd9Sstevel@tonic-gate 	(void) sprintf(name, "%x,%x", child_rp->addr_hi, child_rp->addr_low);
12857c478bd9Sstevel@tonic-gate 
12867c478bd9Sstevel@tonic-gate 	/*
12877c478bd9Sstevel@tonic-gate 	 * set child's addresses from the reg property into parent private
12887c478bd9Sstevel@tonic-gate 	 * data structure.
12897c478bd9Sstevel@tonic-gate 	 */
12907c478bd9Sstevel@tonic-gate 	ddi_set_name_addr(child, name);
12917c478bd9Sstevel@tonic-gate 	kmem_free(child_rp, reglen);
12927c478bd9Sstevel@tonic-gate 
12937c478bd9Sstevel@tonic-gate 	ddi_set_parent_data(child, NULL);
12947c478bd9Sstevel@tonic-gate 
12957c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
12967c478bd9Sstevel@tonic-gate }
12977c478bd9Sstevel@tonic-gate 
12987c478bd9Sstevel@tonic-gate 
12997c478bd9Sstevel@tonic-gate /* ARGSUSED0 */
13007c478bd9Sstevel@tonic-gate static int
13017c478bd9Sstevel@tonic-gate sbbc_uninitchild(dev_info_t *rdip, dev_info_t *child)
13027c478bd9Sstevel@tonic-gate {
13037c478bd9Sstevel@tonic-gate 
13047c478bd9Sstevel@tonic-gate 	SBBC_DBG1(SBBC_DBG_UNINITCHILD, rdip, "Uninitializing %s\n",
13057c478bd9Sstevel@tonic-gate 	    ddi_driver_name(rdip));
13067c478bd9Sstevel@tonic-gate 
13077c478bd9Sstevel@tonic-gate 	ddi_set_name_addr(child, NULL);
13087c478bd9Sstevel@tonic-gate 	ddi_remove_minor_node(child, NULL);
13097c478bd9Sstevel@tonic-gate 	impl_rem_dev_props(child);
13107c478bd9Sstevel@tonic-gate 
13117c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
13127c478bd9Sstevel@tonic-gate 
13137c478bd9Sstevel@tonic-gate }
13147c478bd9Sstevel@tonic-gate 
13157c478bd9Sstevel@tonic-gate 
13167c478bd9Sstevel@tonic-gate /*
13177c478bd9Sstevel@tonic-gate  * The following routine is an interrupt service routine that is used
13187c478bd9Sstevel@tonic-gate  * as a wrapper to all the children requiring interrupt services.
13197c478bd9Sstevel@tonic-gate  */
13207c478bd9Sstevel@tonic-gate static uint_t
13217c478bd9Sstevel@tonic-gate sbbc_intr_wrapper(caddr_t arg)
13227c478bd9Sstevel@tonic-gate {
13237c478bd9Sstevel@tonic-gate 
13247c478bd9Sstevel@tonic-gate 	struct sbbcsoft *sbbcsoftp = (struct sbbcsoft *)arg;
13257c478bd9Sstevel@tonic-gate 	int i, rval;
13267c478bd9Sstevel@tonic-gate 
13277c478bd9Sstevel@tonic-gate 	SBBC_DBG1(SBBC_DBG_INTR, sbbcsoftp->dip, "Isr arg 0x%llx\n", arg);
13287c478bd9Sstevel@tonic-gate 
13297c478bd9Sstevel@tonic-gate 	mutex_enter(&sbbcsoftp->sbbc_intr_mutex);
13307c478bd9Sstevel@tonic-gate 
13317c478bd9Sstevel@tonic-gate 	for (i = 0; i < MAX_SBBC_DEVICES; i++) {
13327c478bd9Sstevel@tonic-gate 		/*
13337c478bd9Sstevel@tonic-gate 		 * Check the interrupt status reg. to determine the cause.
13347c478bd9Sstevel@tonic-gate 		 */
13357c478bd9Sstevel@tonic-gate 		/*
13367c478bd9Sstevel@tonic-gate 		 * Check the error status reg. to determine the cause.
13377c478bd9Sstevel@tonic-gate 		 */
13387c478bd9Sstevel@tonic-gate 		if (sbbcsoftp->child_intr[i] &&
13397c478bd9Sstevel@tonic-gate 		    sbbcsoftp->child_intr[i]->status ==
13407c478bd9Sstevel@tonic-gate 		    SBBC_INTR_STATE_ENABLE) {
13417c478bd9Sstevel@tonic-gate 			/*
13427c478bd9Sstevel@tonic-gate 			 * Dispatch the children interrupt service routines and
13437c478bd9Sstevel@tonic-gate 			 * look for someone to claim.
13447c478bd9Sstevel@tonic-gate 			 */
13457c478bd9Sstevel@tonic-gate 			rval = sbbcsoftp->child_intr[i]->intr_handler(
13467c478bd9Sstevel@tonic-gate 			    sbbcsoftp->child_intr[i]->arg1,
13477c478bd9Sstevel@tonic-gate 			    sbbcsoftp->child_intr[i]->arg2);
13487c478bd9Sstevel@tonic-gate 
13497c478bd9Sstevel@tonic-gate 			if (rval == DDI_INTR_CLAIMED) {
13507c478bd9Sstevel@tonic-gate 				mutex_exit(&sbbcsoftp->sbbc_intr_mutex);
13517c478bd9Sstevel@tonic-gate 				return (rval);
13527c478bd9Sstevel@tonic-gate 			}
13537c478bd9Sstevel@tonic-gate 		}
13547c478bd9Sstevel@tonic-gate 	}
13557c478bd9Sstevel@tonic-gate 
13567c478bd9Sstevel@tonic-gate 	mutex_exit(&sbbcsoftp->sbbc_intr_mutex);
13577c478bd9Sstevel@tonic-gate 
13587c478bd9Sstevel@tonic-gate 	/* for now do not claim since we know its not enabled */
13597c478bd9Sstevel@tonic-gate 	return (DDI_INTR_UNCLAIMED);
13607c478bd9Sstevel@tonic-gate }
13617c478bd9Sstevel@tonic-gate 
13627c478bd9Sstevel@tonic-gate 
13637c478bd9Sstevel@tonic-gate /*
13647c478bd9Sstevel@tonic-gate  * This function checks an SBBC register offset to make sure that it is properly
13657c478bd9Sstevel@tonic-gate  * aligned (i.e. on a 16-byte boundary) and that it corresponds to an accessible
13667c478bd9Sstevel@tonic-gate  * register.  Since the SBBC treates accesses to unaligned or reserved addresses
13677c478bd9Sstevel@tonic-gate  * as unmapped, failing to check for these would leave a loophole that could be
13687c478bd9Sstevel@tonic-gate  * used to crash the system.
13697c478bd9Sstevel@tonic-gate  */
13707c478bd9Sstevel@tonic-gate static int
13717c478bd9Sstevel@tonic-gate sbbc_offset_valid(uint32_t offset) {
13727c478bd9Sstevel@tonic-gate 	/*
13737c478bd9Sstevel@tonic-gate 	 * Check for proper alignment first.
13747c478bd9Sstevel@tonic-gate 	 */
13757c478bd9Sstevel@tonic-gate 	if ((offset % 16) != 0) {
13767c478bd9Sstevel@tonic-gate 		return (0);
13777c478bd9Sstevel@tonic-gate 	}
13787c478bd9Sstevel@tonic-gate 
13797c478bd9Sstevel@tonic-gate 	/*
13807c478bd9Sstevel@tonic-gate 	 * Now start checking for the various reserved ranges.
13817c478bd9Sstevel@tonic-gate 	 * While sticking a bunch of constants in the code (rather than
13827c478bd9Sstevel@tonic-gate 	 * #define'd values) is usually best avoided, it would probably
13837c478bd9Sstevel@tonic-gate 	 * do more harm than good here.  These values were taken from the
13847c478bd9Sstevel@tonic-gate 	 * Serengeti Architecture Programmer's Reference Manual dated
13857c478bd9Sstevel@tonic-gate 	 * August 10, 1999, pages 2-99 through 2-103.  While there are
13867c478bd9Sstevel@tonic-gate 	 * various "clever" ways this check could be performed that would
13877c478bd9Sstevel@tonic-gate 	 * be slightly more efficient, arranging the code in this fashion
13887c478bd9Sstevel@tonic-gate 	 * should maximize maintainability.
13897c478bd9Sstevel@tonic-gate 	 */
13907c478bd9Sstevel@tonic-gate 	if (((offset >= 0x001a0) && (offset <= 0x001ff)) ||
13917c478bd9Sstevel@tonic-gate 	    ((offset >= 0x002a0) && (offset <= 0x002ff)) ||
13927c478bd9Sstevel@tonic-gate 	    ((offset >= 0x00350) && (offset <= 0x003ff)) ||
13937c478bd9Sstevel@tonic-gate 	    ((offset >= 0x00500) && (offset <= 0x00fff)) ||
13947c478bd9Sstevel@tonic-gate 	    ((offset >= 0x01160) && (offset <= 0x011ff)) ||
13957c478bd9Sstevel@tonic-gate 	    ((offset >= 0x01210) && (offset <= 0x017ff)) ||
13967c478bd9Sstevel@tonic-gate 	    ((offset >= 0x01810) && (offset <= 0x01fff)) ||
13977c478bd9Sstevel@tonic-gate 	    ((offset >= 0x02030) && (offset <= 0x022ff)) ||
13987c478bd9Sstevel@tonic-gate 	    ((offset >= 0x02340) && (offset <= 0x03fff)) ||
13997c478bd9Sstevel@tonic-gate 	    ((offset >= 0x04030) && (offset <= 0x05fff)) ||
14007c478bd9Sstevel@tonic-gate 	    ((offset >= 0x060a0) && (offset <= 0x060ff)) ||
14017c478bd9Sstevel@tonic-gate 	    (offset == 0x06120) ||
14027c478bd9Sstevel@tonic-gate 	    ((offset >= 0x06190) && (offset <= 0x061ff)) ||
14037c478bd9Sstevel@tonic-gate 	    ((offset >= 0x06230) && (offset <= 0x062f0)) ||
14047c478bd9Sstevel@tonic-gate 	    (offset > 0x06320)) {
14057c478bd9Sstevel@tonic-gate 		return (0);
14067c478bd9Sstevel@tonic-gate 	}
14077c478bd9Sstevel@tonic-gate 
14087c478bd9Sstevel@tonic-gate 	return (1);
14097c478bd9Sstevel@tonic-gate }
14107c478bd9Sstevel@tonic-gate 
14117c478bd9Sstevel@tonic-gate #ifdef DEBUG
14127c478bd9Sstevel@tonic-gate void
14137c478bd9Sstevel@tonic-gate sbbc_dbg(uint32_t flag, dev_info_t *dip, char *fmt,
14147c478bd9Sstevel@tonic-gate 	uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5)
14157c478bd9Sstevel@tonic-gate {
14167c478bd9Sstevel@tonic-gate 	char *s = NULL;
14177c478bd9Sstevel@tonic-gate 
14187c478bd9Sstevel@tonic-gate 	if (sbbc_dbg_flags && ((sbbc_dbg_flags & flag) == flag)) {
14197c478bd9Sstevel@tonic-gate 		switch (flag) {
14207c478bd9Sstevel@tonic-gate 		case SBBC_DBG_ATTACH:
14217c478bd9Sstevel@tonic-gate 			s = "attach";
14227c478bd9Sstevel@tonic-gate 			break;
14237c478bd9Sstevel@tonic-gate 		case SBBC_DBG_DETACH:
14247c478bd9Sstevel@tonic-gate 			s = "detach";
14257c478bd9Sstevel@tonic-gate 			break;
14267c478bd9Sstevel@tonic-gate 		case SBBC_DBG_CTLOPS:
14277c478bd9Sstevel@tonic-gate 			s = "ctlops";
14287c478bd9Sstevel@tonic-gate 			break;
14297c478bd9Sstevel@tonic-gate 		case SBBC_DBG_INITCHILD:
14307c478bd9Sstevel@tonic-gate 			s = "initchild";
14317c478bd9Sstevel@tonic-gate 			break;
14327c478bd9Sstevel@tonic-gate 		case SBBC_DBG_UNINITCHILD:
14337c478bd9Sstevel@tonic-gate 			s = "uninitchild";
14347c478bd9Sstevel@tonic-gate 			break;
14357c478bd9Sstevel@tonic-gate 		case SBBC_DBG_BUSMAP:
14367c478bd9Sstevel@tonic-gate 			s = "busmap";
14377c478bd9Sstevel@tonic-gate 			break;
14387c478bd9Sstevel@tonic-gate 		case SBBC_DBG_INTR:
14397c478bd9Sstevel@tonic-gate 			s = "intr";
14407c478bd9Sstevel@tonic-gate 			break;
14417c478bd9Sstevel@tonic-gate 		case SBBC_DBG_INTROPS:
14427c478bd9Sstevel@tonic-gate 			s = "intr_ops";
14437c478bd9Sstevel@tonic-gate 			break;
14447c478bd9Sstevel@tonic-gate 		case SBBC_DBG_PCICONF:
14457c478bd9Sstevel@tonic-gate 			s = "pciconfig";
14467c478bd9Sstevel@tonic-gate 			break;
14477c478bd9Sstevel@tonic-gate 		case SBBC_DBG_MAPRANGES:
14487c478bd9Sstevel@tonic-gate 			s = "mapranges";
14497c478bd9Sstevel@tonic-gate 			break;
14507c478bd9Sstevel@tonic-gate 		case SBBC_DBG_PROPERTIES:
14517c478bd9Sstevel@tonic-gate 			s = "properties";
14527c478bd9Sstevel@tonic-gate 			break;
14537c478bd9Sstevel@tonic-gate 		case SBBC_DBG_OPEN:
14547c478bd9Sstevel@tonic-gate 			s = "open";
14557c478bd9Sstevel@tonic-gate 			break;
14567c478bd9Sstevel@tonic-gate 		case SBBC_DBG_CLOSE:
14577c478bd9Sstevel@tonic-gate 			s = "close";
14587c478bd9Sstevel@tonic-gate 			break;
14597c478bd9Sstevel@tonic-gate 		case SBBC_DBG_IOCTL:
14607c478bd9Sstevel@tonic-gate 			s = "ioctl";
14617c478bd9Sstevel@tonic-gate 			break;
14627c478bd9Sstevel@tonic-gate 		default:
14637c478bd9Sstevel@tonic-gate 			s = "Unknown debug flag";
14647c478bd9Sstevel@tonic-gate 			break;
14657c478bd9Sstevel@tonic-gate 		}
14667c478bd9Sstevel@tonic-gate 
14677c478bd9Sstevel@tonic-gate 		cmn_err(CE_CONT, "%s_%s(%d): ", ddi_driver_name(dip), s,
14687c478bd9Sstevel@tonic-gate 			ddi_get_instance(dip));
14697c478bd9Sstevel@tonic-gate 		cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5);
14707c478bd9Sstevel@tonic-gate 	}
14717c478bd9Sstevel@tonic-gate }
14727c478bd9Sstevel@tonic-gate 
14737c478bd9Sstevel@tonic-gate #endif /* DEBUG */
1474