#### Auto-generated patch #### Date: Tue, 22 Mar 2005 15:23:27 -0500 Maintainer: Jason McMullan Summary: CPM/CPM2 I2C driver for Freescale SoC devices Description: CPM/CPM2 I2C driver for Freescale SoC devices Depends: ############################### Index of changes: drivers/i2c/algos/Kconfig | 4 drivers/i2c/algos/Makefile | 1 drivers/i2c/busses/Kconfig | 25 + drivers/i2c/busses/Makefile | 1 include/linux/i2c-id.h | 4 linux/drivers/i2c/algos/i2c-algo-cpm.c | 477 +++++++++++++++++++++++++++++++++ linux/drivers/i2c/busses/i2c-cpm.c | 215 ++++++++++++++ linux/include/linux/i2c-algo-cpm.h | 64 ++++ 8 files changed, 787 insertions(+), 4 deletions(-) --- linux-orig/drivers/i2c/algos/Kconfig +++ linux/drivers/i2c/algos/Kconfig @@ -16,6 +16,10 @@ This support is also available as a module. If so, the module will be called i2c-algo-bit. +config I2C_ALGO_CPM + tristate "MPC (8xx and 8xxx) CPM I2C interface" + depends on (8xx || 85xx ) && I2C + config I2C_ALGOPCF tristate "I2C PCF 8584 interfaces" depends on I2C --- linux-orig/drivers/i2c/algos/Makefile +++ linux/drivers/i2c/algos/Makefile @@ -3,6 +3,7 @@ # obj-$(CONFIG_I2C_ALGOBIT) += i2c-algo-bit.o +obj-$(CONFIG_I2C_ALGO_CPM) += i2c-algo-cpm.o obj-$(CONFIG_I2C_ALGOPCF) += i2c-algo-pcf.o obj-$(CONFIG_I2C_ALGOPCA) += i2c-algo-pca.o obj-$(CONFIG_I2C_ALGOITE) += i2c-algo-ite.o --- /dev/null +++ linux/drivers/i2c/algos/i2c-algo-cpm.c @@ -0,0 +1,477 @@ +/* + * i2c-algo-cpm.c i2x driver algorithms for Motorola MPC CPM + * Copyright (c) 2004 Jason McMullan (jason.mcmullan@timesys.com) + * Copyright (c) 1999 Dan Malek (dmalek@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. + * + * based on i2c-algo-cpm.c + */ + +/* $Id$ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define I2C_VERSION "0.1" +#define I2C_DATE "Mar 22, 2005" + +#define CPM_MAX_READ 513 +/* #define I2C_CHIP_ERRATA */ /* Try uncomment this if you have an older CPU(earlier than rev D4) */ + +#undef DEBUG + +#ifdef DEBUG +int cpm_debug = 0; +MODULE_PARM(cpm_debug, "i"); +#define static /**/ +#define DPRINTK(fmt, args...) printk(KERN_INFO fmt ,##args) +#else +#define DPRINTK(args...) do { } while (0) +#endif + +static void cpm_i2c_param_reset(struct i2c_algo_cpm_data *cpm_adap) +{ + volatile cpm_iic_t *iip = cpm_adap->iip; + + /* Set up the IIC parameters in the parameter ram. + */ + iip->iic_rbase = cpm_adap->dp_offset; + iip->iic_tbase = cpm_adap->dp_offset + sizeof(cbd_t)*2; + + /* 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; + + iip->iic_tfcr = CPMFCR_EB | CPMFCR_GBL; + iip->iic_rfcr = CPMFCR_EB | CPMFCR_GBL; + + /* Set maximum receive size. + */ + iip->iic_mrblr = CPM_MAX_READ; + +} + +static int i2c_cpm_start(struct i2c_algo_cpm_data *cpm) +{ + volatile cpm_i2c_t *i2c = cpm->i2c; + + DPRINTK("i2c_cpm_start: Ready (i2cmr=%.2x, i2cer=%.2x, i2com=%.2x, i2mod=%.2x)\n", i2c->i2c_i2cmr,i2c->i2c_i2cer,i2c->i2c_i2com,i2c->i2c_i2mod); + + /* Wait for IIC transfer */ + cpm->status = -EBUSY; + i2c->i2c_i2mod &= ~I2MOD_EN; + + /* Chip bug, set enable here */ + i2c->i2c_i2cmr = I2CER_MASK; /* Enable interupts */ + i2c->i2c_i2cer = I2CER_MASK; + i2c->i2c_i2mod = I2MOD_EN; /* Enable */ + i2c->i2c_i2com = I2COM_STR | I2COM_MS; /* Begin transmission */ + interruptible_sleep_on(&cpm->wait); + + DPRINTK("i2c_cpm_start: Done (i2cmr=%.2x, i2cer=%.2x, i2com=%.2x, i2mod=%.2x)\n", i2c->i2c_i2cmr,i2c->i2c_i2cer,i2c->i2c_i2com,i2c->i2c_i2mod); + + DPRINTK("i2c_cpm_start: result=%d\n",cpm->status); + return cpm->status; +} + +static irqreturn_t i2c_cpm_interrupt(int unused, void *dev_id, struct pt_regs *regs) + { + struct i2c_algo_cpm_data *cpm_adap = dev_id; + uint8_t events; + +#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. + */ + cpm_adap->i2c->i2c_i2mod &= ~I2MOD_EN; wmb(); +#endif + + events = cpm_adap->i2c->i2c_i2cer; + DPRINTK("i2c_cpm_interrupt: dev_id=0x%p, i2cer=0x%.2x\n", dev_id,events); + + + if (events & I2CER_TXE) { + cpm_adap->status = -ENODEV; + } else if (events & I2CER_BSY) { + cpm_adap->status = -EAGAIN; + } else if (events & (I2CER_RXB | I2CER_TXB)) { + cpm_adap->status = 0; + } + + /* Clear interrupt. + */ + cpm_adap->i2c->i2c_i2cer = events; wmb(); + + /* Get 'em going again. + */ + wake_up_interruptible(&cpm_adap->wait); + + return IRQ_HANDLED; +} + +/* Read from IIC... + * abyte = address byte, with r/w flag already set + */ +static int i2c_cpm_read(struct i2c_algo_cpm_data *cpm, uint8_t abyte, char *buf, int count) +{ + volatile cbd_t *tbdf, *rbdf; + uint8_t *tb; + dma_addr_t tb_dma[2]; + int err; + + DPRINTK("i2c_cpm_read: abyte=0x%x(%c)\n", abyte>>1, "wr"[abyte&1]); + + if (count >= CPM_MAX_READ) + return -EINVAL; + + /* If we are relocated, we must reset the params */ + if (cpm->reloc) + cpm_i2c_param_reset(cpm); + + rbdf = (void *)cpm_dpram_addr(cpm->iip->iic_rbase); + tbdf = (void *)cpm_dpram_addr(cpm->iip->iic_tbase); + + /* 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[0] = abyte; /* Device address byte w/rw flag */ + + + tb_dma[0] = dma_map_single(cpm->device, tb, 1, DMA_TO_DEVICE); + tbdf->cbd_bufaddr = tb_dma[0]; + tbdf->cbd_datlen = 1; + tbdf->cbd_sc = BD_SC_READY | BD_SC_LAST | BD_SC_WRAP | BD_IIC_START; + + cpm->iip->iic_mrblr = count + 1; + + tb_dma[1] = dma_map_single(cpm->device, buf, count, DMA_FROM_DEVICE); + rbdf->cbd_datlen = 0; + rbdf->cbd_bufaddr = tb_dma[1]; + rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP| BD_SC_INTRPT; + + DPRINTK("tbdf->cbd_sc(0x%p) = 0x%.4x\n",&tbdf->cbd_sc,tbdf->cbd_sc); + DPRINTK("rbdf->cbd_sc(0x%p) = 0x%.4x\n",&rbdf->cbd_sc,rbdf->cbd_sc); + err = i2c_cpm_start(cpm); + dma_unmap_single(cpm->device, tb_dma[1], count, DMA_FROM_DEVICE); + dma_unmap_single(cpm->device, tb_dma[0], 1, DMA_TO_DEVICE); + DPRINTK("tbdf->cbd_sc(0x%p) = 0x%.4x\n",&tbdf->cbd_sc,tbdf->cbd_sc); + DPRINTK("rbdf->cbd_sc(0x%p) = 0x%.4x\n",&rbdf->cbd_sc,rbdf->cbd_sc); + if (err) + return err; + + if (tbdf->cbd_sc & BD_SC_READY) { + DPRINTK("i2c_cpm_read: complete but tbuf ready\n"); + DPRINTK("\ttx sc %04x, rx sc %04x\n", tbdf->cbd_sc, rbdf->cbd_sc); + return -EREMOTEIO; + } + + if (tbdf->cbd_sc & BD_SC_NAK) { + DPRINTK("i2c_cpm_read: no ack\n"); + return -EREMOTEIO; + } + + if (rbdf->cbd_sc & BD_SC_EMPTY) { + DPRINTK("i2c_cpm_read: complete but rbuf empty\n"); + DPRINTK("\ttx sc %04x, rx sc %04x\n", tbdf->cbd_sc, rbdf->cbd_sc); + return -EREMOTEIO; + } + + if (rbdf->cbd_sc & BD_SC_OV) { + DPRINTK("i2c_cpm_read: Overrun\n"); + return -EREMOTEIO; + } + + DPRINTK("i2c_cpm_read: read %d bytes\n", rbdf->cbd_datlen); + + if (rbdf->cbd_datlen < count) { + DPRINTK("i2c_cpm_read: short, wanted %d got %d\n", count, rbdf->cbd_datlen); + } + + return count; +} + +/* Write to IIC... + * addr = address byte, with r/w flag already set + */ +static int +i2c_cpm_write(struct i2c_algo_cpm_data *cpm, uint8_t abyte, char *buf,int count) +{ + volatile cbd_t *tbdf, *rbdf; + uint8_t *tb; + dma_addr_t tb_dma[2]; + int err; + + DPRINTK("i2c_cpm_write: abyte=0x%x(%c)\n", abyte>>1, "wr"[abyte&1]); + + /* If we are relocated, we must reset the params */ + if (cpm->reloc) + cpm_i2c_param_reset(cpm); + + tb = cpm->temp; + tb[0] = abyte; /* Device address byte w/rw flag */ + + /* set up 2 descriptors */ + tbdf = (void *)cpm_dpram_addr(cpm->iip->iic_tbase); + rbdf = (void *)cpm_dpram_addr(cpm->iip->iic_rbase); + + tb_dma[0] = dma_map_single(cpm->device, tb, 1, DMA_TO_DEVICE); + tbdf[0].cbd_bufaddr = tb_dma[0]; + tbdf[0].cbd_datlen = 1; + tbdf[0].cbd_sc = BD_SC_READY | BD_SC_INTRPT | BD_IIC_START; + + if (count > 0) { + tb_dma[1] = dma_map_single(cpm->device, buf, count, DMA_TO_DEVICE); + tbdf[1].cbd_bufaddr = tb_dma[1]; + tbdf[1].cbd_datlen = count; + tbdf[1].cbd_sc = BD_SC_READY | BD_SC_INTRPT | BD_SC_LAST | BD_SC_WRAP; + } else { + tbdf[0].cbd_sc |= BD_SC_INTRPT | BD_SC_LAST | BD_SC_WRAP; + tb_dma[1] = 0; + } + + rbdf[0].cbd_sc = BD_SC_WRAP; + + DPRINTK("tbdf->cbd_sc(0x%p) = 0x%.4x(%d),0x%.4x(%d)\n",&tbdf->cbd_sc,tbdf[0].cbd_sc,tbdf[0].cbd_datlen,tbdf[1].cbd_sc,tbdf[1].cbd_datlen); + err = i2c_cpm_start(cpm); + + if (tb_dma[1]) + dma_unmap_single(cpm->device, tb_dma[1], count, DMA_TO_DEVICE); + dma_unmap_single(cpm->device, tb_dma[0], 1, DMA_TO_DEVICE); + + DPRINTK("tbdf->cbd_sc(0x%p) = 0x%.4x(%d),0x%.4x(%d)\n",&tbdf->cbd_sc,tbdf[0].cbd_sc,tbdf[0].cbd_datlen,tbdf[1].cbd_sc,tbdf[1].cbd_datlen); + if (err < 0) + return err; + + + if (tbdf->cbd_sc & BD_SC_NAK) { + DPRINTK("i2c_cpm_write; no ack\n"); + return -ENODEV; + } + + if (tbdf->cbd_sc & BD_SC_READY) { + DPRINTK("i2c_cpm_write: complete but tbuf ready\n"); + return -EREMOTEIO; + } + + return count; +} + +static int cpm_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], + int num) +{ + struct i2c_algo_cpm_data *adap = i2c_adap->algo_data; + struct i2c_msg *pmsg; + int i, ret; + uint8_t addr; + + for (i = 0; i < num; i++) { + pmsg = &msgs[i]; + + DPRINTK("i2c-algo-cpm: #%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 = i2c_cpm_read(adap, addr, pmsg->buf, pmsg->len); + DPRINTK("i2c-algo-cpm: read %d bytes\n", ret); + if (ret < pmsg->len ) { + return (ret<0)? ret : -EREMOTEIO; + } + } else { + /* write bytes from buffer */ + ret = i2c_cpm_write(adap, addr, pmsg->buf, pmsg->len); + DPRINTK("i2c-algo-cpm: 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_PROTOCOL_MANGLING; +} + +/* -----exported algorithm data: ------------------------------------- */ + +static struct i2c_algorithm cpm_algo = { + "CPM2 algorithm", + I2C_ALGO_CPM2, + cpm_xfer, + NULL, + NULL, /* slave_xmit */ + NULL, /* slave_recv */ + algo_control, /* ioctl */ + cpm_func, /* functionality */ +}; + +static int i2c_cpm_init(struct i2c_algo_cpm_data *cpm_adap) +{ + volatile cpm_i2c_t *i2c = cpm_adap->i2c; + unsigned char brg; + + DPRINTK("i2c_cpm_init() - iip=%p\n",cpm_adap->iip); + + cpm_adap->dp_offset = cpm_dpalloc(sizeof(cbd_t)*4+1, 16); + cpm_adap->temp = cpm_dpram_addr(cpm_adap->dp_offset+sizeof(cbd_t)*4); + + cpm_i2c_param_reset(cpm_adap); + + /* Initialize Tx/Rx parameters. + */ + if (! cpm_adap->reloc) + cpm_cp_command(CPM_CP_I2C, CPM_CR_INIT_TRX); + + /* Select an arbitrary address. Just make sure it is unique. + */ + i2c->i2c_i2add = 0xfc; wmb(); + + /* Make clock run at 60 KHz. + */ + brg = (unsigned char) (cpm_adap->intfreq/(32*2*60000) -3); + i2c->i2c_i2brg = brg; + + i2c->i2c_i2mod = I2MOD_FLT; /* Enable filter */ + + /* Disable interrupts. + */ + i2c->i2c_i2cmr = 0; + i2c->i2c_i2cer = 0xff; + + init_waitqueue_head(&cpm_adap->wait); + + /* Install interrupt handler. + */ + DPRINTK("%s[%d] Install ISR for IRQ %d\n", __func__,__LINE__, cpm_adap->irq); + return request_irq(cpm_adap->irq, i2c_cpm_interrupt, 0, "i2c-algo-cpm", cpm_adap); +} + + +static int +i2c_cpm_shutdown(struct i2c_algo_cpm_data *cpm_adap) +{ + volatile cpm_i2c_t *i2c = cpm_adap->i2c; + + /* Shut down IIC. + */ + i2c->i2c_i2mod &= ~I2MOD_EN; + i2c->i2c_i2cmr = 0; + i2c->i2c_i2cer = I2CER_MASK; + free_irq(cpm_adap->irq,cpm_adap); + cpm_dpfree(cpm_adap->dp_offset); + + return(0); +} + +/* + * registering functions to load algorithms at runtime + */ +int i2c_cpm_add_bus(struct i2c_adapter *adap) +{ + struct i2c_algo_cpm_data *cpm_adap = adap->algo_data; + + DPRINTK("i2c-algo-cpm: hw routines for %s registered.\n", adap->name); + + /* register new adapter to i2c module... */ + + adap->id |= cpm_algo.id; + adap->algo = &cpm_algo; + + i2c_add_adapter(adap); + i2c_cpm_init(cpm_adap); + + return 0; +} + +int i2c_cpm_del_bus(struct i2c_adapter *adap) +{ + int res; + struct i2c_algo_cpm_data *cpm_adap = adap->algo_data; + + i2c_cpm_shutdown(cpm_adap); + + if ((res = i2c_del_adapter(adap)) < 0) + return res; + + printk("i2c-algo-cpm: adapter unregistered: %s\n",adap->name); + + return 0; +} + +MODULE_LICENSE("GPL"); +EXPORT_SYMBOL(i2c_cpm_add_bus); +EXPORT_SYMBOL(i2c_cpm_del_bus); + +int __init i2c_algo_cpm_init (void) +{ + printk("i2c-algo-cpm: CPM2 I2C algorithm module version %s (%s)\n", I2C_VERSION, I2C_DATE); + return 0; +} + + +MODULE_AUTHOR("Jason McMullan "); +MODULE_DESCRIPTION("I2C-Bus CPM2 algorithm"); + +static void i2c_algo_cpm_exit(void) +{ +} + +module_init(i2c_algo_cpm_init); +module_exit(i2c_algo_cpm_exit); --- linux-orig/drivers/i2c/busses/Kconfig +++ linux/drivers/i2c/busses/Kconfig @@ -229,6 +229,21 @@ This driver can also be built as a module. If so, the module will be called i2c-mpc. +config I2C_CPM + tristate "Motorola MPC8xx and MPC85xx CPM I2C" + depends on (8xx || 85xx) && I2C + help + This supports the use of the I2C interface in the Motorola + MPC8xx and MPC85xx series embedded processors on the CPM + I2C macrocell. + +config I2C_CPM_BIT + bool "Bit-Bang only?" + depends on I2C_CPM + help + Only use the CPM parallel port bit-banging interface, not the + SDMA CPM device. + config I2C_NFORCE2 tristate "Nvidia Nforce2" depends on I2C && PCI && EXPERIMENTAL @@ -317,10 +332,12 @@ This support is also available as a module. If so, the module will be called i2c-prosavage. -config I2C_RPXLITE - tristate "Embedded Planet RPX Lite/Classic support" - depends on (RPXLITE || RPXCLASSIC) && I2C - select I2C_ALGO8XX +# This appears to be broken... +# +#config I2C_RPXLITE +# tristate "Embedded Planet RPX Lite/Classic support" +# depends on (RPXLITE || RPXCLASSIC) && I2C +# select I2C_ALGO8XX config I2C_S3C2410 tristate "S3C2410 I2C Driver" --- linux-orig/drivers/i2c/busses/Makefile +++ linux/drivers/i2c/busses/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_I2C_AMD756) += i2c-amd756.o obj-$(CONFIG_I2C_AMD756_S4882) += i2c-amd756-s4882.o obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o +obj-$(CONFIG_I2C_CPM) += i2c-cpm.o obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o obj-$(CONFIG_I2C_HYDRA) += i2c-hydra.o --- /dev/null +++ linux/drivers/i2c/busses/i2c-cpm.c @@ -0,0 +1,215 @@ +/* + * CPM I2C interface. + * Copyright (c) 2004 Jason McMullan (jason.mcmullan@timesys.com) + * Copyright (c) 1999 Dan Malek (dmalek@jlc.net). + * + * On-Chip Peripheral version of the Motorola CPM I2C macrocell driver, + * used on the MPC8xx and MPC85xx series processors. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#ifdef CONFIG_CPM2 +#include +#else +#include +#endif + +#ifdef CONFIG_I2C_CPM_BIT +#include + +#define CYCLE_DELAY 10 +#define TIMEOUT (HZ / 2) +#endif /* CONFIG_I2C_CPM_BIT */ + +#ifdef CONFIG_CPM2 +/* SDA and SCL bits on port D */ +#define BIT_SDA (1<<16) +#define BIT_SCL (1<<17) +#endif /* CONFIG_CPM2 */ + +#ifdef CONFIG_I2C_CPM_BIT +static void bit_cpm_setsda(void *data, int state) +{ + if (state) + cpm2_immr->im_ioport.iop_pdatd |= BIT_SDA; + else + cpm2_immr->im_ioport.iop_pdatd &= ~BIT_SDA; +} + +static void bit_cpm_setscl(void *data, int state) +{ + if (state) + cpm2_immr->im_ioport.iop_pdatd |= BIT_SCL; + else + cpm2_immr->im_ioport.iop_pdatd &= ~BIT_SCL; +} + +static int bit_cpm_getsda(void *data) +{ + return (cpm2_immr->im_ioport.iop_pdatd & BIT_SDA) ? 1 : 0; +} + +static int bit_cpm_getscl(void *data) +{ + return (cpm2_immr->im_ioport.iop_pdatd & BIT_SCL) ? 1 : 0; +} + + +static struct i2c_algo_bit_data cpm_data = { + .setsda = bit_cpm_setsda, + .setscl = bit_cpm_setscl, + .getsda = bit_cpm_getsda, + .getscl = bit_cpm_getscl, + .udelay = CYCLE_DELAY, + .mdelay = CYCLE_DELAY, + .timeout = TIMEOUT +}; +#else /* !CONFIG_I2C_CPM_BIT */ +#include + +static struct i2c_algo_cpm_data cpm_data = { .irq = -1 }; +#endif /* !CONFIG_I2C_CPM_BIT */ + +static struct i2c_adapter cpm_ops = { + .owner = THIS_MODULE, + .name = "cpm-i2c", + .id = I2C_HW_CPM2, + .algo_data = &cpm_data, +}; + +#ifdef CONFIG_I2C_CPM_BIT +static int __devinit cpm_i2c_probe_bitbang(struct device *dev) +{ +#ifdef CONFIG_CPM2 + if (cpm2_immr->im_ioport.iop_ppard & (BIT_SCL | BIT_SDA)) { + printk(KERN_INFO "%s: CPM I/O Port D is not configured for I2C bit-bang mode.\n", pdev->bus_id); + return -ENODEV; + } + + /* Configure as open drain... */ + cpm2_immr->im_ioport.iop_podrd |= (BIT_SCL | BIT_SDA); + + /* Inputs... */ + cpm2_immr->im_ioport.iop_pdird &= ~(BIT_SCL | BIT_SDA); +#endif + + return i2c_bit_add_bus(&cpm_ops); +} +#else +static int __devinit cpm_i2c_probe_cpm(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct resource *res; + int err; + struct i2c_algo_cpm_data *data = &cpm_data; + bd_t *binfo = (bd_t *)__res; + + if (data->irq != -1) + return -EBUSY; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) + return -EINVAL; + + data->i2c = (cpm_i2c_t *)ioremap(res->start,sizeof(cpm_i2c_t)); + if (data->i2c == NULL) { + iounmap((void *)data->iip); + return -ENOMEM; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res == NULL) + return -EINVAL; + + data->iip = (iic_t *)ioremap(res->start,sizeof(iic_t)); + if (data->iip == NULL) { + return -ENOMEM; + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (res == NULL) + return -EINVAL; + +#ifdef CONFIG_CPM2 + /* Configure as peripheral... */ + cpm2_immr->im_ioport.iop_ppard |= (BIT_SCL | BIT_SDA); + cpm2_immr->im_ioport.iop_psord |= (BIT_SCL | BIT_SDA); + + /* Inputs... */ + cpm2_immr->im_ioport.iop_pdird &= ~(BIT_SCL | BIT_SDA); +#endif + + data->irq = res->start; + + data->device = dev; + data->intfreq = binfo->bi_intfreq; + + err = i2c_cpm_add_bus(&cpm_ops); + if (err < 0) { + data->irq=-1; + iounmap((void *)data->iip); + iounmap((void *)data->i2c); + return err; + } + + return 0; +} +#endif + +#ifdef CONFIG_I2C_CPM_BIT +static void __devexit cpm_i2c_remove_bitbang(struct device *dev) +{ + i2c_bit_del_bus(&cpm_ops); +} +#else +static void __devexit cpm_i2c_remove_cpm(struct device *dev) +{ + struct i2c_algo_cpm_data *data = &cpm_data; + + i2c_cpm_del_bus(&cpm_ops); + + iounmap((void *)data->iip); + iounmap((void *)data->i2c); +} +#endif + +static struct device_driver cpm_i2c_driver = { + .name = "fsl-cpm-i2c", + .bus = &platform_bus_type, +#ifdef CONFIG_I2C_CPM_BIT + .probe = cpm_i2c_probe_bitbang, + .remove = __devexit_p(cpm_i2c_remove_bitbang), +#else + .probe = cpm_i2c_probe_cpm, + .remove = __devexit_p(cpm_i2c_remove_cpm), +#endif +}; + +int __init cpm_i2c_init(void) +{ + printk(KERN_INFO "%s: CPM2 i2c driver\n",cpm_i2c_driver.name); + + return driver_register(&cpm_i2c_driver); +} + +void __exit cpm_i2c_exit(void) +{ + driver_unregister(&cpm_i2c_driver); +} + +MODULE_LICENSE("GPL"); + +MODULE_AUTHOR("Jason McMullan "); +MODULE_DESCRIPTION("I2C-Bus (on CPM) adapter routines for MPC8xx and MPC85xx boards"); + +module_init(cpm_i2c_init); +module_exit(cpm_i2c_exit); --- /dev/null +++ linux/include/linux/i2c-algo-cpm.h @@ -0,0 +1,64 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-algo-mpc.h i2c driver algorithms for MPC CPMs */ +/* ------------------------------------------------------------------------- */ + +/* $Id$ */ + +#ifndef I2C_ALGO_MPC_H +#define I2C_ALGO_MPC_H 1 + +#include + +#ifdef CONFIG_85xx +#include +#include +#include + +typedef i2c_cpm2_t cpm_i2c_t; +typedef iic_t cpm_iic_t; + +#elif defined(CONFIG_8xx) +#include +#include + +typedef i2c8xx_t cpm_i2c_t; +typedef iic_t cpm_iic_t; +#endif + +#define I2MOD_EN 0x01 +#define I2MOD_PDIV_MASK 0x06 +#define I2MOD_PDIV_4 0x06 +#define I2MOD_PDIV_8 0x04 +#define I2MOD_PDIV_16 0x02 +#define I2MOD_PDIV_32 0x00 +#define I2MOD_FLT 0x08 +#define I2MOD_GCD 0x10 +#define I2MOD_REVD 0x20 + +#define I2COM_MS 0x01 +#define I2COM_STR 0x80 + +#define I2CER_MASK 0x17 +#define I2CER_TXE 0x10 +#define I2CER_BSY 0x04 +#define I2CER_TXB 0x02 +#define I2CER_RXB 0x01 + +struct i2c_algo_cpm_data { + struct device *device; + dma_addr_t dp_offset; + int reloc; + volatile cpm_i2c_t *i2c; + volatile cpm_iic_t *iip; + int irq; + int intfreq; + uint8_t *temp; + + wait_queue_head_t wait; + int status; +}; + +int i2c_cpm_add_bus(struct i2c_adapter *); +int i2c_cpm_del_bus(struct i2c_adapter *); + +#endif /* I2C_ALGO_CPM_H */ --- linux-orig/include/linux/i2c-id.h +++ linux/include/linux/i2c-id.h @@ -201,6 +201,7 @@ #define I2C_ALGO_SIBYTE 0x150000 /* Broadcom SiByte SOCs */ #define I2C_ALGO_SGI 0x160000 /* SGI algorithm */ #define I2C_ALGO_AU1550 0x170000 /* Au1550 PSC algorithm */ +#define I2C_ALGO_CPM2 0x180000 /* MPC8xxx and MPC8xx CPM I2C */ #define I2C_ALGO_EXP 0x800000 /* experimental */ @@ -253,6 +254,9 @@ /* --- ACPI Embedded controller algorithms */ #define I2C_HW_ACPI_EC 0x00 +/* --- Motorola MPC CPM2 I2C adapters -- */ +#define I2C_HW_CPM2 0x00 + /* --- MPC824x PowerPC adapters */ #define I2C_HW_MPC824X 0x00 /* Motorola 8240 / 8245 */