Skip to content

Commit

Permalink
I2C: Inhibit sending slave stop command when SDA and SCL are not both…
Browse files Browse the repository at this point in the history
… asserted

In I2C reset sequence, code previously only waited until SCL line asserted
and, on timeout, sent a slave stop command anyway.  This caused situations where
the I2C master could get locked up for long periods when misbehaving I2C
devices were holding down the clock line.  This change waits for both SCL and
SDA to go high before invoking the slave stop command, and if that wait times
out, skips sending the slave stop.

Change-Id: I9cbb783dbaecb3df2cf1171f969e79749f3cd157
CQ: SW437713
Reviewed-on: http://ralgit01.raleigh.ibm.com/gerrit1/62436
Tested-by: Jenkins Server <pfd-jenkins+hostboot@us.ibm.com>
Tested-by: Jenkins OP Build CI <op-jenkins+hostboot@us.ibm.com>
Tested-by: Jenkins OP HW <op-hw-jenkins+hostboot@us.ibm.com>
Tested-by: FSP CI Jenkins <fsp-CI-jenkins+hostboot@us.ibm.com>
Reviewed-by: William G. Hoffa <wghoffa@us.ibm.com>
  • Loading branch information
Nick Bofferding authored and wghoffa committed Jul 17, 2018
1 parent 85367d8 commit ea3d3a6
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 10 deletions.
34 changes: 25 additions & 9 deletions src/usr/i2c/i2c.C
Original file line number Diff line number Diff line change
Expand Up @@ -2864,7 +2864,8 @@ errlHndl_t i2cSendSlaveStop ( TARGETING::Target * i_target,
continue;
}

// Look for Clock Line (SCL) High such that 'stop' cmd will work
// Look for clock line (SCL) and data line (SDA) to be high
// such that the 'stop' command will work
status_reg.value = 0x0ull;
size_t delay_ns = 0;
for ( ;
Expand All @@ -2884,7 +2885,8 @@ errlHndl_t i2cSendSlaveStop ( TARGETING::Target * i_target,
break;
}

if ( status_reg.scl_input_level != 0 )
if ( (status_reg.scl_input_level != 0)
&& (status_reg.sda_input_level != 0) )
{
break;
}
Expand All @@ -2904,13 +2906,27 @@ errlHndl_t i2cSendSlaveStop ( TARGETING::Target * i_target,

if ( delay_ns > I2C_RESET_POLL_DELAY_TOTAL_NS )
{
// Even though we don't see the Clock Line High, just
// trace a warning here and continue to send the 'stop' cmd
TRACFCOMP( g_trac_i2c, INFO_MRK"i2cSendSlaveStop(): "
"Not seeing SCL High 0x%.16llX after %d ns of "
"polling (max=%d)",
status_reg.value, delay_ns,
I2C_RESET_POLL_DELAY_TOTAL_NS );
// We don't see both clock and data lines high; in this case
// it's not likely for a slave stop command to work. One
// possible nasty side-effect of attempting slave stop is the
// I2C master can become indefinitely busy and prevent writes
// to the mode register from completing. Just continue to the
// next port.
TRACFCOMP( g_trac_i2c, ERR_MRK"i2cSendSlaveStop(): "
"Not seeing both SCL (%d) and SDA (%d) high "
"after %d ns of polling (max=%d). "
"Full status register = 0x%.16llX. "
"Inhibiting sending slave stop to e%/p% for "
"HUID 0x%08X.",
status_reg.scl_input_level,
status_reg.sda_input_level,
delay_ns,
I2C_RESET_POLL_DELAY_TOTAL_NS,
status_reg.value,
i_args.engine,
port,
TARGETING::get_huid(i_target));
continue;
}

cmd.value = 0x0ull;
Expand Down
2 changes: 1 addition & 1 deletion src/usr/i2c/i2c.H
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ union status_reg_t
uint64_t error_in : 1;
uint64_t i2c_port_history_busy : 1;
uint64_t scl_input_level : 1;
uint64_t sda_inupt_level : 1;
uint64_t sda_input_level : 1;
uint64_t i2c_port_busy : 1;
uint64_t i2c_interface_busy : 1;
uint64_t fifo_entry_count : 8;
Expand Down

0 comments on commit ea3d3a6

Please sign in to comment.