8xx: i2c-algo-8xx - fixed timeout detection and transmission errors

Marcelo Tosatti marcelo.tosatti at cyclades.com
Wed Aug 10 23:58:30 EST 2005


On Wed, Aug 10, 2005 at 09:27:57AM +0200, cajus.hahn at de.abb.com wrote:
> 
> 
> 
> 
> Hi all,
> 
> I had some problems on my MPC855M based board when I tried to access the
> i2c bus.
> The cause were some disturbances on the i2c bus. If a slave device or
> another master device hold the SDA or SCL line low, the CPM will get into a
> state, where no more transmissions are made. The  only way I found, to get
> the CPM back to work, was a complete re-initialisation. -> force_reinit()
> In this case the busy-wait for transmissions with count < 16 will lock the
> complete system (CPU load 99.9%)
> I added the define I2C_BUSY_WAIT to switch between faster access or safer
> system.
> I found out that the i2c-algo-8xx.c has a bug in cpm_iic_read() and
> cpm_iic_write(): the timeout detection will not work in the case of count <
> 16.
> I also added the define I2C_INTERRUPTIBLE_SLEEP: the old driver reported
> IO-error (-EIO) if a timeout occured (which was not working, see above) or
> if a signal was pending. This caused some problems if the process receives
> user-signals. The driver will report IO-error, which is not correct. With
> the busy-wait this effect might not be seen, because there will be no
> process scheduling -> no signals might be send.
> My modified file is appended. The defines for  I2C_BUSY_WAIT and
> I2C_INTERRUPTIBLE_SLEEP are active, which let the driver act like the old
> one.
> Maybe this is helpful for others too and some of the modifications find
> it´s way into the official kernel tree.

Cajus,

Can you please prepare a diff with "diff -u" against the old version and your
modified version of the driver? That way it becomes easier for everyone to 
stop the changes you have made.

Thanks in advance.

> 
> Cajus Hahn
> 
> /*
>  * i2c-algo-8xx.c i2x driver algorithms for MPC8XX CPM
>  * Copyright (c) 1999 Dan Malek (dmalek at jlc.net).
>  *
>     This program is free software; you can redistribute it and/or modify
>     it under the terms of the GNU General Public License as published by
>     the Free Software Foundation; either version 2 of the License, or
>     (at your option) any later version.
> 
>     This program is distributed in the hope that it will be useful,
>     but WITHOUT ANY WARRANTY; without even the implied warranty of
>     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>     GNU General Public License for more details.
> 
>     You should have received a copy of the GNU General Public License
>     along with this program; if not, write to the Free Software
>     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
>  *
>  * 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.1.1.1 2004/12/10 08:44:35 cajus Exp $ */
> 
> #include <linux/kernel.h>
> #include <linux/module.h>
> #include <linux/delay.h>
> #include <linux/slab.h>
> #include <linux/version.h>
> #include <linux/init.h>
> #include <asm/uaccess.h>
> #include <linux/ioport.h>
> #include <linux/errno.h>
> #include <linux/sched.h>
> 
> #include <asm/mpc8xx.h>
> #include <asm/commproc.h>
> 
> #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 */
> #define I2C_INTERRUPTIBLE_SLEEP /* Uncomment this if you want the waiting
> in cpm_iic_read and
>                                          cpm_iic_write beeing interruptable
> by signals */
> 
> static wait_queue_head_t iic_wait;
> static ushort r_tbase, r_rbase;
> 
> int cpm_scan = 0;
> int cpm_debug = 0;
> 
> static  void
> cpm_iic_interrupt(void *dev_id, struct pt_regs *regs)
>  {
>       volatile i2c8xx_t *i2c = (i2c8xx_t *)dev_id;
> 
>       if (cpm_debug > 1)
>             printk(KERN_DEBUG "cpm_iic_interrupt(dev_id=%p)\n", dev_id);
> 
> #ifdef I2C_CHIP_ERRATA
>       /* Chip errata, clear enable.
>        * This seems to not be needed on rev D4 or newer CPUs.
>        * Someone with an older CPU needs to verify this.
>        */
>       i2c->i2c_i2mod &= ~1;
> #endif
> 
>       /* Clear interrupt.
>       */
>       i2c->i2c_i2cer = 0xff;
> 
>       /* Get 'me going again.
>       */
> #ifdef I2C_INTERRUPTIBLE_SLEEP
>       wake_up_interruptible(&iic_wait);
> #else
>       wake_up(&iic_wait);
> #endif
> }
> 
> static void
> cpm_iic_init(struct i2c_algo_8xx_data *cpm_adap)
> {
>       volatile iic_t          *iip = cpm_adap->iip;
>       volatile i2c8xx_t *i2c = cpm_adap->i2c;
>       unsigned char brg;
>       bd_t *bd = (bd_t *)__res;
> 
>       if (cpm_debug) printk(KERN_DEBUG "cpm_iic_init() - iip=%p\n",iip);
> 
>       /* Initialize the parameter ram.
>        * We need to make sure many things are initialized to zero,
>        * especially in the case of a microcode patch.
>        */
>       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;
> 
>       /* Set up the IIC parameters in the parameter ram.
>       */
>       iip->iic_tbase = r_tbase = cpm_adap->dp_addr;
>       iip->iic_rbase = r_rbase = cpm_adap->dp_addr + sizeof(cbd_t)*2;
> 
>       iip->iic_tfcr = SMC_EB;
>       iip->iic_rfcr = SMC_EB;
> 
>       /* Set maximum receive size.
>       */
>       iip->iic_mrblr = CPM_MAX_READ;
> 
>       /* Initialize Tx/Rx parameters.
>       */
>       if (cpm_adap->reloc == 0) {
>             volatile cpm8xx_t *cp = cpm_adap->cp;
> 
>             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 */
> 
>       /* Disable interrupts.
>       */
>       i2c->i2c_i2cmr = 0;
>       i2c->i2c_i2cer = 0xff;
> 
>       init_waitqueue_head(&iic_wait);
> 
>       /* Install interrupt handler.
>       */
>       if (cpm_debug) {
>             printk (KERN_DEBUG "%s[%d] Install ISR for IRQ %d\n",
>                   __func__,__LINE__, CPMVEC_I2C);
>       }
>       cpm_install_handler(CPMVEC_I2C, cpm_iic_interrupt, (void *)i2c);
> }
> 
> 
> static int
> cpm_iic_shutdown(struct i2c_algo_8xx_data *cpm_adap)
> {
>       volatile i2c8xx_t *i2c = cpm_adap->i2c;
> 
>       /* Shut down IIC.
>       */
>       i2c->i2c_i2mod &= ~1;
>       i2c->i2c_i2cmr = 0;
>       i2c->i2c_i2cer = 0xff;
> 
>       return(0);
> }
> 
> static void
> cpm_reset_iic_params(volatile iic_t *iip)
> {
>       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;
> 
>       iip->iic_rstate = 0;
>       iip->iic_rdp = 0;
>       iip->iic_rbptr = iip->iic_rbase;
>       iip->iic_rbc = 0;
>       iip->iic_rxtmp = 0;
>       iip->iic_tstate = 0;
>       iip->iic_tdp = 0;
>       iip->iic_tbptr = iip->iic_tbase;
>       iip->iic_tbc = 0;
>       iip->iic_txtmp = 0;
> }
> 
> #define BD_SC_NAK       ((ushort)0x0004) /* NAK - did not respond */
> #define BD_SC_OV        ((ushort)0x0002) /* OV - receive overrun */
> #define CPM_CR_CLOSE_RXBD     ((ushort)0x0007)
> 
> 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;
>             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
>  */
> static int
> cpm_iic_read(struct i2c_algo_8xx_data *cpm, u_char abyte, char *buf, int
> count)
> {
>       volatile iic_t *iip = cpm->iip;
>       volatile i2c8xx_t *i2c = cpm->i2c;
>       volatile cpm8xx_t *cp = cpm->cp;
>       volatile cbd_t    *tbdf, *rbdf;
>       u_char *tb;
>       unsigned long flags, tmo, timedout;
> 
>       if (count >= CPM_MAX_READ)
>             return -EINVAL;
> 
>       /* check for and use a microcode relocation patch */
>       if (cpm->reloc) {
>             cpm_reset_iic_params(iip);
>       }
> 
>       tbdf = (cbd_t *)&cp->cp_dpmem[iip->iic_tbase];
>       rbdf = (cbd_t *)&cp->cp_dpmem[iip->iic_rbase];
> 
>       /* To read, we need an empty buffer of the proper length.
>        * All that is used is the first byte for address, the remainder
>        * is just used for timing (and doesn't really have to exist).
>        */
>       tb = cpm->temp;
>       tb = (u_char *)(((uint)tb + 15) & ~15);
>       tb[0] = abyte;          /* Device address byte w/rw flag */
> 
>       flush_dcache_range((unsigned long) tb, (unsigned long) (tb+1));
> 
>       if (cpm_debug) printk(KERN_DEBUG "cpm_iic_read(abyte=0x%x)\n",
> abyte);
> 
>       tbdf->cbd_bufaddr = __pa(tb);
>       tbdf->cbd_datlen = count + 1;
>       tbdf->cbd_sc =
>             BD_SC_READY | BD_SC_LAST |
>             BD_SC_WRAP | BD_IIC_START;
> 
>       iip->iic_mrblr = count + 1; /* prevent excessive read, +1
>                               is needed otherwise will the
>                               RXB interrupt come too early */
> 
>       /* flush will invalidate too. */
>       flush_dcache_range((unsigned long) buf, (unsigned long) (buf+count));
> 
>       rbdf->cbd_datlen = 0;
>       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 */
>             i2c->i2c_i2cer = 0xff;
>             i2c->i2c_i2mod |= 1;    /* Enable */
>             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 || (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
> 
> #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 */
>       udelay(4);
>       i2c->i2c_i2mod &= ~1;
> #endif
> 
>       if (cpm_debug) {
>             printk(KERN_DEBUG "tx sc %04x, rx sc %04x\n",
>                    tbdf->cbd_sc, rbdf->cbd_sc);
>       }
> 
>       if (tbdf->cbd_sc & BD_SC_READY) {
>             printk(KERN_INFO "IIC read; complete but tbuf ready\n");
>             force_close(cpm);
>             printk(KERN_INFO "tx sc %04x, rx sc %04x\n",
>                    tbdf->cbd_sc, rbdf->cbd_sc);
>       }
> 
>       if (tbdf->cbd_sc & BD_SC_NAK) {
>             if (cpm_debug)
>                   printk(KERN_DEBUG "IIC read; no ack\n");
>             return -EREMOTEIO;
>       }
> 
>       if (rbdf->cbd_sc & BD_SC_EMPTY) {
>             /* force_close(cpm); */
>             if (cpm_debug){
>                   printk(KERN_DEBUG "IIC read; complete but rbuf empty\n");
>                   printk(KERN_DEBUG "tx sc %04x, rx sc %04x\n",
>                          tbdf->cbd_sc, rbdf->cbd_sc);
>             }
>             return -EREMOTEIO;
>       }
> 
>       if (rbdf->cbd_sc & BD_SC_OV) {
>             if (cpm_debug)
>                   printk(KERN_DEBUG "IIC read; Overrun\n");
>             return -EREMOTEIO;;
>       }
> 
>       if (cpm_debug) printk(KERN_DEBUG "read %d bytes\n",
> rbdf->cbd_datlen);
> 
>       if (rbdf->cbd_datlen < count) {
>             if (cpm_debug)
>                   printk(KERN_DEBUG "IIC read; short, wanted %d got %d\n",
>                          count, rbdf->cbd_datlen);
>             return 0;
>       }
> 
>       return count;
> }
> 
> /* Write to IIC...
>  * addr = address byte, with r/w flag already set
>  */
> static int
> cpm_iic_write(struct i2c_algo_8xx_data *cpm, u_char abyte, char *buf,int
> count)
> {
>       volatile iic_t *iip = cpm->iip;
>       volatile i2c8xx_t *i2c = cpm->i2c;
>       volatile cpm8xx_t *cp = cpm->cp;
>       volatile cbd_t    *tbdf;
>       u_char *tb;
>       unsigned long flags, tmo, timedout;
> 
>       /* check for and use a microcode relocation patch */
>       if (cpm->reloc) {
>             cpm_reset_iic_params(iip);
>       }
>       tb = cpm->temp;
>       tb = (u_char *)(((uint)tb + 15) & ~15);
>       *tb = abyte;            /* Device address byte w/rw flag */
> 
>       flush_dcache_range((unsigned long) tb, (unsigned long) (tb+1));
>       flush_dcache_range((unsigned long) buf, (unsigned long) (buf+count));
> 
>       if (cpm_debug) printk(KERN_DEBUG "cpm_iic_write(abyte=0x%x)\n",
> abyte);
> 
>       /* set up 2 descriptors */
>       tbdf = (cbd_t *)&cp->cp_dpmem[iip->iic_tbase];
> 
>       tbdf[0].cbd_bufaddr = __pa(tb);
>       tbdf[0].cbd_datlen = 1;
>       tbdf[0].cbd_sc = BD_SC_READY | BD_IIC_START;
> 
>       tbdf[1].cbd_bufaddr = __pa(buf);
>       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 */
>             i2c->i2c_i2cer = 0xff;
>             i2c->i2c_i2mod |= 1;    /* Enable */
>             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 || (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.
>        Disabling I2C too early may cause too short stop condition */
>       udelay(4);
>       i2c->i2c_i2mod &= ~1;
> #endif
>       if (cpm_debug) {
>             printk(KERN_DEBUG "tx0 sc %04x, tx1 sc %04x\n",
>                    tbdf[0].cbd_sc, tbdf[1].cbd_sc);
>       }
> 
>       if ((tbdf[0].cbd_sc | tbdf[1].cbd_sc) & BD_SC_NAK) {
>             if (cpm_debug)
>                   printk(KERN_DEBUG "IIC write; no ack\n");
>             return 0;
>       }
> 
>       if ((tbdf[0].cbd_sc | tbdf[1].cbd_sc) & BD_SC_READY) {
>             if (cpm_debug)
>                   printk(KERN_DEBUG "IIC write; complete but tbuf
> ready\n");
>             return 0;
>       }
> 
>       return count;
> }
> 
> /* See if an IIC address exists..
>  * addr = 7 bit address, unshifted
>  */
> static int
> cpm_iic_tryaddress(struct i2c_algo_8xx_data *cpm, int addr)
> {
>       volatile iic_t *iip = cpm->iip;
>       volatile i2c8xx_t *i2c = cpm->i2c;
>       volatile cpm8xx_t *cp = cpm->cp;
>       volatile cbd_t *tbdf, *rbdf;
>       u_char *tb;
>       unsigned long flags, len, tmo;
> 
>       if (cpm_debug > 1)
>             printk(KERN_DEBUG "cpm_iic_tryaddress(cpm=%p,addr=%d)\n", cpm,
> addr);
> 
>       /* check for and use a microcode relocation patch */
>       if (cpm->reloc) {
>             cpm_reset_iic_params(iip);
>       }
> 
>       if (cpm_debug && addr == 0) {
>             printk(KERN_DEBUG "iip %p, dp_addr 0x%x\n", cpm->iip,
> cpm->dp_addr);
>             printk(KERN_DEBUG "iic_tbase %d, r_tbase %d\n", iip->iic_tbase,
> r_tbase);
>       }
> 
>       tbdf = (cbd_t *)&cp->cp_dpmem[iip->iic_tbase];
>       rbdf = (cbd_t *)&cp->cp_dpmem[iip->iic_rbase];
> 
>       tb = cpm->temp;
>       tb = (u_char *)(((uint)tb + 15) & ~15);
> 
>       /* do a simple read */
>       tb[0] = (addr << 1) | 1;      /* device address (+ read) */
>       len = 2;
> 
>       flush_dcache_range((unsigned long) tb, (unsigned long) (tb+1));
> 
>       tbdf->cbd_bufaddr = __pa(tb);
>       tbdf->cbd_datlen = len;
>       tbdf->cbd_sc =
>             BD_SC_READY | BD_SC_LAST |
>             BD_SC_WRAP | BD_IIC_START;
> 
>       rbdf->cbd_datlen = 0;
>       rbdf->cbd_bufaddr = __pa(tb+2);
>       rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP | BD_SC_INTRPT;
> 
>       local_irq_save(flags);
>       i2c->i2c_i2cmr = 0x13;  /* Enable some interupts */
>       i2c->i2c_i2cer = 0xff;
>       i2c->i2c_i2mod |= 1;    /* Enable */
>       i2c->i2c_i2com |= 0x80; /* Begin transmission */
> 
>       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
>       /* Chip errata, clear enable. This is not needed on rev D4 CPUs.
>        Disabling I2C too early may cause too short stop condition */
>       udelay(4);
>       i2c->i2c_i2mod &= ~1;
> #endif
> 
>       if (signal_pending(current) || !tmo){
>             force_close(cpm);
>             if(cpm_debug && !tmo)
>                   printk(KERN_DEBUG "IIC tryaddress: timeout!\n");
>             return -EIO;
>       }
> 
>       if (cpm_debug > 1) printk(KERN_DEBUG "back from sleep\n");
> 
>       if (tbdf->cbd_sc & BD_SC_NAK) {
>             if (cpm_debug > 1) printk(KERN_DEBUG "IIC try; no ack\n");
>             return 0;
>       }
> 
>       if (tbdf->cbd_sc & BD_SC_READY) {
>             printk(KERN_INFO "IIC try; complete but tbuf ready\n");
>       }
> 
>       return 1;
> }
> 
> static int cpm_xfer(struct i2c_adapter *i2c_adap,
>                 struct i2c_msg msgs[],
>                 int num)
> {
>       struct i2c_algo_8xx_data *adap = i2c_adap->algo_data;
>       struct i2c_msg *pmsg;
>       int i, ret;
>       u_char addr;
> 
>       for (i = 0; i < num; i++) {
>             pmsg = &msgs[i];
> 
>             if (cpm_debug)
>                   printk(KERN_DEBUG "i2c-algo-8xx.o: "
>                          "#%d addr=0x%x flags=0x%x len=%d\n buf=%lx\n",
>                          i, pmsg->addr, pmsg->flags, pmsg->len, (unsigned
> long)pmsg->buf);
> 
>             addr = pmsg->addr << 1;
>             if (pmsg->flags & I2C_M_RD )
>                   addr |= 1;
>             if (pmsg->flags & I2C_M_REV_DIR_ADDR )
>                   addr ^= 1;
> 
>             if (!(pmsg->flags & I2C_M_NOSTART)) {
>             }
>             if (pmsg->flags & I2C_M_RD ) {
>                   /* read bytes into buffer*/
>                   ret = cpm_iic_read(adap, addr, pmsg->buf, pmsg->len);
>                   if (cpm_debug)
>                         printk(KERN_DEBUG "i2c-algo-8xx.o: read %d
> bytes\n", ret);
>                   if (ret < pmsg->len ) {
>                         return (ret<0)? ret : -EREMOTEIO;
>                   }
>             } else {
>                   /* write bytes from buffer */
>                   ret = cpm_iic_write(adap, addr, pmsg->buf, pmsg->len);
>                   if (cpm_debug)
>                         printk(KERN_DEBUG "i2c-algo-8xx.o: wrote %d\n",
> ret);
>                   if (ret < pmsg->len ) {
>                         return (ret<0) ? ret : -EREMOTEIO;
>                   }
>             }
>       }
>       return (num);
> }
> 
> static int algo_control(struct i2c_adapter *adapter,
>       unsigned int cmd, unsigned long arg)
> {
>       return 0;
> }
> 
> static u32 cpm_func(struct i2c_adapter *adap)
> {
>       return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR |
>              I2C_FUNC_PROTOCOL_MANGLING;
> }
> 
> /* -----exported algorithm data: -------------------------------------  */
> 
> static struct i2c_algorithm cpm_algo = {
>       "MPC8xx CPM algorithm",
>       I2C_ALGO_MPC8XX,
>       cpm_xfer,
>       NULL,
>       NULL,                   /* slave_xmit           */
>       NULL,                   /* slave_recv           */
>       algo_control,                 /* ioctl          */
>       cpm_func,               /* functionality  */
> };
> 
> /*
>  * registering functions to load algorithms at runtime
>  */
> int i2c_8xx_add_bus(struct i2c_adapter *adap)
> {
>       int i;
>       struct i2c_algo_8xx_data *cpm_adap = adap->algo_data;
> 
>       if (cpm_debug)
>             printk(KERN_DEBUG "i2c-algo-8xx.o: hw routines for %s
> registered.\n",
>                    adap->name);
> 
>       /* register new adapter to i2c module... */
> 
>       adap->id |= cpm_algo.id;
>       adap->algo = &cpm_algo;
> 
> #ifdef MODULE
>       MOD_INC_USE_COUNT;
> #endif
> 
>       i2c_add_adapter(adap);
>       cpm_iic_init(cpm_adap);
> 
>       /* scan bus */
>       if (cpm_scan) {
>             printk(KERN_INFO " i2c-algo-8xx.o: scanning bus %s...\n",
>                    adap->name);
>             for (i = 0; i < 128; i++) {
>                   if (cpm_iic_tryaddress(cpm_adap, i)) {
>                         printk("(%02x)",i<<1);
>                   }
>             }
>             printk("\n");
>       }
>       return 0;
> }
> 
> int i2c_8xx_del_bus(struct i2c_adapter *adap)
> {
>       int res;
>       struct i2c_algo_8xx_data *cpm_adap = adap->algo_data;
> 
>       cpm_iic_shutdown(cpm_adap);
> 
>       if ((res = i2c_del_adapter(adap)) < 0)
>             return res;
> 
>       printk(KERN_INFO "i2c-algo-8xx.o: adapter unregistered:
> %s\n",adap->name);
> 
> #ifdef MODULE
>       MOD_DEC_USE_COUNT;
> #endif
>       return 0;
> }
> 
> EXPORT_SYMBOL(i2c_8xx_add_bus);
> EXPORT_SYMBOL(i2c_8xx_del_bus);
> 
> int __init i2c_algo_8xx_init (void)
> {
>       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;
> }
> 
> 
> #ifdef MODULE
> MODULE_AUTHOR("Brad Parker <brad at heeltoe.com>");
> MODULE_DESCRIPTION("I2C-Bus MPC8XX algorithm");
> #ifdef MODULE_LICENSE
> MODULE_LICENSE("GPL");
> #endif
> 
> int init_module(void)
> {
>       return i2c_algo_8xx_init();
> }
> 
> void cleanup_module(void)
> {
> }
> #endif
> 
> _______________________________________________
> Linuxppc-embedded mailing list
> Linuxppc-embedded at ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-embedded



More information about the Linuxppc-embedded mailing list