[Skiboot] [PATCH v9 13/22] core/pci: Support PCI slot
Gavin Shan
gwshan at linux.vnet.ibm.com
Thu Nov 12 13:33:20 AEDT 2015
Every PCIE bridge port or PHB is expected to be bound with PCI slot
, to which various PCI slot's functionalities are attached (e.g. power,
link, reset). This supports PCI slot:
* PCI slot is reprsented by "struct pci_slot".
* "struct pci_slot_ops" represents the functions supported on the
PCI slot. It's initialized by PCI slot core at the beginning and
allowed to be overrided by platform partially or completely.
* On PCI hot plugging event, the PCI devices behind the slot are
enumarated. Device sub-tree is populated and sent to OS by OPAL
message.
* On PCI hot unplugging event, the PCI devices behind the slot are
destroyed. Device sub-tree is removed and the slot is powered off.
Signed-off-by: Gavin Shan <gwshan at linux.vnet.ibm.com>
---
core/Makefile.inc | 4 +-
core/pci-slot.c | 305 +++++++++++++++++++++++++++
core/pci.c | 136 ++++++++++---
core/pcie-slot.c | 510 ++++++++++++++++++++++++++++++++++++++++++++++
include/opal-api.h | 11 +
include/pci-slot.h | 246 ++++++++++++++++++++++
include/pci.h | 65 +-----
include/platform.h | 3 +-
platforms/ibm-fsp/lxvpd.c | 1 +
9 files changed, 1198 insertions(+), 83 deletions(-)
create mode 100644 core/pci-slot.c
create mode 100644 core/pcie-slot.c
create mode 100644 include/pci-slot.h
diff --git a/core/Makefile.inc b/core/Makefile.inc
index 83370c1..604e02c 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
diff --git a/core/pci-slot.c b/core/pci-slot.c
new file mode 100644
index 0000000..56c6704
--- /dev/null
+++ b/core/pci-slot.c
@@ -0,0 +1,305 @@
+/* Copyright 2013-2015 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_DBG(s, fmt, a...) \
+ prlog(PR_DEBUG, "PCI-SLOT-%016llx " fmt, \
+ (s)->id, ##a)
+
+static void pci_slot_prepare_link_change(struct pci_slot *slot,
+ bool is_up)
+{
+ struct phb *phb = slot->phb;
+ struct pci_device *pd = slot->pd;
+ uint32_t aercap, mask;
+
+ /* Get AER capability position */
+ if (pci_has_cap(pd, PCIECAP_ID_AER, true))
+ aercap = pci_cap(pd, PCIECAP_ID_AER, true);
+ else
+ goto restore;
+
+ /* Link down event */
+ pci_cfg_read32(phb, pd->bdfn,
+ aercap + PCIECAP_AER_UE_MASK, &mask);
+ if (is_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 (is_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);
+
+restore:
+ if (is_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, uint8_t *val)
+{
+ int64_t (*func)(struct pci_slot *) = NULL;
+ int64_t (*gfunc)(struct pci_slot *, uint8_t *) = NULL;
+ int64_t (*sfunc)(struct pci_slot *, uint8_t) = NULL;
+ uint64_t now = mftb();
+
+ /* Retrieve the return value */
+ if (slot->state == PCI_SLOT_STATE_NORMAL) {
+ if (val)
+ *val = slot->get_val;
+ return OPAL_SUCCESS;
+ }
+
+ /* Timer isn't expired yet ? */
+ if (slot->delay_tgt_tb &&
+ tb_compare(now, slot->delay_tgt_tb) == TB_ABEFOREB)
+ return slot->delay_tgt_tb - now;
+
+ /* Run out of interval */
+ slot->delay_tgt_tb = 0;
+
+ switch (slot->state & PCI_SLOT_STATE_MASK) {
+ case PCI_SLOT_STATE_LINK:
+ func = slot->ops.poll_link;
+ break;
+ case PCI_SLOT_STATE_HRESET:
+ func = slot->ops.hreset;
+ break;
+ case PCI_SLOT_STATE_FRESET:
+ func = slot->ops.freset;
+ break;
+ case PCI_SLOT_STATE_PFRESET:
+ func = slot->ops.pfreset;
+ break;
+ case PCI_SLOT_STATE_CRESET:
+ func = slot->ops.creset;
+ break;
+ case PCI_SLOT_STATE_GPOWER:
+ gfunc = slot->ops.get_power_status;
+ break;
+ case PCI_SLOT_STATE_GPRESENCE:
+ gfunc = slot->ops.get_presence_status;
+ break;
+ case PCI_SLOT_STATE_SPOWER:
+ sfunc = slot->ops.set_power_status;
+ break;
+ }
+
+ if (func)
+ return func(slot);
+ else if (gfunc)
+ return gfunc(slot, &slot->get_val);
+ else if (sfunc)
+ return sfunc(slot, slot->set_val);
+
+ /* We can't handle the case */
+ prlog(PR_NOTICE, "Slot %016llx not support state %08x\n",
+ slot->id, slot->state);
+ pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL);
+ return OPAL_HARDWARE;
+}
+
+void pci_slot_add_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;
+
+ /* PHB should be always valid */
+ if (!phb)
+ return NULL;
+
+ /*
+ * In case we already had one. If we allocate PHB
+ * slot, the passed 'pd' should be NULL. Otherwise,
+ * both 'phb' and 'pd' are all valid
+ */
+ if (!pd)
+ slot = phb->slot;
+ else
+ slot = pd->slot;
+ if (slot) {
+ PCI_SLOT_DBG(slot, "Already existing\n");
+ return slot;
+ }
+
+ /* Allocate memory chunk */
+ slot = zalloc(sizeof(struct pci_slot));
+ if (!slot) {
+ prerror("Cannot allocate PCI slot\n");
+ return NULL;
+ }
+
+ /*
+ * Build the slot index, which might be combination of
+ * PHB index and device's indicator
+ */
+ if (pd) {
+ slot->id = pd->bdfn;
+ slot->id = ((0x1ul << 63) | (slot->id << 16));
+ }
+ slot->id |= phb->opal_id;
+
+ /*
+ * Initialize the slot. The poll function is aleays
+ * unified for all cases.
+ */
+ slot->phb = phb;
+ slot->pd = pd;
+ pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL);
+
+ /*
+ * PCI slot state poller, which isn't expected to
+ * be overrided by individual platforms.
+ */
+ slot->ops.poll = pci_slot_sm_poll;
+ slot->ops.prepare_link_change = pci_slot_prepare_link_change;
+
+ /* The slot is belonged to PCI device or PHB */
+ if (pd)
+ pd->slot = slot;
+ else
+ phb->slot = slot;
+
+ return slot;
+}
+
+/*
+ * Find PCI slot. The ID might be combination of PHB and slot indexes, or
+ * single PHB index. We're using highest nibble as the indicator for that.
+ */
+struct pci_slot *pci_slot_find(uint64_t id)
+{
+ struct phb *phb;
+ struct pci_device *pd;
+ struct pci_slot *slot = NULL;
+ uint64_t phb_index;
+ uint16_t bdfn;
+
+ if (id & 0xf000000000000000) {
+ phb_index = (id & 0xfffful);
+ bdfn = ((id >> 16) & 0xfffful);
+ } else {
+ phb_index = id;
+ }
+
+ /* Search for PHB */
+ phb = pci_get_phb(phb_index);
+ if (phb)
+ slot = phb->slot;
+
+ /* Search for slot in case it's compound case */
+ if (id & 0xf000000000000000) {
+ pd = pci_find_dev(phb, bdfn);
+ if (pd)
+ slot = pd->slot;
+ }
+
+ return slot;
+}
+
+int pci_slot_hotplug_event(struct pci_slot *slot, bool add,
+ void (*consume)(void *data))
+{
+ struct phb *phb = slot->phb;
+ struct pci_device *pd = slot->pd;
+ struct dt_node *dn = pd->dn;
+ uint8_t link = 0;
+ int64_t rc;
+
+ /* For unplugging event, the phandle of the slot associated
+ * PCI bridge's device node will be transported to OS.
+ */
+ if (!add) {
+ pci_bus_remove(phb, &pd->children);
+ opal_queue_msg(OPAL_MSG_PCI_HOTPLUG, slot, consume,
+ OPAL_PCI_SLOT_POWER_OFF, (u64)(dn->phandle));
+ return 0;
+ }
+
+ /* Check the link before rescanning */
+ if (slot->ops.get_link_status &&
+ (rc = slot->ops.get_link_status(slot, &link))) {
+ PCI_SLOT_DBG(slot, "Error %lld getting link status\n", rc);
+ link = 0;
+ }
+
+ /* Don't rescan in case the link is down. Otherwise, we
+ * will run into EEH error without exception.
+ */
+ if (!link) {
+ PCI_SLOT_DBG(slot, "Link is down and skip rescanning\n");
+ opal_queue_msg(OPAL_MSG_PCI_HOTPLUG, slot, consume,
+ OPAL_PCI_SLOT_POWER_ON, (u64)(dn->phandle));
+ return 0;
+ }
+
+ /* Rescanning the slot for hotplugging event */
+ pci_bus_scan(phb, pd->secondary_bus, pd->subordinate_bus,
+ &pd->children, pd, true);
+ pci_add_device_nodes(phb, &pd->children, dn, &phb->lstate, 0);
+
+ /* Send the message */
+ opal_queue_msg(OPAL_MSG_PCI_HOTPLUG, slot, consume,
+ OPAL_PCI_SLOT_POWER_ON, (u64)(dn->phandle));
+ return 0;
+}
diff --git a/core/pci.c b/core/pci.c
index 22a4675..5990836 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>
@@ -444,11 +445,79 @@ static void pci_cleanup_bridge(struct phb *phb, struct pci_device *pd)
pci_cfg_write16(phb, pd->bdfn, PCI_CFG_CMD, cmd);
}
+/*
+ * Turn off the power suply to the slot if there're nothing connected
+ * to it for 2 purposes: saving power obviously, and initializing the
+ * slot to initial power-off state for hotplug
+ */
+static void pci_try_power_off_slot(struct phb *phb, struct pci_device *pd)
+{
+ uint32_t flags = (PCI_SLOT_FLAG_BOOTUP |
+ PCI_SLOT_FLAG_NO_HOTPLUG_MSG);
+ int64_t rc;
+
+ /* Check if it's pluggable slot */
+ if (!pd ||
+ !pd->slot ||
+ !pd->slot->pluggable ||
+ !pd->slot->ops.set_power_status ||
+ !pd->slot->ops.poll)
+ return;
+
+ /* Check if there're something connected */
+ if (!list_empty(&pd->children))
+ return;
+ /*
+ * Power the slot off. We don't need the notification
+ * message about the device-tree node changes because
+ * we assume there're no devices behind the slot and
+ * their device-tree nodes aren't populated yet. So
+ * we needn't care the device-tree node changes at all.
+ */
+ pci_slot_add_flags(pd->slot, flags);
+ rc = pd->slot->ops.set_power_status(pd->slot, 0);
+ while (rc > 0) {
+ time_wait(rc);
+ rc = pd->slot->ops.poll(pd->slot, NULL);
+ }
-/* 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.
+ pci_slot_remove_flags(pd->slot, flags);
+ if (rc != OPAL_SUCCESS)
+ PCINOTICE(phb, pd->bdfn, "Error %lld powering off slot\n", rc);
+ else
+ PCIDBG(phb, pd->bdfn, "Power off hotpluggable slot\n");
+}
+
+/* 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_bus_remove(struct phb *phb, struct list_head *list)
+{
+ struct pci_device *pd, *tmp;
+
+ if (list_empty(list))
+ return;
+
+ list_for_each_safe(list, pd, tmp, link) {
+ pci_bus_remove(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);
+ }
+}
+
+/* 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..
@@ -458,9 +527,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_bus_scan(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;
@@ -506,10 +575,15 @@ static uint8_t pci_scan(struct phb *phb, uint8_t bus, uint8_t max_bus,
* We only scan downstream if instructed to do so by the
* caller. Typically we avoid the scan when we know the
* link is down already, which happens for the top level
- * root complex, and avoids a long secondary timeout
+ * root complex, and avoids a long secondary timeout. The
+ * power will be turned off if it's a empty hotpluggable
+ * slot.
*/
- if (!scan_downstream)
+ if (!scan_downstream) {
+ list_for_each(list, pd, link)
+ pci_try_power_off_slot(phb, pd);
return bus;
+ }
next_bus = bus + 1;
max_sub = bus;
@@ -577,8 +651,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_bus_scan(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
@@ -595,6 +669,9 @@ static uint8_t pci_scan(struct phb *phb, uint8_t bus, uint8_t max_bus,
pd->subordinate_bus = max_sub;
pci_cfg_write8(phb, pd->bdfn, PCI_CFG_SUBORDINATE_BUS, max_sub);
next_bus = max_sub + 1;
+
+ /* Turn off its power if it's empty hotpluggable slot */
+ pci_try_power_off_slot(phb, pd);
}
return max_sub;
@@ -786,7 +863,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_bus_scan(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);
@@ -1309,12 +1386,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
@@ -1426,23 +1503,29 @@ static void pci_add_one_node(struct phb *phb, struct pci_device *pd,
* XXX The kernel should probably fix that up
*/
dt_add_property(np, "ranges", NULL, 0);
-
- 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 */
if (phb->slot_info)
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)
@@ -1541,7 +1624,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);
}
}
diff --git a/core/pcie-slot.c b/core/pcie-slot.c
new file mode 100644
index 0000000..6269ea4
--- /dev/null
+++ b/core/pcie-slot.c
@@ -0,0 +1,510 @@
+/* Copyright 2013-2015 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_DBG(s, fmt, a...) \
+ prlog(PR_DEBUG, "PCIE-SLOT-%016llx " fmt, \
+ (s)->id, ##a)
+
+static int64_t pcie_slot_get_presence_status(struct pci_slot *slot,
+ uint8_t *val)
+{
+ struct phb *phb = slot->phb;
+ struct pci_device *pd = slot->pd;
+ uint32_t ecap;
+ int16_t slot_cap, slot_sts;
+
+ /*
+ * Upstream ports always have downstream ports connected.
+ * The presence bit of slot state is usually zero. Lets
+ * have hard-code for it.
+ */
+ if (pd->dev_type == PCIE_TYPE_SWITCH_UPPORT) {
+ *val = OPAL_PCI_SLOT_PRESENT;
+ return OPAL_SUCCESS;
+ }
+
+ /*
+ * If downstream port doesn't support slot capability,
+ * we need hardcode it as "presence" according PCIE
+ * spec.
+ *
+ * Note that the power should be supplied to the slot
+ * before detecting the presence bit.
+ */
+ ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false);
+ pci_cfg_read16(phb, pd->bdfn,
+ ecap + PCICAP_EXP_CAPABILITY_REG, &slot_cap);
+ if (pd->dev_type == PCIE_TYPE_SWITCH_DNPORT &&
+ !(slot_cap & PCICAP_EXP_CAP_SLOT)) {
+ *val = OPAL_PCI_SLOT_PRESENT;
+ return OPAL_SUCCESS;
+ }
+
+ /* Check presence bit */
+ pci_cfg_read16(phb, pd->bdfn,
+ ecap + PCICAP_EXP_SLOTSTAT, &slot_sts);
+ if (slot_sts & 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_status(struct pci_slot *slot,
+ uint8_t *val)
+{
+ struct phb *phb = slot->phb;
+ struct pci_device *pd = slot->pd;
+ uint32_t ecap;
+ int16_t lstat;
+
+ /*
+ * Upstream port doesn't have valid link indicator in
+ * data-link status. Lets hardcode it as link-up as
+ * there're always downtream ports.
+ */
+ if (pd->dev_type == PCIE_TYPE_SWITCH_UPPORT) {
+ *val = 1;
+ return OPAL_SUCCESS;
+ }
+
+ ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false);
+ pci_cfg_read16(phb, pd->bdfn,
+ ecap + PCICAP_EXP_LSTAT, &lstat);
+ if (!(lstat & PCICAP_EXP_LSTAT_DLLL_ACT)) {
+ *val = 0;
+ return OPAL_SUCCESS;
+ }
+
+ *val = GETFIELD(PCICAP_EXP_LSTAT_WIDTH, lstat);
+ return OPAL_SUCCESS;
+}
+
+static int64_t pcie_slot_get_power_status(struct pci_slot *slot,
+ uint8_t *val)
+{
+ struct phb *phb = slot->phb;
+ struct pci_device *pd = slot->pd;
+ uint32_t ecap;
+ uint16_t slot_ctl;
+
+ /*
+ * If we don't have power control functionality, the
+ * power is always on.
+ */
+ ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false);
+ if (!(slot->slot_cap & PCICAP_EXP_SLOTCAP_PWCTRL)) {
+ *val = PCI_SLOT_POWER_ON;
+ return OPAL_SUCCESS;
+ }
+
+ /* Check power supply bit */
+ *val = PCI_SLOT_POWER_OFF;
+ pci_cfg_read16(phb, pd->bdfn,
+ ecap + PCICAP_EXP_SLOTCTL, &slot_ctl);
+ if (!(slot_ctl & PCICAP_EXP_SLOTCTL_PWRCTLR))
+ *val = PCI_SLOT_POWER_ON;
+
+ return OPAL_SUCCESS;
+}
+
+static int64_t pcie_slot_get_attention_status(struct pci_slot *slot,
+ uint8_t *val)
+{
+ struct phb *phb = slot->phb;
+ struct pci_device *pd = slot->pd;
+ uint32_t ecap;
+ uint16_t slot_ctl;
+
+ /*
+ * If the slot doesn't support attention capability,
+ * it's always off.
+ */
+ ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false);
+ if (!(slot->slot_cap & PCICAP_EXP_SLOTCAP_ATTNI)) {
+ *val = 0;
+ return OPAL_SUCCESS;
+ }
+
+ /* Check the attention bits */
+ pci_cfg_read16(phb, pd->bdfn,
+ ecap + PCICAP_EXP_SLOTCTL, &slot_ctl);
+ switch (GETFIELD(PCICAP_EXP_SLOTCTL_ATTNI, slot_ctl)) {
+ case PCIE_INDIC_ON:
+ case PCIE_INDIC_BLINK:
+ *val = GETFIELD(PCICAP_EXP_SLOTCTL_ATTNI, slot_ctl);
+ break;
+ case PCIE_INDIC_OFF:
+ default:
+ *val = 0;
+ }
+
+ return OPAL_SUCCESS;
+}
+
+static int64_t pcie_slot_get_latch_status(struct pci_slot *slot,
+ uint8_t *val)
+{
+ struct phb *phb = slot->phb;
+ struct pci_device *pd = slot->pd;
+ uint32_t ecap;
+ uint16_t slot_sts;
+
+ /*
+ * If MRL sensor isn't supported, its state
+ * is always off.
+ */
+ ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false);
+ if (!(slot->slot_cap & PCICAP_EXP_SLOTCAP_MRLSENS)) {
+ *val = 0;
+ return OPAL_SUCCESS;
+ }
+
+ /* Check state of MRL sensor */
+ *val = 0;
+ pci_cfg_read16(phb, pd->bdfn,
+ ecap + PCICAP_EXP_SLOTSTAT, &slot_sts);
+ if (slot_sts & PCICAP_EXP_SLOTSTAT_MRLSENSST)
+ *val = 1;
+
+ return OPAL_SUCCESS;
+}
+
+static int64_t pcie_slot_set_attention_status(struct pci_slot *slot,
+ uint8_t val)
+{
+ struct phb *phb = slot->phb;
+ struct pci_device *pd = slot->pd;
+ uint32_t ecap;
+ uint16_t slot_ctl;
+
+ /*
+ * Drop the request if the slot doesn't support
+ * attention capability
+ */
+ ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false);
+ if (!(slot->slot_cap & PCICAP_EXP_SLOTCAP_ATTNI))
+ return OPAL_SUCCESS;
+
+ /* Write the attention bits */
+ pci_cfg_read16(phb, pd->bdfn,
+ ecap + PCICAP_EXP_SLOTCTL, &slot_ctl);
+ switch (val) {
+ case PCI_SLOT_ATTN_LED_OFF:
+ slot_ctl = SETFIELD(PCICAP_EXP_SLOTCTL_ATTNI,
+ slot_ctl, PCIE_INDIC_OFF);
+ break;
+ case PCI_SLOT_ATTN_LED_ON:
+ slot_ctl = SETFIELD(PCICAP_EXP_SLOTCTL_ATTNI,
+ slot_ctl, PCIE_INDIC_ON);
+ break;
+ case PCI_SLOT_ATTN_LED_BLINK:
+ slot_ctl = SETFIELD(PCICAP_EXP_SLOTCTL_ATTNI,
+ slot_ctl, PCIE_INDIC_BLINK);
+ break;
+ }
+
+ pci_cfg_write16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCTL,
+ slot_ctl);
+ return OPAL_SUCCESS;
+}
+
+static int64_t pcie_slot_set_power_status(struct pci_slot *slot,
+ uint8_t val)
+{
+ struct phb *phb = slot->phb;
+ struct pci_device *pd = slot->pd;
+ uint32_t ecap;
+ uint16_t slot_ctl;
+
+ /*
+ * Drop the request if the slot doesn't support
+ * power control functionality.
+ */
+ ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false);
+ if (!(slot->slot_cap & PCICAP_EXP_SLOTCAP_PWCTRL))
+ return OPAL_SUCCESS;
+
+ /* Set power supply bits */
+ pci_cfg_read16(phb, pd->bdfn,
+ ecap + PCICAP_EXP_SLOTCTL, &slot_ctl);
+ switch (val) {
+ case PCI_SLOT_POWER_OFF:
+ slot_ctl |= PCICAP_EXP_SLOTCTL_PWRCTLR;
+ slot_ctl = SETFIELD(PCICAP_EXP_SLOTCTL_PWRI, slot_ctl,
+ PCIE_INDIC_OFF);
+ break;
+ case PCI_SLOT_POWER_ON:
+ slot_ctl &= ~PCICAP_EXP_SLOTCTL_PWRCTLR;
+ slot_ctl = SETFIELD(PCICAP_EXP_SLOTCTL_PWRI, slot_ctl,
+ PCIE_INDIC_ON);
+ break;
+ }
+
+ pci_cfg_write16(phb, pd->bdfn,
+ ecap + PCICAP_EXP_SLOTCTL, slot_ctl);
+ return OPAL_SUCCESS;
+}
+
+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;
+ 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");
+
+ /*
+ * The link won't come up for ever if the slot
+ * doesn't have an connected adapter
+ */
+ if (slot->ops.get_presence_status)
+ slot->ops.get_presence_status(slot, &presence);
+ if (!presence) {
+ PCIE_SLOT_DBG(slot, "LINK: No adapter, 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;
+ }
+
+ /* Enable the link without check */
+ ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false);
+ 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 slot doesn't support link change report
+ * capability, we assume the link state is finalized
+ * after 1 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));
+ }
+
+ /*
+ * If the slot supports reporting link state change,
+ * we need poll it in determined interval and it's
+ * timeout would be 5s.
+ */
+ 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:
+ /*
+ * We assume the link state is finalized after
+ * determined time delay.
+ */
+ 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:
+ ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false);
+ 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) {
+ PCIE_SLOT_DBG(slot, "LINK: Timeout waiting for up (%04x)\n",
+ val);
+ 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;
+ }
+
+ return pci_slot_set_sm_timeout(slot, msecs_to_tb(20));
+ default:
+ PCIE_SLOT_DBG(slot, "LINK: Unexpected slot state %08x\n",
+ slot->state);
+ }
+
+ pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL);
+ return OPAL_HARDWARE;
+}
+
+/* Reset the secondary bus under the specified slot */
+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)
+{
+ /*
+ * After power-on in fundamental reset, we will
+ * switch to hot reset. The case is coverred by
+ */
+ 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 override the power
+ * management methods for fundamental reset because the
+ * hot reset 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_status) {
+ PCIE_SLOT_DBG(slot, "FRESET: Retrieve power state\n");
+ slot->ops.get_power_status(slot, &power_state);
+ }
+
+ /* In power on state, power it off */
+ if (power_state == PCI_SLOT_POWER_ON &&
+ slot->ops.set_power_status) {
+ PCIE_SLOT_DBG(slot, "FRESET: Power is on, turn off\n");
+ slot->ops.set_power_status(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_status)
+ slot->ops.set_power_status(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:
+ PCIE_SLOT_DBG(slot, "FRESET: Unexpected slot state %08x\n",
+ 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);
+ }
+
+ /* Slot info */
+ 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 = GETFIELD(PCICAP_EXP_LCAP_MAXWDTH, slot->link_cap);
+
+ /* Standard slot operations */
+ slot->ops.get_presence_status = pcie_slot_get_presence_status;
+ slot->ops.get_link_status = pcie_slot_get_link_status;
+ slot->ops.get_power_status = pcie_slot_get_power_status;
+ slot->ops.get_attention_status = pcie_slot_get_attention_status;
+ slot->ops.get_latch_status = pcie_slot_get_latch_status;
+ slot->ops.set_power_status = pcie_slot_set_power_status;
+ slot->ops.set_attention_status = pcie_slot_set_attention_status;
+
+ /*
+ * 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 fff9148..3f94467 100644
--- a/include/opal-api.h
+++ b/include/opal-api.h
@@ -375,6 +375,16 @@ enum OpalPciMaskAction {
OPAL_MASK_ERROR_TYPE = 1
};
+enum OpalPciSlotPresentenceState {
+ OPAL_PCI_SLOT_EMPTY = 0,
+ OPAL_PCI_SLOT_PRESENT = 1
+};
+
+enum OpalPciSlotPowerState {
+ 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 */
@@ -421,6 +431,7 @@ enum opal_msg_type {
OPAL_MSG_DPO = 5,
OPAL_MSG_PRD = 6,
OPAL_MSG_OCC = 7,
+ OPAL_MSG_PCI_HOTPLUG = 8,
OPAL_MSG_TYPE_MAX,
};
diff --git a/include/pci-slot.h b/include/pci-slot.h
new file mode 100644
index 0000000..59f3d86
--- /dev/null
+++ b/include/pci-slot.h
@@ -0,0 +1,246 @@
+/* Copyright 2013-2015 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 <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_status)(struct pci_slot *slot, uint8_t *val);
+ int64_t (*get_link_status)(struct pci_slot *slot, uint8_t *val);
+ int64_t (*get_power_status)(struct pci_slot *slot, uint8_t *val);
+ int64_t (*get_attention_status)(struct pci_slot *slot, uint8_t *val);
+ int64_t (*get_latch_status)(struct pci_slot *slot, uint8_t *val);
+ int64_t (*set_power_status)(struct pci_slot *slot, uint8_t val);
+ int64_t (*set_attention_status)(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, uint8_t *val);
+
+ /* 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_GPRESENCE 0x00000800
+#define PCI_SLOT_STATE_GPRESENCE_START 0x00000801
+
+struct pci_slot {
+ uint32_t flags;
+#define PCI_SLOT_FLAG_BOOTUP 0x1
+#define PCI_SLOT_FLAG_NO_HOTPLUG_MSG 0x2
+
+ struct phb *phb;
+ struct pci_device *pd;
+
+ /* Identifier */
+ uint64_t id;
+
+ /* 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;
+
+ /* State machine */
+ uint32_t state;
+ uint32_t retry_state;
+ uint32_t link_cap;
+ uint32_t slot_cap;
+ uint64_t delay_tgt_tb;
+ uint64_t retries;
+ uint8_t get_val;
+ uint8_t set_val;
+ struct pci_slot_ops ops;
+ void *data;
+};
+
+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)
+{
+ uint64_t target, now = mftb();
+
+ target = now + dur;
+ if (target == 0)
+ target++;
+ slot->delay_tgt_tb = target;
+
+ 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_properties(struct pci_slot *slot,
+ struct dt_node *np);
+extern struct pci_slot *pci_slot_find(uint64_t id);
+extern int pci_slot_hotplug_event(struct pci_slot *slot, bool add,
+ void (*consume)(void *data));
+#endif /* __PCI_SLOT_H */
diff --git a/include/pci.h b/include/pci.h
index 0f56ed0..f938cf5 100644
--- a/include/pci.h
+++ b/include/pci.h
@@ -21,60 +21,6 @@
#include <device.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];
@@ -150,6 +96,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;
@@ -466,6 +413,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 */
@@ -513,6 +461,15 @@ static inline int64_t pci_cfg_write32(struct phb *phb, uint32_t bdfn,
}
/* Utilities */
+extern void pci_bus_remove(struct phb *phb, struct list_head *list);
+extern uint8_t pci_bus_scan(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..e77bbe1 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 {
@@ -72,7 +73,7 @@ struct platform {
/*
* This is called once per PHB before probing. It allows the
- * platform to setup some PHB private data that can be used
+ * platform to setup some PHB private data that can be used
* later on by calls such as pci_get_slot_info() below. The
* "index" argument is the PHB index within the IO HUB (or
* P8 chip).
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"
--
2.1.0
More information about the Skiboot
mailing list