[Skiboot] [PATCH v7 14/17] platforms/ibm-fsp: Support PCI slot
Gavin Shan
gwshan at linux.vnet.ibm.com
Thu Jun 4 16:19:06 AEST 2015
The patch reworks PCI stuff for IBM's Apollo and Firenze platforms to
support PCI slot:
* Platform shared PCI slot is represented by "struct lxvpd_pci_slot"
for Apollo and Firenze. The information of that is fetched from
VPD.
* Apollo platform uses "struct lxvpd_pci_slot" as its platform slot,
while Firenze platform uses "struct firenze_pci_slot" as its
platform slot in order to support external I2C-based PCI slot power
maangement as well as PERST supported by the downstream ports of
particular PLX PCIe switches.
* On Firenze platform, the properties and methods to manage PHB slot
might be overrided to utilize the capability of external power
management.
Signed-off-by: Gavin Shan <gwshan at linux.vnet.ibm.com>
---
platforms/ibm-fsp/Makefile.inc | 3 +-
platforms/ibm-fsp/apollo-pci.c | 94 ++++
platforms/ibm-fsp/apollo.c | 23 +-
platforms/ibm-fsp/firenze-pci.c | 1055 +++++++++++++++++++++++++++++++++++++++
platforms/ibm-fsp/firenze.c | 206 +-------
platforms/ibm-fsp/ibm-fsp.h | 13 +
platforms/ibm-fsp/lxvpd.c | 382 ++++++++------
platforms/ibm-fsp/lxvpd.h | 35 +-
8 files changed, 1430 insertions(+), 381 deletions(-)
create mode 100644 platforms/ibm-fsp/apollo-pci.c
create mode 100644 platforms/ibm-fsp/firenze-pci.c
diff --git a/platforms/ibm-fsp/Makefile.inc b/platforms/ibm-fsp/Makefile.inc
index a885cbb..c5bec84 100644
--- a/platforms/ibm-fsp/Makefile.inc
+++ b/platforms/ibm-fsp/Makefile.inc
@@ -1,6 +1,7 @@
SUBDIRS += $(PLATDIR)/ibm-fsp
-IBM_FSP_OBJS = common.o lxvpd.o apollo.o firenze.o
+IBM_FSP_OBJS = common.o lxvpd.o apollo.o apollo-pci.o \
+ firenze.o firenze-pci.o
IBM_FSP = $(PLATDIR)/ibm-fsp/built-in.o
$(IBM_FSP): $(IBM_FSP_OBJS:%=$(PLATDIR)/ibm-fsp/%)
diff --git a/platforms/ibm-fsp/apollo-pci.c b/platforms/ibm-fsp/apollo-pci.c
new file mode 100644
index 0000000..f13b69d
--- /dev/null
+++ b/platforms/ibm-fsp/apollo-pci.c
@@ -0,0 +1,94 @@
+/* Copyright 2013-2015 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <skiboot.h>
+#include <device.h>
+#include <fsp.h>
+#include <p7ioc.h>
+#include <pci-cfg.h>
+#include <pci.h>
+#include <pci-slot.h>
+
+#include "ibm-fsp.h"
+#include "lxvpd.h"
+
+/* Debugging option */
+#define APOLLO_PCI_DBG(fmt, a...) \
+ prlog(PR_DEBUG, "APOLLO-PCI: " fmt, ##a)
+#define APOLLO_PCI_INFO(fmt, a...) \
+ prlog(PR_INFO, "APOLLO-PCI: " fmt, ##a)
+#define APOLLO_PCI_ERR(fmt, a...) \
+ prlog(PR_ERR, "APOLLO-PCI: " fmt, ##a)
+
+void apollo_pci_setup_phb(struct phb *phb, unsigned int index)
+{
+ struct dt_node *ioc_node;
+ struct lxvpd_pci_slot *s = NULL;
+ struct pci_slot *slot = NULL;
+
+ /* Grab the device-tree node of the IOC */
+ ioc_node = phb->dt_node->parent;
+ if (!ioc_node) {
+ APOLLO_PCI_DBG("No IOC devnode for PHB%04x\n",
+ phb->opal_id);
+ return;
+ }
+
+ /*
+ * Process the pcie slot entries from the lx vpd lid
+ *
+ * FIXME: We currently assume chip 1 always, this will have to be
+ * fixed once we understand the right way to get the BRxy/BRxy "x"
+ * "x" value. It's not working well. I found 2 different root ports
+ * on Firebird-L has been assigned to same slot label.
+ */
+ lxvpd_process_slot_entries(phb, ioc_node, 1,
+ index, sizeof(struct lxvpd_pci_slot));
+
+ /* Fixup P7IOC PHB slot */
+ slot = phb->slot;
+ s = slot ? lxvpd_get_slot(slot) : NULL;
+ if (s)
+ lxvpd_extract_info(slot, s);
+}
+
+void apollo_pci_get_slot_info(struct phb *phb, struct pci_device *pd)
+{
+ struct pci_slot *slot;
+ struct lxvpd_pci_slot *s = NULL;
+
+ if (pd->dev_type != PCIE_TYPE_ROOT_PORT &&
+ pd->dev_type != PCIE_TYPE_SWITCH_UPPORT &&
+ pd->dev_type != PCIE_TYPE_SWITCH_DNPORT &&
+ pd->dev_type != PCIE_TYPE_PCIE_TO_PCIX)
+ return;
+
+ /* Create PCIe slot */
+ slot = pcie_slot_create(phb, pd);
+ if (!slot)
+ return;
+
+ /* Root complex inherits methods from PHB slot */
+ if (!pd->parent && phb->slot)
+ memcpy(&slot->ops, &phb->slot->ops, sizeof(struct pci_slot_ops));
+
+ /* Patch PCIe slot */
+ s = lxvpd_get_slot(slot);
+ if (s) {
+ lxvpd_extract_info(slot, s);
+ slot->ops.add_properties = lxvpd_add_slot_properties;
+ }
+}
diff --git a/platforms/ibm-fsp/apollo.c b/platforms/ibm-fsp/apollo.c
index be7e6cc..ba06c9b 100644
--- a/platforms/ibm-fsp/apollo.c
+++ b/platforms/ibm-fsp/apollo.c
@@ -19,6 +19,7 @@
#include <device.h>
#include <fsp.h>
#include <pci.h>
+#include <pci-slot.h>
#include "ibm-fsp.h"
#include "lxvpd.h"
@@ -28,24 +29,6 @@ static bool apollo_probe(void)
return dt_node_is_compatible(dt_root, "ibm,apollo");
}
-static void apollo_setup_phb(struct phb *phb, unsigned int index)
-{
- struct dt_node *ioc_node;
-
- /* Grab the device-tree node of the IOC */
- ioc_node = phb->dt_node->parent;
- if (!ioc_node)
- return;
-
- /*
- * Process the pcie slot entries from the lx vpd lid
- *
- * FIXME: We currently assume chip 1 always, this will have to be
- * fixed once we understand the right way to get the BRxy/BRxy "x"
- * "x" value. (this actually seems to work...)
- */
- lxvpd_process_slot_entries(phb, ioc_node, 1, index);
-}
DECLARE_PLATFORM(apollo) = {
.name = "Apollo",
@@ -54,8 +37,8 @@ DECLARE_PLATFORM(apollo) = {
.exit = ibm_fsp_exit,
.cec_power_down = ibm_fsp_cec_power_down,
.cec_reboot = ibm_fsp_cec_reboot,
- .pci_setup_phb = apollo_setup_phb,
- .pci_get_slot_info = lxvpd_get_slot_info,
+ .pci_setup_phb = apollo_pci_setup_phb,
+ .pci_get_slot_info = apollo_pci_get_slot_info,
.nvram_info = fsp_nvram_info,
.nvram_start_read = fsp_nvram_start_read,
.nvram_write = fsp_nvram_write,
diff --git a/platforms/ibm-fsp/firenze-pci.c b/platforms/ibm-fsp/firenze-pci.c
new file mode 100644
index 0000000..8b3786e
--- /dev/null
+++ b/platforms/ibm-fsp/firenze-pci.c
@@ -0,0 +1,1055 @@
+/* Copyright 2013-2015 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <skiboot.h>
+#include <device.h>
+#include <fsp.h>
+#include <lock.h>
+#include <timer.h>
+#include <xscom.h>
+#include <pci-cfg.h>
+#include <pci.h>
+#include <pci-slot.h>
+#include <phb3.h>
+#include <chip.h>
+#include <i2c.h>
+
+#include "ibm-fsp.h"
+#include "lxvpd.h"
+
+/* Debugging options */
+#define FIRENZE_PCI_DBG(fmt, a...) \
+ prlog(PR_DEBUG, "FIRENZE-PCI: " fmt, ##a)
+#define FIRENZE_PCI_INFO(fmt, a...) \
+ prlog(PR_INFO, "FIRENZE-PCI: " fmt, ##a)
+#define FIRENZE_PCI_ERR(fmt, a...) \
+ prlog(PR_ERR, "FIRENZE-PCI: " fmt, ##a)
+
+/* Dump PCI slots before sending to FSP */
+#define FIRENZE_PCI_INVENTORY_DUMP
+
+/*
+ * Firenze PCI slot states to override the default set.
+ * Refer to pci-slot.h for the default PCI state set
+ * when you're going to change below values.
+ */
+#define FIRENZE_PCI_SLOT_NORMAL 0x00000000
+#define FIRENZE_PCI_SLOT_HRESET 0x00000200
+#define FIRENZE_PCI_SLOT_HRESET_START 0x00000201
+#define FIRENZE_PCI_SLOT_FRESET 0x00000300
+#define FIRENZE_PCI_SLOT_FRESET_START 0x00000301
+#define FIRENZE_PCI_SLOT_FRESET_WAIT_RSP 0x00000302
+#define FIRENZE_PCI_SLOT_FRESET_DELAY 0x00000303
+#define FIRENZE_PCI_SLOT_FRESET_POWER_STATE 0x00000304
+#define FIRENZE_PCI_SLOT_FRESET_POWER_OFF 0x00000305
+#define FIRENZE_PCI_SLOT_FRESET_POWER_ON 0x00000306
+#define FIRENZE_PCI_SLOT_PERST_DEASSERT 0x00000307
+#define FIRENZE_PCI_SLOT_PERST_DELAY 0x00000308
+#define FIRENZE_PCI_SLOT_PFRESET 0x00000400
+#define FIRENZE_PCI_SLOT_PFRESET_START 0x00000401
+#define FIRENZE_PCI_SLOT_GPOWER 0x00000600
+#define FIRENZE_PCI_SLOT_GPOWER_START 0x00000601
+#define FIRENZE_PCI_SLOT_GPOWER_WAIT_RSP 0x00000602
+#define FIRENZE_PCI_SLOT_GPOWER_DELAY 0x00000603
+#define FIRENZE_PCI_SLOT_GPOWER_STATE 0x00000604
+#define FIRENZE_PCI_SLOT_SPOWER 0x00000700
+#define FIRENZE_PCI_SLOT_SPOWER_START 0x00000701
+#define FIRENZE_PCI_SLOT_SPOWER_WAIT_RSP 0x00000702
+#define FIRENZE_PCI_SLOT_SPOWER_DELAY 0x00000703
+#define FIRENZE_PCI_SLOT_SPOWER_OFF 0x00000704
+#define FIRENZE_PCI_SLOT_SPOWER_ON 0x00000705
+#define FIRENZE_PCI_SLOT_SPOWER_LINK 0x00000706
+#define FIRENZE_PCI_SLOT_SPOWER_EVENT 0x00000707
+#define FIRENZE_PCI_SLOT_SPOWER_DONE 0x00000708
+
+/* Timeout for power status */
+#define FIRENZE_PCI_SLOT_RETRIES 500
+#define FIRENZE_PCI_SLOT_DELAY 10 /* ms */
+#define FIRENZE_PCI_I2C_TIMEOUT 5000 /* ms */
+
+/*
+ * Need figure out more stuff later: LED and presence
+ * detection sensors are accessed from PSI/FSP.
+ */
+struct firenze_pci_slot {
+ struct lxvpd_pci_slot lxvpd_slot; /* LXVPD slot data */
+
+ /* Next slot state */
+ uint32_t next_state;
+
+ /* Power management */
+ struct i2c_bus *i2c_bus; /* Where MAX5961 seats */
+ struct i2c_request *req; /* I2C request message */
+ uint8_t i2c_rw_buf[8]; /* I2C read/write buffer */
+ uint8_t power_mask; /* Bits for power status */
+ uint8_t power_on; /* Bits for power on */
+ uint8_t power_off; /* Bits for power off */
+ uint8_t *power_status; /* Last power status */
+ uint16_t perst_reg; /* PERST config register */
+ uint16_t perst_bit; /* PERST bit */
+};
+
+struct firenze_pci_slot_info {
+ uint8_t index;
+ const char *label;
+ uint8_t external_power_mgt;
+ uint8_t inband_perst;
+ uint8_t chip_id;
+ uint8_t master_id;
+ uint8_t port_id;
+ uint8_t slave_addr;
+ uint8_t channel;
+ uint8_t power_status;
+ uint8_t buddy;
+};
+
+struct firenze_pci_inv {
+ uint32_t hw_proc_id;
+ uint16_t slot_idx;
+ uint16_t reserved;
+ uint16_t vendor_id;
+ uint16_t device_id;
+ uint16_t subsys_vendor_id;
+ uint16_t subsys_device_id;
+};
+
+struct firenze_pci_inv_data {
+ uint32_t version; /* currently 1 */
+ uint32_t num_entries;
+ uint32_t entry_size;
+ uint32_t entry_offset;
+ struct firenze_pci_inv entries[];
+};
+
+/*
+ * Note: According to Tuleta system workbook, I didn't figure
+ * out the I2C mapping info for slot C14/C15.
+ */
+static struct firenze_pci_inv_data *firenze_inv_data;
+static uint32_t firenze_inv_cnt;
+static struct firenze_pci_slot_info firenze_pci_slots[] = {
+ { 0x0B, "C7", 1, 1, 0, 1, 0, 0x35, 1, 0xAA, 0 },
+ { 0x11, "C14", 0, 1, 0, 0, 0, 0x00, 0, 0xAA, 1 },
+ { 0x0F, "C11", 1, 1, 0, 1, 0, 0x32, 1, 0xAA, 2 },
+ { 0x10, "C12", 1, 1, 0, 1, 0, 0x39, 0, 0xAA, 3 },
+ { 0x0A, "C6", 1, 1, 0, 1, 0, 0x35, 0, 0xAA, 0 },
+ { 0x12, "C15", 0, 1, 0, 0, 0, 0x00, 0, 0xAA, 5 },
+ { 0x01, "USB", 0, 0, 0, 0, 0, 0x00, 0, 0xAA, 6 },
+ { 0x0C, "C8", 1, 1, 0, 1, 0, 0x36, 0, 0xAA, 7 },
+ { 0x0D, "C9", 1, 1, 0, 1, 0, 0x36, 1, 0xAA, 7 },
+ { 0x0E, "C10", 1, 1, 0, 1, 0, 0x32, 0, 0xAA, 2 },
+ { 0x09, "C5", 1, 1, 0x10, 1, 0, 0x39, 1, 0xAA, 10 },
+ { 0x08, "C4", 1, 1, 0x10, 1, 0, 0x39, 0, 0xAA, 10 },
+ { 0x07, "C3", 1, 1, 0x10, 1, 0, 0x3A, 1, 0xAA, 12 },
+ { 0x06, "C2", 1, 1, 0x10, 1, 0, 0x3A, 0, 0xAA, 12 }
+};
+
+static void firenze_pci_add_inventory(struct phb *phb,
+ struct pci_device *pd)
+{
+ struct lxvpd_pci_slot *lxvpd_slot;
+ struct firenze_pci_inv *entry;
+ struct proc_chip *chip;
+ size_t size;
+ bool need_init = false;
+
+ /*
+ * Do we need to add that to the FSP inventory for power
+ * management?
+ *
+ * For now, we only add devices that:
+ *
+ * - Are function 0
+ * - Are not an RC or a downstream bridge
+ * - Have a direct parent that has a slot entry
+ * - Slot entry says pluggable
+ * - Aren't an upstream switch that has slot info
+ */
+ if (!pd || !pd->parent)
+ return;
+ if (pd->bdfn & 7)
+ return;
+ if (pd->dev_type == PCIE_TYPE_ROOT_PORT ||
+ pd->dev_type == PCIE_TYPE_SWITCH_DNPORT)
+ return;
+ if (pd->dev_type == PCIE_TYPE_SWITCH_UPPORT &&
+ pd->slot && pd->slot->data)
+ return;
+ if (!pd->parent->slot ||
+ !pd->parent->slot->data)
+ return;
+ lxvpd_slot = pd->parent->slot->data;
+ if (!lxvpd_slot->pluggable)
+ return;
+
+ /* Check if we need to do some (Re)allocation */
+ if (!firenze_inv_data ||
+ firenze_inv_data->num_entries == firenze_inv_cnt) {
+ need_init = !firenze_inv_data;
+
+ /* (Re)allocate the block to the new size */
+ firenze_inv_cnt += 4;
+ size = sizeof(struct firenze_pci_inv_data) +
+ sizeof(struct firenze_pci_inv) * firenze_inv_cnt;
+ firenze_inv_data = realloc(firenze_inv_data, size);
+ }
+
+ /* Initialize the header for a new inventory */
+ if (need_init) {
+ firenze_inv_data->version = 1;
+ firenze_inv_data->num_entries = 0;
+ firenze_inv_data->entry_size =
+ sizeof(struct firenze_pci_inv);
+ firenze_inv_data->entry_offset =
+ offsetof(struct firenze_pci_inv_data, entries);
+ }
+
+ /* Append slot entry */
+ entry = &firenze_inv_data->entries[firenze_inv_data->num_entries++];
+ chip = get_chip(dt_get_chip_id(phb->dt_node));
+ if (!chip) {
+ FIRENZE_PCI_ERR("No chip device node for PHB%04x\n",
+ phb->opal_id);
+ return;
+ }
+
+ entry->hw_proc_id = chip->pcid;
+ entry->reserved = 0;
+ if (pd->parent &&
+ pd->parent->slot &&
+ pd->parent->slot->data) {
+ lxvpd_slot = pd->parent->slot->data;
+ entry->slot_idx = lxvpd_slot->slot_index;
+ }
+
+ pci_cfg_read16(phb, pd->bdfn, PCI_CFG_VENDOR_ID, &entry->vendor_id);
+ pci_cfg_read16(phb, pd->bdfn, PCI_CFG_DEVICE_ID, &entry->device_id);
+ if (pd->is_bridge) {
+ int64_t ssvc = pci_find_cap(phb, pd->bdfn,
+ PCI_CFG_CAP_ID_SUBSYS_VID);
+ if (ssvc <= 0) {
+ entry->subsys_vendor_id = 0xffff;
+ entry->subsys_device_id = 0xffff;
+ } else {
+ pci_cfg_read16(phb, pd->bdfn,
+ ssvc + PCICAP_SUBSYS_VID_VENDOR,
+ &entry->subsys_vendor_id);
+ pci_cfg_read16(phb, pd->bdfn,
+ ssvc + PCICAP_SUBSYS_VID_DEVICE,
+ &entry->subsys_device_id);
+ }
+ } else {
+ pci_cfg_read16(phb, pd->bdfn, PCI_CFG_SUBSYS_VENDOR_ID,
+ &entry->subsys_vendor_id);
+ pci_cfg_read16(phb, pd->bdfn, PCI_CFG_SUBSYS_ID,
+ &entry->subsys_device_id);
+ }
+}
+
+static void firenze_dump_pci_inventory(void)
+{
+#ifdef FIRENZE_PCI_INVENTORY_DUMP
+ struct firenze_pci_inv *e;
+ uint32_t i;
+
+ if (!firenze_inv_data)
+ return;
+
+ FIRENZE_PCI_INFO("Dumping Firenze PCI inventory\n");
+ FIRENZE_PCI_INFO("HWP SLT VDID DVID SVID SDID\n");
+ FIRENZE_PCI_INFO("---------------------------\n");
+ for (i = 0; i < firenze_inv_data->num_entries; i++) {
+ e = &firenze_inv_data->entries[i];
+
+ FIRENZE_PCI_INFO("%03d %03d %04x %04x %04x %04x\n",
+ e->hw_proc_id, e->slot_idx,
+ e->vendor_id, e->device_id,
+ e->subsys_vendor_id, e->subsys_device_id);
+ }
+#endif /* FIRENZE_PCI_INVENTORY_DUMP */
+}
+
+void firenze_pci_send_inventory(void)
+{
+ uint64_t base, abase, end, aend, offset;
+ int64_t rc;
+
+ if (!firenze_inv_data)
+ return;
+
+ /* Dump the inventory */
+ FIRENZE_PCI_INFO("Sending %d inventory to FSP\n",
+ firenze_inv_data->num_entries);
+ firenze_dump_pci_inventory();
+
+ /* Memory location for inventory */
+ base = (uint64_t)firenze_inv_data;
+ end = base +
+ sizeof(struct firenze_pci_inv_data) +
+ firenze_inv_data->num_entries * firenze_inv_data->entry_size;
+ abase = base & ~0xffful;
+ aend = (end + 0xffful) & ~0xffful;
+ offset = PSI_DMA_PCIE_INVENTORY + (base & 0xfff);
+
+ /* We can only accomodate so many entries in the PSI map */
+ if ((aend - abase) > PSI_DMA_PCIE_INVENTORY_SIZE) {
+ FIRENZE_PCI_ERR("Inventory (%lld bytes) too large\n",
+ aend - abase);
+ goto bail;
+ }
+
+ /* Map this in the TCEs */
+ fsp_tce_map(PSI_DMA_PCIE_INVENTORY, (void *)abase, aend - abase);
+
+ /* Send FSP message */
+ rc = fsp_sync_msg(fsp_mkmsg(FSP_CMD_PCI_POWER_CONF, 3,
+ hi32(offset), lo32(offset),
+ end - base), true);
+ if (rc)
+ FIRENZE_PCI_ERR("Error %lld sending inventory\n",
+ rc);
+
+ /* Unmap */
+ fsp_tce_unmap(PSI_DMA_PCIE_INVENTORY, aend - abase);
+bail:
+ /*
+ * We free the inventory. We'll have to redo that on hotplug
+ * when we support it but that isn't the case yet
+ */
+ free(firenze_inv_data);
+ firenze_inv_data = NULL;
+ firenze_inv_cnt = 0;
+}
+
+/* The function is called when the I2C request is completed
+ * successfully, or with errors.
+ */
+static void firenze_i2c_req_done(int rc, struct i2c_request *req)
+{
+ struct pci_slot *slot = req->user_data;
+ uint32_t state;
+
+ /* Check if there are errors for the completion */
+ if (rc) {
+ FIRENZE_PCI_ERR("Error %d from I2C request on slot %016llx\n",
+ rc, slot->id);
+ return;
+ }
+
+ /* Check the request type */
+ if (req->op != SMBUS_READ && req->op != SMBUS_WRITE) {
+ FIRENZE_PCI_ERR("Invalid I2C request %d on slot %016llx\n",
+ req->op, slot->id);
+ return;
+ }
+
+ /* After writting power status to I2C slave, we need at least
+ * 5ms delay for the slave to settle down. We also have the
+ * delay after reading the power status as well.
+ */
+ switch (slot->state) {
+ case FIRENZE_PCI_SLOT_FRESET_WAIT_RSP:
+ FIRENZE_PCI_DBG("%016llx FRESET: I2C request completed\n",
+ slot->id);
+ state = FIRENZE_PCI_SLOT_FRESET_DELAY;
+ break;
+ case FIRENZE_PCI_SLOT_GPOWER_WAIT_RSP:
+ FIRENZE_PCI_DBG("%016llx GPOWER: I2C request completed\n",
+ slot->id);
+ state = FIRENZE_PCI_SLOT_GPOWER_DELAY;
+ break;
+ case FIRENZE_PCI_SLOT_SPOWER_WAIT_RSP:
+ FIRENZE_PCI_DBG("%016llx SPOWER: I2C request completed\n",
+ slot->id);
+ state = FIRENZE_PCI_SLOT_SPOWER_DELAY;
+ break;
+ default:
+ FIRENZE_PCI_ERR("Wrong state %08x on slot %016llx\n",
+ slot->state, slot->id);
+ return;
+ }
+
+ /* Switch to net state */
+ pci_slot_set_state(slot, state);
+}
+
+/* This function is called to setup normal PCI device or PHB slot.
+ * For the later case, the slot doesn't have the associated PCI
+ * device. Besides, the I2C response timeout is set to 5s. We might
+ * improve I2C in future to support priorized requests so that the
+ * timeout can be shortened.
+ */
+static int64_t firenze_pci_slot_freset(struct pci_slot *slot)
+{
+ struct firenze_pci_slot *plat_slot = slot->data;
+ uint8_t *pval, presence = 1;
+
+ switch (slot->state) {
+ case FIRENZE_PCI_SLOT_NORMAL:
+ case FIRENZE_PCI_SLOT_FRESET_START:
+ FIRENZE_PCI_DBG("%016llx FRESET: Starts\n",
+ slot->id);
+
+ /* Bail if nothing is connected */
+ if (slot->ops.get_presence_status)
+ slot->ops.get_presence_status(slot, &presence);
+ if (!presence) {
+ FIRENZE_PCI_DBG("%016llx FRESET: No device\n",
+ slot->id);
+ return OPAL_SUCCESS;
+ }
+
+ /* Prepare link down */
+ if (slot->ops.prepare_link_change) {
+ FIRENZE_PCI_DBG("%016llx FRESET: Prepares link down\n",
+ slot->id);
+ slot->ops.prepare_link_change(slot, false);
+ }
+
+ /* Send I2C request */
+ FIRENZE_PCI_DBG("%016llx FRESET: Check power state\n",
+ slot->id);
+ plat_slot->next_state =
+ FIRENZE_PCI_SLOT_FRESET_POWER_STATE;
+ plat_slot->req->op = SMBUS_READ;
+ slot->retries = FIRENZE_PCI_SLOT_RETRIES;
+ pci_slot_set_state(slot,
+ FIRENZE_PCI_SLOT_FRESET_WAIT_RSP);
+ if (pci_slot_has_flags(slot, PCI_SLOT_FLAG_BOOTUP))
+ i2c_set_req_timeout(plat_slot->req,
+ FIRENZE_PCI_I2C_TIMEOUT);
+ else
+ i2c_set_req_timeout(plat_slot->req, 0ul);
+ i2c_queue_req(plat_slot->req);
+ return pci_slot_set_sm_timeout(slot,
+ msecs_to_tb(FIRENZE_PCI_SLOT_DELAY));
+ case FIRENZE_PCI_SLOT_FRESET_WAIT_RSP:
+ if (slot->retries-- == 0) {
+ FIRENZE_PCI_DBG("%016llx FRESET: Timeout waiting for %08x\n",
+ slot->id, plat_slot->next_state);
+ goto out;
+ }
+
+ check_timers(false);
+ return pci_slot_set_sm_timeout(slot,
+ msecs_to_tb(FIRENZE_PCI_SLOT_DELAY));
+ case FIRENZE_PCI_SLOT_FRESET_DELAY:
+ FIRENZE_PCI_DBG("%016llx FRESET: Delay %dms on I2C completion\n",
+ slot->id, FIRENZE_PCI_SLOT_DELAY);
+ pci_slot_set_state(slot, plat_slot->next_state);
+ return pci_slot_set_sm_timeout(slot,
+ msecs_to_tb(FIRENZE_PCI_SLOT_DELAY));
+ case FIRENZE_PCI_SLOT_FRESET_POWER_STATE:
+ /* Update last power status */
+ pval = (uint8_t *)(plat_slot->req->rw_buf);
+ *plat_slot->power_status = *pval;
+
+ /* Power is on, turn it off */
+ if (((*pval) & plat_slot->power_mask) == plat_slot->power_on) {
+ FIRENZE_PCI_DBG("%016llx FRESET: Power (%02x) on, turn off\n",
+ slot->id, *pval);
+ (*pval) &= ~plat_slot->power_mask;
+ (*pval) |= plat_slot->power_off;
+ plat_slot->req->op = SMBUS_WRITE;
+ slot->retries = FIRENZE_PCI_SLOT_RETRIES;
+ plat_slot->next_state =
+ FIRENZE_PCI_SLOT_FRESET_POWER_OFF;
+ pci_slot_set_state(slot,
+ FIRENZE_PCI_SLOT_FRESET_WAIT_RSP);
+
+ if (pci_slot_has_flags(slot, PCI_SLOT_FLAG_BOOTUP))
+ i2c_set_req_timeout(plat_slot->req,
+ FIRENZE_PCI_I2C_TIMEOUT);
+ else
+ i2c_set_req_timeout(plat_slot->req, 0ul);
+ i2c_queue_req(plat_slot->req);
+ return pci_slot_set_sm_timeout(slot,
+ msecs_to_tb(FIRENZE_PCI_SLOT_DELAY));
+ }
+
+ /* Fall through: Power is off, turn it on */
+ case FIRENZE_PCI_SLOT_FRESET_POWER_OFF:
+ /* Update last power status */
+ pval = (uint8_t *)(plat_slot->req->rw_buf);
+ *plat_slot->power_status = *pval;
+
+ FIRENZE_PCI_DBG("%016llx FRESET: Power (%02x) off, turn on\n",
+ slot->id, *pval);
+ (*pval) &= ~plat_slot->power_mask;
+ (*pval) |= plat_slot->power_on;
+ plat_slot->req->op = SMBUS_WRITE;
+ plat_slot->next_state =
+ FIRENZE_PCI_SLOT_FRESET_POWER_ON;
+ slot->retries = FIRENZE_PCI_SLOT_RETRIES;
+ pci_slot_set_state(slot,
+ FIRENZE_PCI_SLOT_FRESET_WAIT_RSP);
+
+ if (pci_slot_has_flags(slot, PCI_SLOT_FLAG_BOOTUP))
+ i2c_set_req_timeout(plat_slot->req,
+ FIRENZE_PCI_I2C_TIMEOUT);
+ else
+ i2c_set_req_timeout(plat_slot->req, 0ul);
+ i2c_queue_req(plat_slot->req);
+ return pci_slot_set_sm_timeout(slot,
+ msecs_to_tb(FIRENZE_PCI_SLOT_DELAY));
+ case FIRENZE_PCI_SLOT_FRESET_POWER_ON:
+ /* Update last power status */
+ pval = (uint8_t *)(plat_slot->req->rw_buf);
+ *plat_slot->power_status = *pval;
+
+ /* PHB3 slot supports post fundamental reset, we switch
+ * to that. For normal PCI slot, we switch to hot reset
+ * instead.
+ */
+ if (slot->ops.pfreset) {
+ FIRENZE_PCI_DBG("%016llx FRESET: Switch to PFRESET\n",
+ slot->id);
+ pci_slot_set_state(slot,
+ FIRENZE_PCI_SLOT_PFRESET_START);
+ return slot->ops.pfreset(slot);
+ } else if (slot->ops.hreset) {
+ FIRENZE_PCI_DBG("%016llx FRESET: Switch to HRESET\n",
+ slot->id);
+ pci_slot_set_state(slot,
+ FIRENZE_PCI_SLOT_HRESET_START);
+ return slot->ops.hreset(slot);
+ }
+
+ pci_slot_set_state(slot, FIRENZE_PCI_SLOT_NORMAL);
+ return OPAL_SUCCESS;
+ default:
+ FIRENZE_PCI_DBG("%016llx FRESET: Unexpected state %08x\n",
+ slot->id, slot->state);
+ }
+
+out:
+ pci_slot_set_state(slot, FIRENZE_PCI_SLOT_NORMAL);
+ return OPAL_HARDWARE;
+}
+
+static int64_t firenze_pci_slot_perst(struct pci_slot *slot)
+{
+ struct firenze_pci_slot *plat_slot = slot->data;
+ uint8_t presence = 1;
+ uint16_t ctrl;
+
+ switch (slot->state) {
+ case FIRENZE_PCI_SLOT_NORMAL:
+ case FIRENZE_PCI_SLOT_FRESET_START:
+ FIRENZE_PCI_DBG("%016llx PERST: Starts\n",
+ slot->id);
+
+ /* Bail if nothing is connected */
+ if (slot->ops.get_presence_status)
+ slot->ops.get_presence_status(slot, &presence);
+ if (!presence) {
+ FIRENZE_PCI_DBG("%016llx PERST: No device\n",
+ slot->id);
+ return OPAL_SUCCESS;
+ }
+
+ /* Prepare link down */
+ if (slot->ops.prepare_link_change) {
+ FIRENZE_PCI_DBG("%016llx PERST: Prepare link down\n",
+ slot->id);
+ slot->ops.prepare_link_change(slot, false);
+ }
+
+ /* Assert PERST */
+ FIRENZE_PCI_DBG("%016llx PERST: Assert\n",
+ slot->id);
+ pci_cfg_read16(slot->phb, slot->pd->bdfn,
+ plat_slot->perst_reg, &ctrl);
+ ctrl |= plat_slot->perst_bit;
+ pci_cfg_write16(slot->phb, slot->pd->bdfn,
+ plat_slot->perst_reg, ctrl);
+ pci_slot_set_state(slot,
+ FIRENZE_PCI_SLOT_PERST_DEASSERT);
+ return pci_slot_set_sm_timeout(slot, msecs_to_tb(250));
+ case FIRENZE_PCI_SLOT_PERST_DEASSERT:
+ /* Deassert PERST */
+ pci_cfg_read16(slot->phb, slot->pd->bdfn,
+ plat_slot->perst_reg, &ctrl);
+ ctrl &= ~plat_slot->perst_bit;
+ pci_cfg_write16(slot->phb, slot->pd->bdfn,
+ plat_slot->perst_reg, ctrl);
+ pci_slot_set_state(slot,
+ FIRENZE_PCI_SLOT_PERST_DELAY);
+ return pci_slot_set_sm_timeout(slot, msecs_to_tb(1500));
+ case FIRENZE_PCI_SLOT_PERST_DELAY:
+ /*
+ * Switch to post fundamental reset if the slot supports
+ * that. Otherwise, we issue a proceeding hot reset on
+ * the slot.
+ */
+ if (slot->ops.pfreset) {
+ FIRENZE_PCI_DBG("%016llx PERST: Switch to PFRESET\n",
+ slot->id);
+ pci_slot_set_state(slot,
+ FIRENZE_PCI_SLOT_PFRESET_START);
+ return slot->ops.pfreset(slot);
+ } else if (slot->ops.hreset) {
+ FIRENZE_PCI_DBG("%016llx PERST: Switch to HRESET\n",
+ slot->id);
+ pci_slot_set_state(slot,
+ FIRENZE_PCI_SLOT_HRESET_START);
+ return slot->ops.hreset(slot);
+ }
+
+ pci_slot_set_state(slot, FIRENZE_PCI_SLOT_NORMAL);
+ return OPAL_SUCCESS;
+ default:
+ FIRENZE_PCI_DBG("%016llx PERST: Unexpected state %08x\n",
+ slot->id, slot->state);
+ }
+
+ pci_slot_set_state(slot, FIRENZE_PCI_SLOT_NORMAL);
+ return OPAL_HARDWARE;
+}
+
+static int64_t firenze_pci_slot_get_power(struct pci_slot *slot, uint8_t *val)
+{
+ struct firenze_pci_slot *plat_slot = slot->data;
+ uint8_t *pval;
+
+ switch (slot->state) {
+ case FIRENZE_PCI_SLOT_NORMAL:
+ case FIRENZE_PCI_SLOT_GPOWER_START:
+ FIRENZE_PCI_DBG("%016llx GPOWER: Starts\n",
+ slot->id);
+ FIRENZE_PCI_DBG("%016llx GPOWER: Check power state\n",
+ slot->id);
+
+ plat_slot->next_state =
+ FIRENZE_PCI_SLOT_GPOWER_STATE;
+ plat_slot->req->op = SMBUS_READ;
+ slot->retries = FIRENZE_PCI_SLOT_RETRIES;
+ pci_slot_set_state(slot,
+ FIRENZE_PCI_SLOT_GPOWER_WAIT_RSP);
+ if (pci_slot_has_flags(slot, PCI_SLOT_FLAG_BOOTUP))
+ i2c_set_req_timeout(plat_slot->req,
+ FIRENZE_PCI_I2C_TIMEOUT);
+ else
+ i2c_set_req_timeout(plat_slot->req, 0ul);
+ i2c_queue_req(plat_slot->req);
+ return pci_slot_set_sm_timeout(slot,
+ msecs_to_tb(FIRENZE_PCI_SLOT_DELAY));
+ case FIRENZE_PCI_SLOT_GPOWER_WAIT_RSP:
+ if (slot->retries-- == 0) {
+ FIRENZE_PCI_DBG("%016llx GPOWER: Timeout waiting for %08x\n",
+ slot->id, plat_slot->next_state);
+ goto out;
+ }
+
+ check_timers(false);
+ return pci_slot_set_sm_timeout(slot,
+ msecs_to_tb(FIRENZE_PCI_SLOT_DELAY));
+ case FIRENZE_PCI_SLOT_GPOWER_DELAY:
+ FIRENZE_PCI_DBG("%016llx GPOWER: Delay %dms on I2C completion\n",
+ slot->id, FIRENZE_PCI_SLOT_DELAY);
+ pci_slot_set_state(slot, plat_slot->next_state);
+ return pci_slot_set_sm_timeout(slot,
+ msecs_to_tb(FIRENZE_PCI_SLOT_DELAY));
+ case FIRENZE_PCI_SLOT_GPOWER_STATE:
+ /* Update last power status */
+ pval = (uint8_t *)(plat_slot->req->rw_buf);
+ *plat_slot->power_status = *pval;
+
+ if (((*pval) & plat_slot->power_mask) == plat_slot->power_on)
+ *val = 1;
+ else
+ *val = 0;
+ FIRENZE_PCI_DBG("%016llx GPOWER: Power (%02x) %s\n",
+ slot->id, *pval, *val ? "on" : "off");
+
+ pci_slot_set_state(slot, FIRENZE_PCI_SLOT_NORMAL);
+ return OPAL_SUCCESS;
+ default:
+ FIRENZE_PCI_DBG("%016llx GPOWER: Unexpected state %08x\n",
+ slot->id, slot->state);
+ }
+
+out:
+ pci_slot_set_state(slot, FIRENZE_PCI_SLOT_NORMAL);
+ return OPAL_HARDWARE;
+}
+
+/* The function is called when the PCI hotplug message has
+ * been received by OS successfully.
+ */
+static void firenze_pci_slot_consume_msg(void *data)
+{
+ struct pci_slot *slot = data;
+ struct firenze_pci_slot *plat_slot = slot->data;
+
+ FIRENZE_PCI_DBG("%016llx SPOWER: Hotplug message consumed\n",
+ slot->id);
+
+ /* Step over to next state. Don't free the hotplug buffer,
+ * which is still used by the OS. Instead, we will free it
+ * lazily when we have to swap it for another hotplug event
+ */
+ pci_slot_set_state(slot, plat_slot->next_state);
+}
+
+static int64_t firenze_pci_slot_set_power(struct pci_slot *slot, uint8_t val)
+{
+ struct firenze_pci_slot *plat_slot = slot->data;
+ uint8_t link = 0;
+ uint8_t *pval;
+ uint64_t rc = OPAL_SUCCESS;
+
+ switch (slot->state) {
+ case FIRENZE_PCI_SLOT_NORMAL:
+ case FIRENZE_PCI_SLOT_SPOWER_START:
+ FIRENZE_PCI_DBG("%016llx SPOWER: Starts (%s)\n",
+ slot->id, val ? "On" : "Off");
+
+ /* Prepare link down if necessary */
+ if (!val && slot->ops.prepare_link_change) {
+ FIRENZE_PCI_DBG("%016llx SPOWER: Prepares link down\n",
+ slot->id);
+ slot->ops.prepare_link_change(slot, false);
+ }
+
+ /* Populate I2C request. We have two channels there.
+ * So we have to pick the last power status to avoid
+ * corrupting the power status on another channel.
+ */
+ FIRENZE_PCI_DBG("%016llx SPOWER: Powering %s\n",
+ slot->id, val ? "on" : "off");
+ plat_slot->req->op = SMBUS_WRITE;
+ pval = (uint8_t *)plat_slot->req->rw_buf;
+ if (val) {
+ plat_slot->next_state = FIRENZE_PCI_SLOT_SPOWER_ON;
+ *pval = *plat_slot->power_status;
+ (*pval) &= ~plat_slot->power_mask;
+ (*pval) |= plat_slot->power_on;
+ } else {
+ plat_slot->next_state = FIRENZE_PCI_SLOT_SPOWER_OFF;
+ *pval = *plat_slot->power_status;
+ (*pval) &= ~plat_slot->power_mask;
+ (*pval) |= plat_slot->power_off;
+ }
+
+ /* When the OS isn't up yet, we shouldn't fire any PCI
+ * hotplug messages. Nobody will consume that and it
+ * will cause system hang.
+ */
+ if (pci_slot_has_flags(slot, PCI_SLOT_FLAG_NO_HOTPLUG_MSG))
+ plat_slot->next_state = FIRENZE_PCI_SLOT_SPOWER_DONE;
+
+ slot->retries = FIRENZE_PCI_SLOT_RETRIES;
+ pci_slot_set_state(slot, FIRENZE_PCI_SLOT_SPOWER_WAIT_RSP);
+ if (pci_slot_has_flags(slot, PCI_SLOT_FLAG_BOOTUP))
+ i2c_set_req_timeout(plat_slot->req,
+ FIRENZE_PCI_I2C_TIMEOUT);
+ else
+ i2c_set_req_timeout(plat_slot->req, 0ul);
+ i2c_queue_req(plat_slot->req);
+ return pci_slot_set_sm_timeout(slot,
+ msecs_to_tb(FIRENZE_PCI_SLOT_DELAY));
+ case FIRENZE_PCI_SLOT_SPOWER_WAIT_RSP:
+ if (slot->retries-- == 0) {
+ FIRENZE_PCI_DBG("%016llx SPOWER: Timeout waiting for %08x\n",
+ slot->id, plat_slot->next_state);
+ goto out;
+ }
+
+ check_timers(false);
+ return pci_slot_set_sm_timeout(slot,
+ msecs_to_tb(FIRENZE_PCI_SLOT_DELAY));
+ case FIRENZE_PCI_SLOT_SPOWER_DELAY:
+ FIRENZE_PCI_DBG("%016llx SPOWER: Delay %dms on I2C completion\n",
+ slot->id, FIRENZE_PCI_SLOT_DELAY);
+ pci_slot_set_state(slot, plat_slot->next_state);
+ return pci_slot_set_sm_timeout(slot,
+ msecs_to_tb(FIRENZE_PCI_SLOT_DELAY));
+ case FIRENZE_PCI_SLOT_SPOWER_OFF:
+ FIRENZE_PCI_DBG("%016llx SPOWER: Fire unplugging event\n",
+ slot->id);
+
+ /* Update last power status */
+ pval = (uint8_t *)plat_slot->req->rw_buf;
+ *plat_slot->power_status = *pval;
+
+ /* Sending hotplug event. We don't need care the link
+ * status on power-off.
+ */
+ plat_slot->next_state = FIRENZE_PCI_SLOT_SPOWER_DONE;
+ slot->retries = FIRENZE_PCI_SLOT_RETRIES;
+ pci_slot_set_state(slot, FIRENZE_PCI_SLOT_SPOWER_EVENT);
+ pci_slot_hotplug_event(slot, false,
+ firenze_pci_slot_consume_msg);
+ return pci_slot_set_sm_timeout(slot,
+ msecs_to_tb(FIRENZE_PCI_SLOT_DELAY));
+ case FIRENZE_PCI_SLOT_SPOWER_ON:
+ FIRENZE_PCI_DBG("%016llx SPOWER: Waiting for link\n",
+ slot->id);
+
+ /* Update last power status */
+ pval = (uint8_t *)plat_slot->req->rw_buf;
+ *plat_slot->power_status = *pval;
+
+ /* We can't send hotplug event, which is built after
+ * rescanning the affected PCI domain. Instead, we
+ * have to poll link until it's up after power-on.
+ */
+ slot->retries = FIRENZE_PCI_SLOT_RETRIES;
+ pci_slot_set_state(slot, FIRENZE_PCI_SLOT_SPOWER_LINK);
+ /* fall through */
+ case FIRENZE_PCI_SLOT_SPOWER_LINK:
+ if (slot->retries-- == 0) {
+ rc = OPAL_SUCCESS;
+ link = 1;
+ FIRENZE_PCI_DBG("%016llx SPOWER: Timeout waiting for link\n",
+ slot->id);
+ } else {
+ rc = slot->ops.get_link_status(slot, &link);
+ }
+
+ /* Poll if link is down */
+ if (rc != OPAL_SUCCESS || !link)
+ return pci_slot_set_sm_timeout(slot,
+ msecs_to_tb(FIRENZE_PCI_SLOT_DELAY));
+
+ /* Prepare link up */
+ if (slot->ops.prepare_link_change) {
+ FIRENZE_PCI_DBG("%016llx SPOWER: Prepare link up\n",
+ slot->id);
+ slot->ops.prepare_link_change(slot, true);
+ }
+
+ /* Link is up or permanently down. The rescanning won't be
+ * done if the link is down when building the hotplug event.
+ */
+ FIRENZE_PCI_DBG("%016llx SPOWER: Fire plugging event\n",
+ slot->id);
+ plat_slot->next_state = FIRENZE_PCI_SLOT_SPOWER_DONE;
+ slot->retries = FIRENZE_PCI_SLOT_RETRIES;
+ pci_slot_set_state(slot, FIRENZE_PCI_SLOT_SPOWER_EVENT);
+ pci_slot_hotplug_event(slot, true,
+ firenze_pci_slot_consume_msg);
+ return pci_slot_set_sm_timeout(slot,
+ msecs_to_tb(FIRENZE_PCI_SLOT_DELAY));
+ case FIRENZE_PCI_SLOT_SPOWER_EVENT:
+ if (slot->retries-- == 0) {
+ FIRENZE_PCI_DBG("%016llx SPOWER: Timeout waiting for %08x\n",
+ slot->id, plat_slot->next_state);
+ goto out;
+ }
+ return pci_slot_set_sm_timeout(slot,
+ msecs_to_tb(FIRENZE_PCI_SLOT_DELAY));
+ case FIRENZE_PCI_SLOT_SPOWER_DONE:
+ FIRENZE_PCI_DBG("%016llx SPOWER: Done\n",
+ slot->id);
+ pci_slot_set_state(slot, FIRENZE_PCI_SLOT_NORMAL);
+ return OPAL_SUCCESS;
+ default:
+ FIRENZE_PCI_DBG("%016llx SPOWER: Unexpected state %08x\n",
+ slot->id, slot->state);
+ }
+
+out:
+ pci_slot_set_state(slot, FIRENZE_PCI_SLOT_NORMAL);
+ return OPAL_HARDWARE;
+}
+
+static struct i2c_bus *firenze_pci_find_i2c_bus(struct firenze_pci_slot_info *info)
+{
+ struct dt_node *np, *child;
+ uint32_t reg;
+
+ /* Iterate I2C masters */
+ dt_for_each_compatible(dt_root, np, "ibm,power8-i2cm") {
+ if (!np->parent ||
+ !dt_node_is_compatible(np->parent, "ibm,power8-xscom"))
+ continue;
+
+ /* Check chip index */
+ reg = dt_prop_get_u32(np->parent, "ibm,chip-id");
+ if (reg != info->chip_id)
+ continue;
+
+ /* Check I2C master index */
+ reg = dt_prop_get_u32(np, "chip-engine#");
+ if (reg != info->master_id)
+ continue;
+
+ /* Iterate I2C buses */
+ dt_for_each_child(np, child) {
+ if (!dt_node_is_compatible(child, "ibm,power8-i2c-port"))
+ continue;
+
+ /* Check I2C port index */
+ reg = dt_prop_get_u32(child, "reg");
+ if (reg != info->port_id)
+ continue;
+
+ reg = dt_prop_get_u32(child, "ibm,opal-id");
+ return i2c_find_bus_by_id(reg);
+ }
+ }
+
+ return NULL;
+}
+
+static void firenze_pci_slot_init(struct pci_slot *slot)
+{
+ struct lxvpd_pci_slot *s = slot->data;
+ struct firenze_pci_slot *plat_slot = slot->data;
+ uint32_t vdid;
+ uint8_t buddy;
+ int i;
+
+ /* Search for PCI slot info */
+ for (i = 0; i < ARRAY_SIZE(firenze_pci_slots); i++) {
+ if (firenze_pci_slots[i].index == s->slot_index &&
+ !strcmp(firenze_pci_slots[i].label, s->label))
+ break;
+ }
+ if (i >= ARRAY_SIZE(firenze_pci_slots))
+ return;
+
+ /* Search I2C bus for external power mgt */
+ buddy = firenze_pci_slots[i].buddy;
+ plat_slot->i2c_bus = firenze_pci_find_i2c_bus(&firenze_pci_slots[i]);
+ if (plat_slot->i2c_bus) {
+ plat_slot->req = i2c_alloc_req(plat_slot->i2c_bus);
+ if (!plat_slot->req)
+ plat_slot->i2c_bus = NULL;
+
+ plat_slot->req->dev_addr = firenze_pci_slots[i].slave_addr;
+ plat_slot->req->offset_bytes = 1;
+ plat_slot->req->offset = 0x69;
+ plat_slot->req->rw_buf = plat_slot->i2c_rw_buf;
+ plat_slot->req->rw_len = 1;
+ plat_slot->req->completion = firenze_i2c_req_done;
+ plat_slot->req->user_data = slot;
+ switch (firenze_pci_slots[i].channel) {
+ case 0:
+ plat_slot->power_status = &firenze_pci_slots[buddy].power_status;
+ plat_slot->power_mask = 0x33;
+ plat_slot->power_on = 0x22;
+ plat_slot->power_off = 0x33;
+ break;
+ case 1:
+ plat_slot->power_status = &firenze_pci_slots[buddy].power_status;
+ plat_slot->power_mask = 0xcc;
+ plat_slot->power_on = 0x88;
+ plat_slot->power_off = 0xcc;
+ break;
+ default:
+ FIRENZE_PCI_DBG("%016llx: Invalid channel %d\n",
+ slot->id, firenze_pci_slots[i].channel);
+ plat_slot->i2c_bus = NULL;
+ }
+ }
+
+ /*
+ * If the slot has external power logic, to override the
+ * default power management methods. Because of the bad
+ * I2C design, the API supplied by I2C is really hard to
+ * be utilized. To figure out power status retrival or
+ * configuration after we have a blocking API for that.
+ */
+ if (plat_slot->i2c_bus) {
+ slot->ops.freset = firenze_pci_slot_freset;
+ slot->ops.get_power_status = firenze_pci_slot_get_power;
+ slot->ops.set_power_status = firenze_pci_slot_set_power;
+ FIRENZE_PCI_DBG("%016llx: External power mgt initialized\n",
+ slot->id);
+ } else if (firenze_pci_slots[i].inband_perst) {
+ /*
+ * For PLX downstream ports, PCI config register can be
+ * leveraged to do PERST. If the slot doesn't have external
+ * power management stuff, lets try to stick to the PERST
+ * logic if applicable
+ */
+ if (slot->pd->dev_type == PCIE_TYPE_SWITCH_DNPORT) {
+ pci_cfg_read32(slot->phb, slot->pd->bdfn,
+ PCI_CFG_VENDOR_ID, &vdid);
+ switch (vdid) {
+ case 0x873210b5: /* PLX8732 */
+ case 0x874810b5: /* PLX8748 */
+ plat_slot->perst_reg = 0x80;
+ plat_slot->perst_bit = 0x0400;
+ slot->ops.freset = firenze_pci_slot_perst;
+ break;
+ }
+ }
+ }
+
+ /*
+ * Anyway, the slot has platform specific info. That
+ * requires platform specific method to parse it out
+ * properly.
+ */
+ slot->ops.add_properties = lxvpd_add_slot_properties;
+}
+
+void firenze_pci_setup_phb(struct phb *phb, unsigned int index)
+{
+ uint32_t hub_id;
+ struct pci_slot *slot;
+ struct lxvpd_pci_slot *s;
+
+ /* Grab Hub ID used to parse VPDs */
+ hub_id = dt_prop_get_u32_def(phb->dt_node, "ibm,hub-id", 0);
+
+ /* Process the pcie slot entries from the lx vpd lid */
+ lxvpd_process_slot_entries(phb, dt_root, hub_id,
+ index, sizeof(struct firenze_pci_slot));
+
+ /* Fixup PHB3 slot */
+ slot = phb->slot;
+ s = slot ? lxvpd_get_slot(slot) : NULL;
+ if (s) {
+ lxvpd_extract_info(slot, s);
+ firenze_pci_slot_init(slot);
+ }
+}
+
+void firenze_pci_get_slot_info(struct phb *phb, struct pci_device *pd)
+{
+ struct pci_slot *slot;
+ struct lxvpd_pci_slot *s;
+
+ /* Prepare the PCI inventory */
+ firenze_pci_add_inventory(phb, pd);
+
+ if (pd->dev_type != PCIE_TYPE_ROOT_PORT &&
+ pd->dev_type != PCIE_TYPE_SWITCH_UPPORT &&
+ pd->dev_type != PCIE_TYPE_SWITCH_DNPORT &&
+ pd->dev_type != PCIE_TYPE_PCIE_TO_PCIX)
+ return;
+
+ /* Create PCIe slot */
+ slot = pcie_slot_create(phb, pd);
+ if (!slot)
+ return;
+
+ /* Root complex inherits methods from PHB slot */
+ if (!pd->parent && phb->slot)
+ memcpy(&slot->ops, &phb->slot->ops, sizeof(struct pci_slot_ops));
+
+ /* Patch PCIe slot */
+ s = lxvpd_get_slot(slot);
+ if (s) {
+ lxvpd_extract_info(slot, s);
+ firenze_pci_slot_init(slot);
+ }
+}
diff --git a/platforms/ibm-fsp/firenze.c b/platforms/ibm-fsp/firenze.c
index 1b087fb..bd122a7 100644
--- a/platforms/ibm-fsp/firenze.c
+++ b/platforms/ibm-fsp/firenze.c
@@ -26,29 +26,6 @@
#include "ibm-fsp.h"
#include "lxvpd.h"
-/* Structure used to send PCIe card info to FSP */
-struct fsp_pcie_entry {
- uint32_t hw_proc_id;
- uint16_t slot_idx;
- uint16_t reserved;
- uint16_t vendor_id;
- uint16_t device_id;
- uint16_t subsys_vendor_id;
- uint16_t subsys_device_id;
-};
-
-struct fsp_pcie_inventory {
- uint32_t version; /* currently 1 */
- uint32_t num_entries;
- uint32_t entry_size;
- uint32_t entry_offset;
- struct fsp_pcie_entry entries[];
-};
-
-static struct fsp_pcie_inventory *fsp_pcie_inv;
-static unsigned int fsp_pcie_inv_alloc_count;
-#define FSP_PCIE_INV_ALLOC_CHUNK 4
-
static struct dt_node *dt_create_i2c_master(struct dt_node *n, uint32_t eng_id)
{
struct dt_node *i2cm;
@@ -213,181 +190,6 @@ static bool firenze_probe(void)
return true;
}
-static void firenze_send_pci_inventory(void)
-{
- uint64_t base, abase, end, aend, offset;
- int64_t rc;
-
- if (!fsp_pcie_inv)
- return;
-
- prlog(PR_DEBUG, "PLAT: Sending PCI inventory to FSP, table has"
- " %d entries\n",
- fsp_pcie_inv->num_entries);
-
- {
- unsigned int i;
-
- prlog(PR_DEBUG, "HWP SLT VDID DVID SVID SDID\n");
- prlog(PR_DEBUG, "---------------------------\n");
- for (i = 0; i < fsp_pcie_inv->num_entries; i++) {
- struct fsp_pcie_entry *e = &fsp_pcie_inv->entries[i];
-
- prlog(PR_DEBUG, "%03d %03d %04x %04x %04x %04x\n",
- e->hw_proc_id, e->slot_idx,
- e->vendor_id, e->device_id,
- e->subsys_vendor_id, e->subsys_device_id);
- }
- }
-
- /*
- * Get the location of the table in a form we can send
- * to the FSP
- */
- base = (uint64_t)fsp_pcie_inv;
- end = base + sizeof(struct fsp_pcie_inventory) +
- fsp_pcie_inv->num_entries * fsp_pcie_inv->entry_size;
- abase = base & ~0xffful;
- aend = (end + 0xffful) & ~0xffful;
- offset = PSI_DMA_PCIE_INVENTORY + (base & 0xfff);
-
- /* We can only accomodate so many entries in the PSI map */
- if ((aend - abase) > PSI_DMA_PCIE_INVENTORY_SIZE) {
- prerror("PLAT: PCIe inventory too large (%lld bytes)\n",
- aend - abase);
- goto bail;
- }
-
- /* Map this in the TCEs */
- fsp_tce_map(PSI_DMA_PCIE_INVENTORY, (void *)abase, aend - abase);
-
- /* Send FSP message */
- rc = fsp_sync_msg(fsp_mkmsg(FSP_CMD_PCI_POWER_CONF, 3,
- hi32(offset), lo32(offset),
- end - base), true);
- if (rc)
- prerror("PLAT: FSP error %lld sending inventory\n", rc);
-
- /* Unmap */
- fsp_tce_unmap(PSI_DMA_PCIE_INVENTORY, aend - abase);
- bail:
- /*
- * We free the inventory. We'll have to redo that on hotplug
- * when we support it but that isn't the case yet
- */
- free(fsp_pcie_inv);
- fsp_pcie_inv = NULL;
-}
-
-static void firenze_add_pcidev_to_fsp_inventory(struct phb *phb,
- struct pci_device *pd)
-{
- struct fsp_pcie_entry *entry;
- struct proc_chip *chip;
-
- /* Check if we need to do some (Re)allocation */
- if (!fsp_pcie_inv ||
- fsp_pcie_inv->num_entries == fsp_pcie_inv_alloc_count) {
- unsigned int new_count;
- size_t new_size;
- bool need_init = !fsp_pcie_inv;
-
- /* (Re)allocate the block to the new size */
- new_count = fsp_pcie_inv_alloc_count + FSP_PCIE_INV_ALLOC_CHUNK;
- new_size = sizeof(struct fsp_pcie_inventory);
- new_size += sizeof(struct fsp_pcie_entry) * new_count;
- fsp_pcie_inv = realloc(fsp_pcie_inv, new_size);
- fsp_pcie_inv_alloc_count = new_count;
-
- /* Initialize the header for a new inventory */
- if (need_init) {
- fsp_pcie_inv->version = 1;
- fsp_pcie_inv->num_entries = 0;
- fsp_pcie_inv->entry_size =
- sizeof(struct fsp_pcie_entry);
- fsp_pcie_inv->entry_offset =
- offsetof(struct fsp_pcie_inventory, entries);
- }
- }
-
- /* Add entry */
- entry = &fsp_pcie_inv->entries[fsp_pcie_inv->num_entries++];
- chip = get_chip(dt_get_chip_id(phb->dt_node));
- if (!chip) {
- prerror("PLAT: Failed to get chip for PHB !\n");
- return;
- }
- entry->hw_proc_id = chip->pcid;
- entry->slot_idx = pd->parent->slot_info->slot_index;
- entry->reserved = 0;
- pci_cfg_read16(phb, pd->bdfn, PCI_CFG_VENDOR_ID, &entry->vendor_id);
- pci_cfg_read16(phb, pd->bdfn, PCI_CFG_DEVICE_ID, &entry->device_id);
- if (pd->is_bridge) {
- int64_t ssvc = pci_find_cap(phb, pd->bdfn,
- PCI_CFG_CAP_ID_SUBSYS_VID);
- if (ssvc < 0) {
- entry->subsys_vendor_id = 0xffff;
- entry->subsys_device_id = 0xffff;
- } else {
- pci_cfg_read16(phb, pd->bdfn,
- ssvc + PCICAP_SUBSYS_VID_VENDOR,
- &entry->subsys_vendor_id);
- pci_cfg_read16(phb, pd->bdfn,
- ssvc + PCICAP_SUBSYS_VID_DEVICE,
- &entry->subsys_device_id);
- }
- } else {
- pci_cfg_read16(phb, pd->bdfn, PCI_CFG_SUBSYS_VENDOR_ID,
- &entry->subsys_vendor_id);
- pci_cfg_read16(phb, pd->bdfn, PCI_CFG_SUBSYS_ID,
- &entry->subsys_device_id);
- }
-}
-
-static void firenze_get_slot_info(struct phb *phb, struct pci_device * pd)
-{
- /* Call the main LXVPD function first */
- lxvpd_get_slot_info(phb, pd);
-
- /*
- * Do we need to add that to the FSP inventory for power management ?
- *
- * For now, we only add devices that:
- *
- * - Are function 0
- * - Are not an RC or a downstream bridge
- * - Have a direct parent that has a slot entry
- * - Slot entry says pluggable
- * - Aren't an upstream switch that has slot info
- */
- if (!pd || !pd->parent)
- return;
- if (pd->bdfn & 7)
- return;
- if (pd->dev_type == PCIE_TYPE_ROOT_PORT ||
- pd->dev_type == PCIE_TYPE_SWITCH_DNPORT)
- return;
- if (pd->dev_type == PCIE_TYPE_SWITCH_UPPORT &&
- pd->slot_info)
- return;
- if (!pd->parent->slot_info)
- return;
- if (!pd->parent->slot_info->pluggable)
- return;
- firenze_add_pcidev_to_fsp_inventory(phb, pd);
-}
-
-static void firenze_setup_phb(struct phb *phb, unsigned int index)
-{
- uint32_t hub_id;
-
- /* Grab Hub ID used to parse VPDs */
- hub_id = dt_prop_get_u32_def(phb->dt_node, "ibm,hub-id", 0);
-
- /* Process the pcie slot entries from the lx vpd lid */
- lxvpd_process_slot_entries(phb, dt_root, hub_id, index);
-}
-
static uint32_t ibm_fsp_occ_timeout(void)
{
/* Use a fixed 60s value for now */
@@ -411,9 +213,9 @@ DECLARE_PLATFORM(firenze) = {
.exit = ibm_fsp_exit,
.cec_power_down = ibm_fsp_cec_power_down,
.cec_reboot = ibm_fsp_cec_reboot,
- .pci_setup_phb = firenze_setup_phb,
- .pci_get_slot_info = firenze_get_slot_info,
- .pci_probe_complete = firenze_send_pci_inventory,
+ .pci_setup_phb = firenze_pci_setup_phb,
+ .pci_get_slot_info = firenze_pci_get_slot_info,
+ .pci_probe_complete = firenze_pci_send_inventory,
.nvram_info = fsp_nvram_info,
.nvram_start_read = fsp_nvram_start_read,
.nvram_write = fsp_nvram_write,
@@ -422,4 +224,4 @@ DECLARE_PLATFORM(firenze) = {
.start_preload_resource = fsp_start_preload_resource,
.resource_loaded = fsp_resource_loaded,
.sensor_read = ibm_fsp_sensor_read,
-} ;
+};
diff --git a/platforms/ibm-fsp/ibm-fsp.h b/platforms/ibm-fsp/ibm-fsp.h
index 3b24b5b..3f6e9c5 100644
--- a/platforms/ibm-fsp/ibm-fsp.h
+++ b/platforms/ibm-fsp/ibm-fsp.h
@@ -30,4 +30,17 @@ extern int elog_fsp_commit(struct errorlog *buf);
extern int64_t ibm_fsp_sensor_read(uint32_t sensor_hndl, int token,
uint32_t *sensor_data);
+/* Apollo PCI support */
+extern void apollo_pci_setup_phb(struct phb *phb,
+ unsigned int index);
+extern void apollo_pci_get_slot_info(struct phb *phb,
+ struct pci_device *pd);
+
+/* Firenze PCI support */
+extern void firenze_pci_send_inventory(void);
+extern void firenze_pci_setup_phb(struct phb *phb,
+ unsigned int index);
+extern void firenze_pci_get_slot_info(struct phb *phb,
+ struct pci_device *pd);
+
#endif /* __IBM_FSP_COMMON_H */
diff --git a/platforms/ibm-fsp/lxvpd.c b/platforms/ibm-fsp/lxvpd.c
index 7ab4776..74c69aa 100644
--- a/platforms/ibm-fsp/lxvpd.c
+++ b/platforms/ibm-fsp/lxvpd.c
@@ -13,11 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-/*
- * LXVPD support
- *
- */
-
#include <skiboot.h>
#include <device.h>
@@ -28,24 +23,40 @@
#include "lxvpd.h"
+/* Debugging options */
+#define LXVPD_DBG(fmt, a...) \
+ prlog(PR_DEBUG, "LXVPD: " fmt, ##a)
+#define LXVPD_INFO(fmt, a...) \
+ prlog(PR_INFO, "LXVPD: " fmt, ##a)
+#define LXVPD_WARN(fmt, a...) \
+ prlog(PR_WARNING, "LXVPD: " fmt, ##a)
+#define LXVPD_ERR(fmt, a...) \
+ prlog(PR_ERR, "LXVPD: " fmt, ##a)
+
/*
- * XXX TODO: Add 1006 maps to add function loc codes and loc code maps
- * (ie. -Tn part of the location code)
+ * Currently, the lxvpd PCI slot struct is shared by multiple
+ * platforms (Apollo and Firenze), but each slot still has
+ * platform specific features. In order for unified data structs,
+ * "struct lxvpd_slot" is expected to be embedded in platform
+ * PCI slot struct. "entry_size" indicates the size of platform
+ * specific PCI slot instance.
*/
-struct lxvpd_slot_info_data {
- uint8_t num_slots;
- struct pci_slot_info info[];
+struct lxvpd_pci_slot_data {
+ uint8_t num_slots;
+ int32_t entry_size; /* Size of platform PCI slot */
+ void *slots; /* Data of platform PCI slots */
};
-static bool lxvpd_supported_slot(struct phb *phb, struct pci_device *pd)
+static bool lxvpd_supported_slot(struct phb *phb,
+ struct pci_device *pd)
{
- /* PCI/PCI-X we only support top level PHB with NULL "pd" */
- if (phb->phb_type < phb_type_pcie_v1)
- return pd == NULL;
+ /* PHB should always be valid */
+ if (!phb)
+ return false;
- /* Now we have PCI Express, we should never have a NULL "pd" */
+ /* We expect platform slot for root complex */
if (!pd)
- return false;
+ return true;
/* We support the root complex at the top level */
if (pd->dev_type == PCIE_TYPE_ROOT_PORT && !pd->parent)
@@ -71,174 +82,216 @@ static bool lxvpd_supported_slot(struct phb *phb, struct pci_device *pd)
return false;
}
-void lxvpd_get_slot_info(struct phb *phb, struct pci_device * pd)
+void *lxvpd_get_slot(struct pci_slot *slot)
{
- struct lxvpd_slot_info_data *sdata = phb->platform_data;
+ struct phb *phb = slot->phb;
+ struct pci_device *pd = slot->pd;
+ struct lxvpd_pci_slot_data *sdata = phb->platform_data;
+ struct lxvpd_pci_slot *s = NULL;
+ uint8_t slot_num = pd ? ((pd->bdfn >> 3) & 0x1f) : 0xff;
bool is_phb = (pd && pd->parent) ? false : true;
- bool entry_found = false;
- uint8_t idx;
+ uint8_t index;
- /* Check if we have slot info */
- if (!sdata)
- return;
+ /* No attached data to the PHB ? */
+ if (!sdata) {
+ LXVPD_DBG("PHB%04x not have VPD data\n",
+ phb->opal_id);
+ return NULL;
+ }
+
+ /* Platform slot attached ? */
+ s = slot->data;
+ if (s) {
+ LXVPD_DBG("Slot %016llx had platform data [%s]\n",
+ slot->id, s->label);
+ return s;
+ }
- prlog(PR_TRACE, "LXVPD: Get Slot Info PHB%d pd=%x\n", phb->opal_id,
- pd ? pd->bdfn : 0);
/*
- * This code only handles PHBs and PCIe switches at the top level.
- *
- * We do not handle any other switch nor any other type of PCI/PCI-X
- * bridge.
- */
+ * This code only handles PHBs and PCIe switches at the
+ * top level. We do not handle any other switch nor any
+ * other type of PCI/PCI-X bridge. Generally, we have
+ * more strict rules to support slot than PCI core.
+ */
if (!lxvpd_supported_slot(phb, pd)) {
- prlog(PR_TRACE, "LXVPD: Unsupported slot\n");
- return;
+ LXVPD_DBG("Slot %016llx not supported\n",
+ slot->id);
+ return NULL;
}
- /* Iterate the slot map */
- for (idx = 0; idx <= sdata->num_slots; idx++) {
- struct pci_slot_info *info = &sdata->info[idx];
- uint8_t pd_dev = (pd->bdfn >> 3) & 0x1f;
+ /* Iterate the platform slot array */
+ for (index = 0; index < sdata->num_slots; index++) {
+ s = sdata->slots + (index * sdata->entry_size);
/* Match PHB with switch_id == 0 */
- if (is_phb && info->switch_id == 0) {
- entry_found = true;
- break;
+ if (is_phb && s->switch_id == 0) {
+ slot->data = s;
+ s->pci_slot = slot;
+ LXVPD_DBG("Found [%s] for PHB slot %016llx\n",
+ s->label, slot->id);
+
+ return s;
}
/* Match switch port with switch_id != 0 */
- if (!is_phb && info->switch_id !=0 && info->dev_id == pd_dev) {
- entry_found = true;
- break;
+ if (!is_phb && s->switch_id != 0 && s->dev_id == slot_num) {
+ slot->data = s;
+ s->pci_slot = slot;
+ LXVPD_DBG("Found [%s] for slot %016llx\n",
+ s->label, slot->id);
+
+ return s;
}
}
- if (entry_found) {
- pd->slot_info = &sdata->info[idx];
- prlog(PR_TRACE, "PCI: PCIE Slot Info: \n"
- " Label %s\n"
- " Pluggable 0x%x\n"
- " Power Ctl 0x%x\n"
- " Wired Lanes 0x%x\n"
- " Bus Clock 0x%x\n"
- " Connector 0x%x\n"
- " Slot Index %d\n",
- pd->slot_info->label,
- pd->slot_info->pluggable?1:0,
- pd->slot_info->power_ctl?1:0,
- pd->slot_info->wired_lanes,
- pd->slot_info->bus_clock,
- pd->slot_info->connector_type,
- pd->slot_info->slot_index);
- } else {
- prlog(PR_TRACE, "PCI: PCIE Slot Info Not Found\n");
- }
+ LXVPD_DBG("No data found for %sslot %016llx\n",
+ (is_phb ? "PHB " : " "), slot->id);
+ return NULL;
}
-static struct pci_slot_info *lxvpd_alloc_slot_info(struct phb *phb, int count)
+void lxvpd_extract_info(struct pci_slot *slot,
+ struct lxvpd_pci_slot *s)
{
- struct lxvpd_slot_info_data *data;
+ slot->pluggable = s->pluggable ? 1 : 0;
+ slot->power_ctl = s->power_ctl ? 1 : 0;
+ slot->power_led_ctl = s->pwr_led_ctl;
+ slot->attn_led_ctl = s->attn_led_ctl;
+ slot->connector_type = s->connector_type;
+ slot->card_desc = s->card_desc;
+ slot->card_mech = s->card_mech;
+ slot->wired_lanes = s->wired_lanes;
+}
- data = zalloc(sizeof(struct lxvpd_slot_info_data) *
- count * sizeof(struct pci_slot_info));
- assert(data);
- data->num_slots = count;
- phb->platform_data = data;
+static struct lxvpd_pci_slot_data *lxvpd_alloc_slots(struct phb *phb,
+ uint8_t count,
+ uint32_t slot_size)
+{
+ struct lxvpd_pci_slot_data *sdata;
- return data->info;
+ sdata = zalloc(sizeof(struct lxvpd_pci_slot_data) + count * slot_size);
+ assert(sdata);
+ sdata->num_slots = count;
+ sdata->entry_size = slot_size;
+ sdata->slots = sdata + 1;
+ phb->platform_data = sdata;
+
+ return sdata;
}
-static void lxvpd_parse_1004_map(struct phb *phb, const uint8_t *sm, uint8_t sz)
+static void lxvpd_format_label(char *dst, const char *src, size_t len)
{
- const struct pci_slot_entry_1004 *entry = NULL;
- struct pci_slot_info *slot_info, *info;
- uint8_t num_slots, slot, idx;
+ int i;
- num_slots = (sz / sizeof(struct pci_slot_entry_1004));
- slot_info = lxvpd_alloc_slot_info(phb, num_slots);
+ memcpy(dst, src, len);
+
+ /* Remove blank suffix */
+ for (i = strlen(dst) - 1; i >= 0; i--) {
+ if (dst[i] != ' ')
+ break;
+
+ dst[i] = 0;
+ }
+}
+
+static void lxvpd_parse_1004_map(struct phb *phb,
+ const uint8_t *sm,
+ uint8_t size,
+ uint32_t slot_size)
+{
+ struct lxvpd_pci_slot_data *sdata;
+ struct lxvpd_pci_slot *s;
+ const struct pci_slot_entry_1004 *entry;
+ uint8_t num_slots, slot;
+
+ num_slots = (size / sizeof(struct pci_slot_entry_1004));
+ sdata = lxvpd_alloc_slots(phb, num_slots, slot_size);
/* Iterate thru the entries in the keyword */
entry = (const struct pci_slot_entry_1004 *)sm;
- for (slot = 0; slot < num_slots; slot++) {
- info = &slot_info[slot];
+ for (slot = 0; slot < num_slots; slot++, entry++) {
+ s = sdata->slots + slot * sdata->entry_size;
+
+ /* Figure out PCI slot info */
+ lxvpd_format_label(s->label, entry->label, 3);
+ s->slot_index = entry->slot_index;
+ s->switch_id = entry->pba >> 4;
+ s->vswitch_id = entry->pba & 0xf;
+ s->dev_id = entry->sba;
+ s->pluggable = ((entry->p0.byte & 0x20) == 0);
+ s->power_ctl = ((entry->p0.power_ctl & 0x40) == 1);
+ s->bus_clock = entry->p2.bus_clock - 4;
+ s->connector_type = entry->p2.connector_type - 5;
+ s->card_desc = entry->p3.byte >> 6;
+ if (entry->p3.byte < 0xc0)
+ s->card_desc -= 4;
+ s->card_mech = (entry->p3.byte >> 4) & 0x3;
+ s->pwr_led_ctl = (entry->p3.byte & 0xf) >> 2;
+ s->attn_led_ctl = entry->p3.byte & 0x3;
- /* Put slot info into pci device structure */
- info->switch_id = entry->pba >> 4;
- info->vswitch_id = entry->pba &0xf;
- info->dev_id = entry->sba;
- for (idx = 0; idx < 3; idx++)
- info->label[idx] = entry->label[idx];
- info->label[3] = 0;
- info->pluggable = ((entry->p0.byte & 0x20) == 0);
- info->power_ctl = ((entry->p0.power_ctl & 0x40) == 1);
switch(entry->p1.wired_lanes) {
- case 1: info->wired_lanes = PCI_SLOT_WIRED_LANES_PCIX_32; break;
+ case 1: s->wired_lanes = PCI_SLOT_WIRED_LANES_PCIX_32; break;
case 2: /* fall through */
- case 3: info->wired_lanes = PCI_SLOT_WIRED_LANES_PCIX_64; break;
- case 4: info->wired_lanes = PCI_SLOT_WIRED_LANES_PCIE_X1; break;
- case 5: info->wired_lanes = PCI_SLOT_WIRED_LANES_PCIE_X4; break;
- case 6: info->wired_lanes = PCI_SLOT_WIRED_LANES_PCIE_X8; break;
- case 7: info->wired_lanes = PCI_SLOT_WIRED_LANES_PCIE_X16; break;
+ case 3: s->wired_lanes = PCI_SLOT_WIRED_LANES_PCIX_64; break;
+ case 4: s->wired_lanes = PCI_SLOT_WIRED_LANES_PCIE_X1; break;
+ case 5: s->wired_lanes = PCI_SLOT_WIRED_LANES_PCIE_X4; break;
+ case 6: s->wired_lanes = PCI_SLOT_WIRED_LANES_PCIE_X8; break;
+ case 7: s->wired_lanes = PCI_SLOT_WIRED_LANES_PCIE_X16; break;
default:
- info->wired_lanes = PCI_SLOT_WIRED_LANES_UNKNOWN;
+ s->wired_lanes = PCI_SLOT_WIRED_LANES_UNKNOWN;
}
- info->wired_lanes = (entry->p1.wired_lanes - 3);
- info->bus_clock = (entry->p2.bus_clock - 4);
- info->connector_type = (entry->p2.connector_type - 5);
- if (entry->p3.byte < 0xC0)
- info->card_desc = ((entry->p3.byte >> 6) - 4) ;
- else
- info->card_desc = (entry->p3.byte >> 6);
- info->card_mech = ((entry->p3.byte >> 4) & 0x3);
- info->pwr_led_ctl = ((entry->p3.byte & 0xF) >> 2);
- info->attn_led_ctl = (entry->p3.byte & 0x3);
- info->slot_index = entry->slot_index;
- entry++;
+
+ LXVPD_DBG("1004 Platform data [%s] %02x %02x on PHB%04x\n",
+ s->label, s->switch_id, s->dev_id, phb->opal_id);
}
}
-static void lxvpd_parse_1005_map(struct phb *phb, const uint8_t *sm, uint8_t sz)
+static void lxvpd_parse_1005_map(struct phb *phb,
+ const uint8_t *sm,
+ uint8_t size,
+ uint32_t slot_size)
{
- const struct pci_slot_entry_1005 *entry = NULL;
- struct pci_slot_info *slot_info, *info;
- uint8_t num_slots, slot, idx;
+ struct lxvpd_pci_slot_data *sdata;
+ struct lxvpd_pci_slot *s;
+ const struct pci_slot_entry_1005 *entry;
+ uint8_t num_slots, slot;
- num_slots = (sz / sizeof(struct pci_slot_entry_1005));
- slot_info = lxvpd_alloc_slot_info(phb, num_slots);
+ num_slots = (size / sizeof(struct pci_slot_entry_1005));
+ sdata = lxvpd_alloc_slots(phb, num_slots, slot_size);
/* Iterate thru the entries in the keyword */
entry = (const struct pci_slot_entry_1005 *)sm;
- for (slot = 0; slot < num_slots; slot++) {
- info = &slot_info[slot];
+ for (slot = 0; slot < num_slots; slot++, entry++) {
+ s = sdata->slots + slot * sdata->entry_size;
/* Put slot info into pci device structure */
- info->switch_id = entry->pba >> 4;
- info->vswitch_id = entry->pba &0xf;
- info->dev_id = entry->switch_device_id;
- for (idx = 0; idx < 8; idx++)
- info->label[idx] = entry->label[idx];
- info->label[8] = 0;
- info->pluggable = (entry->p0.pluggable == 0);
- info->power_ctl = entry->p0.power_ctl;
- info->wired_lanes = entry->p1.wired_lanes;
- if (info->wired_lanes > PCI_SLOT_WIRED_LANES_PCIE_X32)
- info->wired_lanes = PCI_SLOT_WIRED_LANES_UNKNOWN;
- info->bus_clock = entry->p2.bus_clock;
- info->connector_type = entry->p2.connector_type;
- info->card_desc = (entry->p3.byte >> 6);
- info->card_mech = ((entry->p3.byte >> 4) & 0x3);
- info->pwr_led_ctl = ((entry->p3.byte & 0xF) >> 2);
- info->attn_led_ctl = (entry->p3.byte & 0x3);
- info->slot_index = entry->slot_index;
- entry++;
+ lxvpd_format_label(s->label, entry->label, 8);
+ s->slot_index = entry->slot_index;
+ s->switch_id = entry->pba >> 4;
+ s->vswitch_id = entry->pba & 0xf;
+ s->dev_id = entry->switch_device_id;
+ s->pluggable = (entry->p0.pluggable == 0);
+ s->power_ctl = entry->p0.power_ctl;
+ s->bus_clock = entry->p2.bus_clock;
+ s->connector_type = entry->p2.connector_type;
+ s->card_desc = entry->p3.byte >> 6;
+ s->card_mech = (entry->p3.byte >> 4) & 0x3;
+ s->pwr_led_ctl = (entry->p3.byte & 0xf) >> 2;
+ s->attn_led_ctl = entry->p3.byte & 0x3;
+ s->wired_lanes = entry->p1.wired_lanes;
+ if (s->wired_lanes > PCI_SLOT_WIRED_LANES_PCIE_X32)
+ s->wired_lanes = PCI_SLOT_WIRED_LANES_UNKNOWN;
+
+ LXVPD_DBG("1005 Platform data [%s] %02x %02x on PHB%04x\n",
+ s->label, s->switch_id, s->dev_id, phb->opal_id);
}
}
void lxvpd_process_slot_entries(struct phb *phb,
struct dt_node *node,
uint8_t chip_id,
- uint8_t index)
+ uint8_t index,
+ uint32_t slot_size)
{
const void *lxvpd;
const uint8_t *pr_rec, *pr_end, *sm;
@@ -255,24 +308,23 @@ void lxvpd_process_slot_entries(struct phb *phb,
/* Get LX VPD pointer */
lxvpd = dt_prop_get_def_size(node, "ibm,io-vpd", NULL, &lxvpd_size);
if (!lxvpd) {
- printf("LXVPD: LX VPD not found for %s in %p\n",
- record, phb->dt_node);
+ LXVPD_WARN("No data found for PHB%04x %s\n",
+ phb->opal_id, record);
return;
}
pr_rec = vpd_find_record(lxvpd, lxvpd_size, record, &pr_size);
if (!pr_rec) {
- printf("LXVPD: %s record not found in LX VPD in %p\n",
- record, phb->dt_node);
+ LXVPD_WARN("Record %s not found on PHB%04x\n",
+ record, phb->opal_id);
return;
}
- pr_end = pr_rec + pr_size;
-
- prlog(PR_TRACE, "LXVPD: %s record for PHB%d is %ld bytes\n",
- record, phb->opal_id, pr_size);
- /* As long as there's still something in the PRxy record... */
- while(pr_rec < pr_end) {
+ /* As long as there's still something in the PRxy record */
+ LXVPD_DBG("PHB%04x record %s has %ld bytes\n",
+ phb->opal_id, record, pr_size);
+ pr_end = pr_rec + pr_size;
+ while (pr_rec < pr_end) {
pr_size = pr_end - pr_rec;
/* Find the next MF keyword */
@@ -281,24 +333,56 @@ void lxvpd_process_slot_entries(struct phb *phb,
sm = vpd_find_keyword(pr_rec, pr_size, "SM", &sm_sz);
if (!mf || !sm) {
if (!found)
- printf("LXVPD: Slot Map keyword %s not found\n",
- record);
+ LXVPD_WARN("Slot Map keyword %s not found\n",
+ record);
return;
}
- prlog(PR_TRACE, "LXVPD: Found 0x%04x map...\n", *mf);
+ LXVPD_DBG("Found 0x%04x map...\n", *mf);
switch (*mf) {
case 0x1004:
- lxvpd_parse_1004_map(phb, sm + 1, sm_sz - 1);
+ lxvpd_parse_1004_map(phb, sm + 1, sm_sz - 1, slot_size);
found = true;
break;
case 0x1005:
- lxvpd_parse_1005_map(phb, sm + 1, sm_sz - 1);
+ lxvpd_parse_1005_map(phb, sm + 1, sm_sz - 1, slot_size);
found = true;
break;
/* Add support for 0x1006 maps ... */
}
+
pr_rec = sm + sm_sz;
}
}
+void lxvpd_add_slot_properties(struct pci_slot *slot,
+ struct dt_node *np)
+{
+ struct phb *phb = slot->phb;
+ struct lxvpd_pci_slot *s = slot->data;
+ char loc_code[LOC_CODE_SIZE];
+ size_t base_loc_code_len, slot_label_len;
+
+ /* Check if we have platform specific slot */
+ if (!s || !np)
+ return;
+
+ /* Check PHB base location code */
+ if (!phb->base_loc_code)
+ return;
+
+ /* Check location length is valid */
+ base_loc_code_len = strlen(phb->base_loc_code);
+ slot_label_len = strlen(s->label);
+ if ((base_loc_code_len + slot_label_len + 1) >= LOC_CODE_SIZE)
+ return;
+
+ /* Location code */
+ strcpy(loc_code, phb->base_loc_code);
+ strcat(loc_code, "-");
+ strcat(loc_code, s->label);
+ dt_add_property(np, "ibm,slot-location-code",
+ loc_code, strlen(loc_code) + 1);
+ dt_add_property_string(np, "ibm,slot-label",
+ s->label);
+}
diff --git a/platforms/ibm-fsp/lxvpd.h b/platforms/ibm-fsp/lxvpd.h
index dbb9513..c7ca21b 100644
--- a/platforms/ibm-fsp/lxvpd.h
+++ b/platforms/ibm-fsp/lxvpd.h
@@ -23,8 +23,6 @@
#define LX_VPD_1S4U_BACKPLANE 0x3100040100300043ull
#define LX_VPD_2S4U_BACKPLANE 0x3100040100300044ull
-/* P8 PCI Slot Entry Definitions -- 1005 */
-
struct slot_p0 {
union {
uint8_t byte;
@@ -83,6 +81,7 @@ struct pci_slot_entry_1004 {
uint8_t max_slot_power;
};
+/* P8 PCI Slot Entry Definitions -- 1005 */
struct pci_slot_entry_1005 {
union {
uint8_t pba;
@@ -106,12 +105,30 @@ struct pci_slot_entry_1005 {
uint8_t rsvd_22[2];
};
-struct phb;
-
-extern void lxvpd_process_slot_entries(struct phb *phb,
- struct dt_node *node,
- uint8_t chip_id, uint8_t index);
-
-extern void lxvpd_get_slot_info(struct phb *phb, struct pci_device * pd);
+struct lxvpd_pci_slot {
+ struct pci_slot *pci_slot;
+ uint8_t switch_id;
+ uint8_t vswitch_id;
+ uint8_t dev_id;
+ char label[9];
+ bool pluggable;
+ bool power_ctl;
+ uint8_t wired_lanes;
+ uint8_t bus_clock;
+ uint8_t connector_type;
+ uint8_t card_desc;
+ uint8_t card_mech;
+ uint8_t pwr_led_ctl;
+ uint8_t attn_led_ctl;
+ uint8_t slot_index;
+};
+extern void lxvpd_process_slot_entries(struct phb *phb, struct dt_node *node,
+ uint8_t chip_id, uint8_t index,
+ uint32_t slot_size);
+extern void *lxvpd_get_slot(struct pci_slot *slot);
+extern void lxvpd_extract_info(struct pci_slot *slot,
+ struct lxvpd_pci_slot *s);
+extern void lxvpd_add_slot_properties(struct pci_slot *slot,
+ struct dt_node *np);
#endif /* __LXVPD_H */
--
2.1.0
More information about the Skiboot
mailing list