summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Gonzalez <alex.gonzalez@digi.com>2012-01-30 12:24:10 +0100
committerAlex Gonzalez <alex.gonzalez@digi.com>2012-01-31 10:08:55 +0100
commit8a9575276d12a638bdd79ee112bed359e540751e (patch)
tree726628bcc933f52276cc4c18a1290398b6c8069b
parent2332540dc1a30ab3dda07563079188e6e1f3aebc (diff)
ccxmx53: Dialog I2C, perform a flush write on all accesses
There is a Dialog bug on the PMIC which can leave the I2C bus inaccessible leaving an unbootable system. The workaround suggested by Dialog is to flush all accesses with a last write to a safe register. They provided a patch which when applied does not work well, so this is a rework simplified version of that patch. Signed-off-by: Alex Gonzalez <alex.gonzalez@digi.com>
-rw-r--r--drivers/mfd/da9052-i2c.c124
1 files changed, 122 insertions, 2 deletions
diff --git a/drivers/mfd/da9052-i2c.c b/drivers/mfd/da9052-i2c.c
index 3a1011682224..9371a3154b3a 100644
--- a/drivers/mfd/da9052-i2c.c
+++ b/drivers/mfd/da9052-i2c.c
@@ -19,6 +19,8 @@ static struct da9052 *da9052_i2c;
#define I2C_CONNECTED 0
+#define I2C_BUG_WORKAROUND
+
static int da9052_i2c_is_connected(void)
{
struct da9052_ssc_msg msg;
@@ -100,6 +102,49 @@ static int da9052_i2c_remove(struct i2c_client *client)
return 0;
}
+#ifdef I2C_BUG_WORKAROUND
+#define I2C_DUMMY_REG 0xFF
+const unsigned char i2c_flush_data[] = {I2C_DUMMY_REG, 0xFF};
+
+#if 1 /* Enable safe register addresses checking */
+/* DLG TODO: Add read only registers here (they are not affected with write) */
+static inline int da9052_is_i2c_reg_safe(unsigned char reg)
+{
+ const char safe_table[256] = {
+ [DA9052_STATUSA_REG] = 1,
+ [DA9052_STATUSB_REG] = 1,
+ [DA9052_STATUSC_REG] = 1,
+ [DA9052_STATUSD_REG] = 1,
+ [DA9052_EVENTA_REG] = 1,
+ [DA9052_EVENTB_REG] = 1,
+ [DA9052_EVENTC_REG] = 1,
+ [DA9052_EVENTD_REG] = 1,
+ [DA9052_ADCRESL_REG] = 1,
+ [DA9052_ADCRESH_REG] = 1,
+ [DA9052_VDDRES_REG] = 1,
+ [DA9052_ICHGAV_REG] = 1,
+ [DA9052_TBATRES_REG] = 1,
+ [DA9052_ADCIN4RES_REG] = 1,
+ [DA9052_ADCIN5RES_REG] = 1,
+ [DA9052_ADCIN6RES_REG] = 1,
+ [DA9052_TJUNCRES_REG] = 1,
+ [DA9052_TSIXMSB_REG] = 1,
+ [DA9052_TSIYMSB_REG] = 1,
+ [DA9052_TSILSB_REG] = 1,
+ [DA9052_TSIZMSB_REG] = 1,
+ [I2C_DUMMY_REG] = 1, /* Dummy reg must be a save reg */
+ };
+
+ return safe_table[reg];
+}
+#else
+static inline int da9052_is_i2c_reg_safe(unsigned char reg)
+{
+ return 0;
+}
+#endif
+#endif
+
int da9052_i2c_write(struct da9052 *da9052, struct da9052_ssc_msg *msg)
{
struct i2c_msg i2cmsg;
@@ -127,6 +172,25 @@ int da9052_i2c_write(struct da9052 *da9052, struct da9052_ssc_msg *msg)
return ret;
}
+#ifdef I2C_BUG_WORKAROUND
+ /* Test, whether register to be accessed needs to be flushed */
+ if (!da9052_is_i2c_reg_safe(msg->addr)) {
+ i2cmsg.addr = da9052->slave_addr;
+ i2cmsg.len = 2;
+ i2cmsg.flags = 0; /* Write operation */
+ /* i2c_flush_data is only to read from */
+ i2cmsg.buf = (unsigned char *)i2c_flush_data;
+
+ /* Additional flush write */
+ ret = i2c_transfer(da9052->adapter, &i2cmsg, 1);
+ if (ret < 0) {
+ dev_info(&da9052->i2c_client->dev,\
+ "2 - %s:master_xfer Failed!!\n", __func__);
+ return ret;
+ }
+ }
+#endif
+
return 0;
}
@@ -135,7 +199,7 @@ int da9052_i2c_read(struct da9052 *da9052, struct da9052_ssc_msg *msg)
/*Get the da9052_i2c client details*/
unsigned char buf[2] = {0, 0};
- struct i2c_msg i2cmsg[2];
+ struct i2c_msg i2cmsg[3];
int ret = 0;
/* Copy SSC Msg to local character buffer */
@@ -158,8 +222,28 @@ int da9052_i2c_read(struct da9052 *da9052, struct da9052_ssc_msg *msg)
/*To read the data on I2C set flag to I2C_M_RD */
i2cmsg[1].flags = I2C_M_RD;
- /* Start the i2c transfer by calling host i2c driver function */
+ /* Standard read transfer */
ret = i2c_transfer(da9052->adapter, i2cmsg, 2);
+
+ if (ret < 0) {
+ dev_info(&da9052->i2c_client->dev,\
+ "2 - %s:master_xfer Failed!!\n", __func__);
+ }
+
+#ifdef I2C_BUG_WORKAROUND
+ /* Test, whether register to be accessed needs to be flushed */
+ if (!da9052_is_i2c_reg_safe(msg->addr)) {
+ i2cmsg[2].addr = da9052->slave_addr;
+ i2cmsg[2].len = 2;
+ i2cmsg[2].flags = 0; /* Write operation */
+ /* i2c_flush_data is only to read from */
+ i2cmsg[2].buf = (unsigned char *)i2c_flush_data;
+
+ /* Read transfer with additional flush write */
+ ret = i2c_transfer(da9052->adapter, &i2cmsg[2], 1);
+ }
+#endif
+
if (ret < 0) {
dev_info(&da9052->i2c_client->dev,\
"2 - %s:master_xfer Failed!!\n", __func__);
@@ -249,6 +333,24 @@ int da9052_i2c_write_many(struct da9052 *da9052,
return ret;
}
+#ifdef I2C_BUG_WORKAROUND
+ /* Test, whether last register to be accessed needs to be flushed */
+ if (!da9052_is_i2c_reg_safe(sscmsg[msg_no-1].addr)) {
+ i2cmsg.addr = da9052->slave_addr;
+ i2cmsg.len = 2;
+ i2cmsg.flags = 0; /* Write operation */
+ /* i2c_flush_data is only to read from */
+ i2cmsg.buf = (unsigned char *)i2c_flush_data;
+
+ ret = i2c_transfer(da9052->adapter, &i2cmsg, 1);
+ if (ret < 0) {
+ dev_info(&da9052->i2c_client->dev,
+ "%s: i2c_transfer failed!!!\n", __func__);
+ return ret;
+ }
+ }
+#endif
+
return 0;
}
@@ -328,6 +430,24 @@ int da9052_i2c_read_many(struct da9052 *da9052,
return ret;
}
+#ifdef I2C_BUG_WORKAROUND
+ /* Test, whether last register to be accessed needs to be flushed */
+ if (!da9052_is_i2c_reg_safe(sscmsg[msg_no-1].addr)) {
+ i2cmsg.addr = da9052->slave_addr;
+ i2cmsg.len = 2;
+ i2cmsg.flags = 0; /* Write operation */
+ /* i2c_flush_data is only to read from */
+ i2cmsg.buf = (unsigned char *)i2c_flush_data;
+
+ ret = i2c_transfer(da9052->adapter, &i2cmsg, 1);
+ if (ret < 0) {
+ dev_info(&da9052->i2c_client->dev,
+ "%s: i2c_transfer failed!!!\n", __func__);
+ return ret;
+ }
+ }
+#endif
+
/* Gather READ data */
for (cnt = 0; cnt < msg_no; cnt++)
sscmsg[cnt].data = data_buf[cnt];