linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
From: "Albrecht Dreß" <albrecht.dress@arcor.de>
To: Linux PPC Development <linuxppc-dev@ozlabs.org>,
	Devicetree Discussions <devicetree-discuss@lists.ozlabs.org>,
	Grant Likely <grant.likely@secretlab.ca>,
	"Ben Dooks (embedded platforms)" <ben-linux@fluff.org>
Cc: "Ira W. Snyder" <iws@ovro.caltech.edu>
Subject: [Patch v2 1/2] 5200/mpc: improve i2c bus error recovery
Date: Wed, 17 Feb 2010 19:59:14 +0100	[thread overview]
Message-ID: <1266433154.2322.0@antares> (raw)

This patch improves the recovery of the MPC's I2C bus from errors like bus
hangs resulting in timeouts:
1. make the bus timeout configurable, as it depends on the bus clock and
    the attached slave chip(s); default is still 1 second;
2. detect any of the cases indicated by the CF, BB and RXAK MSR flags if a
    timeout occurs, and add a missing (required) MAL reset;
3. use a more reliable method to fixup the bus if a hang has been detected.
    The sequence is sent 9 times which seems to be necessary if a slave
    "misses" more than one clock cycle.  For 400 kHz bus speed, the fixup i=
s
    also ~70us (81us vs. 150us) faster.

Tested on a custom Lite5200b derived board, with a Dallas RTC, AD sensors
and NXP IO expander chips attached to the i2c.

Changes vs. v1:
- use improved bus fixup sequence for all chips (not only the 5200)
- calculate real clock from defaults if no clock is given in the device tre=
e
- better description (I hope) of the changes.

I didn't split the changes in this file into three parts as recommended by
Grant, as they actually belong together (i.e. they address one single
problem, just in three places of one single source file).

Signed-off-by: Albrecht Dre=DF <albrecht.dress@arcor.de>

---

Note about the reset sequence: I verified the waveforms for 18.4kHz, 85.9kH=
z
and 375kHz (drop me a note if you want to see scope screen shots).  Not
verified on other mpc chips yet.
@Ira: thanks in advance for giving it a try on your box!


--- linux-2.6.32-orig/drivers/i2c/busses/i2c-mpc.c	2009-12-03 04:51:21.0000=
00000 +0100
+++ linux-2.6.32/drivers/i2c/busses/i2c-mpc.c	2010-02-17 16:23:11.000000000=
 +0100
@@ -59,6 +59,7 @@ struct mpc_i2c {
  	wait_queue_head_t queue;
  	struct i2c_adapter adap;
  	int irq;
+	u32 real_clk;
  };

  struct mpc_i2c_divider {
@@ -93,20 +94,23 @@ static irqreturn_t mpc_i2c_isr(int irq,
  /* Sometimes 9th clock pulse isn't generated, and slave doesn't release
   * the bus, because it wants to send ACK.
   * Following sequence of enabling/disabling and sending start/stop genera=
tes
- * the pulse, so it's all OK.
+ * the 9 pulses, so it's all OK.
   */
  static void mpc_i2c_fixup(struct mpc_i2c *i2c)
  {
-	writeccr(i2c, 0);
-	udelay(30);
-	writeccr(i2c, CCR_MEN);
-	udelay(30);
-	writeccr(i2c, CCR_MSTA | CCR_MTX);
-	udelay(30);
-	writeccr(i2c, CCR_MSTA | CCR_MTX | CCR_MEN);
-	udelay(30);
-	writeccr(i2c, CCR_MEN);
-	udelay(30);
+	int k;
+	u32 delay_val =3D 1000000 / i2c->real_clk + 1;
+
+	if (delay_val < 2)
+		delay_val =3D 2;
+
+	for (k =3D 9; k; k--) {
+		writeccr(i2c, 0);
+		writeccr(i2c, CCR_MSTA | CCR_MTX | CCR_MEN);
+		udelay(delay_val);
+		writeccr(i2c, CCR_MEN);
+		udelay(delay_val << 1);
+	}
  }

  static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing)
@@ -186,15 +190,19 @@ static const struct mpc_i2c_divider mpc_
  	{10240, 0x9d}, {12288, 0x9e}, {15360, 0x9f}
  };

-int mpc_i2c_get_fdr_52xx(struct device_node *node, u32 clock, int prescale=
r)
+int mpc_i2c_get_fdr_52xx(struct device_node *node, u32 clock, int prescale=
r,
+			 u32 *real_clk)
  {
  	const struct mpc_i2c_divider *div =3D NULL;
  	unsigned int pvr =3D mfspr(SPRN_PVR);
  	u32 divider;
  	int i;

-	if (!clock)
+	if (!clock) {
+		/* see below - default fdr =3D 0x3f -> div =3D 2048 */
+		*real_clk =3D mpc5xxx_get_bus_frequency(node) / 2048;
  		return -EINVAL;
+	}

  	/* Determine divider value */
  	divider =3D mpc5xxx_get_bus_frequency(node) / clock;
@@ -212,7 +220,8 @@ int mpc_i2c_get_fdr_52xx(struct device_n
  			break;
  	}

-	return div ? (int)div->fdr : -EINVAL;
+	*real_clk =3D mpc5xxx_get_bus_frequency(node) / div->divider;
+	return (int)div->fdr;
  }

  static void mpc_i2c_setclock_52xx(struct device_node *node,
@@ -221,13 +230,14 @@ static void mpc_i2c_setclock_52xx(struct
  {
  	int ret, fdr;

-	ret =3D mpc_i2c_get_fdr_52xx(node, clock, prescaler);
+	ret =3D mpc_i2c_get_fdr_52xx(node, clock, prescaler, &i2c->real_clk);
  	fdr =3D (ret >=3D 0) ? ret : 0x3f; /* backward compatibility */

  	writeb(fdr & 0xff, i2c->base + MPC_I2C_FDR);

  	if (ret >=3D 0)
-		dev_info(i2c->dev, "clock %d Hz (fdr=3D%d)\n", clock, fdr);
+		dev_info(i2c->dev, "clock %u Hz (fdr=3D%d)\n", i2c->real_clk,
+			 fdr);
  }
  #else /* !CONFIG_PPC_MPC52xx */
  static void mpc_i2c_setclock_52xx(struct device_node *node,
@@ -287,14 +297,18 @@ u32 mpc_i2c_get_sec_cfg_8xxx(void)
  	return val;
  }

-int mpc_i2c_get_fdr_8xxx(struct device_node *node, u32 clock, u32 prescale=
r)
+int mpc_i2c_get_fdr_8xxx(struct device_node *node, u32 clock, u32 prescale=
r,
+			 u32 *real_clk)
  {
  	const struct mpc_i2c_divider *div =3D NULL;
  	u32 divider;
  	int i;

-	if (!clock)
+	if (!clock) {
+		/* see below - default fdr =3D 0x1031 -> div =3D 16 * 3072 */
+		*real_clk =3D fsl_get_sys_freq() / prescaler / (16 * 3072);
  		return -EINVAL;
+	}

  	/* Determine proper divider value */
  	if (of_device_is_compatible(node, "fsl,mpc8544-i2c"))
@@ -317,6 +331,7 @@ int mpc_i2c_get_fdr_8xxx(struct device_n
  			break;
  	}

+	*real_clk =3D fsl_get_sys_freq() / prescaler / div->divider;
  	return div ? (int)div->fdr : -EINVAL;
  }

@@ -326,7 +341,7 @@ static void mpc_i2c_setclock_8xxx(struct
  {
  	int ret, fdr;

-	ret =3D mpc_i2c_get_fdr_8xxx(node, clock, prescaler);
+	ret =3D mpc_i2c_get_fdr_8xxx(node, clock, prescaler, &i2c->real_clk);
  	fdr =3D (ret >=3D 0) ? ret : 0x1031; /* backward compatibility */

  	writeb(fdr & 0xff, i2c->base + MPC_I2C_FDR);
@@ -334,7 +349,7 @@ static void mpc_i2c_setclock_8xxx(struct

  	if (ret >=3D 0)
  		dev_info(i2c->dev, "clock %d Hz (dfsrr=3D%d fdr=3D%d)\n",
-			 clock, fdr >> 8, fdr & 0xff);
+			 i2c->real_clk, fdr >> 8, fdr & 0xff);
  }

  #else /* !CONFIG_FSL_SOC */
@@ -446,10 +461,14 @@ static int mpc_xfer(struct i2c_adapter *
  			return -EINTR;
  		}
  		if (time_after(jiffies, orig_jiffies + HZ)) {
+			u8 status =3D readb(i2c->base + MPC_I2C_SR);
+
  			dev_dbg(i2c->dev, "timeout\n");
-			if (readb(i2c->base + MPC_I2C_SR) =3D=3D
-			    (CSR_MCF | CSR_MBB | CSR_RXAK))
+			if ((status & (CSR_MCF | CSR_MBB | CSR_RXAK)) !=3D 0) {
+				writeb(status & ~CSR_MAL,
+				       i2c->base + MPC_I2C_SR);
  				mpc_i2c_fixup(i2c);
+			}
  			return -EIO;
  		}
  		schedule();
@@ -540,6 +559,14 @@ static int __devinit fsl_i2c_probe(struc
  		}
  	}

+	prop =3D of_get_property(op->node, "fsl,timeout", &plen);
+	if (prop && plen =3D=3D sizeof(u32)) {
+		mpc_ops.timeout =3D *prop * HZ / 1000000;
+		if (mpc_ops.timeout < 5)
+			mpc_ops.timeout =3D 5;
+	}
+	dev_info(i2c->dev, "timeout %u us\n", mpc_ops.timeout * 1000000 / HZ);
+
  	dev_set_drvdata(&op->dev, i2c);

  	i2c->adap =3D mpc_ops;

             reply	other threads:[~2010-02-17 18:59 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-02-17 18:59 Albrecht Dreß [this message]
2010-02-17 20:10 ` [Patch v2 1/2] 5200/mpc: improve i2c bus error recovery Grant Likely
2010-02-18  8:09 ` Joakim Tjernlund
2010-02-18  9:09 ` Albrecht Dreß
2010-02-18 12:33   ` Joakim Tjernlund
2010-02-18 13:23 ` Joakim Tjernlund
2010-05-05 22:09 ` Ira W. Snyder
2010-05-06 17:54   ` Albrecht Dreß
2010-05-06 18:06     ` Grant Likely
2010-05-16 17:47       ` Albrecht Dreß
2010-05-19 16:02         ` Grant Likely
2010-06-16 19:30           ` Albrecht Dreß
2013-03-13  5:30 ` panpan2523
  -- strict thread matches above, loose matches on Subject: below --
2010-02-18 15:04 Albrecht Dreß
2010-02-18 17:14 ` Joakim Tjernlund
2010-02-18 17:41   ` Grant Likely
2010-02-18 18:07     ` Joakim Tjernlund
2010-02-18 18:45   ` Albrecht Dreß

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1266433154.2322.0@antares \
    --to=albrecht.dress@arcor.de \
    --cc=ben-linux@fluff.org \
    --cc=devicetree-discuss@lists.ozlabs.org \
    --cc=grant.likely@secretlab.ca \
    --cc=iws@ovro.caltech.edu \
    --cc=linuxppc-dev@ozlabs.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).