xref: /onnv-gate/usr/src/uts/sparc/io/pciex/pcieb_sparc.c (revision 11236:1127b4f9e96b)
110187SKrishna.Elango@Sun.COM /*
210187SKrishna.Elango@Sun.COM  * CDDL HEADER START
310187SKrishna.Elango@Sun.COM  *
410187SKrishna.Elango@Sun.COM  * The contents of this file are subject to the terms of the
510187SKrishna.Elango@Sun.COM  * Common Development and Distribution License (the "License").
610187SKrishna.Elango@Sun.COM  * You may not use this file except in compliance with the License.
710187SKrishna.Elango@Sun.COM  *
810187SKrishna.Elango@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910187SKrishna.Elango@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1010187SKrishna.Elango@Sun.COM  * See the License for the specific language governing permissions
1110187SKrishna.Elango@Sun.COM  * and limitations under the License.
1210187SKrishna.Elango@Sun.COM  *
1310187SKrishna.Elango@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1410187SKrishna.Elango@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510187SKrishna.Elango@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1610187SKrishna.Elango@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1710187SKrishna.Elango@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1810187SKrishna.Elango@Sun.COM  *
1910187SKrishna.Elango@Sun.COM  * CDDL HEADER END
2010187SKrishna.Elango@Sun.COM  */
2110187SKrishna.Elango@Sun.COM /*
2210187SKrishna.Elango@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2310187SKrishna.Elango@Sun.COM  * Use is subject to license terms.
2410187SKrishna.Elango@Sun.COM  */
2510187SKrishna.Elango@Sun.COM 
2610187SKrishna.Elango@Sun.COM /* SPARC specific code used by the pcieb driver */
2710187SKrishna.Elango@Sun.COM 
2810187SKrishna.Elango@Sun.COM #include <sys/types.h>
2910187SKrishna.Elango@Sun.COM #include <sys/ddi.h>
3010187SKrishna.Elango@Sun.COM #include <sys/kmem.h>
3110187SKrishna.Elango@Sun.COM #include <sys/sysmacros.h>
3210187SKrishna.Elango@Sun.COM #include <sys/sunddi.h>
3310187SKrishna.Elango@Sun.COM #include <sys/sunndi.h>
3410187SKrishna.Elango@Sun.COM #include <sys/pcie.h>
3510187SKrishna.Elango@Sun.COM #include <sys/pci_cap.h>
3610187SKrishna.Elango@Sun.COM #include <sys/pcie_impl.h>
3710187SKrishna.Elango@Sun.COM #include <io/pciex/pcieb.h>
3810187SKrishna.Elango@Sun.COM #include "pcieb_plx.h"
3910187SKrishna.Elango@Sun.COM 
4010187SKrishna.Elango@Sun.COM /*LINTLIBRARY*/
4110187SKrishna.Elango@Sun.COM 
4210187SKrishna.Elango@Sun.COM /* PLX specific functions */
4310187SKrishna.Elango@Sun.COM #ifdef	PX_PLX
4410187SKrishna.Elango@Sun.COM static void plx_ro_disable(pcieb_devstate_t *pcieb);
4510187SKrishna.Elango@Sun.COM #ifdef	PRINT_PLX_SEEPROM_CRC
4610187SKrishna.Elango@Sun.COM static void pcieb_print_plx_seeprom_crc_data(pcieb_devstate_t *pcieb_p);
4710187SKrishna.Elango@Sun.COM #endif /* PRINT_PLX_SEEPROM_CRC */
4810187SKrishna.Elango@Sun.COM #endif /* PX_PLX */
4910187SKrishna.Elango@Sun.COM 
5010187SKrishna.Elango@Sun.COM int
pcieb_plat_peekpoke(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t ctlop,void * arg,void * result)5110187SKrishna.Elango@Sun.COM pcieb_plat_peekpoke(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop,
5210187SKrishna.Elango@Sun.COM     void *arg, void *result)
5310187SKrishna.Elango@Sun.COM {
5410187SKrishna.Elango@Sun.COM 	return (ddi_ctlops(dip, rdip, ctlop, arg, result));
5510187SKrishna.Elango@Sun.COM }
5610187SKrishna.Elango@Sun.COM 
5710187SKrishna.Elango@Sun.COM /*ARGSUSED*/
5810187SKrishna.Elango@Sun.COM void
pcieb_set_prot_scan(dev_info_t * dip,ddi_acc_impl_t * hdlp)59*11236SStephen.Hanson@Sun.COM pcieb_set_prot_scan(dev_info_t *dip, ddi_acc_impl_t *hdlp)
60*11236SStephen.Hanson@Sun.COM {
61*11236SStephen.Hanson@Sun.COM }
62*11236SStephen.Hanson@Sun.COM 
63*11236SStephen.Hanson@Sun.COM /*ARGSUSED*/
64*11236SStephen.Hanson@Sun.COM void
pcieb_plat_attach_workaround(dev_info_t * dip)6510187SKrishna.Elango@Sun.COM pcieb_plat_attach_workaround(dev_info_t *dip)
6610187SKrishna.Elango@Sun.COM {
6710187SKrishna.Elango@Sun.COM }
6810187SKrishna.Elango@Sun.COM 
6910187SKrishna.Elango@Sun.COM int
pcieb_plat_intr_ops(dev_info_t * dip,dev_info_t * rdip,ddi_intr_op_t intr_op,ddi_intr_handle_impl_t * hdlp,void * result)7010187SKrishna.Elango@Sun.COM pcieb_plat_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
7110187SKrishna.Elango@Sun.COM     ddi_intr_handle_impl_t *hdlp, void *result)
7210187SKrishna.Elango@Sun.COM {
7310187SKrishna.Elango@Sun.COM 	dev_info_t	*cdip = rdip;
7410187SKrishna.Elango@Sun.COM 	pci_regspec_t	*pci_rp;
7510187SKrishna.Elango@Sun.COM 	int		reglen, len;
7610187SKrishna.Elango@Sun.COM 	uint32_t	d, intr;
7710187SKrishna.Elango@Sun.COM 
7810187SKrishna.Elango@Sun.COM 	if ((intr_op == DDI_INTROP_SUPPORTED_TYPES) ||
7910187SKrishna.Elango@Sun.COM 	    (hdlp->ih_type != DDI_INTR_TYPE_FIXED))
8010187SKrishna.Elango@Sun.COM 		goto done;
8110187SKrishna.Elango@Sun.COM 
8210187SKrishna.Elango@Sun.COM 	/*
8310187SKrishna.Elango@Sun.COM 	 * If the interrupt-map property is defined at this
8410187SKrishna.Elango@Sun.COM 	 * node, it will have performed the interrupt
8510187SKrishna.Elango@Sun.COM 	 * translation as part of the property, so no
8610187SKrishna.Elango@Sun.COM 	 * rotation needs to be done.
8710187SKrishna.Elango@Sun.COM 	 */
8810187SKrishna.Elango@Sun.COM 	if (ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
8910187SKrishna.Elango@Sun.COM 	    "interrupt-map", &len) == DDI_PROP_SUCCESS)
9010187SKrishna.Elango@Sun.COM 		goto done;
9110187SKrishna.Elango@Sun.COM 
9210187SKrishna.Elango@Sun.COM 	cdip = pcie_get_my_childs_dip(dip, rdip);
9310187SKrishna.Elango@Sun.COM 
9410187SKrishna.Elango@Sun.COM 	/*
9510187SKrishna.Elango@Sun.COM 	 * Use the devices reg property to determine its
9610187SKrishna.Elango@Sun.COM 	 * PCI bus number and device number.
9710187SKrishna.Elango@Sun.COM 	 */
9810187SKrishna.Elango@Sun.COM 	if (ddi_getlongprop(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS,
9910187SKrishna.Elango@Sun.COM 	    "reg", (caddr_t)&pci_rp, &reglen) != DDI_SUCCESS)
10010187SKrishna.Elango@Sun.COM 		return (DDI_FAILURE);
10110187SKrishna.Elango@Sun.COM 
10210187SKrishna.Elango@Sun.COM 	intr = hdlp->ih_vector;
10310187SKrishna.Elango@Sun.COM 
10410923SEvan.Yan@Sun.COM 	d = (pcie_ari_is_enabled(dip) == PCIE_ARI_FORW_ENABLED) ? 0 :
10510923SEvan.Yan@Sun.COM 	    PCI_REG_DEV_G(pci_rp[0].pci_phys_hi);
10610923SEvan.Yan@Sun.COM 
10710187SKrishna.Elango@Sun.COM 	/* spin the interrupt */
10810187SKrishna.Elango@Sun.COM 	if ((intr >= PCI_INTA) && (intr <= PCI_INTD))
10910187SKrishna.Elango@Sun.COM 		hdlp->ih_vector = ((intr - 1 + (d % 4)) % 4 + 1);
11010187SKrishna.Elango@Sun.COM 	else
11110187SKrishna.Elango@Sun.COM 		cmn_err(CE_WARN, "%s%d: %s: PCI intr=%x out of range",
11210187SKrishna.Elango@Sun.COM 		    ddi_driver_name(rdip), ddi_get_instance(rdip),
11310187SKrishna.Elango@Sun.COM 		    ddi_driver_name(dip), intr);
11410187SKrishna.Elango@Sun.COM 
11510187SKrishna.Elango@Sun.COM 	kmem_free(pci_rp, reglen);
11610187SKrishna.Elango@Sun.COM 
11710187SKrishna.Elango@Sun.COM done:
11810187SKrishna.Elango@Sun.COM 	/* Pass up the request to our parent. */
11910187SKrishna.Elango@Sun.COM 	return (i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result));
12010187SKrishna.Elango@Sun.COM }
12110187SKrishna.Elango@Sun.COM 
12210187SKrishna.Elango@Sun.COM int
pcieb_plat_pcishpc_probe(dev_info_t * dip,ddi_acc_handle_t config_handle)12310187SKrishna.Elango@Sun.COM pcieb_plat_pcishpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle)
12410187SKrishna.Elango@Sun.COM {
12510187SKrishna.Elango@Sun.COM 	uint16_t cap_ptr;
12610187SKrishna.Elango@Sun.COM 	if ((PCI_CAP_LOCATE(config_handle, PCI_CAP_ID_PCI_HOTPLUG, &cap_ptr)) !=
12710187SKrishna.Elango@Sun.COM 	    DDI_FAILURE) {
12810187SKrishna.Elango@Sun.COM 		return (DDI_SUCCESS);
12910187SKrishna.Elango@Sun.COM 	}
13010187SKrishna.Elango@Sun.COM 
13110187SKrishna.Elango@Sun.COM 	return (DDI_FAILURE);
13210187SKrishna.Elango@Sun.COM }
13310187SKrishna.Elango@Sun.COM 
13410187SKrishna.Elango@Sun.COM /*
13510187SKrishna.Elango@Sun.COM  *  Disable PM on PLX. For PLX Transitioning one port on this switch to
13610187SKrishna.Elango@Sun.COM  *  low power causes links on other ports on the same station to die.
13710187SKrishna.Elango@Sun.COM  *  Due to PLX erratum #34, we can't allow the downstream device go to
13810187SKrishna.Elango@Sun.COM  *  non-D0 state.
13910187SKrishna.Elango@Sun.COM  */
14010187SKrishna.Elango@Sun.COM boolean_t
pcieb_plat_pwr_disable(dev_info_t * dip)14110187SKrishna.Elango@Sun.COM pcieb_plat_pwr_disable(dev_info_t *dip)
14210187SKrishna.Elango@Sun.COM {
14310187SKrishna.Elango@Sun.COM 	uint16_t vendor_id = (PCIE_DIP2UPBUS(dip)->bus_dev_ven_id) & 0xFFFF;
14410187SKrishna.Elango@Sun.COM 	return (IS_PLX_VENDORID(vendor_id) ? B_TRUE : B_FALSE);
14510187SKrishna.Elango@Sun.COM }
14610187SKrishna.Elango@Sun.COM 
14710187SKrishna.Elango@Sun.COM /*ARGSUSED*/
14810187SKrishna.Elango@Sun.COM boolean_t
pcieb_plat_msi_supported(dev_info_t * dip)14910187SKrishna.Elango@Sun.COM pcieb_plat_msi_supported(dev_info_t *dip)
15010187SKrishna.Elango@Sun.COM {
15110187SKrishna.Elango@Sun.COM 	return (B_TRUE);
15210187SKrishna.Elango@Sun.COM }
15310187SKrishna.Elango@Sun.COM 
15410187SKrishna.Elango@Sun.COM /*ARGSUSED*/
15510187SKrishna.Elango@Sun.COM void
pcieb_plat_intr_attach(pcieb_devstate_t * pcieb)15610187SKrishna.Elango@Sun.COM pcieb_plat_intr_attach(pcieb_devstate_t *pcieb)
15710187SKrishna.Elango@Sun.COM {
15810187SKrishna.Elango@Sun.COM }
15910187SKrishna.Elango@Sun.COM 
16010187SKrishna.Elango@Sun.COM /*ARGSUSED*/
16110187SKrishna.Elango@Sun.COM int
pcieb_plat_ctlops(dev_info_t * rdip,ddi_ctl_enum_t ctlop,void * arg)16210187SKrishna.Elango@Sun.COM pcieb_plat_ctlops(dev_info_t *rdip, ddi_ctl_enum_t ctlop, void *arg)
16310187SKrishna.Elango@Sun.COM {
16410187SKrishna.Elango@Sun.COM 	return (DDI_SUCCESS);
16510187SKrishna.Elango@Sun.COM }
16610187SKrishna.Elango@Sun.COM 
16710187SKrishna.Elango@Sun.COM void
pcieb_plat_initchild(dev_info_t * child)16810187SKrishna.Elango@Sun.COM pcieb_plat_initchild(dev_info_t *child)
16910187SKrishna.Elango@Sun.COM {
17010187SKrishna.Elango@Sun.COM 	intptr_t ppd = NULL;
17110187SKrishna.Elango@Sun.COM 	/*
17210187SKrishna.Elango@Sun.COM 	 * XXX set ppd to 1 to disable iommu BDF protection on SPARC.
17310187SKrishna.Elango@Sun.COM 	 * It relies on unused parent private data for PCI devices.
17410187SKrishna.Elango@Sun.COM 	 */
17510187SKrishna.Elango@Sun.COM 	if (ddi_prop_exists(DDI_DEV_T_NONE, child, DDI_PROP_DONTPASS,
17610187SKrishna.Elango@Sun.COM 	    "dvma-share"))
17710187SKrishna.Elango@Sun.COM 		ppd = 1;
17810187SKrishna.Elango@Sun.COM 
17910187SKrishna.Elango@Sun.COM 	ddi_set_parent_data(child, (void *)ppd);
18010187SKrishna.Elango@Sun.COM }
18110187SKrishna.Elango@Sun.COM 
18210187SKrishna.Elango@Sun.COM void
pcieb_plat_uninitchild(dev_info_t * child)18310187SKrishna.Elango@Sun.COM pcieb_plat_uninitchild(dev_info_t *child)
18410187SKrishna.Elango@Sun.COM {
18510187SKrishna.Elango@Sun.COM 	/*
18610187SKrishna.Elango@Sun.COM 	 * XXX Clear parent private data used as a flag to disable
18710187SKrishna.Elango@Sun.COM 	 * iommu BDF protection
18810187SKrishna.Elango@Sun.COM 	 */
18910187SKrishna.Elango@Sun.COM 	if ((intptr_t)ddi_get_parent_data(child) == 1)
19010187SKrishna.Elango@Sun.COM 		ddi_set_parent_data(child, NULL);
19110187SKrishna.Elango@Sun.COM }
19210187SKrishna.Elango@Sun.COM 
19310187SKrishna.Elango@Sun.COM #ifdef PX_PLX
19410187SKrishna.Elango@Sun.COM /*
19510187SKrishna.Elango@Sun.COM  * These are PLX specific workarounds needed during attach.
19610187SKrishna.Elango@Sun.COM  */
19710187SKrishna.Elango@Sun.COM void
pcieb_attach_plx_workarounds(pcieb_devstate_t * pcieb)19810187SKrishna.Elango@Sun.COM pcieb_attach_plx_workarounds(pcieb_devstate_t *pcieb)
19910187SKrishna.Elango@Sun.COM {
20010187SKrishna.Elango@Sun.COM 	dev_info_t	*dip = pcieb->pcieb_dip;
20110187SKrishna.Elango@Sun.COM 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
20210187SKrishna.Elango@Sun.COM 	ddi_acc_handle_t	config_handle = bus_p->bus_cfg_hdl;
20310187SKrishna.Elango@Sun.COM 	uint_t		bus_num, primary, secondary;
20410187SKrishna.Elango@Sun.COM 	uint8_t		dev_type = bus_p->bus_dev_type;
20510187SKrishna.Elango@Sun.COM 	uint16_t	vendor_id = bus_p->bus_dev_ven_id & 0xFFFF;
20610923SEvan.Yan@Sun.COM 	uint16_t	device_id = bus_p->bus_dev_ven_id >> 16;
20710379SZachary.Kissel@Sun.COM 	int 		ce_mask = 0;
20810187SKrishna.Elango@Sun.COM 
20910187SKrishna.Elango@Sun.COM 	if (!IS_PLX_VENDORID(vendor_id))
21010187SKrishna.Elango@Sun.COM 		return;
21110187SKrishna.Elango@Sun.COM 
21210923SEvan.Yan@Sun.COM 	if ((device_id == PXB_DEVICE_PLX_8532) &&
21310923SEvan.Yan@Sun.COM 	    (bus_p->bus_rev_id <= PXB_DEVICE_PLX_AA_REV))
21410923SEvan.Yan@Sun.COM 		/* Clear hotplug capability */
21510923SEvan.Yan@Sun.COM 		bus_p->bus_hp_sup_modes = PCIE_NONE_HP_MODE;
21610923SEvan.Yan@Sun.COM 
21710187SKrishna.Elango@Sun.COM 	/*
21810187SKrishna.Elango@Sun.COM 	 * Due to a PLX HW bug we need to disable the receiver error CE on all
21910187SKrishna.Elango@Sun.COM 	 * ports. To this end we create a property "pcie_ce_mask" with value
22010187SKrishna.Elango@Sun.COM 	 * set to PCIE_AER_CE_RECEIVER_ERR. The pcie module will check for this
22110379SZachary.Kissel@Sun.COM 	 * property before setting the AER CE mask. Be sure to honor all other
22210379SZachary.Kissel@Sun.COM 	 * pcie_ce_mask settings.
22310187SKrishna.Elango@Sun.COM 	 */
22410379SZachary.Kissel@Sun.COM 	ce_mask = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
22510379SZachary.Kissel@Sun.COM 	    "pcie_ce_mask", 0);
22610187SKrishna.Elango@Sun.COM 	(void) ddi_prop_update_int(DDI_DEV_T_NONE, dip,
22710379SZachary.Kissel@Sun.COM 	    "pcie_ce_mask", (PCIE_AER_CE_RECEIVER_ERR|ce_mask));
22810187SKrishna.Elango@Sun.COM 
22910187SKrishna.Elango@Sun.COM 	/*
23010187SKrishna.Elango@Sun.COM 	 * There is a bug in the PLX 8114 bridge, such that an 8-bit
23110187SKrishna.Elango@Sun.COM 	 * write to the secondary bus number register will corrupt an
23210187SKrishna.Elango@Sun.COM 	 * internal shadow copy of the primary bus number.  Reading
23310187SKrishna.Elango@Sun.COM 	 * out the registers and writing the same values back as
23410187SKrishna.Elango@Sun.COM 	 * 16-bits resolves the problem.  This bug was reported by
23510187SKrishna.Elango@Sun.COM 	 * PLX as errata #19.
23610187SKrishna.Elango@Sun.COM 	 */
23710187SKrishna.Elango@Sun.COM 	primary = pci_config_get8(config_handle, PCI_BCNF_PRIBUS);
23810187SKrishna.Elango@Sun.COM 	secondary = pci_config_get8(config_handle, PCI_BCNF_SECBUS);
23910187SKrishna.Elango@Sun.COM 	bus_num = (secondary << 8) | primary;
24010187SKrishna.Elango@Sun.COM 	pci_config_put16(config_handle, PCI_BCNF_PRIBUS, bus_num);
24110187SKrishna.Elango@Sun.COM 
24210187SKrishna.Elango@Sun.COM 	/*
24310187SKrishna.Elango@Sun.COM 	 * Workaround for a race condition between hotplug
24410187SKrishna.Elango@Sun.COM 	 * initialization and actual MSI interrupt registration
24510187SKrishna.Elango@Sun.COM 	 * for hotplug functionality. The hotplug initialization
24610187SKrishna.Elango@Sun.COM 	 * generates an INTx interrupt for hotplug events and this
24710187SKrishna.Elango@Sun.COM 	 * INTx interrupt may interfere with shared leaf drivers
24810187SKrishna.Elango@Sun.COM 	 * using same INTx interrupt, which may eventually block
24910187SKrishna.Elango@Sun.COM 	 * the leaf drivers.
25010187SKrishna.Elango@Sun.COM 	 */
25110187SKrishna.Elango@Sun.COM 	if ((dev_type == PCIE_PCIECAP_DEV_TYPE_DOWN) ||
25210187SKrishna.Elango@Sun.COM 	    (dev_type == PCIE_PCIECAP_DEV_TYPE_ROOT) ||
25310187SKrishna.Elango@Sun.COM 	    (dev_type == PCIE_PCIECAP_DEV_TYPE_PCIE2PCI) ||
25410187SKrishna.Elango@Sun.COM 	    (dev_type == PCIE_PCIECAP_DEV_TYPE_PCI2PCIE)) {
25510187SKrishna.Elango@Sun.COM 		pci_config_put16(config_handle, PCI_CONF_COMM,
25610187SKrishna.Elango@Sun.COM 		    pci_config_get16(config_handle, PCI_CONF_COMM) |
25710187SKrishna.Elango@Sun.COM 		    PCI_COMM_INTX_DISABLE);
25810187SKrishna.Elango@Sun.COM 	}
25910187SKrishna.Elango@Sun.COM 
26010187SKrishna.Elango@Sun.COM 	/*
26110187SKrishna.Elango@Sun.COM 	 * Disable PLX Special Relaxed Ordering
26210187SKrishna.Elango@Sun.COM 	 */
26310187SKrishna.Elango@Sun.COM 	plx_ro_disable(pcieb);
26410187SKrishna.Elango@Sun.COM 
26510187SKrishna.Elango@Sun.COM #ifdef	PRINT_PLX_SEEPROM_CRC
26610187SKrishna.Elango@Sun.COM 	/* check seeprom CRC to ensure the platform config is right */
26710187SKrishna.Elango@Sun.COM 	(void) pcieb_print_plx_seeprom_crc_data(pcieb);
26810187SKrishna.Elango@Sun.COM #endif /* PRINT_PLX_SEEPROM_CRC */
26910187SKrishna.Elango@Sun.COM }
27010187SKrishna.Elango@Sun.COM 
27110187SKrishna.Elango@Sun.COM /*
27210187SKrishna.Elango@Sun.COM  * These are PLX specific workarounds called during child's initchild.
27310187SKrishna.Elango@Sun.COM  */
27410187SKrishna.Elango@Sun.COM int
pcieb_init_plx_workarounds(pcieb_devstate_t * pcieb,dev_info_t * child)27510187SKrishna.Elango@Sun.COM pcieb_init_plx_workarounds(pcieb_devstate_t *pcieb, dev_info_t *child)
27610187SKrishna.Elango@Sun.COM {
27710187SKrishna.Elango@Sun.COM 	int		i;
27810187SKrishna.Elango@Sun.COM 	int		result = DDI_FAILURE;
27910187SKrishna.Elango@Sun.COM 	uint16_t	reg = 0;
28010187SKrishna.Elango@Sun.COM 	ddi_acc_handle_t	config_handle;
28110187SKrishna.Elango@Sun.COM 	uint16_t	vendor_id =
28210187SKrishna.Elango@Sun.COM 	    (PCIE_DIP2UPBUS(pcieb->pcieb_dip))->bus_dev_ven_id & 0xFFFF;
28310187SKrishna.Elango@Sun.COM 
28410187SKrishna.Elango@Sun.COM 	if (!IS_PLX_VENDORID(vendor_id))
28510187SKrishna.Elango@Sun.COM 		return (DDI_SUCCESS);
28610187SKrishna.Elango@Sun.COM 
28710187SKrishna.Elango@Sun.COM 	/*
28810187SKrishna.Elango@Sun.COM 	 * Due to a PLX HW bug, a SW workaround to prevent the chip from
28910187SKrishna.Elango@Sun.COM 	 * wedging is needed.  SW just needs to tranfer 64 TLPs from
29010187SKrishna.Elango@Sun.COM 	 * the downstream port to the child device.
29110187SKrishna.Elango@Sun.COM 	 * The most benign way of doing this is to read the ID register
29210187SKrishna.Elango@Sun.COM 	 * 64 times.  This SW workaround should have minimum performance
29310187SKrishna.Elango@Sun.COM 	 * impact and shouldn't cause a problem for all other bridges
29410187SKrishna.Elango@Sun.COM 	 * and switches.
29510187SKrishna.Elango@Sun.COM 	 *
29610187SKrishna.Elango@Sun.COM 	 * The code needs to be written in a way to make sure it isn't
29710187SKrishna.Elango@Sun.COM 	 * optimized out.
29810187SKrishna.Elango@Sun.COM 	 */
29910187SKrishna.Elango@Sun.COM 	if (!pxb_tlp_count) {
30010187SKrishna.Elango@Sun.COM 		result = DDI_SUCCESS;
30110187SKrishna.Elango@Sun.COM 		goto done;
30210187SKrishna.Elango@Sun.COM 	}
30310187SKrishna.Elango@Sun.COM 
30410187SKrishna.Elango@Sun.COM 	if (pci_config_setup(child, &config_handle) != DDI_SUCCESS) {
30510187SKrishna.Elango@Sun.COM 		result = DDI_FAILURE;
30610187SKrishna.Elango@Sun.COM 		goto done;
30710187SKrishna.Elango@Sun.COM 	}
30810187SKrishna.Elango@Sun.COM 
30910187SKrishna.Elango@Sun.COM 	for (i = 0; i < pxb_tlp_count; i += 1)
31010187SKrishna.Elango@Sun.COM 		reg |= pci_config_get16(config_handle, PCI_CONF_VENID);
31110187SKrishna.Elango@Sun.COM 
31210187SKrishna.Elango@Sun.COM 	if (PCIE_IS_PCIE_BDG(PCIE_DIP2BUS(pcieb->pcieb_dip)))
31310187SKrishna.Elango@Sun.COM 		pcieb_set_pci_perf_parameters(child, config_handle);
31410187SKrishna.Elango@Sun.COM 
31510187SKrishna.Elango@Sun.COM 	pci_config_teardown(&config_handle);
31610187SKrishna.Elango@Sun.COM 	result = DDI_SUCCESS;
31710187SKrishna.Elango@Sun.COM done:
31810187SKrishna.Elango@Sun.COM 	return (result);
31910187SKrishna.Elango@Sun.COM }
32010187SKrishna.Elango@Sun.COM 
32110187SKrishna.Elango@Sun.COM /*
32210187SKrishna.Elango@Sun.COM  * Disable PLX specific relaxed ordering mode.	Due to PLX
32310187SKrishna.Elango@Sun.COM  * erratum #6, use of this mode with Cut-Through Cancellation
32410187SKrishna.Elango@Sun.COM  * can result in dropped Completion type packets.
32510187SKrishna.Elango@Sun.COM  *
32610187SKrishna.Elango@Sun.COM  * Clear the Relaxed Ordering Mode on 8533 and 8548 switches.
32710187SKrishna.Elango@Sun.COM  * To disable RO, clear bit 5 in offset 0x664, an undocumented
32810187SKrishna.Elango@Sun.COM  * bit in the PLX spec, on Ports 0, 8 and 12.  Proprietary PLX
32910187SKrishna.Elango@Sun.COM  * registers are normally accessible only via memspace from Port
33010187SKrishna.Elango@Sun.COM  * 0.  If port 0 is attached go ahead and disable RO on Port 0,
33110187SKrishna.Elango@Sun.COM  * 8 and 12, if they exist.
33210187SKrishna.Elango@Sun.COM  */
33310187SKrishna.Elango@Sun.COM static void
plx_ro_disable(pcieb_devstate_t * pcieb)33410187SKrishna.Elango@Sun.COM plx_ro_disable(pcieb_devstate_t *pcieb)
33510187SKrishna.Elango@Sun.COM {
33610187SKrishna.Elango@Sun.COM 	pcie_bus_t		*bus_p = PCIE_DIP2BUS(pcieb->pcieb_dip);
33710187SKrishna.Elango@Sun.COM 	dev_info_t		*dip = pcieb->pcieb_dip;
33810187SKrishna.Elango@Sun.COM 	uint16_t		device_id = bus_p->bus_dev_ven_id >> 16;
33910187SKrishna.Elango@Sun.COM 	pci_regspec_t		*reg_spec, *addr_spec;
34010187SKrishna.Elango@Sun.COM 	int			rlen, alen;
34110187SKrishna.Elango@Sun.COM 	int			orig_rsize, new_rsize;
34210187SKrishna.Elango@Sun.COM 	uint_t			rnum, anum;
34310187SKrishna.Elango@Sun.COM 	ddi_device_acc_attr_t	attr;
34410187SKrishna.Elango@Sun.COM 	ddi_acc_handle_t	hdl;
34510187SKrishna.Elango@Sun.COM 	caddr_t			regsp;
34610187SKrishna.Elango@Sun.COM 	uint32_t		val, port_enable;
34710187SKrishna.Elango@Sun.COM 	char			*offset;
34810187SKrishna.Elango@Sun.COM 	char			*port_offset;
34910187SKrishna.Elango@Sun.COM 
35010187SKrishna.Elango@Sun.COM 	if (!((device_id == PXB_DEVICE_PLX_8533) ||
35110187SKrishna.Elango@Sun.COM 	    (device_id == PXB_DEVICE_PLX_8548)))
35210187SKrishna.Elango@Sun.COM 		return;
35310187SKrishna.Elango@Sun.COM 
35410187SKrishna.Elango@Sun.COM 	/* You can also only do this on Port 0 */
35510187SKrishna.Elango@Sun.COM 	val = PCIE_CAP_GET(32, bus_p, PCIE_LINKCAP);
35610187SKrishna.Elango@Sun.COM 	val = (val >> PCIE_LINKCAP_PORT_NUMBER_SHIFT) &
35710187SKrishna.Elango@Sun.COM 	    PCIE_LINKCAP_PORT_NUMBER_MASK;
35810187SKrishna.Elango@Sun.COM 
35910187SKrishna.Elango@Sun.COM 	PCIEB_DEBUG(DBG_ATTACH, dip, "PLX RO Disable : bdf=0x%x port=%d\n",
36010187SKrishna.Elango@Sun.COM 	    bus_p->bus_bdf, val);
36110187SKrishna.Elango@Sun.COM 
36210187SKrishna.Elango@Sun.COM 	if (val != 0)
36310187SKrishna.Elango@Sun.COM 		return;
36410187SKrishna.Elango@Sun.COM 
36510187SKrishna.Elango@Sun.COM 	/*
36610187SKrishna.Elango@Sun.COM 	 * Read the reg property, but allocate extra space incase we need to add
36710187SKrishna.Elango@Sun.COM 	 * a new entry later.
36810187SKrishna.Elango@Sun.COM 	 */
36910187SKrishna.Elango@Sun.COM 	if (ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg",
37010187SKrishna.Elango@Sun.COM 	    &orig_rsize) != DDI_SUCCESS)
37110187SKrishna.Elango@Sun.COM 		return;
37210187SKrishna.Elango@Sun.COM 
37310187SKrishna.Elango@Sun.COM 	new_rsize = orig_rsize + sizeof (pci_regspec_t);
37410187SKrishna.Elango@Sun.COM 	reg_spec = kmem_alloc(new_rsize, KM_SLEEP);
37510187SKrishna.Elango@Sun.COM 
37610187SKrishna.Elango@Sun.COM 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg",
37710187SKrishna.Elango@Sun.COM 	    (caddr_t)reg_spec, &orig_rsize) != DDI_SUCCESS)
37810187SKrishna.Elango@Sun.COM 		goto fail;
37910187SKrishna.Elango@Sun.COM 
38010187SKrishna.Elango@Sun.COM 	/* Find the mem32 reg property */
38110187SKrishna.Elango@Sun.COM 	rlen = orig_rsize / sizeof (pci_regspec_t);
38210187SKrishna.Elango@Sun.COM 	for (rnum = 0; rnum < rlen; rnum++) {
38310187SKrishna.Elango@Sun.COM 		if ((reg_spec[rnum].pci_phys_hi & PCI_ADDR_MASK) ==
38410187SKrishna.Elango@Sun.COM 		    PCI_ADDR_MEM32)
38510187SKrishna.Elango@Sun.COM 			goto fix;
38610187SKrishna.Elango@Sun.COM 	}
38710187SKrishna.Elango@Sun.COM 
38810187SKrishna.Elango@Sun.COM 	/*
38910187SKrishna.Elango@Sun.COM 	 * Mem32 reg property was not found.
39010187SKrishna.Elango@Sun.COM 	 * Look for it in assign-address property.
39110187SKrishna.Elango@Sun.COM 	 */
39210187SKrishna.Elango@Sun.COM 	addr_spec = bus_p->bus_assigned_addr;
39310187SKrishna.Elango@Sun.COM 	alen = bus_p->bus_assigned_entries;
39410187SKrishna.Elango@Sun.COM 	for (anum = 0; anum < alen; anum++) {
39510187SKrishna.Elango@Sun.COM 		if ((addr_spec[anum].pci_phys_hi & PCI_ADDR_MASK) ==
39610187SKrishna.Elango@Sun.COM 		    PCI_ADDR_MEM32)
39710187SKrishna.Elango@Sun.COM 			goto update;
39810187SKrishna.Elango@Sun.COM 	}
39910187SKrishna.Elango@Sun.COM 
40010187SKrishna.Elango@Sun.COM 	/* Unable to find mem space assigned address, give up. */
40110187SKrishna.Elango@Sun.COM 	goto fail;
40210187SKrishna.Elango@Sun.COM 
40310187SKrishna.Elango@Sun.COM update:
40410187SKrishna.Elango@Sun.COM 	/*
40510187SKrishna.Elango@Sun.COM 	 * Add the mem32 access to the reg spec.
40610187SKrishna.Elango@Sun.COM 	 * Use the last entry which was previously allocated.
40710187SKrishna.Elango@Sun.COM 	 */
40810187SKrishna.Elango@Sun.COM 	reg_spec[rnum].pci_phys_hi = (addr_spec[anum].pci_phys_hi &
40910187SKrishna.Elango@Sun.COM 	    ~PCI_REG_REL_M);
41010187SKrishna.Elango@Sun.COM 	reg_spec[rnum].pci_phys_mid = 0;
41110187SKrishna.Elango@Sun.COM 	reg_spec[rnum].pci_phys_low = 0;
41210187SKrishna.Elango@Sun.COM 	reg_spec[rnum].pci_size_hi = addr_spec[anum].pci_size_hi;
41310187SKrishna.Elango@Sun.COM 	reg_spec[rnum].pci_size_low = addr_spec[anum].pci_size_low;
41410187SKrishna.Elango@Sun.COM 
41510187SKrishna.Elango@Sun.COM 	/* Create the new reg_spec data and update the property */
41610187SKrishna.Elango@Sun.COM 	if (ddi_prop_update_int_array(DDI_DEV_T_NONE, dip, "reg",
41710187SKrishna.Elango@Sun.COM 	    (int *)reg_spec, (new_rsize / sizeof (int))) != DDI_SUCCESS)
41810187SKrishna.Elango@Sun.COM 		goto fail;
41910187SKrishna.Elango@Sun.COM 
42010187SKrishna.Elango@Sun.COM fix:
42110187SKrishna.Elango@Sun.COM 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
42210187SKrishna.Elango@Sun.COM 	attr.devacc_attr_endian_flags  = DDI_STRUCTURE_LE_ACC;
42310187SKrishna.Elango@Sun.COM 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
42410187SKrishna.Elango@Sun.COM 
42510187SKrishna.Elango@Sun.COM 	if (ddi_regs_map_setup(dip, rnum, &regsp, 0, 0, &attr,
42610187SKrishna.Elango@Sun.COM 	    &hdl) != DDI_SUCCESS)
42710187SKrishna.Elango@Sun.COM 		goto fail;
42810187SKrishna.Elango@Sun.COM 
42910187SKrishna.Elango@Sun.COM 	/* Grab register which shows which ports are enabled */
43010187SKrishna.Elango@Sun.COM 	offset = (char *)regsp + PLX_INGRESS_PORT_ENABLE;
43110187SKrishna.Elango@Sun.COM 	port_enable = ddi_get32(hdl, (uint32_t *)offset);
43210187SKrishna.Elango@Sun.COM 
43310187SKrishna.Elango@Sun.COM 	if ((port_enable == 0xFFFFFFFF) || (port_enable == 0))
43410187SKrishna.Elango@Sun.COM 		goto done;
43510187SKrishna.Elango@Sun.COM 
43610187SKrishna.Elango@Sun.COM 	offset = (char *)regsp + PLX_INGRESS_CONTROL_SHADOW;
43710187SKrishna.Elango@Sun.COM 
43810187SKrishna.Elango@Sun.COM 	/* Disable RO on Port 0 */
43910187SKrishna.Elango@Sun.COM 	port_offset = 0x0 + offset;
44010187SKrishna.Elango@Sun.COM 	val = ddi_get32(hdl, (uint32_t *)port_offset);
44110187SKrishna.Elango@Sun.COM 	if (val & PLX_RO_MODE_BIT)
44210187SKrishna.Elango@Sun.COM 		val ^= PLX_RO_MODE_BIT;
44310187SKrishna.Elango@Sun.COM 	ddi_put32(hdl, (uint32_t *)port_offset, val);
44410187SKrishna.Elango@Sun.COM 
44510187SKrishna.Elango@Sun.COM 	/* Disable RO on Port 8, but make sure its enabled */
44610187SKrishna.Elango@Sun.COM 	if (!(port_enable & (1 << 8)))
44710187SKrishna.Elango@Sun.COM 		goto port12;
44810187SKrishna.Elango@Sun.COM 
44910187SKrishna.Elango@Sun.COM 	port_offset = (8 * 0x1000) + offset;
45010187SKrishna.Elango@Sun.COM 	val = ddi_get32(hdl, (uint32_t *)port_offset);
45110187SKrishna.Elango@Sun.COM 	if (val & PLX_RO_MODE_BIT)
45210187SKrishna.Elango@Sun.COM 		val ^= PLX_RO_MODE_BIT;
45310187SKrishna.Elango@Sun.COM 	ddi_put32(hdl, (uint32_t *)port_offset, val);
45410187SKrishna.Elango@Sun.COM 
45510187SKrishna.Elango@Sun.COM port12:
45610187SKrishna.Elango@Sun.COM 	/* Disable RO on Port 12, but make sure it exists */
45710187SKrishna.Elango@Sun.COM 	if (!(port_enable & (1 << 12)))
45810187SKrishna.Elango@Sun.COM 		goto done;
45910187SKrishna.Elango@Sun.COM 
46010187SKrishna.Elango@Sun.COM 	port_offset = (12 * 0x1000) + offset;
46110187SKrishna.Elango@Sun.COM 	val = ddi_get32(hdl, (uint32_t *)port_offset);
46210187SKrishna.Elango@Sun.COM 	if (val & PLX_RO_MODE_BIT)
46310187SKrishna.Elango@Sun.COM 		val ^= PLX_RO_MODE_BIT;
46410187SKrishna.Elango@Sun.COM 	ddi_put32(hdl, (uint32_t *)port_offset, val);
46510187SKrishna.Elango@Sun.COM 
46610187SKrishna.Elango@Sun.COM 	goto done;
46710187SKrishna.Elango@Sun.COM 
46810187SKrishna.Elango@Sun.COM done:
46910187SKrishna.Elango@Sun.COM 	ddi_regs_map_free(&hdl);
47010187SKrishna.Elango@Sun.COM fail:
47110187SKrishna.Elango@Sun.COM 	kmem_free(reg_spec, new_rsize);
47210187SKrishna.Elango@Sun.COM }
47310187SKrishna.Elango@Sun.COM 
47410187SKrishna.Elango@Sun.COM #ifdef	PRINT_PLX_SEEPROM_CRC
47510187SKrishna.Elango@Sun.COM static void
pcieb_print_plx_seeprom_crc_data(pcieb_devstate_t * pcieb_p)47610187SKrishna.Elango@Sun.COM pcieb_print_plx_seeprom_crc_data(pcieb_devstate_t *pcieb_p)
47710187SKrishna.Elango@Sun.COM {
47810187SKrishna.Elango@Sun.COM 	ddi_acc_handle_t h;
47910187SKrishna.Elango@Sun.COM 	dev_info_t *dip = pcieb_p->pcieb_dip;
48010187SKrishna.Elango@Sun.COM 	uint16_t vendorid = (PCIE_DIP2BUS(dip)->bus_dev_ven_id) & 0xFFFF;
48110187SKrishna.Elango@Sun.COM 	int nregs;
48210187SKrishna.Elango@Sun.COM 	caddr_t mp;
48310187SKrishna.Elango@Sun.COM 	off_t bar_size;
48410187SKrishna.Elango@Sun.COM 	ddi_device_acc_attr_t mattr = {
48510187SKrishna.Elango@Sun.COM 		DDI_DEVICE_ATTR_V0,
48610187SKrishna.Elango@Sun.COM 		DDI_STRUCTURE_LE_ACC,
48710187SKrishna.Elango@Sun.COM 		DDI_STRICTORDER_ACC
48810187SKrishna.Elango@Sun.COM 	};
48910187SKrishna.Elango@Sun.COM 	uint32_t addr_reg_off = 0x260, data_reg_off = 0x264, data = 0x6BE4;
49010187SKrishna.Elango@Sun.COM 
49110187SKrishna.Elango@Sun.COM 	if (vendorid != PXB_VENDOR_PLX)
49210187SKrishna.Elango@Sun.COM 		return;
49310187SKrishna.Elango@Sun.COM 	if (ddi_dev_nregs(dip, &nregs) != DDI_SUCCESS)
49410187SKrishna.Elango@Sun.COM 		return;
49510187SKrishna.Elango@Sun.COM 	if (nregs < 2)	/* check for CONF entry only, no BARs */
49610187SKrishna.Elango@Sun.COM 		return;
49710187SKrishna.Elango@Sun.COM 	if (ddi_dev_regsize(dip, 1, &bar_size) != DDI_SUCCESS)
49810187SKrishna.Elango@Sun.COM 		return;
49910187SKrishna.Elango@Sun.COM 	if (ddi_regs_map_setup(dip, 1, (caddr_t *)&mp, 0, bar_size,
50010187SKrishna.Elango@Sun.COM 	    &mattr, &h) != DDI_SUCCESS)
50110187SKrishna.Elango@Sun.COM 		return;
50210187SKrishna.Elango@Sun.COM 	ddi_put32(h, (uint32_t *)((uchar_t *)mp + addr_reg_off), data);
50310187SKrishna.Elango@Sun.COM 	delay(drv_usectohz(1000000));
50410187SKrishna.Elango@Sun.COM 	printf("%s#%d: EEPROM StatusReg = %x, CRC = %x\n",
50510187SKrishna.Elango@Sun.COM 	    ddi_driver_name(dip), ddi_get_instance(dip),
50610187SKrishna.Elango@Sun.COM 	    ddi_get32(h, (uint32_t *)((uchar_t *)mp + addr_reg_off)),
50710187SKrishna.Elango@Sun.COM 	    ddi_get32(h, (uint32_t *)((uchar_t *)mp + data_reg_off)));
50810187SKrishna.Elango@Sun.COM #ifdef PLX_HOT_RESET_DISABLE
50910187SKrishna.Elango@Sun.COM 	/* prevent hot reset from propogating downstream. */
51010187SKrishna.Elango@Sun.COM 	data = ddi_get32(h, (uint32_t *)((uchar_t *)mp + 0x1DC));
51110187SKrishna.Elango@Sun.COM 	ddi_put32(h, (uint32_t *)((uchar_t *)mp + 0x1DC), data | 0x80000);
51210187SKrishna.Elango@Sun.COM 	delay(drv_usectohz(1000000));
51310187SKrishna.Elango@Sun.COM 	printf("%s#%d: EEPROM 0x1DC prewrite=%x postwrite=%x\n",
51410187SKrishna.Elango@Sun.COM 	    ddi_driver_name(dip), ddi_get_instance(dip), data,
51510187SKrishna.Elango@Sun.COM 	    ddi_get32(h, (uint32_t *)((uchar_t *)mp + 0x1DC)));
51610187SKrishna.Elango@Sun.COM #endif /* PLX_HOT_RESET_DISABLE */
51710187SKrishna.Elango@Sun.COM 	ddi_regs_map_free(&h);
51810187SKrishna.Elango@Sun.COM }
51910187SKrishna.Elango@Sun.COM #endif /* PRINT_PLX_SEEPROM_CRC */
52010187SKrishna.Elango@Sun.COM #endif /* PX_PLX */
521