From mboxrd@z Thu Jan 1 00:00:00 1970 From: Pablo Bitton Subject: Re: [PATCH 6/6] i2c: davinci: bus recovery procedure to clear the bus Date: Mon, 13 Sep 2010 16:23:00 +0200 Message-ID: References: <1264549293-25556-1-git-send-email-khilman@deeprootsystems.com> <1264549293-25556-7-git-send-email-khilman@deeprootsystems.com> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="===============1028932657==" Return-path: In-Reply-To: <1264549293-25556-7-git-send-email-khilman-1D3HCaltpLuhEniVeURVKkEOCMrvLtNR@public.gmane.org> List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: davinci-linux-open-source-bounces-VycZQUHpC/PFrsHnngEfi1aTQe2KTcn/@public.gmane.org Errors-To: davinci-linux-open-source-bounces-VycZQUHpC/PFrsHnngEfi1aTQe2KTcn/@public.gmane.org To: Philby John Cc: davinci-linux-open-source-VycZQUHpC/PFrsHnngEfi1aTQe2KTcn/@public.gmane.org, linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Ben Dooks List-Id: linux-i2c@vger.kernel.org --===============1028932657== Content-Type: multipart/alternative; boundary=001485e76f566df5ea049024d53b --001485e76f566df5ea049024d53b Content-Type: text/plain; charset=ISO-8859-1 Hi Philby, On Wed, Jan 27, 2010 at 1:41 AM, Kevin Hilman wrote: > From: Philby John > > Come out of i2c time out condition by following the > bus recovery procedure outlined in the i2c protocol v3 spec. > The kernel must be robust enough to gracefully recover > from i2c bus failure without having to reset the machine. > This is done by first NACKing the slave, pulsing the SCL > line 9 times and then sending the stop command. > > This patch has been tested on a DM6446 and DM355 > > Signed-off-by: Philby John > Signed-off-by: Srinivasan, Nageswari > Acked-by: Kevin Hilman > --- > drivers/i2c/busses/i2c-davinci.c | 57 > +++++++++++++++++++++++++++++++++++-- > 1 files changed, 53 insertions(+), 4 deletions(-) > > diff --git a/drivers/i2c/busses/i2c-davinci.c > b/drivers/i2c/busses/i2c-davinci.c > index 35f9daa..5459065 100644 > --- a/drivers/i2c/busses/i2c-davinci.c > +++ b/drivers/i2c/busses/i2c-davinci.c > @@ -36,6 +36,7 @@ > #include > #include > #include > +#include > > #include > #include > @@ -43,6 +44,7 @@ > /* ----- global defines ----------------------------------------------- */ > > #define DAVINCI_I2C_TIMEOUT (1*HZ) > +#define DAVINCI_I2C_MAX_TRIES 2 > #define I2C_DAVINCI_INTR_ALL (DAVINCI_I2C_IMR_AAS | \ > DAVINCI_I2C_IMR_SCD | \ > DAVINCI_I2C_IMR_ARDY | \ > @@ -130,6 +132,44 @@ static inline u16 davinci_i2c_read_reg(struct > davinci_i2c_dev *i2c_dev, int reg) > return __raw_readw(i2c_dev->base + reg); > } > > +/* Generate a pulse on the i2c clock pin. */ > +static void generic_i2c_clock_pulse(unsigned int scl_pin) > +{ > + u16 i; > + > + if (scl_pin) { > + /* Send high and low on the SCL line */ > + for (i = 0; i < 9; i++) { > + gpio_set_value(scl_pin, 0); > + udelay(20); > + gpio_set_value(scl_pin, 1); > + udelay(20); > + } > + } > +} > + > +/* This routine does i2c bus recovery as specified in the > + * i2c protocol Rev. 03 section 3.16 titled "Bus clear" > + */ > +static void i2c_recover_bus(struct davinci_i2c_dev *dev) > +{ > + u32 flag = 0; > + struct davinci_i2c_platform_data *pdata = dev->dev->platform_data; > + > + dev_err(dev->dev, "initiating i2c bus recovery\n"); > + /* Send NACK to the slave */ > + flag = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG); > + flag |= DAVINCI_I2C_MDR_NACK; > + /* write the data into mode register */ > + davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag); > + if (pdata) > + generic_i2c_clock_pulse(pdata->scl_pin); > + /* Send STOP */ > + flag = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG); > + flag |= DAVINCI_I2C_MDR_STP; > + davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag); > +} > + > static inline void davinci_i2c_reset_ctrl(struct davinci_i2c_dev *i2c_dev, > int val) > { > @@ -235,14 +275,22 @@ static int i2c_davinci_wait_bus_not_busy(struct > davinci_i2c_dev *dev, > char allow_sleep) > { > unsigned long timeout; > + static u16 to_cnt; > > timeout = jiffies + dev->adapter.timeout; > while (davinci_i2c_read_reg(dev, DAVINCI_I2C_STR_REG) > & DAVINCI_I2C_STR_BB) { > - if (time_after(jiffies, timeout)) { > - dev_warn(dev->dev, > - "timeout waiting for bus ready\n"); > - return -ETIMEDOUT; > + if (to_cnt <= DAVINCI_I2C_MAX_TRIES) { > + if (time_after(jiffies, timeout)) { > + dev_warn(dev->dev, > + "timeout waiting for bus ready\n"); > + to_cnt++; > + return -ETIMEDOUT; > + } else { > + to_cnt = 0; > + i2c_recover_bus(dev); > + i2c_davinci_init(dev); > + } > } > if (allow_sleep) > schedule_timeout(1); > The resulting loop has the following drawbacks: 1) If to_cnt reaches DAVINCI_I2C_MAX_TRIES+1 (which it currently can't, see 2) and the i2c bus collapses, the kernel will be stuck in the loop forever, especially if allow_sleep is false. 2) The to_cnt static var never increments beyond 1. It's initialized to zero and then kept at zero until the timeout expires. When the timeout expires, to_cnt is incremented once, until the next call to the function, where it will be zeroed again. 3) Do we really want to retry recovering the bus thousands of times, until the timeout arrives? It seems to me that if the bus recovery procedure didn't help after one or two tries, it probably never will. I also have the following nitpicks: a) The timeout variable actually holds the finish time. b) The i2c_recover_bus function uses dev_err to report a bus recovery process, but if all recovery attempts failed and the timeout was reached, only dev_warn is used to report the timeout. Other than that the patch is very helpful, thanks a lot. Below is my suggestion for the wait_bus_not_busy function. My patch has been tested on a DM6446. Signed-off-by: Pablo Bitton -- diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -220,16 +261,24 @@ static int i2c_davinci_init(struct davinci_i2c_dev *dev) static int i2c_davinci_wait_bus_not_busy(struct davinci_i2c_dev *dev, char allow_sleep) { - unsigned long timeout; + unsigned long finish_time; + unsigned long to_cnt = 0; - timeout = jiffies + dev->adapter.timeout; + finish_time = jiffies + dev->adapter.timeout; + /* While bus busy */ while (davinci_i2c_read_reg(dev, DAVINCI_I2C_STR_REG) & DAVINCI_I2C_STR_BB) { - if (time_after(jiffies, timeout)) { + if (time_after(jiffies, finish_time)) { dev_warn(dev->dev, "timeout waiting for bus ready\n"); return -ETIMEDOUT; } + else if (to_cnt <= DAVINCI_I2C_MAX_TRIES) { + dev_warn(dev->dev, "bus busy, performing bus recovery\n"); + ++to_cnt; + i2c_recover_bus(dev); + i2c_davinci_init(dev); + } if (allow_sleep) schedule_timeout(1); } --001485e76f566df5ea049024d53b Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable
Hi Philby,

On Wed, Jan 27, 2010 at 1:41 AM, Kevin Hilman <khilman@= deeprootsystems.com> wrote:
From: Philby John &l= t;pjohn-k0rHJ+Hhz/SVS+Kkd+ZcAg@public.gmane.org= com>

Come out of i2c time out condition by following the
bus recovery pro= cedure outlined in the i2c protocol v3 spec.
The kernel must be robust e= nough to gracefully recover
from i2c bus failure without having to reset= the machine.
This is done by first NACKing the slave, pulsing the SCL
line 9 times an= d then sending the stop command.

This patch has been tested on a DM6= 446 and DM355

Signed-off-by: Philby John <pjohn-k0rHJ+Hhz/SB+jHODAdFcQ@public.gmane.org>
Signed-off-by: Srinivasan, Nageswari <nageswari-l0cyMroinI0@public.gmane.org>
Acked-by: Kevin Hilman <= khilman-1D3HCaltpLuhEniVeURVKkEOCMrvLtNR@public.gmane.org<= /a>>
---
=A0drivers/i2c/busses/i2c-davinci.c | =A0 57 +++++++++++++++++++++++= ++++++++++++--
=A01 files changed, 53 insertions(+), 4 deletions(-)
<= br>diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-d= avinci.c
index 35f9daa..5459065 100644
--- a/drivers/i2c/busses/i2c-davinci.c
= +++ b/drivers/i2c/busses/i2c-davinci.c
@@ -36,6 +36,7 @@
=A0#include = <linux/platform_device.h>
=A0#include <linux/io.h>
=A0#in= clude <linux/cpufreq.h>
+#include <linux/gpio.h>

=A0#include <mach/hardware.h>=A0#include <mach/i2c.h>
@@ -43,6 +44,7 @@
=A0/* ----- global= defines ----------------------------------------------- */

=A0#defi= ne DAVINCI_I2C_TIMEOUT =A0 =A0(1*HZ)
+#define DAVINCI_I2C_MAX_TRIES =A02
=A0#define I2C_DAVINCI_INTR_ALL =A0 = =A0(DAVINCI_I2C_IMR_AAS | \
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 DAVINCI_I2C_IMR_SCD | \
=A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 DAVINCI_I2C_IMR_ARDY | \
@@ -130,6 += 132,44 @@ static inline u16 davinci_i2c_read_reg(struct davinci_i2c_dev *i2= c_dev, int reg)
=A0 =A0 =A0 =A0return __raw_readw(i2c_dev->base + reg);
=A0}

+= /* Generate a pulse on the i2c clock pin. */
+static void generic_i2c_cl= ock_pulse(unsigned int scl_pin)
+{
+ =A0 =A0 =A0 u16 i;
+
+ =A0= =A0 =A0 if (scl_pin) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Send high and low on the SCL line */
+ = =A0 =A0 =A0 =A0 =A0 =A0 =A0 for (i =3D 0; i < 9; i++) {
+ =A0 =A0 =A0= =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 gpio_set_value(scl_pin, 0);
+ =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 udelay(20);
+ =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 gpio_set_value(scl_pin, 1);
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 udelay(20);
+ =A0 =A0 =A0 = =A0 =A0 =A0 =A0 }
+ =A0 =A0 =A0 }
+}
+
+/* This routine does i2= c bus recovery as specified in the
+ * i2c protocol Rev. 03 section 3.16= titled "Bus clear"
+ */
+static void i2c_recover_bus(struct davinci_i2c_dev *dev)
+{
+ =A0 = =A0 =A0 u32 flag =3D 0;
+ =A0 =A0 =A0 struct davinci_i2c_platform_data *= pdata =3D dev->dev->platform_data;
+
+ =A0 =A0 =A0 dev_err(dev-= >dev, "initiating i2c bus recovery\n");
+ =A0 =A0 =A0 /* Send NACK to the slave */
+ =A0 =A0 =A0 flag =3D davinc= i_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG);
+ =A0 =A0 =A0 flag |=3D =A0DAV= INCI_I2C_MDR_NACK;
+ =A0 =A0 =A0 /* write the data into mode register */=
+ =A0 =A0 =A0 davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag); + =A0 =A0 =A0 if (pdata)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 generic_i2c_clock= _pulse(pdata->scl_pin);
+ =A0 =A0 =A0 /* Send STOP */
+ =A0 =A0 = =A0 flag =3D davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG);
+ =A0 =A0 = =A0 flag |=3D DAVINCI_I2C_MDR_STP;
+ =A0 =A0 =A0 davinci_i2c_write_reg(d= ev, DAVINCI_I2C_MDR_REG, flag);
+}
+
=A0static inline void davinci_i2c_reset_ctrl(struct davinci_i2c_= dev *i2c_dev,
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0int = val)
=A0{
@@ -235,14 +275,22 @@ static int i2c_davinci_wait_bus_not_b= usy(struct davinci_i2c_dev *dev,
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 char allow_sleep)
=A0{
=A0 =A0 =A0 =A0unsigned long timeout;
= + =A0 =A0 =A0 static u16 to_cnt;

=A0 =A0 =A0 =A0timeout =3D jiffies = + dev->adapter.timeout;
=A0 =A0 =A0 =A0while (davinci_i2c_read_reg(de= v, DAVINCI_I2C_STR_REG)
=A0 =A0 =A0 =A0 =A0 =A0 =A0 & DAVINCI_I2C_STR_BB) {
- =A0 =A0 =A0 = =A0 =A0 =A0 =A0 if (time_after(jiffies, timeout)) {
- =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_warn(dev->dev,
- =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"timeout waiting for bus re= ady\n");
- =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -ETIMEDOUT;
+ =A0 = =A0 =A0 =A0 =A0 =A0 =A0 if (to_cnt <=3D DAVINCI_I2C_MAX_TRIES) {
+ = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (time_after(jiffies, timeout= )) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_w= arn(dev->dev,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "timeout= waiting for bus ready\n");
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 to_cnt++;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 return -ETIMEDOUT;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0 =A0 =A0 } else {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 to_cnt =3D 0;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 i2c_recover_b= us(dev);
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 i= 2c_davinci_init(dev);
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 }=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0}
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if (all= ow_sleep)
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0schedule_timeou= t(1);
=A0
The resulting loop=A0has the following drawbacks:
1) If to_cnt reaches DAVINCI_I2C_MAX_TRIES+1 (which it currently can&#= 39;t, see 2) and the i2c bus collapses,
=A0=A0=A0 the kernel will be stuck in the loop forever, especially if = allow_sleep is false.
2) The to_cnt static var never increments beyond 1. It's initializ= ed to zero and then kept at zero until the timeout expires.
=A0=A0=A0 When the timeout expires, to_cnt is incremented once, until = the next call to the function, where it will be zeroed again.
=A03) Do we really want to retry recovering the bus thousands of times= , until the timeout arrives?
=A0=A0=A0 It seems to me that if the bus recovery procedure didn't= help after one or two tries, it probably never will.
=A0
I=A0 also have the following nitpicks:
a) The timeout variable actually holds the finish time.
b) The i2c_recover_bus function uses dev_err to report a bus recovery = process,
=A0=A0=A0 but if all recovery attempts failed and the timeout was reac= hed, only dev_warn is used to report the timeout.
=A0
Other than that the patch is very helpful, thanks a lot.
=A0
Below is my suggestion for the wait_bus_not_busy function. My patch ha= s been tested on a DM6446.
=A0
--

diff --git a/drivers/i2c/busses/i2c-davinci= .c b/drivers/i2c/busses/i2c-davinci.c
--- a/drivers/i2c/busses/i2c-davin= ci.c
+++ b/drivers/i2c/busses/i2c-davinci.c


@@ -220,16 +261,24 @@ static int i2c_davinc= i_init(struct davinci_i2c_dev *dev)
= =A0static int i2c_davinci_wait_bus_not_busy(struct davinci_i2c_dev *= dev,
=A0=A0char allow_= sleep)
=A0{
-=A0=A0=A0=A0 unsigned long timeout;
+=A0=A0=A0=A0 unsigned long finish_= time;
+=A0=A0=A0=A0 unsigned lon= g to_cnt =3D 0;
=A0
-=A0=A0=A0=A0 timeout =3D jiffies + dev->= ;adapter.timeout;
+=A0=A0=A0=A0 finish_time =3D jiffi= es + dev->adapter.timeout;
+=A0=A0= =A0=A0 /* While bus busy */
=A0= =A0=A0=A0=A0 while (davinci_= i2c_read_reg(dev, DAVINCI_I2C_STR_REG)
=A0=A0=A0=A0=A0=A0=A0 = & DAVINCI_I2C_STR_BB) {
-=A0= =A0=A0=A0=A0=A0=A0=A0=A0=A0 if (time_after(jiffies, timeout)) {
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 if = (time_after(jiffies, finish_time)) {
= =A0=A0=A0=A0=A0 =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 dev_warn(dev= ->dev,
=A0=A0"timeout waiting for bus = ready\n");
=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 return -ETIMEDOUT;
=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 else if (to_cnt <=3D DAVINCI_I= 2C_MAX_TRIES) {
+=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0=A0 de= v_warn(dev->dev, "bus busy, performing bus recovery\n");
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0=A0=A0=A0=A0=A0=A0
++to_cnt;
+<= span style=3D"mso-tab-count: 4">=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0=A0
i2c_recover_bus(dev);
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0 i2c_davinci_init(dev);
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 }=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 if (allow_sleep)
=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 schedule_timeout(1);
=A0