Skip to content

Instantly share code, notes, and snippets.

@aventuri
Created August 30, 2017 12:45
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aventuri/72bb355da22491beba03cc4d36e12e46 to your computer and use it in GitHub Desktop.
Save aventuri/72bb355da22491beba03cc4d36e12e46 to your computer and use it in GitHub Desktop.
A20 quick patch for I2C bus recovery when SDA stuck (9 clock ticks technique)
diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
index 30059c1..2eb357e 100644
--- a/drivers/i2c/busses/i2c-mv64xxx.c
+++ b/drivers/i2c/busses/i2c-mv64xxx.c
@@ -80,6 +80,15 @@
/* Bridge Status values */
#define MV64XXX_I2C_BRIDGE_STATUS_ERROR BIT(0)
+/* sun4i TWI LCR register */
+#define SUN4I_I2C_LCR_REG 0x20
+#define SUN4I_I2C_LCR_SCL_STATE BIT(5)
+#define SUN4I_I2C_LCR_SDA_STATE BIT(4)
+#define SUN4I_I2C_LCR_SCL_CTL BIT(3)
+#define SUN4I_I2C_LCR_SCL_CTL_EN BIT(2)
+#define SUN4I_I2C_LCR_SDA_CTL BIT(1)
+#define SUN4I_I2C_LCR_SDA_CTL_EN BIT(0)
+
/* Driver states */
enum {
MV64XXX_I2C_STATE_INVALID,
@@ -168,6 +177,84 @@ static struct mv64xxx_i2c_regs mv64xxx_i2c_regs_sun4i = {
.soft_reset = 0x18,
};
+static void sun4i_i2c_set_scl(struct i2c_adapter *adap, int val)
+{
+ struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap);
+ if (val)
+ writel(SUN4I_I2C_LCR_SCL_CTL | SUN4I_I2C_LCR_SCL_CTL_EN,
+ drv_data->reg_base + SUN4I_I2C_LCR_REG);
+ else
+ writel(SUN4I_I2C_LCR_SCL_CTL_EN, /* write a 0 out of SCL */
+ drv_data->reg_base + SUN4I_I2C_LCR_REG);
+}
+
+static int sun4i_i2c_get_scl(struct i2c_adapter *adap)
+{
+ struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap);
+ int val;
+
+ /* read the state of SCL */
+ val = readl(drv_data->reg_base + SUN4I_I2C_LCR_REG);
+ return val & SUN4I_I2C_LCR_SCL_STATE;
+}
+
+static int sun4i_i2c_get_sda(struct i2c_adapter *adap)
+{
+ struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap);
+ int val;
+
+ /* read the state of SDA */
+ val = readl(drv_data->reg_base + SUN4I_I2C_LCR_REG);
+ return val & SUN4I_I2C_LCR_SDA_STATE;
+}
+
+static void sun4i_i2c_scl_prepare_recovery(struct i2c_adapter *adap)
+{
+ struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap);
+ int val;
+
+ /* Disable interrupts */
+ //sun4i_i2c_write_reg(dev, DAVINCI_I2C_IMR_REG, 0);
+
+ /* put I2C into reset */
+ //sun4i_i2c_reset_ctrl(dev, 0);
+
+ /* DEBUG read the state of SCL & SDA */
+ val = readl(drv_data->reg_base + SUN4I_I2C_LCR_REG);
+ dev_err(&drv_data->adapter.dev, "mv64xxx: I2C bus recovering, LCR val: 0x%0x, ", val);
+
+ /* SCL output, SDA input */
+ //sun4i_i2c_write_reg(dev, DAVINCI_I2C_DIR_REG, DAVINCI_I2C_DIR_PDIR0);
+
+ /* change to GPIO mode */
+ //sun4i_i2c_write_reg(dev, DAVINCI_I2C_FUNC_REG,
+ // DAVINCI_I2C_FUNC_PFUNC0);
+}
+
+static void sun4i_i2c_scl_unprepare_recovery(struct i2c_adapter *adap)
+{
+ struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap);
+ int val;
+
+ /* change back to I2C mode */
+ writel(0, drv_data->reg_base + SUN4I_I2C_LCR_REG);
+
+ /* DEBUG read the state of SCL & SDA */
+ val = readl(drv_data->reg_base + SUN4I_I2C_LCR_REG);
+ dev_err(&drv_data->adapter.dev, "mv64xxx: I2C bus recovered, LCR val: 0x%0x, ", val);
+
+ //sun4i_i2c_unprepare_recovery(adap);
+}
+
+static struct i2c_bus_recovery_info sun4i_i2c_scl_recovery_info = {
+ .recover_bus = i2c_generic_scl_recovery,
+ .set_scl = sun4i_i2c_set_scl,
+ .get_scl = sun4i_i2c_get_scl,
+ .get_sda = sun4i_i2c_get_sda,
+ .prepare_recovery = sun4i_i2c_scl_prepare_recovery,
+ .unprepare_recovery = sun4i_i2c_scl_unprepare_recovery,
+};
+
static void
mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data,
struct i2c_msg *msg)
@@ -561,6 +648,8 @@ mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data)
"mv64xxx: I2C bus locked, block: %d, "
"time_left: %d\n", drv_data->block,
(int)time_left);
+
+ i2c_recover_bus(&drv_data->adapter);
mv64xxx_i2c_hw_init(drv_data);
}
} else
@@ -931,6 +1020,7 @@ mv64xxx_i2c_probe(struct platform_device *pd)
drv_data->adapter.class = I2C_CLASS_DEPRECATED;
drv_data->adapter.nr = pd->id;
drv_data->adapter.dev.of_node = pd->dev.of_node;
+ drv_data->adapter.bus_recovery_info = &sun4i_i2c_scl_recovery_info; // FIXME should be more generic as this driver is shared
platform_set_drvdata(pd, drv_data);
i2c_set_adapdata(&drv_data->adapter, drv_data);
@sergey-suloev
Copy link

Hi,
what do commented lines mean ?

@aventuri
Copy link
Author

aventuri commented Jun 2, 2019

hi, sorry for the late reply, i stumbled here by chance today! :-)
i suppose the commented lines are coming from sample code i got my inspiration from ("DAVINCI" is TI stuff).
i hacked this patch for strengthening the A20 I2C drive, the "i2c-mv64xxx" module and it worked out pretty well for "lousy" electric wires.. but i didn't followup a proper upstreaming as the modules is shared among many SOCs and i do not think they have "override" registers!
hope you found it useful for your use case!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment