125cf1a30Sjl /*
225cf1a30Sjl  * CDDL HEADER START
325cf1a30Sjl  *
425cf1a30Sjl  * The contents of this file are subject to the terms of the
525cf1a30Sjl  * Common Development and Distribution License (the "License").
625cf1a30Sjl  * You may not use this file except in compliance with the License.
725cf1a30Sjl  *
825cf1a30Sjl  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
925cf1a30Sjl  * or http://www.opensolaris.org/os/licensing.
1025cf1a30Sjl  * See the License for the specific language governing permissions
1125cf1a30Sjl  * and limitations under the License.
1225cf1a30Sjl  *
1325cf1a30Sjl  * When distributing Covered Code, include this CDDL HEADER in each
1425cf1a30Sjl  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1525cf1a30Sjl  * If applicable, add the following below this CDDL HEADER, with the
1625cf1a30Sjl  * fields enclosed by brackets "[]" replaced with your own identifying
1725cf1a30Sjl  * information: Portions Copyright [yyyy] [name of copyright owner]
1825cf1a30Sjl  *
1925cf1a30Sjl  * CDDL HEADER END
2025cf1a30Sjl  */
2107d06da5SSurya Prakki 
2225cf1a30Sjl /*
2307d06da5SSurya Prakki  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2425cf1a30Sjl  * Use is subject to license terms.
2525cf1a30Sjl  */
2725cf1a30Sjl /*
2825cf1a30Sjl  * CMU-CH nexus utility routines:
2925cf1a30Sjl  *	property and config routines for attach()
3025cf1a30Sjl  *	reg/intr/range/assigned-address property routines for bus_map()
3125cf1a30Sjl  *	init_child()
3225cf1a30Sjl  *	fault handling
3325cf1a30Sjl  *	debug functions
3425cf1a30Sjl  */
3625cf1a30Sjl #include <sys/types.h>
3725cf1a30Sjl #include <sys/kmem.h>
3825cf1a30Sjl #include <sys/async.h>
3925cf1a30Sjl #include <sys/sysmacros.h>
4025cf1a30Sjl #include <sys/sunddi.h>
4125cf1a30Sjl #include <sys/sunndi.h>
4225cf1a30Sjl #include <sys/fm/protocol.h>
4325cf1a30Sjl #include <sys/fm/io/pci.h>
4425cf1a30Sjl #include <sys/fm/util.h>
4525cf1a30Sjl #include <sys/ddi_impldefs.h>
4625cf1a30Sjl #include <sys/pcicmu/pcicmu.h>
4725cf1a30Sjl #include <sys/promif.h>
4925cf1a30Sjl /*
5025cf1a30Sjl  * get_pcmu_properties
5125cf1a30Sjl  *
5225cf1a30Sjl  * This function is called from the attach routine to get the key
5325cf1a30Sjl  * properties of the pci nodes.
5425cf1a30Sjl  *
5525cf1a30Sjl  * used by: pcmu_attach()
5625cf1a30Sjl  *
5725cf1a30Sjl  * return value: DDI_FAILURE on failure
5825cf1a30Sjl  */
5925cf1a30Sjl int
get_pcmu_properties(pcmu_t * pcmu_p,dev_info_t * dip)6025cf1a30Sjl get_pcmu_properties(pcmu_t *pcmu_p, dev_info_t *dip)
6125cf1a30Sjl {
6225cf1a30Sjl 	int i;
6425cf1a30Sjl 	/*
6525cf1a30Sjl 	 * Get the device's port id.
6625cf1a30Sjl 	 */
6725cf1a30Sjl 	if ((pcmu_p->pcmu_id = (uint32_t)pcmu_get_portid(dip)) == -1u) {
6825cf1a30Sjl 		cmn_err(CE_WARN, "%s%d: no portid property\n",
6925cf1a30Sjl 		    ddi_driver_name(dip), ddi_get_instance(dip));
7025cf1a30Sjl 		return (DDI_FAILURE);
7125cf1a30Sjl 	}
7325cf1a30Sjl 	/*
7425cf1a30Sjl 	 * Get the bus-ranges property.
7525cf1a30Sjl 	 */
7625cf1a30Sjl 	i = sizeof (pcmu_p->pcmu_bus_range);
7725cf1a30Sjl 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
7825cf1a30Sjl 	    "bus-range", (caddr_t)&pcmu_p->pcmu_bus_range, &i) != DDI_SUCCESS) {
7925cf1a30Sjl 		cmn_err(CE_WARN, "%s%d: no bus-range property\n",
8025cf1a30Sjl 		    ddi_driver_name(dip), ddi_get_instance(dip));
8125cf1a30Sjl 		return (DDI_FAILURE);
8225cf1a30Sjl 	}
8325cf1a30Sjl 	PCMU_DBG2(PCMU_DBG_ATTACH, dip,
8425cf1a30Sjl 	    "get_pcmu_properties: bus-range (%x,%x)\n",
8525cf1a30Sjl 	    pcmu_p->pcmu_bus_range.lo, pcmu_p->pcmu_bus_range.hi);
8725cf1a30Sjl 	/*
8825cf1a30Sjl 	 * Get the ranges property.
8925cf1a30Sjl 	 */
9025cf1a30Sjl 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "ranges",
9125cf1a30Sjl 	    (caddr_t)&pcmu_p->pcmu_ranges, &pcmu_p->pcmu_ranges_length) !=
9225cf1a30Sjl 	    DDI_SUCCESS) {
9325cf1a30Sjl 		cmn_err(CE_WARN, "%s%d: no ranges property\n",
9425cf1a30Sjl 		    ddi_driver_name(dip), ddi_get_instance(dip));
9525cf1a30Sjl 		return (DDI_FAILURE);
9625cf1a30Sjl 	}
9725cf1a30Sjl 	pcmu_fix_ranges(pcmu_p->pcmu_ranges,
9825cf1a30Sjl 	    pcmu_p->pcmu_ranges_length / sizeof (pcmu_ranges_t));
10025cf1a30Sjl 	/*
10125cf1a30Sjl 	 * Determine the number upa slot interrupts.
10225cf1a30Sjl 	 */
10325cf1a30Sjl 	pcmu_p->pcmu_numproxy = pcmu_get_numproxy(pcmu_p->pcmu_dip);
10425cf1a30Sjl 	PCMU_DBG1(PCMU_DBG_ATTACH, dip, "get_pcmu_properties: numproxy=%d\n",
10525cf1a30Sjl 	    pcmu_p->pcmu_numproxy);
10625cf1a30Sjl 	return (DDI_SUCCESS);
10725cf1a30Sjl }
10925cf1a30Sjl /*
11025cf1a30Sjl  * free_pcmu_properties:
11125cf1a30Sjl  *
11225cf1a30Sjl  * This routine frees the memory used to cache the
11325cf1a30Sjl  * "ranges" properties of the pci bus device node.
11425cf1a30Sjl  *
11525cf1a30Sjl  * used by: pcmu_detach()
11625cf1a30Sjl  *
11725cf1a30Sjl  * return value: none
11825cf1a30Sjl  */
11925cf1a30Sjl void
free_pcmu_properties(pcmu_t * pcmu_p)12025cf1a30Sjl free_pcmu_properties(pcmu_t *pcmu_p)
12125cf1a30Sjl {
12225cf1a30Sjl 	kmem_free(pcmu_p->pcmu_ranges, pcmu_p->pcmu_ranges_length);
12325cf1a30Sjl }
12525cf1a30Sjl /*
12625cf1a30Sjl  * pcmu_reloc_reg
12725cf1a30Sjl  *
12825cf1a30Sjl  * If the "reg" entry (*pcmu_rp) is relocatable, lookup "assigned-addresses"
12925cf1a30Sjl  * property to fetch corresponding relocated address.
13025cf1a30Sjl  *
13125cf1a30Sjl  * used by: pcmu_map()
13225cf1a30Sjl  *
13325cf1a30Sjl  * return value:
13425cf1a30Sjl  *
13525cf1a30Sjl  *	DDI_SUCCESS		- on success
13625cf1a30Sjl  *	DDI_ME_INVAL		- regspec is invalid
13725cf1a30Sjl  */
13825cf1a30Sjl int
pcmu_reloc_reg(dev_info_t * dip,dev_info_t * rdip,pcmu_t * pcmu_p,pci_regspec_t * rp)13925cf1a30Sjl pcmu_reloc_reg(dev_info_t *dip, dev_info_t *rdip, pcmu_t *pcmu_p,
140*9b65801eSToomas Soome     pci_regspec_t *rp)
14125cf1a30Sjl {
14225cf1a30Sjl 	int assign_len, assign_entries, i;
14325cf1a30Sjl 	pci_regspec_t *assign_p;
14425cf1a30Sjl 	register uint32_t phys_hi = rp->pci_phys_hi;
14525cf1a30Sjl 	register uint32_t mask = PCI_REG_ADDR_M | PCI_CONF_ADDR_MASK;
14625cf1a30Sjl 	register uint32_t phys_addr = phys_hi & mask;
14825cf1a30Sjl 	PCMU_DBG5(PCMU_DBG_MAP | PCMU_DBG_CONT, dip,
14925cf1a30Sjl 	    "\tpcmu_reloc_reg fr: %x.%x.%x %x.%x\n",
15025cf1a30Sjl 	    rp->pci_phys_hi, rp->pci_phys_mid, rp->pci_phys_low,
15125cf1a30Sjl 	    rp->pci_size_hi, rp->pci_size_low);
15325cf1a30Sjl 	if ((phys_hi & PCI_RELOCAT_B) || !(phys_hi & PCI_ADDR_MASK)) {
15425cf1a30Sjl 		return (DDI_SUCCESS);
15525cf1a30Sjl 	}
15725cf1a30Sjl 	/* phys_mid must be 0 regardless space type. XXX-64 bit mem space */
15825cf1a30Sjl 	if (rp->pci_phys_mid != 0 || rp->pci_size_hi != 0) {
15925cf1a30Sjl 		PCMU_DBG0(PCMU_DBG_MAP | PCMU_DBG_CONT, pcmu_p->pcmu_dip,
16025cf1a30Sjl 		    "phys_mid or size_hi not 0\n");
16125cf1a30Sjl 		return (DDI_ME_INVAL);
16225cf1a30Sjl 	}
16425cf1a30Sjl 	if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
16525cf1a30Sjl 	    "assigned-addresses", (caddr_t)&assign_p, &assign_len)) {
16625cf1a30Sjl 		return (DDI_ME_INVAL);
16725cf1a30Sjl 	}
16925cf1a30Sjl 	assign_entries = assign_len / sizeof (pci_regspec_t);
17025cf1a30Sjl 	for (i = 0; i < assign_entries; i++, assign_p++) {
17125cf1a30Sjl 		if ((assign_p->pci_phys_hi & mask) == phys_addr) {
17225cf1a30Sjl 			rp->pci_phys_low += assign_p->pci_phys_low;
17325cf1a30Sjl 			break;
17425cf1a30Sjl 		}
17525cf1a30Sjl 	}
17625cf1a30Sjl 	kmem_free(assign_p - i, assign_len);
17725cf1a30Sjl 	PCMU_DBG5(PCMU_DBG_MAP | PCMU_DBG_CONT, dip,
17825cf1a30Sjl 	    "\tpcmu_reloc_reg to: %x.%x.%x %x.%x\n",
17925cf1a30Sjl 	    rp->pci_phys_hi, rp->pci_phys_mid, rp->pci_phys_low,
18025cf1a30Sjl 	    rp->pci_size_hi, rp->pci_size_low);
18125cf1a30Sjl 	return (i < assign_entries ? DDI_SUCCESS : DDI_ME_INVAL);
18225cf1a30Sjl }
18425cf1a30Sjl /*
18525cf1a30Sjl  * use "ranges" to translate relocated pci regspec into parent space
18625cf1a30Sjl  */
18725cf1a30Sjl int
pcmu_xlate_reg(pcmu_t * pcmu_p,pci_regspec_t * pcmu_rp,struct regspec * new_rp)18825cf1a30Sjl pcmu_xlate_reg(pcmu_t *pcmu_p, pci_regspec_t *pcmu_rp, struct regspec *new_rp)
18925cf1a30Sjl {
19025cf1a30Sjl 	int n;
19125cf1a30Sjl 	pcmu_ranges_t *rng_p = pcmu_p->pcmu_ranges;
19225cf1a30Sjl 	int rng_n = pcmu_p->pcmu_ranges_length / sizeof (pcmu_ranges_t);
19425cf1a30Sjl 	uint32_t space_type = PCI_REG_ADDR_G(pcmu_rp->pci_phys_hi);
19525cf1a30Sjl 	uint32_t reg_end, reg_begin = pcmu_rp->pci_phys_low;
19625cf1a30Sjl 	uint32_t sz = pcmu_rp->pci_size_low;
19825cf1a30Sjl 	uint32_t rng_begin, rng_end;
20025cf1a30Sjl 	if (space_type == PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) {
20125cf1a30Sjl 		if (reg_begin > PCI_CONF_HDR_SIZE) {
20225cf1a30Sjl 			return (DDI_ME_INVAL);
20325cf1a30Sjl 		}
20425cf1a30Sjl 		sz = sz ? MIN(sz, PCI_CONF_HDR_SIZE) : PCI_CONF_HDR_SIZE;
20525cf1a30Sjl 		reg_begin += pcmu_rp->pci_phys_hi;
20625cf1a30Sjl 	}
20725cf1a30Sjl 	reg_end = reg_begin + sz - 1;
20925cf1a30Sjl 	for (n = 0; n < rng_n; n++, rng_p++) {
21025cf1a30Sjl 		if (space_type != PCI_REG_ADDR_G(rng_p->child_high)) {
21125cf1a30Sjl 			continue;	/* not the same space type */
21225cf1a30Sjl 		}
21425cf1a30Sjl 		rng_begin = rng_p->child_low;
21525cf1a30Sjl 		if (space_type == PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) {
21625cf1a30Sjl 			rng_begin += rng_p->child_high;
21725cf1a30Sjl 		}
21825cf1a30Sjl 		rng_end = rng_begin + rng_p->size_low - 1;
21925cf1a30Sjl 		if (reg_begin >= rng_begin && reg_end <= rng_end) {
22025cf1a30Sjl 			break;
22125cf1a30Sjl 		}
22225cf1a30Sjl 	}
22325cf1a30Sjl 	if (n >= rng_n) {
22425cf1a30Sjl 		return (DDI_ME_REGSPEC_RANGE);
22525cf1a30Sjl 	}
22725cf1a30Sjl 	new_rp->regspec_addr = reg_begin - rng_begin + rng_p->parent_low;
22825cf1a30Sjl 	new_rp->regspec_bustype = rng_p->parent_high;
22925cf1a30Sjl 	new_rp->regspec_size = sz;
23025cf1a30Sjl 	PCMU_DBG4(PCMU_DBG_MAP | PCMU_DBG_CONT, pcmu_p->pcmu_dip,
23125cf1a30Sjl 	    "\tpcmu_xlate_reg: entry %d new_rp %x.%x %x\n",
23225cf1a30Sjl 	    n, new_rp->regspec_bustype, new_rp->regspec_addr, sz);
23325cf1a30Sjl 	return (DDI_SUCCESS);
23425cf1a30Sjl }
23725cf1a30Sjl /*
23825cf1a30Sjl  * pcmu_report_dev
23925cf1a30Sjl  *
24025cf1a30Sjl  * This function is called from our control ops routine on a
24125cf1a30Sjl  * DDI_CTLOPS_REPORTDEV request.
24225cf1a30Sjl  *
24325cf1a30Sjl  * The display format is
24425cf1a30Sjl  *
24525cf1a30Sjl  *	<name><inst> at <pname><pinst> device <dev> function <func>
24625cf1a30Sjl  *
24725cf1a30Sjl  * where
24825cf1a30Sjl  *
24925cf1a30Sjl  *	<name>		this device's name property
25025cf1a30Sjl  *	<inst>		this device's instance number
25125cf1a30Sjl  *	<name>		parent device's name property
25225cf1a30Sjl  *	<inst>		parent device's instance number
25325cf1a30Sjl  *	<dev>		this device's device number
25425cf1a30Sjl  *	<func>		this device's function number
25525cf1a30Sjl  */
25625cf1a30Sjl int
pcmu_report_dev(dev_info_t * dip)25725cf1a30Sjl pcmu_report_dev(dev_info_t *dip)
25825cf1a30Sjl {
25925cf1a30Sjl 	if (dip == (dev_info_t *)0) {
26025cf1a30Sjl 		return (DDI_FAILURE);
26125cf1a30Sjl 	}
26225cf1a30Sjl 	cmn_err(CE_CONT, "?PCI-device: %s@%s, %s%d\n", ddi_node_name(dip),
26325cf1a30Sjl 	    ddi_get_name_addr(dip), ddi_driver_name(dip),
26425cf1a30Sjl 	    ddi_get_instance(dip));
26525cf1a30Sjl 	return (DDI_SUCCESS);
26625cf1a30Sjl }
26825cf1a30Sjl /*
26925cf1a30Sjl  * name_child
27025cf1a30Sjl  *
27125cf1a30Sjl  * This function is called from pcmu_init_child to name a node. It is
27225cf1a30Sjl  * also passed as a callback for node merging functions.
27325cf1a30Sjl  *
27425cf1a30Sjl  * return value: DDI_SUCCESS, DDI_FAILURE
27525cf1a30Sjl  */
27625cf1a30Sjl static int
name_child(dev_info_t * child,char * name,int namelen)27725cf1a30Sjl name_child(dev_info_t *child, char *name, int namelen)
27825cf1a30Sjl {
27925cf1a30Sjl 	pci_regspec_t *pcmu_rp;
28025cf1a30Sjl 	int reglen;
28125cf1a30Sjl 	uint_t func;
28225cf1a30Sjl 	char **unit_addr;
28325cf1a30Sjl 	uint_t n;
28525cf1a30Sjl 	/*
28625cf1a30Sjl 	 * Set the address portion of the node name based on
28725cf1a30Sjl 	 * unit-address property, if it exists.
28825cf1a30Sjl 	 * The interpretation of the unit-address is DD[,F]
28925cf1a30Sjl 	 * where DD is the device id and F is the function.
29025cf1a30Sjl 	 */
29125cf1a30Sjl 	if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child,
29225cf1a30Sjl 	    DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) ==
29325cf1a30Sjl 	    DDI_PROP_SUCCESS) {
29425cf1a30Sjl 		if (n != 1 || *unit_addr == NULL || **unit_addr == 0) {
29525cf1a30Sjl 			cmn_err(CE_WARN, "unit-address property in %s.conf"
29625cf1a30Sjl 			    " not well-formed", ddi_driver_name(child));
29725cf1a30Sjl 			ddi_prop_free(unit_addr);
29825cf1a30Sjl 			return (DDI_FAILURE);
29925cf1a30Sjl 		}
30025cf1a30Sjl 		(void) snprintf(name, namelen, "%s", *unit_addr);
30125cf1a30Sjl 		ddi_prop_free(unit_addr);
30225cf1a30Sjl 		return (DDI_SUCCESS);
30325cf1a30Sjl 	}
30525cf1a30Sjl 	/*
30625cf1a30Sjl 	 * The unit-address property is does not exist. Set the address
30725cf1a30Sjl 	 * portion of the node name based on the function and device number.
30825cf1a30Sjl 	 */
30925cf1a30Sjl 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
31025cf1a30Sjl 	    "reg", (int **)&pcmu_rp, (uint_t *)&reglen) == DDI_SUCCESS) {
31125cf1a30Sjl 		if (((reglen * sizeof (int)) % sizeof (pci_regspec_t)) != 0) {
31225cf1a30Sjl 			cmn_err(CE_WARN, "reg property not well-formed");
31325cf1a30Sjl 			return (DDI_FAILURE);
31425cf1a30Sjl 		}
31625cf1a30Sjl 		func = PCI_REG_FUNC_G(pcmu_rp[0].pci_phys_hi);
31725cf1a30Sjl 		if (func != 0) {
31825cf1a30Sjl 			(void) snprintf(name, namelen, "%x,%x",
31907d06da5SSurya Prakki 			    PCI_REG_DEV_G(pcmu_rp[0].pci_phys_hi), func);
32025cf1a30Sjl 		} else {
32125cf1a30Sjl 			(void) snprintf(name, namelen, "%x",
32207d06da5SSurya Prakki 			    PCI_REG_DEV_G(pcmu_rp[0].pci_phys_hi));
32325cf1a30Sjl 		}
32425cf1a30Sjl 		ddi_prop_free(pcmu_rp);
32525cf1a30Sjl 		return (DDI_SUCCESS);
32625cf1a30Sjl 	}
32725cf1a30Sjl 	cmn_err(CE_WARN, "cannot name pci child '%s'", ddi_node_name(child));
32825cf1a30Sjl 	return (DDI_FAILURE);
32925cf1a30Sjl }
33125cf1a30Sjl int
pcmu_uninit_child(pcmu_t * pcmu_p,dev_info_t * child)33225cf1a30Sjl pcmu_uninit_child(pcmu_t *pcmu_p, dev_info_t *child)
33325cf1a30Sjl {
33425cf1a30Sjl 	PCMU_DBG2(PCMU_DBG_CTLOPS, pcmu_p->pcmu_dip,
33525cf1a30Sjl 	    "DDI_CTLOPS_UNINITCHILD: arg=%s%d\n",
33625cf1a30Sjl 	    ddi_driver_name(child), ddi_get_instance(child));
33825cf1a30Sjl 	ddi_set_name_addr(child, NULL);
33925cf1a30Sjl 	ddi_remove_minor_node(child, NULL);
34025cf1a30Sjl 	impl_rem_dev_props(child);
34225cf1a30Sjl 	PCMU_DBG0(PCMU_DBG_PWR, ddi_get_parent(child), "\n\n");
34325cf1a30Sjl 	return (DDI_SUCCESS);
34425cf1a30Sjl }
34625cf1a30Sjl /*
34725cf1a30Sjl  * pcmu_init_child
34825cf1a30Sjl  *
34925cf1a30Sjl  * This function is called from our control ops routine on a
35025cf1a30Sjl  * DDI_CTLOPS_INITCHILD request.  It builds and sets the device's
35125cf1a30Sjl  * parent private data area.
35225cf1a30Sjl  *
35325cf1a30Sjl  * used by: pcmu_ctlops()
35425cf1a30Sjl  *
35525cf1a30Sjl  * return value: none
35625cf1a30Sjl  */
35725cf1a30Sjl int
pcmu_init_child(pcmu_t * pcmu_p,dev_info_t * child)35825cf1a30Sjl pcmu_init_child(pcmu_t *pcmu_p, dev_info_t *child)
35925cf1a30Sjl {
36025cf1a30Sjl 	char name[10];
36125cf1a30Sjl 	ddi_acc_handle_t config_handle;
36225cf1a30Sjl 	uint8_t bcr;
36325cf1a30Sjl 	uint8_t header_type;
36525cf1a30Sjl 	if (name_child(child, name, 10) != DDI_SUCCESS)
36625cf1a30Sjl 		return (DDI_FAILURE);
36725cf1a30Sjl 	ddi_set_name_addr(child, name);
36925cf1a30Sjl 	PCMU_DBG2(PCMU_DBG_PWR, ddi_get_parent(child),
37025cf1a30Sjl 	    "INITCHILD: config regs setup for %s@%s\n",
37125cf1a30Sjl 	    ddi_node_name(child), ddi_get_name_addr(child));
37325cf1a30Sjl 	/*
37425cf1a30Sjl 	 * Map the child configuration space to for initialization.
37525cf1a30Sjl 	 * We assume the obp will do the following in the devices
37625cf1a30Sjl 	 * config space:
37725cf1a30Sjl 	 *
37825cf1a30Sjl 	 *	Set the latency-timer register to values appropriate
37925cf1a30Sjl 	 *	for the devices on the bus (based on other devices
38025cf1a30Sjl 	 *	MIN_GNT and MAX_LAT registers.
38125cf1a30Sjl 	 *
38225cf1a30Sjl 	 *	Set the fast back-to-back enable bit in the command
38325cf1a30Sjl 	 *	register if it's supported and all devices on the bus
38425cf1a30Sjl 	 *	have the capability.
38525cf1a30Sjl 	 *
38625cf1a30Sjl 	 */
38725cf1a30Sjl 	if (pci_config_setup(child, &config_handle) != DDI_SUCCESS) {
38825cf1a30Sjl 		ddi_set_name_addr(child, NULL);
38925cf1a30Sjl 		return (DDI_FAILURE);
39025cf1a30Sjl 	}
39225cf1a30Sjl 	/*
39325cf1a30Sjl 	 * Determine the configuration header type.
39425cf1a30Sjl 	 */
39525cf1a30Sjl 	header_type = pci_config_get8(config_handle, PCI_CONF_HEADER);
39625cf1a30Sjl 	PCMU_DBG2(PCMU_DBG_INIT_CLD, pcmu_p->pcmu_dip, "%s: header_type=%x\n",
39725cf1a30Sjl 	    ddi_driver_name(child), header_type);
39925cf1a30Sjl 	/*
40025cf1a30Sjl 	 * If the device has a bus control register then program it
40125cf1a30Sjl 	 * based on the settings in the command register.
40225cf1a30Sjl 	 */
40325cf1a30Sjl 	if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_ONE) {
40425cf1a30Sjl 		bcr = pci_config_get8(config_handle, PCI_BCNF_BCNTRL);
40525cf1a30Sjl 		if (pcmu_command_default & PCI_COMM_PARITY_DETECT)
40625cf1a30Sjl 			bcr |= PCI_BCNF_BCNTRL_PARITY_ENABLE;
40725cf1a30Sjl 		if (pcmu_command_default & PCI_COMM_SERR_ENABLE)
40825cf1a30Sjl 			bcr |= PCI_BCNF_BCNTRL_SERR_ENABLE;
40925cf1a30Sjl 		bcr |= PCI_BCNF_BCNTRL_MAST_AB_MODE;
41025cf1a30Sjl 		pci_config_put8(config_handle, PCI_BCNF_BCNTRL, bcr);
41125cf1a30Sjl 	}
41325cf1a30Sjl 	pci_config_teardown(&config_handle);
41425cf1a30Sjl 	return (DDI_SUCCESS);
41525cf1a30Sjl }
41725cf1a30Sjl /*
41825cf1a30Sjl  * pcmu_get_reg_set_size
41925cf1a30Sjl  *
42025cf1a30Sjl  * Given a dev info pointer to a pci child and a register number, this
42125cf1a30Sjl  * routine returns the size element of that reg set property.
42225cf1a30Sjl  *
42325cf1a30Sjl  * used by: pcmu_ctlops() - DDI_CTLOPS_REGSIZE
42425cf1a30Sjl  *
42525cf1a30Sjl  * return value: size of reg set on success, zero on error
42625cf1a30Sjl  */
42725cf1a30Sjl off_t
pcmu_get_reg_set_size(dev_info_t * child,int rnumber)42825cf1a30Sjl pcmu_get_reg_set_size(dev_info_t *child, int rnumber)
42925cf1a30Sjl {
43025cf1a30Sjl 	pci_regspec_t *pcmu_rp;
43125cf1a30Sjl 	off_t size;
43225cf1a30Sjl 	int i;
43425cf1a30Sjl 	if (rnumber < 0) {
43525cf1a30Sjl 		return (0);
43625cf1a30Sjl 	}
43825cf1a30Sjl 	/*
43925cf1a30Sjl 	 * Get the reg property for the device.
44025cf1a30Sjl 	 */
44125cf1a30Sjl 	if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg",
44225cf1a30Sjl 	    (caddr_t)&pcmu_rp, &i) != DDI_SUCCESS) {
44325cf1a30Sjl 		return (0);
44425cf1a30Sjl 	}
44625cf1a30Sjl 	if (rnumber >= (i / (int)sizeof (pci_regspec_t))) {
44725cf1a30Sjl 		kmem_free(pcmu_rp, i);
44825cf1a30Sjl 		return (0);
44925cf1a30Sjl 	}
45125cf1a30Sjl 	size = pcmu_rp[rnumber].pci_size_low |
45207d06da5SSurya Prakki 	    ((uint64_t)pcmu_rp[rnumber].pci_size_hi << 32);
45325cf1a30Sjl 	kmem_free(pcmu_rp, i);
45425cf1a30Sjl 	return (size);
45525cf1a30Sjl }
45825cf1a30Sjl /*
45925cf1a30Sjl  * pcmu_get_nreg_set
46025cf1a30Sjl  *
46125cf1a30Sjl  * Given a dev info pointer to a pci child, this routine returns the
46225cf1a30Sjl  * number of sets in its "reg" property.
46325cf1a30Sjl  *
46425cf1a30Sjl  * used by: pcmu_ctlops() - DDI_CTLOPS_NREGS
46525cf1a30Sjl  *
46625cf1a30Sjl  * return value: # of reg sets on success, zero on error
46725cf1a30Sjl  */
46825cf1a30Sjl uint_t
pcmu_get_nreg_set(dev_info_t * child)46925cf1a30Sjl pcmu_get_nreg_set(dev_info_t *child)
47025cf1a30Sjl {
47125cf1a30Sjl 	pci_regspec_t *pcmu_rp;
47225cf1a30Sjl 	int i, n;
47425cf1a30Sjl 	/*
47525cf1a30Sjl 	 * Get the reg property for the device.
47625cf1a30Sjl 	 */
47725cf1a30Sjl 	if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg",
47825cf1a30Sjl 	    (caddr_t)&pcmu_rp, &i) != DDI_SUCCESS) {
47925cf1a30Sjl 		return (0);
48025cf1a30Sjl 	}
48125cf1a30Sjl 	n = i / (int)sizeof (pci_regspec_t);
48225cf1a30Sjl 	kmem_free(pcmu_rp, i);
48325cf1a30Sjl 	return (n);
48425cf1a30Sjl }
48625cf1a30Sjl int
pcmu_cfg_report(dev_info_t * dip,ddi_fm_error_t * derr,pcmu_errstate_t * pcmu_err_p,int caller,uint32_t prierr)48725cf1a30Sjl pcmu_cfg_report(dev_info_t *dip, ddi_fm_error_t *derr,
48825cf1a30Sjl     pcmu_errstate_t *pcmu_err_p, int caller, uint32_t prierr)
48925cf1a30Sjl {
49025cf1a30Sjl 	int fatal = 0;
49125cf1a30Sjl 	int nonfatal = 0;
49225cf1a30Sjl 	int i;
49325cf1a30Sjl 	pcmu_t *pcmu_p;
49425cf1a30Sjl 	int instance = ddi_get_instance(dip);
49625cf1a30Sjl 	ASSERT(dip);
49825cf1a30Sjl 	pcmu_p = get_pcmu_soft_state(instance);
50025cf1a30Sjl 	derr->fme_ena = derr->fme_ena ? derr->fme_ena :
50125cf1a30Sjl 	    fm_ena_generate(0, FM_ENA_FMT1);
50325cf1a30Sjl 	for (i = 0; pci_err_tbl[i].err_class != NULL; i++) {
50425cf1a30Sjl 		if (pcmu_err_p->pcmu_cfg_stat & pci_err_tbl[i].reg_bit) {
50525cf1a30Sjl 			char buf[FM_MAX_CLASS];
50625cf1a30Sjl 			char *aux_msg = NULL;
50825cf1a30Sjl 			switch (pci_err_tbl[i].reg_bit) {
50925cf1a30Sjl 			case PCI_STAT_R_MAST_AB:
510*9b65801eSToomas Soome 				aux_msg = "Receieved Master Abort";
511*9b65801eSToomas Soome 				/* FALLTHROUGH */
51225cf1a30Sjl 			case PCI_STAT_R_TARG_AB:
51325cf1a30Sjl 				if (aux_msg != NULL)
514*9b65801eSToomas Soome 					aux_msg = "Receieved Target Abort";
51525cf1a30Sjl 				if (prierr) {
51625cf1a30Sjl 					/*
51725cf1a30Sjl 					 * piow case are already handled in
51825cf1a30Sjl 					 * pcmu_pbm_afsr_report()
51925cf1a30Sjl 					 */
52025cf1a30Sjl 					break;
52125cf1a30Sjl 				}
52225cf1a30Sjl 				if (caller != PCI_TRAP_CALL) {
52325cf1a30Sjl 					/*
52425cf1a30Sjl 					 * if we haven't come from trap handler
52525cf1a30Sjl 					 * we won't have an address
52625cf1a30Sjl 					 */
52725cf1a30Sjl 					fatal++;
52825cf1a30Sjl 				}
52925cf1a30Sjl 				break;
53025cf1a30Sjl 			default:
53125cf1a30Sjl 				/*
53225cf1a30Sjl 				 * dpe on dma write or ta on dma
53325cf1a30Sjl 				 */
53425cf1a30Sjl 				nonfatal++;
53525cf1a30Sjl 				break;
53625cf1a30Sjl 			}
53725cf1a30Sjl 			(void) snprintf(buf, FM_MAX_CLASS, "%s %s: %s %s",
53825cf1a30Sjl 			    (pcmu_p->pcmu_pcbm_p)->pcbm_nameinst_str,
53925cf1a30Sjl 			    (pcmu_p->pcmu_pcbm_p)->pcbm_nameaddr_str,
54025cf1a30Sjl 			    "PCI config space:", aux_msg);
54107d06da5SSurya Prakki 			cmn_err(CE_WARN, "%s %s=0x%p", buf, "pbm-csr",
54207d06da5SSurya Prakki 			    (void *)(pcmu_p->pcmu_pcbm_p)->pcbm_ctrl_reg);
54325cf1a30Sjl 		}
54425cf1a30Sjl 	}
54625cf1a30Sjl 	if (fatal)
54725cf1a30Sjl 		return (DDI_FM_FATAL);
54825cf1a30Sjl 	else if (nonfatal)
54925cf1a30Sjl 		return (DDI_FM_NONFATAL);
55125cf1a30Sjl 	return (DDI_FM_OK);
55225cf1a30Sjl }
55425cf1a30Sjl void
pcmu_child_cfg_save(dev_info_t * dip)55525cf1a30Sjl pcmu_child_cfg_save(dev_info_t *dip)
55625cf1a30Sjl {
55725cf1a30Sjl 	dev_info_t *cdip;
55825cf1a30Sjl 	int ret = DDI_SUCCESS;
56025cf1a30Sjl 	/*
56125cf1a30Sjl 	 * Save the state of the configuration headers of child
56225cf1a30Sjl 	 * nodes.
56325cf1a30Sjl 	 */
56525cf1a30Sjl 	for (cdip = ddi_get_child(dip); cdip != NULL;
56625cf1a30Sjl 	    cdip = ddi_get_next_sibling(cdip)) {
56825cf1a30Sjl 		/*
56925cf1a30Sjl 		 * Not interested in children who are not already
57025cf1a30Sjl 		 * init'ed.  They will be set up in pcmu_init_child().
57125cf1a30Sjl 		 */
57225cf1a30Sjl 		if (i_ddi_node_state(cdip) < DS_INITIALIZED) {
57325cf1a30Sjl 			PCMU_DBG2(PCMU_DBG_DETACH, dip, "DDI_SUSPEND: skipping "
57425cf1a30Sjl 			    "%s%d not in CF1\n", ddi_driver_name(cdip),
57525cf1a30Sjl 			    ddi_get_instance(cdip));
57725cf1a30Sjl 			continue;
57825cf1a30Sjl 		}
58025cf1a30Sjl 		/*
58125cf1a30Sjl 		 * Only save config registers if not already saved by child.
58225cf1a30Sjl 		 */
58325cf1a30Sjl 		if (ddi_prop_exists(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS,
58425cf1a30Sjl 		    SAVED_CONFIG_REGS) == 1) {
58625cf1a30Sjl 			continue;
58725cf1a30Sjl 		}
58925cf1a30Sjl 		/*
59025cf1a30Sjl 		 * The nexus needs to save config registers.  Create a property
59125cf1a30Sjl 		 * so it knows to restore on resume.
59225cf1a30Sjl 		 */
59325cf1a30Sjl 		ret = ndi_prop_create_boolean(DDI_DEV_T_NONE, cdip,
59425cf1a30Sjl 		    "nexus-saved-config-regs");
59625cf1a30Sjl 		if (ret != DDI_PROP_SUCCESS) {
59725cf1a30Sjl 			cmn_err(CE_WARN, "%s%d can't update prop %s",
59825cf1a30Sjl 			    ddi_driver_name(cdip), ddi_get_instance(cdip),
59925cf1a30Sjl 			    "nexus-saved-config-regs");
60025cf1a30Sjl 		}
60225cf1a30Sjl 		(void) pci_save_config_regs(cdip);
60325cf1a30Sjl 	}
60425cf1a30Sjl }
60625cf1a30Sjl void
pcmu_child_cfg_restore(dev_info_t * dip)60725cf1a30Sjl pcmu_child_cfg_restore(dev_info_t *dip)
60825cf1a30Sjl {
60925cf1a30Sjl 	dev_info_t *cdip;
61125cf1a30Sjl 	/*
61225cf1a30Sjl 	 * Restore config registers for children that did not save
61325cf1a30Sjl 	 * their own registers.  Children pwr states are UNKNOWN after
61425cf1a30Sjl 	 * a resume since it is possible for the PM framework to call
61525cf1a30Sjl 	 * resume without an actual power cycle. (ie if suspend fails).
61625cf1a30Sjl 	 */
61725cf1a30Sjl 	for (cdip = ddi_get_child(dip); cdip != NULL;
61825cf1a30Sjl 	    cdip = ddi_get_next_sibling(cdip)) {
62025cf1a30Sjl 		/*
62125cf1a30Sjl 		 * Not interested in children who are not already
62225cf1a30Sjl 		 * init'ed.  They will be set up by pcmu_init_child().
62325cf1a30Sjl 		 */
62425cf1a30Sjl 		if (i_ddi_node_state(cdip) < DS_INITIALIZED) {
62525cf1a30Sjl 			PCMU_DBG2(PCMU_DBG_DETACH, dip,
62625cf1a30Sjl 			    "DDI_RESUME: skipping %s%d not in CF1\n",
62725cf1a30Sjl 			    ddi_driver_name(cdip), ddi_get_instance(cdip));
62825cf1a30Sjl 			continue;
62925cf1a30Sjl 		}
63125cf1a30Sjl 		/*
63225cf1a30Sjl 		 * Only restore config registers if saved by nexus.
63325cf1a30Sjl 		 */
63425cf1a30Sjl 		if (ddi_prop_exists(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS,
63525cf1a30Sjl 		    "nexus-saved-config-regs") == 1) {
63625cf1a30Sjl 			(void) pci_restore_config_regs(cdip);
63825cf1a30Sjl 			PCMU_DBG2(PCMU_DBG_PWR, dip,
63925cf1a30Sjl 			    "DDI_RESUME: nexus restoring %s%d config regs\n",
64025cf1a30Sjl 			    ddi_driver_name(cdip), ddi_get_instance(cdip));
64225cf1a30Sjl 			if (ndi_prop_remove(DDI_DEV_T_NONE, cdip,
64325cf1a30Sjl 			    "nexus-saved-config-regs") != DDI_PROP_SUCCESS) {
64425cf1a30Sjl 				cmn_err(CE_WARN, "%s%d can't remove prop %s",
64525cf1a30Sjl 				    ddi_driver_name(cdip),
64625cf1a30Sjl 				    ddi_get_instance(cdip),
64725cf1a30Sjl 				    "nexus-saved-config-regs");
64825cf1a30Sjl 			}
64925cf1a30Sjl 		}
65025cf1a30Sjl 	}
65125cf1a30Sjl }
65325cf1a30Sjl #ifdef DEBUG
65425cf1a30Sjl extern uint64_t pcmu_debug_flags;
65625cf1a30Sjl pcmu_dflag_to_str_t pcmu_dflag_strings [] = {
65725cf1a30Sjl 	{PCMU_DBG_ATTACH,	"pcmu_attach"},
65825cf1a30Sjl 	{PCMU_DBG_DETACH,	"pcmu_detach"},
65925cf1a30Sjl 	{PCMU_DBG_MAP,		"pcmu_map"},
66025cf1a30Sjl 	{PCMU_DBG_A_INTX,	"pcmu_add_intx"},
66125cf1a30Sjl 	{PCMU_DBG_R_INTX,	"pcmu_rem_intx"},
66225cf1a30Sjl 	{PCMU_DBG_INIT_CLD,	"pcmu_init_child"},
66325cf1a30Sjl 	{PCMU_DBG_CTLOPS,	"pcmu_ctlops"},
66425cf1a30Sjl 	{PCMU_DBG_INTR,		"pcmu_intr_wrapper"},
66525cf1a30Sjl 	{PCMU_DBG_ERR_INTR,	"pcmu_pbm_error_intr"},
66625cf1a30Sjl 	{PCMU_DBG_BUS_FAULT,	"pcmu_fault"},
66725cf1a30Sjl 	{PCMU_DBG_IB,		"pcmu_ib"},
66825cf1a30Sjl 	{PCMU_DBG_CB,		"pcmu_cb"},
66925cf1a30Sjl 	{PCMU_DBG_PBM,		"pcmu_pbm"},
67025cf1a30Sjl 	{PCMU_DBG_OPEN,		"pcmu_open"},
67125cf1a30Sjl 	{PCMU_DBG_CLOSE,	"pcmu_close"},
67225cf1a30Sjl 	{PCMU_DBG_IOCTL,	"pcmu_ioctl"},
67325cf1a30Sjl 	{PCMU_DBG_PWR,		"pcmu_pwr"}
67425cf1a30Sjl };
67625cf1a30Sjl void
pcmu_debug(uint64_t flag,dev_info_t * dip,char * fmt,uintptr_t a1,uintptr_t a2,uintptr_t a3,uintptr_t a4,uintptr_t a5)67725cf1a30Sjl pcmu_debug(uint64_t flag, dev_info_t *dip, char *fmt,
678*9b65801eSToomas Soome     uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5)
67925cf1a30Sjl {
68025cf1a30Sjl 	char *s = "pcmu unknown";
68125cf1a30Sjl 	uint_t cont = 0;
68225cf1a30Sjl 	int i;
68325cf1a30Sjl 	int no_rec = (sizeof (pcmu_dflag_strings) /
68425cf1a30Sjl 	    sizeof (pcmu_dflag_to_str_t));
68625cf1a30Sjl 	if (flag & PCMU_DBG_CONT) {
68725cf1a30Sjl 		flag &= ~PCMU_DBG_CONT;
68825cf1a30Sjl 		cont = 1;
68925cf1a30Sjl 	}
69025cf1a30Sjl 	if ((pcmu_debug_flags & flag) == flag) {
69125cf1a30Sjl 		for (i = 0; i < no_rec; i++) {
69225cf1a30Sjl 			if (pcmu_dflag_strings[i].flag == flag) {
69325cf1a30Sjl 				s = pcmu_dflag_strings[i].string;
69425cf1a30Sjl 				break;
69525cf1a30Sjl 			}
69625cf1a30Sjl 		}
69725cf1a30Sjl 		if (s && cont == 0) {
69825cf1a30Sjl 			prom_printf("%s(%d): %s: ", ddi_driver_name(dip),
69925cf1a30Sjl 			    ddi_get_instance(dip), s);
70025cf1a30Sjl 		}
70125cf1a30Sjl 		prom_printf(fmt, a1, a2, a3, a4, a5);
70225cf1a30Sjl 	}
70325cf1a30Sjl }
70425cf1a30Sjl #endif