[Skiboot] [PATCH 2/2] hdat: parse processor attached i2c devices

Oliver O'Halloran oohall at gmail.com
Thu Apr 6 14:01:58 AEST 2017


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 at gmail.com>
---
 hdata/Makefile.inc       |   2 +-
 hdata/hdata.h            |   2 +
 hdata/i2c.c              | 192 +++++++++++++++++++++++++++++++++++++++++++++++
 hdata/spira.c            |   4 +-
 hdata/spira.h            |   2 +
 hdata/test/hdata_to_dt.c |   1 +
 6 files changed, 201 insertions(+), 2 deletions(-)
 create mode 100644 hdata/i2c.c

diff --git a/hdata/Makefile.inc b/hdata/Makefile.inc
index 44f8c863ed6a..5b79dfee1454 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 53927a3acc5b..e3871860ae73 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 000000000000..127068fafdeb
--- /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 512784f105aa..3340a096f257 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 d10f1fa1d7cc..01adf8abfc1f 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 2f343e2d4bc9..717fc9af7298 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"
-- 
2.9.3



More information about the Skiboot mailing list