Merge "i2c: i2c-msm-geni: Add bus recovery support for slave devices"
This commit is contained in:

committed by
Gerrit - the friendly Code Review server

commit
94d692b043
@@ -166,6 +166,7 @@ struct geni_i2c_dev {
|
||||
bool gpi_reset;
|
||||
bool prev_cancel_pending; //Halt cancel till IOS in good state
|
||||
bool is_i2c_rtl_based; /* doing pending cancel only for rtl based SE's */
|
||||
bool bus_recovery_enable; //To be enabled by client if needed
|
||||
};
|
||||
|
||||
static struct geni_i2c_dev *gi2c_dev_dbg[MAX_SE];
|
||||
@@ -262,11 +263,11 @@ static void geni_i2c_err(struct geni_i2c_dev *gi2c, int err)
|
||||
{
|
||||
if (err == I2C_NACK || err == GENI_ABORT_DONE) {
|
||||
I2C_LOG_DBG(gi2c->ipcl, false, gi2c->dev, "%s\n",
|
||||
gi2c_log[err].msg);
|
||||
gi2c_log[err].msg);
|
||||
goto err_ret;
|
||||
} else {
|
||||
I2C_LOG_ERR(gi2c->ipcl, true, gi2c->dev, "%s\n",
|
||||
gi2c_log[err].msg);
|
||||
gi2c_log[err].msg);
|
||||
}
|
||||
geni_se_dump_dbg_regs(&gi2c->i2c_rsc, gi2c->base, gi2c->ipcl);
|
||||
err_ret:
|
||||
@@ -302,6 +303,117 @@ static void do_reg68_war_for_rtl_se(struct geni_i2c_dev *gi2c)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* geni_i2c_stop_with_cancel(): stops GENI SE with cancel command.
|
||||
* @gi2c: I2C dev handle
|
||||
*
|
||||
* This is a generic function to stop serial engine, to be called as required.
|
||||
*
|
||||
* Return: 0 if Success, non zero value if failed.
|
||||
*/
|
||||
static int geni_i2c_stop_with_cancel(struct geni_i2c_dev *gi2c)
|
||||
{
|
||||
int timeout = 0;
|
||||
|
||||
reinit_completion(&gi2c->xfer);
|
||||
geni_cancel_m_cmd(gi2c->base);
|
||||
timeout = wait_for_completion_timeout(&gi2c->xfer, HZ);
|
||||
if (!timeout) {
|
||||
I2C_LOG_DBG(gi2c->ipcl, true, gi2c->dev,
|
||||
"%s:Cancel failed\n", __func__);
|
||||
reinit_completion(&gi2c->xfer);
|
||||
geni_abort_m_cmd(gi2c->base);
|
||||
timeout = wait_for_completion_timeout(&gi2c->xfer, HZ);
|
||||
if (!timeout) {
|
||||
I2C_LOG_DBG(gi2c->ipcl, true, gi2c->dev,
|
||||
"%s:Abort failed\n", __func__);
|
||||
return !timeout;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* geni_i2c_is_bus_recovery_required: Checks if Bus recovery enabled/required ?
|
||||
* @gi2c: Handle of the I2C device
|
||||
*
|
||||
* Return: TRUE if SDA is stuck LOW due to some issue else false.
|
||||
*/
|
||||
static bool geni_i2c_is_bus_recovery_required(struct geni_i2c_dev *gi2c)
|
||||
{
|
||||
u32 geni_ios = readl_relaxed(gi2c->base + SE_GENI_IOS);
|
||||
|
||||
/*
|
||||
* SE_GENI_IOS will show I2C CLK/SDA line status, BIT 0 is SDA and
|
||||
* BIT 1 is clk status. SE_GENI_IOS register set when CLK/SDA line
|
||||
* is pulled high.
|
||||
*/
|
||||
return (((geni_ios & 1) == 0) && (gi2c->err == -EPROTO ||
|
||||
gi2c->err == -EBUSY ||
|
||||
gi2c->err == -ETIMEDOUT));
|
||||
}
|
||||
|
||||
/**
|
||||
* geni_i2c_bus_recovery(): Function to recover i2c bus when required
|
||||
* @gi2c: I2C device handle
|
||||
*
|
||||
* Use this function only when bus in bad state for some reason and need to
|
||||
* reset the slave to bring slave into proper state. This should put
|
||||
* bus into proper state once executed successfully.
|
||||
*
|
||||
* Return: 0 for Success OR Respective error code/value for failure.
|
||||
*/
|
||||
static int geni_i2c_bus_recovery(struct geni_i2c_dev *gi2c)
|
||||
{
|
||||
int timeout = 0;
|
||||
|
||||
/* Must be enabled by client "only" if required. */
|
||||
if (gi2c->bus_recovery_enable &&
|
||||
geni_i2c_is_bus_recovery_required(gi2c)) {
|
||||
GENI_SE_ERR(gi2c->ipcl, false, gi2c->dev,
|
||||
"SDA Line stuck\n", gi2c->err);
|
||||
} else {
|
||||
GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev,
|
||||
"Bus Recovery not required/enabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
I2C_LOG_DBG(gi2c->ipcl, false, gi2c->dev, "%s: start\n", __func__);
|
||||
|
||||
reinit_completion(&gi2c->xfer);
|
||||
geni_setup_m_cmd(gi2c->base, I2C_BUS_CLEAR, 0);
|
||||
timeout = wait_for_completion_timeout(&gi2c->xfer, HZ);
|
||||
if (!timeout) {
|
||||
geni_i2c_err(gi2c, GENI_TIMEOUT);
|
||||
gi2c->cur = NULL;
|
||||
timeout = geni_i2c_stop_with_cancel(gi2c);
|
||||
if (timeout) {
|
||||
I2C_LOG_DBG(gi2c->ipcl, true, gi2c->dev,
|
||||
"%s: Bus clear Failed\n", __func__);
|
||||
return timeout;
|
||||
}
|
||||
}
|
||||
I2C_LOG_DBG(gi2c->ipcl, false, gi2c->dev,
|
||||
"%s: BUS_CLEAR success\n", __func__);
|
||||
|
||||
reinit_completion(&gi2c->xfer);
|
||||
geni_setup_m_cmd(gi2c->base, I2C_STOP_ON_BUS, 0);
|
||||
timeout = wait_for_completion_timeout(&gi2c->xfer, HZ);
|
||||
if (!timeout) {
|
||||
geni_i2c_err(gi2c, GENI_TIMEOUT);
|
||||
gi2c->cur = NULL;
|
||||
timeout = geni_i2c_stop_with_cancel(gi2c);
|
||||
if (timeout) {
|
||||
I2C_LOG_DBG(gi2c->ipcl, true, gi2c->dev,
|
||||
"%s:Bus Stop Failed\n", __func__);
|
||||
return timeout;
|
||||
}
|
||||
}
|
||||
I2C_LOG_DBG(gi2c->ipcl, false, gi2c->dev, "%s: success\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_pending_cancel(struct geni_i2c_dev *gi2c)
|
||||
{
|
||||
int timeout = 0;
|
||||
@@ -1408,13 +1520,16 @@ static int geni_i2c_xfer(struct i2c_adapter *adap,
|
||||
wait_for_completion_timeout(&gi2c->xfer, HZ);
|
||||
if (!timeout)
|
||||
GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
|
||||
"Abort failed\n");
|
||||
"Abort failed\n");
|
||||
}
|
||||
|
||||
ret = gi2c->err;
|
||||
if (gi2c->err) {
|
||||
I2C_LOG_ERR(gi2c->ipcl, true, gi2c->dev,
|
||||
"i2c error :%d\n", gi2c->err);
|
||||
if (geni_i2c_bus_recovery(gi2c))
|
||||
GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
|
||||
"%s:Bus Recovery failed\n", __func__);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1583,6 +1698,12 @@ static int geni_i2c_probe(struct platform_device *pdev)
|
||||
dev_info(&pdev->dev, "Multi-EE usecase\n");
|
||||
}
|
||||
|
||||
/* Strictly it's client/slave device decision for an SE */
|
||||
if (of_property_read_bool(pdev->dev.of_node, "qcom,bus-recovery")) {
|
||||
gi2c->bus_recovery_enable = true;
|
||||
dev_dbg(&pdev->dev, "%s:I2C Bus recovery enabled\n", __func__);
|
||||
}
|
||||
|
||||
if (of_property_read_u32(pdev->dev.of_node, "qcom,clk-freq-out",
|
||||
&gi2c->i2c_rsc.clk_freq_out))
|
||||
gi2c->i2c_rsc.clk_freq_out = KHz(400);
|
||||
|
Reference in New Issue
Block a user