566 lines
17 KiB
C
566 lines
17 KiB
C
// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
|
|
/*
|
|
* PCIe Slots
|
|
*
|
|
* Copyright 2013-2019 IBM Corp.
|
|
*/
|
|
|
|
#include <skiboot.h>
|
|
#include <opal-msg.h>
|
|
#include <pci-cfg.h>
|
|
#include <pci.h>
|
|
#include <pci-slot.h>
|
|
|
|
/* Debugging options */
|
|
#define PCIE_SLOT_PREFIX "PCIE-SLOT-%016llx "
|
|
#define PCIE_SLOT_DBG(s, fmt, a...) \
|
|
prlog(PR_DEBUG, PCIE_SLOT_PREFIX fmt, (s)->id, ##a)
|
|
|
|
static int64_t pcie_slot_get_presence_state(struct pci_slot *slot, uint8_t *val)
|
|
{
|
|
struct phb *phb = slot->phb;
|
|
struct pci_device *pd = slot->pd;
|
|
uint32_t ecap;
|
|
uint16_t state;
|
|
|
|
/* The presence is always on if it's a switch upstream port */
|
|
if (pd->dev_type == PCIE_TYPE_SWITCH_UPPORT) {
|
|
*val = OPAL_PCI_SLOT_PRESENT;
|
|
return OPAL_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* The presence is always on if a switch downstream port
|
|
* doesn't support slot capability according to PCIE spec.
|
|
*/
|
|
if (pd->dev_type == PCIE_TYPE_SWITCH_DNPORT &&
|
|
!(slot->pcie_cap & PCICAP_EXP_CAP_SLOT)) {
|
|
*val = OPAL_PCI_SLOT_PRESENT;
|
|
return OPAL_SUCCESS;
|
|
}
|
|
|
|
/* Retrieve presence status */
|
|
ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false);
|
|
pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTSTAT, &state);
|
|
if (state & PCICAP_EXP_SLOTSTAT_PDETECTST)
|
|
*val = OPAL_PCI_SLOT_PRESENT;
|
|
else
|
|
*val = OPAL_PCI_SLOT_EMPTY;
|
|
|
|
return OPAL_SUCCESS;
|
|
}
|
|
|
|
static int64_t pcie_slot_get_link_state(struct pci_slot *slot,
|
|
uint8_t *val)
|
|
{
|
|
struct phb *phb = slot->phb;
|
|
struct pci_device *pd = slot->pd;
|
|
uint32_t ecap;
|
|
int16_t state;
|
|
|
|
/*
|
|
* The link behind switch upstream port is always on
|
|
* since it doesn't have a valid link indicator.
|
|
*/
|
|
if (pd->dev_type == PCIE_TYPE_SWITCH_UPPORT) {
|
|
*val = 1;
|
|
return OPAL_SUCCESS;
|
|
}
|
|
|
|
/* Retrieve link width */
|
|
ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false);
|
|
pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_LSTAT, &state);
|
|
if (state & PCICAP_EXP_LSTAT_DLLL_ACT)
|
|
*val = ((state & PCICAP_EXP_LSTAT_WIDTH) >> 4);
|
|
else
|
|
*val = 0;
|
|
|
|
return OPAL_SUCCESS;
|
|
}
|
|
|
|
static int64_t pcie_slot_get_power_state(struct pci_slot *slot __unused,
|
|
uint8_t *val)
|
|
{
|
|
/* We should return the cached power state that is same to
|
|
* the PCI slot hotplug state (added/removed). Otherwise,
|
|
* the OS will see mismatched states, causing the adapter
|
|
* behind the slot can't be probed successfully on request
|
|
* of hot add. So we could run into the situation where the
|
|
* OS sees power-off but it's on in hardware.
|
|
*/
|
|
*val = slot->power_state;
|
|
|
|
return OPAL_SUCCESS;
|
|
}
|
|
|
|
static int64_t pcie_slot_get_attention_state(struct pci_slot *slot,
|
|
uint8_t *val)
|
|
{
|
|
struct phb *phb = slot->phb;
|
|
struct pci_device *pd = slot->pd;
|
|
uint32_t ecap;
|
|
uint16_t state;
|
|
|
|
/* Attention is off if the capability is missing */
|
|
if (!(slot->slot_cap & PCICAP_EXP_SLOTCAP_ATTNI)) {
|
|
*val = 0;
|
|
return OPAL_SUCCESS;
|
|
}
|
|
|
|
/* Retrieve attention state */
|
|
ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false);
|
|
pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCTL, &state);
|
|
state = (state & PCICAP_EXP_SLOTCTL_ATTNI) >> 6;
|
|
switch (state) {
|
|
case PCIE_INDIC_ON:
|
|
*val = PCI_SLOT_ATTN_LED_ON;
|
|
break;
|
|
case PCIE_INDIC_BLINK:
|
|
*val = PCI_SLOT_ATTN_LED_BLINK;
|
|
break;
|
|
case PCIE_INDIC_OFF:
|
|
default:
|
|
*val = PCI_SLOT_ATTN_LED_OFF;
|
|
}
|
|
|
|
return OPAL_SUCCESS;
|
|
}
|
|
|
|
static int64_t pcie_slot_get_latch_state(struct pci_slot *slot,
|
|
uint8_t *val)
|
|
{
|
|
struct phb *phb = slot->phb;
|
|
struct pci_device *pd = slot->pd;
|
|
uint32_t ecap;
|
|
uint16_t state;
|
|
|
|
/* Latch is off if MRL sensor doesn't exist */
|
|
if (!(slot->slot_cap & PCICAP_EXP_SLOTCAP_MRLSENS)) {
|
|
*val = 0;
|
|
return OPAL_SUCCESS;
|
|
}
|
|
|
|
/* Retrieve MRL sensor state */
|
|
ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false);
|
|
pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTSTAT, &state);
|
|
if (state & PCICAP_EXP_SLOTSTAT_MRLSENSST)
|
|
*val = 1;
|
|
else
|
|
*val = 0;
|
|
|
|
return OPAL_SUCCESS;
|
|
}
|
|
|
|
static int64_t pcie_slot_set_attention_state(struct pci_slot *slot,
|
|
uint8_t val)
|
|
{
|
|
struct phb *phb = slot->phb;
|
|
struct pci_device *pd = slot->pd;
|
|
uint32_t ecap;
|
|
uint16_t state;
|
|
|
|
/* Drop the request if functionality doesn't exist */
|
|
if (!(slot->slot_cap & PCICAP_EXP_SLOTCAP_ATTNI))
|
|
return OPAL_SUCCESS;
|
|
|
|
/* Update with the requested state */
|
|
ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false);
|
|
pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCTL, &state);
|
|
state &= ~PCICAP_EXP_SLOTCTL_ATTNI;
|
|
switch (val) {
|
|
case PCI_SLOT_ATTN_LED_ON:
|
|
state |= (PCIE_INDIC_ON << 6);
|
|
break;
|
|
case PCI_SLOT_ATTN_LED_BLINK:
|
|
state |= (PCIE_INDIC_BLINK << 6);
|
|
break;
|
|
case PCI_SLOT_ATTN_LED_OFF:
|
|
state |= (PCIE_INDIC_OFF << 6);
|
|
break;
|
|
default:
|
|
prlog(PR_ERR, PCIE_SLOT_PREFIX
|
|
"Invalid attention state (0x%x)\n", slot->id, val);
|
|
return OPAL_PARAMETER;
|
|
}
|
|
|
|
pci_cfg_write16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCTL, state);
|
|
return OPAL_SUCCESS;
|
|
}
|
|
|
|
static int64_t pcie_slot_set_power_state_ext(struct pci_slot *slot, uint8_t val,
|
|
bool surprise_check)
|
|
{
|
|
struct phb *phb = slot->phb;
|
|
struct pci_device *pd = slot->pd;
|
|
uint32_t ecap;
|
|
uint16_t state;
|
|
|
|
if (slot->power_state == val)
|
|
return OPAL_SUCCESS;
|
|
|
|
/* Update the power state and return immediately if the power
|
|
* control functionality isn't supported on the PCI slot.
|
|
*/
|
|
if (!(slot->slot_cap & PCICAP_EXP_SLOTCAP_PWCTRL)) {
|
|
slot->power_state = val;
|
|
return OPAL_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Suprise hotpluggable slots need to be handled with care since
|
|
* many systems do not implement the presence detect side-band
|
|
* signal. Instead, they rely on in-band presence to report the
|
|
* existence of a hotplugged card.
|
|
*
|
|
* This is problematic because:
|
|
* a) When PERST is asserted in-band presence doesn't work, and
|
|
* b) Switches assert PERST as a part of the "slot power down" sequence
|
|
*
|
|
* To work around the problem we leave the slot physically powered on
|
|
* and exit early here. This way when a new card is inserted, the switch
|
|
* will raise an interrupt due to the PresDet status changing.
|
|
*/
|
|
if (surprise_check && slot->surprise_pluggable) {
|
|
slot->power_state = val;
|
|
if (val == PCI_SLOT_POWER_OFF)
|
|
return OPAL_SUCCESS;
|
|
|
|
/*
|
|
* Some systems have the slot power disabled by default
|
|
* so we always perform the power-on step. This is not
|
|
* *strictly* required, but it's probably a good idea.
|
|
*/
|
|
}
|
|
|
|
pci_slot_set_state(slot, PCI_SLOT_STATE_SPOWER_START);
|
|
slot->power_state = val;
|
|
ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false);
|
|
pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCTL, &state);
|
|
state &= ~(PCICAP_EXP_SLOTCTL_PWRCTLR | PCICAP_EXP_SLOTCTL_PWRI);
|
|
switch (val) {
|
|
case PCI_SLOT_POWER_OFF:
|
|
state |= (PCICAP_EXP_SLOTCTL_PWRCTLR | (PCIE_INDIC_OFF << 8));
|
|
break;
|
|
case PCI_SLOT_POWER_ON:
|
|
state |= (PCIE_INDIC_ON << 8);
|
|
break;
|
|
default:
|
|
pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL);
|
|
prlog(PR_ERR, PCIE_SLOT_PREFIX
|
|
"Invalid power state (0x%x)\n", slot->id, val);
|
|
return OPAL_PARAMETER;
|
|
}
|
|
|
|
pci_cfg_write16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCTL, state);
|
|
pci_slot_set_state(slot, PCI_SLOT_STATE_SPOWER_DONE);
|
|
|
|
return OPAL_ASYNC_COMPLETION;
|
|
}
|
|
|
|
static int64_t pcie_slot_set_power_state(struct pci_slot *slot, uint8_t val)
|
|
{
|
|
return pcie_slot_set_power_state_ext(slot, val, true);
|
|
}
|
|
|
|
static int64_t pcie_slot_sm_poll_link(struct pci_slot *slot)
|
|
{
|
|
struct phb *phb = slot->phb;
|
|
struct pci_device *pd = slot->pd;
|
|
uint32_t ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false);
|
|
uint16_t val;
|
|
uint8_t presence = 0;
|
|
|
|
switch (slot->state) {
|
|
case PCI_SLOT_STATE_LINK_START_POLL:
|
|
PCIE_SLOT_DBG(slot, "LINK: Start polling\n");
|
|
|
|
/* Link is down for ever without devices attached */
|
|
if (slot->ops.get_presence_state)
|
|
slot->ops.get_presence_state(slot, &presence);
|
|
if (!presence) {
|
|
PCIE_SLOT_DBG(slot, "LINK: No adapter, end polling\n");
|
|
pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL);
|
|
return OPAL_SUCCESS;
|
|
}
|
|
|
|
/* Enable the link without check */
|
|
pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_LCTL, &val);
|
|
val &= ~PCICAP_EXP_LCTL_LINK_DIS;
|
|
pci_cfg_write16(phb, pd->bdfn, ecap + PCICAP_EXP_LCTL, val);
|
|
|
|
/*
|
|
* If the link change report isn't supported, we expect
|
|
* the link is up and stabilized after one second.
|
|
*/
|
|
if (!(slot->link_cap & PCICAP_EXP_LCAP_DL_ACT_REP)) {
|
|
pci_slot_set_state(slot,
|
|
PCI_SLOT_STATE_LINK_DELAY_FINALIZED);
|
|
return pci_slot_set_sm_timeout(slot, secs_to_tb(1));
|
|
}
|
|
|
|
/*
|
|
* Poll the link state if link state change report is
|
|
* supported on the link.
|
|
*/
|
|
pci_slot_set_state(slot, PCI_SLOT_STATE_LINK_POLLING);
|
|
slot->retries = 250;
|
|
return pci_slot_set_sm_timeout(slot, msecs_to_tb(20));
|
|
case PCI_SLOT_STATE_LINK_DELAY_FINALIZED:
|
|
PCIE_SLOT_DBG(slot, "LINK: No link report, end polling\n");
|
|
if (slot->ops.prepare_link_change)
|
|
slot->ops.prepare_link_change(slot, true);
|
|
pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL);
|
|
return OPAL_SUCCESS;
|
|
case PCI_SLOT_STATE_LINK_POLLING:
|
|
pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_LSTAT, &val);
|
|
if (val & PCICAP_EXP_LSTAT_DLLL_ACT) {
|
|
PCIE_SLOT_DBG(slot, "LINK: Link is up, end polling\n");
|
|
if (slot->ops.prepare_link_change)
|
|
slot->ops.prepare_link_change(slot, true);
|
|
pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL);
|
|
return OPAL_SUCCESS;
|
|
}
|
|
|
|
/* Check link state again until timeout */
|
|
if (slot->retries-- == 0) {
|
|
prlog(PR_ERR, PCIE_SLOT_PREFIX
|
|
"LINK: Timeout waiting for up (%04x)\n",
|
|
slot->id, val);
|
|
pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL);
|
|
return OPAL_SUCCESS;
|
|
}
|
|
|
|
return pci_slot_set_sm_timeout(slot, msecs_to_tb(20));
|
|
default:
|
|
prlog(PR_ERR, PCIE_SLOT_PREFIX
|
|
"Link: Unexpected slot state %08x\n",
|
|
slot->id, slot->state);
|
|
}
|
|
|
|
pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL);
|
|
return OPAL_HARDWARE;
|
|
}
|
|
|
|
static void pcie_slot_reset(struct pci_slot *slot, bool assert)
|
|
{
|
|
struct phb *phb = slot->phb;
|
|
struct pci_device *pd = slot->pd;
|
|
uint16_t ctl;
|
|
|
|
pci_cfg_read16(phb, pd->bdfn, PCI_CFG_BRCTL, &ctl);
|
|
if (assert)
|
|
ctl |= PCI_CFG_BRCTL_SECONDARY_RESET;
|
|
else
|
|
ctl &= ~PCI_CFG_BRCTL_SECONDARY_RESET;
|
|
pci_cfg_write16(phb, pd->bdfn, PCI_CFG_BRCTL, ctl);
|
|
}
|
|
|
|
static int64_t pcie_slot_sm_hreset(struct pci_slot *slot)
|
|
{
|
|
switch (slot->state) {
|
|
case PCI_SLOT_STATE_NORMAL:
|
|
PCIE_SLOT_DBG(slot, "HRESET: Starts\n");
|
|
if (slot->ops.prepare_link_change) {
|
|
PCIE_SLOT_DBG(slot, "HRESET: Prepare for link down\n");
|
|
slot->ops.prepare_link_change(slot, false);
|
|
}
|
|
/* fall through */
|
|
case PCI_SLOT_STATE_HRESET_START:
|
|
PCIE_SLOT_DBG(slot, "HRESET: Assert\n");
|
|
pcie_slot_reset(slot, true);
|
|
pci_slot_set_state(slot, PCI_SLOT_STATE_HRESET_HOLD);
|
|
return pci_slot_set_sm_timeout(slot, msecs_to_tb(250));
|
|
case PCI_SLOT_STATE_HRESET_HOLD:
|
|
PCIE_SLOT_DBG(slot, "HRESET: Deassert\n");
|
|
pcie_slot_reset(slot, false);
|
|
pci_slot_set_state(slot, PCI_SLOT_STATE_LINK_START_POLL);
|
|
return pci_slot_set_sm_timeout(slot, msecs_to_tb(1800));
|
|
default:
|
|
PCIE_SLOT_DBG(slot, "HRESET: Unexpected slot state %08x\n",
|
|
slot->state);
|
|
}
|
|
|
|
pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL);
|
|
return OPAL_HARDWARE;
|
|
}
|
|
|
|
/*
|
|
* Usually, individual platforms need to override the power
|
|
* management methods for fundamental reset, but the hot
|
|
* reset method is commonly shared.
|
|
*/
|
|
static int64_t pcie_slot_sm_freset(struct pci_slot *slot)
|
|
{
|
|
uint8_t power_state = PCI_SLOT_POWER_ON;
|
|
|
|
switch (slot->state) {
|
|
case PCI_SLOT_STATE_NORMAL:
|
|
PCIE_SLOT_DBG(slot, "FRESET: Starts\n");
|
|
if (slot->ops.prepare_link_change)
|
|
slot->ops.prepare_link_change(slot, false);
|
|
|
|
/* Retrieve power state */
|
|
if (slot->ops.get_power_state) {
|
|
PCIE_SLOT_DBG(slot, "FRESET: Retrieve power state\n");
|
|
slot->ops.get_power_state(slot, &power_state);
|
|
}
|
|
|
|
/* In power on state, power it off */
|
|
if (power_state == PCI_SLOT_POWER_ON) {
|
|
PCIE_SLOT_DBG(slot, "FRESET: Power is on, turn off\n");
|
|
pcie_slot_set_power_state_ext(slot,
|
|
PCI_SLOT_POWER_OFF, false);
|
|
pci_slot_set_state(slot,
|
|
PCI_SLOT_STATE_FRESET_POWER_OFF);
|
|
return pci_slot_set_sm_timeout(slot, msecs_to_tb(50));
|
|
}
|
|
/* No power state change, */
|
|
/* fallthrough */
|
|
case PCI_SLOT_STATE_FRESET_POWER_OFF:
|
|
PCIE_SLOT_DBG(slot, "FRESET: Power is off, turn on\n");
|
|
pcie_slot_set_power_state_ext(slot, PCI_SLOT_POWER_ON, false);
|
|
|
|
pci_slot_set_state(slot, PCI_SLOT_STATE_LINK_START_POLL);
|
|
return pci_slot_set_sm_timeout(slot, msecs_to_tb(50));
|
|
default:
|
|
prlog(PR_ERR, PCIE_SLOT_PREFIX
|
|
"FRESET: Unexpected slot state %08x\n",
|
|
slot->id, slot->state);
|
|
}
|
|
|
|
pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL);
|
|
return OPAL_HARDWARE;
|
|
}
|
|
|
|
struct pci_slot *pcie_slot_create(struct phb *phb, struct pci_device *pd)
|
|
{
|
|
struct pci_slot *slot;
|
|
uint32_t ecap;
|
|
uint16_t slot_ctl;
|
|
|
|
/* Allocate PCI slot */
|
|
slot = pci_slot_alloc(phb, pd);
|
|
if (!slot)
|
|
return NULL;
|
|
|
|
/* Cache the link and slot capabilities */
|
|
ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false);
|
|
pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_CAPABILITY_REG,
|
|
&slot->pcie_cap);
|
|
pci_cfg_read32(phb, pd->bdfn, ecap + PCICAP_EXP_LCAP,
|
|
&slot->link_cap);
|
|
|
|
/* Leave PCI slot capability blank if PCI slot isn't supported */
|
|
if (slot->pcie_cap & PCICAP_EXP_CAP_SLOT)
|
|
pci_cfg_read32(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCAP,
|
|
&slot->slot_cap);
|
|
else
|
|
slot->slot_cap = 0;
|
|
|
|
if (slot->slot_cap & PCICAP_EXP_SLOTCAP_HPLUG_CAP)
|
|
slot->pluggable = 1;
|
|
|
|
/* Assume the slot is powered on by default */
|
|
slot->power_state = PCI_SLOT_POWER_ON;
|
|
if (slot->slot_cap & PCICAP_EXP_SLOTCAP_PWCTRL) {
|
|
slot->power_ctl = 1;
|
|
|
|
pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCTL,
|
|
&slot_ctl);
|
|
if (slot_ctl & PCICAP_EXP_SLOTCTL_PWRCTLR)
|
|
slot->power_state = PCI_SLOT_POWER_OFF;
|
|
}
|
|
|
|
if (slot->slot_cap & PCICAP_EXP_SLOTCAP_PWRI)
|
|
slot->power_led_ctl = PCI_SLOT_PWR_LED_CTL_KERNEL;
|
|
if (slot->slot_cap & PCICAP_EXP_SLOTCAP_ATTNI)
|
|
slot->attn_led_ctl = PCI_SLOT_ATTN_LED_CTL_KERNEL;
|
|
slot->wired_lanes = ((slot->link_cap & PCICAP_EXP_LCAP_MAXWDTH) >> 4);
|
|
|
|
/* The surprise hotplug capability is claimed when it's supported
|
|
* in the slot's capability bits or link state change reporting is
|
|
* supported in PCIe link capability. It means the surprise hotplug
|
|
* relies on presence or link state change events. In order for the
|
|
* link state change event to be properly raised during surprise hot
|
|
* add/remove, the power supply to the slot should be always on.
|
|
*
|
|
* For PCI slots that don't claim surprise hotplug capability explicitly.
|
|
* Its PDC (Presence Detection Change) isn't reliable. To mark that as
|
|
* broken on them.
|
|
*/
|
|
if (slot->pcie_cap & PCICAP_EXP_CAP_SLOT) {
|
|
if (slot->slot_cap & PCICAP_EXP_SLOTCAP_HPLUG_SURP) {
|
|
slot->surprise_pluggable = 1;
|
|
} else if (slot->link_cap & PCICAP_EXP_LCAP_DL_ACT_REP) {
|
|
slot->surprise_pluggable = 1;
|
|
|
|
pci_slot_add_flags(slot, PCI_SLOT_FLAG_BROKEN_PDC);
|
|
}
|
|
}
|
|
|
|
/* Standard slot operations */
|
|
slot->ops.get_presence_state = pcie_slot_get_presence_state;
|
|
slot->ops.get_link_state = pcie_slot_get_link_state;
|
|
slot->ops.get_power_state = pcie_slot_get_power_state;
|
|
slot->ops.get_attention_state = pcie_slot_get_attention_state;
|
|
slot->ops.get_latch_state = pcie_slot_get_latch_state;
|
|
slot->ops.set_power_state = pcie_slot_set_power_state;
|
|
slot->ops.set_attention_state = pcie_slot_set_attention_state;
|
|
|
|
/*
|
|
* State machine (SM) based reset stuff. The poll function is always
|
|
* unified for all cases.
|
|
*/
|
|
slot->ops.poll_link = pcie_slot_sm_poll_link;
|
|
slot->ops.hreset = pcie_slot_sm_hreset;
|
|
slot->ops.freset = pcie_slot_sm_freset;
|
|
|
|
slot->wired_lanes = PCI_SLOT_WIRED_LANES_UNKNOWN;
|
|
slot->connector_type = PCI_SLOT_CONNECTOR_PCIE_NS;
|
|
slot->card_desc = PCI_SLOT_DESC_NON_STANDARD;
|
|
slot->card_mech = PCI_SLOT_MECH_NONE;
|
|
slot->power_led_ctl = PCI_SLOT_PWR_LED_CTL_NONE;
|
|
slot->attn_led_ctl = PCI_SLOT_ATTN_LED_CTL_NONE;
|
|
|
|
return slot;
|
|
}
|
|
|
|
/* FIXME: this is kind of insane */
|
|
struct pci_slot *pcie_slot_create_dynamic(struct phb *phb,
|
|
struct pci_device *pd)
|
|
{
|
|
uint32_t ecap, val;
|
|
struct pci_slot *slot;
|
|
|
|
if (!phb || !pd || pd->slot)
|
|
return NULL;
|
|
|
|
/* Try to create slot whose details aren't provided by platform. */
|
|
if (pd->dev_type != PCIE_TYPE_SWITCH_DNPORT)
|
|
return NULL;
|
|
|
|
ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false);
|
|
pci_cfg_read32(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCAP, &val);
|
|
if (!(val & PCICAP_EXP_SLOTCAP_HPLUG_CAP))
|
|
return NULL;
|
|
|
|
slot = pcie_slot_create(phb, pd);
|
|
|
|
/* On superMicro's "p8dnu" platform, we create dynamic PCI slots
|
|
* for all downstream ports of PEX9733 that is connected to PHB
|
|
* direct slot. The power supply to the PCI slot is lost after
|
|
* PCI adapter is removed from it. The power supply can't be
|
|
* turned on when the slot is in empty state. The power supply
|
|
* isn't turned on automatically when inserting PCI adapter to
|
|
* the slot at later point. We set a flag to the slot here, to
|
|
* turn on the power supply in (suprise or managed) hot-add path.
|
|
*
|
|
* We have same issue with PEX8718 as above on "p8dnu" platform.
|
|
*/
|
|
if (dt_node_is_compatible(dt_root, "supermicro,p8dnu") && slot &&
|
|
slot->pd && (slot->pd->vdid == 0x973310b5 ||
|
|
slot->pd->vdid == 0x871810b5))
|
|
pci_slot_add_flags(slot, PCI_SLOT_FLAG_FORCE_POWERON);
|
|
|
|
return slot;
|
|
}
|