diff options
author | Oliver O'Halloran <oohall@gmail.com> | 2017-09-19 17:40:05 +1000 |
---|---|---|
committer | Stewart Smith <stewart@linux.vnet.ibm.com> | 2017-09-20 23:25:01 -0500 |
commit | 0e075173caebd2c6a5daf7c8cd59c4a6007f1913 (patch) | |
tree | 52b798fbdbada95270316f665ebfd9b01136c2f4 | |
parent | 5e09fe1f19d3f56b1cebb44439da5b5de6e1fccd (diff) | |
download | skiboot-0e075173caebd2c6a5daf7c8cd59c4a6007f1913.zip skiboot-0e075173caebd2c6a5daf7c8cd59c4a6007f1913.tar.gz skiboot-0e075173caebd2c6a5daf7c8cd59c4a6007f1913.tar.bz2 |
hw/p8-i2c: Rework timeout handling
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>
-rw-r--r-- | hw/p8-i2c.c | 35 |
1 files changed, 22 insertions, 13 deletions
diff --git a/hw/p8-i2c.c b/hw/p8-i2c.c index 9cc016f..ffbace9 100644 --- a/hw/p8-i2c.c +++ b/hw/p8-i2c.c @@ -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); @@ -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; @@ -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; @@ -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) @@ -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); } @@ -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); @@ -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); } @@ -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); @@ -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); |