sunpci.c revision 6f6c7d2b51705d612c5f11ed385afd87c89c1a12
/*
* 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
*/
/*
*/
#include <sys/sysmacros.h>
#include <sys/pci_impl.h>
int pci_enable_wakeup = 1;
int
{
/* Check for fault management capabilities */
}
}
void
{
}
{
}
{
}
{
}
{
}
void
{
}
void
{
}
void
{
}
void
{
}
/*
* We need to separate the old interfaces from the new ones and leave them
* in here for a while. Previous versions of the OS defined the new interfaces
* to the old interfaces. This way we can fix things up so that we can
* eventually remove these interfaces.
* or earlier will actually have a reference to pci_config_getb in the binary.
*/
#ifdef _ILP32
{
}
{
}
{
}
{
}
void
{
}
void
{
}
void
{
}
void
{
}
#endif /* _ILP32 */
/*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;
#ifdef __sparc
#else
#endif
int pcie = 0;
#ifdef __sparc
return (DDI_FAILURE);
}
#else
/* Set up cautious config access handle */
!= DDI_SUCCESS) {
return (DDI_FAILURE);
}
#endif
/*
* Determine if it implements capabilities
*/
if (!(status & 0x10)) {
goto no_cap;
}
/*
* 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 )
*/
#ifdef __sparc
#endif
for (i = 0; i < (PCIE_CONF_HDR_SIZE / sizeof (uint32_t)); i++) {
#ifdef __sparc
(int32_t *)p);
if (ret == DDI_SUCCESS) {
#else
/*
* ddi_peek doesn't work on x86, so we use cautious pci
* config access instead.
*/
if (*p != -1) {
#endif
/* 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;
/*
* Determine if it implements capabilities
*/
if (!(status & 0x10)) {
return (words_saved);
}
if (!xspace)
/*
* Walk the capabilities
*/
while (cap_ptr != PCI_CAP_NEXT_PTR_NULL) {
/* Search for this cap id in our table */
if (!xspace) {
}
break;
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);
}
/*ARGSUSED*/
static uint32_t
{
switch ((reg & PCI_HTCAP_ADDRMAP_MAPTYPE_MASK) >>
/* HT3.1 spec, ch 7.7, 40-bit dma */
break;
/* HT3.1 spec, ch 7.8, 64-bit dma */
nwords = 4;
break;
default:
nwords = 0;
}
return (nwords);
}
/*ARGSUSED*/
static uint32_t
{
/* HT3.1 spec, ch 7.17 */
return (nwords);
}
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_SUCCESS);
}
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);
}
/*ARGSUSED*/
static int
{
/* we don't deal with bridges, etc here */
if (header_type != PCI_HEADER_ZERO) {
return (DDI_FAILURE);
}
if ((status & PCI_STAT_CAP) == 0) {
return (DDI_FAILURE);
}
/*
* Walk the capabilities searching for a PM entry.
*/
while (cap_ptr != PCI_CAP_NEXT_PTR_NULL) {
if (cap_id == PCI_CAP_ID_PM) {
break;
}
}
if (cap_ptr == PCI_CAP_NEXT_PTR_NULL) {
return (DDI_FAILURE);
}
*pmcap_offsetp = cap_ptr;
return (DDI_SUCCESS);
}
/*
* Do common pci-specific suspend actions:
* - enable wakeup if appropriate for the device
* - put device in lowest D-state that supports wakeup, or D3 if none
* - turn off bus mastering in control register
* For lack of per-dip storage (parent private date is pretty busy)
* we use properties to store the necessary context
* To avoid grotting through pci config space on every suspend,
* we leave the prop in existence after resume, cause we know that
* the detach framework code will dispose of it for us.
*/
typedef struct pci_pm_context {
int ppc_flags;
#define SAVED_PM_CONTEXT "pci-pm-context"
/* values for ppc_flags */
#define PPCF_NOPMCAP 1
/*
* Handle pci-specific suspend processing
* PM CSR and PCI CMD are saved by pci_save_config_regs().
* If device can wake up system via PME, enable it to do so
* Set device power level to lowest that can generate PME, or D3 if none can
* Turn off bus master enable in pci command register
*/
#if defined(__x86)
#endif
int
{
pci_pm_context_t *p;
int ret;
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
fromprop = 0;
&p->ppc_cap_offset) != DDI_SUCCESS) {
p->ppc_flags |= PPCF_NOPMCAP;
SAVED_PM_CONTEXT, (uchar_t *)p,
sizeof (pci_pm_context_t));
if (ret != DDI_PROP_SUCCESS) {
ret = DDI_FAILURE;
} else {
ret = DDI_SUCCESS;
}
goto done;
}
/*
* Upon suspend, set the power level to the lowest that can
* wake the system. If none can, then set to lowest.
* XXX later we will need to check policy to see if this
* XXX device has had wakeup disabled
*/
p->ppc_suspend_level =
else if ((pmcap & PCI_PMCAP_D2_PME) !=
0)
else if ((pmcap & PCI_PMCAP_D1_PME) != 0)
else if ((pmcap & PCI_PMCAP_D0_PME) != 0)
else
/*
* we defer updating the property to catch the saved
* register values as well
*/
}
/* If we set this in kmem_zalloc'd memory, we already returned above */
if ((p->ppc_flags & PPCF_NOPMCAP) != 0) {
goto done;
}
pmcsr &= (PCI_PMCSR_STATE_MASK);
/*
* Push out saved register values
*/
(uchar_t *)p, sizeof (pci_pm_context_t));
if (ret == DDI_PROP_SUCCESS) {
goto done;
}
/* Failed; put things back the way we found them */
(void) pci_restore_config_regs(dip);
if (fromprop)
ddi_prop_free(p);
else
kmem_free(p, sizeof (*p));
return (DDI_FAILURE);
done:
/*
* According to 8.2.2 of "PCI Bus Power Management Interface
* Specification Revision 1.2":
* "When placing a function into D3, the operating system software is
* required to disable I/O and memory space as well as bus mastering via
* the PCI Command register."
*/
#if defined(__x86)
if (pci_enable_wakeup) {
if (ret) {
}
}
#endif
if (p) {
/*
* Some BIOS (e.g. Toshiba M10) expects pci-ide to be in D0
* state when we set SLP_EN, otherwise it takes 5 minutes for
* the BIOS to put the system into S3.
*/
pmcsr = 0;
}
/*
* pmcsr is the last write-operation to the device's PCI
* config space, because we found that there are
* some faulty devices whose PCI config space may not
* respond correctly once in D3 state.
*/
pmcsr);
}
if (fromprop)
ddi_prop_free(p);
else
kmem_free(p, sizeof (*p));
}
return (DDI_SUCCESS);
}
/*
* The inverse of pci_post_suspend; handle pci-specific resume processing
* First, turn device back on, then restore config space.
*/
int
{
pci_pm_context_t *p;
/* E_FUNC_SET_NOT_USED */
int flags;
#if defined(__x86)
int retval;
#endif
return (DDI_FAILURE);
}
pmcap = p->ppc_cap_offset;
ddi_prop_free(p);
#if defined(__x86)
/*
* Turn platform wake enable back off
*/
if (pci_enable_wakeup) {
if (retval) {
}
}
#endif
if ((flags & PPCF_NOPMCAP) != 0)
goto done;
return (DDI_FAILURE);
}
done:
return (DDI_SUCCESS);
}