[Skiboot] [PATCH 10/10] hw/p8-i2c: Add buses at runtime

Oliver O'Halloran oohall at gmail.com
Mon Oct 12 13:53:14 AEDT 2020


When booting skiboot we initialise any I2C masters after probing the
platform, but before initialising the platform (or NVRAM). Due to this we
need to ensure that all the I2C masters we might want to use are in the DT
before the platform is initialised. This causes problems since we might
want to add another master (e.g. on witherspoon we might need to add a port
for the I2C OpenCAPI I2C bus).

It's possible to hack around this by adding a new node in the probe
function, but the probe function runs very early before many essential
services are initialised (e.g. LPC). We might want to add another I2C bus
after learning that we have a riser card installed and it's not possible to
do that until later in boot.

To accommodate those use cases we can add support for adding a new bus at
runtime.

Signed-off-by: Oliver O'Halloran <oohall at gmail.com>
---
 hw/p8-i2c.c   | 83 ++++++++++++++++++++++++++++++++++++++++++++++-----
 include/i2c.h |  3 ++
 2 files changed, 78 insertions(+), 8 deletions(-)

diff --git a/hw/p8-i2c.c b/hw/p8-i2c.c
index 4abbad8673b4..59f85b1dfc14 100644
--- a/hw/p8-i2c.c
+++ b/hw/p8-i2c.c
@@ -196,6 +196,7 @@ enum p8_i2c_master_type {
 };
 
 struct p8_i2c_master {
+	struct dt_node		*dt_node;
 	struct lock		lock;		/* Lock to guard the members */
 	enum p8_i2c_master_type	type;		/* P8 vs. Centaur */
 	uint64_t		start_time;	/* Request start time */
@@ -1441,10 +1442,10 @@ static void p8_i2c_add_bus_prop(struct p8_i2c_master_port *port)
 }
 
 static struct p8_i2c_master_port *p8_i2c_init_one_port(struct p8_i2c_master *m,
-				struct dt_node *n, uint64_t lb_freq)
+				struct dt_node *n)
 {
 	struct p8_i2c_master_port *port;
-	uint64_t def_timeout;
+	uint64_t def_timeout, lb_freq;
 	uint32_t speed, div;
 
 	port = zalloc(sizeof(*port));
@@ -1453,6 +1454,7 @@ static struct p8_i2c_master_port *p8_i2c_init_one_port(struct p8_i2c_master *m,
 
 	def_timeout = m->irq_ok ? I2C_TIMEOUT_IRQ_MS : I2C_TIMEOUT_POLL_MS;
 
+	lb_freq = dt_prop_get_u32_def(m->dt_node, "clock-frequency", 150000000);
 	speed = dt_prop_get_u32_def(n, "bus-frequency", 100000);
 	div = p8_i2c_get_bit_rate_divisor(lb_freq, speed);
 
@@ -1479,7 +1481,8 @@ static struct p8_i2c_master_port *p8_i2c_init_one_port(struct p8_i2c_master *m,
 	return port;
 }
 
-static void p8_i2c_init_one(struct dt_node *i2cm, enum p8_i2c_master_type type)
+static struct p8_i2c_master *p8_i2c_init_one(struct dt_node *i2cm,
+						enum p8_i2c_master_type type)
 {
 	struct p8_i2c_master *master;
 	struct list_head *chip_list;
@@ -1493,7 +1496,7 @@ static void p8_i2c_init_one(struct dt_node *i2cm, enum p8_i2c_master_type type)
 		log_simple_error(&e_info(OPAL_RC_I2C_INIT),
 				 "I2C: Failed to allocate master "
 				 "structure\n");
-		return;
+		return NULL;
 	}
 	master->type = type;
 
@@ -1505,6 +1508,7 @@ static void p8_i2c_init_one(struct dt_node *i2cm, enum p8_i2c_master_type type)
 	master->chip_id = dt_get_chip_id(i2cm);
 	master->engine_id = dt_prop_get_u32(i2cm, "chip-engine#");
 	master->xscom_base = dt_get_address(i2cm, 0, NULL);
+	master->dt_node = i2cm;
 	if (master->type == I2C_CENTAUR) {
 		struct centaur_chip *centaur = get_centaur(master->chip_id);
 		if (centaur == NULL) {
@@ -1512,7 +1516,7 @@ static void p8_i2c_init_one(struct dt_node *i2cm, enum p8_i2c_master_type type)
 					 "I2C: Failed to get centaur 0x%x ",
 					 master->chip_id);
 			free(master);
-			return;
+			return NULL;
 		}
 		chip_list = &centaur->i2cms;
 
@@ -1522,7 +1526,7 @@ static void p8_i2c_init_one(struct dt_node *i2cm, enum p8_i2c_master_type type)
 		if (master->engine_id > 0) {
 			prlog(PR_ERR, "I2C: Skipping Centaur Master #1\n");
 			free(master);
-			return;
+			return NULL;
 		}
 	} else {
 		struct proc_chip *chip = get_chip(master->chip_id);
@@ -1560,7 +1564,7 @@ static void p8_i2c_init_one(struct dt_node *i2cm, enum p8_i2c_master_type type)
 			centaur_enable_sensor_cache(master->chip_id);
 
 		free(master);
-		return;
+		return NULL;
 	}
 
 	master->fifo_size = GETFIELD(I2C_EXTD_STAT_FIFO_SIZE, ex_stat);
@@ -1576,7 +1580,9 @@ static void p8_i2c_init_one(struct dt_node *i2cm, enum p8_i2c_master_type type)
 
 	/* initialise ports */
 	dt_for_each_child(i2cm, i2cm_port)
-		p8_i2c_init_one_port(master, i2cm_port, lb_freq);
+		p8_i2c_init_one_port(master, i2cm_port);
+
+	return master;
 }
 
 void p8_i2c_init(void)
@@ -1615,3 +1621,64 @@ struct i2c_bus *p8_i2c_find_bus_by_port(uint32_t chip_id, int eng, int port_num)
 
 	return NULL;
 }
+
+/* Adds a new i2c port to the DT and initialises it */
+struct i2c_bus *p8_i2c_add_bus(uint32_t chip_id, int eng_id, int port_id,
+				uint32_t bus_speed)
+{
+	struct proc_chip *c = get_chip(chip_id);
+	struct p8_i2c_master *m, *master = NULL;
+	struct p8_i2c_master_port *port;
+	struct dt_node *pn;
+
+	if (!c) {
+		prerror("I2C: Unable to add i2c bus: c%de%dp%d: chip doesn't exist\n",
+			chip_id, eng_id, port_id);
+		return NULL;
+	}
+
+	list_for_each(&c->i2cms, m, link) {
+		if (m->engine_id == eng_id) {
+			master = m;
+			break;
+		}
+	}
+
+	if (!master) {
+		struct dt_node *mn;
+
+		mn = p8_i2c_add_master_node(c->devnode, eng_id);
+		if (!mn) {
+			prerror("I2C: Unable to add DT node for I2CM c%xe%d\n",
+					chip_id, eng_id);
+			return NULL;
+		}
+
+		master = p8_i2c_init_one(mn, I2C_POWER8);
+		if (!master) {
+			prerror("I2C: Unable to initialise I2CM c%xe%d\n",
+					chip_id, eng_id);
+			return NULL;
+		}
+	}
+
+	list_for_each(&master->ports, port, link)
+		if (port->port_num == port_id)
+			return &port->bus;
+
+	pn = __p8_i2c_add_port_node(master->dt_node, port_id, bus_speed);
+	if (!pn) {
+		prerror("I2C: Unable to add dt node for bus c%xe%dp%d\n",
+					chip_id, eng_id, port_id);
+		return NULL;
+	}
+
+	port = p8_i2c_init_one_port(master, pn);
+	if (!port) {
+		prerror("I2C: Unable to init bus c%xe%dp%d\n",
+					chip_id, eng_id, port_id);
+		return NULL;
+	}
+
+	return &port->bus;
+}
diff --git a/include/i2c.h b/include/i2c.h
index 9ad307b3f445..a644bd427ce1 100644
--- a/include/i2c.h
+++ b/include/i2c.h
@@ -69,6 +69,9 @@ struct dt_node *__p8_i2c_add_port_node(struct dt_node *master, int port_id,
 struct dt_node *p8_i2c_add_port_node(struct dt_node *xscom, int eng_id,
 					int port_id, uint32_t bus_freq);
 
+struct i2c_bus *p8_i2c_add_bus(uint32_t chip_id, int eng_id, int port_id,
+					uint32_t bus_speed);
+
 int64_t i2c_queue_req(struct i2c_request *req);
 
 static inline uint64_t i2c_run_req(struct i2c_request *req)
-- 
2.26.2



More information about the Skiboot mailing list