xref: /titanic_51/usr/src/uts/sun4u/montecarlo/io/acebus.c (revision cd21e7c548ae2a3b5e522244bf798f2a6b4ba02d)
103831d35Sstevel /*
203831d35Sstevel  * CDDL HEADER START
303831d35Sstevel  *
403831d35Sstevel  * The contents of this file are subject to the terms of the
503831d35Sstevel  * Common Development and Distribution License (the "License").
603831d35Sstevel  * You may not use this file except in compliance with the License.
703831d35Sstevel  *
803831d35Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
903831d35Sstevel  * or http://www.opensolaris.org/os/licensing.
1003831d35Sstevel  * See the License for the specific language governing permissions
1103831d35Sstevel  * and limitations under the License.
1203831d35Sstevel  *
1303831d35Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
1403831d35Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1503831d35Sstevel  * If applicable, add the following below this CDDL HEADER, with the
1603831d35Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
1703831d35Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
1803831d35Sstevel  *
1903831d35Sstevel  * CDDL HEADER END
2003831d35Sstevel  */
2103831d35Sstevel 
2203831d35Sstevel /*
2319397407SSherry Moore  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
2403831d35Sstevel  * Use is subject to license terms.
2503831d35Sstevel  */
26*cd21e7c5SGarrett D'Amore /*
27*cd21e7c5SGarrett D'Amore  * Copyright 2012 Garrett D'Amore <garrett@damore.org>.  All rights reserved.
28*cd21e7c5SGarrett D'Amore  */
2903831d35Sstevel 
3003831d35Sstevel 
3103831d35Sstevel #include <sys/types.h>
3203831d35Sstevel #include <sys/conf.h>
3303831d35Sstevel #include <sys/ddi.h>
3403831d35Sstevel #include <sys/sunddi.h>
3503831d35Sstevel #include <sys/ddi_impldefs.h>
3603831d35Sstevel #include <sys/ddi_subrdefs.h>
3703831d35Sstevel #include <sys/pci.h>
3803831d35Sstevel #include <sys/pci/pci_nexus.h>
3903831d35Sstevel #include <sys/autoconf.h>
4003831d35Sstevel #include <sys/cmn_err.h>
4103831d35Sstevel #include <sys/errno.h>
4203831d35Sstevel #include <sys/kmem.h>
4303831d35Sstevel #include <sys/debug.h>
4403831d35Sstevel #include <sys/sysmacros.h>
4503831d35Sstevel #include <sys/acebus.h>
4603831d35Sstevel 
4703831d35Sstevel #ifdef DEBUG
4803831d35Sstevel static uint_t acebus_debug_flags = 0;
4903831d35Sstevel #endif
5003831d35Sstevel 
5103831d35Sstevel /*
5203831d35Sstevel  * The values of the following variables are used to initialize
5303831d35Sstevel  * the cache line size and latency timer registers in the ebus
5403831d35Sstevel  * configuration header.  Variables are used instead of constants
5503831d35Sstevel  * to allow tuning from the /etc/system file.
5603831d35Sstevel  */
5703831d35Sstevel static uint8_t acebus_cache_line_size = 0x10;	/* 64 bytes */
5803831d35Sstevel static uint8_t acebus_latency_timer = 0x40;	/* 64 PCI cycles */
5903831d35Sstevel 
6003831d35Sstevel /*
6103831d35Sstevel  * function prototypes for bus ops routines:
6203831d35Sstevel  */
6303831d35Sstevel static int
6403831d35Sstevel acebus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
6503831d35Sstevel 	off_t offset, off_t len, caddr_t *addrp);
6603831d35Sstevel static int
6703831d35Sstevel acebus_ctlops(dev_info_t *dip, dev_info_t *rdip,
6803831d35Sstevel 	ddi_ctl_enum_t op, void *arg, void *result);
6903831d35Sstevel static int
7003831d35Sstevel acebus_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
7103831d35Sstevel     ddi_intr_handle_impl_t *hdlp, void *result);
7203831d35Sstevel 
7303831d35Sstevel /*
7403831d35Sstevel  * function prototypes for dev ops routines:
7503831d35Sstevel  */
7603831d35Sstevel static int acebus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
7703831d35Sstevel static int acebus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
7803831d35Sstevel 
7903831d35Sstevel /*
8003831d35Sstevel  * general function prototypes:
8103831d35Sstevel  */
8203831d35Sstevel static int acebus_config(ebus_devstate_t *ebus_p);
8303831d35Sstevel static int acebus_apply_range(ebus_devstate_t *ebus_p, dev_info_t *rdip,
8403831d35Sstevel     ebus_regspec_t *ebus_rp, pci_regspec_t *rp);
8503831d35Sstevel static int acebus_get_ranges_prop(ebus_devstate_t *ebus_p);
8603831d35Sstevel #ifdef	ACEBUS_HOTPLUG
8703831d35Sstevel static int acebus_update_props(ebus_devstate_t *ebus_p);
8803831d35Sstevel static int acebus_set_imap(dev_info_t *dip);
8903831d35Sstevel #endif
9003831d35Sstevel 
9103831d35Sstevel #define	getprop(dip, name, addr, intp)		\
9203831d35Sstevel 		ddi_getlongprop(DDI_DEV_T_ANY, (dip), DDI_PROP_DONTPASS, \
9303831d35Sstevel 				(name), (caddr_t)(addr), (intp))
9403831d35Sstevel 
9503831d35Sstevel /*
9603831d35Sstevel  * bus ops and dev ops structures:
9703831d35Sstevel  */
9803831d35Sstevel static struct bus_ops acebus_bus_ops = {
9903831d35Sstevel 	BUSO_REV,
10003831d35Sstevel 	acebus_map,
10103831d35Sstevel 	NULL,
10203831d35Sstevel 	NULL,
10303831d35Sstevel 	NULL,
10403831d35Sstevel 	i_ddi_map_fault,
105*cd21e7c5SGarrett D'Amore 	NULL,
10603831d35Sstevel 	ddi_dma_allochdl,
10703831d35Sstevel 	ddi_dma_freehdl,
10803831d35Sstevel 	ddi_dma_bindhdl,
10903831d35Sstevel 	ddi_dma_unbindhdl,
11003831d35Sstevel 	ddi_dma_flush,
11103831d35Sstevel 	ddi_dma_win,
11203831d35Sstevel 	ddi_dma_mctl,
11303831d35Sstevel 	acebus_ctlops,
11403831d35Sstevel 	ddi_bus_prop_op,
11503831d35Sstevel 	0,				/* (*bus_get_eventcookie)();	*/
11603831d35Sstevel 	0,				/* (*bus_add_eventcall)();	*/
11703831d35Sstevel 	0,				/* (*bus_remove_eventcall)();	*/
11803831d35Sstevel 	0,				/* (*bus_post_event)();		*/
11903831d35Sstevel 	0,				/* (*bus_intr_ctl)();		*/
12003831d35Sstevel 	NULL,				/* (*bus_config)();		*/
12103831d35Sstevel 	NULL,				/* (*bus_unconfig)();		*/
12203831d35Sstevel 	NULL,				/* (*bus_fm_init)();		*/
12303831d35Sstevel 	NULL,				/* (*bus_fm_fini)();		*/
12403831d35Sstevel 	NULL,				/* (*bus_fm_access_enter)();	*/
12503831d35Sstevel 	NULL,				/* (*bus_fm_access_fini)();	*/
12603831d35Sstevel 	NULL,				/* (*bus_power)();		*/
12703831d35Sstevel 	acebus_intr_ops			/* (*bus_intr_op)();		*/
12803831d35Sstevel };
12903831d35Sstevel 
13003831d35Sstevel static struct dev_ops acebus_ops = {
13103831d35Sstevel 	DEVO_REV,
13203831d35Sstevel 	0,
13303831d35Sstevel 	ddi_no_info,
13403831d35Sstevel 	nulldev,
13503831d35Sstevel 	nulldev,
13603831d35Sstevel 	acebus_attach,
13703831d35Sstevel 	acebus_detach,
13803831d35Sstevel 	nodev,
13903831d35Sstevel 	(struct cb_ops *)0,
14019397407SSherry Moore 	&acebus_bus_ops,
14119397407SSherry Moore 	NULL,
14219397407SSherry Moore 	ddi_quiesce_not_supported,	/* devo_quiesce */
14303831d35Sstevel };
14403831d35Sstevel 
14503831d35Sstevel /*
14603831d35Sstevel  * module definitions:
14703831d35Sstevel  */
14803831d35Sstevel #include <sys/modctl.h>
14903831d35Sstevel extern struct mod_ops mod_driverops;
15003831d35Sstevel 
15103831d35Sstevel static struct modldrv modldrv = {
15203831d35Sstevel 	&mod_driverops, 	/* Type of module.  This one is a driver */
15319397407SSherry Moore 	"Alarm Card ebus nexus",	/* Name of module. */
15403831d35Sstevel 	&acebus_ops,		/* driver ops */
15503831d35Sstevel };
15603831d35Sstevel 
15703831d35Sstevel static struct modlinkage modlinkage = {
15803831d35Sstevel 	MODREV_1, (void *)&modldrv, NULL
15903831d35Sstevel };
16003831d35Sstevel 
16103831d35Sstevel /*
16203831d35Sstevel  * driver global data:
16303831d35Sstevel  */
16403831d35Sstevel static void *per_acebus_state;		/* per-ebus soft state pointer */
16503831d35Sstevel 
16603831d35Sstevel 
16703831d35Sstevel int
16803831d35Sstevel _init(void)
16903831d35Sstevel {
17003831d35Sstevel 	int e;
17103831d35Sstevel 
17203831d35Sstevel 	/*
17303831d35Sstevel 	 * Initialize per-ebus soft state pointer.
17403831d35Sstevel 	 */
17503831d35Sstevel 	e = ddi_soft_state_init(&per_acebus_state, sizeof (ebus_devstate_t), 1);
17603831d35Sstevel 	if (e != 0)
17703831d35Sstevel 		return (e);
17803831d35Sstevel 
17903831d35Sstevel 	/*
18003831d35Sstevel 	 * Install the module.
18103831d35Sstevel 	 */
18203831d35Sstevel 	e = mod_install(&modlinkage);
18303831d35Sstevel 	if (e != 0)
18403831d35Sstevel 		ddi_soft_state_fini(&per_acebus_state);
18503831d35Sstevel 	return (e);
18603831d35Sstevel }
18703831d35Sstevel 
18803831d35Sstevel int
18903831d35Sstevel _fini(void)
19003831d35Sstevel {
19103831d35Sstevel 	int e;
19203831d35Sstevel 
19303831d35Sstevel 	/*
19403831d35Sstevel 	 * Remove the module.
19503831d35Sstevel 	 */
19603831d35Sstevel 	e = mod_remove(&modlinkage);
19703831d35Sstevel 	if (e != 0)
19803831d35Sstevel 		return (e);
19903831d35Sstevel 
20003831d35Sstevel 	/*
20103831d35Sstevel 	 * Free the soft state info.
20203831d35Sstevel 	 */
20303831d35Sstevel 	ddi_soft_state_fini(&per_acebus_state);
20403831d35Sstevel 	return (e);
20503831d35Sstevel }
20603831d35Sstevel 
20703831d35Sstevel int
20803831d35Sstevel _info(struct modinfo *modinfop)
20903831d35Sstevel {
21003831d35Sstevel 	return (mod_info(&modlinkage, modinfop));
21103831d35Sstevel }
21203831d35Sstevel 
21303831d35Sstevel /* device driver entry points */
21403831d35Sstevel 
21503831d35Sstevel /*
21603831d35Sstevel  * attach entry point:
21703831d35Sstevel  *
21803831d35Sstevel  * normal attach:
21903831d35Sstevel  *
22003831d35Sstevel  *	create soft state structure (dip, reg, nreg and state fields)
22103831d35Sstevel  *	map in configuration header
22203831d35Sstevel  *	make sure device is properly configured
22303831d35Sstevel  *	report device
22403831d35Sstevel  */
22503831d35Sstevel static int
22603831d35Sstevel acebus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
22703831d35Sstevel {
22803831d35Sstevel 	ebus_devstate_t *ebus_p;	/* per ebus state pointer */
22903831d35Sstevel 	int instance;
23003831d35Sstevel 
23103831d35Sstevel 	DBG1(D_ATTACH, NULL, "dip=%x\n", dip);
23203831d35Sstevel 	switch (cmd) {
23303831d35Sstevel 	case DDI_ATTACH:
23403831d35Sstevel 
23503831d35Sstevel 		/*
23603831d35Sstevel 		 * Allocate soft state for this instance.
23703831d35Sstevel 		 */
23803831d35Sstevel 		instance = ddi_get_instance(dip);
23903831d35Sstevel 		if (ddi_soft_state_zalloc(per_acebus_state, instance)
24003831d35Sstevel 		    != DDI_SUCCESS) {
24103831d35Sstevel 			DBG(D_ATTACH, NULL, "failed to alloc soft state\n");
24203831d35Sstevel 			return (DDI_FAILURE);
24303831d35Sstevel 		}
24403831d35Sstevel 		ebus_p = get_acebus_soft_state(instance);
24503831d35Sstevel 		ebus_p->dip = dip;
24603831d35Sstevel 
24703831d35Sstevel 		/*
24803831d35Sstevel 		 * Make sure the master enable and memory access enable
24903831d35Sstevel 		 * bits are set in the config command register.
25003831d35Sstevel 		 */
25103831d35Sstevel 		if (!acebus_config(ebus_p)) {
25203831d35Sstevel 			free_acebus_soft_state(instance);
25303831d35Sstevel 			return (DDI_FAILURE);
25403831d35Sstevel 		}
25503831d35Sstevel 
25603831d35Sstevel 		(void) ddi_prop_create(DDI_DEV_T_NONE, dip,
25703831d35Sstevel 		    DDI_PROP_CANSLEEP, "no-dma-interrupt-sync", NULL, 0);
25803831d35Sstevel 		/* Get our ranges property for mapping child registers. */
25903831d35Sstevel 		if (acebus_get_ranges_prop(ebus_p) != DDI_SUCCESS) {
26003831d35Sstevel 			free_acebus_soft_state(instance);
26103831d35Sstevel 			return (DDI_FAILURE);
26203831d35Sstevel 		}
26303831d35Sstevel 
26403831d35Sstevel 		/*
26503831d35Sstevel 		 * Make the state as attached and report the device.
26603831d35Sstevel 		 */
26703831d35Sstevel 		ebus_p->state = ATTACHED;
26803831d35Sstevel 		ddi_report_dev(dip);
26903831d35Sstevel 		DBG(D_ATTACH, ebus_p, "returning\n");
27003831d35Sstevel 		return (DDI_SUCCESS);
27103831d35Sstevel 
27203831d35Sstevel 	case DDI_RESUME:
27303831d35Sstevel 
27403831d35Sstevel 		instance = ddi_get_instance(dip);
27503831d35Sstevel 		ebus_p = get_acebus_soft_state(instance);
27603831d35Sstevel 
27703831d35Sstevel 		/*
27803831d35Sstevel 		 * Make sure the master enable and memory access enable
27903831d35Sstevel 		 * bits are set in the config command register.
28003831d35Sstevel 		 */
28103831d35Sstevel 		if (!acebus_config(ebus_p)) {
28203831d35Sstevel 			free_acebus_soft_state(instance);
28303831d35Sstevel 			return (DDI_FAILURE);
28403831d35Sstevel 		}
28503831d35Sstevel 
28603831d35Sstevel 		ebus_p->state = RESUMED;
28703831d35Sstevel 		return (DDI_SUCCESS);
28803831d35Sstevel 	}
28903831d35Sstevel 	return (DDI_FAILURE);
29003831d35Sstevel }
29103831d35Sstevel 
29203831d35Sstevel /*
29303831d35Sstevel  * detach entry point:
29403831d35Sstevel  */
29503831d35Sstevel static int
29603831d35Sstevel acebus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
29703831d35Sstevel {
29803831d35Sstevel 	int instance = ddi_get_instance(dip);
29903831d35Sstevel 	ebus_devstate_t *ebus_p = get_acebus_soft_state(instance);
30003831d35Sstevel 
30103831d35Sstevel 	switch (cmd) {
30203831d35Sstevel 	case DDI_DETACH:
30303831d35Sstevel 		DBG1(D_DETACH, ebus_p, "DDI_DETACH dip=%p\n", dip);
30403831d35Sstevel 		ddi_prop_remove_all(dip);
30503831d35Sstevel 		kmem_free(ebus_p->rangep, ebus_p->range_cnt *
30603831d35Sstevel 		    sizeof (struct ebus_pci_rangespec));
30703831d35Sstevel 		free_acebus_soft_state(instance);
30803831d35Sstevel 		return (DDI_SUCCESS);
30903831d35Sstevel 
31003831d35Sstevel 	case DDI_SUSPEND:
31103831d35Sstevel 		DBG1(D_DETACH, ebus_p, "DDI_SUSPEND dip=%p\n", dip);
31203831d35Sstevel 		ebus_p->state = SUSPENDED;
31303831d35Sstevel 		return (DDI_SUCCESS);
31403831d35Sstevel 	}
31503831d35Sstevel 	return (DDI_FAILURE);
31603831d35Sstevel }
31703831d35Sstevel 
31803831d35Sstevel 
31903831d35Sstevel static int
32003831d35Sstevel acebus_get_ranges_prop(ebus_devstate_t *ebus_p)
32103831d35Sstevel {
32203831d35Sstevel 	struct ebus_pci_rangespec *rangep;
32303831d35Sstevel 	int nrange, range_len;
32403831d35Sstevel 
32503831d35Sstevel 	if (ddi_getlongprop(DDI_DEV_T_ANY, ebus_p->dip, DDI_PROP_DONTPASS,
32603831d35Sstevel 	    "ranges", (caddr_t)&rangep, &range_len) != DDI_SUCCESS) {
32703831d35Sstevel 
32803831d35Sstevel 		cmn_err(CE_WARN, "%s%d: can't get ranges property",
32903831d35Sstevel 		    ddi_get_name(ebus_p->dip), ddi_get_instance(ebus_p->dip));
33003831d35Sstevel 		return (DDI_ME_REGSPEC_RANGE);
33103831d35Sstevel 	}
33203831d35Sstevel 
33303831d35Sstevel 	nrange = range_len / sizeof (struct ebus_pci_rangespec);
33403831d35Sstevel 
33503831d35Sstevel 	if (nrange == 0)  {
33603831d35Sstevel 		kmem_free(rangep, range_len);
33703831d35Sstevel 		return (DDI_FAILURE);
33803831d35Sstevel 	}
33903831d35Sstevel 
34003831d35Sstevel #ifdef	DEBUG
34119397407SSherry Moore 	{
34203831d35Sstevel 		int i;
34303831d35Sstevel 
34403831d35Sstevel 		for (i = 0; i < nrange; i++) {
34519397407SSherry Moore 			DBG5(D_MAP, ebus_p,
34619397407SSherry Moore 			    "ebus range addr 0x%x.0x%x PCI range "
34703831d35Sstevel 			    "addr 0x%x.0x%x.0x%x ", rangep[i].ebus_phys_hi,
34803831d35Sstevel 			    rangep[i].ebus_phys_low, rangep[i].pci_phys_hi,
34903831d35Sstevel 			    rangep[i].pci_phys_mid, rangep[i].pci_phys_low);
35003831d35Sstevel 			DBG1(D_MAP, ebus_p, "Size 0x%x\n", rangep[i].rng_size);
35103831d35Sstevel 		}
35203831d35Sstevel 	}
35303831d35Sstevel #endif /* DEBUG */
35403831d35Sstevel 
35503831d35Sstevel 	ebus_p->rangep = rangep;
35603831d35Sstevel 	ebus_p->range_cnt = nrange;
35703831d35Sstevel 
35803831d35Sstevel 	return (DDI_SUCCESS);
35903831d35Sstevel }
36003831d35Sstevel 
36103831d35Sstevel 
36203831d35Sstevel /* bus driver entry points */
36303831d35Sstevel 
36403831d35Sstevel /*
36503831d35Sstevel  * bus map entry point:
36603831d35Sstevel  *
36703831d35Sstevel  * 	if map request is for an rnumber
36803831d35Sstevel  *		get the corresponding regspec from device node
36903831d35Sstevel  * 	build a new regspec in our parent's format
37003831d35Sstevel  *	build a new map_req with the new regspec
37103831d35Sstevel  *	call up the tree to complete the mapping
37203831d35Sstevel  */
37303831d35Sstevel static int
37403831d35Sstevel acebus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
37503831d35Sstevel 	off_t off, off_t len, caddr_t *addrp)
37603831d35Sstevel {
37703831d35Sstevel 	ebus_devstate_t *ebus_p = get_acebus_soft_state(ddi_get_instance(dip));
37803831d35Sstevel 	ebus_regspec_t *ebus_rp, *ebus_regs;
37903831d35Sstevel 	pci_regspec_t pci_reg;
38003831d35Sstevel 	ddi_map_req_t p_map_request;
38103831d35Sstevel 	int rnumber, i, n;
38203831d35Sstevel 	int rval = DDI_SUCCESS;
38303831d35Sstevel 
38403831d35Sstevel 	/*
38503831d35Sstevel 	 * Handle the mapping according to its type.
38603831d35Sstevel 	 */
38703831d35Sstevel 	DBG4(D_MAP, ebus_p, "rdip=%s%d: off=%x len=%x\n",
38803831d35Sstevel 	    ddi_get_name(rdip), ddi_get_instance(rdip), off, len);
38903831d35Sstevel 	switch (mp->map_type) {
39003831d35Sstevel 	case DDI_MT_REGSPEC:
39103831d35Sstevel 
39203831d35Sstevel 		/*
39303831d35Sstevel 		 * We assume the register specification is in ebus format.
39403831d35Sstevel 		 * We must convert it into a PCI format regspec and pass
39503831d35Sstevel 		 * the request to our parent.
39603831d35Sstevel 		 */
39703831d35Sstevel 		DBG3(D_MAP, ebus_p, "rdip=%s%d: REGSPEC - handlep=%x\n",
39803831d35Sstevel 		    ddi_get_name(rdip), ddi_get_instance(rdip),
39903831d35Sstevel 		    mp->map_handlep);
40003831d35Sstevel 		ebus_rp = (ebus_regspec_t *)mp->map_obj.rp;
40103831d35Sstevel 		break;
40203831d35Sstevel 
40303831d35Sstevel 	case DDI_MT_RNUMBER:
40403831d35Sstevel 
40503831d35Sstevel 		/*
40603831d35Sstevel 		 * Get the "reg" property from the device node and convert
40703831d35Sstevel 		 * it to our parent's format.
40803831d35Sstevel 		 */
40903831d35Sstevel 		rnumber = mp->map_obj.rnumber;
41003831d35Sstevel 		DBG4(D_MAP, ebus_p, "rdip=%s%d: rnumber=%x handlep=%x\n",
41103831d35Sstevel 		    ddi_get_name(rdip), ddi_get_instance(rdip),
41203831d35Sstevel 		    rnumber, mp->map_handlep);
41303831d35Sstevel 
41403831d35Sstevel 		if (getprop(rdip, "reg", &ebus_regs, &i) != DDI_SUCCESS) {
41503831d35Sstevel 			DBG(D_MAP, ebus_p, "can't get reg property\n");
41603831d35Sstevel 			return (DDI_ME_RNUMBER_RANGE);
41703831d35Sstevel 		}
41803831d35Sstevel 		n = i / sizeof (ebus_regspec_t);
41903831d35Sstevel 
42003831d35Sstevel 		if (rnumber < 0 || rnumber >= n) {
42103831d35Sstevel 			DBG(D_MAP, ebus_p, "rnumber out of range\n");
42203831d35Sstevel 			return (DDI_ME_RNUMBER_RANGE);
42303831d35Sstevel 		}
42403831d35Sstevel 		ebus_rp = &ebus_regs[rnumber];
42503831d35Sstevel 		break;
42603831d35Sstevel 
42703831d35Sstevel 	default:
42803831d35Sstevel 		return (DDI_ME_INVAL);
42903831d35Sstevel 
43003831d35Sstevel 	}
43103831d35Sstevel 
43203831d35Sstevel 	/* Adjust our reg property with offset and length */
43303831d35Sstevel 	ebus_rp->addr_low += off;
43403831d35Sstevel 	if (len)
43503831d35Sstevel 		ebus_rp->size = len;
43603831d35Sstevel 
43703831d35Sstevel 	/*
43803831d35Sstevel 	 * Now we have a copy the "reg" entry we're attempting to map.
43903831d35Sstevel 	 * Translate this into our parents PCI address using the ranges
44003831d35Sstevel 	 * property.
44103831d35Sstevel 	 */
44203831d35Sstevel 	rval = acebus_apply_range(ebus_p, rdip, ebus_rp, &pci_reg);
44303831d35Sstevel 
44403831d35Sstevel 	if (mp->map_type == DDI_MT_RNUMBER)
44503831d35Sstevel 		kmem_free((caddr_t)ebus_regs, i);
44603831d35Sstevel 
44703831d35Sstevel 	if (rval != DDI_SUCCESS)
44803831d35Sstevel 		return (rval);
44903831d35Sstevel 
45003831d35Sstevel #ifdef	ACEBUS_HOTPLUG
45103831d35Sstevel 	/*
45203831d35Sstevel 	 * The map operation provides a translated (not a re-assigned, or
45303831d35Sstevel 	 * relocated) ebus address for the child in its address space(range).
45403831d35Sstevel 	 * Ebus address space is relocatible but its child address space
45503831d35Sstevel 	 * is not. As specified by their 'reg' properties, they reside
45603831d35Sstevel 	 * at a fixed offset in their parent's (ebus's) space.
45703831d35Sstevel 	 *
45803831d35Sstevel 	 * By setting this bit, we will not run into HostPCI nexus
45903831d35Sstevel 	 * trying to relocate a translated ebus address (which is already
46003831d35Sstevel 	 * relocated) and failing the operation.
46103831d35Sstevel 	 * The reason for doing this here is that the PCI hotplug configurator
46203831d35Sstevel 	 * always marks the ebus space as relocatible (unlike OBP) and that
46303831d35Sstevel 	 * information is implied for the child too, which is wrong.
46403831d35Sstevel 	 */
46503831d35Sstevel 	pci_reg.pci_phys_hi |= PCI_RELOCAT_B;
46603831d35Sstevel #endif
46703831d35Sstevel #ifdef DEBUG
46803831d35Sstevel 	DBG5(D_MAP, ebus_p, "(%x,%x,%x)(%x,%x)\n",
46903831d35Sstevel 	    pci_reg.pci_phys_hi,
47003831d35Sstevel 	    pci_reg.pci_phys_mid,
47103831d35Sstevel 	    pci_reg.pci_phys_low,
47203831d35Sstevel 	    pci_reg.pci_size_hi,
47303831d35Sstevel 	    pci_reg.pci_size_low);
47403831d35Sstevel #endif
47503831d35Sstevel 
47603831d35Sstevel 	p_map_request = *mp;
47703831d35Sstevel 	p_map_request.map_type = DDI_MT_REGSPEC;
47803831d35Sstevel 	p_map_request.map_obj.rp = (struct regspec *)&pci_reg;
47903831d35Sstevel 	rval = ddi_map(dip, &p_map_request, 0, 0, addrp);
48003831d35Sstevel 	DBG1(D_MAP, ebus_p, "parent returned %x\n", rval);
48103831d35Sstevel 	return (rval);
48203831d35Sstevel }
48303831d35Sstevel 
48403831d35Sstevel 
48503831d35Sstevel static int
48603831d35Sstevel acebus_apply_range(ebus_devstate_t *ebus_p, dev_info_t *rdip,
48703831d35Sstevel     ebus_regspec_t *ebus_rp, pci_regspec_t *rp)
48803831d35Sstevel {
48903831d35Sstevel 	int b;
49003831d35Sstevel 	int rval = DDI_SUCCESS;
49103831d35Sstevel 	struct ebus_pci_rangespec *rangep = ebus_p->rangep;
49203831d35Sstevel 	int nrange = ebus_p->range_cnt;
49303831d35Sstevel 	static const char out_of_range[] =
49403831d35Sstevel 	    "Out of range register specification from device node <%s>";
49503831d35Sstevel 
49603831d35Sstevel 	DBG3(D_MAP, ebus_p, "Range Matching Addr 0x%x.%x size 0x%x\n",
49703831d35Sstevel 	    ebus_rp->addr_hi, ebus_rp->addr_low, ebus_rp->size);
49803831d35Sstevel 
49903831d35Sstevel 	for (b = 0; b < nrange; ++b, ++rangep) {
50003831d35Sstevel 
50103831d35Sstevel 		/* Check for the correct space */
50203831d35Sstevel 		if (ebus_rp->addr_hi == rangep->ebus_phys_hi)
50303831d35Sstevel 			/* See if we fit in this range */
50403831d35Sstevel 			if ((ebus_rp->addr_low >=
50503831d35Sstevel 			    rangep->ebus_phys_low) &&
50603831d35Sstevel 			    ((ebus_rp->addr_low + ebus_rp->size - 1)
50703831d35Sstevel 			    <= (rangep->ebus_phys_low +
50803831d35Sstevel 			    rangep->rng_size - 1))) {
50903831d35Sstevel 				uint_t addr_offset = ebus_rp->addr_low -
51003831d35Sstevel 				    rangep->ebus_phys_low;
51103831d35Sstevel 				/*
51203831d35Sstevel 				 * Use the range entry to translate
51303831d35Sstevel 				 * the EBUS physical address into the
51403831d35Sstevel 				 * parents PCI space.
51503831d35Sstevel 				 */
51603831d35Sstevel 				rp->pci_phys_hi =
51703831d35Sstevel 				    rangep->pci_phys_hi;
51803831d35Sstevel 				rp->pci_phys_mid = rangep->pci_phys_mid;
51903831d35Sstevel 				rp->pci_phys_low =
52003831d35Sstevel 				    rangep->pci_phys_low + addr_offset;
52103831d35Sstevel 				rp->pci_size_hi = 0;
52203831d35Sstevel 				rp->pci_size_low =
52303831d35Sstevel 				    min(ebus_rp->size, (rangep->rng_size -
52403831d35Sstevel 				    addr_offset));
52503831d35Sstevel 
52603831d35Sstevel 				DBG2(D_MAP, ebus_p, "Child hi0x%x lo0x%x ",
52703831d35Sstevel 				    rangep->ebus_phys_hi,
52803831d35Sstevel 				    rangep->ebus_phys_low);
52903831d35Sstevel 				DBG4(D_MAP, ebus_p, "Parent hi0x%x "
53003831d35Sstevel 				    "mid0x%x lo0x%x size 0x%x\n",
53103831d35Sstevel 				    rangep->pci_phys_hi,
53203831d35Sstevel 				    rangep->pci_phys_mid,
53303831d35Sstevel 				    rangep->pci_phys_low,
53403831d35Sstevel 				    rangep->rng_size);
53503831d35Sstevel 
53603831d35Sstevel 				break;
53703831d35Sstevel 			}
53803831d35Sstevel 	}
53903831d35Sstevel 
54003831d35Sstevel 	if (b == nrange)  {
54103831d35Sstevel 		cmn_err(CE_WARN, out_of_range, ddi_get_name(rdip));
54203831d35Sstevel 		return (DDI_ME_REGSPEC_RANGE);
54303831d35Sstevel 	}
54403831d35Sstevel 
54503831d35Sstevel 	return (rval);
54603831d35Sstevel }
54703831d35Sstevel 
54803831d35Sstevel 
54903831d35Sstevel /*
55003831d35Sstevel  * control ops entry point:
55103831d35Sstevel  *
55203831d35Sstevel  * Requests handled completely:
55303831d35Sstevel  *	DDI_CTLOPS_INITCHILD
55403831d35Sstevel  *	DDI_CTLOPS_UNINITCHILD
55503831d35Sstevel  *	DDI_CTLOPS_REPORTDEV
55603831d35Sstevel  *	DDI_CTLOPS_REGSIZE
55703831d35Sstevel  *	DDI_CTLOPS_NREGS
55803831d35Sstevel  *
55903831d35Sstevel  * All others passed to parent.
56003831d35Sstevel  */
56103831d35Sstevel static int
56203831d35Sstevel acebus_ctlops(dev_info_t *dip, dev_info_t *rdip,
56303831d35Sstevel 	ddi_ctl_enum_t op, void *arg, void *result)
56403831d35Sstevel {
56503831d35Sstevel #ifdef DEBUG
56603831d35Sstevel 	ebus_devstate_t *ebus_p = get_acebus_soft_state(ddi_get_instance(dip));
56703831d35Sstevel #endif
56803831d35Sstevel 	ebus_regspec_t *ebus_rp;
56903831d35Sstevel 	int32_t reglen;
57003831d35Sstevel 	int i, n;
57103831d35Sstevel 	char name[10];
57203831d35Sstevel 
57303831d35Sstevel 	switch (op) {
57403831d35Sstevel 	case DDI_CTLOPS_INITCHILD: {
57503831d35Sstevel 		dev_info_t *child = (dev_info_t *)arg;
57603831d35Sstevel 		/*
57703831d35Sstevel 		 * Set the address portion of the node name based on the
57803831d35Sstevel 		 * address/offset.
57903831d35Sstevel 		 */
58003831d35Sstevel 		DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_INITCHILD: rdip=%s%d\n",
58103831d35Sstevel 		    ddi_get_name(child), ddi_get_instance(child));
58203831d35Sstevel 
58303831d35Sstevel 		if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
58403831d35Sstevel 		    "reg", (caddr_t)&ebus_rp, &reglen) != DDI_SUCCESS) {
58503831d35Sstevel 
58603831d35Sstevel 			DBG(D_CTLOPS, ebus_p, "can't get reg property\n");
58703831d35Sstevel 			return (DDI_FAILURE);
58803831d35Sstevel 
58903831d35Sstevel 		}
59003831d35Sstevel 
59103831d35Sstevel 		(void) sprintf(name, "%x,%x", ebus_rp->addr_hi,
59203831d35Sstevel 		    ebus_rp->addr_low);
59303831d35Sstevel 		ddi_set_name_addr(child, name);
59403831d35Sstevel 		kmem_free((caddr_t)ebus_rp, reglen);
59503831d35Sstevel 
59603831d35Sstevel 		ddi_set_parent_data(child, NULL);
59703831d35Sstevel 
59803831d35Sstevel 		return (DDI_SUCCESS);
59903831d35Sstevel 
60003831d35Sstevel 	}
60103831d35Sstevel 
60203831d35Sstevel 	case DDI_CTLOPS_UNINITCHILD:
60303831d35Sstevel 		DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_UNINITCHILD: rdip=%s%d\n",
60403831d35Sstevel 		    ddi_get_name((dev_info_t *)arg),
60503831d35Sstevel 		    ddi_get_instance((dev_info_t *)arg));
60603831d35Sstevel 		ddi_set_name_addr((dev_info_t *)arg, NULL);
60703831d35Sstevel 		ddi_remove_minor_node((dev_info_t *)arg, NULL);
60803831d35Sstevel 		impl_rem_dev_props((dev_info_t *)arg);
60903831d35Sstevel 		return (DDI_SUCCESS);
61003831d35Sstevel 
61103831d35Sstevel 	case DDI_CTLOPS_REPORTDEV:
61203831d35Sstevel 
61303831d35Sstevel 		DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_REPORTDEV: rdip=%s%d\n",
61403831d35Sstevel 		    ddi_get_name(rdip), ddi_get_instance(rdip));
61503831d35Sstevel 		cmn_err(CE_CONT, "?%s%d at %s%d: offset %s\n",
61603831d35Sstevel 		    ddi_driver_name(rdip), ddi_get_instance(rdip),
61703831d35Sstevel 		    ddi_driver_name(dip), ddi_get_instance(dip),
61803831d35Sstevel 		    ddi_get_name_addr(rdip));
61903831d35Sstevel 		return (DDI_SUCCESS);
62003831d35Sstevel 
62103831d35Sstevel 	case DDI_CTLOPS_REGSIZE:
62203831d35Sstevel 
62303831d35Sstevel 		DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_REGSIZE: rdip=%s%d\n",
62403831d35Sstevel 		    ddi_get_name(rdip), ddi_get_instance(rdip));
62503831d35Sstevel 		if (getprop(rdip, "reg", &ebus_rp, &i) != DDI_SUCCESS) {
62603831d35Sstevel 			DBG(D_CTLOPS, ebus_p, "can't get reg property\n");
62703831d35Sstevel 			return (DDI_FAILURE);
62803831d35Sstevel 		}
62903831d35Sstevel 		n = i / sizeof (ebus_regspec_t);
63003831d35Sstevel 		if (*(int *)arg < 0 || *(int *)arg >= n) {
63103831d35Sstevel 			DBG(D_MAP, ebus_p, "rnumber out of range\n");
63203831d35Sstevel 			kmem_free((caddr_t)ebus_rp, i);
63303831d35Sstevel 			return (DDI_FAILURE);
63403831d35Sstevel 		}
63503831d35Sstevel 		*((off_t *)result) = ebus_rp[*(int *)arg].size;
63603831d35Sstevel 		kmem_free((caddr_t)ebus_rp, i);
63703831d35Sstevel 		return (DDI_SUCCESS);
63803831d35Sstevel 
63903831d35Sstevel 	case DDI_CTLOPS_NREGS:
64003831d35Sstevel 
64103831d35Sstevel 		DBG2(D_CTLOPS, ebus_p, "DDI_CTLOPS_NREGS: rdip=%s%d\n",
64203831d35Sstevel 		    ddi_get_name(rdip), ddi_get_instance(rdip));
64303831d35Sstevel 		if (getprop(rdip, "reg", &ebus_rp, &i) != DDI_SUCCESS) {
64403831d35Sstevel 			DBG(D_CTLOPS, ebus_p, "can't get reg property\n");
64503831d35Sstevel 			return (DDI_FAILURE);
64603831d35Sstevel 		}
64703831d35Sstevel 		*((uint_t *)result) = i / sizeof (ebus_regspec_t);
64803831d35Sstevel 		kmem_free((caddr_t)ebus_rp, i);
64903831d35Sstevel 		return (DDI_SUCCESS);
65003831d35Sstevel 	}
65103831d35Sstevel 
65203831d35Sstevel 	/*
65303831d35Sstevel 	 * Now pass the request up to our parent.
65403831d35Sstevel 	 */
65503831d35Sstevel 	DBG2(D_CTLOPS, ebus_p, "passing request to parent: rdip=%s%d\n",
65603831d35Sstevel 	    ddi_get_name(rdip), ddi_get_instance(rdip));
65703831d35Sstevel 	return (ddi_ctlops(dip, rdip, op, arg, result));
65803831d35Sstevel }
65903831d35Sstevel 
66003831d35Sstevel struct ebus_string_to_pil {
66103831d35Sstevel 	int8_t *string;
66203831d35Sstevel 	uint32_t pil;
66303831d35Sstevel };
66403831d35Sstevel 
66503831d35Sstevel static struct ebus_string_to_pil acebus_name_to_pil[] = {{"SUNW,CS4231", 9},
66603831d35Sstevel 						    {"fdthree", 8},
66703831d35Sstevel 						    {"ecpp", 3},
66803831d35Sstevel 						    {"su", 12},
66903831d35Sstevel 						    {"se", 12},
67003831d35Sstevel 						    {"power", 14}};
67103831d35Sstevel 
67203831d35Sstevel static struct ebus_string_to_pil acebus_device_type_to_pil[] = {{"serial", 12},
67303831d35Sstevel 								{"block", 8}};
67403831d35Sstevel 
67503831d35Sstevel static int
67603831d35Sstevel acebus_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
67703831d35Sstevel     ddi_intr_handle_impl_t *hdlp, void *result)
67803831d35Sstevel {
67903831d35Sstevel #ifdef DEBUG
68003831d35Sstevel 	ebus_devstate_t *ebus_p = get_acebus_soft_state(ddi_get_instance(dip));
68103831d35Sstevel #endif
68203831d35Sstevel 	int8_t		*name, *device_type;
68303831d35Sstevel 	int32_t		i, max_children, max_device_types, len;
68403831d35Sstevel 
68503831d35Sstevel 	/*
68603831d35Sstevel 	 * NOTE: These ops below will never be supported in this nexus
68703831d35Sstevel 	 * driver, hence they always return immediately.
68803831d35Sstevel 	 */
68903831d35Sstevel 	switch (intr_op) {
69003831d35Sstevel 	case DDI_INTROP_GETCAP:
69103831d35Sstevel 		*(int *)result = DDI_INTR_FLAG_LEVEL;
69203831d35Sstevel 		return (DDI_SUCCESS);
693fd715b00Srameshc 	case DDI_INTROP_SUPPORTED_TYPES:
694fd715b00Srameshc 		*(int *)result = i_ddi_get_intx_nintrs(rdip) ?
695fd715b00Srameshc 		    DDI_INTR_TYPE_FIXED : 0;
696fd715b00Srameshc 		return (DDI_SUCCESS);
69703831d35Sstevel 	case DDI_INTROP_SETCAP:
69803831d35Sstevel 	case DDI_INTROP_SETMASK:
69903831d35Sstevel 	case DDI_INTROP_CLRMASK:
70003831d35Sstevel 	case DDI_INTROP_GETPENDING:
70103831d35Sstevel 		return (DDI_ENOTSUP);
70203831d35Sstevel 	default:
70303831d35Sstevel 		break;
70403831d35Sstevel 	}
70503831d35Sstevel 
706fd715b00Srameshc 	if (hdlp->ih_pri)
70703831d35Sstevel 		goto done;
70803831d35Sstevel 
70903831d35Sstevel 	/*
71003831d35Sstevel 	 * This is a hack to set the PIL for the devices under ebus.
71103831d35Sstevel 	 * We first look up a device by it's specific name, if we can't
71203831d35Sstevel 	 * match the name, we try and match it's device_type property.
71303831d35Sstevel 	 * Lastly we default a PIL level of 1.
71403831d35Sstevel 	 */
71503831d35Sstevel 	DBG1(D_INTR, ebus_p, "ebus_p %p\n", ebus_p);
71603831d35Sstevel 
71703831d35Sstevel 	name = ddi_get_name(rdip);
71803831d35Sstevel 	max_children = sizeof (acebus_name_to_pil) /
71903831d35Sstevel 	    sizeof (struct ebus_string_to_pil);
72003831d35Sstevel 
72103831d35Sstevel 	for (i = 0; i < max_children; i++) {
72203831d35Sstevel 		if (strcmp(acebus_name_to_pil[i].string, name) == 0) {
72303831d35Sstevel 			DBG2(D_INTR, ebus_p, "child name %s; match PIL %d\n",
72403831d35Sstevel 			    acebus_name_to_pil[i].string,
72503831d35Sstevel 			    acebus_name_to_pil[i].pil);
72603831d35Sstevel 
72703831d35Sstevel 			hdlp->ih_pri = acebus_name_to_pil[i].pil;
72803831d35Sstevel 			goto done;
72903831d35Sstevel 		}
73003831d35Sstevel 	}
73103831d35Sstevel 
73203831d35Sstevel 	if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
73303831d35Sstevel 	    "device_type", (caddr_t)&device_type, &len) == DDI_SUCCESS) {
73403831d35Sstevel 
73503831d35Sstevel 		max_device_types = sizeof (acebus_device_type_to_pil) /
73603831d35Sstevel 		    sizeof (struct ebus_string_to_pil);
73703831d35Sstevel 
73803831d35Sstevel 		for (i = 0; i < max_device_types; i++) {
73903831d35Sstevel 			if (strcmp(acebus_device_type_to_pil[i].string,
74003831d35Sstevel 			    device_type) == 0) {
74103831d35Sstevel 				DBG2(D_INTR, ebus_p,
74203831d35Sstevel 				    "Device type %s; match PIL %d\n",
74303831d35Sstevel 				    acebus_device_type_to_pil[i].string,
74403831d35Sstevel 				    acebus_device_type_to_pil[i].pil);
74503831d35Sstevel 
74603831d35Sstevel 				hdlp->ih_pri = acebus_device_type_to_pil[i].pil;
74703831d35Sstevel 				break;
74803831d35Sstevel 			}
74903831d35Sstevel 		}
75003831d35Sstevel 
75103831d35Sstevel 		kmem_free(device_type, len);
75203831d35Sstevel 	}
75303831d35Sstevel 
75403831d35Sstevel 	/*
75503831d35Sstevel 	 * If we get here, we need to set a default value
75603831d35Sstevel 	 * for the PIL.
75703831d35Sstevel 	 */
75803831d35Sstevel 	if (hdlp->ih_pri == 0) {
75903831d35Sstevel 		hdlp->ih_pri = 1;
76003831d35Sstevel 		cmn_err(CE_WARN, "%s%d assigning default interrupt level %d "
76103831d35Sstevel 		    "for device %s%d", ddi_driver_name(dip),
76203831d35Sstevel 		    ddi_get_instance(dip), hdlp->ih_pri, ddi_driver_name(rdip),
76303831d35Sstevel 		    ddi_get_instance(rdip));
76403831d35Sstevel 	}
76503831d35Sstevel 
76603831d35Sstevel done:
76703831d35Sstevel 	/* Pass up the request to our parent. */
76803831d35Sstevel 	return (i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result));
76903831d35Sstevel }
77003831d35Sstevel 
77103831d35Sstevel 
77203831d35Sstevel static int
77303831d35Sstevel acebus_config(ebus_devstate_t *ebus_p)
77403831d35Sstevel {
77503831d35Sstevel 	ddi_acc_handle_t conf_handle;
77603831d35Sstevel 	uint16_t comm;
77703831d35Sstevel #ifdef	ACEBUS_HOTPLUG
77803831d35Sstevel 	int tcr_reg;
77903831d35Sstevel 	caddr_t csr_io;
78003831d35Sstevel 	ddi_device_acc_attr_t csr_attr = {   /* CSR map attributes */
78103831d35Sstevel 		DDI_DEVICE_ATTR_V0,
78203831d35Sstevel 		DDI_STRUCTURE_LE_ACC,
78303831d35Sstevel 		DDI_STRICTORDER_ACC
78403831d35Sstevel 	};
78503831d35Sstevel 	ddi_acc_handle_t csr_handle;
78603831d35Sstevel #endif
78703831d35Sstevel 
78803831d35Sstevel 	/*
78903831d35Sstevel 	 * Make sure the master enable and memory access enable
79003831d35Sstevel 	 * bits are set in the config command register.
79103831d35Sstevel 	 */
79203831d35Sstevel 	if (pci_config_setup(ebus_p->dip, &conf_handle) != DDI_SUCCESS)
79303831d35Sstevel 		return (0);
79403831d35Sstevel 
79503831d35Sstevel 	comm = pci_config_get16(conf_handle, PCI_CONF_COMM),
79603831d35Sstevel #ifdef DEBUG
79703831d35Sstevel 	    DBG1(D_ATTACH, ebus_p, "command register was 0x%x\n", comm);
79803831d35Sstevel #endif
79903831d35Sstevel 	comm |= (PCI_COMM_ME|PCI_COMM_MAE|PCI_COMM_SERR_ENABLE|
80003831d35Sstevel 	    PCI_COMM_PARITY_DETECT);
80103831d35Sstevel 	pci_config_put16(conf_handle, PCI_CONF_COMM, comm),
80203831d35Sstevel #ifdef DEBUG
80303831d35Sstevel 	    DBG1(D_MAP, ebus_p, "command register is now 0x%x\n",
80403831d35Sstevel 	    pci_config_get16(conf_handle, PCI_CONF_COMM));
80503831d35Sstevel #endif
80603831d35Sstevel 	pci_config_put8(conf_handle, PCI_CONF_CACHE_LINESZ,
80703831d35Sstevel 	    (uchar_t)acebus_cache_line_size);
80803831d35Sstevel 	pci_config_put8(conf_handle, PCI_CONF_LATENCY_TIMER,
80903831d35Sstevel 	    (uchar_t)acebus_latency_timer);
81003831d35Sstevel 	pci_config_teardown(&conf_handle);
81103831d35Sstevel 
81203831d35Sstevel #ifdef	ACEBUS_HOTPLUG
81303831d35Sstevel 	if (acebus_update_props(ebus_p) != DDI_SUCCESS) {
81403831d35Sstevel 		cmn_err(CE_WARN, "%s%d: Could not update special properties.",
81503831d35Sstevel 		    ddi_driver_name(ebus_p->dip),
81603831d35Sstevel 		    ddi_get_instance(ebus_p->dip));
81703831d35Sstevel 		return (0);
81803831d35Sstevel 	}
81903831d35Sstevel 
82003831d35Sstevel 	if (ddi_regs_map_setup(ebus_p->dip, CSR_IO_RINDEX,
82103831d35Sstevel 	    (caddr_t *)&csr_io, 0, CSR_SIZE, &csr_attr,
82203831d35Sstevel 	    &csr_handle) != DDI_SUCCESS) {
82303831d35Sstevel 		cmn_err(CE_WARN, "%s%d: Could not map Ebus CSR.",
82403831d35Sstevel 		    ddi_driver_name(ebus_p->dip),
82503831d35Sstevel 		    ddi_get_instance(ebus_p->dip));
82603831d35Sstevel 	}
82703831d35Sstevel #ifdef	DEBUG
82803831d35Sstevel 	if (acebus_debug_flags) {
82903831d35Sstevel 		DBG3(D_ATTACH, ebus_p, "tcr[123] = %x,%x,%x\n",
83003831d35Sstevel 		    ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
83103831d35Sstevel 		    TCR1_OFF)),
83203831d35Sstevel 		    ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
83303831d35Sstevel 		    TCR2_OFF)),
83403831d35Sstevel 		    ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
83503831d35Sstevel 		    TCR3_OFF)));
83603831d35Sstevel 		DBG2(D_ATTACH, ebus_p, "pmd-aux=%x, freq-aux=%x\n",
83703831d35Sstevel 		    ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
83803831d35Sstevel 		    PMD_AUX_OFF)),
83903831d35Sstevel 		    ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
84003831d35Sstevel 		    FREQ_AUX_OFF)));
84103831d35Sstevel #ifdef ACEBUS_DEBUG
84203831d35Sstevel 		for (comm = 0; comm < 4; comm++)
84303831d35Sstevel 			prom_printf("dcsr%d=%x, dacr%d=%x, dbcr%d=%x\n", comm,
84403831d35Sstevel 			    ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
84503831d35Sstevel 			    0x700000+(0x2000*comm))), comm,
84603831d35Sstevel 			    ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
84703831d35Sstevel 			    0x700000+(0x2000*comm)+4)), comm,
84803831d35Sstevel 			    ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
84903831d35Sstevel 			    0x700000+(0x2000*comm)+8)));
85003831d35Sstevel #endif
85103831d35Sstevel 	} /* acebus_debug_flags */
85203831d35Sstevel #endif
85303831d35Sstevel 	/* If TCR registers are not initialized, initialize them here */
85403831d35Sstevel 	tcr_reg = ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
85503831d35Sstevel 	    TCR1_OFF));
85603831d35Sstevel 	if ((tcr_reg == 0) || (tcr_reg == -1))
85703831d35Sstevel 		ddi_put32(csr_handle, (uint32_t *)((caddr_t)csr_io + TCR1_OFF),
85803831d35Sstevel 		    TCR1_REGVAL);
85903831d35Sstevel 	tcr_reg = ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
86003831d35Sstevel 	    TCR2_OFF));
86103831d35Sstevel 	if ((tcr_reg == 0) || (tcr_reg == -1))
86203831d35Sstevel 		ddi_put32(csr_handle, (uint32_t *)((caddr_t)csr_io + TCR2_OFF),
86303831d35Sstevel 		    TCR2_REGVAL);
86403831d35Sstevel 	tcr_reg = ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
86503831d35Sstevel 	    TCR3_OFF));
86603831d35Sstevel 	if ((tcr_reg == 0) || (tcr_reg == -1))
86703831d35Sstevel 		ddi_put32(csr_handle, (uint32_t *)((caddr_t)csr_io + TCR3_OFF),
86803831d35Sstevel 		    TCR3_REGVAL);
86903831d35Sstevel #ifdef	DEBUG
87003831d35Sstevel 	if (acebus_debug_flags) {
87103831d35Sstevel 		DBG3(D_ATTACH, ebus_p, "wrote tcr[123] = %x,%x,%x\n",
87203831d35Sstevel 		    ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
87303831d35Sstevel 		    TCR1_OFF)),
87403831d35Sstevel 		    ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
87503831d35Sstevel 		    TCR2_OFF)),
87603831d35Sstevel 		    ddi_get32(csr_handle, (uint32_t *)((caddr_t)csr_io +
87703831d35Sstevel 		    TCR3_OFF)));
87803831d35Sstevel 	}
87903831d35Sstevel #endif
88003831d35Sstevel 
88103831d35Sstevel 	ddi_regs_map_free(&csr_handle);
88203831d35Sstevel #endif	/* ACEBUS_HOTPLUG */
88303831d35Sstevel 	return (1);	/* return success */
88403831d35Sstevel }
88503831d35Sstevel 
88603831d35Sstevel #ifdef DEBUG
88703831d35Sstevel extern void prom_printf(const char *, ...);
88803831d35Sstevel 
88903831d35Sstevel static void
89003831d35Sstevel acebus_debug(uint_t flag, ebus_devstate_t *ebus_p, char *fmt,
89103831d35Sstevel 	uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5)
89203831d35Sstevel {
89303831d35Sstevel 	char *s;
89403831d35Sstevel 
89503831d35Sstevel 	if (acebus_debug_flags & flag) {
89603831d35Sstevel 		switch (flag) {
89703831d35Sstevel 		case D_ATTACH:
89803831d35Sstevel 			s = "attach"; break;
89903831d35Sstevel 		case D_DETACH:
90003831d35Sstevel 			s = "detach"; break;
90103831d35Sstevel 		case D_MAP:
90203831d35Sstevel 			s = "map"; break;
90303831d35Sstevel 		case D_CTLOPS:
90403831d35Sstevel 			s = "ctlops"; break;
90503831d35Sstevel 		case D_INTR:
90603831d35Sstevel 			s = "intr"; break;
90703831d35Sstevel 		}
90803831d35Sstevel 		if (ebus_p)
90903831d35Sstevel 			cmn_err(CE_CONT, "%s%d: %s: ",
91003831d35Sstevel 			    ddi_get_name(ebus_p->dip),
91103831d35Sstevel 			    ddi_get_instance(ebus_p->dip), s);
91203831d35Sstevel 		else
91303831d35Sstevel 			cmn_err(CE_CONT, "ebus: ");
91403831d35Sstevel 		cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5);
91503831d35Sstevel 	}
91603831d35Sstevel }
91703831d35Sstevel #endif
91803831d35Sstevel 
91903831d35Sstevel #ifdef	ACEBUS_HOTPLUG
92003831d35Sstevel #define	EBUS_CHILD_PHYS_LOW_RANGE	0x10
92103831d35Sstevel #define	EBUS_CHILD_PHYS_HI_RANGE	0x14
92203831d35Sstevel 
92303831d35Sstevel static int
92403831d35Sstevel acebus_update_props(ebus_devstate_t *ebus_p)
92503831d35Sstevel {
92603831d35Sstevel 	dev_info_t *dip = ebus_p->dip;
92703831d35Sstevel 	struct ebus_pci_rangespec er[2], *erp;
92803831d35Sstevel 	pci_regspec_t *pci_rp, *prp;
92903831d35Sstevel 	int length, rnums, imask[3], i, found = 0;
93003831d35Sstevel 
93103831d35Sstevel 	/*
93203831d35Sstevel 	 * If "ranges" property is found, then the device is initialized
93303831d35Sstevel 	 * by OBP, hence simply return.
93403831d35Sstevel 	 * Otherwise we create all the properties here.
93503831d35Sstevel 	 */
93603831d35Sstevel 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
93703831d35Sstevel 	    "ranges", (int **)&erp, (uint_t *)&length) == DDI_PROP_SUCCESS) {
93803831d35Sstevel 		ddi_prop_free(erp);
93903831d35Sstevel 		return (DDI_SUCCESS);
94003831d35Sstevel 	}
94103831d35Sstevel 
94203831d35Sstevel 	/*
94303831d35Sstevel 	 * interrupt-map is the only property that comes from a .conf file.
94403831d35Sstevel 	 * Since it doesn't have the nodeid field set, it must be done here.
94503831d35Sstevel 	 * Other properties can come from OBP or created here.
94603831d35Sstevel 	 */
94703831d35Sstevel 	if (acebus_set_imap(dip) != DDI_SUCCESS) {
94803831d35Sstevel 		return (DDI_FAILURE);
94903831d35Sstevel 	}
95003831d35Sstevel 
95103831d35Sstevel 	/*
95203831d35Sstevel 	 * Create the "ranges" property.
95303831d35Sstevel 	 * Ebus has BAR0 and BAR1 allocated (both in memory space).
95403831d35Sstevel 	 * Other BARs are 0.
95503831d35Sstevel 	 * Hence there are 2 memory ranges it operates in. (one for each BAR).
95603831d35Sstevel 	 * ie. there are 2 entries in its ranges property.
95703831d35Sstevel 	 */
95803831d35Sstevel 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
95903831d35Sstevel 	    DDI_PROP_DONTPASS, "assigned-addresses",
96019397407SSherry Moore 	    (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) {
96103831d35Sstevel 		cmn_err(CE_WARN, "%s%d: Could not get assigned-addresses",
96203831d35Sstevel 		    ddi_driver_name(dip), ddi_get_instance(dip));
96303831d35Sstevel 		return (DDI_FAILURE);
96403831d35Sstevel 	}
96503831d35Sstevel 	/*
96603831d35Sstevel 	 * Create the 1st mem range in which it operates corresponding
96703831d35Sstevel 	 * to BAR0
96803831d35Sstevel 	 */
96903831d35Sstevel 	er[0].ebus_phys_hi = EBUS_CHILD_PHYS_LOW_RANGE;
97003831d35Sstevel 	rnums = (length * sizeof (int))/sizeof (pci_regspec_t);
97103831d35Sstevel 	for (i = 0; i < rnums; i++) {
97203831d35Sstevel 		prp = pci_rp + i;
97303831d35Sstevel 		if (PCI_REG_REG_G(prp->pci_phys_hi) == er[0].ebus_phys_hi) {
97403831d35Sstevel 			found = 1;
97503831d35Sstevel 			break;
97603831d35Sstevel 		}
97703831d35Sstevel 	}
97803831d35Sstevel 	if (!found) {
97903831d35Sstevel 		cmn_err(CE_WARN, "No assigned space for memory range 0.");
98003831d35Sstevel 		ddi_prop_free(pci_rp);
98103831d35Sstevel 		return (DDI_FAILURE);
98203831d35Sstevel 	}
98303831d35Sstevel 	found = 0;
98403831d35Sstevel 	er[0].ebus_phys_low = 0;
98503831d35Sstevel 	er[0].pci_phys_hi = prp->pci_phys_hi;
98603831d35Sstevel 	er[0].pci_phys_mid = prp->pci_phys_mid;
98703831d35Sstevel 	er[0].pci_phys_low = prp->pci_phys_low;
98803831d35Sstevel 	er[0].rng_size = prp->pci_size_low;
98903831d35Sstevel 
99003831d35Sstevel 	/*
99103831d35Sstevel 	 * Create the 2nd mem range in which it operates corresponding
99203831d35Sstevel 	 * to BAR1
99303831d35Sstevel 	 */
99403831d35Sstevel 	er[1].ebus_phys_hi = EBUS_CHILD_PHYS_HI_RANGE;
99503831d35Sstevel 	for (i = 0; i < rnums; i++) {
99603831d35Sstevel 		prp = pci_rp + i;
99703831d35Sstevel 		if (PCI_REG_REG_G(prp->pci_phys_hi) == er[1].ebus_phys_hi) {
99803831d35Sstevel 			found = 1;
99903831d35Sstevel 			break;
100003831d35Sstevel 		}
100103831d35Sstevel 	}
100203831d35Sstevel 	if (!found) {
100303831d35Sstevel 		cmn_err(CE_WARN, "No assigned space for memory range 1.");
100403831d35Sstevel 		ddi_prop_free(pci_rp);
100503831d35Sstevel 		return (DDI_FAILURE);
100603831d35Sstevel 	}
100703831d35Sstevel 	er[1].ebus_phys_low = 0;
100803831d35Sstevel 	er[1].pci_phys_hi = prp->pci_phys_hi;
100903831d35Sstevel 	er[1].pci_phys_mid = prp->pci_phys_mid;
101003831d35Sstevel 	er[1].pci_phys_low = prp->pci_phys_low;
101103831d35Sstevel 	er[1].rng_size = prp->pci_size_low;
101203831d35Sstevel 
101303831d35Sstevel 	ddi_prop_free(pci_rp);
101403831d35Sstevel 	length = sizeof (er) / sizeof (int);
101503831d35Sstevel 	if (ddi_prop_update_int_array(DDI_DEV_T_NONE, dip,
101603831d35Sstevel 	    "ranges", (int *)er, length) != DDI_PROP_SUCCESS) {
101703831d35Sstevel 		cmn_err(CE_WARN, "%s%d: Could not create ranges property",
101803831d35Sstevel 		    ddi_driver_name(dip), ddi_get_instance(dip));
101903831d35Sstevel 		return (DDI_FAILURE);
102003831d35Sstevel 	}
102103831d35Sstevel 	/* The following properties are as defined by PCI 1275 bindings. */
102203831d35Sstevel 	if (ddi_prop_update_int(DDI_DEV_T_NONE, dip,
102303831d35Sstevel 	    "#address-cells", 2) != DDI_PROP_SUCCESS)
102403831d35Sstevel 			return (DDI_FAILURE);
102503831d35Sstevel 	if (ddi_prop_update_int(DDI_DEV_T_NONE, dip,
102603831d35Sstevel 	    "#size-cells", 1) != DDI_PROP_SUCCESS)
102703831d35Sstevel 			return (DDI_FAILURE);
102803831d35Sstevel 	if (ddi_prop_update_int(DDI_DEV_T_NONE, dip,
102903831d35Sstevel 	    "#interrupt-cells", 1) != DDI_PROP_SUCCESS)
103003831d35Sstevel 			return (DDI_FAILURE);
103103831d35Sstevel 
103203831d35Sstevel 	imask[0] = 0x1f;
103303831d35Sstevel 	imask[1] = 0x00ffffff;
103403831d35Sstevel 	imask[2] = 0x00000003;
103503831d35Sstevel 	length = sizeof (imask) / sizeof (int);
103603831d35Sstevel 	if (ddi_prop_update_int_array(DDI_DEV_T_NONE, dip,
103703831d35Sstevel 	    "interrupt-map-mask", (int *)imask, length) != DDI_PROP_SUCCESS) {
103803831d35Sstevel 		cmn_err(CE_WARN, "%s%d: Could not update imap mask property",
103903831d35Sstevel 		    ddi_driver_name(dip), ddi_get_instance(dip));
104003831d35Sstevel 		return (DDI_FAILURE);
104103831d35Sstevel 	}
104203831d35Sstevel 
104303831d35Sstevel 	return (DDI_SUCCESS);
104403831d35Sstevel }
104503831d35Sstevel 
104603831d35Sstevel /*
104703831d35Sstevel  * This function takes in the ac-interrupt-map property from the .conf file,
104803831d35Sstevel  * fills in the 'nodeid' information and then creates the 'interrupt-map'
104903831d35Sstevel  * property.
105003831d35Sstevel  */
105103831d35Sstevel static int
105203831d35Sstevel acebus_set_imap(dev_info_t *dip)
105303831d35Sstevel {
105403831d35Sstevel 	int *imapp, *timapp, length, num, i, default_ival = 0;
105503831d35Sstevel 	dev_info_t *tdip = dip;
105603831d35Sstevel 	int *port_id, imap_ok = 1;
105703831d35Sstevel 	int ilength;
105803831d35Sstevel 	int acebus_default_se_imap[5];
105903831d35Sstevel 
106003831d35Sstevel 	/*
106103831d35Sstevel 	 * interrupt-map is specified via .conf file in hotplug mode,
106203831d35Sstevel 	 * since the child configuration is static.
106303831d35Sstevel 	 * It could even be hardcoded in the driver.
106403831d35Sstevel 	 */
106503831d35Sstevel 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
106603831d35Sstevel 	    "ac-interrupt-map", (int **)&imapp, (uint_t *)&ilength) !=
106703831d35Sstevel 	    DDI_PROP_SUCCESS) {
106803831d35Sstevel 		/* assume default implementation */
106903831d35Sstevel 		acebus_default_se_imap[0] = 0x14;
107003831d35Sstevel 		acebus_default_se_imap[1] = 0x400000;
107103831d35Sstevel 		acebus_default_se_imap[2] = 1;
107203831d35Sstevel 		acebus_default_se_imap[3] = 0;
107303831d35Sstevel 		acebus_default_se_imap[4] = 2;
107403831d35Sstevel 		imapp = acebus_default_se_imap;
107503831d35Sstevel 		ilength = 5;
107603831d35Sstevel 		default_ival = 1;
107703831d35Sstevel 	}
107803831d35Sstevel 	num = ilength / 5;	/* there are 5 integer cells in our property */
107903831d35Sstevel 	timapp = imapp;
108003831d35Sstevel 	for (i = 0; i < num; i++) {
108103831d35Sstevel 		if (*(timapp+i*5+3) == 0)
108203831d35Sstevel 			imap_ok = 0;
108303831d35Sstevel 	}
108403831d35Sstevel 	if (imap_ok) {
108503831d35Sstevel 		if (!default_ival)
108603831d35Sstevel 			ddi_prop_free(imapp);
108703831d35Sstevel 		return (DDI_SUCCESS);
108803831d35Sstevel 	}
108903831d35Sstevel 
109003831d35Sstevel 	while (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, tdip,
109103831d35Sstevel 	    DDI_PROP_DONTPASS, "upa-portid", (int **)&port_id,
109203831d35Sstevel 	    (uint_t *)&length) != DDI_PROP_SUCCESS) {
109303831d35Sstevel 		tdip = ddi_get_parent(tdip);
109403831d35Sstevel 		if (tdip == NULL) {
109503831d35Sstevel 			cmn_err(CE_WARN, "%s%d: Could not get imap parent",
109603831d35Sstevel 			    ddi_driver_name(dip), ddi_get_instance(dip));
109703831d35Sstevel 			if (!default_ival)
109803831d35Sstevel 				ddi_prop_free(imapp);
109903831d35Sstevel 			return (DDI_FAILURE);
110003831d35Sstevel 		}
110103831d35Sstevel 	}
110203831d35Sstevel 	timapp = imapp;
110303831d35Sstevel 	for (i = 0; i < num; i++) {
110403831d35Sstevel 		*(timapp+i*5+3) = ddi_get_nodeid(tdip);
110503831d35Sstevel 	}
110603831d35Sstevel 
110703831d35Sstevel 	if (ddi_prop_update_int_array(DDI_DEV_T_NONE, dip,
110803831d35Sstevel 	    "interrupt-map", imapp, ilength) != DDI_PROP_SUCCESS) {
110903831d35Sstevel 		cmn_err(CE_WARN, "%s%d: Could not update AC imap property",
111003831d35Sstevel 		    ddi_driver_name(dip), ddi_get_instance(dip));
111103831d35Sstevel 		if (!default_ival)
111203831d35Sstevel 			ddi_prop_free(imapp);
111303831d35Sstevel 		return (DDI_FAILURE);
111403831d35Sstevel 	}
111503831d35Sstevel 	if (!default_ival)
111603831d35Sstevel 		ddi_prop_free(imapp);
111703831d35Sstevel 	return (DDI_SUCCESS);
111803831d35Sstevel }
111903831d35Sstevel #endif	/* ACEBUS_HOTPLUG */
1120