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..32eae36
--- /dev/null
+++ b/core/pci-slot.c
@@ -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.
+ */
+
+#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, uint8_t *val)
+{
+	uint64_t now = mftb();
+	int64_t ret;
+
+	/* Return timeout value if we have */
+	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;
+	case PCI_SLOT_STATE_GPOWER:
+		ret = slot->ops.get_power_status(slot, val);
+		break;
+	case PCI_SLOT_STATE_GPRESENCE:
+		ret = slot->ops.get_presence_status(slot, val);
+		break;
+	case PCI_SLOT_STATE_SPOWER:
+		ret = slot->ops.set_power_status(slot, *val);
+		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_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 has
+	 * been existing or not.
+	 */
+	slot = pd ? pd->slot : phb->slot;
+	if (slot) {
+		prlog(PR_ERR, PCI_SLOT_PREFIX
+		      "Already existing\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 shouldn't be overrided by individual platform */
+	slot->phb = phb;
+	slot->pd = pd;
+	pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL);
+	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);
+	slot = phb ? phb->slot : NULL;
+	if (!(id & PCI_SLOT_ID_PREFIX))
+		return slot;
+
+	bdfn = PCI_SLOT_BDFN(id);
+	pd = phb ? pci_find_dev(phb, bdfn) : NULL;
+	slot = pd ? pd->slot : NULL;
+	return slot;
+}
+
+int pci_slot_hotplug_event(struct pci_slot *slot, bool add,
+			   void (*consumed)(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 ret;
+
+	/*
+	 * On unplugging event, the subordinate devices attached
+	 * to the PCI slot will be removed. In the mean time, a
+	 * signal sent to kernel indicating the completion of the
+	 * PCI unplugging.
+	 */
+	if (!add) {
+		pci_remove_bus(phb, &pd->children);
+		return opal_queue_msg(OPAL_MSG_PCI_HOTPLUG,
+				      slot, consumed,
+				      OPAL_PCI_SLOT_POWER_OFF,
+				      (u64)(dn->phandle));
+	}
+
+	/*
+	 * On PCI plugging event, we are not able to scan the subordinate
+	 * devices attached to the specified PCI slot if the PCIe link is
+	 * down. Otherwise, we will hit EEH error without exception.
+	 */
+	if (slot->ops.get_link_status &&
+	    (ret = slot->ops.get_link_status(slot, &link))) {
+		prlog(PR_ERR, PCI_SLOT_PREFIX
+		      "Error %lld getting link status\n", slot->id, ret);
+		link = 0;
+	}
+
+	if (link) {
+		pci_scan_bus(phb, pd->secondary_bus, pd->subordinate_bus,
+			     &pd->children, pd, true);
+		pci_add_device_nodes(phb, &pd->children, dn, &phb->lstate, 0);
+	}
+
+	return opal_queue_msg(OPAL_MSG_PCI_HOTPLUG,
+			      slot, consumed,
+			      OPAL_PCI_SLOT_POWER_ON,
+			      (u64)(dn->phandle));
+}
diff --git a/core/pci.c b/core/pci.c
index e05ed6c..5bdf8da 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>
@@ -443,11 +444,72 @@ 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)
+		return;
+
+	/* Bail if there're something connected */
+	if (!list_empty(&pd->children))
+		return;
+
+	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_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_remove_bus(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_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..
@@ -457,9 +519,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;
@@ -505,10 +567,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;
@@ -576,8 +643,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
@@ -594,6 +661,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;
@@ -785,7 +855,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);
@@ -1311,12 +1381,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
@@ -1431,14 +1501,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 */
@@ -1446,8 +1516,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)
@@ -1546,7 +1623,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..d3abca6
--- /dev/null
+++ b/core/pcie-slot.c
@@ -0,0 +1,481 @@
+/* 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_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;
+
+	/*
+	 * We assume the downstream ports are always connected
+	 * to the upstream port. Hard code the presence bit for
+	 * the case.
+	 */
+	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
+	 * the downtream ports are always connected to the
+	 * upstream port.
+	 */
+	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;
+
+	/*
+	 * We assume the power is always on if we don't have
+	 * power control functionality.
+	 */
+	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;
+
+	/* Attention is assumed as off if we don't have the capability */
+	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;
+
+	/* We assume latch is always off if MRL sensor not existing */
+	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;
+	default:
+		prlog(PR_ERR, PCIE_SLOT_PREFIX
+		      "Invalid attention state (0x%x)\n", slot->id, val);
+	}
+
+	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 in case power control isn't supported */
+	if (!(slot->slot_cap & PCICAP_EXP_SLOTCAP_PWCTRL))
+		return OPAL_SUCCESS;
+
+	ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false);
+	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");
+
+		/* Link is down for ever without devices attached */
+		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");
+			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 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:
+		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) {
+			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 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:
+		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);
+	}
+
+	/* 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 41af0e5..50de6d1 100644
--- a/include/opal-api.h
+++ b/include/opal-api.h
@@ -376,6 +376,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 */
@@ -422,6 +432,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..93ab6cb
--- /dev/null
+++ b/include/pci-slot.h
@@ -0,0 +1,251 @@
+/* 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 <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;
+	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)
+{
+	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 (*consumed)(void *data));
+#endif /* __PCI_SLOT_H */
diff --git a/include/pci.h b/include/pci.h
index 2b73598..632856b 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;
@@ -461,6 +408,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 */
@@ -518,7 +466,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..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"
 
