Skip to content

Commit

Permalink
hw/p8-i2c: Rework timeout handling
Browse files Browse the repository at this point in the history
Currently we treat a timeout as a hard failure and will automatically
fail any transations that hit their timeout. This results in
unnecessarily failing I2C requests if interrupts are dropped, etc.
Although these are bad things that we should log we can handle them
better by checking the actual hardware status and completing the
transation if there are no real errors. This patch reworks the timeout
handling to check the status and continue the transaction if it can.
if it can while logging an error if it detects a timeout due to a
dropped interrupt.

Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
  • Loading branch information
oohal authored and stewartsmith committed Sep 21, 2017
1 parent 5e09fe1 commit 0e07517
Showing 1 changed file with 22 additions and 13 deletions.
35 changes: 22 additions & 13 deletions hw/p8-i2c.c
Expand Up @@ -655,6 +655,10 @@ static void p8_i2c_status_error(struct p8_i2c_master_port *port,
log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER),
"I2C: Transfer error occurred\n");
p8_i2c_print_debug_info(port, req, end_time);
} else if (status == I2C_STAT_PSEUDO_TIMEOUT) {
log_simple_error(&e_info(OPAL_RC_I2C_TIMEOUT),
"I2C: request timed out!\n");
p8_i2c_print_debug_info(port, req, end_time);
}

p8_i2c_translate_error(req, status);
Expand Down Expand Up @@ -882,7 +886,7 @@ static void p8_i2c_status_cmd_completion(struct p8_i2c_master *master,
p8_i2c_complete_request(master, req, rc);
}

static void p8_i2c_check_status(struct p8_i2c_master *master)
static void p8_i2c_check_status(struct p8_i2c_master *master, bool timeout)
{
struct p8_i2c_master_port *port;
struct i2c_request *req;
Expand All @@ -905,7 +909,7 @@ static void p8_i2c_check_status(struct p8_i2c_master *master)
}

/* Nothing happened ? Go back */
if (!(status & (I2C_STAT_ANY_ERR | I2C_STAT_DATA_REQ |
if (!timeout && !(status & (I2C_STAT_ANY_ERR | I2C_STAT_DATA_REQ |
I2C_STAT_CMD_COMP)))
return;

Expand Down Expand Up @@ -946,6 +950,8 @@ static void p8_i2c_check_status(struct p8_i2c_master *master)
p8_i2c_status_data_request(master, req, status);
else if (status & I2C_STAT_CMD_COMP)
p8_i2c_status_cmd_completion(master, req, now);
else if (timeout)
p8_i2c_status_error(port, req, I2C_STAT_PSEUDO_TIMEOUT, now);
}

static int p8_i2c_check_initial_status(struct p8_i2c_master_port *port)
Expand Down Expand Up @@ -1307,8 +1313,7 @@ void p9_i2c_bus_owner_change(u32 chip_id)
master->state = state_idle;

p8_i2c_check_work(master);
p8_i2c_check_status(master);

p8_i2c_check_status(master, false);
done:
unlock(&master->lock);
}
Expand Down Expand Up @@ -1383,7 +1388,7 @@ static uint64_t p8_i2c_run_request(struct i2c_request *req)
uint64_t poll_interval = 0;

lock(&master->lock);
p8_i2c_check_status(master);
p8_i2c_check_status(master, false);
p8_i2c_check_work(master);
poll_interval = master->poll_interval;
unlock(&master->lock);
Expand Down Expand Up @@ -1442,13 +1447,17 @@ static void p8_i2c_timeout(struct timer *t __unused, void *data, uint64_t now)
request->timeout = 0ul;
port = container_of(req->bus, struct p8_i2c_master_port, bus);

/* Allright, we have a request and it has timed out ... */
log_simple_error(&e_info(OPAL_RC_I2C_TIMEOUT),
"I2C: Request timeout !\n");
p8_i2c_print_debug_info(port, req, now);
DBG("timeout on c%de%d\n",
master->chip_id, master->engine_id);

/*
* Run through the usual path with timeout checking. The command might
* have been completed successfully and we just lost an interrupt
* somewhere.
*/
p8_i2c_check_status(port->master, true);
p8_i2c_check_work(port->master);

/* Use the standard error path */
p8_i2c_status_error(port, req, I2C_STAT_PSEUDO_TIMEOUT, now);
exit:
unlock(&master->lock);
}
Expand Down Expand Up @@ -1522,7 +1531,7 @@ static void p8_i2c_poll(struct timer *t __unused, void *data, uint64_t now)
return;

lock(&master->lock);
p8_i2c_check_status(master);
p8_i2c_check_status(master, false);
if (master->state != state_idle)
schedule_timer_at(&master->poller, now + master->poll_interval);
p8_i2c_check_work(master);
Expand All @@ -1544,7 +1553,7 @@ void p8_i2c_interrupt(uint32_t chip_id)
lock(&master->lock);

/* Run the state machine */
p8_i2c_check_status(master);
p8_i2c_check_status(master, false);

/* Check for new work */
p8_i2c_check_work(master);
Expand Down

0 comments on commit 0e07517

Please sign in to comment.