8xx: i2c-algo-8xx - fixed timeout detection and transmission errors
Cajus Hahn
cajus.hahn at de.abb.com
Thu Aug 11 17:42:33 EST 2005
Marcello,
my "old" version is from the linuxppc_2_4_devel CVS archive on www.denx.de:/cvsroot.
The i2c-algo-8xx.c there has the revision number 1.5.
Regards
Cajus
--- ../../i2c-algo-8xx.c 2005-08-10 16:19:09.000000000 +0200
+++ drivers/i2c/i2c-algo-8xx.c 2005-08-10 08:03:46.000000000 +0200
@@ -19,12 +19,19 @@
* moved into proper i2c interface; separated out platform specific
* parts into i2c-rpx.c
* Brad Parker (brad at heeltoe.com)
+ *
+ * added define for BUSY_WAIT and INTERRUPTIBLE_SLEEP, added I2C_ALGO_8XX_DATE + ..VERSION
+ * fixed bug in cpm_iic_read and cpm_iic_write (timeout never detected if count < 16)
+ * added force_reinit(): in certain cases (disturbances on the I2C bus) a timeout will
+ * occur. After this a complete re-initialisation will be necessary, otherwise all
+ * following transmissions will have a timeout.
+ * Cajus Hahn, 09.08.2005
*/
// XXX todo
// timeout sleep?
-/* $Id: i2c-algo-8xx.c,v 1.7 2002/08/03 22:48:18 ac9410 Exp $ */
+/* $Id: i2c-algo-8xx.c,v 1.2 2005/08/10 06:03:46 cajus Exp $ */
#include <linux/kernel.h>
#include <linux/module.h>
@@ -43,8 +50,16 @@
#include <linux/i2c.h>
#include <linux/i2c-algo-8xx.h>
+#define I2C_ALGO_8XX_DATE "20050809"
+#define I2C_ALGO_8XX_VERSION "2.6.2"
+
#define CPM_MAX_READ 513
/* #define I2C_CHIP_ERRATA */ /* Try uncomment this if you have an older CPU(earlier than rev D4) */
+#define I2C_BUSY_WAIT /* Uncomment this if you want to do a busy wait in cpm_iic_read and
+ cpm_iic_write. In a timeout case the CPU load will be 99.9% ! */
+#define I2C_INTERRUPTIBLE_SLEEP /* Uncomment this if you want the waiting in cpm_iic_read and
+ cpm_iic_write being interruptable by signals */
+
static wait_queue_head_t iic_wait;
static ushort r_tbase, r_rbase;
@@ -73,7 +88,11 @@
/* Get 'me going again.
*/
+#ifdef I2C_INTERRUPTIBLE_SLEEP
wake_up_interruptible(&iic_wait);
+#else
+ wake_up(&iic_wait);
+#endif
}
static void
@@ -201,20 +220,77 @@
static void force_close(struct i2c_algo_8xx_data *cpm)
{
volatile i2c8xx_t *i2c = cpm->i2c;
+
+ if (cpm_debug)
+ printk(KERN_DEBUG "force_close()");
+
if (cpm->reloc == 0) { /* micro code disabled */
volatile cpm8xx_t *cp = cpm->cp;
-
- if (cpm_debug) printk(KERN_DEBUG "force_close()\n");
cp->cp_cpcr =
mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_CLOSE_RXBD) |
CPM_CR_FLG;
while (cp->cp_cpcr & CPM_CR_FLG);
}
+
i2c->i2c_i2cmr = 0x00; /* Disable all interrupts */
i2c->i2c_i2cer = 0xff;
}
+static void force_reinit(struct i2c_algo_8xx_data *cpm)
+{
+ volatile iic_t *iip = cpm->iip;
+ volatile i2c8xx_t *i2c = cpm->i2c;
+ volatile cpm8xx_t *cp = cpm->cp;
+ unsigned char brg;
+ bd_t *bd = (bd_t *)__res;
+
+ // Disable interrupts.
+ i2c->i2c_i2cmr = 0;
+ i2c->i2c_i2cer = 0xff;
+ // Clear enable
+ i2c->i2c_i2mod &= ~1;
+
+ // Initialize the parameter ram.
+ iip->iic_rstate = 0;
+ iip->iic_rdp = 0;
+ iip->iic_rbptr = 0;
+ iip->iic_rbc = 0;
+ iip->iic_rxtmp = 0;
+ iip->iic_tstate = 0;
+ iip->iic_tdp = 0;
+ iip->iic_tbptr = 0;
+ iip->iic_tbc = 0;
+ iip->iic_txtmp = 0;
+ iip->iic_tbase = r_tbase;
+ iip->iic_rbase = r_rbase;
+ iip->iic_tfcr = SMC_EB;
+ iip->iic_rfcr = SMC_EB;
+ iip->iic_mrblr = CPM_MAX_READ;
+
+ if (cpm->reloc == 0)
+ {
+ cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_INIT_TRX) | CPM_CR_FLG;
+ while (cp->cp_cpcr & CPM_CR_FLG);
+ }
+ else
+ {
+ iip->iic_rbptr = iip->iic_rbase;
+ iip->iic_tbptr = iip->iic_tbase;
+ iip->iic_rstate = 0;
+ iip->iic_tstate = 0;
+ }
+
+ // Select an arbitrary address. Just make sure it is unique.
+ i2c->i2c_i2add = 0xfe;
+
+ // Make clock run at 60 KHz.
+ brg = (unsigned char) (bd->bi_intfreq/(32*2*60000) -3);
+ i2c->i2c_i2brg = brg;
+
+ i2c->i2c_i2mod = 0x00;
+ i2c->i2c_i2com = 0x01; /* Master mode */
+}
/* Read from IIC...
* abyte = address byte, with r/w flag already set
@@ -227,7 +303,7 @@
volatile cpm8xx_t *cp = cpm->cp;
volatile cbd_t *tbdf, *rbdf;
u_char *tb;
- unsigned long flags, tmo;
+ unsigned long flags, tmo, timedout;
if (count >= CPM_MAX_READ)
return -EINVAL;
@@ -269,7 +345,10 @@
rbdf->cbd_bufaddr = __pa(buf);
rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP| BD_SC_INTRPT;
+ timedout = 0;
+#ifdef I2C_BUSY_WAIT
if(count > 16){
+#endif
/* Chip bug, set enable here */
local_irq_save(flags);
i2c->i2c_i2cmr = 0x13; /* Enable some interupts */
@@ -278,23 +357,40 @@
i2c->i2c_i2com |= 0x80; /* Begin transmission */
/* Wait for IIC transfer */
+#ifdef I2C_INTERRUPTIBLE_SLEEP
tmo = interruptible_sleep_on_timeout(&iic_wait,1*HZ);
+#else
+ tmo = sleep_on_timeout(&iic_wait,1*HZ);
+#endif
+ if(tmo == 0) timedout=1;
local_irq_restore(flags);
+#ifdef I2C_BUSY_WAIT
} else { /* busy wait for small transfers, its faster */
i2c->i2c_i2cmr = 0x00; /* Disable I2C interupts */
i2c->i2c_i2cer = 0xff;
i2c->i2c_i2mod |= 1; /* Enable */
i2c->i2c_i2com |= 0x80; /* Begin transmission */
tmo = jiffies + 1*HZ;
- while(!(i2c->i2c_i2cer & 0x11 || time_after(jiffies, tmo))); /* Busy wait, with a timeout */
+ while(!(i2c->i2c_i2cer & 0x11 || (timedout = time_after(jiffies, tmo)))); /* Busy wait, with a timeout */
}
+#endif
+ if(timedout)
+ {
+ printk(KERN_DEBUG "cpm_iic_read: timeout!\n");
+ force_reinit(cpm);
+ return -EIO;
+ }
+
+#ifdef I2C_INTERRUPTIBLE_SLEEP
+ if (signal_pending(current))
+ {
+ force_close(cpm);
+ if (cpm_debug)
+ printk(KERN_DEBUG "cpm_iic_read: signal_pending! \n");
+ return -EINTR;
+ }
+#endif
- if (signal_pending(current) || !tmo){
- force_close(cpm);
- if(cpm_debug)
- printk(KERN_DEBUG "IIC read: timeout!\n");
- return -EIO;
- }
#ifdef I2C_CHIP_ERRATA
/* Chip errata, clear enable. This is not needed on rev D4 CPUs.
Disabling I2C too early may cause too short stop condition */
@@ -359,7 +455,7 @@
volatile cpm8xx_t *cp = cpm->cp;
volatile cbd_t *tbdf;
u_char *tb;
- unsigned long flags, tmo;
+ unsigned long flags, tmo, timedout;
/* check for and use a microcode relocation patch */
if (cpm->reloc) {
@@ -385,7 +481,10 @@
tbdf[1].cbd_datlen = count;
tbdf[1].cbd_sc = BD_SC_READY | BD_SC_INTRPT | BD_SC_LAST | BD_SC_WRAP;
+ timedout = 0;
+#ifdef I2C_BUSY_WAIT
if(count > 16){
+#endif
/* Chip bug, set enable here */
local_irq_save(flags);
i2c->i2c_i2cmr = 0x13; /* Enable some interupts */
@@ -394,23 +493,39 @@
i2c->i2c_i2com |= 0x80; /* Begin transmission */
/* Wait for IIC transfer */
+#ifdef I2C_INTERRUPTIBLE_SLEEP
tmo = interruptible_sleep_on_timeout(&iic_wait,1*HZ);
+#else
+ tmo = sleep_on_timeout(&iic_wait,1*HZ);
+#endif
+ if(tmo == 0) timedout=1;
local_irq_restore(flags);
+#ifdef I2C_BUSY_WAIT
} else { /* busy wait for small transfers, its faster */
i2c->i2c_i2cmr = 0x00; /* Disable I2C interupts */
i2c->i2c_i2cer = 0xff;
i2c->i2c_i2mod |= 1; /* Enable */
i2c->i2c_i2com |= 0x80; /* Begin transmission */
tmo = jiffies + 1*HZ;
- while(!(i2c->i2c_i2cer & 0x12 || time_after(jiffies, tmo))); /* Busy wait, with a timeout */
- }
-
- if (signal_pending(current) || !tmo){
- force_close(cpm);
- if(cpm_debug && !tmo)
- printk(KERN_DEBUG "IIC write: timeout!\n");
- return -EIO;
+ while(!(i2c->i2c_i2cer & 0x12 || (timedout = time_after(jiffies, tmo)))); /* Busy wait, with a timeout */
}
+#endif
+ if(timedout)
+ {
+ printk(KERN_DEBUG "cpm_iic_write: timeout!\n");
+ force_reinit(cpm);
+ return -EIO;
+ }
+
+#ifdef I2C_INTERRUPTIBLE_SLEEP
+ if (signal_pending(current))
+ {
+ force_close(cpm);
+ if (cpm_debug)
+ printk(KERN_DEBUG "cpm_iic_write: signal_pending! \n");
+ return -EINTR;
+ }
+#endif
#if I2C_CHIP_ERRATA
/* Chip errata, clear enable. This is not needed on rev D4 CPUs.
@@ -495,7 +610,11 @@
if (cpm_debug > 1) printk(KERN_DEBUG "about to sleep\n");
/* wait for IIC transfer */
+#ifdef I2C_INTERRUPTIBLE_SLEEP
tmo = interruptible_sleep_on_timeout(&iic_wait,1*HZ);
+#else
+ tmo = sleep_on_timeout(&iic_wait,1*HZ);
+#endif
local_irq_restore(flags);
#ifdef I2C_CHIP_ERRATA
@@ -658,7 +777,7 @@
int __init i2c_algo_8xx_init (void)
{
- printk(KERN_INFO "i2c-algo-8xx.o: i2c mpc8xx algorithm module version %s (%s)\n", I2C_VERSION, I2C_DATE);
+ printk(KERN_INFO "i2c-algo-8xx.o: i2c mpc8xx algorithm module version %s (%s)\n", I2C_ALGO_8XX_VERSION, I2C_ALGO_8XX_DATE);
return 0;
}
More information about the Linuxppc-embedded
mailing list