[Skiboot] [PATCH 5/6] zz: Rework PCI slots definition and hotplug control

Frederic Barrat fbarrat at linux.ibm.com
Mon Oct 18 23:37:50 AEDT 2021


The ZZ platform reuses most of the P8, fsp-based system infrastructure
for PCI slot definitions (firenze-pci, lxvpd). However the PCI hotplug
controller hardware is different. We got lucky as the i2c bus
definitions used on firenze were invalid on ZZ, so PCI hotplug control
errors out nicely, as opposed to sending random i2c requests to an
unsuspecting device.

This patch defines an interface to the hotplug controller used on
ZZ. It also provides the updated i2c information for each PCI slot.

Signed-off-by: Frederic Barrat <fbarrat at linux.ibm.com>
---
 platforms/ibm-fsp/firenze-pci.c |   5 +-
 platforms/ibm-fsp/ibm-fsp.h     |   1 +
 platforms/ibm-fsp/zz.c          | 189 ++++++++++++++++++++++++++++++++
 3 files changed, 194 insertions(+), 1 deletion(-)

diff --git a/platforms/ibm-fsp/firenze-pci.c b/platforms/ibm-fsp/firenze-pci.c
index 5fbbc2ff..08467632 100644
--- a/platforms/ibm-fsp/firenze-pci.c
+++ b/platforms/ibm-fsp/firenze-pci.c
@@ -969,7 +969,10 @@ void firenze_pci_get_slot_info(struct phb *phb, struct pci_device *pd)
 	s = lxvpd_get_slot(slot);
 	if (s) {
 		lxvpd_extract_info(slot, s);
-		firenze_pci_slot_init(slot);
+		if (proc_gen == proc_gen_p9)
+			zz_pci_slot_init(slot);
+		else
+			firenze_pci_slot_init(slot);
 	}
 }
 
diff --git a/platforms/ibm-fsp/ibm-fsp.h b/platforms/ibm-fsp/ibm-fsp.h
index bb191645..b5b61b56 100644
--- a/platforms/ibm-fsp/ibm-fsp.h
+++ b/platforms/ibm-fsp/ibm-fsp.h
@@ -31,6 +31,7 @@ extern void firenze_pci_get_slot_info(struct phb *phb,
 				      struct pci_device *pd);
 extern void firenze_pci_add_loc_code(struct dt_node *np,
 				      struct pci_device *pd);
+void zz_pci_slot_init(struct pci_slot *slot);
 
 /* VPD support */
 void vpd_iohub_load(struct dt_node *hub_node);
diff --git a/platforms/ibm-fsp/zz.c b/platforms/ibm-fsp/zz.c
index 493d6030..8ce3e936 100644
--- a/platforms/ibm-fsp/zz.c
+++ b/platforms/ibm-fsp/zz.c
@@ -6,6 +6,7 @@
 #include <fsp.h>
 #include <pci.h>
 #include <pci-cfg.h>
+#include <pci-slot.h>
 #include <chip.h>
 #include <i2c.h>
 #include <timebase.h>
@@ -141,6 +142,193 @@ static void add_opencapi_dt_nodes(void)
 	}
 }
 
+#define UCD_GPIO_SELECT 0xFA
+#define UCD_GPIO_CONFIG 0xFB
+
+struct hp_controller {
+	struct lock lock;
+	struct i2c_request req;
+};
+
+struct hp_controller ucd;
+
+struct zz_pci_slot_info {
+	uint8_t		index;
+	const char	*label;
+	uint8_t		slave_addr;
+	uint8_t		enable_port;
+	uint8_t		pgood_port;
+};
+
+static struct zz_pci_slot_info zz_pci_slots[] = {
+	/* UCD 9090-0 */
+	{ 0x06,  "C2", 0xC8, 0, 18 },
+	{ 0x07,  "C3", 0xC8, 4, 22 },
+	{ 0x08,  "C4", 0xC8, 5, 15 },
+	{ 0x09,  "C5", 0xC8, 6, 16 },
+	{ 0x0A,  "C6", 0xC8, 7, 17 },
+	/* UCD 90120A-0 */
+	{ 0x0B,  "C7", 0xD0, 0, 18 },
+	{ 0x0C,  "C8", 0xD0, 1, 19 },
+	{ 0x0D,  "C9", 0xD0, 4, 20 },
+	{ 0x0E, "C10", 0xD0, 5, 21 },
+	{ 0x0F, "C11", 0xD0, 6, 24 },
+	{ 0x10, "C12", 0xD0, 7, 25 },
+};
+
+static struct zz_pci_slot_info *zz_get_slot_info(struct lxvpd_pci_slot *s)
+{
+	for (int i = 0; i < ARRAY_SIZE(zz_pci_slots); i++) {
+		if (zz_pci_slots[i].index == s->slot_index &&
+		    !strcmp(zz_pci_slots[i].label, s->label)) {
+			return &zz_pci_slots[i];
+		}
+	}
+	return NULL;
+}
+
+static void hp_controller_init(struct hp_controller *hpc)
+{
+	init_lock(&hpc->lock);
+	hpc->req.offset = 0;
+	hpc->req.offset_bytes = 1;
+	hpc->req.rw_len = 1;
+	hpc->req.timeout = 100;
+	hpc->req.bus = p8_i2c_find_bus_by_port(0, 2, 1);
+	if (!hpc->req.bus) {
+		prerror("PLAT: Unable to find PCI power controller I2C bus\n");
+		return;
+	}
+}
+
+static int64_t __ucd_op(struct hp_controller *hpc, enum i2c_operation op,
+			uint32_t addr, uint8_t port, uint8_t *state)
+{
+	int64_t rc;
+
+	if (!hpc->req.bus)
+		return OPAL_HARDWARE;
+
+	hpc->req.op = SMBUS_WRITE;
+	hpc->req.dev_addr = addr >> 1;
+	hpc->req.offset = UCD_GPIO_SELECT;
+	hpc->req.rw_buf = &port;
+	rc = i2c_request_sync(&hpc->req);
+	if (rc)
+		return rc;
+
+	hpc->req.op = op;
+	hpc->req.dev_addr = addr >> 1;
+	hpc->req.offset = UCD_GPIO_CONFIG;
+	hpc->req.rw_buf = state;
+	return i2c_request_sync(&hpc->req);
+}
+
+static int64_t ucd_read(struct hp_controller *hpc, uint32_t addr,
+			uint8_t port, uint8_t *state)
+{
+	return __ucd_op(hpc, SMBUS_READ, addr, port, state);
+}
+
+static int64_t ucd_write(struct hp_controller *hpc, uint32_t addr,
+			 uint8_t port, uint8_t state)
+{
+	return __ucd_op(hpc, SMBUS_WRITE, addr, port, &state);
+}
+
+static int64_t hp_controller_read_state(struct hp_controller *hpc,
+					struct zz_pci_slot_info *info,
+					uint8_t *val)
+{
+	uint8_t enable, pgood;
+	int64_t rc;
+
+	lock(&hpc->lock);
+
+	rc = ucd_read(hpc, info->slave_addr, info->pgood_port, &pgood);
+	if (rc)
+		goto unlock_err;
+
+	rc = ucd_read(hpc, info->slave_addr, info->enable_port, &enable);
+	if (rc)
+		goto unlock_err;
+
+	unlock(&hpc->lock);
+
+	*val = ((enable & 0xE) == 0xE) && ((pgood & 0xE) == 0x8);
+	return OPAL_SUCCESS;
+
+unlock_err:
+	unlock(&hpc->lock);
+	prerror("PLAT: Can't read the state of PCI slot %s, rc=%lld\n",
+		info->label, rc);
+	return rc;
+}
+
+static int64_t hp_controller_write_state(struct hp_controller *hpc,
+					 struct zz_pci_slot_info *info,
+					 uint8_t val)
+{
+	uint8_t enable;
+	int64_t rc;
+
+	if (val == PCI_SLOT_POWER_ON)
+		enable = 0x7;
+	else
+		enable = 0x3;
+
+	lock(&hpc->lock);
+	rc = ucd_write(hpc, info->slave_addr, info->enable_port, enable);
+	unlock(&hpc->lock);
+	return rc;
+}
+
+static int64_t zz_pci_slot_get_power_state(struct pci_slot *slot, uint8_t *val)
+{
+	struct lxvpd_pci_slot *s = slot->data;
+	struct zz_pci_slot_info *info = NULL;
+
+	info = zz_get_slot_info(s);
+	if (!info)
+		return OPAL_PARAMETER;
+
+	return hp_controller_read_state(&ucd, info, val);
+}
+
+static int64_t zz_pci_slot_set_power_state(struct pci_slot *slot,
+					   uint8_t val)
+{
+	struct lxvpd_pci_slot *s = slot->data;
+	struct zz_pci_slot_info *info = NULL;
+
+	if (val != PCI_SLOT_POWER_OFF && val != PCI_SLOT_POWER_ON)
+		return OPAL_PARAMETER;
+
+	info = zz_get_slot_info(s);
+	if (!info)
+		return OPAL_PARAMETER;
+
+	slot->power_state = val;
+	return hp_controller_write_state(&ucd, info, val);
+}
+
+void zz_pci_slot_init(struct pci_slot *slot)
+{
+	struct lxvpd_pci_slot *s = slot->data;
+	struct zz_pci_slot_info *info;
+
+	slot->ops.add_properties = lxvpd_add_slot_properties;
+
+	info = zz_get_slot_info(s);
+	if (!info)
+		return; /* Slot doesn't support power management */
+
+	prlog(PR_INFO, "PLAT: PCI slot %s supports power management\n",
+	      info->label);
+	slot->ops.get_power_state = zz_pci_slot_get_power_state;
+	slot->ops.set_power_state = zz_pci_slot_set_power_state;
+}
+
 static bool zz_probe(void)
 {
 	/* FIXME: make this neater when the dust settles */
@@ -179,6 +367,7 @@ static void zz_init(void)
 {
 	ibm_fsp_init();
 	hservice_fsp_init();
+	hp_controller_init(&ucd);
 }
 
 DECLARE_PLATFORM(zz) = {
-- 
2.31.1



More information about the Skiboot mailing list