Patchwork [U-Boot] i2c: add NACK logic in write

login
register
mail settings
Submitter Akshay Saraswat
Date March 25, 2013, 11:17 a.m.
Message ID <1364210238-15751-1-git-send-email-akshay.s@samsung.com>
Download mbox | patch
Permalink /patch/230622/
State New
Delegated to: Minkyu Kang
Headers show

Comments

Akshay Saraswat - March 25, 2013, 11:17 a.m.
From: Naveen Krishna Chatradhi <ch.naveen@samsung.com>

Adding NACK logic for i2c write.
Verified by reading and writing to device with address 9 on i2c-0.

Signed-off-by: Naveen Krishna Chatradhi <ch.naveen@samsung.com>
Signed-off-by: Akshay Saraswat <akshay.s@samsung.com>
---
 drivers/i2c/s3c24x0_i2c.c | 111 +++++++++++++++++++++++-----------------------
 1 file changed, 56 insertions(+), 55 deletions(-)
Vadim Bendebury - April 23, 2013, 2:44 a.m.
On Mon, Mar 25, 2013 at 4:17 AM, Akshay Saraswat <akshay.s@samsung.com> wrote:
>
> From: Naveen Krishna Chatradhi <ch.naveen@samsung.com>
>
> Adding NACK logic for i2c write.
> Verified by reading and writing to device with address 9 on i2c-0.
>
> Signed-off-by: Naveen Krishna Chatradhi <ch.naveen@samsung.com>
> Signed-off-by: Akshay Saraswat <akshay.s@samsung.com>
> ---
>  drivers/i2c/s3c24x0_i2c.c | 111
> +++++++++++++++++++++++-----------------------
>  1 file changed, 56 insertions(+), 55 deletions(-)
>
> diff --git a/drivers/i2c/s3c24x0_i2c.c b/drivers/i2c/s3c24x0_i2c.c
> index 769a2ba..d2b4eb0 100644
> --- a/drivers/i2c/s3c24x0_i2c.c
> +++ b/drivers/i2c/s3c24x0_i2c.c
> @@ -201,6 +201,35 @@ unsigned int i2c_get_bus_num(void)
>  }
>  #endif
>
> +/*
> + * Verify the whether I2C ACK was received or not
> + *
> + * @param i2c  pointer to I2C register base
> + * @param buf  array of data
> + * @param len  length of data
> + * return      I2C_OK when transmission done
> + *             I2C_NACK otherwise
> + */
> +static int i2c_send_verify(struct s3c24x0_i2c *i2c, unsigned char buf[],
> +                                                       unsigned char len)
> +{
> +       int i, result = I2C_OK;
> +
> +       if (IsACK(i2c)) {
> +               for (i = 0; (i < len) && (result == I2C_OK); i++) {
> +                       writel(buf[i], &i2c->iicds);
> +                       ReadWriteByte(i2c);
> +                       result = WaitForXfer(i2c);
> +                       if (!IsACK(i2c))
> +                               result = I2C_NACK;
> +               }
> +       } else {
> +               result = I2C_NACK;
> +       }
> +
> +       return result;
> +}
> +
>  void i2c_init(int speed, int slaveadd)
>  {
>         struct s3c24x0_i2c *i2c;
> @@ -302,41 +331,30 @@ static int i2c_transfer(struct s3c24x0_i2c *i2c,
>                 return I2C_NOK_TOUT;
>
>         writel(readl(&i2c->iiccon) | I2CCON_ACKGEN, &i2c->iiccon);
> -       result = I2C_OK;
> +
> +       if (addr && addr_len) {
> +               writel(chip, &i2c->iicds);
> +               /* send START */
> +               writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP,
> +                       &i2c->iicstat);
> +               if (WaitForXfer(i2c) == I2C_OK)
> +                       result = i2c_send_verify(i2c, addr, addr_len);
> +               else
> +                       result = I2C_NACK;
> +       } else
> +               result = I2C_NACK;
>
>         switch (cmd_type) {
>         case I2C_WRITE:
> -               if (addr && addr_len) {
> -                       writel(chip, &i2c->iicds);
> -                       /* send START */
> -                       writel(I2C_MODE_MT | I2C_TXRX_ENA |
> I2C_START_STOP,
> -                              &i2c->iicstat);
> -                       i = 0;
> -                       while ((i < addr_len) && (result == I2C_OK)) {
> -                               result = WaitForXfer(i2c);
> -                               writel(addr[i], &i2c->iicds);
> -                               ReadWriteByte(i2c);
> -                               i++;
> -                       }
> -                       i = 0;
> -                       while ((i < data_len) && (result == I2C_OK)) {
> -                               result = WaitForXfer(i2c);
> -                               writel(data[i], &i2c->iicds);
> -                               ReadWriteByte(i2c);
> -                               i++;
> -                       }
> -               } else {
> +               if (result == I2C_OK)
> +                       result = i2c_send_verify(i2c, data, data_len);
> +               else {
>                         writel(chip, &i2c->iicds);
>                         /* send START */
>                         writel(I2C_MODE_MT | I2C_TXRX_ENA |
> I2C_START_STOP,
> -                              &i2c->iicstat);
> -                       i = 0;
> -                       while ((i < data_len) && (result = I2C_OK)) {
> -                               result = WaitForXfer(i2c);
> -                               writel(data[i], &i2c->iicds);
> -                               ReadWriteByte(i2c);
> -                               i++;
> -                       }
> +                               &i2c->iicstat);
> +                       if (WaitForXfer(i2c) == I2C_OK)
> +                               result = i2c_send_verify(i2c, data,
> data_len);
>                 }
>
>                 if (result == I2C_OK)
> @@ -348,42 +366,25 @@ static int i2c_transfer(struct s3c24x0_i2c *i2c,
>                 break;
>
>         case I2C_READ:
> -               if (addr && addr_len) {
> -                       writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat);
> +               if (result == I2C_OK) {
> +                       writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat);
>                         writel(chip, &i2c->iicds);
>                         /* send START */
>                         writel(readl(&i2c->iicstat) | I2C_START_STOP,
>                                &i2c->iicstat);
> -                       result = WaitForXfer(i2c);
> -                       if (IsACK(i2c)) {
> -                               i = 0;
> -                               while ((i < addr_len) && (result ==
> I2C_OK)) {
> -                                       writel(addr[i], &i2c->iicds);
> -                                       ReadWriteByte(i2c);
> -                                       result = WaitForXfer(i2c);
> -                                       i++;
> -                               }
> -
> -                               writel(chip, &i2c->iicds);
> -                               /* resend START */
> -                               writel(I2C_MODE_MR | I2C_TXRX_ENA |
> -                                      I2C_START_STOP, &i2c->iicstat);
>                         ReadWriteByte(i2c);
>                         result = WaitForXfer(i2c);
> -                               i = 0;
> -                               while ((i < data_len) && (result ==
> I2C_OK)) {
> -                                       /* disable ACK for final READ */
> -                                       if (i == data_len - 1)
> -                                               writel(readl(&i2c->iiccon)
> +                       i = 0;
> +                       while ((i < data_len) && (result == I2C_OK)) {
> +                               /* disable ACK for final READ */
> +                               if (i == data_len - 1)
> +                                       writel(readl(&i2c->iiccon)
>                                                         & ~I2CCON_ACKGEN,
>                                                         &i2c->iiccon);
>                                 ReadWriteByte(i2c);
>                                 result = WaitForXfer(i2c);
> -                                       data[i] = readl(&i2c->iicds);
> -                                       i++;
> -                               }
> -                       } else {
> -                               result = I2C_NACK;
> +                               data[i] = readl(&i2c->iicds);
> +                               i++;
>                         }
>
>                 } else {
> --
> 1.8.0
>

Guys, can we please hold on to this. I have a version which was
debugged and worked with the Infineon TPM, (which is generates NACKs
quite liberally, and not just on writes, but on reads too). I will
submit it a bit later.

--vb




> _______________________________________________
> U-Boot mailing list
> U-Boot@lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot

Patch

diff --git a/drivers/i2c/s3c24x0_i2c.c b/drivers/i2c/s3c24x0_i2c.c
index 769a2ba..d2b4eb0 100644
--- a/drivers/i2c/s3c24x0_i2c.c
+++ b/drivers/i2c/s3c24x0_i2c.c
@@ -201,6 +201,35 @@  unsigned int i2c_get_bus_num(void)
 }
 #endif
 
+/*
+ * Verify the whether I2C ACK was received or not
+ *
+ * @param i2c	pointer to I2C register base
+ * @param buf	array of data
+ * @param len	length of data
+ * return	I2C_OK when transmission done
+ *		I2C_NACK otherwise
+ */
+static int i2c_send_verify(struct s3c24x0_i2c *i2c, unsigned char buf[],
+							unsigned char len)
+{
+	int i, result = I2C_OK;
+
+	if (IsACK(i2c)) {
+		for (i = 0; (i < len) && (result == I2C_OK); i++) {
+			writel(buf[i], &i2c->iicds);
+			ReadWriteByte(i2c);
+			result = WaitForXfer(i2c);
+			if (!IsACK(i2c))
+				result = I2C_NACK;
+		}
+	} else {
+		result = I2C_NACK;
+	}
+
+	return result;
+}
+
 void i2c_init(int speed, int slaveadd)
 {
 	struct s3c24x0_i2c *i2c;
@@ -302,41 +331,30 @@  static int i2c_transfer(struct s3c24x0_i2c *i2c,
 		return I2C_NOK_TOUT;
 
 	writel(readl(&i2c->iiccon) | I2CCON_ACKGEN, &i2c->iiccon);
-	result = I2C_OK;
+
+	if (addr && addr_len) {
+		writel(chip, &i2c->iicds);
+		/* send START */
+		writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP,
+			&i2c->iicstat);
+		if (WaitForXfer(i2c) == I2C_OK)
+			result = i2c_send_verify(i2c, addr, addr_len);
+		else
+			result = I2C_NACK;
+	} else
+		result = I2C_NACK;
 
 	switch (cmd_type) {
 	case I2C_WRITE:
-		if (addr && addr_len) {
-			writel(chip, &i2c->iicds);
-			/* send START */
-			writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP,
-			       &i2c->iicstat);
-			i = 0;
-			while ((i < addr_len) && (result == I2C_OK)) {
-				result = WaitForXfer(i2c);
-				writel(addr[i], &i2c->iicds);
-				ReadWriteByte(i2c);
-				i++;
-			}
-			i = 0;
-			while ((i < data_len) && (result == I2C_OK)) {
-				result = WaitForXfer(i2c);
-				writel(data[i], &i2c->iicds);
-				ReadWriteByte(i2c);
-				i++;
-			}
-		} else {
+		if (result == I2C_OK)
+			result = i2c_send_verify(i2c, data, data_len);
+		else {
 			writel(chip, &i2c->iicds);
 			/* send START */
 			writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP,
-			       &i2c->iicstat);
-			i = 0;
-			while ((i < data_len) && (result = I2C_OK)) {
-				result = WaitForXfer(i2c);
-				writel(data[i], &i2c->iicds);
-				ReadWriteByte(i2c);
-				i++;
-			}
+				&i2c->iicstat);
+			if (WaitForXfer(i2c) == I2C_OK)
+				result = i2c_send_verify(i2c, data, data_len);
 		}
 
 		if (result == I2C_OK)
@@ -348,42 +366,25 @@  static int i2c_transfer(struct s3c24x0_i2c *i2c,
 		break;
 
 	case I2C_READ:
-		if (addr && addr_len) {
-			writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat);
+		if (result == I2C_OK) {
+			writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat);
 			writel(chip, &i2c->iicds);
 			/* send START */
 			writel(readl(&i2c->iicstat) | I2C_START_STOP,
 			       &i2c->iicstat);
-			result = WaitForXfer(i2c);
-			if (IsACK(i2c)) {
-				i = 0;
-				while ((i < addr_len) && (result == I2C_OK)) {
-					writel(addr[i], &i2c->iicds);
-					ReadWriteByte(i2c);
-					result = WaitForXfer(i2c);
-					i++;
-				}
-
-				writel(chip, &i2c->iicds);
-				/* resend START */
-				writel(I2C_MODE_MR | I2C_TXRX_ENA |
-				       I2C_START_STOP, &i2c->iicstat);
 			ReadWriteByte(i2c);
 			result = WaitForXfer(i2c);
-				i = 0;
-				while ((i < data_len) && (result == I2C_OK)) {
-					/* disable ACK for final READ */
-					if (i == data_len - 1)
-						writel(readl(&i2c->iiccon)
+			i = 0;
+			while ((i < data_len) && (result == I2C_OK)) {
+				/* disable ACK for final READ */
+				if (i == data_len - 1)
+					writel(readl(&i2c->iiccon)
 							& ~I2CCON_ACKGEN,
 							&i2c->iiccon);
 				ReadWriteByte(i2c);
 				result = WaitForXfer(i2c);
-					data[i] = readl(&i2c->iicds);
-					i++;
-				}
-			} else {
-				result = I2C_NACK;
+				data[i] = readl(&i2c->iicds);
+				i++;
 			}
 
 		} else {