Skip to content

Commit

Permalink
i2c: fsi: Add bus recovery
Browse files Browse the repository at this point in the history
Bus recovery should reset the bus with the standard i2c recovery
procedure. Populate the necessary fields so that the standard procedure
can perform the reset.

Signed-off-by: Eddie James <eajames@linux.vnet.ibm.com>
Tested-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
  • Loading branch information
Eddie James authored and Wolfram Sang committed Jul 20, 2018
1 parent f4cdc31 commit 720d5ce
Showing 1 changed file with 132 additions and 0 deletions.
132 changes: 132 additions & 0 deletions drivers/i2c/busses/i2c-fsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,115 @@ static int fsi_i2c_read_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,
return 0;
}

static int fsi_i2c_get_scl(struct i2c_adapter *adap)
{
u32 stat = 0;
struct fsi_i2c_port *port = adap->algo_data;
struct fsi_i2c_master *i2c = port->master;

fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat);

return !!(stat & I2C_STAT_SCL_IN);
}

static void fsi_i2c_set_scl(struct i2c_adapter *adap, int val)
{
u32 dummy = 0;
struct fsi_i2c_port *port = adap->algo_data;
struct fsi_i2c_master *i2c = port->master;

if (val)
fsi_i2c_write_reg(i2c->fsi, I2C_FSI_SET_SCL, &dummy);
else
fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_SCL, &dummy);
}

static int fsi_i2c_get_sda(struct i2c_adapter *adap)
{
u32 stat = 0;
struct fsi_i2c_port *port = adap->algo_data;
struct fsi_i2c_master *i2c = port->master;

fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat);

return !!(stat & I2C_STAT_SDA_IN);
}

static void fsi_i2c_set_sda(struct i2c_adapter *adap, int val)
{
u32 dummy = 0;
struct fsi_i2c_port *port = adap->algo_data;
struct fsi_i2c_master *i2c = port->master;

if (val)
fsi_i2c_write_reg(i2c->fsi, I2C_FSI_SET_SDA, &dummy);
else
fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_SDA, &dummy);
}

static void fsi_i2c_prepare_recovery(struct i2c_adapter *adap)
{
int rc;
u32 mode;
struct fsi_i2c_port *port = adap->algo_data;
struct fsi_i2c_master *i2c = port->master;

rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_MODE, &mode);
if (rc)
return;

mode |= I2C_MODE_DIAG;
fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
}

static void fsi_i2c_unprepare_recovery(struct i2c_adapter *adap)
{
int rc;
u32 mode;
struct fsi_i2c_port *port = adap->algo_data;
struct fsi_i2c_master *i2c = port->master;

rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_MODE, &mode);
if (rc)
return;

mode &= ~I2C_MODE_DIAG;
fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
}

static int fsi_i2c_reset_bus(struct fsi_i2c_master *i2c,
struct fsi_i2c_port *port)
{
int rc;
u32 stat, dummy = 0;

/* force bus reset, ignore errors */
i2c_recover_bus(&port->adapter);

/* reset errors */
rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_ERR, &dummy);
if (rc)
return rc;

/* wait for command complete */
usleep_range(I2C_RESET_SLEEP_MIN_US, I2C_RESET_SLEEP_MAX_US);

rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat);
if (rc)
return rc;

if (stat & I2C_STAT_CMD_COMP)
return 0;

/* failed to get command complete; reset engine again */
rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_I2C, &dummy);
if (rc)
return rc;

/* re-init engine again */
return fsi_i2c_dev_init(i2c);
}

static int fsi_i2c_reset_engine(struct fsi_i2c_master *i2c, u16 port)
{
int rc;
Expand Down Expand Up @@ -368,13 +477,25 @@ static int fsi_i2c_abort(struct fsi_i2c_port *port, u32 status)
int rc;
unsigned long start;
u32 cmd = I2C_CMD_WITH_STOP;
u32 stat;
struct fsi_i2c_master *i2c = port->master;
struct fsi_device *fsi = i2c->fsi;

rc = fsi_i2c_reset_engine(i2c, port->port);
if (rc)
return rc;

rc = fsi_i2c_read_reg(fsi, I2C_FSI_STAT, &stat);
if (rc)
return rc;

/* if sda is low, peform full bus reset */
if (!(stat & I2C_STAT_SDA_IN)) {
rc = fsi_i2c_reset_bus(i2c, port);
if (rc)
return rc;
}

/* skip final stop command for these errors */
if (status & (I2C_STAT_PARITY | I2C_STAT_LOST_ARB | I2C_STAT_STOP_ERR))
return 0;
Expand Down Expand Up @@ -522,6 +643,16 @@ static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA;
}

static struct i2c_bus_recovery_info fsi_i2c_bus_recovery_info = {
.recover_bus = i2c_generic_scl_recovery,
.get_scl = fsi_i2c_get_scl,
.set_scl = fsi_i2c_set_scl,
.get_sda = fsi_i2c_get_sda,
.set_sda = fsi_i2c_set_sda,
.prepare_recovery = fsi_i2c_prepare_recovery,
.unprepare_recovery = fsi_i2c_unprepare_recovery,
};

static const struct i2c_algorithm fsi_i2c_algorithm = {
.master_xfer = fsi_i2c_xfer,
.functionality = fsi_i2c_functionality,
Expand Down Expand Up @@ -564,6 +695,7 @@ static int fsi_i2c_probe(struct device *dev)
port->adapter.dev.of_node = np;
port->adapter.dev.parent = dev;
port->adapter.algo = &fsi_i2c_algorithm;
port->adapter.bus_recovery_info = &fsi_i2c_bus_recovery_info;
port->adapter.algo_data = port;

snprintf(port->adapter.name, sizeof(port->adapter.name),
Expand Down

0 comments on commit 720d5ce

Please sign in to comment.