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 gpi_reset;
|
||||||
bool prev_cancel_pending; //Halt cancel till IOS in good state
|
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 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];
|
static struct geni_i2c_dev *gi2c_dev_dbg[MAX_SE];
|
||||||
@@ -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)
|
static int do_pending_cancel(struct geni_i2c_dev *gi2c)
|
||||||
{
|
{
|
||||||
int timeout = 0;
|
int timeout = 0;
|
||||||
@@ -1415,6 +1527,9 @@ static int geni_i2c_xfer(struct i2c_adapter *adap,
|
|||||||
if (gi2c->err) {
|
if (gi2c->err) {
|
||||||
I2C_LOG_ERR(gi2c->ipcl, true, gi2c->dev,
|
I2C_LOG_ERR(gi2c->ipcl, true, gi2c->dev,
|
||||||
"i2c error :%d\n", gi2c->err);
|
"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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1583,6 +1698,12 @@ static int geni_i2c_probe(struct platform_device *pdev)
|
|||||||
dev_info(&pdev->dev, "Multi-EE usecase\n");
|
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",
|
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))
|
||||||
gi2c->i2c_rsc.clk_freq_out = KHz(400);
|
gi2c->i2c_rsc.clk_freq_out = KHz(400);
|
||||||
|
Reference in New Issue
Block a user