sunpci.c revision 9164eb65b5c2638abc35517e4302cf4c142c3855
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/ddifm_impl.h>
#include <sys/pci_impl.h>
int
{
/* Check for fault management capabilities */
}
void
{
}
/*
* pci_ereport_setup, pci_ereport_teardown, pci_ereport_post:
* Interfaces to be used by ereport capable PCI device drivers to setup,
* teardown, and post generic PCI error reports. This is to guarantee a
* consistant error report model for all PCI devices. Please see
* PSARC/2004/391.
*/
typedef struct pci_erpt {
} pci_erpt_t;
pci_fm_err_t pci_err_tbl[] = {
};
pci_fm_err_t pci_bdg_err_tbl[] = {
};
void
{
uint16_t pci_devstat = 0;
int have_pciex;
return;
}
return;
/*
* Setup config space and store config address
* in pci_erpt struct.
*/
} else {
return;
}
/*
* Determine if this device supports a capabilities list. We
* do so by looking at a bit in the status register. If we are
* unable to retrieve the status register, something is horribly
* wrong and we should just bail.
*/
return;
if ((pci_devstat & PCI_STAT_CAP) == 0)
return;
/*
* Determine if we are on a machine with pci express. We do so
* by looping through the capabilities of the device and looking
* to see if one of those capabilities is support of PCI
* express.
*/
have_pciex = 0;
0xff) {
0xff) {
if (cap_id == PCI_CAP_ID_PCI_E) {
have_pciex = 1;
break;
}
break;
}
}
/*
* If not pci express, we're done
*/
if (have_pciex == 0)
return;
/*
* Save and export the pci express capabilities reg.
*/
(void) ndi_prop_update_int(DDI_DEV_T_NONE,
/*
* Find and export any slot capabilities register
*/
if (pcie_cap & PCIE_PCIECAP_SLOT_IMPL) {
(uint32_t *)
(void) ndi_prop_update_int(DDI_DEV_T_NONE,
}
}
void
{
}
return;
}
void
{
struct i_ddi_fmhdl *fmhdl;
char buf[FM_MAX_CLASS];
int i;
return;
}
return;
}
goto done;
}
goto done;
}
/*
* Generate an ereport for this error bit.
*/
pci_err_tbl[i].err_class);
/*
* Generate a corresponding ereport on behalf
* of the target (the parent dip) of the
* transaction.
*/
"%s.%s", PCI_ERROR_SUBCLASS,
pci_err_tbl[i].terr_class);
NULL);
}
}
}
}
/*
* Clear error bits
*/
done:
}
/*
* Generic pci-pci bridge error report function
*/
void
{
struct i_ddi_fmhdl *fmhdl;
char buf[FM_MAX_CLASS];
int i;
return;
}
return;
}
== 0xffff) {
PCI_NR);
goto done;
}
PCI_NR);
goto done;
}
if (bdg_ctrl & PCI_BCNF_BCNTRL_DTO_STAT) {
}
pci_bdg_err_tbl[i].err_class);
}
}
}
/*
* Clear error bits
*/
done:
*status = cfg_sec_stat;
}
/*
* Generic pci-pci bridge error analysis function
*/
int
{
int ret;
int fatal = 0;
int nonfatal = 0;
int unknown = 0;
/*
* special case for pokes - we only consider master abort
* and target abort as nonfatal. Sserr with no master abort is
* instance, so return unknown and parent will determine if
* nonfatal (if another child returned nonfatal - ie master
* or target abort) or fatal otherwise
*/
if (pci_cfg_sec_stat & (PCI_STAT_R_TARG_AB |
nonfatal++;
if (pci_cfg_stat & PCI_STAT_S_SYSERR)
unknown++;
/*
* Only sserr on primary bus is considered fatal.
* In all other conditions, the bridge has been able to notify
* the initiator of the error condition, so let the initiator
* (be it the host for PIO or the leaf device for DMA) handle it
*/
if (pci_cfg_stat & PCI_STAT_S_SYSERR)
fatal++;
if (pci_cfg_stat & (PCI_STAT_PERROR |
nonfatal++;
if (pci_cfg_sec_stat & (PCI_STAT_R_TARG_AB |
nonfatal++;
}
/*
* now check children below the bridge
*/
if (ret == DDI_FM_FATAL)
fatal++;
else if (ret == DDI_FM_NONFATAL)
nonfatal++;
else if (ret == DDI_FM_UNKNOWN)
unknown++;
}
#ifdef _LP64
#else /* _ILP32 */
#endif
{
}
#ifdef _LP64
#else /* _ILP32 */
#endif
{
}
#ifdef _LP64
#else /* _ILP32 */
#endif
{
}
#ifdef _LP64
#else /* _ILP32 */
#endif
{
}
#ifdef _LP64
void
#else /* _ILP32 */
void
#endif
{
}
#ifdef _LP64
void
#else /* _ILP32 */
void
#endif
{
}
#ifdef _LP64
void
#else /* _ILP32 */
void
#endif
{
}
#ifdef _LP64
void
#else /* _ILP32 */
void
#endif
{
}
/*ARGSUSED*/
int
{
return (DDI_SUCCESS);
}
/*
* Note about saving and restoring config space.
* PCI devices have only upto 256 bytes of config space while PCI Express
* devices can have upto 4k config space. In case of PCI Express device,
* we save all 4k config space and restore it even if it doesn't make use
* of all 4k. But some devices don't respond to reads to non-existent
* registers within the config space. To avoid any panics, we use ddi_peek
* to do the reads. A bit mask is used to indicate which words of the
* config space are accessible. While restoring the config space, only those
* readable words are restored. We do all this in 32 bit size words.
*/
#define INDEX_SHIFT 3
#define BITMASK 0x7
/*
* Table below specifies the number of registers to be saved for each PCI
* capability. pci_generic_save saves the number of words specified in the
* table. Any special considerations will be taken care by the capability
* specific save function e.g. use pci_msi_save to save registers associated
* with MSI capability. PCI_UNKNOWN_SIZE indicates that number of registers
* to be saved is variable and will be determined by the specific save function.
* including read only registers. Regsiters are saved and restored in 32 bit
* size words.
*/
static pci_cap_entry_t pci_cap_table[] = {
/*
* {PCI_CAP_ID_cPCI_CRC, 0, NULL},
* {PCI_CAP_ID_VPD, 0, NULL},
* {PCI_CAP_ID_cPCI_HS, 0, NULL},
* {PCI_CAP_ID_PCI_HOTPLUG, 0, NULL},
* {PCI_CAP_ID_AGP_8X, 0, NULL},
* {PCI_CAP_ID_SECURE_DEV, 0, NULL},
*/
{PCI_CAP_NEXT_PTR_NULL, 0, NULL}
};
/*
* Save the configuration registers for cdip as a property
* so that it persists after detach/uninitchild.
*/
int
{
int ret;
int pcie = 0;
return (DDI_FAILURE);
}
/*
* Determine if it is a pci express device. If it is, save entire
* 4k config space treating it as a array of 32 bit integers.
* If it is not, do it in a usual PCI way.
*/
/*
* Walk the capabilities searching for pci express capability
*/
while (cap_ptr != PCI_CAP_NEXT_PTR_NULL) {
cap_ptr + PCI_CAP_ID);
if (cap_id == PCI_CAP_ID_PCI_E) {
pcie = 1;
break;
}
}
if (pcie) {
/* PCI express device. Can have data in all 4k space */
KM_SLEEP);
p = regbuf;
/*
* Allocate space for mask.
* mask size is 128 bytes (4096 / 4 / 8 )
*/
for (i = 0; i < (PCIE_CONF_HDR_SIZE / sizeof (uint32_t)); i++) {
(int32_t *)p) == DDI_SUCCESS) {
/* it is readable register. set the bit */
maskbuf[i >> INDEX_SHIFT] |=
}
p++;
}
maskbufsz)) != DDI_PROP_SUCCESS) {
"saving config space for %s@%d\n",
}
} else {
KM_SLEEP);
}
/*
* Allocate maximum space required for capability descriptions.
* The maximum number of capabilties saved is the number of
* capabilities listed in the pci_cap_table.
*/
sizeof (pci_config_header_state_t));
regbufsz = sizeof (pci_config_header_state_t) +
} else if (ncaps) {
ncaps * sizeof (pci_cap_save_desc_t));
if (ret != DDI_PROP_SUCCESS)
}
}
if (ret != DDI_PROP_SUCCESS)
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/*
* Saves registers associated with PCI capabilities.
* Returns number of 32 bit words saved.
* Number of capabilities saved is returned in ncapsp.
*/
static uint32_t
{
}
static uint32_t
{
*ncapsp = 0;
if (!xspace)
/*
* Walk the capabilities
*/
while (cap_ptr != PCI_CAP_NEXT_PTR_NULL) {
/* Search for this cap id in our table */
if (!xspace)
pci_cap_entp++;
/*
* If this cap id is not found in the table, there is nothing
* to save.
*/
continue;
if (pci_cap_entp->cap_save_func) {
cap_descp++;
words_saved += nwords;
(*ncapsp)++;
}
}
}
return (words_saved);
}
static void
{
int i;
for (i = 0; i < nwords; i++) {
regbuf++;
cap_ptr += 4;
}
}
static uint32_t
{
return (nwords);
}
/*ARGSUSED*/
static uint32_t
{
/* Figure out how many registers to be saved */
/* If 64 bit address capable add one word */
if (msi_ctrl & PCI_MSI_64BIT_MASK)
nwords++;
/* If per vector masking capable, add two more words */
if (msi_ctrl & PCI_MSI_PVM_MASK)
nwords += 2;
return (nwords);
}
/*ARGSUSED*/
static uint32_t
{
/* Figure out how many registers to be saved */
/* If it is version 1 or version 2, add 4 words */
nwords += 4;
return (nwords);
}
/*ARGSUSED*/
static uint32_t
{
return (0);
}
static void
{
/*
* Copy the power state bits from the PMCSR to our saved copy.
* This is to make sure that we don't change the D state when
* we restore config space of the device.
*/
(*saved_pmcsrp) &= ~PCI_PMCSR_STATE_MASK;
}
static void
{
int i, j;
for (i = 0; i < (elements / sizeof (pci_cap_save_desc_t)); i++) {
regbuf++;
offset += 4;
}
cap_descp++;
}
}
/*
* Restore config_regs from a single devinfo node.
*/
int
{
return (DDI_FAILURE);
}
goto restoreconfig_err;
}
/* pcie device and has 4k config space saved */
p = regbuf;
for (i = 0; i < PCIE_CONF_HDR_SIZE / sizeof (uint32_t); i++) {
/* If the word is readable then restore it */
if (maskbuf[i >> INDEX_SHIFT] &
p++;
}
}
} else {
return (DDI_FAILURE);
}
chs_p->chs_command);
}
/*
* PCI capability related regsiters are saved.
* Restore them based on the description.
*/
sizeof (pci_config_header_state_t));
}
}
/*
* Make sure registers are flushed
*/
}
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}