mirror of
https://github.com/archlinuxarm/PKGBUILDs.git
synced 2024-11-08 22:45:43 +00:00
434 lines
12 KiB
Diff
434 lines
12 KiB
Diff
From 7c0c7fc3189f456f1899bf4aa0a27e3f71f6a808 Mon Sep 17 00:00:00 2001
|
|
From: Wayne Zou <b36644@freescale.com>
|
|
Date: Mon, 21 Nov 2011 14:44:33 +0800
|
|
Subject: [PATCH] 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)
|
|
---
|
|
drivers/mfd/da9052-i2c.c | 317 ++++++++++++++++++++++++----------------------
|
|
1 files changed, 166 insertions(+), 151 deletions(-)
|
|
|
|
diff --git a/drivers/mfd/da9052-i2c.c b/drivers/mfd/da9052-i2c.c
|
|
index 6209e97..457523f 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;
|
|
}
|
|
|
|
--
|
|
1.5.4.4
|
|
|