All of lore.kernel.org
 help / color / mirror / Atom feed
* [U-Boot] [PATCH 1/4] i2c: i2c-cdns: Detect unsupported sequences for rev 1.0
@ 2016-12-27 22:46 Moritz Fischer
  2016-12-27 22:46 ` [U-Boot] [PATCH 2/4] i2c: i2c-cdns: Reorder timeout loop for interrupt waiting Moritz Fischer
                   ` (3 more replies)
  0 siblings, 4 replies; 9+ messages in thread
From: Moritz Fischer @ 2016-12-27 22:46 UTC (permalink / raw)
  To: u-boot

Revision 1.0 of this IP has a couple of issues, such as not supporting
repeated start conditions for read transfers.

So scan through the list of i2c messages for these conditions
and report an error if they are attempted.

This has been fixed for revision 1.4 of the IP, so only report the error
when the IP can really not do it.

Signed-off-by: Moritz Fischer <moritz.fischer@ettus.com>
Cc: Heiko Schocher <hs@denx.de>
Cc: Michal Simek <michal.simek@xilinx.com>
Cc: u-boot at lists.denx.de
---
 drivers/i2c/i2c-cdns.c | 69 ++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 55 insertions(+), 14 deletions(-)

diff --git a/drivers/i2c/i2c-cdns.c b/drivers/i2c/i2c-cdns.c
index f49f60b..c69e7e8 100644
--- a/drivers/i2c/i2c-cdns.c
+++ b/drivers/i2c/i2c-cdns.c
@@ -67,6 +67,7 @@ struct cdns_i2c_regs {
 
 #define CDNS_I2C_FIFO_DEPTH		16
 #define CDNS_I2C_TRANSFER_SIZE_MAX	255 /* Controller transfer limit */
+#define CDNS_I2C_BROKEN_HOLD_BIT	BIT(0)
 
 #ifdef DEBUG
 static void cdns_i2c_debug_status(struct cdns_i2c_regs *cdns_i2c)
@@ -114,6 +115,13 @@ struct i2c_cdns_bus {
 	int id;
 	unsigned int input_freq;
 	struct cdns_i2c_regs __iomem *regs;	/* register base */
+
+	int hold_flag;
+	u32 quirks;
+};
+
+struct cdns_i2c_platform_data {
+	u32 quirks;
 };
 
 /* Wait for an interrupt */
@@ -236,18 +244,14 @@ static int cdns_i2c_probe_chip(struct udevice *bus, uint chip_addr,
 }
 
 static int cdns_i2c_write_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
-			       u32 len, bool next_is_read)
+			       u32 len)
 {
 	u8 *cur_data = data;
 
 	struct cdns_i2c_regs *regs = i2c_bus->regs;
 
-	setbits_le32(&regs->control, CDNS_I2C_CONTROL_CLR_FIFO |
-		CDNS_I2C_CONTROL_HOLD);
+	setbits_le32(&regs->control, CDNS_I2C_CONTROL_CLR_FIFO);
 
-	/* if next is a read, we need to clear HOLD, doesn't work */
-	if (next_is_read)
-		clrbits_le32(&regs->control, CDNS_I2C_CONTROL_HOLD);
 
 	clrbits_le32(&regs->control, CDNS_I2C_CONTROL_RW);
 
@@ -267,7 +271,9 @@ static int cdns_i2c_write_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
 	}
 
 	/* All done... release the bus */
-	clrbits_le32(&regs->control, CDNS_I2C_CONTROL_HOLD);
+	if (!i2c_bus->hold_flag)
+		clrbits_le32(&regs->control, CDNS_I2C_CONTROL_HOLD);
+
 	/* Wait for the address and data to be sent */
 	if (!cdns_i2c_wait(regs, CDNS_I2C_INTERRUPT_COMP))
 		return -ETIMEDOUT;
@@ -285,7 +291,7 @@ static int cdns_i2c_read_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
 	struct cdns_i2c_regs *regs = i2c_bus->regs;
 
 	/* Check the hardware can handle the requested bytes */
-	if ((len < 0) || (len > CDNS_I2C_TRANSFER_SIZE_MAX))
+	if ((len < 0))
 		return -EINVAL;
 
 	setbits_le32(&regs->control, CDNS_I2C_CONTROL_CLR_FIFO |
@@ -310,7 +316,8 @@ static int cdns_i2c_read_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
 			*(cur_data++) = readl(&regs->data);
 	} while (readl(&regs->transfer_size) != 0);
 	/* All done... release the bus */
-	clrbits_le32(&regs->control, CDNS_I2C_CONTROL_HOLD);
+	if (!i2c_bus->hold_flag)
+		clrbits_le32(&regs->control, CDNS_I2C_CONTROL_HOLD);
 
 #ifdef DEBUG
 	cdns_i2c_debug_status(regs);
@@ -322,19 +329,43 @@ static int cdns_i2c_xfer(struct udevice *dev, struct i2c_msg *msg,
 			 int nmsgs)
 {
 	struct i2c_cdns_bus *i2c_bus = dev_get_priv(dev);
-	int ret;
+	int ret, count;
+	bool hold_quirk;
+
+
+	printf("i2c_xfer: %d messages\n", nmsgs);
+	hold_quirk = !!(i2c_bus->quirks & CDNS_I2C_BROKEN_HOLD_BIT);
+
+	if (nmsgs > 1) {
+		/*
+		 * This controller does not give completion interrupt after a
+		 * master receive message if HOLD bit is set (repeated start),
+		 * resulting in SW timeout. Hence, if a receive message is
+		 * followed by any other message, an error is returned
+		 * indicating that this sequence is not supported.
+		 */
+		for (count = 0; (count < nmsgs - 1) && hold_quirk; count++) {
+			if (msg[count].flags & I2C_M_RD) {
+				printf("Can't do repeated start after a receive message\n");
+				return -EOPNOTSUPP;
+			}
+		}
+
+		i2c_bus->hold_flag = 1;
+		setbits_le32(&i2c_bus->regs->control, CDNS_I2C_CONTROL_HOLD);
+	} else {
+		i2c_bus->hold_flag = 0;
+	}
 
 	debug("i2c_xfer: %d messages\n", nmsgs);
 	for (; nmsgs > 0; nmsgs--, msg++) {
-		bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD);
-
 		debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len);
 		if (msg->flags & I2C_M_RD) {
 			ret = cdns_i2c_read_data(i2c_bus, msg->addr, msg->buf,
 						 msg->len);
 		} else {
 			ret = cdns_i2c_write_data(i2c_bus, msg->addr, msg->buf,
-						  msg->len, next_is_read);
+						  msg->len);
 		}
 		if (ret) {
 			debug("i2c_write: error sending\n");
@@ -348,11 +379,16 @@ static int cdns_i2c_xfer(struct udevice *dev, struct i2c_msg *msg,
 static int cdns_i2c_ofdata_to_platdata(struct udevice *dev)
 {
 	struct i2c_cdns_bus *i2c_bus = dev_get_priv(dev);
+	struct cdns_i2c_platform_data *pdata =
+		(struct cdns_i2c_platform_data *)dev_get_driver_data(dev);
 
 	i2c_bus->regs = (struct cdns_i2c_regs *)dev_get_addr(dev);
 	if (!i2c_bus->regs)
 		return -ENOMEM;
 
+	if (pdata)
+		i2c_bus->quirks = pdata->quirks;
+
 	i2c_bus->input_freq = 100000000; /* TODO hardcode input freq for now */
 
 	return 0;
@@ -364,8 +400,13 @@ static const struct dm_i2c_ops cdns_i2c_ops = {
 	.set_bus_speed = cdns_i2c_set_bus_speed,
 };
 
+static const struct cdns_i2c_platform_data r1p10_i2c_def = {
+	.quirks = CDNS_I2C_BROKEN_HOLD_BIT,
+};
+
 static const struct udevice_id cdns_i2c_of_match[] = {
-	{ .compatible = "cdns,i2c-r1p10" },
+	{ .compatible = "cdns,i2c-r1p10", .data = (ulong)&r1p10_i2c_def },
+	{ .compatible = "cdns,i2c-r1p14" },
 	{ /* end of table */ }
 };
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [U-Boot] [PATCH 2/4] i2c: i2c-cdns: Reorder timeout loop for interrupt waiting
  2016-12-27 22:46 [U-Boot] [PATCH 1/4] i2c: i2c-cdns: Detect unsupported sequences for rev 1.0 Moritz Fischer
@ 2016-12-27 22:46 ` Moritz Fischer
  2017-01-02 13:43   ` Michal Simek
  2016-12-27 22:46 ` [U-Boot] [PATCH 3/4] i2c: i2c-cdns: Implement workaround for hold quirk of the rev 1.0 Moritz Fischer
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 9+ messages in thread
From: Moritz Fischer @ 2016-12-27 22:46 UTC (permalink / raw)
  To: u-boot

Reorder the timeout loop such that we first check if the
condition is already true, and then call udelay() so if
the condition is already true, break early.

Signed-off-by: Moritz Fischer <moritz.fischer@ettus.com>
Cc: Heiko Schocher <hs@denx.de>
Cc: Michal Simek <michal.simek@xilinx.com>
Cc: u-boot at lists.denx.de
---
 drivers/i2c/i2c-cdns.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/i2c/i2c-cdns.c b/drivers/i2c/i2c-cdns.c
index c69e7e8..9a1b520 100644
--- a/drivers/i2c/i2c-cdns.c
+++ b/drivers/i2c/i2c-cdns.c
@@ -130,10 +130,10 @@ static u32 cdns_i2c_wait(struct cdns_i2c_regs *cdns_i2c, u32 mask)
 	int timeout, int_status;
 
 	for (timeout = 0; timeout < 100; timeout++) {
-		udelay(100);
 		int_status = readl(&cdns_i2c->interrupt_status);
 		if (int_status & mask)
 			break;
+		udelay(100);
 	}
 
 	/* Clear interrupt status flags */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [U-Boot] [PATCH 3/4] i2c: i2c-cdns: Implement workaround for hold quirk of the rev 1.0
  2016-12-27 22:46 [U-Boot] [PATCH 1/4] i2c: i2c-cdns: Detect unsupported sequences for rev 1.0 Moritz Fischer
  2016-12-27 22:46 ` [U-Boot] [PATCH 2/4] i2c: i2c-cdns: Reorder timeout loop for interrupt waiting Moritz Fischer
@ 2016-12-27 22:46 ` Moritz Fischer
  2017-01-02 14:31   ` Michal Simek
  2016-12-27 22:46 ` [U-Boot] [PATCH 4/4] i2c: i2c-cdns: No need for dedicated probe function Moritz Fischer
  2017-01-02 14:29 ` [U-Boot] [PATCH 1/4] i2c: i2c-cdns: Detect unsupported sequences for rev 1.0 Michal Simek
  3 siblings, 1 reply; 9+ messages in thread
From: Moritz Fischer @ 2016-12-27 22:46 UTC (permalink / raw)
  To: u-boot

Revision 1.0 of this IP has a quirk where if during a long read transfer
the transfer_size register will go to 0, the master will send a NACK to
the slave prematurely.
The way to work around this is to reprogram the transfer_size register
mid-transfer when the only the receive fifo is known full, i.e. the I2C
bus is known non-active.
The workaround is based on the implementation in the linux-kernel.

Signed-off-by: Moritz Fischer <moritz.fischer@ettus.com>
Cc: Heiko Schocher <hs@denx.de>
Cc: Michal Simek <michal.simek@xilinx.com>
Cc: u-boot at lists.denx.de
---
 drivers/i2c/i2c-cdns.c | 121 ++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 89 insertions(+), 32 deletions(-)

diff --git a/drivers/i2c/i2c-cdns.c b/drivers/i2c/i2c-cdns.c
index 9a1b520..4a46dbf 100644
--- a/drivers/i2c/i2c-cdns.c
+++ b/drivers/i2c/i2c-cdns.c
@@ -17,6 +17,7 @@
 #include <i2c.h>
 #include <fdtdec.h>
 #include <mapmem.h>
+#include <wait_bit.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -67,6 +68,8 @@ struct cdns_i2c_regs {
 
 #define CDNS_I2C_FIFO_DEPTH		16
 #define CDNS_I2C_TRANSFER_SIZE_MAX	255 /* Controller transfer limit */
+#define CDNS_I2C_TRANSFER_SIZE		(CDNS_I2C_TRANSFER_SIZE_MAX - 3)
+
 #define CDNS_I2C_BROKEN_HOLD_BIT	BIT(0)
 
 #ifdef DEBUG
@@ -247,15 +250,20 @@ static int cdns_i2c_write_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
 			       u32 len)
 {
 	u8 *cur_data = data;
-
 	struct cdns_i2c_regs *regs = i2c_bus->regs;
 
+	/* Set the controller in Master transmit mode and clear FIFO */
 	setbits_le32(&regs->control, CDNS_I2C_CONTROL_CLR_FIFO);
-
-
 	clrbits_le32(&regs->control, CDNS_I2C_CONTROL_RW);
 
+	/* Check message size against FIFO depth, and set hold bus bit
+	 * if it is greater than FIFO depth */
+	if (len > CDNS_I2C_FIFO_DEPTH)
+		setbits_le32(&regs->control, CDNS_I2C_CONTROL_HOLD);
+
+	/* Clear the interrupts in status register */
 	writel(0xFF, &regs->interrupt_status);
+
 	writel(addr, &regs->address);
 
 	while (len--) {
@@ -280,48 +288,98 @@ static int cdns_i2c_write_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
 	return 0;
 }
 
+static inline bool cdns_is_hold_quirk(int hold_quirk, int curr_recv_count)
+{
+	return hold_quirk && (curr_recv_count == CDNS_I2C_FIFO_DEPTH + 1);
+}
+
 static int cdns_i2c_read_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
-			      u32 len)
+			      u32 recv_count)
 {
-	u32 status;
-	u32 i = 0;
 	u8 *cur_data = data;
-
-	/* TODO: Fix this */
 	struct cdns_i2c_regs *regs = i2c_bus->regs;
+	int curr_recv_count;
+	int updatetx, hold_quirk;
 
 	/* Check the hardware can handle the requested bytes */
-	if ((len < 0))
+	if ((recv_count < 0))
 		return -EINVAL;
 
+	curr_recv_count = recv_count;
+
+	/* Check for the message size against the FIFO depth */
+	if (recv_count > CDNS_I2C_FIFO_DEPTH)
+		setbits_le32(&regs->control, CDNS_I2C_CONTROL_HOLD);
+
 	setbits_le32(&regs->control, CDNS_I2C_CONTROL_CLR_FIFO |
 		CDNS_I2C_CONTROL_RW);
 
+	if (recv_count > CDNS_I2C_TRANSFER_SIZE) {
+		curr_recv_count = CDNS_I2C_TRANSFER_SIZE;
+		writel(curr_recv_count, &regs->transfer_size);
+	} else {
+		writel(recv_count, &regs->transfer_size);
+	}
+
 	/* Start reading data */
 	writel(addr, &regs->address);
-	writel(len, &regs->transfer_size);
-
-	/* Wait for data */
-	do {
-		status = cdns_i2c_wait(regs, CDNS_I2C_INTERRUPT_COMP |
-			CDNS_I2C_INTERRUPT_DATA);
-		if (!status) {
-			/* Release the bus */
-			clrbits_le32(&regs->control, CDNS_I2C_CONTROL_HOLD);
-			return -ETIMEDOUT;
+
+	updatetx = recv_count > curr_recv_count;
+
+	hold_quirk = (i2c_bus->quirks & CDNS_I2C_BROKEN_HOLD_BIT) && updatetx;
+
+	while (recv_count) {
+		while (readl(&regs->status) & CDNS_I2C_STATUS_RXDV) {
+			if (recv_count < CDNS_I2C_FIFO_DEPTH &&
+			    !i2c_bus->hold_flag) {
+				clrbits_le32(&regs->control,
+					     CDNS_I2C_CONTROL_HOLD);
+			}
+			*(cur_data)++ = readl(&regs->data);
+			recv_count--;
+			curr_recv_count--;
+
+			if (cdns_is_hold_quirk(hold_quirk, curr_recv_count))
+				break;
 		}
-		debug("Read %d bytes\n",
-		      len - readl(&regs->transfer_size));
-		for (; i < len - readl(&regs->transfer_size); i++)
-			*(cur_data++) = readl(&regs->data);
-	} while (readl(&regs->transfer_size) != 0);
-	/* All done... release the bus */
-	if (!i2c_bus->hold_flag)
-		clrbits_le32(&regs->control, CDNS_I2C_CONTROL_HOLD);
 
-#ifdef DEBUG
-	cdns_i2c_debug_status(regs);
-#endif
+		if (cdns_is_hold_quirk(hold_quirk, curr_recv_count)) {
+			/* wait while fifo is full */
+			while (readl(&regs->transfer_size) !=
+				     (curr_recv_count - CDNS_I2C_FIFO_DEPTH))
+				;
+			/*
+			 * Check number of bytes to be received against maximum
+			 * transfer size and update register accordingly.
+			 */
+			if ((recv_count - CDNS_I2C_FIFO_DEPTH) >
+			    CDNS_I2C_TRANSFER_SIZE) {
+				writel(CDNS_I2C_TRANSFER_SIZE,
+				       &regs->transfer_size);
+				curr_recv_count = CDNS_I2C_TRANSFER_SIZE +
+					CDNS_I2C_FIFO_DEPTH;
+			} else {
+				writel(recv_count - CDNS_I2C_FIFO_DEPTH,
+				       &regs->transfer_size);
+				curr_recv_count = recv_count;
+			}
+		} else if (recv_count && !hold_quirk && !curr_recv_count) {
+			writel(addr, &regs->address);
+			if (recv_count > CDNS_I2C_TRANSFER_SIZE) {
+				writel(CDNS_I2C_TRANSFER_SIZE,
+				       &regs->transfer_size);
+				curr_recv_count = CDNS_I2C_TRANSFER_SIZE;
+			} else {
+				writel(recv_count, &regs->transfer_size);
+				curr_recv_count = recv_count;
+			}
+		}
+	}
+
+	/* Wait for the address and data to be sent */
+	if (!cdns_i2c_wait(regs, CDNS_I2C_INTERRUPT_COMP))
+		return -ETIMEDOUT;
+
 	return 0;
 }
 
@@ -332,8 +390,8 @@ static int cdns_i2c_xfer(struct udevice *dev, struct i2c_msg *msg,
 	int ret, count;
 	bool hold_quirk;
 
+	debug("i2c_xfer: %d messages\n", nmsgs);
 
-	printf("i2c_xfer: %d messages\n", nmsgs);
 	hold_quirk = !!(i2c_bus->quirks & CDNS_I2C_BROKEN_HOLD_BIT);
 
 	if (nmsgs > 1) {
@@ -357,7 +415,6 @@ static int cdns_i2c_xfer(struct udevice *dev, struct i2c_msg *msg,
 		i2c_bus->hold_flag = 0;
 	}
 
-	debug("i2c_xfer: %d messages\n", nmsgs);
 	for (; nmsgs > 0; nmsgs--, msg++) {
 		debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len);
 		if (msg->flags & I2C_M_RD) {
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [U-Boot] [PATCH 4/4] i2c: i2c-cdns: No need for dedicated probe function
  2016-12-27 22:46 [U-Boot] [PATCH 1/4] i2c: i2c-cdns: Detect unsupported sequences for rev 1.0 Moritz Fischer
  2016-12-27 22:46 ` [U-Boot] [PATCH 2/4] i2c: i2c-cdns: Reorder timeout loop for interrupt waiting Moritz Fischer
  2016-12-27 22:46 ` [U-Boot] [PATCH 3/4] i2c: i2c-cdns: Implement workaround for hold quirk of the rev 1.0 Moritz Fischer
@ 2016-12-27 22:46 ` Moritz Fischer
  2017-01-02 14:28   ` Michal Simek
  2017-01-02 14:29 ` [U-Boot] [PATCH 1/4] i2c: i2c-cdns: Detect unsupported sequences for rev 1.0 Michal Simek
  3 siblings, 1 reply; 9+ messages in thread
From: Moritz Fischer @ 2016-12-27 22:46 UTC (permalink / raw)
  To: u-boot

The generic probe code in dm works, so get rid of the leftover cruft.

Signed-off-by: Moritz Fischer <moritz.fischer@ettus.com>
Cc: Heiko Schocher <hs@denx.de>
Cc: Michal Simek <michal.simek@xilinx.com>
Cc: u-boot at lists.denx.de
---
 drivers/i2c/i2c-cdns.c | 21 ---------------------
 1 file changed, 21 deletions(-)

diff --git a/drivers/i2c/i2c-cdns.c b/drivers/i2c/i2c-cdns.c
index 4a46dbf..cd5cce0 100644
--- a/drivers/i2c/i2c-cdns.c
+++ b/drivers/i2c/i2c-cdns.c
@@ -226,26 +226,6 @@ static int cdns_i2c_set_bus_speed(struct udevice *dev, unsigned int speed)
 	return 0;
 }
 
-/* Probe to see if a chip is present. */
-static int cdns_i2c_probe_chip(struct udevice *bus, uint chip_addr,
-				uint chip_flags)
-{
-	struct i2c_cdns_bus *i2c_bus = dev_get_priv(bus);
-	struct cdns_i2c_regs *regs = i2c_bus->regs;
-
-	/* Attempt to read a byte */
-	setbits_le32(&regs->control, CDNS_I2C_CONTROL_CLR_FIFO |
-		CDNS_I2C_CONTROL_RW);
-	clrbits_le32(&regs->control, CDNS_I2C_CONTROL_HOLD);
-	writel(0xFF, &regs->interrupt_status);
-	writel(chip_addr, &regs->address);
-	writel(1, &regs->transfer_size);
-
-	return (cdns_i2c_wait(regs, CDNS_I2C_INTERRUPT_COMP |
-		CDNS_I2C_INTERRUPT_NACK) &
-		CDNS_I2C_INTERRUPT_COMP) ? 0 : -ETIMEDOUT;
-}
-
 static int cdns_i2c_write_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
 			       u32 len)
 {
@@ -453,7 +433,6 @@ static int cdns_i2c_ofdata_to_platdata(struct udevice *dev)
 
 static const struct dm_i2c_ops cdns_i2c_ops = {
 	.xfer = cdns_i2c_xfer,
-	.probe_chip = cdns_i2c_probe_chip,
 	.set_bus_speed = cdns_i2c_set_bus_speed,
 };
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [U-Boot] [PATCH 2/4] i2c: i2c-cdns: Reorder timeout loop for interrupt waiting
  2016-12-27 22:46 ` [U-Boot] [PATCH 2/4] i2c: i2c-cdns: Reorder timeout loop for interrupt waiting Moritz Fischer
@ 2017-01-02 13:43   ` Michal Simek
  0 siblings, 0 replies; 9+ messages in thread
From: Michal Simek @ 2017-01-02 13:43 UTC (permalink / raw)
  To: u-boot

On 27.12.2016 23:46, Moritz Fischer wrote:
> Reorder the timeout loop such that we first check if the
> condition is already true, and then call udelay() so if
> the condition is already true, break early.
> 
> Signed-off-by: Moritz Fischer <moritz.fischer@ettus.com>
> Cc: Heiko Schocher <hs@denx.de>
> Cc: Michal Simek <michal.simek@xilinx.com>
> Cc: u-boot at lists.denx.de
> ---
>  drivers/i2c/i2c-cdns.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/i2c/i2c-cdns.c b/drivers/i2c/i2c-cdns.c
> index c69e7e8..9a1b520 100644
> --- a/drivers/i2c/i2c-cdns.c
> +++ b/drivers/i2c/i2c-cdns.c
> @@ -130,10 +130,10 @@ static u32 cdns_i2c_wait(struct cdns_i2c_regs *cdns_i2c, u32 mask)
>  	int timeout, int_status;
>  
>  	for (timeout = 0; timeout < 100; timeout++) {
> -		udelay(100);
>  		int_status = readl(&cdns_i2c->interrupt_status);
>  		if (int_status & mask)
>  			break;
> +		udelay(100);
>  	}
>  
>  	/* Clear interrupt status flags */
> 

Reviewed-by: Michal Simek <michal.simek@xilinx.com>

Thanks,
Michal

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [U-Boot] [PATCH 4/4] i2c: i2c-cdns: No need for dedicated probe function
  2016-12-27 22:46 ` [U-Boot] [PATCH 4/4] i2c: i2c-cdns: No need for dedicated probe function Moritz Fischer
@ 2017-01-02 14:28   ` Michal Simek
  0 siblings, 0 replies; 9+ messages in thread
From: Michal Simek @ 2017-01-02 14:28 UTC (permalink / raw)
  To: u-boot

Hi, +Siva

do you know why this was done in this way? I know I was playing with
similar patch and using xfer function but can't remember why it was
failing.

Thanks,
Michal

On 27.12.2016 23:46, Moritz Fischer wrote:
> The generic probe code in dm works, so get rid of the leftover cruft.
> 
> Signed-off-by: Moritz Fischer <moritz.fischer@ettus.com>
> Cc: Heiko Schocher <hs@denx.de>
> Cc: Michal Simek <michal.simek@xilinx.com>
> Cc: u-boot at lists.denx.de
> ---
>  drivers/i2c/i2c-cdns.c | 21 ---------------------
>  1 file changed, 21 deletions(-)
> 
> diff --git a/drivers/i2c/i2c-cdns.c b/drivers/i2c/i2c-cdns.c
> index 4a46dbf..cd5cce0 100644
> --- a/drivers/i2c/i2c-cdns.c
> +++ b/drivers/i2c/i2c-cdns.c
> @@ -226,26 +226,6 @@ static int cdns_i2c_set_bus_speed(struct udevice *dev, unsigned int speed)
>  	return 0;
>  }
>  
> -/* Probe to see if a chip is present. */
> -static int cdns_i2c_probe_chip(struct udevice *bus, uint chip_addr,
> -				uint chip_flags)
> -{
> -	struct i2c_cdns_bus *i2c_bus = dev_get_priv(bus);
> -	struct cdns_i2c_regs *regs = i2c_bus->regs;
> -
> -	/* Attempt to read a byte */
> -	setbits_le32(&regs->control, CDNS_I2C_CONTROL_CLR_FIFO |
> -		CDNS_I2C_CONTROL_RW);
> -	clrbits_le32(&regs->control, CDNS_I2C_CONTROL_HOLD);
> -	writel(0xFF, &regs->interrupt_status);
> -	writel(chip_addr, &regs->address);
> -	writel(1, &regs->transfer_size);
> -
> -	return (cdns_i2c_wait(regs, CDNS_I2C_INTERRUPT_COMP |
> -		CDNS_I2C_INTERRUPT_NACK) &
> -		CDNS_I2C_INTERRUPT_COMP) ? 0 : -ETIMEDOUT;
> -}
> -
>  static int cdns_i2c_write_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
>  			       u32 len)
>  {
> @@ -453,7 +433,6 @@ static int cdns_i2c_ofdata_to_platdata(struct udevice *dev)
>  
>  static const struct dm_i2c_ops cdns_i2c_ops = {
>  	.xfer = cdns_i2c_xfer,
> -	.probe_chip = cdns_i2c_probe_chip,
>  	.set_bus_speed = cdns_i2c_set_bus_speed,
>  };
>  
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [U-Boot] [PATCH 1/4] i2c: i2c-cdns: Detect unsupported sequences for rev 1.0
  2016-12-27 22:46 [U-Boot] [PATCH 1/4] i2c: i2c-cdns: Detect unsupported sequences for rev 1.0 Moritz Fischer
                   ` (2 preceding siblings ...)
  2016-12-27 22:46 ` [U-Boot] [PATCH 4/4] i2c: i2c-cdns: No need for dedicated probe function Moritz Fischer
@ 2017-01-02 14:29 ` Michal Simek
  2017-01-02 19:35   ` Moritz Fischer
  3 siblings, 1 reply; 9+ messages in thread
From: Michal Simek @ 2017-01-02 14:29 UTC (permalink / raw)
  To: u-boot

+Siva: please test it.

On 27.12.2016 23:46, Moritz Fischer wrote:
> Revision 1.0 of this IP has a couple of issues, such as not supporting
> repeated start conditions for read transfers.
> 
> So scan through the list of i2c messages for these conditions
> and report an error if they are attempted.
> 
> This has been fixed for revision 1.4 of the IP, so only report the error
> when the IP can really not do it.
> 
> Signed-off-by: Moritz Fischer <moritz.fischer@ettus.com>
> Cc: Heiko Schocher <hs@denx.de>
> Cc: Michal Simek <michal.simek@xilinx.com>
> Cc: u-boot at lists.denx.de
> ---
>  drivers/i2c/i2c-cdns.c | 69 ++++++++++++++++++++++++++++++++++++++++----------
>  1 file changed, 55 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/i2c/i2c-cdns.c b/drivers/i2c/i2c-cdns.c
> index f49f60b..c69e7e8 100644
> --- a/drivers/i2c/i2c-cdns.c
> +++ b/drivers/i2c/i2c-cdns.c
> @@ -67,6 +67,7 @@ struct cdns_i2c_regs {
>  
>  #define CDNS_I2C_FIFO_DEPTH		16
>  #define CDNS_I2C_TRANSFER_SIZE_MAX	255 /* Controller transfer limit */
> +#define CDNS_I2C_BROKEN_HOLD_BIT	BIT(0)
>  
>  #ifdef DEBUG
>  static void cdns_i2c_debug_status(struct cdns_i2c_regs *cdns_i2c)
> @@ -114,6 +115,13 @@ struct i2c_cdns_bus {
>  	int id;
>  	unsigned int input_freq;
>  	struct cdns_i2c_regs __iomem *regs;	/* register base */
> +

no reason.

> +	int hold_flag;
> +	u32 quirks;
> +};
> +
> +struct cdns_i2c_platform_data {
> +	u32 quirks;
>  };
>  
>  /* Wait for an interrupt */
> @@ -236,18 +244,14 @@ static int cdns_i2c_probe_chip(struct udevice *bus, uint chip_addr,
>  }
>  
>  static int cdns_i2c_write_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
> -			       u32 len, bool next_is_read)
> +			       u32 len)
>  {
>  	u8 *cur_data = data;
>  
>  	struct cdns_i2c_regs *regs = i2c_bus->regs;
>  
> -	setbits_le32(&regs->control, CDNS_I2C_CONTROL_CLR_FIFO |
> -		CDNS_I2C_CONTROL_HOLD);
> +	setbits_le32(&regs->control, CDNS_I2C_CONTROL_CLR_FIFO);
>  
> -	/* if next is a read, we need to clear HOLD, doesn't work */
> -	if (next_is_read)
> -		clrbits_le32(&regs->control, CDNS_I2C_CONTROL_HOLD);
>  

two blank line after removing this code.

>  	clrbits_le32(&regs->control, CDNS_I2C_CONTROL_RW);
>  
> @@ -267,7 +271,9 @@ static int cdns_i2c_write_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
>  	}
>  
>  	/* All done... release the bus */
> -	clrbits_le32(&regs->control, CDNS_I2C_CONTROL_HOLD);
> +	if (!i2c_bus->hold_flag)
> +		clrbits_le32(&regs->control, CDNS_I2C_CONTROL_HOLD);
> +
>  	/* Wait for the address and data to be sent */
>  	if (!cdns_i2c_wait(regs, CDNS_I2C_INTERRUPT_COMP))
>  		return -ETIMEDOUT;
> @@ -285,7 +291,7 @@ static int cdns_i2c_read_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
>  	struct cdns_i2c_regs *regs = i2c_bus->regs;
>  
>  	/* Check the hardware can handle the requested bytes */
> -	if ((len < 0) || (len > CDNS_I2C_TRANSFER_SIZE_MAX))
> +	if ((len < 0))
>  		return -EINVAL;
>  
>  	setbits_le32(&regs->control, CDNS_I2C_CONTROL_CLR_FIFO |
> @@ -310,7 +316,8 @@ static int cdns_i2c_read_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
>  			*(cur_data++) = readl(&regs->data);
>  	} while (readl(&regs->transfer_size) != 0);
>  	/* All done... release the bus */
> -	clrbits_le32(&regs->control, CDNS_I2C_CONTROL_HOLD);
> +	if (!i2c_bus->hold_flag)
> +		clrbits_le32(&regs->control, CDNS_I2C_CONTROL_HOLD);
>  
>  #ifdef DEBUG
>  	cdns_i2c_debug_status(regs);
> @@ -322,19 +329,43 @@ static int cdns_i2c_xfer(struct udevice *dev, struct i2c_msg *msg,
>  			 int nmsgs)
>  {
>  	struct i2c_cdns_bus *i2c_bus = dev_get_priv(dev);
> -	int ret;
> +	int ret, count;
> +	bool hold_quirk;
> +
> +

ditto.

> +	printf("i2c_xfer: %d messages\n", nmsgs);

debug?


> +	hold_quirk = !!(i2c_bus->quirks & CDNS_I2C_BROKEN_HOLD_BIT);
> +
> +	if (nmsgs > 1) {
> +		/*
> +		 * This controller does not give completion interrupt after a
> +		 * master receive message if HOLD bit is set (repeated start),
> +		 * resulting in SW timeout. Hence, if a receive message is
> +		 * followed by any other message, an error is returned
> +		 * indicating that this sequence is not supported.
> +		 */
> +		for (count = 0; (count < nmsgs - 1) && hold_quirk; count++) {
> +			if (msg[count].flags & I2C_M_RD) {
> +				printf("Can't do repeated start after a receive message\n");
> +				return -EOPNOTSUPP;
> +			}
> +		}
> +
> +		i2c_bus->hold_flag = 1;
> +		setbits_le32(&i2c_bus->regs->control, CDNS_I2C_CONTROL_HOLD);
> +	} else {
> +		i2c_bus->hold_flag = 0;
> +	}
>  
>  	debug("i2c_xfer: %d messages\n", nmsgs);
>  	for (; nmsgs > 0; nmsgs--, msg++) {
> -		bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD);
> -
>  		debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len);
>  		if (msg->flags & I2C_M_RD) {
>  			ret = cdns_i2c_read_data(i2c_bus, msg->addr, msg->buf,
>  						 msg->len);
>  		} else {
>  			ret = cdns_i2c_write_data(i2c_bus, msg->addr, msg->buf,
> -						  msg->len, next_is_read);
> +						  msg->len);
>  		}
>  		if (ret) {
>  			debug("i2c_write: error sending\n");
> @@ -348,11 +379,16 @@ static int cdns_i2c_xfer(struct udevice *dev, struct i2c_msg *msg,
>  static int cdns_i2c_ofdata_to_platdata(struct udevice *dev)
>  {
>  	struct i2c_cdns_bus *i2c_bus = dev_get_priv(dev);
> +	struct cdns_i2c_platform_data *pdata =
> +		(struct cdns_i2c_platform_data *)dev_get_driver_data(dev);
>  
>  	i2c_bus->regs = (struct cdns_i2c_regs *)dev_get_addr(dev);
>  	if (!i2c_bus->regs)
>  		return -ENOMEM;
>  
> +	if (pdata)
> +		i2c_bus->quirks = pdata->quirks;
> +
>  	i2c_bus->input_freq = 100000000; /* TODO hardcode input freq for now */
>  
>  	return 0;
> @@ -364,8 +400,13 @@ static const struct dm_i2c_ops cdns_i2c_ops = {
>  	.set_bus_speed = cdns_i2c_set_bus_speed,
>  };
>  
> +static const struct cdns_i2c_platform_data r1p10_i2c_def = {
> +	.quirks = CDNS_I2C_BROKEN_HOLD_BIT,
> +};
> +
>  static const struct udevice_id cdns_i2c_of_match[] = {
> -	{ .compatible = "cdns,i2c-r1p10" },
> +	{ .compatible = "cdns,i2c-r1p10", .data = (ulong)&r1p10_i2c_def },
> +	{ .compatible = "cdns,i2c-r1p14" },

This should be maybe v2 because you have added this in previous patch.

Thanks,
Michal

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [U-Boot] [PATCH 3/4] i2c: i2c-cdns: Implement workaround for hold quirk of the rev 1.0
  2016-12-27 22:46 ` [U-Boot] [PATCH 3/4] i2c: i2c-cdns: Implement workaround for hold quirk of the rev 1.0 Moritz Fischer
@ 2017-01-02 14:31   ` Michal Simek
  0 siblings, 0 replies; 9+ messages in thread
From: Michal Simek @ 2017-01-02 14:31 UTC (permalink / raw)
  To: u-boot

On 27.12.2016 23:46, Moritz Fischer wrote:
> Revision 1.0 of this IP has a quirk where if during a long read transfer
> the transfer_size register will go to 0, the master will send a NACK to
> the slave prematurely.
> The way to work around this is to reprogram the transfer_size register
> mid-transfer when the only the receive fifo is known full, i.e. the I2C
> bus is known non-active.
> The workaround is based on the implementation in the linux-kernel.
> 
> Signed-off-by: Moritz Fischer <moritz.fischer@ettus.com>
> Cc: Heiko Schocher <hs@denx.de>
> Cc: Michal Simek <michal.simek@xilinx.com>
> Cc: u-boot at lists.denx.de
> ---
>  drivers/i2c/i2c-cdns.c | 121 ++++++++++++++++++++++++++++++++++++-------------
>  1 file changed, 89 insertions(+), 32 deletions(-)
> 
> diff --git a/drivers/i2c/i2c-cdns.c b/drivers/i2c/i2c-cdns.c
> index 9a1b520..4a46dbf 100644
> --- a/drivers/i2c/i2c-cdns.c
> +++ b/drivers/i2c/i2c-cdns.c
> @@ -17,6 +17,7 @@
>  #include <i2c.h>
>  #include <fdtdec.h>
>  #include <mapmem.h>
> +#include <wait_bit.h>
>  
>  DECLARE_GLOBAL_DATA_PTR;
>  
> @@ -67,6 +68,8 @@ struct cdns_i2c_regs {
>  
>  #define CDNS_I2C_FIFO_DEPTH		16
>  #define CDNS_I2C_TRANSFER_SIZE_MAX	255 /* Controller transfer limit */
> +#define CDNS_I2C_TRANSFER_SIZE		(CDNS_I2C_TRANSFER_SIZE_MAX - 3)
> +
>  #define CDNS_I2C_BROKEN_HOLD_BIT	BIT(0)
>  
>  #ifdef DEBUG
> @@ -247,15 +250,20 @@ static int cdns_i2c_write_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
>  			       u32 len)
>  {
>  	u8 *cur_data = data;
> -
>  	struct cdns_i2c_regs *regs = i2c_bus->regs;
>  
> +	/* Set the controller in Master transmit mode and clear FIFO */
>  	setbits_le32(&regs->control, CDNS_I2C_CONTROL_CLR_FIFO);
> -
> -
>  	clrbits_le32(&regs->control, CDNS_I2C_CONTROL_RW);
>  
> +	/* Check message size against FIFO depth, and set hold bus bit
> +	 * if it is greater than FIFO depth */
> +	if (len > CDNS_I2C_FIFO_DEPTH)
> +		setbits_le32(&regs->control, CDNS_I2C_CONTROL_HOLD);
> +
> +	/* Clear the interrupts in status register */
>  	writel(0xFF, &regs->interrupt_status);
> +
>  	writel(addr, &regs->address);
>  
>  	while (len--) {
> @@ -280,48 +288,98 @@ static int cdns_i2c_write_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
>  	return 0;
>  }
>  
> +static inline bool cdns_is_hold_quirk(int hold_quirk, int curr_recv_count)
> +{
> +	return hold_quirk && (curr_recv_count == CDNS_I2C_FIFO_DEPTH + 1);
> +}
> +
>  static int cdns_i2c_read_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
> -			      u32 len)
> +			      u32 recv_count)
>  {
> -	u32 status;
> -	u32 i = 0;
>  	u8 *cur_data = data;
> -
> -	/* TODO: Fix this */
>  	struct cdns_i2c_regs *regs = i2c_bus->regs;
> +	int curr_recv_count;
> +	int updatetx, hold_quirk;
>  
>  	/* Check the hardware can handle the requested bytes */
> -	if ((len < 0))
> +	if ((recv_count < 0))
>  		return -EINVAL;
>  
> +	curr_recv_count = recv_count;
> +
> +	/* Check for the message size against the FIFO depth */
> +	if (recv_count > CDNS_I2C_FIFO_DEPTH)
> +		setbits_le32(&regs->control, CDNS_I2C_CONTROL_HOLD);
> +
>  	setbits_le32(&regs->control, CDNS_I2C_CONTROL_CLR_FIFO |
>  		CDNS_I2C_CONTROL_RW);
>  
> +	if (recv_count > CDNS_I2C_TRANSFER_SIZE) {
> +		curr_recv_count = CDNS_I2C_TRANSFER_SIZE;
> +		writel(curr_recv_count, &regs->transfer_size);
> +	} else {
> +		writel(recv_count, &regs->transfer_size);
> +	}
> +
>  	/* Start reading data */
>  	writel(addr, &regs->address);
> -	writel(len, &regs->transfer_size);
> -
> -	/* Wait for data */
> -	do {
> -		status = cdns_i2c_wait(regs, CDNS_I2C_INTERRUPT_COMP |
> -			CDNS_I2C_INTERRUPT_DATA);
> -		if (!status) {
> -			/* Release the bus */
> -			clrbits_le32(&regs->control, CDNS_I2C_CONTROL_HOLD);
> -			return -ETIMEDOUT;
> +
> +	updatetx = recv_count > curr_recv_count;
> +
> +	hold_quirk = (i2c_bus->quirks & CDNS_I2C_BROKEN_HOLD_BIT) && updatetx;
> +
> +	while (recv_count) {
> +		while (readl(&regs->status) & CDNS_I2C_STATUS_RXDV) {
> +			if (recv_count < CDNS_I2C_FIFO_DEPTH &&
> +			    !i2c_bus->hold_flag) {
> +				clrbits_le32(&regs->control,
> +					     CDNS_I2C_CONTROL_HOLD);
> +			}
> +			*(cur_data)++ = readl(&regs->data);
> +			recv_count--;
> +			curr_recv_count--;
> +
> +			if (cdns_is_hold_quirk(hold_quirk, curr_recv_count))
> +				break;
>  		}
> -		debug("Read %d bytes\n",
> -		      len - readl(&regs->transfer_size));
> -		for (; i < len - readl(&regs->transfer_size); i++)
> -			*(cur_data++) = readl(&regs->data);
> -	} while (readl(&regs->transfer_size) != 0);
> -	/* All done... release the bus */
> -	if (!i2c_bus->hold_flag)
> -		clrbits_le32(&regs->control, CDNS_I2C_CONTROL_HOLD);
>  
> -#ifdef DEBUG
> -	cdns_i2c_debug_status(regs);
> -#endif
> +		if (cdns_is_hold_quirk(hold_quirk, curr_recv_count)) {
> +			/* wait while fifo is full */
> +			while (readl(&regs->transfer_size) !=
> +				     (curr_recv_count - CDNS_I2C_FIFO_DEPTH))
> +				;
> +			/*
> +			 * Check number of bytes to be received against maximum
> +			 * transfer size and update register accordingly.
> +			 */
> +			if ((recv_count - CDNS_I2C_FIFO_DEPTH) >
> +			    CDNS_I2C_TRANSFER_SIZE) {
> +				writel(CDNS_I2C_TRANSFER_SIZE,
> +				       &regs->transfer_size);
> +				curr_recv_count = CDNS_I2C_TRANSFER_SIZE +
> +					CDNS_I2C_FIFO_DEPTH;
> +			} else {
> +				writel(recv_count - CDNS_I2C_FIFO_DEPTH,
> +				       &regs->transfer_size);
> +				curr_recv_count = recv_count;
> +			}
> +		} else if (recv_count && !hold_quirk && !curr_recv_count) {
> +			writel(addr, &regs->address);
> +			if (recv_count > CDNS_I2C_TRANSFER_SIZE) {
> +				writel(CDNS_I2C_TRANSFER_SIZE,
> +				       &regs->transfer_size);
> +				curr_recv_count = CDNS_I2C_TRANSFER_SIZE;
> +			} else {
> +				writel(recv_count, &regs->transfer_size);
> +				curr_recv_count = recv_count;
> +			}
> +		}
> +	}
> +
> +	/* Wait for the address and data to be sent */
> +	if (!cdns_i2c_wait(regs, CDNS_I2C_INTERRUPT_COMP))
> +		return -ETIMEDOUT;
> +
>  	return 0;
>  }
>  
> @@ -332,8 +390,8 @@ static int cdns_i2c_xfer(struct udevice *dev, struct i2c_msg *msg,
>  	int ret, count;
>  	bool hold_quirk;
>  
> +	debug("i2c_xfer: %d messages\n", nmsgs);
>  
> -	printf("i2c_xfer: %d messages\n", nmsgs);

revert back?



>  	hold_quirk = !!(i2c_bus->quirks & CDNS_I2C_BROKEN_HOLD_BIT);
>  
>  	if (nmsgs > 1) {
> @@ -357,7 +415,6 @@ static int cdns_i2c_xfer(struct udevice *dev, struct i2c_msg *msg,
>  		i2c_bus->hold_flag = 0;
>  	}
>  
> -	debug("i2c_xfer: %d messages\n", nmsgs);

reason?

>  	for (; nmsgs > 0; nmsgs--, msg++) {
>  		debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len);
>  		if (msg->flags & I2C_M_RD) {
> 

Thanks,
Michal

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [U-Boot] [PATCH 1/4] i2c: i2c-cdns: Detect unsupported sequences for rev 1.0
  2017-01-02 14:29 ` [U-Boot] [PATCH 1/4] i2c: i2c-cdns: Detect unsupported sequences for rev 1.0 Michal Simek
@ 2017-01-02 19:35   ` Moritz Fischer
  0 siblings, 0 replies; 9+ messages in thread
From: Moritz Fischer @ 2017-01-02 19:35 UTC (permalink / raw)
  To: u-boot

Hi Michal,

On Mon, Jan 2, 2017 at 6:29 AM, Michal Simek <michal.simek@xilinx.com> wrote:
> +Siva: please test it.
>
> On 27.12.2016 23:46, Moritz Fischer wrote:
>> Revision 1.0 of this IP has a couple of issues, such as not supporting
>> repeated start conditions for read transfers.
>>
>> So scan through the list of i2c messages for these conditions
>> and report an error if they are attempted.
>>
>> This has been fixed for revision 1.4 of the IP, so only report the error
>> when the IP can really not do it.
>>
>> Signed-off-by: Moritz Fischer <moritz.fischer@ettus.com>
>> Cc: Heiko Schocher <hs@denx.de>
>> Cc: Michal Simek <michal.simek@xilinx.com>
>> Cc: u-boot at lists.denx.de
>> ---
>>  drivers/i2c/i2c-cdns.c | 69 ++++++++++++++++++++++++++++++++++++++++----------
>>  1 file changed, 55 insertions(+), 14 deletions(-)
>>
>> diff --git a/drivers/i2c/i2c-cdns.c b/drivers/i2c/i2c-cdns.c
>> index f49f60b..c69e7e8 100644
>> --- a/drivers/i2c/i2c-cdns.c
>> +++ b/drivers/i2c/i2c-cdns.c
>> @@ -67,6 +67,7 @@ struct cdns_i2c_regs {
>>
>>  #define CDNS_I2C_FIFO_DEPTH          16
>>  #define CDNS_I2C_TRANSFER_SIZE_MAX   255 /* Controller transfer limit */
>> +#define CDNS_I2C_BROKEN_HOLD_BIT     BIT(0)
>>
>>  #ifdef DEBUG
>>  static void cdns_i2c_debug_status(struct cdns_i2c_regs *cdns_i2c)
>> @@ -114,6 +115,13 @@ struct i2c_cdns_bus {
>>       int id;
>>       unsigned int input_freq;
>>       struct cdns_i2c_regs __iomem *regs;     /* register base */
>> +
>
> no reason.
>
>> +     int hold_flag;
>> +     u32 quirks;
>> +};
>> +
>> +struct cdns_i2c_platform_data {
>> +     u32 quirks;
>>  };
>>
>>  /* Wait for an interrupt */
>> @@ -236,18 +244,14 @@ static int cdns_i2c_probe_chip(struct udevice *bus, uint chip_addr,
>>  }
>>
>>  static int cdns_i2c_write_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
>> -                            u32 len, bool next_is_read)
>> +                            u32 len)
>>  {
>>       u8 *cur_data = data;
>>
>>       struct cdns_i2c_regs *regs = i2c_bus->regs;
>>
>> -     setbits_le32(&regs->control, CDNS_I2C_CONTROL_CLR_FIFO |
>> -             CDNS_I2C_CONTROL_HOLD);
>> +     setbits_le32(&regs->control, CDNS_I2C_CONTROL_CLR_FIFO);
>>
>> -     /* if next is a read, we need to clear HOLD, doesn't work */
>> -     if (next_is_read)
>> -             clrbits_le32(&regs->control, CDNS_I2C_CONTROL_HOLD);
>>
>
> two blank line after removing this code.

Whoops...
>
>>       clrbits_le32(&regs->control, CDNS_I2C_CONTROL_RW);
>>
>> @@ -267,7 +271,9 @@ static int cdns_i2c_write_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
>>       }
>>
>>       /* All done... release the bus */
>> -     clrbits_le32(&regs->control, CDNS_I2C_CONTROL_HOLD);
>> +     if (!i2c_bus->hold_flag)
>> +             clrbits_le32(&regs->control, CDNS_I2C_CONTROL_HOLD);
>> +
>>       /* Wait for the address and data to be sent */
>>       if (!cdns_i2c_wait(regs, CDNS_I2C_INTERRUPT_COMP))
>>               return -ETIMEDOUT;
>> @@ -285,7 +291,7 @@ static int cdns_i2c_read_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
>>       struct cdns_i2c_regs *regs = i2c_bus->regs;
>>
>>       /* Check the hardware can handle the requested bytes */
>> -     if ((len < 0) || (len > CDNS_I2C_TRANSFER_SIZE_MAX))
>> +     if ((len < 0))
>>               return -EINVAL;
>>
>>       setbits_le32(&regs->control, CDNS_I2C_CONTROL_CLR_FIFO |
>> @@ -310,7 +316,8 @@ static int cdns_i2c_read_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
>>                       *(cur_data++) = readl(&regs->data);
>>       } while (readl(&regs->transfer_size) != 0);
>>       /* All done... release the bus */
>> -     clrbits_le32(&regs->control, CDNS_I2C_CONTROL_HOLD);
>> +     if (!i2c_bus->hold_flag)
>> +             clrbits_le32(&regs->control, CDNS_I2C_CONTROL_HOLD);
>>
>>  #ifdef DEBUG
>>       cdns_i2c_debug_status(regs);
>> @@ -322,19 +329,43 @@ static int cdns_i2c_xfer(struct udevice *dev, struct i2c_msg *msg,
>>                        int nmsgs)
>>  {
>>       struct i2c_cdns_bus *i2c_bus = dev_get_priv(dev);
>> -     int ret;
>> +     int ret, count;
>> +     bool hold_quirk;
>> +
>> +
>
> ditto.
>
>> +     printf("i2c_xfer: %d messages\n", nmsgs);
>
> debug?

Yeah, will fix.
>
>
>> +     hold_quirk = !!(i2c_bus->quirks & CDNS_I2C_BROKEN_HOLD_BIT);
>> +
>> +     if (nmsgs > 1) {
>> +             /*
>> +              * This controller does not give completion interrupt after a
>> +              * master receive message if HOLD bit is set (repeated start),
>> +              * resulting in SW timeout. Hence, if a receive message is
>> +              * followed by any other message, an error is returned
>> +              * indicating that this sequence is not supported.
>> +              */
>> +             for (count = 0; (count < nmsgs - 1) && hold_quirk; count++) {
>> +                     if (msg[count].flags & I2C_M_RD) {
>> +                             printf("Can't do repeated start after a receive message\n");
>> +                             return -EOPNOTSUPP;
>> +                     }
>> +             }
>> +
>> +             i2c_bus->hold_flag = 1;
>> +             setbits_le32(&i2c_bus->regs->control, CDNS_I2C_CONTROL_HOLD);
>> +     } else {
>> +             i2c_bus->hold_flag = 0;
>> +     }
>>
>>       debug("i2c_xfer: %d messages\n", nmsgs);
>>       for (; nmsgs > 0; nmsgs--, msg++) {
>> -             bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD);
>> -
>>               debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len);
>>               if (msg->flags & I2C_M_RD) {
>>                       ret = cdns_i2c_read_data(i2c_bus, msg->addr, msg->buf,
>>                                                msg->len);
>>               } else {
>>                       ret = cdns_i2c_write_data(i2c_bus, msg->addr, msg->buf,
>> -                                               msg->len, next_is_read);
>> +                                               msg->len);
>>               }
>>               if (ret) {
>>                       debug("i2c_write: error sending\n");
>> @@ -348,11 +379,16 @@ static int cdns_i2c_xfer(struct udevice *dev, struct i2c_msg *msg,
>>  static int cdns_i2c_ofdata_to_platdata(struct udevice *dev)
>>  {
>>       struct i2c_cdns_bus *i2c_bus = dev_get_priv(dev);
>> +     struct cdns_i2c_platform_data *pdata =
>> +             (struct cdns_i2c_platform_data *)dev_get_driver_data(dev);
>>
>>       i2c_bus->regs = (struct cdns_i2c_regs *)dev_get_addr(dev);
>>       if (!i2c_bus->regs)
>>               return -ENOMEM;
>>
>> +     if (pdata)
>> +             i2c_bus->quirks = pdata->quirks;
>> +
>>       i2c_bus->input_freq = 100000000; /* TODO hardcode input freq for now */
>>
>>       return 0;
>> @@ -364,8 +400,13 @@ static const struct dm_i2c_ops cdns_i2c_ops = {
>>       .set_bus_speed = cdns_i2c_set_bus_speed,
>>  };
>>
>> +static const struct cdns_i2c_platform_data r1p10_i2c_def = {
>> +     .quirks = CDNS_I2C_BROKEN_HOLD_BIT,
>> +};
>> +
>>  static const struct udevice_id cdns_i2c_of_match[] = {
>> -     { .compatible = "cdns,i2c-r1p10" },
>> +     { .compatible = "cdns,i2c-r1p10", .data = (ulong)&r1p10_i2c_def },
>> +     { .compatible = "cdns,i2c-r1p14" },
>
> This should be maybe v2 because you have added this in previous patch.

Yeah, the patch series needs to be rebased on top of the other patches
you already applied.
Tbh, I should've just sent all that together as single series, sorry
for the noise

Cheers,

Moritz

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2017-01-02 19:35 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-12-27 22:46 [U-Boot] [PATCH 1/4] i2c: i2c-cdns: Detect unsupported sequences for rev 1.0 Moritz Fischer
2016-12-27 22:46 ` [U-Boot] [PATCH 2/4] i2c: i2c-cdns: Reorder timeout loop for interrupt waiting Moritz Fischer
2017-01-02 13:43   ` Michal Simek
2016-12-27 22:46 ` [U-Boot] [PATCH 3/4] i2c: i2c-cdns: Implement workaround for hold quirk of the rev 1.0 Moritz Fischer
2017-01-02 14:31   ` Michal Simek
2016-12-27 22:46 ` [U-Boot] [PATCH 4/4] i2c: i2c-cdns: No need for dedicated probe function Moritz Fischer
2017-01-02 14:28   ` Michal Simek
2017-01-02 14:29 ` [U-Boot] [PATCH 1/4] i2c: i2c-cdns: Detect unsupported sequences for rev 1.0 Michal Simek
2017-01-02 19:35   ` Moritz Fischer

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.