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