[Skiboot] [PATCH v4 02/10] core/pci: PCI slot management core

Gavin Shan gwshan at linux.vnet.ibm.com
Tue Feb 17 17:48:49 AEDT 2015


The patch implements PCI slot core for unified PCI slot management.
Each PCIE bridge port or PHB is expected to be bound with PCI slot
instance, which in turn manages various states (e.g. power, link,
reset) states and accomodate state change request from OS.

With "struct pci_slot" introduced to support PCI slot instances, and
PCI slot states introduced to manage its power/link/reset states when
handling the request from upper OS. The more important, the methods
to manage the states allow to be overrided by individual platforms
to make their own implementation there.

Signed-off-by: Gavin Shan <gwshan at linux.vnet.ibm.com>
---
 core/Makefile.inc         |   2 +-
 core/pci-slot.c           | 742 ++++++++++++++++++++++++++++++++++++++++++++++
 core/pci.c                |   1 +
 include/pci-slot.h        | 201 +++++++++++++
 include/pci.h             |  56 +---
 include/platform.h        |   3 +-
 platforms/ibm-fsp/lxvpd.c |   1 +
 7 files changed, 950 insertions(+), 56 deletions(-)
 create mode 100644 core/pci-slot.c
 create mode 100644 include/pci-slot.h

diff --git a/core/Makefile.inc b/core/Makefile.inc
index 8540695..8aba7b3 100644
--- a/core/Makefile.inc
+++ b/core/Makefile.inc
@@ -3,7 +3,7 @@
 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 += timebase.o opal-msg.o pci.o pci-slot.o pci-opal.o fast-reboot.o
 CORE_OBJS += device.o exceptions.o trace.o affinity.o vpd.o
 CORE_OBJS += hostservices.o platform.o nvram.o flash-nvram.o hmi.o
 CORE_OBJS += console-log.o ipmi.o time-utils.o pel.o pool.o errorlog.o
diff --git a/core/pci-slot.c b/core/pci-slot.c
new file mode 100644
index 0000000..3692c07
--- /dev/null
+++ b/core/pci-slot.c
@@ -0,0 +1,742 @@
+/* Copyright 2013-2014 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 <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 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 = 1;
+		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 = 1;
+		return OPAL_SUCCESS;
+	}
+
+	/* Check presence bit */
+	*val = 0;
+	pci_cfg_read16(phb, pd->bdfn,
+		       ecap + PCICAP_EXP_SLOTSTAT, &slot_sts);
+	if (slot_sts & PCICAP_EXP_SLOTSTAT_PDETECTST)
+		*val = 1;
+
+	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 void pcie_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;
+
+	/* Nothing to do without AER capability */
+	if (!pci_has_cap(pd, PCIECAP_ID_AER, true))
+		return;
+
+	/* Link down event */
+	aercap = pci_cap(pd, PCIECAP_ID_AER, true);
+	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);
+
+	/*
+	 * If it's link up event, we just had hot or fundamental
+	 * reset. The bus numbers for downstream bridges should
+	 * be restored for the case. And the downstream devices
+	 * should be reconfigured. However, we should restore the
+	 * bus numbers first of all.
+	 */
+	if (is_up) {
+		pci_restore_bridge_buses(phb, pd);
+		pci_device_init(phb, pd);
+	}
+}
+
+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:
+		PCI_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) {
+			PCI_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.
+		 */
+		PCI_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) {
+			PCI_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) {
+			PCI_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:
+		PCI_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:
+		PCI_SLOT_DBG(slot, "HRESET: Starts\n");
+		if (slot->ops.prepare_link_change) {
+			PCI_SLOT_DBG(slot, "HRESET: Prepare for link down\n");
+			slot->ops.prepare_link_change(slot, false);
+		}
+		/* fall through */
+	case PCI_SLOT_STATE_HRESET_START:
+		PCI_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:
+		PCI_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:
+		PCI_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:
+		PCI_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) {
+			PCI_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) {
+			PCI_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:
+		PCI_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:
+		PCI_SLOT_DBG(slot, "FRESET: Unexpected slot state %08x\n",
+			     slot->state);
+	}
+
+	pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL);
+	return OPAL_HARDWARE;
+}
+
+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;
+
+	/* 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;
+}
+
+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.prepare_link_change   = pcie_slot_prepare_link_change;
+	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/core/pci.c b/core/pci.c
index 093a585..2869249 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 <lock.h>
 #include <device.h>
diff --git a/include/pci-slot.h b/include/pci-slot.h
new file mode 100644
index 0000000..7387c9c
--- /dev/null
+++ b/include/pci-slot.h
@@ -0,0 +1,201 @@
+/* Copyright 2013-2014 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 {
+	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		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 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);
+#endif /* __PCI_SLOT_H */
diff --git a/include/pci.h b/include/pci.h
index 57ba547..94c43a2 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 {
 	uint8_t    switch_id;
@@ -126,6 +72,7 @@ struct pci_device {
 	uint32_t		cap[64];
 	uint32_t		mps;		/* Max payload size capability */
 
+	struct pci_slot		*slot;
 	struct pci_slot_info    *slot_info;
 	struct pci_device	*parent;
 	struct list_head	children;
@@ -436,6 +383,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 */
diff --git a/include/platform.h b/include/platform.h
index 690772e..39625b6 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 {
@@ -71,7 +72,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 43a1c5e..7ab4776 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"
 
-- 
1.8.3.2



More information about the Skiboot mailing list