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 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


Reply via email to