All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] i2c: cadence: Clear HOLD bit before xfer_size register rolls over
@ 2020-11-24  7:46 ` Raviteja Narayanam
  0 siblings, 0 replies; 6+ messages in thread
From: Raviteja Narayanam @ 2020-11-24  7:46 UTC (permalink / raw)
  To: linux-i2c
  Cc: michal.simek, linux-arm-kernel, linux-kernel, git, Raviteja Narayanam

On Xilinx zynq SOC if the delay between address register write and
control register write in cdns_mrecv function is more, the xfer size
register rolls over and controller is stuck. This is an IP bug and
is resolved in later versions of IP.

To avoid this scenario, disable the interrupts on the current processor
core between the two register writes and enable them later. This can
help achieve the timing constraint.

Signed-off-by: Raviteja Narayanam <raviteja.narayanam@xilinx.com>
---
 drivers/i2c/busses/i2c-cadence.c | 48 ++++++++++++++++++++++++++++++++++------
 1 file changed, 41 insertions(+), 7 deletions(-)

diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c
index e4b7f2a..81b7c45 100644
--- a/drivers/i2c/busses/i2c-cadence.c
+++ b/drivers/i2c/busses/i2c-cadence.c
@@ -578,6 +578,11 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id)
 {
 	unsigned int ctrl_reg;
 	unsigned int isr_status;
+	unsigned long flags;
+	bool hold_clear = false;
+	bool irq_save = false;
+
+	u32 addr;
 
 	id->p_recv_buf = id->p_msg->buf;
 	id->recv_count = id->p_msg->len;
@@ -618,14 +623,43 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id)
 		cdns_i2c_writereg(id->recv_count, CDNS_I2C_XFER_SIZE_OFFSET);
 	}
 
-	/* Set the slave address in address register - triggers operation */
-	cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK,
-						CDNS_I2C_ADDR_OFFSET);
-	/* Clear the bus hold flag if bytes to receive is less than FIFO size */
+	/* Determine hold_clear based on number of bytes to receive and hold flag */
 	if (!id->bus_hold_flag &&
-		((id->p_msg->flags & I2C_M_RECV_LEN) != I2C_M_RECV_LEN) &&
-		(id->recv_count <= CDNS_I2C_FIFO_DEPTH))
-			cdns_i2c_clear_bus_hold(id);
+	    ((id->p_msg->flags & I2C_M_RECV_LEN) != I2C_M_RECV_LEN) &&
+	    (id->recv_count <= CDNS_I2C_FIFO_DEPTH)) {
+		if (cdns_i2c_readreg(CDNS_I2C_CR_OFFSET) & CDNS_I2C_CR_HOLD) {
+			hold_clear = true;
+			if (id->quirks & CDNS_I2C_BROKEN_HOLD_BIT)
+				irq_save = true;
+		}
+	}
+
+	addr = id->p_msg->addr;
+	addr &= CDNS_I2C_ADDR_MASK;
+
+	if (hold_clear) {
+		ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET) & ~CDNS_I2C_CR_HOLD;
+		/*
+		 * In case of Xilinx Zynq SOC, clear the HOLD bit before transfer size
+		 * register reaches '0'. This is an IP bug which causes transfer size
+		 * register overflow to 0xFF. To satisfy this timing requirement,
+		 * disable the interrupts on current processor core between register
+		 * writes to slave address register and control register.
+		 */
+		if (irq_save)
+			local_irq_save(flags);
+
+		cdns_i2c_writereg(addr, CDNS_I2C_ADDR_OFFSET);
+		cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);
+		/* Read it back to avoid bufferring and make sure write happens */
+		cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
+
+		if (irq_save)
+			local_irq_restore(flags);
+	} else {
+		cdns_i2c_writereg(addr, CDNS_I2C_ADDR_OFFSET);
+	}
+
 	cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET);
 }
 
-- 
2.7.4


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

* [PATCH] i2c: cadence: Clear HOLD bit before xfer_size register rolls over
@ 2020-11-24  7:46 ` Raviteja Narayanam
  0 siblings, 0 replies; 6+ messages in thread
From: Raviteja Narayanam @ 2020-11-24  7:46 UTC (permalink / raw)
  To: linux-i2c
  Cc: git, Raviteja Narayanam, michal.simek, linux-arm-kernel, linux-kernel

On Xilinx zynq SOC if the delay between address register write and
control register write in cdns_mrecv function is more, the xfer size
register rolls over and controller is stuck. This is an IP bug and
is resolved in later versions of IP.

To avoid this scenario, disable the interrupts on the current processor
core between the two register writes and enable them later. This can
help achieve the timing constraint.

Signed-off-by: Raviteja Narayanam <raviteja.narayanam@xilinx.com>
---
 drivers/i2c/busses/i2c-cadence.c | 48 ++++++++++++++++++++++++++++++++++------
 1 file changed, 41 insertions(+), 7 deletions(-)

diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c
index e4b7f2a..81b7c45 100644
--- a/drivers/i2c/busses/i2c-cadence.c
+++ b/drivers/i2c/busses/i2c-cadence.c
@@ -578,6 +578,11 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id)
 {
 	unsigned int ctrl_reg;
 	unsigned int isr_status;
+	unsigned long flags;
+	bool hold_clear = false;
+	bool irq_save = false;
+
+	u32 addr;
 
 	id->p_recv_buf = id->p_msg->buf;
 	id->recv_count = id->p_msg->len;
@@ -618,14 +623,43 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id)
 		cdns_i2c_writereg(id->recv_count, CDNS_I2C_XFER_SIZE_OFFSET);
 	}
 
-	/* Set the slave address in address register - triggers operation */
-	cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK,
-						CDNS_I2C_ADDR_OFFSET);
-	/* Clear the bus hold flag if bytes to receive is less than FIFO size */
+	/* Determine hold_clear based on number of bytes to receive and hold flag */
 	if (!id->bus_hold_flag &&
-		((id->p_msg->flags & I2C_M_RECV_LEN) != I2C_M_RECV_LEN) &&
-		(id->recv_count <= CDNS_I2C_FIFO_DEPTH))
-			cdns_i2c_clear_bus_hold(id);
+	    ((id->p_msg->flags & I2C_M_RECV_LEN) != I2C_M_RECV_LEN) &&
+	    (id->recv_count <= CDNS_I2C_FIFO_DEPTH)) {
+		if (cdns_i2c_readreg(CDNS_I2C_CR_OFFSET) & CDNS_I2C_CR_HOLD) {
+			hold_clear = true;
+			if (id->quirks & CDNS_I2C_BROKEN_HOLD_BIT)
+				irq_save = true;
+		}
+	}
+
+	addr = id->p_msg->addr;
+	addr &= CDNS_I2C_ADDR_MASK;
+
+	if (hold_clear) {
+		ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET) & ~CDNS_I2C_CR_HOLD;
+		/*
+		 * In case of Xilinx Zynq SOC, clear the HOLD bit before transfer size
+		 * register reaches '0'. This is an IP bug which causes transfer size
+		 * register overflow to 0xFF. To satisfy this timing requirement,
+		 * disable the interrupts on current processor core between register
+		 * writes to slave address register and control register.
+		 */
+		if (irq_save)
+			local_irq_save(flags);
+
+		cdns_i2c_writereg(addr, CDNS_I2C_ADDR_OFFSET);
+		cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);
+		/* Read it back to avoid bufferring and make sure write happens */
+		cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
+
+		if (irq_save)
+			local_irq_restore(flags);
+	} else {
+		cdns_i2c_writereg(addr, CDNS_I2C_ADDR_OFFSET);
+	}
+
 	cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET);
 }
 
-- 
2.7.4


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH] i2c: cadence: Clear HOLD bit before xfer_size register rolls over
  2020-11-24  7:46 ` Raviteja Narayanam
@ 2021-06-23  8:46   ` Michal Simek
  -1 siblings, 0 replies; 6+ messages in thread
From: Michal Simek @ 2021-06-23  8:46 UTC (permalink / raw)
  To: Raviteja Narayanam; +Cc: linux-i2c, git, linux-arm, LKML

út 24. 11. 2020 v 8:48 odesílatel Raviteja Narayanam
<raviteja.narayanam@xilinx.com> napsal:
>
> On Xilinx zynq SOC if the delay between address register write and
> control register write in cdns_mrecv function is more, the xfer size
> register rolls over and controller is stuck. This is an IP bug and

and the controller

> is resolved in later versions of IP.
>
> To avoid this scenario, disable the interrupts on the current processor
> core between the two register writes and enable them later. This can
> help achieve the timing constraint.
>
> Signed-off-by: Raviteja Narayanam <raviteja.narayanam@xilinx.com>
> ---
>  drivers/i2c/busses/i2c-cadence.c | 48 ++++++++++++++++++++++++++++++++++------
>  1 file changed, 41 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c
> index e4b7f2a..81b7c45 100644
> --- a/drivers/i2c/busses/i2c-cadence.c
> +++ b/drivers/i2c/busses/i2c-cadence.c
> @@ -578,6 +578,11 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id)
>  {
>         unsigned int ctrl_reg;
>         unsigned int isr_status;
> +       unsigned long flags;
> +       bool hold_clear = false;
> +       bool irq_save = false;
> +
> +       u32 addr;
>
>         id->p_recv_buf = id->p_msg->buf;
>         id->recv_count = id->p_msg->len;
> @@ -618,14 +623,43 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id)
>                 cdns_i2c_writereg(id->recv_count, CDNS_I2C_XFER_SIZE_OFFSET);
>         }
>
> -       /* Set the slave address in address register - triggers operation */
> -       cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK,
> -                                               CDNS_I2C_ADDR_OFFSET);
> -       /* Clear the bus hold flag if bytes to receive is less than FIFO size */
> +       /* Determine hold_clear based on number of bytes to receive and hold flag */
>         if (!id->bus_hold_flag &&
> -               ((id->p_msg->flags & I2C_M_RECV_LEN) != I2C_M_RECV_LEN) &&
> -               (id->recv_count <= CDNS_I2C_FIFO_DEPTH))
> -                       cdns_i2c_clear_bus_hold(id);
> +           ((id->p_msg->flags & I2C_M_RECV_LEN) != I2C_M_RECV_LEN) &&
> +           (id->recv_count <= CDNS_I2C_FIFO_DEPTH)) {
> +               if (cdns_i2c_readreg(CDNS_I2C_CR_OFFSET) & CDNS_I2C_CR_HOLD) {
> +                       hold_clear = true;
> +                       if (id->quirks & CDNS_I2C_BROKEN_HOLD_BIT)
> +                               irq_save = true;
> +               }
> +       }
> +
> +       addr = id->p_msg->addr;
> +       addr &= CDNS_I2C_ADDR_MASK;
> +
> +       if (hold_clear) {
> +               ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET) & ~CDNS_I2C_CR_HOLD;
> +               /*
> +                * In case of Xilinx Zynq SOC, clear the HOLD bit before transfer size
> +                * register reaches '0'. This is an IP bug which causes transfer size
> +                * register overflow to 0xFF. To satisfy this timing requirement,
> +                * disable the interrupts on current processor core between register
> +                * writes to slave address register and control register.
> +                */
> +               if (irq_save)
> +                       local_irq_save(flags);
> +
> +               cdns_i2c_writereg(addr, CDNS_I2C_ADDR_OFFSET);
> +               cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);
> +               /* Read it back to avoid bufferring and make sure write happens */
> +               cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
> +
> +               if (irq_save)
> +                       local_irq_restore(flags);
> +       } else {
> +               cdns_i2c_writereg(addr, CDNS_I2C_ADDR_OFFSET);
> +       }
> +
>         cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET);
>  }

Unfortunately we can't do anything with this on the Zynq platform. It
is better than nothing.
ZynqMP is not affected.

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

Thanks,
Michal

-- 
Michal Simek, Ing. (M.Eng), OpenPGP -> KeyID: FE3D1F91
w: www.monstr.eu p: +42-0-721842854
Maintainer of Linux kernel - Xilinx Microblaze
Maintainer of Linux kernel - Xilinx Zynq ARM and ZynqMP ARM64 SoCs
U-Boot custodian - Xilinx Microblaze/Zynq/ZynqMP/Versal SoCs

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

* Re: [PATCH] i2c: cadence: Clear HOLD bit before xfer_size register rolls over
@ 2021-06-23  8:46   ` Michal Simek
  0 siblings, 0 replies; 6+ messages in thread
From: Michal Simek @ 2021-06-23  8:46 UTC (permalink / raw)
  To: Raviteja Narayanam; +Cc: linux-i2c, git, linux-arm, LKML

út 24. 11. 2020 v 8:48 odesílatel Raviteja Narayanam
<raviteja.narayanam@xilinx.com> napsal:
>
> On Xilinx zynq SOC if the delay between address register write and
> control register write in cdns_mrecv function is more, the xfer size
> register rolls over and controller is stuck. This is an IP bug and

and the controller

> is resolved in later versions of IP.
>
> To avoid this scenario, disable the interrupts on the current processor
> core between the two register writes and enable them later. This can
> help achieve the timing constraint.
>
> Signed-off-by: Raviteja Narayanam <raviteja.narayanam@xilinx.com>
> ---
>  drivers/i2c/busses/i2c-cadence.c | 48 ++++++++++++++++++++++++++++++++++------
>  1 file changed, 41 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c
> index e4b7f2a..81b7c45 100644
> --- a/drivers/i2c/busses/i2c-cadence.c
> +++ b/drivers/i2c/busses/i2c-cadence.c
> @@ -578,6 +578,11 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id)
>  {
>         unsigned int ctrl_reg;
>         unsigned int isr_status;
> +       unsigned long flags;
> +       bool hold_clear = false;
> +       bool irq_save = false;
> +
> +       u32 addr;
>
>         id->p_recv_buf = id->p_msg->buf;
>         id->recv_count = id->p_msg->len;
> @@ -618,14 +623,43 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id)
>                 cdns_i2c_writereg(id->recv_count, CDNS_I2C_XFER_SIZE_OFFSET);
>         }
>
> -       /* Set the slave address in address register - triggers operation */
> -       cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK,
> -                                               CDNS_I2C_ADDR_OFFSET);
> -       /* Clear the bus hold flag if bytes to receive is less than FIFO size */
> +       /* Determine hold_clear based on number of bytes to receive and hold flag */
>         if (!id->bus_hold_flag &&
> -               ((id->p_msg->flags & I2C_M_RECV_LEN) != I2C_M_RECV_LEN) &&
> -               (id->recv_count <= CDNS_I2C_FIFO_DEPTH))
> -                       cdns_i2c_clear_bus_hold(id);
> +           ((id->p_msg->flags & I2C_M_RECV_LEN) != I2C_M_RECV_LEN) &&
> +           (id->recv_count <= CDNS_I2C_FIFO_DEPTH)) {
> +               if (cdns_i2c_readreg(CDNS_I2C_CR_OFFSET) & CDNS_I2C_CR_HOLD) {
> +                       hold_clear = true;
> +                       if (id->quirks & CDNS_I2C_BROKEN_HOLD_BIT)
> +                               irq_save = true;
> +               }
> +       }
> +
> +       addr = id->p_msg->addr;
> +       addr &= CDNS_I2C_ADDR_MASK;
> +
> +       if (hold_clear) {
> +               ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET) & ~CDNS_I2C_CR_HOLD;
> +               /*
> +                * In case of Xilinx Zynq SOC, clear the HOLD bit before transfer size
> +                * register reaches '0'. This is an IP bug which causes transfer size
> +                * register overflow to 0xFF. To satisfy this timing requirement,
> +                * disable the interrupts on current processor core between register
> +                * writes to slave address register and control register.
> +                */
> +               if (irq_save)
> +                       local_irq_save(flags);
> +
> +               cdns_i2c_writereg(addr, CDNS_I2C_ADDR_OFFSET);
> +               cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);
> +               /* Read it back to avoid bufferring and make sure write happens */
> +               cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
> +
> +               if (irq_save)
> +                       local_irq_restore(flags);
> +       } else {
> +               cdns_i2c_writereg(addr, CDNS_I2C_ADDR_OFFSET);
> +       }
> +
>         cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET);
>  }

Unfortunately we can't do anything with this on the Zynq platform. It
is better than nothing.
ZynqMP is not affected.

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

Thanks,
Michal

-- 
Michal Simek, Ing. (M.Eng), OpenPGP -> KeyID: FE3D1F91
w: www.monstr.eu p: +42-0-721842854
Maintainer of Linux kernel - Xilinx Microblaze
Maintainer of Linux kernel - Xilinx Zynq ARM and ZynqMP ARM64 SoCs
U-Boot custodian - Xilinx Microblaze/Zynq/ZynqMP/Versal SoCs

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH] i2c: cadence: Clear HOLD bit before xfer_size register rolls over
  2020-11-24  7:46 ` Raviteja Narayanam
@ 2021-06-23 16:18   ` Wolfram Sang
  -1 siblings, 0 replies; 6+ messages in thread
From: Wolfram Sang @ 2021-06-23 16:18 UTC (permalink / raw)
  To: Raviteja Narayanam
  Cc: linux-i2c, michal.simek, linux-arm-kernel, linux-kernel, git

[-- Attachment #1: Type: text/plain, Size: 618 bytes --]

On Tue, Nov 24, 2020 at 01:16:05PM +0530, Raviteja Narayanam wrote:
> On Xilinx zynq SOC if the delay between address register write and
> control register write in cdns_mrecv function is more, the xfer size
> register rolls over and controller is stuck. This is an IP bug and
> is resolved in later versions of IP.
> 
> To avoid this scenario, disable the interrupts on the current processor
> core between the two register writes and enable them later. This can
> help achieve the timing constraint.
> 
> Signed-off-by: Raviteja Narayanam <raviteja.narayanam@xilinx.com>

Applied to for-next, thanks!


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [PATCH] i2c: cadence: Clear HOLD bit before xfer_size register rolls over
@ 2021-06-23 16:18   ` Wolfram Sang
  0 siblings, 0 replies; 6+ messages in thread
From: Wolfram Sang @ 2021-06-23 16:18 UTC (permalink / raw)
  To: Raviteja Narayanam
  Cc: linux-i2c, michal.simek, linux-arm-kernel, linux-kernel, git


[-- Attachment #1.1: Type: text/plain, Size: 618 bytes --]

On Tue, Nov 24, 2020 at 01:16:05PM +0530, Raviteja Narayanam wrote:
> On Xilinx zynq SOC if the delay between address register write and
> control register write in cdns_mrecv function is more, the xfer size
> register rolls over and controller is stuck. This is an IP bug and
> is resolved in later versions of IP.
> 
> To avoid this scenario, disable the interrupts on the current processor
> core between the two register writes and enable them later. This can
> help achieve the timing constraint.
> 
> Signed-off-by: Raviteja Narayanam <raviteja.narayanam@xilinx.com>

Applied to for-next, thanks!


[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

[-- Attachment #2: Type: text/plain, Size: 176 bytes --]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

end of thread, other threads:[~2021-06-23 16:20 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-24  7:46 [PATCH] i2c: cadence: Clear HOLD bit before xfer_size register rolls over Raviteja Narayanam
2020-11-24  7:46 ` Raviteja Narayanam
2021-06-23  8:46 ` Michal Simek
2021-06-23  8:46   ` Michal Simek
2021-06-23 16:18 ` Wolfram Sang
2021-06-23 16:18   ` Wolfram Sang

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.