@@ -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);
}
}
@@ -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);
@@ -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) = {
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@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(-)