[PATCH] I2C: Add I2C support for the MPC8260
Kumar Gala
galak at gate.crashing.org
Thu Nov 24 06:06:18 EST 2005
On Wed, 23 Nov 2005, Heiko Schocher wrote:
* There is a bunch of code commented out in one form or another, is this
needed?
* Single line comment style
* some other comments are inline.
- kumar
> Hello
>
> On Wednesday, November 23, 2005 8:01 AM Kumar Gala wrote:
> > Can we rename the driver from mpc8260 -> cpm2. The driver should work
> > on any device that has a "CPM2" which includes a number of MPC82xx
> > and MPC85xx processors. So calling it and its config options, etc
> > MPC8260 is going to be confusing to users.
>
> [PATCH] I2C: Add I2C Bus support for MPC with CPM2.
>
> Signed-off-by: Heiko Schocher <hs at denx.de>
>
> ---
>
> drivers/i2c/busses/Kconfig | 10 +
> drivers/i2c/busses/Makefile | 1
> drivers/i2c/busses/i2c-cpm2.c | 616 +++++++++++++++++++++++++++++++++++++++++
> include/linux/i2c-algo-cpm2.h | 26 ++
> include/linux/i2c-id.h | 3
> 5 files changed, 656 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index 4010fe9..2acb4cc 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -270,6 +270,16 @@ config I2C_MPC
> This driver can also be built as a module. If so, the module
> will be called i2c-mpc.
>
> +config I2C_CPM2
> + tristate "CPM2"
> + depends on I2C && PPC32
it should really depend on CPM2 being set not PPC32.
> + help
> + If you say yes to this option, support will be included for the
> + I2C interface on PPC with CPM2
> +
> + This driver can also be built as a module. If so, the module
> + will be called i2c-cpm2.
> +
> config I2C_NFORCE2
> tristate "Nvidia nForce2, nForce3 and nForce4"
> depends on I2C && PCI
> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> index f1df00f..a4c1b69 100644
> --- a/drivers/i2c/busses/Makefile
> +++ b/drivers/i2c/busses/Makefile
> @@ -43,6 +43,7 @@ obj-$(CONFIG_I2C_VIAPRO) += i2c-viapro.o
> obj-$(CONFIG_I2C_VOODOO3) += i2c-voodoo3.o
> obj-$(CONFIG_SCx200_ACB) += scx200_acb.o
> obj-$(CONFIG_SCx200_I2C) += scx200_i2c.o
> +obj-$(CONFIG_I2C_CPM2) += i2c-cpm2.o
>
> ifeq ($(CONFIG_I2C_DEBUG_BUS),y)
> EXTRA_CFLAGS += -DDEBUG
> diff --git a/drivers/i2c/busses/i2c-cpm2.c b/drivers/i2c/busses/i2c-cpm2.c
> new file mode 100644
> index 0000000..80dc532
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-cpm2.c
> @@ -0,0 +1,616 @@
> +/*
> + * (C) Copyright 2005
> + * Heiko Schocher <hs at denx.de>
> + *
> + * This is a combined i2c adapter and algorithm driver for
> + * PPC with CPM2
> + *
> + * Release 0.1
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/config.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/sched.h>
> +#include <linux/init.h>
> +#include <linux/pci.h>
> +#include <asm/io.h>
> +#include <linux/fsl_devices.h>
> +#include <linux/i2c.h>
> +#include <linux/interrupt.h>
> +#include <linux/delay.h>
> +#include <asm/immap_cpm2.h>
> +#include <asm/mpc8260.h>
> +#include <asm/cpm2.h>
> +
> +#include <linux/i2c-algo-cpm2.h>
> +#include <linux/platform_device.h>
> +
> +#define CPM_MAX_READ 513
> +
> +static wait_queue_head_t iic_wait;
> +static ushort r_tbase, r_rbase;
> +
> +int cpm_scan = 0;
> +int cpm_debug = 0;
> +
It would be nice to rename the struct so its unique between i2c-mpc and
i2c-cpm2. Since its reasonable for both drivers to exist in a system.
> +struct mpc_i2c {
> + u32 interrupt;
> + wait_queue_head_t queue;
> + struct i2c_adapter adap;
> + int irq;
> + u32 flags;
> + struct i2c_algo_cpm2_data *data;
> +};
> +
> +static struct i2c_algo_cpm2_data cpm2_data;
> +
> +static void
> +cpm2_iic_init(struct i2c_algo_cpm2_data *data)
> +{
> + volatile cpm_cpm2_t *cp;
> + volatile cpm2_map_t *immap;
> +
> + cp = cpmp; /* Get pointer to Communication Processor */
> + immap = (cpm2_map_t *)CPM_MAP_ADDR; /* and to internal registers */
> +
> + *(ushort *)(&immap->im_dprambase[PROFF_I2C_BASE]) = PROFF_I2C;
> + data->iip = (iic_t *)&immap->im_dprambase[PROFF_I2C];
> +
> + data->i2c = (i2c_cpm2_t *)&(immap->im_i2c);
> + data->cp = cp;
> +
> + /* Initialize Port D IIC pins.
> + */
> + immap->im_ioport.iop_ppard |= 0x00030000;
> + immap->im_ioport.iop_pdird &= ~0x00030000;
> + immap->im_ioport.iop_podrd |= 0x00030000;
> + immap->im_ioport.iop_psord |= 0x00030000;
> +
> + /* Allocate space for two transmit and two receive buffer
> + * descriptors in the DP ram.
> + */
> + data->dp_addr = cpm_dpalloc(sizeof(cbd_t) * 4, 8);
> +
> + /* ptr to i2c area */
> + data->i2c = (i2c_cpm2_t *)&(((cpm2_map_t *)CPM_MAP_ADDR)->im_i2c);
> +}
> +
> +static irqreturn_t cpm2_i2c_isr(int irq, void *dev_id, struct pt_regs *regs)
> +{
> + struct mpc_i2c *mpc_i2c = dev_id;
> + struct i2c_algo_cpm2_data *cpm_adap = mpc_i2c->data;
> + volatile i2c_cpm2_t *i2c = cpm_adap->i2c;
> +
> + if (cpm_debug > 1)
> + printk(KERN_DEBUG "cpm_iic_interrupt(dev_id=%p)\n", dev_id);
> +
> + /* Clear interrupt.
> + */
> + i2c->i2c_i2cer = 0xff;
> +
> + /* Get 'me going again.
> + */
> + wake_up_interruptible(&iic_wait);
> + return IRQ_HANDLED;
> +}
> +
> +
> +static void
> +cpm_iic_init(struct i2c_algo_cpm2_data *cpm_adap)
> +{
> + volatile iic_t *iip = cpm_adap->iip;
> + volatile i2c_cpm2_t *i2c = cpm_adap->i2c;
> +
> + 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 = CPMFCR_GBL | CPMFCR_EB;
> + iip->iic_rfcr = CPMFCR_GBL | CPMFCR_EB;
> +
> + /* Set maximum receive size.
> + */
> + iip->iic_mrblr = CPM_MAX_READ;
> +
> + /* Initialize Tx/Rx parameters.
> + */
> + {
> + volatile cpm_cpm2_t *cp = cpm_adap->cp;
> + cp->cp_cpcr =
> + mk_cr_cmd(CPM_CR_I2C_PAGE, CPM_CR_I2C_SBLOCK, 0x00, CPM_CR_INIT_TRX) | CPM_CR_FLG;
> + while (cp->cp_cpcr & CPM_CR_FLG);
> + }
> +
> + /* Select an arbitrary address. Just make sure it is unique.
> + */
> + i2c->i2c_i2add = 0x34;
> +
> + /* Divider is 2 * ( 15 + 3 )
> + */
> + i2c->i2c_i2brg = 0x0f;
> +
> + /* Pre-divider is BRGCLK/4
> + */
> + i2c->i2c_i2mod = 0x06;
> +
> + /* Disable interrupts.
> + */
> + i2c->i2c_i2cmr = 0;
> + i2c->i2c_i2cer = 0xff;
> +
> + init_waitqueue_head(&iic_wait);
> +}
> +
> +
> +#if 0
> +static int
> +cpm_iic_shutdown(struct i2c_algo_cpm2_data *cpm_adap)
> +{
> + volatile i2c_cpm2_t *i2c = cpm_adap->i2c;
> +
> + /* Shut down IIC.
> + */
> + i2c->i2c_i2mod = 0;
> + i2c->i2c_i2cmr = 0;
> + i2c->i2c_i2cer = 0xff;
> +
> + return(0);
> +}
> +#endif
> +
> +static void
> +cpm_reset_iic_params(volatile iic_t *iip)
> +{
> + iip->iic_tbase = r_tbase;
> + iip->iic_rbase = r_rbase;
> +
> + iip->iic_tfcr = CPMFCR_GBL | CPMFCR_EB;
> + iip->iic_rfcr = CPMFCR_GBL | CPMFCR_EB;
> +
> + iip->iic_mrblr = CPM_MAX_READ;
> +
> + 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;
> +}
> +
> +#define BD_SC_NAK ((ushort)0x0004) /* NAK - did not respond */
> +#define CPM_CR_CLOSE_RXBD ((ushort)0x0007)
> +
> +static void force_close(struct i2c_algo_cpm2_data *cpm)
> +{
> +#if 0
> + volatile cpm_cpm2_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);
> +#endif
> +}
> +
> +
> +/* Read from IIC...
> + * abyte = address byte, with r/w flag already set
> + */
> +static int
> +cpm_iic_read(struct i2c_algo_cpm2_data *cpm, u_char abyte, char *buf, int count)
> +{
> + volatile cpm2_map_t *immap = (cpm2_map_t *)CPM_MAP_ADDR;
> + volatile iic_t *iip = cpm->iip;
> + volatile i2c_cpm2_t *i2c = cpm->i2c;
> + volatile cbd_t *tbdf, *rbdf;
> + u_char *tb;
> + unsigned long flags;
> +
> + if (count >= CPM_MAX_READ)
> + return -EINVAL;
> +
> + tbdf = (cbd_t *)&immap->im_dprambase[iip->iic_tbase];
> + rbdf = (cbd_t *)&immap->im_dprambase[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).
> + */
> + if (/*cpm->reloc*/0) {
> + cpm_reset_iic_params(iip);
> + }
> + tb = cpm->temp;
> + tb = (u_char *)(((uint)tb + 15) & ~15);
> + tb[0] = abyte; /* Device address byte w/rw flag */
> +
> + dma_cache_wback_inv (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_INTRPT | BD_SC_LAST |
> + BD_SC_WRAP | BD_IIC_START;
> +
> + rbdf->cbd_datlen = 0;
> + rbdf->cbd_bufaddr = __pa(buf);
> + rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP;
> +
> + if (count > 0 && count < CPM_MAX_READ)
> + iip->iic_mrblr = count; /* prevent excessive read */
> +
> + dma_cache_inv (buf, count);
> +
> + /* 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 = 0x81; /* Start master */
> +
> + /* Wait for IIC transfer */
> + interruptible_sleep_on(&iic_wait);
> + local_irq_restore(flags);
> + if (signal_pending(current))
> + return -EIO;
> +
> + 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_NAK) {
> + printk(KERN_INFO "IIC read; no ack\n");
> + return 0;
> + }
> +
> + if (rbdf->cbd_sc & BD_SC_EMPTY) {
> + printk(KERN_INFO "IIC read; complete but rbuf empty\n");
> + force_close(cpm);
> + printk(KERN_INFO "tx sc %04x, rx sc %04x\n",
> + tbdf->cbd_sc, rbdf->cbd_sc);
> + }
> +
> + if (cpm_debug) printk(KERN_DEBUG "read %d bytes\n", rbdf->cbd_datlen);
> +
> + if (rbdf->cbd_datlen < count) {
> + printk(KERN_INFO "IIC read; short, wanted %d got %d\n",
> + count, rbdf->cbd_datlen);
> + return 0;
> + }
> + if (cpm_debug) {
> + int u;
> + for (u = 0; u < count; u++) {
> + printk(KERN_DEBUG "buf[%d] = 0x%x\n", u, buf[u]);
> + }
> + }
> +
> +
> + return count;
> +}
> +
> +/* Write to IIC...
> + * addr = address byte, with r/w flag already set
> + */
> +static int
> +cpm_iic_write(struct i2c_algo_cpm2_data *cpm, u_char abyte, char *buf,int count)
> +{
> + volatile cpm2_map_t *immap = (cpm2_map_t *)CPM_MAP_ADDR;
> + volatile iic_t *iip = cpm->iip;
> + volatile i2c_cpm2_t *i2c = cpm->i2c;
> + volatile cbd_t *tbdf;
> + u_char *tb;
> + unsigned long flags;
> +
> + tb = cpm->temp;
> + tb = (u_char *)(((uint)tb + 15) & ~15);
> + *tb = abyte; /* Device address byte w/rw flag */
> +
> + dma_cache_wback_inv (tb, 1);
> + dma_cache_wback_inv (buf, count);
> +
> + if (cpm_debug) printk(KERN_DEBUG "cpm_iic_write(abyte=0x%x)\n", abyte);
> + if (cpm_debug) printk(KERN_DEBUG "buf[0] = 0x%x, buf[1] = 0x%x\n", buf[0], buf[1]);
> +
> + /* set up 2 descriptors */
> + tbdf = (cbd_t *)&immap->im_dprambase[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;
> +
> + /* 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 = 0x81; /* Start master */
> +
> + /* Wait for IIC transfer */
> + interruptible_sleep_on(&iic_wait);
> + local_irq_restore(flags);
> + if (signal_pending(current))
> + return -EIO;
> +
> + if (cpm_debug) {
> + printk(KERN_DEBUG "tx0 sc %04x, tx1 sc %04x\n",
> + tbdf[0].cbd_sc, tbdf[1].cbd_sc);
> + }
> +
> + if (tbdf->cbd_sc & BD_SC_NAK) {
> + printk(KERN_INFO "IIC write; no ack\n");
> + return 0;
> + }
> +
> + if (tbdf->cbd_sc & BD_SC_READY) {
> + printk(KERN_INFO "IIC write; complete but tbuf ready\n");
> + return 0;
> + }
> +
> + return count;
> +}
> +
> +#if 0
> +/* See if an IIC address exists..
> + * addr = 7 bit address, unshifted
> + */
> +static int
> +cpm_iic_tryaddress(struct i2c_algo_cpm2_data *cpm, int addr)
> +{
> + volatile cpm2_map_t *immap = (cpm2_map_t *)CPM_MAP_ADDR;
> + volatile iic_t *iip = cpm->iip;
> + volatile i2c_cpm2_t *i2c = cpm->i2c;
> + volatile cbd_t *tbdf, *rbdf;
> + u_char *tb;
> + unsigned long flags, len;
> +
> + if (cpm_debug > 1)
> + printk(KERN_DEBUG "cpm_iic_tryaddress(cpm=%p,addr=%d)\n", cpm, addr);
> +
> + 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 *)&immap->im_dprambase[iip->iic_tbase];
> + rbdf = (cbd_t *)&immap->im_dprambase[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;
> +
> + dma_cache_wback_inv (tb, 1);
> +
> + tbdf->cbd_bufaddr = __pa(tb);
> + tbdf->cbd_datlen = len;
> + tbdf->cbd_sc =
> + BD_SC_READY | BD_SC_INTRPT | 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;
> +
> + local_irq_save(flags);
> + i2c->i2c_i2cmr = 0x13; /* Enable some interupts */
> + i2c->i2c_i2cer = 0xff;
> + i2c->i2c_i2mod |= 1; /* Enable */
> + i2c->i2c_i2com = 0x81; /* Start master */
> +
> + if (cpm_debug > 1) printk(KERN_DEBUG "about to sleep\n");
> +
> + /* wait for IIC transfer */
> + interruptible_sleep_on(&iic_wait);
> + local_irq_restore(flags);
> + if (signal_pending(current))
> + 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;
> +}
> +#endif
> +
> +static int cpm2_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
> +{
> + struct i2c_msg *pmsg;
> + int i;
> + int ret = 0;
> + struct mpc_i2c *mpc_i2c = i2c_get_adapdata(adap);
> + struct i2c_algo_cpm2_data *data = mpc_i2c->data;
> + u_char addr;
> +
> + for (i = 0; i < num; i++) {
> + pmsg = &msgs[i];
> +
> + if (cpm_debug)
> + printk(KERN_DEBUG "i2c-cpm2.o: "
> + "#%d addr=0x%x flags=0x%x len=%d\n",
> + i, pmsg->addr, pmsg->flags, pmsg->len);
> +
> + 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(data, addr, pmsg->buf, pmsg->len);
> + if (cpm_debug)
> + printk(KERN_DEBUG "i2c-cpm2.o: read %d bytes\n", ret);
> + if (ret < pmsg->len ) {
> + return (ret<0)? ret : -EREMOTEIO;
> + }
> + } else {
> + /* write bytes from buffer */
> + ret = cpm_iic_write(data, addr, pmsg->buf, pmsg->len);
> + if (cpm_debug)
> + printk(KERN_DEBUG "i2c-cpm2.o: wrote %d\n", ret);
> + if (ret < pmsg->len ) {
> + return (ret<0) ? ret : -EREMOTEIO;
> + }
> + }
> + }
> + return (ret < 0) ? ret : num;
> +}
> +
> +static u32 cpm2_functionality(struct i2c_adapter *adap)
> +{
> + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR |
> + I2C_FUNC_PROTOCOL_MANGLING;
> +}
> +
> +static struct i2c_algorithm cpm2_algo = {
> + .master_xfer = cpm2_xfer,
> + .functionality = cpm2_functionality,
> +};
> +
> +static struct i2c_adapter cpm2_ops = {
> + .owner = THIS_MODULE,
> + .name = "MPC CPM2 adapter",
> + .id = I2C_HW_MPC82xx,
> + .algo = &cpm2_algo,
> + .class = I2C_CLASS_HWMON,
> + .timeout = 1,
> + .retries = 1
> +};
> +
> +static int fsl_i2c_probe(struct device *device)
> +{
> + int result = 0;
> + struct mpc_i2c *i2c;
> + struct platform_device *pdev = to_platform_device(device);
> + struct fsl_i2c_platform_data *pdata;
> +
> + pdata = (struct fsl_i2c_platform_data *) pdev->dev.platform_data;
> +
> + if (!(i2c = kmalloc(sizeof(*i2c), GFP_KERNEL))) {
> + return -ENOMEM;
> + }
> + memset(i2c, 0, sizeof(*i2c));
> +
> + i2c->data = &cpm2_data;
> + /* CPM Plattform initialisation */
> + cpm2_iic_init (i2c->data);
> + /* CPM init */
> + cpm_iic_init (i2c->data);
> + i2c->irq = platform_get_irq(pdev, 0);
> +
> + if (i2c->irq != 0) {
> + if ((result = request_irq(i2c->irq, cpm2_i2c_isr,
> + SA_SHIRQ, "i2c-mpc", i2c)) < 0) {
> + printk(KERN_ERR
> + "i2c-mpc - failed to attach interrupt\n");
> + goto fail_irq;
> + }
> + }
> + dev_set_drvdata(device, i2c);
> +
> + i2c->adap = cpm2_ops;
> + i2c_set_adapdata(&i2c->adap, i2c);
> + i2c->adap.dev.parent = &pdev->dev;
> + if ((result = i2c_add_adapter(&i2c->adap)) < 0) {
> + printk(KERN_ERR "i2c-mpc - failed to add adapter\n");
> + goto fail_add;
> + }
> +
> + return result;
> +
> + fail_add:
> + if (i2c->irq != 0)
> + free_irq(i2c->irq, NULL);
> + fail_irq:
> + kfree(i2c);
> + return result;
> +};
> +
> +static int fsl_i2c_remove(struct device *device)
> +{
> + struct mpc_i2c *i2c = dev_get_drvdata(device);
> +
> + cpm_dpfree (i2c->data->dp_addr);
> +
> + i2c_del_adapter(&i2c->adap);
> + dev_set_drvdata(device, NULL);
> +
> + if (i2c->irq != 0)
> + free_irq(i2c->irq, i2c);
> +
> + kfree(i2c);
> + return 0;
> +};
> +
> +/* Structure for a device driver */
> +static struct device_driver fsl_i2c_driver = {
> + .name = "fsl-cpm-i2c",
> + .bus = &platform_bus_type,
> + .probe = fsl_i2c_probe,
> + .remove = fsl_i2c_remove,
> +};
> +
> +static int __init fsl_i2c_init(void)
> +{
> + return driver_register(&fsl_i2c_driver);
> +}
> +
> +static void __exit fsl_i2c_exit(void)
> +{
> + driver_unregister(&fsl_i2c_driver);
> +}
> +
> +module_init(fsl_i2c_init);
> +module_exit(fsl_i2c_exit);
> +
> +MODULE_AUTHOR("Heiko Schocher <hs at denx.de>");
> +MODULE_DESCRIPTION
> + ("I2C-Bus adapter for MPC with CPM2 processors");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/i2c-algo-cpm2.h b/include/linux/i2c-algo-cpm2.h
> new file mode 100644
> index 0000000..f03277c
> --- /dev/null
> +++ b/include/linux/i2c-algo-cpm2.h
> @@ -0,0 +1,26 @@
> +/* ----------------------------------------------------------- */
> +/* i2c-algo-cpm2.h i2c driver algorithms for MPC with CPM2 */
> +/* ----------------------------------------------------------- */
> +
> +/* $Id$ */
> +
> +#ifndef I2C_ALGO_CPM2_H
> +#define I2C_ALGO_CPM2_H 1
> +
> +#include <linux/i2c.h>
> +
> +struct i2c_algo_cpm2_data {
> + uint dp_addr;
> + int reloc;
> + volatile i2c_cpm2_t *i2c;
> + volatile iic_t *iip;
> + volatile cpm_cpm2_t *cp;
> +
> + int (*setisr) (int irq,
> + void (*func)(int, void (*)(void *), void *),
> + void *data);
> +
> + u_char temp[513];
> +};
> +
> +#endif /* I2C_ALGO_CPM2_H */
> diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h
> index 1543daa..e7a12cc 100644
> --- a/include/linux/i2c-id.h
> +++ b/include/linux/i2c-id.h
> @@ -223,6 +223,9 @@
> /* --- PowerPC on-chip adapters */
> #define I2C_HW_OCP 0x120000 /* IBM on-chip I2C adapter */
>
> +/* --- MPC82xx PowerPC adapters */
> +#define I2C_HW_MPC82xx 0x100002 /* MPC82xx I2C adapter */
> +
> /* --- Broadcom SiByte adapters */
> #define I2C_HW_SIBYTE 0x150000
>
>
More information about the Linuxppc-dev
mailing list