aboutsummaryrefslogtreecommitdiff
path: root/hw/p8-i2c.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/p8-i2c.c')
-rw-r--r--hw/p8-i2c.c58
1 files changed, 37 insertions, 21 deletions
diff --git a/hw/p8-i2c.c b/hw/p8-i2c.c
index f4666e2..a627417 100644
--- a/hw/p8-i2c.c
+++ b/hw/p8-i2c.c
@@ -1063,8 +1063,11 @@ static int occ_i2c_lock(struct p8_i2c_master *master)
busflag = PPC_BIT(16 + (master->engine_id - 1) * 2);
- DBG("occflags = %llx (locks = %.6llx)\n", (u64)occflags,
- GETFIELD(PPC_BITMASK(16, 22), occflags));
+ DBG("I2C: c%de%d: occflags = %llx (locks = %x:%x:%x)\n",
+ master->chip_id, master->engine_id, (u64) occflags,
+ (u32) GETFIELD(PPC_BITMASK(16, 17), occflags),
+ (u32) GETFIELD(PPC_BITMASK(18, 19), occflags),
+ (u32) GETFIELD(PPC_BITMASK(20, 21), occflags));
rc = xscom_write(master->chip_id, OCCFLG_SET, busflag);
if (rc) {
@@ -1073,8 +1076,11 @@ static int occ_i2c_lock(struct p8_i2c_master *master)
}
/* If the OCC also has this bus locked then wait for IRQ */
- if (occflags & (busflag << 1))
+ if (occflags & (busflag >> 1)) {
+ DBG("I2C: c%de%d: Master in use by OCC\n",
+ master->chip_id, master->engine_id);
return 1;
+ }
master->occ_lock_acquired = true;
@@ -1098,8 +1104,8 @@ static int occ_i2c_unlock(struct p8_i2c_master *master)
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);
+ DBG("I2C: spurious unlock for c%de%d already cleared (flags = %.16llx)",
+ master->chip_id, master->engine_id, occflags);
}
rc = xscom_write(master->chip_id, OCCFLG_CLEAR, busflag);
@@ -1161,7 +1167,7 @@ static int p8_i2c_start_request(struct p8_i2c_master *master,
if (rc > 0) {
/* Wait for OCC IRQ */
master->state = state_occache_dis;
- schedule_timer(&master->recovery, rc);
+ schedule_timer(&master->recovery, msecs_to_tb(10));
return 0;
}
@@ -1281,29 +1287,29 @@ 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);
+ /* spurious */
+ if (master->state != state_occache_dis)
+ goto done;
+
/* Can we now lock this master? */
- rc = occ_i2c_lock(master);
- if (rc) {
- unlock(&master->lock);
- continue;
- }
+ if (occ_i2c_lock(master))
+ goto done;
- /* Run the state machine */
- p8_i2c_check_status(master);
+ /* clear the existing wait timer */
+ cancel_timer(&master->recovery);
+
+ /* re-start the request now that we own the master */
+ master->state = state_idle;
- /* Check for new work */
p8_i2c_check_work(master);
+ p8_i2c_check_status(master);
+done:
unlock(&master->lock);
}
}
@@ -1453,8 +1459,18 @@ static void p8_i2c_recover(struct timer *t __unused, void *data,
struct p8_i2c_master *master = data;
lock(&master->lock);
- assert(master->state == state_recovery ||
- master->state == state_occache_dis);
+
+ /*
+ * The recovery timer can race with the OCC interrupt. If the interrupt
+ * comes in just before this is called, then we'll get a spurious
+ * timeout which we need to ignore.
+ */
+ if (master->state != state_recovery &&
+ master->state != state_occache_dis) {
+ unlock(&master->lock);
+ return;
+ }
+
master->state = state_idle;
/* We may or may not still have work pending, re-enable the sensor cache