summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOliver O'Halloran <oohall@gmail.com>2017-04-06 14:01:58 +1000
committerStewart Smith <stewart@linux.vnet.ibm.com>2017-04-07 14:51:34 +1000
commitae4e50eb35695f9438b6b9544a02fc891c4dd20d (patch)
tree7a617c3a1dabc54836428b4aca1d0501d24a7006
parent510e2b066b172a4b46e18315ddac30721b5a7526 (diff)
downloadtalos-skiboot-ae4e50eb35695f9438b6b9544a02fc891c4dd20d.tar.gz
talos-skiboot-ae4e50eb35695f9438b6b9544a02fc891c4dd20d.zip
hdat: parse processor attached i2c devices
Adds basic parsing for i2c devices that are attached to the processor I2C interfaces. This is mainly VPD SEEPROMs. Signed-off-by: Oliver O'Halloran <oohall@gmail.com> Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
-rw-r--r--hdata/Makefile.inc2
-rw-r--r--hdata/hdata.h2
-rw-r--r--hdata/i2c.c192
-rw-r--r--hdata/spira.c4
-rw-r--r--hdata/spira.h2
-rw-r--r--hdata/test/hdata_to_dt.c1
6 files changed, 201 insertions, 2 deletions
diff --git a/hdata/Makefile.inc b/hdata/Makefile.inc
index 44f8c863..5b79dfee 100644
--- a/hdata/Makefile.inc
+++ b/hdata/Makefile.inc
@@ -2,7 +2,7 @@
SUBDIRS += hdata
HDATA_OBJS = spira.o paca.o pcia.o hdif.o memory.o fsp.o iohub.o vpd.o slca.o
-HDATA_OBJS += cpu-common.o vpd-common.o hostservices.o
+HDATA_OBJS += cpu-common.o vpd-common.o hostservices.o i2c.o
DEVSRC_OBJ = hdata/built-in.o
$(DEVSRC_OBJ): $(HDATA_OBJS:%=hdata/%)
diff --git a/hdata/hdata.h b/hdata/hdata.h
index 53927a3a..e3871860 100644
--- a/hdata/hdata.h
+++ b/hdata/hdata.h
@@ -47,6 +47,8 @@ extern void slca_vpd_add_loc_code(struct dt_node *node, uint16_t slca_index);
extern void slca_dt_add_sai_node(void);
extern bool hservices_from_hdat(const void *fdt, size_t size);
+int parse_i2c_devs(const struct HDIF_common_hdr *hdr, int idata_index,
+ struct dt_node *xscom);
#endif /* __HDATA_H */
diff --git a/hdata/i2c.c b/hdata/i2c.c
new file mode 100644
index 00000000..127068fa
--- /dev/null
+++ b/hdata/i2c.c
@@ -0,0 +1,192 @@
+#include <device.h>
+#include <cpu.h>
+#include <vpd.h>
+#include <interrupts.h>
+#include <ccan/str/str.h>
+#include <chip.h>
+
+#include "spira.h"
+#include "hdata.h"
+
+struct i2c_dev {
+ uint8_t i2cm_engine;
+ uint8_t i2cm_port;
+ __be16 i2c_bus_freq;
+
+ /* i2c slave info */
+ uint8_t type;
+ uint8_t i2c_addr;
+ uint8_t i2c_port;
+ uint8_t __reserved;
+
+ __be32 purpose;
+ __be32 i2c_link;
+ __be16 slca_index;
+};
+
+#define P9_I2CM_XSCOM_SIZE 0x1000
+#define P9_I2CM_XSCOM_BASE 0xa0000
+
+static struct dt_node *get_i2cm_node(struct dt_node *xscom, int engine)
+{
+ uint64_t xscom_base = P9_I2CM_XSCOM_BASE + P9_I2CM_XSCOM_SIZE * engine;
+ struct dt_node *i2cm;
+
+ i2cm = dt_find_by_name_addr(xscom, "i2cm", xscom_base);
+ if (!i2cm) {
+ i2cm = dt_new_addr(xscom, "i2cm", xscom_base);
+ dt_add_property_cells(i2cm, "reg", xscom_base,
+ P9_I2CM_XSCOM_SIZE);
+
+ dt_add_property_strings(i2cm, "compatible",
+ "ibm,power8-i2cm", "ibm,power9-i2cm");
+
+ dt_add_property_cells(i2cm, "#size-cells", 0);
+ dt_add_property_cells(i2cm, "#address-cells", 1);
+ dt_add_property_cells(i2cm, "chip-engine#", engine);
+
+ /* XXX: verify this */
+ dt_add_property_cells(i2cm, "clock-frequency", 150000000);
+ }
+
+ return i2cm;
+}
+
+static struct dt_node *get_bus_node(struct dt_node *i2cm, int port, int freq)
+{
+ struct dt_node *bus;
+
+ bus = dt_find_by_name_addr(i2cm, "i2c-bus", port);
+ if (!bus) {
+ bus = dt_new_addr(i2cm, "i2c-bus", port);
+ dt_add_property_cells(bus, "reg", port);
+ dt_add_property_cells(bus, "#size-cells", 0);
+ dt_add_property_cells(bus, "#address-cells", 1);
+
+ /* The P9 I2C master is fully compatible with the P8 one */
+ dt_add_property_strings(bus, "compatible", "ibm,opal-i2c",
+ "ibm,power8-i2c-port", "ibm,power9-i2c-port");
+
+ /*
+ * use the clock frequency as the bus frequency until we
+ * have actual devices on the bus. Adding a device will
+ * reduce the frequency to something that all devices
+ * can tolerate.
+ */
+ dt_add_property_cells(bus, "bus-frequency", freq);
+ }
+
+ return bus;
+}
+
+struct hdat_i2c_type {
+ uint32_t id;
+ const char *name;
+ const char *compat;
+};
+
+struct hdat_i2c_type hdat_i2c_devs[] = {
+ /* XXX: Please verify that all VPD EEPROMs are of this type */
+ { 0x2, "eeprom", "atmel,24c128" }
+};
+
+struct hdat_i2c_label {
+ uint32_t id;
+ const char *label;
+};
+
+struct hdat_i2c_label hdat_i2c_labels[] = {
+ { 0x1, "9551-led-controller" },
+ { 0x2, "seeprom" },
+ { 0x5, "module-vpd" },
+ { 0x6, "dimm SPD" },
+ { 0x7, "proc-vpd" },
+ { 0x8, "sbe-eeprom" },
+ { 0x9, "planar-vpd" }
+};
+
+/*
+ * this is pretty half-assed, to generate the labels properly we need to look
+ * up associated SLCA index and determine what kind of module the device is on
+ * and why
+ */
+static struct hdat_i2c_type *map_type(uint32_t type)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hdat_i2c_devs); i++)
+ if (hdat_i2c_devs[i].id == type)
+ return &hdat_i2c_devs[i];
+
+ return NULL;
+}
+
+static const char *map_label(uint32_t type)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hdat_i2c_labels); i++)
+ if (hdat_i2c_labels[i].id == type)
+ return hdat_i2c_labels[i].label;
+
+ return NULL;
+}
+
+int parse_i2c_devs(const struct HDIF_common_hdr *hdr, int idata_index,
+ struct dt_node *xscom)
+{
+ struct dt_node *i2cm, *bus, *node;
+ const struct hdat_i2c_type *type;
+ const struct i2c_dev *dev;
+ const char *label, *name, *compat;
+ uint32_t i2c_addr;
+ int i, count;
+
+ /*
+ * This code makes a few assumptions about XSCOM addrs, etc
+ * and will need updating for new processors
+ */
+ assert(proc_gen == proc_gen_p9);
+
+ count = HDIF_get_iarray_size(hdr, idata_index);
+ for (i = 0; i < count; i++) {
+ dev = HDIF_get_iarray_item(hdr, idata_index, i, NULL);
+
+ i2cm = get_i2cm_node(xscom, dev->i2cm_engine);
+ bus = get_bus_node(i2cm, dev->i2cm_port,
+ be16_to_cpu(dev->i2c_bus_freq));
+
+ /*
+ * Looks like hostboot gives the address as an 8 bit, left
+ * justified quantity (i.e it includes the R/W bit). So we need
+ * to strip it off to get an address linux can use.
+ */
+ i2c_addr = dev->i2c_addr >> 1;
+
+ prlog(PR_TRACE, "HDAT I2C: found e%dp%d - %x\n",
+ dev->i2cm_engine, dev->i2cm_port, i2c_addr);
+
+ type = map_type(dev->type);
+ label = map_label(be32_to_cpu(dev->purpose));
+ if (type) {
+ compat = type->compat;
+ name = type->name;
+ } else {
+ name = "unknown";
+ compat = NULL;
+ }
+
+ node = dt_new_addr(bus, name, i2c_addr);
+ dt_add_property_cells(node, "reg", i2c_addr);
+ dt_add_property_cells(node, "link-id",
+ be32_to_cpu(dev->i2c_link));
+ if (compat)
+ dt_add_property_string(node, "compatible", compat);
+ if (label)
+ dt_add_property_string(node, "label", label);
+
+ /* XXX: SLCA index? */
+ }
+
+ return 0;
+}
diff --git a/hdata/spira.c b/hdata/spira.c
index 512784f1..3340a096 100644
--- a/hdata/spira.c
+++ b/hdata/spira.c
@@ -468,8 +468,10 @@ static bool add_xscom_sppcrd(uint64_t xscom_base)
/* Add PSI Host bridge */
add_psihb_node(np);
- if (proc_gen >= proc_gen_p9)
+ if (proc_gen >= proc_gen_p9) {
add_xive_node(np);
+ parse_i2c_devs(hdif, SPPCRD_IDATA_HOST_I2C, np);
+ }
}
return i > 0;
diff --git a/hdata/spira.h b/hdata/spira.h
index d10f1fa1..01adf8ab 100644
--- a/hdata/spira.h
+++ b/hdata/spira.h
@@ -1046,6 +1046,8 @@ struct sppcrd_chip_tod {
/* Idata index 4 : Module VPD */
#define SPPCRD_IDATA_MODULE_VPD 4
+/* Idata index 5 : Chip attached I2C devices */
+#define SPPCRD_IDATA_HOST_I2C 5
/*
* Host Services Data.
diff --git a/hdata/test/hdata_to_dt.c b/hdata/test/hdata_to_dt.c
index 2f343e2d..717fc9af 100644
--- a/hdata/test/hdata_to_dt.c
+++ b/hdata/test/hdata_to_dt.c
@@ -106,6 +106,7 @@ static bool spira_check_ptr(const void *ptr, const char *file, unsigned int line
#include "../vpd-common.c"
#include "../slca.c"
#include "../hostservices.c"
+#include "../i2c.c"
#include "../../core/vpd.c"
#include "../../core/device.c"
#include "../../core/chip.c"
OpenPOWER on IntegriCloud