Commit 720d5ce9 authored by Eddie James's avatar Eddie James Committed by Wolfram Sang

i2c: fsi: Add bus recovery

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: default avatarEddie James <eajames@linux.vnet.ibm.com>
Tested-by: default avatarJoel Stanley <joel@jms.id.au>
Signed-off-by: default avatarWolfram Sang <wsa@the-dreams.de>
parent f4cdc319
...@@ -326,6 +326,115 @@ static int fsi_i2c_read_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg, ...@@ -326,6 +326,115 @@ static int fsi_i2c_read_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,
return 0; 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) static int fsi_i2c_reset_engine(struct fsi_i2c_master *i2c, u16 port)
{ {
int rc; int rc;
...@@ -368,6 +477,7 @@ static int fsi_i2c_abort(struct fsi_i2c_port *port, u32 status) ...@@ -368,6 +477,7 @@ static int fsi_i2c_abort(struct fsi_i2c_port *port, u32 status)
int rc; int rc;
unsigned long start; unsigned long start;
u32 cmd = I2C_CMD_WITH_STOP; u32 cmd = I2C_CMD_WITH_STOP;
u32 stat;
struct fsi_i2c_master *i2c = port->master; struct fsi_i2c_master *i2c = port->master;
struct fsi_device *fsi = i2c->fsi; struct fsi_device *fsi = i2c->fsi;
...@@ -375,6 +485,17 @@ static int fsi_i2c_abort(struct fsi_i2c_port *port, u32 status) ...@@ -375,6 +485,17 @@ static int fsi_i2c_abort(struct fsi_i2c_port *port, u32 status)
if (rc) if (rc)
return 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 */ /* skip final stop command for these errors */
if (status & (I2C_STAT_PARITY | I2C_STAT_LOST_ARB | I2C_STAT_STOP_ERR)) if (status & (I2C_STAT_PARITY | I2C_STAT_LOST_ARB | I2C_STAT_STOP_ERR))
return 0; return 0;
...@@ -522,6 +643,16 @@ static u32 fsi_i2c_functionality(struct i2c_adapter *adap) ...@@ -522,6 +643,16 @@ static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA; 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 = { static const struct i2c_algorithm fsi_i2c_algorithm = {
.master_xfer = fsi_i2c_xfer, .master_xfer = fsi_i2c_xfer,
.functionality = fsi_i2c_functionality, .functionality = fsi_i2c_functionality,
...@@ -564,6 +695,7 @@ static int fsi_i2c_probe(struct device *dev) ...@@ -564,6 +695,7 @@ static int fsi_i2c_probe(struct device *dev)
port->adapter.dev.of_node = np; port->adapter.dev.of_node = np;
port->adapter.dev.parent = dev; port->adapter.dev.parent = dev;
port->adapter.algo = &fsi_i2c_algorithm; port->adapter.algo = &fsi_i2c_algorithm;
port->adapter.bus_recovery_info = &fsi_i2c_bus_recovery_info;
port->adapter.algo_data = port; port->adapter.algo_data = port;
snprintf(port->adapter.name, sizeof(port->adapter.name), snprintf(port->adapter.name, sizeof(port->adapter.name),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment