[Skiboot] [PATCH] p8-i2c: occ: Add support for OCC to use I2C engines

Shilpasri G Bhat shilpa.bhat at linux.vnet.ibm.com
Fri Jun 2 20:25:46 AEST 2017


This patch adds support to share the I2C engines with host and OCC.
OCC uses I2C engines to read DIMM temperatures and to communicate with
GPU. OCC Flag register is used for locking between host and OCC. Host
requests for the bus by setting a bit in OCC Flag register. OCC sends
an interrupt to indicate the change in ownership.

Originally-from: Oliver O'Halloran <oohall at gmail.com>
Signed-off-by: Shilpasri G Bhat <shilpa.bhat at linux.vnet.ibm.com>
---
 hw/occ.c      |   4 ++
 hw/p8-i2c.c   | 125 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 include/i2c.h |   3 ++
 3 files changed, 128 insertions(+), 4 deletions(-)

diff --git a/hw/occ.c b/hw/occ.c
index 4be4df4..34ef9a7 100644
--- a/hw/occ.c
+++ b/hw/occ.c
@@ -27,6 +27,7 @@
 #include <opal-api.h>
 #include <opal-msg.h>
 #include <timer.h>
+#include <i2c.h>
 
 /* OCC Communication Area for PStates */
 
@@ -1397,6 +1398,9 @@ void occ_p9_interrupt(uint32_t chip_id)
 	if (ireg & OCB_OCI_OCIMISC_IRQ_SHMEM)
 		occ_throttle_poll(NULL);
 
+	if (ireg & OCB_OCI_OCIMISC_IRQ_I2C)
+		p9_i2c_bus_owner_change(chip_id);
+
 	/* We may have masked-out OCB_OCI_OCIMISC_IRQ in the previous
 	 * OCCMISC_AND write. Check if there are any new source bits set,
 	 * and trigger another interrupt if so.
diff --git a/hw/p8-i2c.c b/hw/p8-i2c.c
index fefc1d4..107a6d6 100644
--- a/hw/p8-i2c.c
+++ b/hw/p8-i2c.c
@@ -1016,6 +1016,78 @@ static int p8_i2c_check_initial_status(struct p8_i2c_master_port *port)
 	return 0;
 }
 
+static bool occ_uses_master(struct p8_i2c_master *master)
+{
+	if (master->type == I2C_CENTAUR)
+		return true;
+
+	/* OCC uses I2CM Engines 1,2 and 3 */
+	if (master->type == I2C_POWER8 && proc_gen == proc_gen_p9)
+		return master->engine_id >= 1;
+
+	return false;
+}
+
+#define OCCFLG_BASE  0x00000000006C08A
+#define OCCFLG_CLEAR 0x00000000006C08B
+#define OCCFLG_SET   0x00000000006C08C
+
+static int p9_request_occ_bus(struct p8_i2c_master *master)
+{
+	u64 occflags, busflag;
+	int rc;
+
+	rc = xscom_read(master->chip_id, OCCFLG_BASE, &occflags);
+	if (rc) {
+		prerror("I2C: Failed to read OCC FLAG register\n");
+		return rc;
+	}
+
+	assert(master->engine_id > 0);
+
+	busflag = PPC_BIT(16 + (master->engine_id - 1) * 2);
+
+	DBG("occflags = %llx (locks = %.6llx)\n", (u64)occflags,
+	    GETFIELD(PPC_BITMASK(16, 22), occflags));
+
+	rc = xscom_write(master->chip_id, OCCFLG_SET, busflag);
+	if (rc) {
+		prerror("I2C: Failed to write OCC FLAG register\n");
+		return rc;
+	}
+
+	/* If the OCC also has this bus locked then wait for IRQ */
+	if (occflags & (busflag << 1))
+		return 1;
+
+	return 0;
+}
+
+static int p9_release_occ_bus(struct p8_i2c_master *master)
+{
+	u64 busflag, occflags;
+	int rc;
+
+	rc = xscom_read(master->chip_id, OCCFLG_BASE, &occflags);
+	if (rc) {
+		prerror("I2C: Failed to read OCC Flag register\n");
+		return rc;
+	}
+
+	busflag = PPC_BIT(16 + (master->engine_id - 1) * 2);
+
+	if (!(occflags & busflag)) {
+		prerror("I2C: busflag for %d already cleared (flags = %.16llx)",
+			master->engine_id, occflags);
+	}
+
+	rc = xscom_write(master->chip_id, OCCFLG_CLEAR, busflag);
+	if (rc)
+		prerror("I2C: Failed to write OCC Flag register\n");
+
+	return rc;
+}
+
 static int p8_i2c_start_request(struct p8_i2c_master *master,
 				struct i2c_request *req)
 {
@@ -1031,10 +1103,24 @@ static int p8_i2c_start_request(struct p8_i2c_master *master,
 	/* Get port */
 	port = container_of(req->bus, struct p8_i2c_master_port, bus);
 
-	/* Check if we need to disable the OCC cache first */
-	if (master->type == I2C_CENTAUR && !master->occ_cache_dis) {
+	/*
+	 * Check if we need to disable the OCC cache first
+	 * on P9 we need to set the "I2C master using bit"
+	 */
+	if (occ_uses_master(master) && !master->occ_cache_dis) {
 		DBG("Disabling OCC cache...\n");
-		rc = centaur_disable_sensor_cache(master->chip_id);
+
+		if (master->type == I2C_CENTAUR) {
+			rc = centaur_disable_sensor_cache(master->chip_id);
+		} else {
+			rc = p9_request_occ_bus(master);
+			if (rc) {
+				/* Wait for OCC IRQ */
+				master->state = state_occache_dis;
+				return 0;
+			}
+		}
+
 		if (rc < 0) {
 			log_simple_error(&e_info(OPAL_RC_I2C_START_REQ),
 					 "I2C: Failed "
@@ -1163,6 +1249,30 @@ static void p8_i2c_check_work(struct p8_i2c_master *master)
 	}
 }
 
+/* OCC IRQ Handler for I2C Ownership Change*/
+void p9_i2c_bus_owner_change(u32 chip_id)
+{
+	struct proc_chip *chip = get_chip(chip_id);
+	struct p8_i2c_master *master = NULL;
+
+	assert(chip);
+	list_for_each(&chip->i2cms, master, link) {
+		if (master->state == state_idle  ||
+		    master->state != state_occache_dis)
+			continue;
+
+		lock(&master->lock);
+
+		/* Run the state machine */
+		p8_i2c_check_status(master);
+
+		/* Check for new work */
+		p8_i2c_check_work(master);
+
+		unlock(&master->lock);
+	}
+}
+
 static int p8_i2c_queue_request(struct i2c_request *req)
 {
 	struct i2c_bus *bus = req->bus;
@@ -1318,7 +1428,12 @@ static void p8_i2c_recover(struct timer *t __unused, void *data,
 	 */
 	if (master->occ_cache_dis && list_empty(&master->req_list)) {
 		DBG("Re-enabling OCC cache after recovery\n");
-		centaur_enable_sensor_cache(master->chip_id);
+
+		if (master->type == I2C_CENTAUR)
+			centaur_enable_sensor_cache(master->chip_id);
+		else
+			p9_release_occ_bus(master);
+
 		master->occ_cache_dis = false;
 	}
 
@@ -1338,6 +1453,7 @@ static void p8_i2c_enable_scache(struct timer *t __unused, void *data,
 	if (master->state == state_idle && master->occ_cache_dis) {
 		DBG("Re-enabling OCC cache\n");
 		centaur_enable_sensor_cache(master->chip_id);
+		p9_release_occ_bus(master);
 		master->occ_cache_dis = false;
 	}
 	unlock(&master->lock);
@@ -1506,6 +1622,7 @@ static void p8_i2c_init_one(struct dt_node *i2cm, enum p8_i2c_master_type type)
 				 "Failed to read EXTD_STAT_REG\n");
 		if (master->type == I2C_CENTAUR)
 			centaur_enable_sensor_cache(master->chip_id);
+
 		free(master);
 		return;
 	}
diff --git a/include/i2c.h b/include/i2c.h
index 2c4c6a1..e5e8584 100644
--- a/include/i2c.h
+++ b/include/i2c.h
@@ -110,4 +110,7 @@ static inline int i2c_check_quirk(struct i2c_request *req, int *rc)
 extern void p8_i2c_init(void);
 extern void p8_i2c_interrupt(uint32_t chip_id);
 
+/* P9 I2C Ownership Change OCC interrupt handler */
+extern void p9_i2c_bus_owner_change(u32 chip_id);
+
 #endif /* __I2C_H */
-- 
1.8.3.1



More information about the Skiboot mailing list