aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/Makefile.inc4
-rw-r--r--core/pci-slot.c206
-rw-r--r--core/pci.c77
-rw-r--r--core/pcie-slot.c452
-rw-r--r--include/opal-api.h10
-rw-r--r--include/pci-slot.h255
-rw-r--r--include/pci.h66
-rw-r--r--include/platform.h1
-rw-r--r--platforms/ibm-fsp/lxvpd.c1
9 files changed, 992 insertions, 80 deletions
diff --git a/core/Makefile.inc b/core/Makefile.inc
index 5af0d7c..13b287c 100644
--- a/core/Makefile.inc
+++ b/core/Makefile.inc
@@ -3,8 +3,8 @@
SUBDIRS += core
CORE_OBJS = relocate.o console.o stack.o init.o chip.o mem_region.o
CORE_OBJS += malloc.o lock.o cpu.o utils.o fdt.o opal.o interrupts.o
-CORE_OBJS += timebase.o opal-msg.o pci.o pci-opal.o fast-reboot.o
-CORE_OBJS += device.o exceptions.o trace.o affinity.o vpd.o
+CORE_OBJS += timebase.o opal-msg.o pci.o pci-slot.o pcie-slot.o pci-opal.o
+CORE_OBJS += fast-reboot.o device.o exceptions.o trace.o affinity.o vpd.o
CORE_OBJS += hostservices.o platform.o nvram.o nvram-format.o hmi.o
CORE_OBJS += console-log.o ipmi.o time-utils.o pel.o pool.o errorlog.o
CORE_OBJS += timer.o i2c.o rtc.o flash.o sensor.o ipmi-opal.o
diff --git a/core/pci-slot.c b/core/pci-slot.c
new file mode 100644
index 0000000..8735598
--- /dev/null
+++ b/core/pci-slot.c
@@ -0,0 +1,206 @@
+/* Copyright 2013-2016 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <skiboot.h>
+#include <opal-msg.h>
+#include <pci-cfg.h>
+#include <pci.h>
+#include <pci-slot.h>
+
+/* Debugging options */
+#define PCI_SLOT_PREFIX "PCI-SLOT-%016llx "
+#define PCI_SLOT_DBG(s, fmt, a...) \
+ prlog(PR_DEBUG, PCI_SLOT_PREFIX fmt, (s)->id, ##a)
+
+static void pci_slot_prepare_link_change(struct pci_slot *slot, bool up)
+{
+ struct phb *phb = slot->phb;
+ struct pci_device *pd = slot->pd;
+ uint32_t aercap, mask;
+
+ /*
+ * Mask the link down and receiver error before the link becomes
+ * down. Otherwise, unmask the errors when the link is up.
+ */
+ if (pci_has_cap(pd, PCIECAP_ID_AER, true)) {
+ aercap = pci_cap(pd, PCIECAP_ID_AER, true);
+
+ /* Link down error */
+ pci_cfg_read32(phb, pd->bdfn, aercap + PCIECAP_AER_UE_MASK,
+ &mask);
+ if (up)
+ mask &= ~PCIECAP_AER_UE_MASK_SURPRISE_DOWN;
+ else
+ mask |= PCIECAP_AER_UE_MASK_SURPRISE_DOWN;
+ pci_cfg_write32(phb, pd->bdfn, aercap + PCIECAP_AER_UE_MASK,
+ mask);
+
+ /* Receiver error */
+ pci_cfg_read32(phb, pd->bdfn, aercap + PCIECAP_AER_CE_MASK,
+ &mask);
+ if (up)
+ mask &= ~PCIECAP_AER_CE_RECVR_ERR;
+ else
+ mask |= PCIECAP_AER_CE_RECVR_ERR;
+ pci_cfg_write32(phb, pd->bdfn, aercap + PCIECAP_AER_CE_MASK,
+ mask);
+ }
+
+ /*
+ * We're coming back from reset. We need restore bus ranges
+ * and reinitialize the affected bridges and devices.
+ */
+ if (up) {
+ pci_restore_bridge_buses(phb, pd);
+ if (phb->ops->device_init)
+ pci_walk_dev(phb, pd, phb->ops->device_init, NULL);
+ }
+}
+
+static int64_t pci_slot_sm_poll(struct pci_slot *slot)
+{
+ uint64_t now = mftb();
+ int64_t ret;
+
+ /* Return remaining timeout if we're still waiting */
+ if (slot->delay_tgt_tb &&
+ tb_compare(now, slot->delay_tgt_tb) == TB_ABEFOREB)
+ return slot->delay_tgt_tb - now;
+
+ slot->delay_tgt_tb = 0;
+ switch (slot->state & PCI_SLOT_STATE_MASK) {
+ case PCI_SLOT_STATE_LINK:
+ ret = slot->ops.poll_link(slot);
+ break;
+ case PCI_SLOT_STATE_HRESET:
+ ret = slot->ops.hreset(slot);
+ break;
+ case PCI_SLOT_STATE_FRESET:
+ ret = slot->ops.freset(slot);
+ break;
+ case PCI_SLOT_STATE_PFRESET:
+ ret = slot->ops.pfreset(slot);
+ break;
+ case PCI_SLOT_STATE_CRESET:
+ ret = slot->ops.creset(slot);
+ break;
+ default:
+ prlog(PR_ERR, PCI_SLOT_PREFIX
+ "Invalid state %08x\n", slot->id, slot->state);
+ pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL);
+ return OPAL_HARDWARE;
+ }
+
+ return ret;
+}
+
+void pci_slot_add_dt_properties(struct pci_slot *slot,
+ struct dt_node *np)
+{
+ /* Bail without device node */
+ if (!np)
+ return;
+
+ dt_add_property_cells(np, "ibm,reset-by-firmware", 1);
+ dt_add_property_cells(np, "ibm,slot-pluggable", slot->pluggable);
+ dt_add_property_cells(np, "ibm,slot-power-ctl", slot->power_ctl);
+ dt_add_property_cells(np, "ibm,slot-power-led-ctlled",
+ slot->power_led_ctl);
+ dt_add_property_cells(np, "ibm,slot-attn-led", slot->attn_led_ctl);
+ dt_add_property_cells(np, "ibm,slot-connector-type",
+ slot->connector_type);
+ dt_add_property_cells(np, "ibm,slot-card-desc", slot->card_desc);
+ dt_add_property_cells(np, "ibm,slot-card-mech", slot->card_mech);
+ dt_add_property_cells(np, "ibm,slot-wired-lanes", slot->wired_lanes);
+
+ if (slot->ops.add_properties)
+ slot->ops.add_properties(slot, np);
+}
+
+struct pci_slot *pci_slot_alloc(struct phb *phb,
+ struct pci_device *pd)
+{
+ struct pci_slot *slot = NULL;
+
+ /*
+ * The function can be used to allocate either PHB slot or normal
+ * one. For both cases, the @phb should be always valid.
+ */
+ if (!phb)
+ return NULL;
+
+ /*
+ * When @pd is NULL, we're going to create a PHB slot. Otherwise,
+ * a normal slot will be created. Check if the specified slot
+ * already exists or not.
+ */
+ slot = pd ? pd->slot : phb->slot;
+ if (slot) {
+ prlog(PR_ERR, PCI_SLOT_PREFIX "Already exists\n", slot->id);
+ return slot;
+ }
+
+ /* Allocate memory chunk */
+ slot = zalloc(sizeof(struct pci_slot));
+ if (!slot) {
+ prlog(PR_ERR, "%s: Out of memory\n", __func__);
+ return NULL;
+ }
+
+ /*
+ * The polling function sholdn't be overridden by individual
+ * platforms
+ */
+ slot->phb = phb;
+ slot->pd = pd;
+ pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL);
+ slot->power_state = PCI_SLOT_POWER_ON;
+ slot->ops.poll = pci_slot_sm_poll;
+ slot->ops.prepare_link_change = pci_slot_prepare_link_change;
+ if (!pd) {
+ slot->id = PCI_PHB_SLOT_ID(phb);
+ phb->slot = slot;
+ } else {
+ slot->id = PCI_SLOT_ID(phb, pd->bdfn);
+ pd->slot = slot;
+ }
+
+ return slot;
+}
+
+struct pci_slot *pci_slot_find(uint64_t id)
+{
+ struct phb *phb;
+ struct pci_device *pd;
+ struct pci_slot *slot;
+ uint64_t index;
+ uint16_t bdfn;
+
+ index = PCI_SLOT_PHB_INDEX(id);
+ phb = pci_get_phb(index);
+
+ /* PHB slot */
+ if (!(id & PCI_SLOT_ID_PREFIX)) {
+ slot = phb ? phb->slot : NULL;
+ return slot;
+ }
+
+ /* Normal PCI slot */
+ bdfn = PCI_SLOT_BDFN(id);
+ pd = phb ? pci_find_dev(phb, bdfn) : NULL;
+ slot = pd ? pd->slot : NULL;
+ return slot;
+}
diff --git a/core/pci.c b/core/pci.c
index d869ec2..9f5c1f3 100644
--- a/core/pci.c
+++ b/core/pci.c
@@ -18,6 +18,7 @@
#include <cpu.h>
#include <pci.h>
#include <pci-cfg.h>
+#include <pci-slot.h>
#include <timebase.h>
#include <device.h>
#include <fsp.h>
@@ -459,11 +460,33 @@ static void pci_cleanup_bridge(struct phb *phb, struct pci_device *pd)
pci_cfg_write16(phb, pd->bdfn, PCI_CFG_CMD, cmd);
}
+/* Remove all subordinate PCI devices leading from the indicated
+ * PCI bus. It's used to remove all PCI devices behind one PCI
+ * slot at unplugging time
+ */
+void pci_remove_bus(struct phb *phb, struct list_head *list)
+{
+ struct pci_device *pd, *tmp;
+
+ list_for_each_safe(list, pd, tmp, link) {
+ pci_remove_bus(phb, &pd->children);
+
+ /* Release device node and PCI slot */
+ if (pd->dn)
+ dt_free(pd->dn);
+ if (pd->slot)
+ free(pd->slot);
+
+ /* Remove from parent list and release itself */
+ list_del(&pd->link);
+ free(pd);
+ }
+}
-/* pci_scan - Perform a recursive scan of the bus at bus_number
- * populating the list passed as an argument. This also
- * performs the bus numbering, so it returns the largest
- * bus number that was assigned.
+/* Perform a recursive scan of the bus at bus_number populating
+ * the list passed as an argument. This also performs the bus
+ * numbering, so it returns the largest bus number that was
+ * assigned.
*
* Note: Eventually this might want to access some VPD information
* in order to know what slots to scan and what not etc..
@@ -473,9 +496,9 @@ static void pci_cleanup_bridge(struct phb *phb, struct pci_device *pd)
* XXX NOTE: We might also want to setup the PCIe MPS/MRSS properly
* here as Linux may or may not do it
*/
-static uint8_t pci_scan(struct phb *phb, uint8_t bus, uint8_t max_bus,
- struct list_head *list, struct pci_device *parent,
- bool scan_downstream)
+uint8_t pci_scan_bus(struct phb *phb, uint8_t bus, uint8_t max_bus,
+ struct list_head *list, struct pci_device *parent,
+ bool scan_downstream)
{
struct pci_device *pd = NULL;
uint8_t dev, fn, next_bus, max_sub, save_max;
@@ -592,8 +615,8 @@ static uint8_t pci_scan(struct phb *phb, uint8_t bus, uint8_t max_bus,
/* Perform recursive scan */
if (do_scan) {
- max_sub = pci_scan(phb, next_bus, max_bus,
- &pd->children, pd, true);
+ max_sub = pci_scan_bus(phb, next_bus, max_bus,
+ &pd->children, pd, true);
} else if (!use_max) {
/* XXX Empty bridge... we leave room for hotplug
* slots etc.. but we should be smarter at figuring
@@ -801,7 +824,7 @@ static void pci_scan_phb(void *data)
/* Scan root port and downstream ports if applicable */
PCIDBG(phb, 0, "Scanning (upstream%s)...\n",
has_link ? "+downsteam" : " only");
- pci_scan(phb, 0, 0xff, &phb->devices, NULL, has_link);
+ pci_scan_bus(phb, 0, 0xff, &phb->devices, NULL, has_link);
/* Configure MPS (Max Payload Size) for PCIe domain */
pci_walk_dev(phb, NULL, pci_get_mps, &mps);
@@ -1327,12 +1350,12 @@ static void pci_print_summary_line(struct phb *phb, struct pci_device *pd,
rev_class & 0xff, rev_class >> 8, cname, slotstr);
}
-
-static void pci_add_one_node(struct phb *phb, struct pci_device *pd,
- struct dt_node *parent_node,
- struct pci_lsi_state *lstate, uint8_t swizzle)
+static void pci_add_one_device_node(struct phb *phb,
+ struct pci_device *pd,
+ struct dt_node *parent_node,
+ struct pci_lsi_state *lstate,
+ uint8_t swizzle)
{
- struct pci_device *child;
struct dt_node *np;
const char *cname;
#define MAX_NAME 256
@@ -1447,14 +1470,14 @@ static void pci_add_one_node(struct phb *phb, struct pci_device *pd,
* Instead add a ranges property that explicitly translates 1:1.
*/
dt_add_property(np, "ranges", ranges_direct, sizeof(ranges_direct));
-
- list_for_each(&pd->children, child, link)
- pci_add_one_node(phb, child, np, lstate, swizzle);
}
-static void pci_add_nodes(struct phb *phb)
+void pci_add_device_nodes(struct phb *phb,
+ struct list_head *list,
+ struct dt_node *parent_node,
+ struct pci_lsi_state *lstate,
+ uint8_t swizzle)
{
- struct pci_lsi_state *lstate = &phb->lstate;
struct pci_device *pd;
/* If the PHB has its own slot info, add them */
@@ -1462,8 +1485,15 @@ static void pci_add_nodes(struct phb *phb)
pci_add_slot_properties(phb, phb->slot_info, NULL);
/* Add all child devices */
- list_for_each(&phb->devices, pd, link)
- pci_add_one_node(phb, pd, phb->dt_node, lstate, 0);
+ list_for_each(list, pd, link) {
+ pci_add_one_device_node(phb, pd, parent_node,
+ lstate, swizzle);
+ if (list_empty(&pd->children))
+ continue;
+
+ pci_add_device_nodes(phb, &pd->children,
+ pd->dn, lstate, swizzle);
+ }
}
static void __pci_reset(struct list_head *list)
@@ -1544,7 +1574,8 @@ void pci_init_slots(void)
for (i = 0; i < ARRAY_SIZE(phbs); i++) {
if (!phbs[i])
continue;
- pci_add_nodes(phbs[i]);
+ pci_add_device_nodes(phbs[i], &phbs[i]->devices,
+ phbs[i]->dt_node, &phbs[i]->lstate, 0);
}
/* PHB final fixup */
diff --git a/core/pcie-slot.c b/core/pcie-slot.c
new file mode 100644
index 0000000..62933a4
--- /dev/null
+++ b/core/pcie-slot.c
@@ -0,0 +1,452 @@
+/* Copyright 2013-2016 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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->slot_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)
+{
+ /* The power is always on if no functionality is supported */
+ if (!(slot->slot_cap & PCICAP_EXP_SLOTCAP_PWCTRL))
+ *val = PCI_SLOT_POWER_ON;
+ else
+ *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(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_PWCTRL))
+ return OPAL_SUCCESS;
+
+ if (slot->power_state == val)
+ return OPAL_SUCCESS;
+
+ 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_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 &&
+ slot->ops.set_power_state) {
+ PCIE_SLOT_DBG(slot, "FRESET: Power is on, turn off\n");
+ slot->ops.set_power_state(slot, PCI_SLOT_POWER_OFF);
+ 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, fall through */
+ case PCI_SLOT_STATE_FRESET_POWER_OFF:
+ PCIE_SLOT_DBG(slot, "FRESET: Power is off, turn on\n");
+ if (slot->ops.set_power_state)
+ slot->ops.set_power_state(slot, PCI_SLOT_POWER_ON);
+ pci_slot_set_state(slot, PCI_SLOT_STATE_HRESET_START);
+ 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;
+
+ /* Allocate PCI slot */
+ slot = pci_slot_alloc(phb, pd);
+ if (!slot)
+ return NULL;
+
+ /* Cache the link and slot capabilities */
+ if (pd) {
+ ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false);
+ pci_cfg_read32(phb, pd->bdfn, ecap + PCICAP_EXP_LCAP,
+ &slot->link_cap);
+ pci_cfg_read32(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCAP,
+ &slot->slot_cap);
+ }
+
+ if ((slot->slot_cap & PCICAP_EXP_SLOTCAP_HPLUG_SURP) &&
+ (slot->slot_cap & PCICAP_EXP_SLOTCAP_HPLUG_CAP))
+ slot->pluggable = 1;
+ if (slot->slot_cap & PCICAP_EXP_SLOTCAP_PWCTRL)
+ slot->power_ctl = 1;
+ 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);
+
+ /* 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->ops.pfreset = NULL;
+
+ return slot;
+}
diff --git a/include/opal-api.h b/include/opal-api.h
index 2f91715..b4d96b8 100644
--- a/include/opal-api.h
+++ b/include/opal-api.h
@@ -376,6 +376,16 @@ enum OpalPciMaskAction {
OPAL_MASK_ERROR_TYPE = 1
};
+enum OpalPciSlotPresence {
+ OPAL_PCI_SLOT_EMPTY = 0,
+ OPAL_PCI_SLOT_PRESENT = 1
+};
+
+enum OpalPciSlotPower {
+ OPAL_PCI_SLOT_POWER_OFF = 0,
+ OPAL_PCI_SLOT_POWER_ON = 1
+};
+
enum OpalSlotLedType {
OPAL_SLOT_LED_TYPE_ID = 0, /* IDENTIFY LED */
OPAL_SLOT_LED_TYPE_FAULT = 1, /* FAULT LED */
diff --git a/include/pci-slot.h b/include/pci-slot.h
new file mode 100644
index 0000000..cf22432
--- /dev/null
+++ b/include/pci-slot.h
@@ -0,0 +1,255 @@
+/* Copyright 2013-2016 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __PCI_SLOT_H
+#define __PCI_SLOT_H
+
+#include <opal.h>
+#include <device.h>
+#include <timebase.h>
+#include <timer.h>
+#include <ccan/list/list.h>
+
+/*
+ * PCI Slot Info: Wired Lane Values
+ *
+ * Values 0 to 6 match slot map 1005. In case of *any* change here
+ * make sure to keep the lxvpd.c parsing code in sync *and* the
+ * corresponding label strings in pci.c
+ */
+#define PCI_SLOT_WIRED_LANES_UNKNOWN 0x00
+#define PCI_SLOT_WIRED_LANES_PCIE_X1 0x01
+#define PCI_SLOT_WIRED_LANES_PCIE_X2 0x02
+#define PCI_SLOT_WIRED_LANES_PCIE_X4 0x03
+#define PCI_SLOT_WIRED_LANES_PCIE_X8 0x04
+#define PCI_SLOT_WIRED_LANES_PCIE_X16 0x05
+#define PCI_SLOT_WIRED_LANES_PCIE_X32 0x06
+#define PCI_SLOT_WIRED_LANES_PCIX_32 0x07
+#define PCI_SLOT_WIRED_LANES_PCIX_64 0x08
+
+/* PCI Slot Info: Bus Clock Values */
+#define PCI_SLOT_BUS_CLK_RESERVED 0x00
+#define PCI_SLOT_BUS_CLK_GEN_1 0x01
+#define PCI_SLOT_BUS_CLK_GEN_2 0x02
+#define PCI_SLOT_BUS_CLK_GEN_3 0x03
+
+/* PCI Slot Info: Connector Type Values */
+#define PCI_SLOT_CONNECTOR_PCIE_EMBED 0x00
+#define PCI_SLOT_CONNECTOR_PCIE_X1 0x01
+#define PCI_SLOT_CONNECTOR_PCIE_X2 0x02
+#define PCI_SLOT_CONNECTOR_PCIE_X4 0x03
+#define PCI_SLOT_CONNECTOR_PCIE_X8 0x04
+#define PCI_SLOT_CONNECTOR_PCIE_X16 0x05
+#define PCI_SLOT_CONNECTOR_PCIE_NS 0x0E /* Non-Standard */
+
+/* PCI Slot Info: Card Description Values */
+#define PCI_SLOT_DESC_NON_STANDARD 0x00 /* Embed/Non-Standard */
+#define PCI_SLOT_DESC_PCIE_FH_FL 0x00 /* Full Height, Full Length */
+#define PCI_SLOT_DESC_PCIE_FH_HL 0x01 /* Full Height, Half Length */
+#define PCI_SLOT_DESC_PCIE_HH_FL 0x02 /* Half Height, Full Length */
+#define PCI_SLOT_DESC_PCIE_HH_HL 0x03 /* Half Height, Half Length */
+
+/* PCI Slot Info: Mechanicals Values */
+#define PCI_SLOT_MECH_NONE 0x00
+#define PCI_SLOT_MECH_RIGHT 0x01
+#define PCI_SLOT_MECH_LEFT 0x02
+#define PCI_SLOT_MECH_RIGHT_LEFT 0x03
+
+/* PCI Slot Info: Power LED Control Values */
+#define PCI_SLOT_PWR_LED_CTL_NONE 0x00 /* No Control */
+#define PCI_SLOT_PWR_LED_CTL_FSP 0x01 /* FSP Controlled */
+#define PCI_SLOT_PWR_LED_CTL_KERNEL 0x02 /* Kernel Controlled */
+
+/* PCI Slot Info: ATTN LED Control Values */
+#define PCI_SLOT_ATTN_LED_CTL_NONE 0x00 /* No Control */
+#define PCI_SLOT_ATTN_LED_CTL_FSP 0x01 /* FSP Controlled */
+#define PCI_SLOT_ATTN_LED_CTL_KERNEL 0x02 /* Kernel Controlled */
+
+/* Attention LED */
+#define PCI_SLOT_ATTN_LED_OFF 0
+#define PCI_SLOT_ATTN_LED_ON 1
+#define PCI_SLOT_ATTN_LED_BLINK 2
+
+/* Power state */
+#define PCI_SLOT_POWER_OFF 0
+#define PCI_SLOT_POWER_ON 1
+
+/*
+ * We have hard and soft reset for slot. Hard reset requires
+ * power-off and then power-on, but soft reset only resets
+ * secondary bus.
+ */
+struct pci_slot;
+struct pci_slot_ops {
+ /* For slot management */
+ int64_t (*get_presence_state)(struct pci_slot *slot, uint8_t *val);
+ int64_t (*get_link_state)(struct pci_slot *slot, uint8_t *val);
+ int64_t (*get_power_state)(struct pci_slot *slot, uint8_t *val);
+ int64_t (*get_attention_state)(struct pci_slot *slot, uint8_t *val);
+ int64_t (*get_latch_state)(struct pci_slot *slot, uint8_t *val);
+ int64_t (*set_power_state)(struct pci_slot *slot, uint8_t val);
+ int64_t (*set_attention_state)(struct pci_slot *slot, uint8_t val);
+
+ /* SM based functions for reset */
+ void (*prepare_link_change)(struct pci_slot *slot, bool is_up);
+ int64_t (*poll_link)(struct pci_slot *slot);
+ int64_t (*creset)(struct pci_slot *slot);
+ int64_t (*freset)(struct pci_slot *slot);
+ int64_t (*pfreset)(struct pci_slot *slot);
+ int64_t (*hreset)(struct pci_slot *slot);
+ int64_t (*poll)(struct pci_slot *slot);
+
+ /* Auxillary functions */
+ void (*add_properties)(struct pci_slot *slot, struct dt_node *np);
+};
+
+/*
+ * The PCI slot state is split up into base and number. With this
+ * design, the individual platforms can introduce their own PCI
+ * slot states with addition to the base. Eventually, the base
+ * state can be recognized by PCI slot core.
+ */
+#define PCI_SLOT_STATE_MASK 0xFFFFFF00
+#define PCI_SLOT_STATE_NORMAL 0x00000000
+#define PCI_SLOT_STATE_LINK 0x00000100
+#define PCI_SLOT_STATE_LINK_START_POLL 0x00000101
+#define PCI_SLOT_STATE_LINK_DELAY_FINALIZED 0x00000102
+#define PCI_SLOT_STATE_LINK_POLLING 0x00000103
+#define PCI_SLOT_STATE_HRESET 0x00000200
+#define PCI_SLOT_STATE_HRESET_START 0x00000201
+#define PCI_SLOT_STATE_HRESET_HOLD 0x00000202
+#define PCI_SLOT_STATE_FRESET 0x00000300
+#define PCI_SLOT_STATE_FRESET_POWER_OFF 0x00000301
+#define PCI_SLOT_STATE_PFRESET 0x00000400
+#define PCI_SLOT_STATE_PFRESET_START 0x00000401
+#define PCI_SLOT_STATE_CRESET 0x00000500
+#define PCI_SLOT_STATE_CRESET_START 0x00000501
+#define PCI_SLOT_STATE_GPOWER 0x00000600
+#define PCI_SLOT_STATE_GPOWER_START 0x00000601
+#define PCI_SLOT_STATE_SPOWER 0x00000700
+#define PCI_SLOT_STATE_SPOWER_START 0x00000701
+#define PCI_SLOT_STATE_SPOWER_DONE 0x00000702
+#define PCI_SLOT_STATE_GPRESENCE 0x00000800
+#define PCI_SLOT_STATE_GPRESENCE_START 0x00000801
+
+
+struct pci_slot {
+ uint32_t flags;
+#define PCI_SLOT_FLAG_BOOTUP 0x1
+
+ struct phb *phb;
+ struct pci_device *pd;
+
+ /* Identifier */
+ uint64_t id;
+ struct timer timer;
+ uint64_t async_token;
+ uint8_t power_state;
+
+ /* Slot information */
+ uint8_t pluggable;
+ uint8_t power_ctl;
+ uint8_t power_led_ctl;
+ uint8_t attn_led_ctl;
+ uint8_t connector_type;
+ uint8_t card_desc;
+ uint8_t card_mech;
+ uint8_t wired_lanes;
+
+ /*
+ * PCI slot is driven by state machine with polling function.
+ * @delay_tgt_tb tracks the current delay while @retries has
+ * the left rounds of delays. They should be set prior to
+ * switching next PCI slot state and changed (decreased)
+ * accordingly in the polling function.
+ */
+ uint32_t state;
+ uint32_t retry_state;
+ uint32_t link_cap;
+ uint32_t slot_cap;
+ uint64_t delay_tgt_tb;
+ uint64_t retries;
+ struct pci_slot_ops ops;
+ void *data;
+};
+
+#define PCI_SLOT_ID_PREFIX 0x8000000000000000
+#define PCI_SLOT_ID(phb, bdfn) \
+ (PCI_SLOT_ID_PREFIX | ((uint64_t)(bdfn) << 16) | (phb)->opal_id)
+#define PCI_PHB_SLOT_ID(phb) ((phb)->opal_id)
+#define PCI_SLOT_PHB_INDEX(id) ((id) & 0xfffful)
+#define PCI_SLOT_BDFN(id) (((id) >> 16) & 0xfffful)
+
+static inline uint32_t pci_slot_add_flags(struct pci_slot *slot,
+ uint32_t flags)
+{
+ uint32_t old = 0;
+
+ if (slot) {
+ old = slot->flags;
+ slot->flags |= flags;
+ }
+
+ return old;
+}
+
+static inline bool pci_slot_has_flags(struct pci_slot *slot,
+ uint32_t flags)
+{
+ if (!slot)
+ return false;
+
+ if ((slot->flags & flags) == flags)
+ return true;
+
+ return false;
+}
+
+static inline uint32_t pci_slot_remove_flags(struct pci_slot *slot,
+ uint32_t flags)
+{
+ uint32_t old = 0;
+
+ if (slot) {
+ old = slot->flags;
+ slot->flags &= ~flags;
+ }
+
+ return old;
+}
+
+static inline void pci_slot_set_state(struct pci_slot *slot, uint32_t state)
+{
+ if (slot)
+ slot->state = state;
+}
+
+static inline uint64_t pci_slot_set_sm_timeout(struct pci_slot *slot,
+ uint64_t dur)
+{
+ if (slot)
+ slot->delay_tgt_tb = mftb() + dur;
+ return dur;
+}
+
+extern struct pci_slot *pci_slot_alloc(struct phb *phb,
+ struct pci_device *pd);
+extern struct pci_slot *pcie_slot_create(struct phb *phb,
+ struct pci_device *pd);
+extern void pci_slot_add_dt_properties(struct pci_slot *slot,
+ struct dt_node *np);
+extern struct pci_slot *pci_slot_find(uint64_t id);
+#endif /* __PCI_SLOT_H */
diff --git a/include/pci.h b/include/pci.h
index 69be49e..486b712 100644
--- a/include/pci.h
+++ b/include/pci.h
@@ -22,60 +22,6 @@
#include <lock.h>
#include <ccan/list/list.h>
-/* PCI Slot Info: Wired Lane Values
- *
- * Values 0 to 6 match slot map 1005. In case of *any* change here
- * make sure to keep the lxvpd.c parsing code in sync *and* the
- * corresponding label strings in pci.c
- */
-#define PCI_SLOT_WIRED_LANES_UNKNOWN 0x00
-#define PCI_SLOT_WIRED_LANES_PCIE_X1 0x01
-#define PCI_SLOT_WIRED_LANES_PCIE_X2 0x02
-#define PCI_SLOT_WIRED_LANES_PCIE_X4 0x03
-#define PCI_SLOT_WIRED_LANES_PCIE_X8 0x04
-#define PCI_SLOT_WIRED_LANES_PCIE_X16 0x05
-#define PCI_SLOT_WIRED_LANES_PCIE_X32 0x06
-#define PCI_SLOT_WIRED_LANES_PCIX_32 0x07
-#define PCI_SLOT_WIRED_LANES_PCIX_64 0x08
-
-/* PCI Slot Info: Bus Clock Values */
-#define PCI_SLOT_BUS_CLK_RESERVED 0x00
-#define PCI_SLOT_BUS_CLK_GEN_1 0x01
-#define PCI_SLOT_BUS_CLK_GEN_2 0x02
-#define PCI_SLOT_BUS_CLK_GEN_3 0x03
-
-/* PCI Slot Info: Connector Type Values */
-#define PCI_SLOT_CONNECTOR_PCIE_EMBED 0x00
-#define PCI_SLOT_CONNECTOR_PCIE_X1 0x01
-#define PCI_SLOT_CONNECTOR_PCIE_X2 0x02
-#define PCI_SLOT_CONNECTOR_PCIE_X4 0x03
-#define PCI_SLOT_CONNECTOR_PCIE_X8 0x04
-#define PCI_SLOT_CONNECTOR_PCIE_X16 0x05
-#define PCI_SLOT_CONNECTOR_PCIE_NS 0x0E /* Non-Standard */
-
-/* PCI Slot Info: Card Description Values */
-#define PCI_SLOT_DESC_NON_STANDARD 0x00 /* Embed/Non-Standard Connector */
-#define PCI_SLOT_DESC_PCIE_FH_FL 0x00 /* Full Height, Full Length */
-#define PCI_SLOT_DESC_PCIE_FH_HL 0x01 /* Full Height, Half Length */
-#define PCI_SLOT_DESC_PCIE_HH_FL 0x02 /* Half Height, Full Length */
-#define PCI_SLOT_DESC_PCIE_HH_HL 0x03 /* Half Height, Half Length */
-
-/* PCI Slot Info: Mechanicals Values */
-#define PCI_SLOT_MECH_NONE 0x00
-#define PCI_SLOT_MECH_RIGHT 0x01
-#define PCI_SLOT_MECH_LEFT 0x02
-#define PCI_SLOT_MECH_RIGHT_LEFT 0x03
-
-/* PCI Slot Info: Power LED Control Values */
-#define PCI_SLOT_PWR_LED_CTL_NONE 0x00 /* No Control */
-#define PCI_SLOT_PWR_LED_CTL_FSP 0x01 /* FSP Controlled */
-#define PCI_SLOT_PWR_LED_CTL_KERNEL 0x02 /* Kernel Controlled */
-
-/* PCI Slot Info: ATTN LED Control Values */
-#define PCI_SLOT_ATTN_LED_CTL_NONE 0x00 /* No Control */
-#define PCI_SLOT_ATTN_LED_CTL_FSP 0x01 /* FSP Controlled */
-#define PCI_SLOT_ATTN_LED_CTL_KERNEL 0x02 /* Kernel Controlled */
-
/* PCI Slot Entry Information */
struct pci_slot_info {
char label[16];
@@ -151,6 +97,7 @@ struct pci_device {
struct list_head pcrf;
struct dt_node *dn;
+ struct pci_slot *slot;
struct pci_slot_info *slot_info;
struct pci_device *parent;
struct list_head children;
@@ -462,6 +409,7 @@ struct phb {
uint32_t mps;
/* PCI-X only slot info, for PCI-E this is in the RC bridge */
+ struct pci_slot *slot;
struct pci_slot_info *slot_info;
/* Base location code used to generate the children one */
@@ -519,7 +467,15 @@ static inline int64_t pci_cfg_write32(struct phb *phb, uint32_t bdfn,
}
/* Utilities */
-
+extern void pci_remove_bus(struct phb *phb, struct list_head *list);
+extern uint8_t pci_scan_bus(struct phb *phb, uint8_t bus, uint8_t max_bus,
+ struct list_head *list, struct pci_device *parent,
+ bool scan_downstream);
+extern void pci_add_device_nodes(struct phb *phb,
+ struct list_head *list,
+ struct dt_node *parent_node,
+ struct pci_lsi_state *lstate,
+ uint8_t swizzle);
extern int64_t pci_find_cap(struct phb *phb, uint16_t bdfn, uint8_t cap);
extern int64_t pci_find_ecap(struct phb *phb, uint16_t bdfn, uint16_t cap,
uint8_t *version);
diff --git a/include/platform.h b/include/platform.h
index f1bdc30..d07994f 100644
--- a/include/platform.h
+++ b/include/platform.h
@@ -20,6 +20,7 @@
/* Some fwd declarations for types used further down */
struct phb;
struct pci_device;
+struct pci_slot;
struct errorlog;
enum resource_id {
diff --git a/platforms/ibm-fsp/lxvpd.c b/platforms/ibm-fsp/lxvpd.c
index 90c9e09..542c49a 100644
--- a/platforms/ibm-fsp/lxvpd.c
+++ b/platforms/ibm-fsp/lxvpd.c
@@ -24,6 +24,7 @@
#include <vpd.h>
#include <pci.h>
#include <pci-cfg.h>
+#include <pci-slot.h>
#include "lxvpd.h"