summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWayne Zou <b36644@freescale.com>2011-11-21 14:44:33 +0800
committerAndy Voltz <andy.voltz@timesys.com>2012-03-09 11:26:45 -0500
commitc3a9d14201269f0bd5653e62491553f39ad8bcce (patch)
treef3a48709627d0ffe0f07ecaf06eddfcdfa8c3943
parent4f554cea0e5b598779ff726468f47caba29ac00e (diff)
ENGR00162711 DA9053: Add dummy write for DA9053 I2C register access
DA9053 i2c issue: Rarely the i2c interface of DA9053 hang and it can not be recovered if not power off totally. The Dialog suggests adding dummy write for DA9053 I2C register access, in order to decrease the failure of DA9053 register access and possibility of i2c failure. Signed-off-by: Wayne Zou <b36644@freescale.com> (cherry picked from commit bfd7cba1eeb46977b18a3c5fa65d812817a8294d)
-rw-r--r--drivers/mfd/da9052-i2c.c317
1 files changed, 166 insertions, 151 deletions
diff --git a/drivers/mfd/da9052-i2c.c b/drivers/mfd/da9052-i2c.c
index 6209e977f10c..457523f45f57 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 DA9052_I2C_BUG_WORKAROUND
+
static int da9052_i2c_is_connected(void)
{
struct da9052_ssc_msg msg;
@@ -76,6 +78,15 @@ static int __devinit da9052_i2c_probe(struct i2c_client *client,
/* Validate I2C connectivity */
if ( I2C_CONNECTED == da9052_i2c_is_connected()) {
+ /* Enable Repeated Write Mode permanently */
+ struct da9052_ssc_msg ctrl_msg = {
+ DA9052_CONTROLB_REG, DA9052_CONTROLB_WRITEMODE};
+ if (da9052_i2c_write(da9052_i2c, &ctrl_msg) < 0) {
+ dev_info(&da9052_i2c->i2c_client->dev,
+ "%s: repeated mode not set!!\n", __func__);
+ return -ENODEV;
+ }
+
/* I2C is connected */
da9052_i2c->connecting_device = I2C;
if( 0!= da9052_ssc_init(da9052_i2c) )
@@ -100,27 +111,59 @@ static int da9052_i2c_remove(struct i2c_client *client)
return 0;
}
+#ifdef DA9052_I2C_BUG_WORKAROUND
+const unsigned char i2c_flush_data[] = {0xFF, 0xFF};
+static const char safe_table[256] = {
+ [DA9052_STATUSA_REG] = 1,
+ [DA9052_STATUSB_REG] = 1,
+ [DA9052_STATUSC_REG] = 1,
+ [DA9052_STATUSD_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,
+};
+/* Enable safe register addresses */
+static inline int da9052_is_i2c_reg_safe(unsigned char reg)
+{
+ return safe_table[reg];
+}
+#endif
+
int da9052_i2c_write(struct da9052 *da9052, struct da9052_ssc_msg *msg)
{
struct i2c_msg i2cmsg;
- unsigned char buf[2] = {0};
+ unsigned char buf[4] = {0};
int ret = 0;
- /* Copy the ssc msg to local character buffer */
- buf[0] = msg->addr;
- buf[1] = msg->data;
-
/*Construct a i2c msg for a da9052 driver ssc message request */
i2cmsg.addr = da9052->slave_addr;
- i2cmsg.len = 2;
i2cmsg.buf = buf;
-
- /* To write the data on I2C set flag to zero */
i2cmsg.flags = 0;
+ i2cmsg.len = 2;
+
+ /* Copy the ssc msg and additional data to flush chip I2C registers */
+ buf[0] = msg->addr;
+ buf[1] = msg->data;
+#ifdef DA9052_I2C_BUG_WORKAROUND
+ if (!da9052_is_i2c_reg_safe(msg->addr)) {
+ i2cmsg.len = 4;
+ buf[2] = i2c_flush_data[0];
+ buf[3] = i2c_flush_data[1];
+ }
+#endif
/* Start the i2c transfer by calling host i2c driver function */
ret = i2c_transfer(da9052->adapter, &i2cmsg, 1);
-
if (ret < 0) {
dev_info(&da9052->i2c_client->dev,\
"_%s:master_xfer Failed!!\n", __func__);
@@ -132,10 +175,8 @@ int da9052_i2c_write(struct da9052 *da9052, struct da9052_ssc_msg *msg)
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 */
@@ -145,107 +186,82 @@ int da9052_i2c_read(struct da9052 *da9052, struct da9052_ssc_msg *msg)
i2cmsg[0].addr = da9052->slave_addr ;
i2cmsg[0].len = 1;
i2cmsg[0].buf = &buf[0];
-
- /*To write the data on I2C set flag to zero */
i2cmsg[0].flags = 0;
- /* Read the data from da9052*/
/*Construct a i2c msg for a da9052 driver ssc message request */
i2cmsg[1].addr = da9052->slave_addr ;
i2cmsg[1].len = 1;
i2cmsg[1].buf = &buf[1];
-
- /*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);
+
+#ifdef DA9052_I2C_BUG_WORKAROUND
+ if (!da9052_is_i2c_reg_safe(msg->addr)) {
+ /* Prepare additional message to flush chip I2C registers */
+ i2cmsg[2].addr = da9052->slave_addr;
+ i2cmsg[2].len = 2;
+ i2cmsg[2].flags = 0; /* Write operation */
+ 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__);
+ dev_info(&da9052->i2c_client->dev,
+ "2 - %s:master_xfer Failed!!\n", __func__);
return ret;
}
- msg->data = *i2cmsg[1].buf;
-
+ msg->data = buf[1];
return 0;
}
int da9052_i2c_write_many(struct da9052 *da9052,
struct da9052_ssc_msg *sscmsg, int msg_no)
{
-
struct i2c_msg i2cmsg;
- unsigned char data_buf[MAX_READ_WRITE_CNT+1];
- struct da9052_ssc_msg ctrlb_msg;
- struct da9052_ssc_msg *msg_queue = sscmsg;
int ret = 0;
- /* Flag to check if requested registers are contiguous */
- unsigned char cont_data = 1;
- unsigned char cnt = 0;
-
- /* Check if requested registers are contiguous */
- for (cnt = 1; cnt < msg_no; cnt++) {
- if ((msg_queue[cnt].addr - msg_queue[cnt-1].addr) != 1) {
- /* Difference is not 1, i.e. non-contiguous registers */
- cont_data = 0;
- break;
- }
- }
-
- if (cont_data == 0) {
- /* Requested registers are non-contiguous */
- for (cnt = 0; cnt < msg_no; cnt++) {
- ret = da9052->write(da9052, &msg_queue[cnt]);
- if (ret != 0)
- return ret;
- }
- return 0;
- }
- /*
- * Requested registers are contiguous
- * or PAGE WRITE sequence of I2C transactions is as below
- * (slave_addr + reg_addr + data_1 + data_2 + ...)
- * First read current WRITE MODE via CONTROL_B register of DA9052
- */
- ctrlb_msg.addr = DA9052_CONTROLB_REG;
- ctrlb_msg.data = 0x0;
- ret = da9052->read(da9052, &ctrlb_msg);
-
- if (ret != 0)
- return ret;
-
- /* Check if PAGE WRITE mode is set */
- if (ctrlb_msg.data & DA9052_CONTROLB_WRITEMODE) {
- /* REPEAT WRITE mode is configured */
- /* Now set DA9052 into PAGE WRITE mode */
- ctrlb_msg.data &= ~DA9052_CONTROLB_WRITEMODE;
- ret = da9052->write(da9052, &ctrlb_msg);
-
- if (ret != 0)
- return ret;
- }
-
- /* Put first register address */
- data_buf[0] = msg_queue[0].addr;
-
- for (cnt = 0; cnt < msg_no; cnt++)
- data_buf[cnt+1] = msg_queue[cnt].data;
-
- /* Construct a i2c msg for PAGE WRITE */
+ int safe = 1;
+ unsigned char *data_ptr;
+#ifdef DA9052_I2C_BUG_WORKAROUND
+ unsigned char data_buf[2 * MAX_READ_WRITE_CNT + 2];
+#else
+ unsigned char data_buf[2 * MAX_READ_WRITE_CNT];
+#endif
+
+ BUG_ON(msg_no < 0);
+ BUG_ON(msg_no >= MAX_READ_WRITE_CNT);
+
+ /* Construct a i2c msg for REPEATED WRITE */
i2cmsg.addr = da9052->slave_addr ;
- /* First register address + all data*/
- i2cmsg.len = (msg_no + 1);
+ i2cmsg.len = 2*msg_no;
i2cmsg.buf = data_buf;
-
- /*To write the data on I2C set flag to zero */
i2cmsg.flags = 0;
+ for (data_ptr = data_buf; msg_no; msg_no--) {
+ safe &= da9052_is_i2c_reg_safe(sscmsg->addr);
+ *(data_ptr++) = sscmsg->addr;
+ *(data_ptr++) = sscmsg->data;
+ sscmsg++;
+ }
+#ifdef DA9052_I2C_BUG_WORKAROUND
+ if (!safe) {
+ i2cmsg.len += 2;
+ *(data_ptr++) = i2c_flush_data[0];
+ *data_ptr = i2c_flush_data[1];
+ }
+#endif
+
/* Start the i2c transfer by calling host i2c driver function */
ret = i2c_transfer(da9052->adapter, &i2cmsg, 1);
if (ret < 0) {
- dev_info(&da9052->i2c_client->dev,\
- "1 - i2c_transfer function falied in [%s]!!!\n", __func__);
+ dev_info(&da9052->i2c_client->dev,
+ "1 - i2c_transfer function falied in [%s]!!!\n",
+ __func__);
return ret;
}
@@ -255,83 +271,82 @@ int da9052_i2c_write_many(struct da9052 *da9052,
int da9052_i2c_read_many(struct da9052 *da9052,
struct da9052_ssc_msg *sscmsg, int msg_no)
{
-
- struct i2c_msg i2cmsg;
+#ifdef DA9052_I2C_BUG_WORKAROUND
+ struct i2c_msg i2cmsg[2 * MAX_READ_WRITE_CNT];
+#else
+ struct i2c_msg i2cmsg[2 * MAX_READ_WRITE_CNT + 1];
+#endif
unsigned char data_buf[MAX_READ_WRITE_CNT];
- struct da9052_ssc_msg *msg_queue = sscmsg;
+ struct i2c_msg *msg_ptr = i2cmsg;
int ret = 0;
- /* Flag to check if requested registers are contiguous */
- unsigned char cont_data = 1;
- unsigned char cnt = 0;
-
- /* Check if requested registers are contiguous */
- for (cnt = 1; cnt < msg_no; cnt++) {
- if ((msg_queue[cnt].addr - msg_queue[cnt-1].addr) != 1) {
- /* Difference is not 1, i.e. non-contiguous registers */
- cont_data = 0;
- break;
+ int safe = 1;
+ int last_reg_read = -2;
+ int cnt;
+
+ BUG_ON(msg_no < 0);
+ BUG_ON(msg_no >= MAX_READ_WRITE_CNT);
+
+ /* Construct a i2c msgs for a da9052 driver ssc message request */
+ for (cnt = 0; cnt < msg_no; cnt++) {
+ if ((int)sscmsg[cnt].addr != last_reg_read + 1) {
+ safe &= da9052_is_i2c_reg_safe(sscmsg[cnt].addr);
+
+ /* Build messages for first register, read in a row */
+ msg_ptr->addr = da9052->slave_addr;
+ msg_ptr->len = 1;
+ msg_ptr->buf = &sscmsg[cnt].addr;
+ msg_ptr->flags = 0;
+ msg_ptr++;
+
+ msg_ptr->addr = da9052->slave_addr;
+ msg_ptr->len = 1;
+ msg_ptr->buf = &data_buf[cnt];
+ msg_ptr->flags = I2C_M_RD;
+ msg_ptr++;
+
+ last_reg_read = sscmsg[cnt].addr;
+ } else {
+ /* Increase read counter for consecutive reads */
+ (msg_ptr - 1)->len++;
}
}
- if (cont_data == 0) {
- /* Requested registers are non-contiguous */
- for (cnt = 0; cnt < msg_no; cnt++) {
- ret = da9052->read(da9052, &msg_queue[cnt]);
- if (ret != 0) {
- dev_info(&da9052->i2c_client->dev,\
- "Error in %s", __func__);
- return ret;
- }
- }
- return 0;
+#ifdef DA9052_I2C_BUG_WORKAROUND
+ if (!safe) {
+ /* Prepare additional message to flush chip I2C registers */
+ msg_ptr->addr = da9052->slave_addr;
+ msg_ptr->len = 2;
+ msg_ptr->flags = 0; /* Write operation */
+ msg_ptr->buf = (unsigned char *)i2c_flush_data;
+ msg_ptr++;
}
-
- /*
- * We want to perform PAGE READ via I2C
- * For PAGE READ sequence of I2C transactions is as below
- * (slave_addr + reg_addr) + (slave_addr + data_1 + data_2 + ...)
- */
- /* Copy address of first register */
- data_buf[0] = msg_queue[0].addr;
-
- /* Construct a i2c msg for first transaction of PAGE READ i.e. write */
- i2cmsg.addr = da9052->slave_addr ;
- i2cmsg.len = 1;
- i2cmsg.buf = data_buf;
-
- /*To write the data on I2C set flag to zero */
- i2cmsg.flags = 0;
-
- /* Start the i2c transfer by calling host i2c driver function */
- ret = i2c_transfer(da9052->adapter, &i2cmsg, 1);
- if (ret < 0) {
- dev_info(&da9052->i2c_client->dev,\
- "1 - i2c_transfer function falied in [%s]!!!\n", __func__);
- return ret;
+#endif
+
+ /* Using one transfer seems not to work well with D9052.
+ * Read transfer with additional flush write
+ * Performing many transfers is stable on D9052
+ */
+ for (cnt = 0; cnt < (msg_ptr - i2cmsg) - 1; cnt += 2) {
+ ret = i2c_transfer(da9052->adapter, &i2cmsg[cnt], 2);
+ if (ret < 0) {
+ dev_info(&da9052->i2c_client->dev,
+ "2 - %s:master_xfer Failed on msg[%d]!!\n",
+ __func__, cnt);
+ return ret;
+ }
}
-
- /* Now Read the data from da9052 */
- /* Construct a i2c msg for second transaction of PAGE READ i.e. read */
- i2cmsg.addr = da9052->slave_addr ;
- i2cmsg.len = msg_no;
- i2cmsg.buf = data_buf;
-
- /*To read the data on I2C set flag to I2C_M_RD */
- i2cmsg.flags = I2C_M_RD;
-
- /* Start the i2c transfer by calling host i2c driver function */
- ret = i2c_transfer(da9052->adapter,
- &i2cmsg, 1);
- if (ret < 0) {
- dev_info(&da9052->i2c_client->dev,\
- "2 - i2c_transfer function falied in [%s]!!!\n", __func__);
- return ret;
+ if (cnt < (msg_ptr - i2cmsg)) {
+ ret = i2c_transfer(da9052->adapter, &i2cmsg[cnt], 1);
+ if (ret < 0) {
+ dev_info(&da9052->i2c_client->dev,
+ "2 - %s:master_xfer Failed on msg[%d]!!\n",
+ __func__, cnt);
+ return ret;
+ }
}
- /* Gather READ data */
for (cnt = 0; cnt < msg_no; cnt++)
sscmsg[cnt].data = data_buf[cnt];
-
return 0;
}