[Skiboot] [PATCH v12 13/23] core/pci: Support PCI slot

Gavin Shan gwshan at linux.vnet.ibm.com
Fri Jun 10 15:03:42 AEST 2016


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           | 206 +++++++++++++++++++++
 core/pci.c                |  77 +++++---
 core/pcie-slot.c          | 452 ++++++++++++++++++++++++++++++++++++++++++++++
 include/opal-api.h        |  10 +
 include/pci-slot.h        | 255 ++++++++++++++++++++++++++
 include/pci.h             |  66 ++-----
 include/platform.h        |   1 +
 platforms/ibm-fsp/lxvpd.c |   1 +
 9 files changed, 992 insertions(+), 80 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 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 b36d5a6..d5e0b60 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>
@@ -452,11 +453,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..
@@ -466,9 +489,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;
@@ -585,8 +608,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
@@ -794,7 +817,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);
@@ -1320,12 +1343,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
@@ -1440,14 +1463,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 */
@@ -1455,8 +1478,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)
@@ -1537,7 +1567,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 41af0e5..2b8bf57 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"
 
-- 
2.1.0



More information about the Skiboot mailing list