xref: /onnv-gate/usr/src/uts/sun4u/montecarlo/io/acebus.c (revision 7656:2621e50fdf4a)
11708Sstevel /*
21708Sstevel  * CDDL HEADER START
31708Sstevel  *
41708Sstevel  * The contents of this file are subject to the terms of the
51708Sstevel  * Common Development and Distribution License (the "License").
61708Sstevel  * You may not use this file except in compliance with the License.
71708Sstevel  *
81708Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91708Sstevel  * or http://www.opensolaris.org/os/licensing.
101708Sstevel  * See the License for the specific language governing permissions
111708Sstevel  * and limitations under the License.
121708Sstevel  *
131708Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
141708Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151708Sstevel  * If applicable, add the following below this CDDL HEADER, with the
161708Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
171708Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
181708Sstevel  *
191708Sstevel  * CDDL HEADER END
201708Sstevel  */
211708Sstevel 
221708Sstevel /*
23*7656SSherry.Moore@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
241708Sstevel  * Use is subject to license terms.
251708Sstevel  */
261708Sstevel 
271708Sstevel 
281708Sstevel #include <sys/types.h>
291708Sstevel #include <sys/conf.h>
301708Sstevel #include <sys/ddi.h>
311708Sstevel #include <sys/sunddi.h>
321708Sstevel #include <sys/ddi_impldefs.h>
331708Sstevel #include <sys/ddi_subrdefs.h>
341708Sstevel #include <sys/pci.h>
351708Sstevel #include <sys/pci/pci_nexus.h>
361708Sstevel #include <sys/autoconf.h>
371708Sstevel #include <sys/cmn_err.h>
381708Sstevel #include <sys/errno.h>
391708Sstevel #include <sys/kmem.h>
401708Sstevel #include <sys/debug.h>
411708Sstevel #include <sys/sysmacros.h>
421708Sstevel #include <sys/acebus.h>
431708Sstevel 
441708Sstevel #ifdef DEBUG
451708Sstevel static uint_t acebus_debug_flags = 0;
461708Sstevel #endif
471708Sstevel 
481708Sstevel /*
491708Sstevel  * The values of the following variables are used to initialize
501708Sstevel  * the cache line size and latency timer registers in the ebus
511708Sstevel  * configuration header.  Variables are used instead of constants
521708Sstevel  * to allow tuning from the /etc/system file.
531708Sstevel  */
541708Sstevel static uint8_t acebus_cache_line_size = 0x10;	/* 64 bytes */
551708Sstevel static uint8_t acebus_latency_timer = 0x40;	/* 64 PCI cycles */
561708Sstevel 
571708Sstevel /*
581708Sstevel  * function prototypes for bus ops routines:
591708Sstevel  */
601708Sstevel static int
611708Sstevel acebus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
621708Sstevel 	off_t offset, off_t len, caddr_t *addrp);
631708Sstevel static int
641708Sstevel acebus_ctlops(dev_info_t *dip, dev_info_t *rdip,
651708Sstevel 	ddi_ctl_enum_t op, void *arg, void *result);
661708Sstevel static int
671708Sstevel acebus_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
681708Sstevel     ddi_intr_handle_impl_t *hdlp, void *result);
691708Sstevel 
701708Sstevel /*
711708Sstevel  * function prototypes for dev ops routines:
721708Sstevel  */
731708Sstevel static int acebus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
741708Sstevel static int acebus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
751708Sstevel 
761708Sstevel /*
771708Sstevel  * general function prototypes:
781708Sstevel  */
791708Sstevel static int acebus_config(ebus_devstate_t *ebus_p);
801708Sstevel static int acebus_apply_range(ebus_devstate_t *ebus_p, dev_info_t *rdip,
811708Sstevel     ebus_regspec_t *ebus_rp, pci_regspec_t *rp);
821708Sstevel static int acebus_get_ranges_prop(ebus_devstate_t *ebus_p);
831708Sstevel #ifdef	ACEBUS_HOTPLUG
841708Sstevel static int acebus_update_props(ebus_devstate_t *ebus_p);
851708Sstevel static int acebus_set_imap(dev_info_t *dip);
861708Sstevel #endif
871708Sstevel 
881708Sstevel #define	getprop(dip, name, addr, intp)		\
891708Sstevel 		ddi_getlongprop(DDI_DEV_T_ANY, (dip), DDI_PROP_DONTPASS, \
901708Sstevel 				(name), (caddr_t)(addr), (intp))
911708Sstevel 
921708Sstevel /*
931708Sstevel  * bus ops and dev ops structures:
941708Sstevel  */
951708Sstevel static struct bus_ops acebus_bus_ops = {
961708Sstevel 	BUSO_REV,
971708Sstevel 	acebus_map,
981708Sstevel 	NULL,
991708Sstevel 	NULL,
1001708Sstevel 	NULL,
1011708Sstevel 	i_ddi_map_fault,
1021708Sstevel 	ddi_dma_map,
1031708Sstevel 	ddi_dma_allochdl,
1041708Sstevel 	ddi_dma_freehdl,
1051708Sstevel 	ddi_dma_bindhdl,
1061708Sstevel 	ddi_dma_unbindhdl,
1071708Sstevel 	ddi_dma_flush,
1081708Sstevel 	ddi_dma_win,
1091708Sstevel 	ddi_dma_mctl,
1101708Sstevel 	acebus_ctlops,
1111708Sstevel 	ddi_bus_prop_op,
1121708Sstevel 	0,				/* (*bus_get_eventcookie)();	*/
1131708Sstevel 	0,				/* (*bus_add_eventcall)();	*/
1141708Sstevel 	0,				/* (*bus_remove_eventcall)();	*/
1151708Sstevel 	0,				/* (*bus_post_event)();		*/
1161708Sstevel 	0,				/* (*bus_intr_ctl)();		*/
1171708Sstevel 	NULL,				/* (*bus_config)();		*/
1181708Sstevel 	NULL,				/* (*bus_unconfig)();		*/
1191708Sstevel 	NULL,				/* (*bus_fm_init)();		*/
1201708Sstevel 	NULL,				/* (*bus_fm_fini)();		*/
1211708Sstevel 	NULL,				/* (*bus_fm_access_enter)();	*/
1221708Sstevel 	NULL,				/* (*bus_fm_access_fini)();	*/
1231708Sstevel 	NULL,				/* (*bus_power)();		*/
1241708Sstevel 	acebus_intr_ops			/* (*bus_intr_op)();		*/
1251708Sstevel };
1261708Sstevel 
1271708Sstevel static struct dev_ops acebus_ops = {
1281708Sstevel 	DEVO_REV,
1291708Sstevel 	0,
1301708Sstevel 	ddi_no_info,
1311708Sstevel 	nulldev,
1321708Sstevel 	nulldev,
1331708Sstevel 	acebus_attach,
1341708Sstevel 	acebus_detach,
1351708Sstevel 	nodev,
1361708Sstevel 	(struct cb_ops *)0,
137*7656SSherry.Moore@Sun.COM 	&acebus_bus_ops,
138*7656SSherry.Moore@Sun.COM 	NULL,
139*7656SSherry.Moore@Sun.COM 	ddi_quiesce_not_supported,	/* devo_quiesce */
1401708Sstevel };
1411708Sstevel 
1421708Sstevel /*
1431708Sstevel  * module definitions:
1441708Sstevel  */
1451708Sstevel #include <sys/modctl.h>
1461708Sstevel extern struct mod_ops mod_driverops;
1471708Sstevel 
1481708Sstevel static struct modldrv modldrv = {
1491708Sstevel 	&mod_driverops, 	/* Type of module.  This one is a driver */
150*7656SSherry.Moore@Sun.COM 	"Alarm Card ebus nexus",	/* Name of module. */
1511708Sstevel 	&acebus_ops,		/* driver ops */
1521708Sstevel };
1531708Sstevel 
1541708Sstevel static struct modlinkage modlinkage = {
1551708Sstevel 	MODREV_1, (void *)&modldrv, NULL
1561708Sstevel };
1571708Sstevel 
1581708Sstevel /*
1591708Sstevel  * driver global data:
1601708Sstevel  */
1611708Sstevel static void *per_acebus_state;		/* per-ebus soft state pointer */
1621708Sstevel 
1631708Sstevel 
1641708Sstevel int
_init(void)1651708Sstevel _init(void)
1661708Sstevel {
1671708Sstevel 	int e;
1681708Sstevel 
1691708Sstevel 	/*
1701708Sstevel 	 * Initialize per-ebus soft state pointer.
1711708Sstevel 	 */
1721708Sstevel 	e = ddi_soft_state_init(&per_acebus_state, sizeof (ebus_devstate_t), 1);
1731708Sstevel 	if (e != 0)
1741708Sstevel 		return (e);
1751708Sstevel 
1761708Sstevel 	/*
1771708Sstevel 	 * Install the module.
1781708Sstevel 	 */
1791708Sstevel 	e = mod_install(&modlinkage);
1801708Sstevel 	if (e != 0)
1811708Sstevel 		ddi_soft_state_fini(&per_acebus_state);
1821708Sstevel 	return (e);
1831708Sstevel }
1841708Sstevel 
1851708Sstevel int
_fini(void)1861708Sstevel _fini(void)
1871708Sstevel {
1881708Sstevel 	int e;
1891708Sstevel 
1901708Sstevel 	/*
1911708Sstevel 	 * Remove the module.
1921708Sstevel 	 */
1931708Sstevel 	e = mod_remove(&modlinkage);
1941708Sstevel 	if (e != 0)
1951708Sstevel 		return (e);
1961708Sstevel 
1971708Sstevel 	/*
1981708Sstevel 	 * Free the soft state info.
1991708Sstevel 	 */
2001708Sstevel 	ddi_soft_state_fini(&per_acebus_state);
2011708Sstevel 	return (e);
2021708Sstevel }
2031708Sstevel 
2041708Sstevel int
_info(struct modinfo * modinfop)2051708Sstevel _info(struct modinfo *modinfop)
2061708Sstevel {
2071708Sstevel 	return (mod_info(&modlinkage, modinfop));
2081708Sstevel }
2091708Sstevel 
2101708Sstevel /* device driver entry points */
2111708Sstevel 
2121708Sstevel /*
2131708Sstevel  * attach entry point:
2141708Sstevel  *
2151708Sstevel  * normal attach:
2161708Sstevel  *
2171708Sstevel  *	create soft state structure (dip, reg, nreg and state fields)
2181708Sstevel  *	map in configuration header
2191708Sstevel  *	make sure device is properly configured
2201708Sstevel  *	report device
2211708Sstevel  */
2221708Sstevel static int
acebus_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)2231708Sstevel acebus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2241708Sstevel {
2251708Sstevel 	ebus_devstate_t *ebus_p;	/* per ebus state pointer */
2261708Sstevel 	int instance;
2271708Sstevel 
2281708Sstevel 	DBG1(D_ATTACH, NULL, "dip=%x\n", dip);
2291708Sstevel 	switch (cmd) {
2301708Sstevel 	case DDI_ATTACH:
2311708Sstevel 
2321708Sstevel 		/*
2331708Sstevel 		 * Allocate soft state for this instance.
2341708Sstevel 		 */
2351708Sstevel 		instance = ddi_get_instance(dip);
2361708Sstevel 		if (ddi_soft_state_zalloc(per_acebus_state, instance)
237*7656SSherry.Moore@Sun.COM 		    != DDI_SUCCESS) {
2381708Sstevel 			DBG(D_ATTACH, NULL, "failed to alloc soft state\n");
2391708Sstevel 			return (DDI_FAILURE);
2401708Sstevel 		}
2411708Sstevel 		ebus_p = get_acebus_soft_state(instance);
2421708Sstevel 		ebus_p->dip = dip;
2431708Sstevel 
2441708Sstevel 		/*
2451708Sstevel 		 * Make sure the master enable and memory access enable
2461708Sstevel 		 * bits are set in the config command register.
2471708Sstevel 		 */
2481708Sstevel 		if (!acebus_config(ebus_p)) {
2491708Sstevel 			free_acebus_soft_state(instance);
2501708Sstevel 			return (DDI_FAILURE);
2511708Sstevel 		}
2521708Sstevel 
2531708Sstevel 		(void) ddi_prop_create(DDI_DEV_T_NONE, dip,
254*7656SSherry.Moore@Sun.COM 		    DDI_PROP_CANSLEEP, "no-dma-interrupt-sync", NULL, 0);
2551708Sstevel 		/* Get our ranges property for mapping child registers. */
2561708Sstevel 		if (acebus_get_ranges_prop(ebus_p) != DDI_SUCCESS) {
2571708Sstevel 			free_acebus_soft_state(instance);
2581708Sstevel 			return (DDI_FAILURE);
2591708Sstevel 		}
2601708Sstevel 
2611708Sstevel 		/*
2621708Sstevel 		 * Make the state as attached and report the device.
2631708Sstevel 		 */
2641708Sstevel 		ebus_p->state = ATTACHED;
2651708Sstevel 		ddi_report_dev(dip);
2661708Sstevel 		DBG(D_ATTACH, ebus_p, "returning\n");
2671708Sstevel 		return (DDI_SUCCESS);
2681708Sstevel 
2691708Sstevel 	case DDI_RESUME:
2701708Sstevel 
2711708Sstevel 		instance = ddi_get_instance(dip);
2721708Sstevel 		ebus_p = get_acebus_soft_state(instance);
2731708Sstevel 
2741708Sstevel 		/*
2751708Sstevel 		 * Make sure the master enable and memory access enable
2761708Sstevel 		 * bits are set in the config command register.
2771708Sstevel 		 */
2781708Sstevel 		if (!acebus_config(ebus_p)) {
2791708Sstevel 			free_acebus_soft_state(instance);
2801708Sstevel 			return (DDI_FAILURE);
2811708Sstevel 		}
2821708Sstevel 
2831708Sstevel 		ebus_p->state = RESUMED;
2841708Sstevel 		return (DDI_SUCCESS);
2851708Sstevel 	}
2861708Sstevel 	return (DDI_FAILURE);
2871708Sstevel }
2881708Sstevel 
2891708Sstevel /*
2901708Sstevel  * detach entry point:
2911708Sstevel  */
2921708Sstevel static int
acebus_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)2931708Sstevel acebus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
2941708Sstevel {
2951708Sstevel 	int instance = ddi_get_instance(dip);
2961708Sstevel 	ebus_devstate_t *ebus_p = get_acebus_soft_state(instance);
2971708Sstevel 
2981708Sstevel 	switch (cmd) {
2991708Sstevel 	case DDI_DETACH:
3001708Sstevel 		DBG1(D_DETACH, ebus_p, "DDI_DETACH dip=%p\n", dip);
3011708Sstevel 		ddi_prop_remove_all(dip);
3021708Sstevel 		kmem_free(ebus_p->rangep, ebus_p->range_cnt *
3031708Sstevel 		    sizeof (struct ebus_pci_rangespec));
3041708Sstevel 		free_acebus_soft_state(instance);
3051708Sstevel 		return (DDI_SUCCESS);
3061708Sstevel 
3071708Sstevel 	case DDI_SUSPEND:
3081708Sstevel 		DBG1(D_DETACH, ebus_p, "DDI_SUSPEND dip=%p\n", dip);
3091708Sstevel 		ebus_p->state = SUSPENDED;
3101708Sstevel 		return (DDI_SUCCESS);
3111708Sstevel 	}
3121708Sstevel 	return (DDI_FAILURE);
3131708Sstevel }
3141708Sstevel 
3151708Sstevel 
3161708Sstevel static int
acebus_get_ranges_prop(ebus_devstate_t * ebus_p)3171708Sstevel acebus_get_ranges_prop(ebus_devstate_t *ebus_p)
3181708Sstevel {
3191708Sstevel 	struct ebus_pci_rangespec *rangep;
3201708Sstevel 	int nrange, range_len;
3211708Sstevel 
3221708Sstevel 	if (ddi_getlongprop(DDI_DEV_T_ANY, ebus_p->dip, DDI_PROP_DONTPASS,
3231708Sstevel 	    "ranges", (caddr_t)&rangep, &range_len) != DDI_SUCCESS) {
3241708Sstevel 
3251708Sstevel 		cmn_err(CE_WARN, "%s%d: can't get ranges property",
3261708Sstevel 		    ddi_get_name(ebus_p->dip), ddi_get_instance(ebus_p->dip));
3271708Sstevel 		return (DDI_ME_REGSPEC_RANGE);
3281708Sstevel 	}
3291708Sstevel 
3301708Sstevel 	nrange = range_len / sizeof (struct ebus_pci_rangespec);
3311708Sstevel 
3321708Sstevel 	if (nrange == 0)  {
3331708Sstevel 		kmem_free(rangep, range_len);
3341708Sstevel 		return (DDI_FAILURE);
3351708Sstevel 	}
3361708Sstevel 
3371708Sstevel #ifdef	DEBUG
338*7656SSherry.Moore@Sun.COM 	{
339*7656SSherry.Moore@Sun.COM 		int i;
3401708Sstevel 
341*7656SSherry.Moore@Sun.COM 		for (i = 0; i < nrange; i++) {
342*7656SSherry.Moore@Sun.COM 			DBG5(D_MAP, ebus_p,
343*7656SSherry.Moore@Sun.COM 			    "ebus range addr 0x%x.0x%x PCI range "
344*7656SSherry.Moore@Sun.COM 			    "addr 0x%x.0x%x.0x%x ", rangep[i].ebus_phys_hi,
3451708Sstevel 			    rangep[i].ebus_phys_low, rangep[i].pci_phys_hi,
3461708Sstevel 			    rangep[i].pci_phys_mid, rangep[i].pci_phys_low);
347*7656SSherry.Moore@Sun.COM 			DBG1(D_MAP, ebus_p, "Size 0x%x\n", rangep[i].rng_size);
348*7656SSherry.Moore@Sun.COM 		}
3491708Sstevel 	}
3501708Sstevel #endif /* DEBUG */
3511708Sstevel 
3521708Sstevel 	ebus_p->rangep = rangep;
3531708Sstevel 	ebus_p->range_cnt = nrange;
3541708Sstevel 
3551708Sstevel 	return (DDI_SUCCESS);
3561708Sstevel }
3571708Sstevel 
3581708Sstevel 
3591708Sstevel /* bus driver entry points */
3601708Sstevel 
3611708Sstevel /*
3621708Sstevel  * bus map entry point:
3631708Sstevel  *
3641708Sstevel  * 	if map request is for an rnumber
3651708Sstevel  *		get the corresponding regspec from device node
3661708Sstevel  * 	build a new regspec in our parent's format
3671708Sstevel  *	build a new map_req with the new regspec
3681708Sstevel  *	call up the tree to complete the mapping
3691708Sstevel  */
3701708Sstevel static int
acebus_map(dev_info_t * dip,dev_info_t * rdip,ddi_map_req_t * mp,off_t off,off_t len,caddr_t * addrp)3711708Sstevel acebus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
3721708Sstevel 	off_t off, off_t len, caddr_t *addrp)
3731708Sstevel {
3741708Sstevel 	ebus_devstate_t *ebus_p = get_acebus_soft_state(ddi_get_instance(dip));
3751708Sstevel 	ebus_regspec_t *ebus_rp, *ebus_regs;
3761708Sstevel 	pci_regspec_t pci_reg;
3771708Sstevel 	ddi_map_req_t p_map_request;
3781708Sstevel 	int rnumber, i, n;
3791708Sstevel 	int rval = DDI_SUCCESS;
3801708Sstevel 
3811708Sstevel 	/*
3821708Sstevel 	 * Handle the mapping according to its type.
3831708Sstevel 	 */
3841708Sstevel 	DBG4(D_MAP, ebus_p, "rdip=%s%d: off=%x len=%x\n",
3851708Sstevel 	    ddi_get_name(rdip), ddi_get_instance(rdip), off, len);
3861708Sstevel 	switch (mp->map_type) {
3871708Sstevel 	case DDI_MT_REGSPEC:
3881708Sstevel 
3891708Sstevel 		/*
3901708Sstevel 		 * We assume the register specification is in ebus format.
3911708Sstevel 		 * We must convert it into a PCI format regspec and pass
3921708Sstevel 		 * the request to our parent.
3931708Sstevel 		 */
3941708Sstevel 		DBG3(D_MAP, ebus_p, "rdip=%s%d: REGSPEC - handlep=%x\n",
395*7656SSherry.Moore@Sun.COM 		    ddi_get_name(rdip), ddi_get_instance(rdip),
396*7656SSherry.Moore@Sun.COM 		    mp->map_handlep);
3971708Sstevel 		ebus_rp = (ebus_regspec_t *)mp->map_obj.rp;
3981708Sstevel 		break;
3991708Sstevel 
4001708Sstevel 	case DDI_MT_RNUMBER:
4011708Sstevel 
4021708Sstevel 		/*
4031708Sstevel 		 * Get the "reg" property from the device node and convert
4041708Sstevel 		 * it to our parent's format.
4051708Sstevel 		 */
4061708Sstevel 		rnumber = mp->map_obj.rnumber;
4071708Sstevel 		DBG4(D_MAP, ebus_p, "rdip=%s%d: rnumber=%x handlep=%x\n",
408*7656SSherry.Moore@Sun.COM 		    ddi_get_name(rdip), ddi_get_instance(rdip),
409*7656SSherry.Moore@Sun.COM 		    rnumber, mp->map_handlep);
4101708Sstevel 
4111708Sstevel 		if (getprop(rdip, "reg", &ebus_regs, &i) != DDI_SUCCESS) {
4121708Sstevel 			DBG(D_MAP, ebus_p, "can't get reg property\n");
4131708Sstevel 			return (DDI_ME_RNUMBER_RANGE);
4141708Sstevel 		}
4151708Sstevel 		n = i / sizeof (ebus_regspec_t);
4161708Sstevel 
4171708Sstevel 		if (rnumber < 0 || rnumber >= n) {
4181708Sstevel 			DBG(D_MAP, ebus_p, "rnumber out of range\n");
4191708Sstevel 			return (DDI_ME_RNUMBER_RANGE);
4201708Sstevel 		}
4211708Sstevel 		ebus_rp = &ebus_regs[rnumber];
4221708Sstevel 		break;
4231708Sstevel 
4241708Sstevel 	default:
4251708Sstevel 		return (DDI_ME_INVAL);
4261708Sstevel 
4271708Sstevel 	}
4281708Sstevel 
4291708Sstevel 	/* Adjust our reg property with offset and length */
4301708Sstevel 	ebus_rp->addr_low += off;
4311708Sstevel 	if (len)
4321708Sstevel 		ebus_rp->size = len;
4331708Sstevel 
4341708Sstevel 	/*
4351708Sstevel 	 * Now we have a copy the "reg" entry we're attempting to map.
4361708Sstevel 	 * Translate this into our parents PCI address using the ranges
4371708Sstevel 	 * property.
4381708Sstevel 	 */
4391708Sstevel 	rval = acebus_apply_range(ebus_p, rdip, ebus_rp, &pci_reg);
4401708Sstevel 
4411708Sstevel 	if (mp->map_type == DDI_MT_RNUMBER)
4421708Sstevel 		kmem_free((caddr_t)ebus_regs, i);
4431708Sstevel 
4441708Sstevel 	if (rval != DDI_SUCCESS)
4451708Sstevel 		return (rval);
4461708Sstevel 
4471708Sstevel #ifdef	ACEBUS_HOTPLUG
4481708Sstevel 	/*
4491708Sstevel 	 * The map operation provides a translated (not a re-assigned, or
4501708Sstevel 	 * relocated) ebus address for the child in its address space(range).
4511708Sstevel 	 * Ebus address space is relocatible but its child address space
4521708Sstevel 	 * is not. As specified by their 'reg' properties, they reside
4531708Sstevel 	 * at a fixed offset in their parent's (ebus's) space.
4541708Sstevel 	 *
4551708Sstevel 	 * By setting this bit, we will not run into HostPCI nexus
4561708Sstevel 	 * trying to relocate a translated ebus address (which is already
4571708Sstevel 	 * relocated) and failing the operation.
4581708Sstevel 	 * The reason for doing this here is that the PCI hotplug configurator
4591708Sstevel 	 * always marks the ebus space as relocatible (unlike OBP) and that
4601708Sstevel 	 * information is implied for the child too, which is wrong.
4611708Sstevel 	 */
4621708Sstevel 	pci_reg.pci_phys_hi |= PCI_RELOCAT_B;
4631708Sstevel #endif
4641708Sstevel #ifdef DEBUG
4651708Sstevel 	DBG5(D_MAP, ebus_p, "(%x,%x,%x)(%x,%x)\n",
466*7656SSherry.Moore@Sun.COM 	    pci_reg.pci_phys_hi,
467*7656SSherry.Moore@Sun.COM 	    pci_reg.pci_phys_mid,
468*7656SSherry.Moore@Sun.COM 	    pci_reg.pci_phys_low,
469*7656SSherry.Moore@Sun.COM 	    pci_reg.pci_size_hi,
470*7656SSherry.Moore@Sun.COM 	    pci_reg.pci_size_low);
4711708Sstevel #endif
4721708Sstevel 
4731708Sstevel 	p_map_request = *mp;
4741708Sstevel 	p_map_request.map_type = DDI_MT_REGSPEC;
4751708Sstevel 	p_map_request.map_obj.rp = (struct regspec *)&pci_reg;
4761708Sstevel 	rval = ddi_map(dip, &p_map_request, 0, 0, addrp);
4771708Sstevel 	DBG1(D_MAP, ebus_p, "parent returned %x\n", rval);
4781708Sstevel 	return (rval);
4791708Sstevel }
4801708Sstevel 
4811708Sstevel 
4821708Sstevel static int
acebus_apply_range(ebus_devstate_t * ebus_p,dev_info_t * rdip,ebus_regspec_t * ebus_rp,pci_regspec_t * rp)4831708Sstevel acebus_apply_range(ebus_devstate_t *ebus_p, dev_info_t *rdip,
4841708Sstevel     ebus_regspec_t *ebus_rp, pci_regspec_t *rp)
4851708Sstevel {
4861708Sstevel 	int b;
4871708Sstevel 	int rval = DDI_SUCCESS;
4881708Sstevel 	struct ebus_pci_rangespec *rangep = ebus_p->rangep;
4891708Sstevel 	int nrange = ebus_p->range_cnt;
4901708Sstevel 	static const char out_of_range[] =
4911708Sstevel 	    "Out of range register specification from device node <%s>";
4921708Sstevel 
4931708Sstevel 	DBG3(D_MAP, ebus_p, "Range Matching Addr 0x%x.%x size 0x%x\n",
4941708Sstevel 	    ebus_rp->addr_hi, ebus_rp->addr_low, ebus_rp->size);
4951708Sstevel 
4961708Sstevel 	for (b = 0; b < nrange; ++b, ++rangep) {
4971708Sstevel 
4981708Sstevel 		/* Check for the correct space */
4991708Sstevel 		if (ebus_rp->addr_hi == rangep->ebus_phys_hi)
5001708Sstevel 			/* See if we fit in this range */
5011708Sstevel 			if ((ebus_rp->addr_low >=
5021708Sstevel 			    rangep->ebus_phys_low) &&
5031708Sstevel 			    ((ebus_rp->addr_low + ebus_rp->size - 1)
504*7656SSherry.Moore@Sun.COM 			    <= (rangep->ebus_phys_low +
505*7656SSherry.Moore@Sun.COM 			    rangep->rng_size - 1))) {
5061708Sstevel 				uint_t addr_offset = ebus_rp->addr_low -
5071708Sstevel 				    rangep->ebus_phys_low;
5081708Sstevel 				/*
5091708Sstevel 				 * Use the range entry to translate
5101708Sstevel 				 * the EBUS physical address into the
5111708Sstevel 				 * parents PCI space.
5121708Sstevel 				 */
5131708Sstevel 				rp->pci_phys_hi =
5141708Sstevel 				    rangep->pci_phys_hi;
5151708Sstevel 				rp->pci_phys_mid = rangep->pci_phys_mid;
5161708Sstevel 				rp->pci_phys_low =
5171708Sstevel 				    rangep->pci_phys_low + addr_offset;
5181708Sstevel 				rp->pci_size_hi = 0;
5191708Sstevel 				rp->pci_size_low =
5201708Sstevel 				    min(ebus_rp->size, (rangep->rng_size -
521*7656SSherry.Moore@Sun.COM 				    addr_offset));
5221708Sstevel 
5231708Sstevel 				DBG2(D_MAP, ebus_p, "Child hi0x%x lo0x%x ",
5241708Sstevel 				    rangep->ebus_phys_hi,
5251708Sstevel 				    rangep->ebus_phys_low);
5261708Sstevel 				DBG4(D_MAP, ebus_p, "Parent hi0x%x "
527*7656SSherry.Moore@Sun.COM 				    "mid0x%x lo0x%x size 0x%x\n",
528*7656SSherry.Moore@Sun.COM 				    rangep->pci_phys_hi,
529*7656SSherry.Moore@Sun.COM 				    rangep->pci_phys_mid,
530*7656SSherry.Moore@Sun.COM 				    rangep->pci_phys_low,
531*7656SSherry.Moore@Sun.COM 				    rangep->rng_size);
5321708Sstevel 
5331708Sstevel 				break;
5341708Sstevel 			}
5351708Sstevel 	}
5361708Sstevel 
5371708Sstevel 	if (b == nrange)  {
5381708Sstevel 		cmn_err(CE_WARN, out_of_range, ddi_get_name(rdip));
5391708Sstevel 		return (DDI_ME_REGSPEC_RANGE);
5401708Sstevel 	}
5411708Sstevel 
5421708Sstevel 	return (rval);
5431708Sstevel }
5441708Sstevel 
5451708Sstevel 
5461708Sstevel /*
5471708Sstevel  * control ops entry point:
5481708Sstevel  *
5491708Sstevel  * Requests handled completely:
5501708Sstevel  *	DDI_CTLOPS_INITCHILD
5511708Sstevel  *	DDI_CTLOPS_UNINITCHILD
5521708Sstevel  *	DDI_CTLOPS_REPORTDEV
5531708Sstevel  *	DDI_CTLOPS_REGSIZE
5541708Sstevel  *	DDI_CTLOPS_NREGS
5551708Sstevel  *
5561708Sstevel  * All others passed to parent.
5571708Sstevel  */
5581708Sstevel static int
acebus_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t op,void * arg,void * result)5591708Sstevel acebus_ctlops(dev_info_t *dip, dev_info_t *rdip,
5601708Sstevel 	ddi_ctl_enum_t op, void *arg, void *result)
5611708Sstevel {
5621708Sstevel #ifdef DEBUG
5631708Sstevel 	ebus_devstate_t *ebus_p = get_acebus_soft_state(ddi_get_instance(dip));
5641708Sstevel #endif
5651708Sstevel 	ebus_regspec_t *ebus_rp;
5661708Sstevel 	int32_t reglen;
5671708Sstevel 	int i, n;
5681708Sstevel 	char name[10];
5691708Sstevel 
5701708Sstevel 	switch (op) {
5711708Sstevel 	case DDI_CTLOPS_INITCHILD: {
5721708Sstevel 		dev_info_t *child = (dev_info_t *)arg;
5731708Sstevel 		/*
5741708Sstevel 		 * Set the address portion of the node name based on the
5751708Sstevel 		 * address/offset.
5761708Sstevel 		 */
5771708Sstevel 		DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_INITCHILD: rdip=%s%d\n",
5781708Sstevel 		    ddi_get_name(child), ddi_get_instance(child));
5791708Sstevel 
5801708Sstevel 		if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
5811708Sstevel 		    "reg", (caddr_t)&ebus_rp, &reglen) != DDI_SUCCESS) {
5821708Sstevel 
5831708Sstevel 			DBG(D_CTLOPS, ebus_p, "can't get reg property\n");
5841708Sstevel 			return (DDI_FAILURE);
5851708Sstevel 
5861708Sstevel 		}
5871708Sstevel 
5881708Sstevel 		(void) sprintf(name, "%x,%x", ebus_rp->addr_hi,
5891708Sstevel 		    ebus_rp->addr_low);
5901708Sstevel 		ddi_set_name_addr(child, name);
5911708Sstevel 		kmem_free((caddr_t)ebus_rp, reglen);
5921708Sstevel 
5931708Sstevel 		ddi_set_parent_data(child, NULL);
5941708Sstevel 
5951708Sstevel 		return (DDI_SUCCESS);
5961708Sstevel 
5971708Sstevel 	}
5981708Sstevel 
5991708Sstevel 	case DDI_CTLOPS_UNINITCHILD:
6001708Sstevel 		DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_UNINITCHILD: rdip=%s%d\n",
601*7656SSherry.Moore@Sun.COM 		    ddi_get_name((dev_info_t *)arg),
602*7656SSherry.Moore@Sun.COM 		    ddi_get_instance((dev_info_t *)arg));
6031708Sstevel 		ddi_set_name_addr((dev_info_t *)arg, NULL);
6041708Sstevel 		ddi_remove_minor_node((dev_info_t *)arg, NULL);
6051708Sstevel 		impl_rem_dev_props((dev_info_t *)arg);
6061708Sstevel 		return (DDI_SUCCESS);
6071708Sstevel 
6081708Sstevel 	case DDI_CTLOPS_REPORTDEV:
6091708Sstevel 
6101708Sstevel 		DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_REPORTDEV: rdip=%s%d\n",
611*7656SSherry.Moore@Sun.COM 		    ddi_get_name(rdip), ddi_get_instance(rdip));
6121708Sstevel 		cmn_err(CE_CONT, "?%s%d at %s%d: offset %s\n",
613*7656SSherry.Moore@Sun.COM 		    ddi_driver_name(rdip), ddi_get_instance(rdip),
614*7656SSherry.Moore@Sun.COM 		    ddi_driver_name(dip), ddi_get_instance(dip),
615*7656SSherry.Moore@Sun.COM 		    ddi_get_name_addr(rdip));
6161708Sstevel 		return (DDI_SUCCESS);
6171708Sstevel 
6181708Sstevel 	case DDI_CTLOPS_REGSIZE:
6191708Sstevel 
6201708Sstevel 		DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_REGSIZE: rdip=%s%d\n",
621*7656SSherry.Moore@Sun.COM 		    ddi_get_name(rdip), ddi_get_instance(rdip));
6221708Sstevel 		if (getprop(rdip, "reg", &ebus_rp, &i) != DDI_SUCCESS) {
6231708Sstevel 			DBG(D_CTLOPS, ebus_p, "can't get reg property\n");
6241708Sstevel 			return (DDI_FAILURE);
6251708Sstevel 		}
6261708Sstevel 		n = i / sizeof (ebus_regspec_t);
6271708Sstevel 		if (*(int *)arg < 0 || *(int *)arg >= n) {
6281708Sstevel 			DBG(D_MAP, ebus_p, "rnumber out of range\n");
6291708Sstevel 			kmem_free((caddr_t)ebus_rp, i);
6301708Sstevel 			return (DDI_FAILURE);
6311708Sstevel 		}
6321708Sstevel 		*((off_t *)result) = ebus_rp[*(int *)arg].size;
6331708Sstevel 		kmem_free((caddr_t)ebus_rp, i);
6341708Sstevel 		return (DDI_SUCCESS);
6351708Sstevel 
6361708Sstevel 	case DDI_CTLOPS_NREGS:
6371708Sstevel 
6381708Sstevel 		DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_NREGS: rdip=%s%d\n",
639*7656SSherry.Moore@Sun.COM 		    ddi_get_name(rdip), ddi_get_instance(rdip));
6401708Sstevel 		if (getprop(rdip, "reg", &ebus_rp, &i) != DDI_SUCCESS) {
6411708Sstevel 			DBG(D_CTLOPS, ebus_p, "can't get reg property\n");
6421708Sstevel 			return (DDI_FAILURE);
6431708Sstevel 		}
6441708Sstevel 		*((uint_t *)result) = i / sizeof (ebus_regspec_t);
6451708Sstevel 		kmem_free((caddr_t)ebus_rp, i);
6461708Sstevel 		return (DDI_SUCCESS);
6471708Sstevel 	}
6481708Sstevel 
6491708Sstevel 	/*
6501708Sstevel 	 * Now pass the request up to our parent.
6511708Sstevel 	 */
6521708Sstevel 	DBG2(D_CTLOPS, ebus_p, "passing request to parent: rdip=%s%d\n",
653*7656SSherry.Moore@Sun.COM 	    ddi_get_name(rdip), ddi_get_instance(rdip));
6541708Sstevel 	return (ddi_ctlops(dip, rdip, op, arg, result));
6551708Sstevel }
6561708Sstevel 
6571708Sstevel struct ebus_string_to_pil {
6581708Sstevel 	int8_t *string;
6591708Sstevel 	uint32_t pil;
6601708Sstevel };
6611708Sstevel 
6621708Sstevel static struct ebus_string_to_pil acebus_name_to_pil[] = {{"SUNW,CS4231", 9},
6631708Sstevel 						    {"fdthree", 8},
6641708Sstevel 						    {"ecpp", 3},
6651708Sstevel 						    {"su", 12},
6661708Sstevel 						    {"se", 12},
6671708Sstevel 						    {"power", 14}};
6681708Sstevel 
6691708Sstevel static struct ebus_string_to_pil acebus_device_type_to_pil[] = {{"serial", 12},
6701708Sstevel 								{"block", 8}};
6711708Sstevel 
6721708Sstevel static int
acebus_intr_ops(dev_info_t * dip,dev_info_t * rdip,ddi_intr_op_t intr_op,ddi_intr_handle_impl_t * hdlp,void * result)6731708Sstevel acebus_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
6741708Sstevel     ddi_intr_handle_impl_t *hdlp, void *result)
6751708Sstevel {
6761708Sstevel #ifdef DEBUG
6771708Sstevel 	ebus_devstate_t *ebus_p = get_acebus_soft_state(ddi_get_instance(dip));
6781708Sstevel #endif
6791708Sstevel 	int8_t		*name, *device_type;
6801708Sstevel 	int32_t		i, max_children, max_device_types, len;
6811708Sstevel 
6821708Sstevel 	/*
6831708Sstevel 	 * NOTE: These ops below will never be supported in this nexus
6841708Sstevel 	 * driver, hence they always return immediately.
6851708Sstevel 	 */
6861708Sstevel 	switch (intr_op) {
6871708Sstevel 	case DDI_INTROP_GETCAP:
6881708Sstevel 		*(int *)result = DDI_INTR_FLAG_LEVEL;
6891708Sstevel 		return (DDI_SUCCESS);
6903920Srameshc 	case DDI_INTROP_SUPPORTED_TYPES:
6913920Srameshc 		*(int *)result = i_ddi_get_intx_nintrs(rdip) ?
6923920Srameshc 		    DDI_INTR_TYPE_FIXED : 0;
6933920Srameshc 		return (DDI_SUCCESS);
6941708Sstevel 	case DDI_INTROP_SETCAP:
6951708Sstevel 	case DDI_INTROP_SETMASK:
6961708Sstevel 	case DDI_INTROP_CLRMASK:
6971708Sstevel 	case DDI_INTROP_GETPENDING:
6981708Sstevel 		return (DDI_ENOTSUP);
6991708Sstevel 	default:
7001708Sstevel 		break;
7011708Sstevel 	}
7021708Sstevel 
7033920Srameshc 	if (hdlp->ih_pri)
7041708Sstevel 		goto done;
7051708Sstevel 
7061708Sstevel 	/*
7071708Sstevel 	 * This is a hack to set the PIL for the devices under ebus.
7081708Sstevel 	 * We first look up a device by it's specific name, if we can't
7091708Sstevel 	 * match the name, we try and match it's device_type property.
7101708Sstevel 	 * Lastly we default a PIL level of 1.
7111708Sstevel 	 */
7121708Sstevel 	DBG1(D_INTR, ebus_p, "ebus_p %p\n", ebus_p);
7131708Sstevel 
7141708Sstevel 	name = ddi_get_name(rdip);
7151708Sstevel 	max_children = sizeof (acebus_name_to_pil) /
7161708Sstevel 	    sizeof (struct ebus_string_to_pil);
7171708Sstevel 
7181708Sstevel 	for (i = 0; i < max_children; i++) {
7191708Sstevel 		if (strcmp(acebus_name_to_pil[i].string, name) == 0) {
7201708Sstevel 			DBG2(D_INTR, ebus_p, "child name %s; match PIL %d\n",
7211708Sstevel 			    acebus_name_to_pil[i].string,
7221708Sstevel 			    acebus_name_to_pil[i].pil);
7231708Sstevel 
7241708Sstevel 			hdlp->ih_pri = acebus_name_to_pil[i].pil;
7251708Sstevel 			goto done;
7261708Sstevel 		}
7271708Sstevel 	}
7281708Sstevel 
7291708Sstevel 	if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
7301708Sstevel 	    "device_type", (caddr_t)&device_type, &len) == DDI_SUCCESS) {
7311708Sstevel 
7321708Sstevel 		max_device_types = sizeof (acebus_device_type_to_pil) /
7331708Sstevel 		    sizeof (struct ebus_string_to_pil);
7341708Sstevel 
7351708Sstevel 		for (i = 0; i < max_device_types; i++) {
7361708Sstevel 			if (strcmp(acebus_device_type_to_pil[i].string,
7371708Sstevel 			    device_type) == 0) {
7381708Sstevel 				DBG2(D_INTR, ebus_p,
7391708Sstevel 				    "Device type %s; match PIL %d\n",
7401708Sstevel 				    acebus_device_type_to_pil[i].string,
7411708Sstevel 				    acebus_device_type_to_pil[i].pil);
7421708Sstevel 
7431708Sstevel 				hdlp->ih_pri = acebus_device_type_to_pil[i].pil;
7441708Sstevel 				break;
7451708Sstevel 			}
7461708Sstevel 		}
7471708Sstevel 
7481708Sstevel 		kmem_free(device_type, len);
7491708Sstevel 	}
7501708Sstevel 
7511708Sstevel 	/*
7521708Sstevel 	 * If we get here, we need to set a default value
7531708Sstevel 	 * for the PIL.
7541708Sstevel 	 */
7551708Sstevel 	if (hdlp->ih_pri == 0) {
7561708Sstevel 		hdlp->ih_pri = 1;
7571708Sstevel 		cmn_err(CE_WARN, "%s%d assigning default interrupt level %d "
7581708Sstevel 		    "for device %s%d", ddi_driver_name(dip),
7591708Sstevel 		    ddi_get_instance(dip), hdlp->ih_pri, ddi_driver_name(rdip),
7601708Sstevel 		    ddi_get_instance(rdip));
7611708Sstevel 	}
7621708Sstevel 
7631708Sstevel done:
7641708Sstevel 	/* Pass up the request to our parent. */
7651708Sstevel 	return (i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result));
7661708Sstevel }
7671708Sstevel 
7681708Sstevel 
7691708Sstevel static int
acebus_config(ebus_devstate_t * ebus_p)7701708Sstevel acebus_config(ebus_devstate_t *ebus_p)
7711708Sstevel {
7721708Sstevel 	ddi_acc_handle_t conf_handle;
7731708Sstevel 	uint16_t comm;
7741708Sstevel #ifdef	ACEBUS_HOTPLUG
7751708Sstevel 	int tcr_reg;
7761708Sstevel 	caddr_t csr_io;
7771708Sstevel 	ddi_device_acc_attr_t csr_attr = {   /* CSR map attributes */
7781708Sstevel 		DDI_DEVICE_ATTR_V0,
7791708Sstevel 		DDI_STRUCTURE_LE_ACC,
7801708Sstevel 		DDI_STRICTORDER_ACC
7811708Sstevel 	};
7821708Sstevel 	ddi_acc_handle_t csr_handle;
7831708Sstevel #endif
7841708Sstevel 
7851708Sstevel 	/*
7861708Sstevel 	 * Make sure the master enable and memory access enable
7871708Sstevel 	 * bits are set in the config command register.
7881708Sstevel 	 */
7891708Sstevel 	if (pci_config_setup(ebus_p->dip, &conf_handle) != DDI_SUCCESS)
7901708Sstevel 		return (0);
7911708Sstevel 
7921708Sstevel 	comm = pci_config_get16(conf_handle, PCI_CONF_COMM),
7931708Sstevel #ifdef DEBUG
7941708Sstevel 	    DBG1(D_ATTACH, ebus_p, "command register was 0x%x\n", comm);
7951708Sstevel #endif
7961708Sstevel 	comm |= (PCI_COMM_ME|PCI_COMM_MAE|PCI_COMM_SERR_ENABLE|
7971708Sstevel 	    PCI_COMM_PARITY_DETECT);
7981708Sstevel 	pci_config_put16(conf_handle, PCI_CONF_COMM, comm),
7991708Sstevel #ifdef DEBUG
8001708Sstevel 	    DBG1(D_MAP, ebus_p, "command register is now 0x%x\n",
801*7656SSherry.Moore@Sun.COM 	    pci_config_get16(conf_handle, PCI_CONF_COMM));
8021708Sstevel #endif
8031708Sstevel 	pci_config_put8(conf_handle, PCI_CONF_CACHE_LINESZ,
8041708Sstevel 	    (uchar_t)acebus_cache_line_size);
8051708Sstevel 	pci_config_put8(conf_handle, PCI_CONF_LATENCY_TIMER,
8061708Sstevel 	    (uchar_t)acebus_latency_timer);
8071708Sstevel 	pci_config_teardown(&conf_handle);
8081708Sstevel 
8091708Sstevel #ifdef	ACEBUS_HOTPLUG
8101708Sstevel 	if (acebus_update_props(ebus_p) != DDI_SUCCESS) {
8111708Sstevel 		cmn_err(CE_WARN, "%s%d: Could not update special properties.",
8121708Sstevel 		    ddi_driver_name(ebus_p->dip),
8131708Sstevel 		    ddi_get_instance(ebus_p->dip));
8141708Sstevel 		return (0);
8151708Sstevel 	}
8161708Sstevel 
8171708Sstevel 	if (ddi_regs_map_setup(ebus_p->dip, CSR_IO_RINDEX,
8181708Sstevel 	    (caddr_t *)&csr_io, 0, CSR_SIZE, &csr_attr,
8191708Sstevel 	    &csr_handle) != DDI_SUCCESS) {
8201708Sstevel 		cmn_err(CE_WARN, "%s%d: Could not map Ebus CSR.",
8211708Sstevel 		    ddi_driver_name(ebus_p->dip),
8221708Sstevel 		    ddi_get_instance(ebus_p->dip));
8231708Sstevel 	}
8241708Sstevel #ifdef	DEBUG
8251708Sstevel 	if (acebus_debug_flags) {
8261708Sstevel 		DBG3(D_ATTACH, ebus_p, "tcr[123] = %x,%x,%x\n",
827*7656SSherry.Moore@Sun.COM 		    ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
828*7656SSherry.Moore@Sun.COM 		    TCR1_OFF)),
829*7656SSherry.Moore@Sun.COM 		    ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
830*7656SSherry.Moore@Sun.COM 		    TCR2_OFF)),
831*7656SSherry.Moore@Sun.COM 		    ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
832*7656SSherry.Moore@Sun.COM 		    TCR3_OFF)));
8331708Sstevel 		DBG2(D_ATTACH, ebus_p, "pmd-aux=%x, freq-aux=%x\n",
834*7656SSherry.Moore@Sun.COM 		    ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
835*7656SSherry.Moore@Sun.COM 		    PMD_AUX_OFF)),
836*7656SSherry.Moore@Sun.COM 		    ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
837*7656SSherry.Moore@Sun.COM 		    FREQ_AUX_OFF)));
8381708Sstevel #ifdef ACEBUS_DEBUG
8391708Sstevel 		for (comm = 0; comm < 4; comm++)
8401708Sstevel 			prom_printf("dcsr%d=%x, dacr%d=%x, dbcr%d=%x\n", comm,
841*7656SSherry.Moore@Sun.COM 			    ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
842*7656SSherry.Moore@Sun.COM 			    0x700000+(0x2000*comm))), comm,
843*7656SSherry.Moore@Sun.COM 			    ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
844*7656SSherry.Moore@Sun.COM 			    0x700000+(0x2000*comm)+4)), comm,
845*7656SSherry.Moore@Sun.COM 			    ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
846*7656SSherry.Moore@Sun.COM 			    0x700000+(0x2000*comm)+8)));
8471708Sstevel #endif
8481708Sstevel 	} /* acebus_debug_flags */
8491708Sstevel #endif
8501708Sstevel 	/* If TCR registers are not initialized, initialize them here */
8511708Sstevel 	tcr_reg = ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
852*7656SSherry.Moore@Sun.COM 	    TCR1_OFF));
8531708Sstevel 	if ((tcr_reg == 0) || (tcr_reg == -1))
8541708Sstevel 		ddi_put32(csr_handle, (uint32_t *)((caddr_t)csr_io + TCR1_OFF),
855*7656SSherry.Moore@Sun.COM 		    TCR1_REGVAL);
8561708Sstevel 	tcr_reg = ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
857*7656SSherry.Moore@Sun.COM 	    TCR2_OFF));
8581708Sstevel 	if ((tcr_reg == 0) || (tcr_reg == -1))
8591708Sstevel 		ddi_put32(csr_handle, (uint32_t *)((caddr_t)csr_io + TCR2_OFF),
860*7656SSherry.Moore@Sun.COM 		    TCR2_REGVAL);
8611708Sstevel 	tcr_reg = ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
862*7656SSherry.Moore@Sun.COM 	    TCR3_OFF));
8631708Sstevel 	if ((tcr_reg == 0) || (tcr_reg == -1))
8641708Sstevel 		ddi_put32(csr_handle, (uint32_t *)((caddr_t)csr_io + TCR3_OFF),
865*7656SSherry.Moore@Sun.COM 		    TCR3_REGVAL);
8661708Sstevel #ifdef	DEBUG
8671708Sstevel 	if (acebus_debug_flags) {
8681708Sstevel 		DBG3(D_ATTACH, ebus_p, "wrote tcr[123] = %x,%x,%x\n",
869*7656SSherry.Moore@Sun.COM 		    ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
870*7656SSherry.Moore@Sun.COM 		    TCR1_OFF)),
871*7656SSherry.Moore@Sun.COM 		    ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
872*7656SSherry.Moore@Sun.COM 		    TCR2_OFF)),
873*7656SSherry.Moore@Sun.COM 		    ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
874*7656SSherry.Moore@Sun.COM 		    TCR3_OFF)));
8751708Sstevel 	}
8761708Sstevel #endif
8771708Sstevel 
8781708Sstevel 	ddi_regs_map_free(&csr_handle);
8791708Sstevel #endif	/* ACEBUS_HOTPLUG */
8801708Sstevel 	return (1);	/* return success */
8811708Sstevel }
8821708Sstevel 
8831708Sstevel #ifdef DEBUG
8841708Sstevel extern void prom_printf(const char *, ...);
8851708Sstevel 
8861708Sstevel static void
acebus_debug(uint_t flag,ebus_devstate_t * ebus_p,char * fmt,uintptr_t a1,uintptr_t a2,uintptr_t a3,uintptr_t a4,uintptr_t a5)8871708Sstevel acebus_debug(uint_t flag, ebus_devstate_t *ebus_p, char *fmt,
8881708Sstevel 	uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5)
8891708Sstevel {
8901708Sstevel 	char *s;
8911708Sstevel 
8921708Sstevel 	if (acebus_debug_flags & flag) {
8931708Sstevel 		switch (flag) {
8941708Sstevel 		case D_ATTACH:
8951708Sstevel 			s = "attach"; break;
8961708Sstevel 		case D_DETACH:
8971708Sstevel 			s = "detach"; break;
8981708Sstevel 		case D_MAP:
8991708Sstevel 			s = "map"; break;
9001708Sstevel 		case D_CTLOPS:
9011708Sstevel 			s = "ctlops"; break;
9021708Sstevel 		case D_INTR:
9031708Sstevel 			s = "intr"; break;
9041708Sstevel 		}
9051708Sstevel 		if (ebus_p)
9061708Sstevel 			cmn_err(CE_CONT, "%s%d: %s: ",
907*7656SSherry.Moore@Sun.COM 			    ddi_get_name(ebus_p->dip),
908*7656SSherry.Moore@Sun.COM 			    ddi_get_instance(ebus_p->dip), s);
9091708Sstevel 		else
9101708Sstevel 			cmn_err(CE_CONT, "ebus: ");
9111708Sstevel 		cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5);
9121708Sstevel 	}
9131708Sstevel }
9141708Sstevel #endif
9151708Sstevel 
9161708Sstevel #ifdef	ACEBUS_HOTPLUG
9171708Sstevel #define	EBUS_CHILD_PHYS_LOW_RANGE	0x10
9181708Sstevel #define	EBUS_CHILD_PHYS_HI_RANGE	0x14
9191708Sstevel 
9201708Sstevel static int
acebus_update_props(ebus_devstate_t * ebus_p)9211708Sstevel acebus_update_props(ebus_devstate_t *ebus_p)
9221708Sstevel {
9231708Sstevel 	dev_info_t *dip = ebus_p->dip;
9241708Sstevel 	struct ebus_pci_rangespec er[2], *erp;
9251708Sstevel 	pci_regspec_t *pci_rp, *prp;
9261708Sstevel 	int length, rnums, imask[3], i, found = 0;
9271708Sstevel 
9281708Sstevel 	/*
9291708Sstevel 	 * If "ranges" property is found, then the device is initialized
9301708Sstevel 	 * by OBP, hence simply return.
9311708Sstevel 	 * Otherwise we create all the properties here.
9321708Sstevel 	 */
9331708Sstevel 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
9341708Sstevel 	    "ranges", (int **)&erp, (uint_t *)&length) == DDI_PROP_SUCCESS) {
9351708Sstevel 		ddi_prop_free(erp);
9361708Sstevel 		return (DDI_SUCCESS);
9371708Sstevel 	}
9381708Sstevel 
9391708Sstevel 	/*
9401708Sstevel 	 * interrupt-map is the only property that comes from a .conf file.
9411708Sstevel 	 * Since it doesn't have the nodeid field set, it must be done here.
9421708Sstevel 	 * Other properties can come from OBP or created here.
9431708Sstevel 	 */
9441708Sstevel 	if (acebus_set_imap(dip) != DDI_SUCCESS) {
9451708Sstevel 		return (DDI_FAILURE);
9461708Sstevel 	}
9471708Sstevel 
9481708Sstevel 	/*
9491708Sstevel 	 * Create the "ranges" property.
9501708Sstevel 	 * Ebus has BAR0 and BAR1 allocated (both in memory space).
9511708Sstevel 	 * Other BARs are 0.
9521708Sstevel 	 * Hence there are 2 memory ranges it operates in. (one for each BAR).
9531708Sstevel 	 * ie. there are 2 entries in its ranges property.
9541708Sstevel 	 */
9551708Sstevel 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
956*7656SSherry.Moore@Sun.COM 	    DDI_PROP_DONTPASS, "assigned-addresses",
957*7656SSherry.Moore@Sun.COM 	    (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) {
9581708Sstevel 		cmn_err(CE_WARN, "%s%d: Could not get assigned-addresses",
959*7656SSherry.Moore@Sun.COM 		    ddi_driver_name(dip), ddi_get_instance(dip));
9601708Sstevel 		return (DDI_FAILURE);
9611708Sstevel 	}
9621708Sstevel 	/*
9631708Sstevel 	 * Create the 1st mem range in which it operates corresponding
9641708Sstevel 	 * to BAR0
9651708Sstevel 	 */
9661708Sstevel 	er[0].ebus_phys_hi = EBUS_CHILD_PHYS_LOW_RANGE;
9671708Sstevel 	rnums = (length * sizeof (int))/sizeof (pci_regspec_t);
9681708Sstevel 	for (i = 0; i < rnums; i++) {
9691708Sstevel 		prp = pci_rp + i;
9701708Sstevel 		if (PCI_REG_REG_G(prp->pci_phys_hi) == er[0].ebus_phys_hi) {
9711708Sstevel 			found = 1;
9721708Sstevel 			break;
9731708Sstevel 		}
9741708Sstevel 	}
9751708Sstevel 	if (!found) {
9761708Sstevel 		cmn_err(CE_WARN, "No assigned space for memory range 0.");
9771708Sstevel 		ddi_prop_free(pci_rp);
9781708Sstevel 		return (DDI_FAILURE);
9791708Sstevel 	}
9801708Sstevel 	found = 0;
9811708Sstevel 	er[0].ebus_phys_low = 0;
9821708Sstevel 	er[0].pci_phys_hi = prp->pci_phys_hi;
9831708Sstevel 	er[0].pci_phys_mid = prp->pci_phys_mid;
9841708Sstevel 	er[0].pci_phys_low = prp->pci_phys_low;
9851708Sstevel 	er[0].rng_size = prp->pci_size_low;
9861708Sstevel 
9871708Sstevel 	/*
9881708Sstevel 	 * Create the 2nd mem range in which it operates corresponding
9891708Sstevel 	 * to BAR1
9901708Sstevel 	 */
9911708Sstevel 	er[1].ebus_phys_hi = EBUS_CHILD_PHYS_HI_RANGE;
9921708Sstevel 	for (i = 0; i < rnums; i++) {
9931708Sstevel 		prp = pci_rp + i;
9941708Sstevel 		if (PCI_REG_REG_G(prp->pci_phys_hi) == er[1].ebus_phys_hi) {
9951708Sstevel 			found = 1;
9961708Sstevel 			break;
9971708Sstevel 		}
9981708Sstevel 	}
9991708Sstevel 	if (!found) {
10001708Sstevel 		cmn_err(CE_WARN, "No assigned space for memory range 1.");
10011708Sstevel 		ddi_prop_free(pci_rp);
10021708Sstevel 		return (DDI_FAILURE);
10031708Sstevel 	}
10041708Sstevel 	er[1].ebus_phys_low = 0;
10051708Sstevel 	er[1].pci_phys_hi = prp->pci_phys_hi;
10061708Sstevel 	er[1].pci_phys_mid = prp->pci_phys_mid;
10071708Sstevel 	er[1].pci_phys_low = prp->pci_phys_low;
10081708Sstevel 	er[1].rng_size = prp->pci_size_low;
10091708Sstevel 
10101708Sstevel 	ddi_prop_free(pci_rp);
10111708Sstevel 	length = sizeof (er) / sizeof (int);
10121708Sstevel 	if (ddi_prop_update_int_array(DDI_DEV_T_NONE, dip,
10131708Sstevel 	    "ranges", (int *)er, length) != DDI_PROP_SUCCESS) {
10141708Sstevel 		cmn_err(CE_WARN, "%s%d: Could not create ranges property",
1015*7656SSherry.Moore@Sun.COM 		    ddi_driver_name(dip), ddi_get_instance(dip));
10161708Sstevel 		return (DDI_FAILURE);
10171708Sstevel 	}
10181708Sstevel 	/* The following properties are as defined by PCI 1275 bindings. */
10191708Sstevel 	if (ddi_prop_update_int(DDI_DEV_T_NONE, dip,
10201708Sstevel 	    "#address-cells", 2) != DDI_PROP_SUCCESS)
10211708Sstevel 			return (DDI_FAILURE);
10221708Sstevel 	if (ddi_prop_update_int(DDI_DEV_T_NONE, dip,
10231708Sstevel 	    "#size-cells", 1) != DDI_PROP_SUCCESS)
10241708Sstevel 			return (DDI_FAILURE);
10251708Sstevel 	if (ddi_prop_update_int(DDI_DEV_T_NONE, dip,
10261708Sstevel 	    "#interrupt-cells", 1) != DDI_PROP_SUCCESS)
10271708Sstevel 			return (DDI_FAILURE);
10281708Sstevel 
10291708Sstevel 	imask[0] = 0x1f;
10301708Sstevel 	imask[1] = 0x00ffffff;
10311708Sstevel 	imask[2] = 0x00000003;
10321708Sstevel 	length = sizeof (imask) / sizeof (int);
10331708Sstevel 	if (ddi_prop_update_int_array(DDI_DEV_T_NONE, dip,
10341708Sstevel 	    "interrupt-map-mask", (int *)imask, length) != DDI_PROP_SUCCESS) {
10351708Sstevel 		cmn_err(CE_WARN, "%s%d: Could not update imap mask property",
10361708Sstevel 		    ddi_driver_name(dip), ddi_get_instance(dip));
10371708Sstevel 		return (DDI_FAILURE);
10381708Sstevel 	}
10391708Sstevel 
10401708Sstevel 	return (DDI_SUCCESS);
10411708Sstevel }
10421708Sstevel 
10431708Sstevel /*
10441708Sstevel  * This function takes in the ac-interrupt-map property from the .conf file,
10451708Sstevel  * fills in the 'nodeid' information and then creates the 'interrupt-map'
10461708Sstevel  * property.
10471708Sstevel  */
10481708Sstevel static int
acebus_set_imap(dev_info_t * dip)10491708Sstevel acebus_set_imap(dev_info_t *dip)
10501708Sstevel {
10511708Sstevel 	int *imapp, *timapp, length, num, i, default_ival = 0;
10521708Sstevel 	dev_info_t *tdip = dip;
10531708Sstevel 	int *port_id, imap_ok = 1;
10541708Sstevel 	int ilength;
10551708Sstevel 	int acebus_default_se_imap[5];
10561708Sstevel 
10571708Sstevel 	/*
10581708Sstevel 	 * interrupt-map is specified via .conf file in hotplug mode,
10591708Sstevel 	 * since the child configuration is static.
10601708Sstevel 	 * It could even be hardcoded in the driver.
10611708Sstevel 	 */
10621708Sstevel 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
10631708Sstevel 	    "ac-interrupt-map", (int **)&imapp, (uint_t *)&ilength) !=
10641708Sstevel 	    DDI_PROP_SUCCESS) {
10651708Sstevel 		/* assume default implementation */
10661708Sstevel 		acebus_default_se_imap[0] = 0x14;
10671708Sstevel 		acebus_default_se_imap[1] = 0x400000;
10681708Sstevel 		acebus_default_se_imap[2] = 1;
10691708Sstevel 		acebus_default_se_imap[3] = 0;
10701708Sstevel 		acebus_default_se_imap[4] = 2;
10711708Sstevel 		imapp = acebus_default_se_imap;
10721708Sstevel 		ilength = 5;
10731708Sstevel 		default_ival = 1;
10741708Sstevel 	}
10751708Sstevel 	num = ilength / 5;	/* there are 5 integer cells in our property */
10761708Sstevel 	timapp = imapp;
10771708Sstevel 	for (i = 0; i < num; i++) {
10781708Sstevel 		if (*(timapp+i*5+3) == 0)
10791708Sstevel 			imap_ok = 0;
10801708Sstevel 	}
10811708Sstevel 	if (imap_ok) {
10821708Sstevel 		if (!default_ival)
10831708Sstevel 			ddi_prop_free(imapp);
10841708Sstevel 		return (DDI_SUCCESS);
10851708Sstevel 	}
10861708Sstevel 
10871708Sstevel 	while (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, tdip,
10881708Sstevel 	    DDI_PROP_DONTPASS, "upa-portid", (int **)&port_id,
10891708Sstevel 	    (uint_t *)&length) != DDI_PROP_SUCCESS) {
10901708Sstevel 		tdip = ddi_get_parent(tdip);
10911708Sstevel 		if (tdip == NULL) {
10921708Sstevel 			cmn_err(CE_WARN, "%s%d: Could not get imap parent",
1093*7656SSherry.Moore@Sun.COM 			    ddi_driver_name(dip), ddi_get_instance(dip));
10941708Sstevel 			if (!default_ival)
10951708Sstevel 				ddi_prop_free(imapp);
10961708Sstevel 			return (DDI_FAILURE);
10971708Sstevel 		}
10981708Sstevel 	}
10991708Sstevel 	timapp = imapp;
11001708Sstevel 	for (i = 0; i < num; i++) {
11011708Sstevel 		*(timapp+i*5+3) = ddi_get_nodeid(tdip);
11021708Sstevel 	}
11031708Sstevel 
11041708Sstevel 	if (ddi_prop_update_int_array(DDI_DEV_T_NONE, dip,
1105*7656SSherry.Moore@Sun.COM 	    "interrupt-map", imapp, ilength) != DDI_PROP_SUCCESS) {
11061708Sstevel 		cmn_err(CE_WARN, "%s%d: Could not update AC imap property",
1107*7656SSherry.Moore@Sun.COM 		    ddi_driver_name(dip), ddi_get_instance(dip));
11081708Sstevel 		if (!default_ival)
11091708Sstevel 			ddi_prop_free(imapp);
11101708Sstevel 		return (DDI_FAILURE);
11111708Sstevel 	}
11121708Sstevel 	if (!default_ival)
11131708Sstevel 		ddi_prop_free(imapp);
11141708Sstevel 	return (DDI_SUCCESS);
11151708Sstevel }
11161708Sstevel #endif	/* ACEBUS_HOTPLUG */
1117