aboutsummaryrefslogtreecommitdiff
path: root/hw/p8-i2c.c
diff options
context:
space:
mode:
authorShilpasri G Bhat <shilpa.bhat@linux.vnet.ibm.com>2017-06-02 15:55:46 +0530
committerStewart Smith <stewart@linux.vnet.ibm.com>2017-06-06 20:49:05 +1000
commitc5fa0d718e9cda8999dcb83088118a7ea61814c5 (patch)
tree086b5cd211b8b4ab6591fa24d281c9443de50418 /hw/p8-i2c.c
parent682da462cdcd79b668c2f1999bb9cf955cbef63d (diff)
downloadskiboot-c5fa0d718e9cda8999dcb83088118a7ea61814c5.zip
skiboot-c5fa0d718e9cda8999dcb83088118a7ea61814c5.tar.gz
skiboot-c5fa0d718e9cda8999dcb83088118a7ea61814c5.tar.bz2
p8-i2c: occ: Add support for OCC to use I2C engines
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@gmail.com> Signed-off-by: Shilpasri G Bhat <shilpa.bhat@linux.vnet.ibm.com> [stewart@linux.vnet.ibm.com: Pretty heavily rework logic, including fixing bus owner change and separating out occ lock from sensor cache] Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
Diffstat (limited to 'hw/p8-i2c.c')
-rw-r--r--hw/p8-i2c.c147
1 files changed, 147 insertions, 0 deletions
diff --git a/hw/p8-i2c.c b/hw/p8-i2c.c
index fefc1d4..d55d08e 100644
--- a/hw/p8-i2c.c
+++ b/hw/p8-i2c.c
@@ -200,6 +200,7 @@ struct p8_i2c_master {
uint32_t bytes_sent;
bool irq_ok; /* Interrupt working ? */
bool occ_cache_dis; /* I have disabled the cache */
+ bool occ_lock_acquired; /* Acquired lock from OCC */
enum request_state {
state_idle,
state_occache_dis,
@@ -232,6 +233,8 @@ struct p8_i2c_request {
uint64_t timeout;
};
+static int occ_i2c_unlock(struct p8_i2c_master *master);
+
static void p8_i2c_print_debug_info(struct p8_i2c_master_port *port,
struct i2c_request *req, uint64_t end_time)
{
@@ -427,6 +430,10 @@ static void p8_i2c_complete_request(struct p8_i2c_master *master,
schedule_timer(&master->sensor_cache,
msecs_to_tb(SENSOR_CACHE_EN_DELAY));
+ /* If we're done with i2c master, allow OCC to use it */
+ if (master->occ_lock_acquired && list_empty(&master->req_list))
+ occ_i2c_unlock(master);
+
unlock(&master->lock);
if (req->completion)
req->completion(ret, req);
@@ -1016,6 +1023,94 @@ static int p8_i2c_check_initial_status(struct p8_i2c_master_port *port)
return 0;
}
+/*
+ * On POWER9, the I2C may also wish to use some of the i2cm engines,
+ * to do things like read sensor data. There's a couple of shared
+ * registers with the OCC to negotiate locking of the i2cm engines.
+ * See occ/src/occ_405/lock/lock.c
+ */
+static bool occ_uses_master(struct p8_i2c_master *master)
+{
+ /* OCC uses I2CM Engines 1,2 and 3, only on POWER9 */
+ 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 occ_i2c_lock(struct p8_i2c_master *master)
+{
+ u64 occflags, busflag;
+ int rc;
+
+ if (!occ_uses_master(master))
+ return 0;
+
+ if (master->occ_lock_acquired)
+ return 0;
+
+ 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;
+
+ master->occ_lock_acquired = true;
+
+ return 0;
+}
+
+static int occ_i2c_unlock(struct p8_i2c_master *master)
+{
+ u64 busflag, occflags;
+ int rc;
+
+ if (!occ_uses_master(master))
+ return 0;
+
+ 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");
+
+ master->occ_lock_acquired = false;
+
+ return rc;
+}
+
static int p8_i2c_start_request(struct p8_i2c_master *master,
struct i2c_request *req)
{
@@ -1035,6 +1130,7 @@ static int p8_i2c_start_request(struct p8_i2c_master *master,
if (master->type == I2C_CENTAUR && !master->occ_cache_dis) {
DBG("Disabling OCC cache...\n");
rc = centaur_disable_sensor_cache(master->chip_id);
+
if (rc < 0) {
log_simple_error(&e_info(OPAL_RC_I2C_START_REQ),
"I2C: Failed "
@@ -1052,6 +1148,23 @@ static int p8_i2c_start_request(struct p8_i2c_master *master,
}
}
+ /*
+ * on P9 we need to set the "I2C master using bit" so we don't
+ * conflict with the OCC's use of the i2c master.
+ */
+ rc = occ_i2c_lock(master);
+ if (rc < 0) {
+ log_simple_error(&e_info(OPAL_RC_I2C_START_REQ),
+ "I2C: Failed to get I2CM lock from OCC\n");
+ return rc;
+ }
+ if (rc > 0) {
+ /* Wait for OCC IRQ */
+ master->state = state_occache_dis;
+ schedule_timer(&master->recovery, rc);
+ return 0;
+ }
+
/* Convert the offset if needed */
if (req->offset_bytes) {
int i;
@@ -1163,6 +1276,36 @@ 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;
+ int rc;
+
+ assert(chip);
+ list_for_each(&chip->i2cms, master, link) {
+ if (master->state == state_idle ||
+ master->state != state_occache_dis)
+ continue;
+
+ lock(&master->lock);
+
+ /* Can we now lock this master? */
+ rc = occ_i2c_lock(master);
+ if (rc)
+ continue;
+
+ /* 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;
@@ -1322,6 +1465,9 @@ static void p8_i2c_recover(struct timer *t __unused, void *data,
master->occ_cache_dis = false;
}
+ if (master->occ_lock_acquired && list_empty(&master->req_list))
+ occ_i2c_unlock(master);
+
/* Re-check for new work */
p8_i2c_check_work(master);
unlock(&master->lock);
@@ -1506,6 +1652,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;
}