[Skiboot] [PATCH 09/10] hdat/i2c: Rework i2c device creation
Oliver O'Halloran
oohall at gmail.com
Mon Oct 12 13:53:13 AEDT 2020
We've got functions to instantiate I2C buses at various places inside of
the skiboot code base (in hdat, firenze-pci, and in witherspoon). The
HDAT ones are the most generic so re-work those a bit and export the
functions used to add DT nodes for I2C masters and the ports below them.
Signed-off-by: Oliver O'Halloran <oohall at gmail.com>
---
hdata/i2c.c | 171 ++++++++++++++++++++++++++++++++------------------
include/i2c.h | 5 ++
2 files changed, 114 insertions(+), 62 deletions(-)
diff --git a/hdata/i2c.c b/hdata/i2c.c
index a2fe8ac1b994..9f95e6146205 100644
--- a/hdata/i2c.c
+++ b/hdata/i2c.c
@@ -7,86 +7,128 @@
#include <interrupts.h>
#include <ccan/str/str.h>
#include <chip.h>
+#include <i2c.h>
#include "spira.h"
#include "hdata.h"
-struct i2c_dev {
- uint8_t i2cm_engine;
- uint8_t i2cm_port;
- __be16 i2c_bus_freq;
+/*
+ * These should probably be in hw/p8-i2c.c. However, that would require the HDAT
+ * test to #include hw/p8-i2c.c which is probably going to be more trouble than
+ * it's worth. So these helpers are here instead.
+ */
+struct dt_node *p8_i2c_add_master_node(struct dt_node *xscom, int eng_id)
+{
+ uint64_t clk, size, xscom_base;
+ struct dt_node *i2cm;
- /* i2c slave info */
- uint8_t type;
- uint8_t dev_addr;
- uint8_t dev_port;
- uint8_t __reserved;
+ dt_for_each_compatible(xscom, i2cm, "ibm,power8-i2cm")
+ if (dt_prop_get_u32(i2cm, "chip-engine#") == eng_id)
+ return i2cm;
- __be32 purpose;
- __be32 i2c_link;
- __be16 slca_index;
-};
+ /* XXX: Might need to be updated for new chips */
+ if (proc_gen >= proc_gen_p9)
+ size = 0x1000;
+ else
+ size = 0x20;
-#define P9_I2CM_XSCOM_SIZE 0x1000
-#define P9_I2CM_XSCOM_BASE 0xa0000
+ xscom_base = 0xa0000 + size * eng_id;
-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 * (uint64_t)engine;
- struct dt_node *i2cm;
- uint64_t freq, clock;
-
- 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);
-
- freq = dt_prop_get_u64_def(xscom, "bus-frequency", 0);
- clock = (u32)(freq / 4);
- if (clock)
- dt_add_property_cells(i2cm, "clock-frequency", clock);
- else
- dt_add_property_cells(i2cm, "clock-frequency", 150000000);
+ i2cm = dt_new_addr(xscom, "i2cm", xscom_base);
+ if (!i2cm)
+ return NULL;
+
+ if (proc_gen >= proc_gen_p9) {
+ dt_add_property_strings(i2cm, "compatible", "ibm,power8-i2cm",
+ "ibm,power9-i2cm");
+ } else {
+ dt_add_property_strings(i2cm, "compatible", "ibm,power8-i2cm");
}
+ dt_add_property_cells(i2cm, "reg", xscom_base, size);
+ dt_add_property_cells(i2cm, "#size-cells", 0);
+ dt_add_property_cells(i2cm, "#address-cells", 1);
+ dt_add_property_cells(i2cm, "chip-engine#", eng_id);
+
+ /*
+ * The i2cm runs at 1/4th the PIB frequency. If we don't know the PIB
+ * frequency then pick 150MHz which should be in the right ballpark.
+ */
+ clk = dt_prop_get_u64_def(xscom, "bus-frequency", 0);
+ if (clk)
+ dt_add_property_cells(i2cm, "clock-frequency", clk / 4);
+ else
+ 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 *__p8_i2c_add_port_node(struct dt_node *master, int port_id,
+ uint32_t bus_speed)
{
- struct dt_node *bus;
+ struct dt_node *port;
+ uint32_t speed;
+
+ dt_for_each_child(master, port)
+ if (dt_prop_get_u32(port, "reg") == port_id)
+ goto check_speed;
- 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);
+ port = dt_new_addr(master, "i2c-bus", port_id);
+ if (!port)
+ return NULL;
- /* The P9 I2C master is fully compatible with the P8 one */
- dt_add_property_strings(bus, "compatible", "ibm,opal-i2c",
+ dt_add_property_cells(port, "reg", port_id);
+ dt_add_property_cells(port, "#size-cells", 0);
+ dt_add_property_cells(port, "#address-cells", 1);
+
+ /* The P9 I2C master is fully compatible with the P8 one */
+ if (proc_gen >= proc_gen_p9) {
+ dt_add_property_strings(port, "compatible", "ibm,opal-i2c",
"ibm,power8-i2c-port", "ibm,power9-i2c-port");
+ } else {
+ dt_add_property_strings(port, "compatible", "ibm,opal-i2c",
+ "ibm,power8-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 * 1000);
+check_speed:
+ speed = dt_prop_get_u32_def(port, "bus-frequency", 0xffffffff);
+ if (bus_speed < speed) {
+ dt_check_del_prop(port, "bus-frequency");
+ dt_add_property_cells(port, "bus-frequency", bus_speed);
}
- return bus;
+ return port;
+}
+
+
+struct dt_node *p8_i2c_add_port_node(struct dt_node *xscom, int eng_id,
+ int port_id, uint32_t bus_freq)
+{
+ struct dt_node *i2cm;
+
+ i2cm = p8_i2c_add_master_node(xscom, eng_id);
+ if (!i2cm)
+ return NULL;
+
+ return __p8_i2c_add_port_node(i2cm, port_id, bus_freq);
}
+struct i2c_dev {
+ uint8_t i2cm_engine;
+ uint8_t i2cm_port;
+ __be16 i2c_bus_freq;
+
+ /* i2c slave info */
+ uint8_t type;
+ uint8_t dev_addr;
+ uint8_t dev_port;
+ uint8_t __reserved;
+
+ __be32 purpose;
+ __be32 i2c_link;
+ __be16 slca_index;
+};
+
struct hdat_i2c_type {
uint32_t id;
const char *name;
@@ -192,7 +234,7 @@ struct host_i2c_hdr {
int parse_i2c_devs(const struct HDIF_common_hdr *hdr, int idata_index,
struct dt_node *xscom)
{
- struct dt_node *i2cm, *bus, *node;
+ struct dt_node *bus, *node;
const struct hdat_i2c_type *type;
const struct hdat_i2c_info *info;
const struct i2c_dev *dev;
@@ -263,9 +305,14 @@ int parse_i2c_devs(const struct HDIF_common_hdr *hdr, int idata_index,
(proc_gen == proc_gen_p9 || proc_gen == proc_gen_p10))
continue;
- i2cm = get_i2cm_node(xscom, dev->i2cm_engine);
- bus = get_bus_node(i2cm, dev->i2cm_port,
- be16_to_cpu(dev->i2c_bus_freq));
+ bus = p8_i2c_add_port_node(xscom, dev->i2cm_engine, dev->i2cm_port,
+ be16_to_cpu(dev->i2c_bus_freq) * 1000);
+
+ if (!bus) {
+ prerror("Unable to add node for e%dp%d under %s\n",
+ dev->i2cm_engine, dev->i2cm_port, xscom->name);
+ continue;
+ }
/*
* Looks like hostboot gives the address as an 8 bit, left
diff --git a/include/i2c.h b/include/i2c.h
index 8ec32ad1c6f3..9ad307b3f445 100644
--- a/include/i2c.h
+++ b/include/i2c.h
@@ -63,6 +63,11 @@ extern struct i2c_bus *i2c_find_bus_by_id(uint32_t opal_id);
/* not generic, but useful */
struct i2c_bus *p8_i2c_find_bus_by_port(uint32_t chip_id, int eng, int port_id);
+struct dt_node *p8_i2c_add_master_node(struct dt_node *xscom, int eng_id);
+struct dt_node *__p8_i2c_add_port_node(struct dt_node *master, int port_id,
+ uint32_t bus_speed);
+struct dt_node *p8_i2c_add_port_node(struct dt_node *xscom, int eng_id,
+ int port_id, uint32_t bus_freq);
int64_t i2c_queue_req(struct i2c_request *req);
--
2.26.2
More information about the Skiboot
mailing list