diff mbox series

hw/p8-i2c: Rework timeout handling

Message ID 20170919074005.5370-1-oohall@gmail.com
State Accepted
Headers show
Series hw/p8-i2c: Rework timeout handling | expand

Commit Message

Oliver O'Halloran Sept. 19, 2017, 7:40 a.m. UTC
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>
---
 hw/p8-i2c.c | 35 ++++++++++++++++++++++-------------
 1 file changed, 22 insertions(+), 13 deletions(-)

Comments

Stewart Smith Sept. 21, 2017, 4:53 a.m. UTC | #1
Oliver O'Halloran <oohall@gmail.com> writes:
> 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.

Merged as of 0e075173caebd2c6a5daf7c8cd59c4a6007f1913.
diff mbox series

Patch

diff --git a/hw/p8-i2c.c b/hw/p8-i2c.c
index 9cc016fe98a5..ffbace936a9f 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);