diff options
author | Oliver O'Halloran <oohall@gmail.com> | 2017-09-15 15:40:57 +1000 |
---|---|---|
committer | Stewart Smith <stewart@linux.vnet.ibm.com> | 2017-09-15 02:49:27 -0500 |
commit | ad484081ef8a51811e7902aec436fa8f1ca9604a (patch) | |
tree | 7480682af82e81aff8fcb20006574daf95c1a353 | |
parent | 26c19bb8967b9d5b95181e1e615fc23574dad431 (diff) | |
download | skiboot-ad484081ef8a51811e7902aec436fa8f1ca9604a.zip skiboot-ad484081ef8a51811e7902aec436fa8f1ca9604a.tar.gz skiboot-ad484081ef8a51811e7902aec436fa8f1ca9604a.tar.bz2 |
hdata: Parse IOSLOT information
Add structure definitions that describe the physical PCIe topology of
a system and parse them into the device-tree based PCIe slot
description.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
-rw-r--r-- | hdata/iohub.c | 382 | ||||
-rw-r--r-- | hdata/spira.h | 88 |
2 files changed, 470 insertions, 0 deletions
diff --git a/hdata/iohub.c b/hdata/iohub.c index 79a6b07..ecfc6de 100644 --- a/hdata/iohub.c +++ b/hdata/iohub.c @@ -25,6 +25,7 @@ #include <p7ioc.h> #include <vpd.h> #include <inttypes.h> +#include <string.h> #include "hdata.h" @@ -453,6 +454,378 @@ static void io_add_p8_cec_vpd(const struct HDIF_common_hdr *sp_iohubs) io_get_lx_info(kwvpd, kwvpd_sz, 0, dt_root); } +/* + * Assumptions: + * + * a) the IOSLOT index is the hub ID -CHECK + * + */ + +static struct dt_node *dt_slots; + +static void add_i2c_link(struct dt_node *node, const char *link_name, + u32 i2c_link) +{ + /* FIXME: Do something not shit */ + dt_add_property_cells(node, link_name, i2c_link); +} + +/* + * the root of the slots node has #address-cells = 2, <hub-index, phb-index> + * can we ditch hub-index? + */ + + +static const struct slot_map_details *find_slot_details( + const struct HDIF_common_hdr *ioslot, int entry) +{ + const struct slot_map_details *details = NULL; + const struct HDIF_array_hdr *arr; + unsigned int i; + + arr = HDIF_get_iarray(ioslot, IOSLOT_IDATA_DETAILS, NULL); + HDIF_iarray_for_each(arr, i, details) + if (be16_to_cpu(details->entry) == entry) + break; + + return details; +} + +static void parse_slot_details(struct dt_node *slot, + const struct slot_map_details *details) +{ + u32 slot_caps; + + /* + * generic slot options + */ + + dt_add_property_cells(slot, "max-power", + be16_to_cpu(details->max_power)); + + if (details->perst_ctl_type == SLOT_PERST_PHB_OR_SW) + dt_add_property(slot, "pci-perst", NULL, 0); + else if (details->perst_ctl_type == SLOT_PERST_SW_GPIO) + dt_add_property_cells(slot, "gpio-perst", details->perst_gpio); + + if (details->presence_det_type == SLOT_PRESENCE_PCI) + dt_add_property(slot, "pci-presence-detect", NULL, 0); + + /* + * specific slot capabilities + */ + slot_caps = be32_to_cpu(details->slot_caps); + + if (slot_caps & SLOT_CAP_LSI) + dt_add_property(slot, "lsi", NULL, 0); + + if (slot_caps & SLOT_CAP_CAPI) { + /* XXX: should we be more specific here? + * + * Also we should double check that this slot + * is a root connected slot. + */ + dt_add_property(slot, "capi", NULL, 0); + } + + if (slot_caps & SLOT_CAP_CCARD) { + dt_add_property(slot, "cable-card", NULL, 0); + + if (details->presence_det_type == SLOT_PRESENCE_I2C) + add_i2c_link(slot, "i2c-presence-detect", + be32_to_cpu(details->i2c_cable_card)); + } + + if (slot_caps & SLOT_CAP_HOTPLUG) { + dt_add_property(slot, "hotplug", NULL, 0); + + /* + * Power control should only exist when the slot is hotplug + * capable + */ + if (details->power_ctrl_type == SLOT_PWR_I2C) + add_i2c_link(slot, "i2c-power-ctrl", + be32_to_cpu(details->i2c_power_ctl)); + } + + /* + * NB: Additional NVLink specific info is added to this node + * when the SMP Link structures are parsed later on. + */ + if (slot_caps & SLOT_CAP_NVLINK) + dt_add_property(slot, "nvlink", NULL, 0); +} + +static struct dt_node *find_slot_entry_node(struct dt_node *root, u32 entry_id) +{ + struct dt_node *node; + + for (node = dt_first(root); node; node = dt_next(root, node)) { + if (!dt_has_node_property(node, DT_PRIVATE "entry_id", NULL)) + continue; + + if (dt_prop_get_u32(node, DT_PRIVATE "entry_id") == entry_id) + return node; + } + + return NULL; +} + +/* + * The internal HDAT representation of the various types of slot is kinda + * dumb, translate it into something more sensible + */ +enum slot_types { + st_root, + st_slot, + st_rc_slot, + st_sw_upstream, + st_sw_downstream, + st_builtin +}; + +static const char *st_name(enum slot_types type) +{ + switch(type) { + case st_root: return "root-complex"; + case st_slot: return "pluggable"; + case st_rc_slot: return "pluggable"; /* differentiate? */ + case st_sw_upstream: return "switch-up"; + case st_sw_downstream: return "switch-down"; + case st_builtin: return "builtin"; + } + + return "(none)"; +} + +static enum slot_types xlate_type(uint8_t type, u32 features) +{ + bool is_slot = features & SLOT_FEAT_SLOT; + + switch (type) { + case SLOT_TYPE_ROOT_COMPLEX: + return is_slot ? st_rc_slot : st_root; + case SLOT_TYPE_BUILTIN: + return st_builtin; + case SLOT_TYPE_SWITCH_UP: + return st_sw_upstream; + case SLOT_TYPE_SWITCH_DOWN: + return is_slot ? st_slot : st_sw_downstream; + } + + return -1; /* shouldn't happen */ +} + +static bool is_port(struct dt_node *n) +{ + //return dt_node_is_compatible(n, "compatible", "ibm,pcie-port"); + return dt_node_is_compatible(n, "ibm,pcie-port"); +} + +/* this only works inside parse_one_ioslot() */ +#define SM_LOG(level, fmt, ...) \ + prlog(level, "SLOTMAP: %x:%d:%d " \ + fmt, /* user input */ \ + chip_id, entry->phb_index, eid, \ + ##__VA_ARGS__ /* user args */) + +#define SM_ERR(fmt, ...) SM_LOG(PR_ERR, fmt, ##__VA_ARGS__) +#define SM_DBG(fmt, ...) SM_LOG(PR_DEBUG, fmt, ##__VA_ARGS__) + +static void parse_one_slot(const struct slot_map_entry *entry, + const struct slot_map_details *details, int chip_id) +{ + struct dt_node *node, *parent = NULL; + u16 eid, pid, vid, did; + u32 flags; + int type; + + flags = be32_to_cpu(entry->features); + type = xlate_type(entry->type, flags); + + eid = be16_to_cpu(entry->entry_id); + pid = be16_to_cpu(entry->parent_id); + + SM_DBG("%s - eid = %d, pid = %d, name = %8s\n", + st_name(type), eid, pid, + strnlen(entry->name, 8) ? entry->name : ""); + + /* empty slot, ignore it */ + if (eid == 0x0 && pid == 0x0) + return; + + if (type != st_root && type != st_rc_slot) { + parent = find_slot_entry_node(dt_slots, pid); + if (!parent) { + SM_ERR("Unable to find node for parent slot (id = %d)\n", + pid); + return; + } + } + + switch (type) { + case st_root: + case st_rc_slot: + node = dt_new_2addr(dt_slots, "root-complex", + chip_id, entry->phb_index); + dt_add_property_cells(node, "reg", chip_id, entry->phb_index); + dt_add_property_cells(node, "#address-cells", 2); + dt_add_property_cells(node, "#size-cells", 0); + dt_add_property_strings(node, "compatible", + "ibm,pcie-port", "ibm,pcie-root-port"); + dt_add_property_cells(node, "ibm,chip-id", chip_id); + parent = node; + + /* + * The representation of slots attached directly to the + * root complex is a bit wierd. If this is just a root + * complex then stop here, otherwise fall through to create + * the slot node. + */ + if (type == st_root) + break; + + /* fallthrough*/ + case st_sw_upstream: + case st_builtin: + case st_slot: + if (!is_port(parent)) { + SM_ERR("%s connected to %s (%d), should be %s or %s!\n", + st_name(type), parent->name, pid, + st_name(st_root), st_name(st_sw_downstream)); + return; + } + + vid = (be32_to_cpu(entry->vendor_id) & 0xffff); + did = (be32_to_cpu(entry->device_id) & 0xffff); + + prlog(PR_DEBUG, "Found %s slot with %x:%x\n", + st_name(type), vid, did); + + /* The VID:DID is only meaningful for builtins and switches */ + if (vid && did) { + node = dt_new_2addr(parent, st_name(type), vid, did); + dt_add_property_cells(node, "reg", vid, did); + } else { + /* + * If we get no vdid then create a "wildcard" slot + * that matches any device + */ + node = dt_new(parent, st_name(type)); + } + + if (type == st_sw_upstream) { + dt_add_property_cells(node, "#address-cells", 1); + dt_add_property_cells(node, "#size-cells", 0); + dt_add_property_cells(node, "upstream-port", + entry->up_port); + } + break; + + case st_sw_downstream: /* slot connected to switch output */ + node = dt_new_addr(parent, "down-port", entry->down_port); + dt_add_property_strings(node, "compatible", + "ibm,pcie-port"); + dt_add_property_cells(node, "reg", entry->down_port); + + break; + + default: + SM_ERR("Unknown slot map type %x\n", entry->type); + return; + } + + /* + * Now add any generic slot map properties. + */ + + /* private since we don't want hdat stuff leaking */ + dt_add_property_cells(node, DT_PRIVATE "entry_id", eid); + + if (entry->mrw_slot_id) + dt_add_property_cells(node, "mrw-slot-id", + be16_to_cpu(entry->mrw_slot_id)); + + if (entry->lane_mask) + dt_add_property_cells(node, "lane-mask", + be16_to_cpu(entry->lane_mask)); + + /* what is the difference between this and the lane reverse? */ + if (entry->lane_reverse) + dt_add_property_cells(node, "lanes-reversed", + be16_to_cpu(entry->lane_reverse)); + + if (strnlen(entry->name, sizeof(entry->name))) + dt_add_property_nstr(node, "slot-name", + entry->name, sizeof(entry->name)); + if (entry->type == st_slot || entry->type == st_rc_slot) + dt_add_property(node, "ibm,pluggable", NULL, 0); + + if (details) + parse_slot_details(node, details); +} + +/* + * Under the IOHUB structure we have and idata array describing + * the PHBs under each chip. The IOHUB structure also has a child + * array called IOSLOT which describes slot map. The i`th element + * of the IOSLOT array corresponds to the slot map of the i`th + * element of the iohubs idata array. + * + * Probably. + * + * Furthermore, arrayarrayarrayarrayarray. + */ + +static struct dt_node *get_slot_node(void) +{ + struct dt_node *slots = dt_find_by_name(dt_root, "ibm,pcie-slots"); + + if (!slots) { + slots = dt_new(dt_root, "ibm,pcie-slots"); + dt_add_property_cells(slots, "#address-cells", 2); + dt_add_property_cells(slots, "#size-cells", 0); + } + + return slots; +} + +static void io_parse_slots(const void *sp_iohubs, int hub_id) +{ + const struct HDIF_child_ptr *ioslot_arr; + const struct HDIF_array_hdr *entry_arr; + const struct HDIF_common_hdr *ioslot; + const struct slot_map_entry *entry; + unsigned int i, count; + + dt_slots = get_slot_node(); + + ioslot_arr = HDIF_child_arr(sp_iohubs, CECHUB_CHILD_IOSLOTS); + if (!ioslot_arr) + return; + + count = be32_to_cpu(ioslot_arr->count); /* should only be 1 */ + if (!count) + return; + + prlog(PR_DEBUG, "CEC: Found slot map for IOHUB %d\n", hub_id); + if (count > 1) + prerror("CEC: Multiple IOSLOTs found for IO HUB %d\n", hub_id); + + ioslot = HDIF_child(sp_iohubs, ioslot_arr, 0, "IOSLOT"); + if (!ioslot) + return; + + entry_arr = HDIF_get_iarray(ioslot, IOSLOT_IDATA_SLOTMAP, NULL); + HDIF_iarray_for_each(entry_arr, i, entry) { + const struct slot_map_details *details; + + details = find_slot_details(ioslot, + be16_to_cpu(entry->entry_id)); + parse_one_slot(entry, details, hub_id); + } +} + static void io_parse_fru(const void *sp_iohubs) { unsigned int i; @@ -471,6 +844,10 @@ static void io_parse_fru(const void *sp_iohubs) for (i = 0; i < count; i++) { const struct cechub_io_hub *hub; unsigned int size, hub_id; + uint32_t chip_id; + + if(i > 0) + break; hub = HDIF_get_iarray_item(sp_iohubs, CECHUB_FRU_IO_HUBS, i, &size); @@ -534,6 +911,11 @@ static void io_parse_fru(const void *sp_iohubs) hub_id); hn = NULL; } + + chip_id = pcid_to_chip_id(be32_to_cpu(hub->proc_chip_id)); + + /* parse the slot map if we have one */ + io_parse_slots(sp_iohubs, chip_id); } /* On P8, grab the CEC VPD */ diff --git a/hdata/spira.h b/hdata/spira.h index 0276d4a..8e8c74e 100644 --- a/hdata/spira.h +++ b/hdata/spira.h @@ -564,6 +564,7 @@ struct msvpd_hb_reserved_mem { */ #define CECHUB_FRU_HDIF_SIG "IO HUB" #define IOKID_FRU_HDIF_SIG "IO KID" +#define IOSLOT_FRU_HDIF_SIG "IOSLOT" /* Idata index 0: FRU ID data * @@ -700,6 +701,93 @@ struct cechub_io_hub { /* Child index 0: IO Daugther Card */ #define CECHUB_CHILD_IO_KIDS 0 +/* Child index 1: PCIe Slot Mapping Information */ +#define CECHUB_CHILD_IOSLOTS 1 + +#define IOSLOT_IDATA_SLOTMAP 0 + +struct slot_map_entry { + __be16 entry_id; + __be16 parent_id; + uint8_t phb_index; /* only valid for ROOT and SWITCH_UP */ + + uint8_t type; +#define SLOT_TYPE_ROOT_COMPLEX 0x0 +#define SLOT_TYPE_SWITCH_UP 0x1 +#define SLOT_TYPE_SWITCH_DOWN 0x2 +#define SLOT_TYPE_BUILTIN 0x3 + + uint8_t lane_swapped; + uint8_t reserved; + __be16 lane_mask; + __be16 lane_reverse; + + /* what can I do with this? reference something under/vpd/ ? */ + __be16 slca_idx; + + __be16 mrw_slot_id; + + __be32 features; +#define SLOT_FEAT_SLOT 0x1 + + uint8_t up_port; + uint8_t down_port; /* the switch port this device is attached to */ + + __be32 vendor_id; + __be32 device_id; + __be32 sub_vendor_id; + __be32 sub_device_id; + char name[8]; +} __packed; + +#define IOSLOT_IDATA_DETAILS 1 + +struct slot_map_details { + __be16 entry; + + /* Phyp junk, ignore */ + uint8_t mgc_load_source; + uint8_t hddw_order; + __be16 mmio_size_32; /* In MB */ + __be16 mmio_size_64; + __be16 dma_size_32; + __be16 dma_size_64; + + uint8_t power_ctrl_type; /* slot power control source */ +#define SLOT_PWR_NONE 0x0 +#define SLOT_PWR_I2C 0x1 + + uint8_t presence_det_type; /* slot presence detect source */ +#define SLOT_PRESENCE_NONE 0x0 +#define SLOT_PRESENCE_PCI 0x1 +#define SLOT_PRESENCE_I2C 0x2 + + uint8_t perst_ctl_type; /* slot PERST source */ +#define SLOT_PERST_NONE 0x0 +#define SLOT_PERST_PHB_OR_SW 0x1 +#define SLOT_PERST_SW_GPIO 0x2 + uint8_t perst_gpio; + + __be16 max_power; /* in W? */ + + __be32 slot_caps; +#define SLOT_CAP_LSI 0x01 /* phyp junk? */ +#define SLOT_CAP_CAPI 0x02 +#define SLOT_CAP_CCARD 0x04 +#define SLOT_CAP_HOTPLUG 0x08 +#define SLOT_CAP_SRIOV 0x10 /* phyp junk */ +#define SLOT_CAP_ELLOCO 0x20 /* why is this seperate from the nvlink cap? */ +#define SLOT_CAP_NVLINK 0x30 + + __be16 reserved1; + + /* I2C Link IDs */ + __be32 i2c_power_ctl; + __be32 i2c_pgood; + __be32 i2c_cable_card; /* opencapi presence detect? */ + __be32 i2c_mex_fpga; +}; + /* * IO KID is a dauther card structure */ |