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