pcie.c revision eae2e508a8e70b1ec407b10bd068c080651bbe5c
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/sysmacros.h>
#include <sys/pcie_impl.h>
static void pcie_init_pfd(dev_info_t *);
static void pcie_fini_pfd(dev_info_t *);
#ifdef DEBUG
uint_t pcie_debug_flags = 0;
/* Common Debugging shortcuts */
#else /* DEBUG */
#define PCIE_DBG_CFG 0 &&
#define PCIE_DBG 0 &&
#define PCIE_DBG_CAP 0 &&
#define PCIE_DBG_AER 0 &&
#endif /* DEBUG */
int pcie_intel_error_disable = 1;
/* Variable to control default PCI-Express config settings */
/* xxx_fw are bits that are controlled by FW and should not be modified */
0xF800; /* Reserved Bits */
0xF000; /* Reserved Bits */
/* PCI-Express Base error defaults */
/* PCI-Express Device Control Register */
/* PCI-Express AER Root Control Register */
#define PCIE_ROOT_SYS_ERR (PCIE_ROOTCTL_SYS_ERR_ON_CE_EN | \
#if defined(__xpv)
#else
#endif /* __xpv */
/* PCI-Express Root Error Command Register */
/* ECRC settings in the PCIe AER Control Register */
/*
* instead of using #defines have the platform's PCIe Root Complex driver set
* these masks using the pcie_get_XXX_mask and pcie_set_XXX_mask functions. For
* x86 the closest thing to a PCIe root complex driver is NPE. For SPARC the
* closest PCIe root complex driver is PX.
*
* pcie_serr_disable_flag : disable SERR only (in RCR and command reg) x86
* systems may want to disable SERR in general. For root ports, enabling SERR
* causes NMIs which are not handled and results in a watchdog timeout error.
*/
/* Default severities needed for eversholt. Error handling doesn't care */
/*
* modload support
*/
extern struct mod_ops mod_miscops;
&mod_miscops, /* Type of module */
"PCIE: PCI Express Architecture %I%"
};
struct modlinkage modlinkage = {
(void *)&modlmisc,
};
/*
* Global Variables needed for a non-atomic version of ddi_fm_ereport_post.
* Currently used to send the pci.fabric ereports whose payload depends on the
* type of PCI device it is being sent for.
*/
char *pcie_nv_buf;
int
_init(void)
{
int rval;
return (rval);
}
int
_fini()
{
int rval;
return (rval);
}
int
{
}
/*
* PCI-Express child device initialization.
* This function enables generic pci-express interrupts and error
* handling.
*
* @param pdip root dip (root nexus's dip)
* @param cdip child's dip (device's dip)
* @return DDI_SUCCESS or DDI_FAILURE
*/
/* ARGSUSED */
int
{
PCIE_DBG("%s: BUS not found.\n",
return (DDI_FAILURE);
}
/* Clear the device's status register */
/* Setup the device's command register */
/*
* If the device has a bus control register then program it
* based on the settings in the command register.
*/
if (PCIE_IS_BDG(bus_p)) {
/* Clear the device's secondary status register */
/* Setup the device's secondary command register */
if (pcie_command_default & PCI_COMM_SERR_ENABLE) {
else
}
/*
* Enable Master Abort Mode only if URs have not been masked.
* For PCI and PCIe-PCI bridges, enabling this bit causes a
* bit is masked, posted requests are dropped and non-posted
* requests are returned with -1.
*/
if (pcie_aer_uce_mask & PCIE_AER_UCE_UR)
else
reg16);
}
if (PCIE_IS_PCIE(bus_p)) {
/* Setup PCIe device control register */
/* Enable PCIe errors */
}
return (DDI_SUCCESS);
}
static void
{
pfd_p->pe_severity_flags = 0;
/* Allocate the root fault struct for both RC and RP */
if (PCIE_IS_ROOT(bus_p))
if (PCIE_IS_BDG(bus_p))
if (PCIE_IS_PCIE(bus_p)) {
if (PCIE_IS_RP(bus_p))
PCIE_RP_REG(pfd_p) =
if (PCIE_IS_RP(bus_p))
else if (PCIE_IS_PCIE_BDG(bus_p))
if (PCIX_ECC_VERSION_CHECK(bus_p)) {
PCIX_BDG_ECC_REG(pfd_p, 0) =
}
}
} else if (PCIE_IS_PCIX(bus_p)) {
if (PCIE_IS_BDG(bus_p)) {
if (PCIX_ECC_VERSION_CHECK(bus_p)) {
PCIX_BDG_ECC_REG(pfd_p, 0) =
}
} else {
if (PCIX_ECC_VERSION_CHECK(bus_p))
}
}
}
static void
{
if (PCIE_IS_PCIE(bus_p)) {
if (PCIX_ECC_VERSION_CHECK(bus_p)) {
sizeof (pf_pcix_ecc_regs_t));
sizeof (pf_pcix_ecc_regs_t));
}
sizeof (pf_pcix_bdg_err_regs_t));
}
if (PCIE_IS_RP(bus_p))
sizeof (pf_pcie_adv_rp_err_regs_t));
else if (PCIE_IS_PCIE_BDG(bus_p))
sizeof (pf_pcie_adv_bdg_err_regs_t));
sizeof (pf_pcie_adv_err_regs_t));
if (PCIE_IS_RP(bus_p))
sizeof (pf_pcie_rp_err_regs_t));
} else if (PCIE_IS_PCIX(bus_p)) {
if (PCIE_IS_BDG(bus_p)) {
if (PCIX_ECC_VERSION_CHECK(bus_p)) {
sizeof (pf_pcix_ecc_regs_t));
sizeof (pf_pcix_ecc_regs_t));
}
sizeof (pf_pcix_bdg_err_regs_t));
} else {
if (PCIX_ECC_VERSION_CHECK(bus_p))
sizeof (pf_pcix_ecc_regs_t));
sizeof (pf_pcix_err_regs_t));
}
}
if (PCIE_IS_BDG(bus_p))
sizeof (pf_pci_bdg_err_regs_t));
if (PCIE_IS_ROOT(bus_p))
}
/*
* Special functions to allocate pf_data_t's for PCIe root complexes.
* Note: Root Complex not Root Port
*/
void
{
pfd_p->pe_severity_flags = 0;
}
void
{
}
void
{
/* Fake that there are AER logs */
/* Needed only for handle lookup */
}
void
{
}
/*
* Initialize PCIe Bus Private Data
*
* to key registers.
*/
{
pcie_bus_t *bus_p = 0;
int range_size;
/* allocate memory for pcie bus data */
/* Set back pointer to dip */
/* Create an config access special to error handling */
goto fail;
}
bus_p->bus_fm_flags = 0;
goto fail;
/* Save the Vendor Id Device Id */
/* Save the Header Type */
"pcie2pci-sec-bus", 0);
/* Figure out the device type and all the relavant capability offsets */
!= DDI_FAILURE) {
} else {
}
!= DDI_FAILURE) {
if (PCIE_IS_BDG(bus_p))
else
} else {
}
if (PCIE_IS_BDG(bus_p)) {
/* get "bus_range" property */
range_size = sizeof (pci_bus_range_t);
!= DDI_PROP_SUCCESS)
goto fail;
/* get secondary bus number */
/* Get "ranges" property */
bus_p->bus_addr_entries = 0;
}
/* save "assigned-addresses" property array, ignore failues */
else
bus_p->bus_assigned_entries = 0;
/* save RP dip and RP bdf */
if (PCIE_IS_RP(bus_p)) {
} else {
/*
* When debugging be aware that some NVIDIA x86
* architectures have 2 nodes for each RP, One at Bus
* 0x0 and one at Bus 0x80. The requester is from Bus
* 0x80
*/
if (PCIE_IS_ROOT(parent_bus_p)) {
break;
}
}
}
PCIE_DBG("Add %s(dip 0x%p, bdf 0x%x, secbus 0x%x)\n",
#ifdef DEBUG
#endif
return (bus_p);
fail:
if (eh)
return (NULL);
}
int
{
if (!bus_p)
return (DDI_FAILURE);
return (pcie_enable_ce(cdip));
}
/*
* PCI-Express child device de-initialization.
* This function disables generic pci-express interrupts and error
* handling.
*/
void
{
}
void
{
}
void
{
/*
* Clear any pending errors
*/
if (!PCIE_IS_PCIE(bus_p))
return;
/*
* Enable Baseline Error Handling but leave CE reporting off (poweron
* default).
*/
}
/* Enable Root Port Baseline Error Receiving */
if (PCIE_IS_ROOT(bus_p) &&
#if defined(__xpv)
/*
* When we're booted under the hypervisor we won't receive
* MSI's, so to ensure that uncorrectable errors aren't ignored
* we set the SERR_FAT and SERR_NONFAT bits in the Root Control
* Register.
*/
#else
#endif /* __xpv */
reg16);
}
/*
* Enable PCI-Express Advanced Error Handling if Exists
*/
if (!PCIE_HAS_AER(bus_p))
return;
/* Set Uncorrectable Severity */
reg32);
}
/* Enable Uncorrectable errors */
reg32);
}
/* x86 doesn't do this except for RC */
/* Enable ECRC generation and checking */
}
/* Enable Secondary Uncorrectable errors if this is a bridge */
if (!PCIE_IS_PCIE_BDG(bus_p))
goto root;
/* Set Uncorrectable Severity */
reg32);
}
}
root:
/*
* Enable Root Control this is a Root device
*/
if (!PCIE_IS_ROOT(bus_p))
return;
#if !defined(__xpv)
}
#endif /* __xpv */
}
/*
* This function is used for enabling CE reporting and setting the AER CE mask.
* When called from outside the pcie module it should always be preceded by
* a call to pcie_enable_errors.
*/
int
{
if (!PCIE_IS_PCIE(bus_p))
return (DDI_SUCCESS);
/*
* The "pcie_ce_mask" property is used to control both the CE reporting
* enable field in the device control register and the AER CE mask. We
* leave CE reporting disabled if pcie_ce_mask is set to -1.
*/
/*
* Nothing to do since CE reporting has already been disabled.
*/
return (DDI_SUCCESS);
}
if (PCIE_HAS_AER(bus_p)) {
/* Enable AER CE */
0);
/* Clear any pending AER CE errors */
}
/* clear any pending CE errors */
/* Enable CE reporting */
return (DDI_SUCCESS);
}
/* ARGSUSED */
void
{
if (!PCIE_IS_PCIE(bus_p))
return;
/*
* Disable PCI-Express Baseline Error Handling
*/
/*
* Disable PCI-Express Advanced Error Handling if Exists
*/
if (!PCIE_HAS_AER(bus_p))
goto root;
/* Disable Uncorrectable errors */
/* Disable Correctable errors */
/* Disable ECRC generation and checking */
aer_reg &= ~(PCIE_AER_CTL_ECRC_GEN_ENA |
}
/*
* Disable Secondary Uncorrectable errors if this is a bridge
*/
if (!PCIE_IS_PCIE_BDG(bus_p))
goto root;
root:
/*
* disable Root Control this is a Root device
*/
if (!PCIE_IS_ROOT(bus_p))
return;
if (!pcie_serr_disable_flag) {
}
if (!PCIE_HAS_AER(bus_p))
return;
}
}
/*
* Extract bdf from "reg" property.
*/
int
{
int reglen;
return (DDI_FAILURE);
if (reglen < (sizeof (pci_regspec_t) / sizeof (int))) {
return (DDI_FAILURE);
}
/* Get phys_hi from first element. All have same bdf. */
return (DDI_SUCCESS);
}
{
;
return (cdip);
}
{
/*
* As part of the probing, the PCI fcode interpreter may setup a DMA
* request if a given card has a fcode on it using dip and rdip of the
* case, return zero for the bdf since we cannot get to the bdf value
* of the actual device which will be initiating this DMA.
*/
return (0);
/*
* For a given rdip, return the bdf value of dip's (px or px_pci)
* immediate child or secondary bus-id if dip is a PCIe2PCI bridge.
*
* XXX - For now, return bdf value of zero for all PCI and PCI-X devices
* since this needs more work.
*/
}
return (pcie_aer_uce_mask);
}
return (pcie_aer_ce_mask);
}
return (pcie_aer_suce_mask);
}
return (pcie_serr_disable_flag);
}
void
if (mask & PCIE_AER_UCE_UR)
else
if (mask & PCIE_AER_UCE_ECRC)
pcie_ecrc_value = 0;
}
void
}
void
}
void
}
/*
* Is the rdip a child of dip. Used for checking certain CTLOPS from bubbling
* up erronously. Ex. ISA ctlops to a PCI-PCI Bridge.
*/
{
break;
}
{
if (PCIE_IS_PCIE(bus_p)) {
return (B_TRUE);
}
return (B_FALSE);
}
#ifdef DEBUG
static void
{
}
/*
* For debugging purposes set pcie_dbg_print != 0 to see printf messages
* during interrupt.
*
* When a proper solution is in place this code will disappear.
* Potential solutions are:
* o circular buffers
* o taskq to print at lower pil
*/
int pcie_dbg_print = 0;
static void
{
if (!pcie_debug_flags) {
return;
}
if (servicing_interrupt()) {
if (pcie_dbg_print) {
}
} else {
}
}
#endif /* DEBUG */