I'm working to adapt some work by Jochen Friedrich to support CPM2 i2c devices. It appears I have the bus loaded and think I am configuring it properly, but my results tell me different. I see no messages when I attach a i2c monitor after u-boot loads.
Can anyone spot what is going wrong based on the following information and the resulting output? Does this look proper for a successful configuration of the cpm2 i2c bus? i2c-cpm: iip e0008afc, dp_addr 0x240 i2c-cpm: iic_tbase 576, iic_rbase 608 Any insight would be greatly appreciated. -Alan My Device tree entry: [EMAIL PROTECTED] { compatible = "fsl,mpc8248-i2c", "fsl,cpm2-i2c", "fsl,cpm-i2c"; reg = <11860 20 8afc 30>; interrupts = <1 8>; interrupt-parent = <&PIC>; fsl,cpm-command = <29600000>; }; ... and then later: [EMAIL PROTECTED] { #address-cells = <1>; #size-cells = <1>; ranges = <0 0 10000>; [EMAIL PROTECTED] { compatible = "fsl,cpm-muram-data"; reg = <80 1f80 9800 800>; }; }; dump of 0xe0008afc (my immr_base=0xe0000000) at boot e0008afc : 02600240 10100201 00000000 00000000 e0008b0c : 02600000 00000000 00000000 00000000 e0008b1c : 02400000 00000000 78c7ebaf bdefeab1 Probe debug results: i2c-cpm: cpm_iic_init() i2c-cpm: iip e0008afc, dp_addr 0x240 i2c-cpm: iic_tbase 576, iic_rbase 608 Log after modprobe: modprobe i2c-dev Jan 1 00:08:02 192 kernel: i2c /dev entries driver Jan 1 00:08:02 192 kernel: i2c-core: driver [dev_driver] registered Jan 1 00:08:02 192 kernel: i2c-dev: adapter [i2c-cpm] registered as minor 0 Log entries after trying an i2cset: (NOTE: sometimes it hangs doing this) i2cset 0 0x41 0x1 0xff b cpm_xfer:478 cpm_parse_message:329 kernel: i2c-adapter i2c-0: ioctl, cmd=0x720, arg=0xbfecaa0c kernel: i2c-adapter i2c-0: master_xfer[0] W, addr=0x41, len2 kernel: cpm_xfer:478 kernel: i2c-adapter i2c-0: i2c-algo-cpm.o: R: 0 T: 0 kernel: cpm_parse_message:329 kernel: i2c-adapter i2c-0: cpm_iic_write(abyte=0x82) kernel: i2c-adapter i2c-0: tx sc 0 bc00 kernel: i2c-adapter i2c-0: test ready. kernel: i2c-adapter i2c-0: not ready. cpm_xfer:478 cpm_parse_message:329 cpm_parse_message:329 kernel: i2c-adapter i2c-0: force_close() kernel: i2c-adapter i2c-0: IIC read: timeout! kernel: i2c-adapter i2c-0: ioctl, cmd=0x720, arg=0xbfecaa0c kernel: i2c-adapter i2c-0: master_xfer[0] W, addr=0x41, len1 kernel: i2c-adapter i2c-0: master_xfer[1] R, addr=0x41, len1 kernel: cpm_xfer:478 kernel: i2c-adapter i2c-0: i2c-algo-cpm.o: R: 0 T: 0 kernel: cpm_parse_message:329 kernel: i2c-adapter i2c-0: cpm_iic_write(abyte=0x82) kernel: i2c-adapter i2c-0: tx sc 0 9400 kernel: i2c-adapter i2c-0: i2c-algo-cpm.o: R: 0 T: 1 kernel: cpm_parse_message:329 kernel: i2c-adapter i2c-0: cpm_iic_read(abyte=0x83) kernel: i2c-adapter i2c-0: test ready. kernel: i2c-adapter i2c-0: not ready. kernel: i2c-adapter i2c-0: force_close() kernel: i2c-adapter i2c-0: IIC read: timeout!
/* * Freescale CPM1/CPM2 I2C interface. * Copyright (c) 1999 Dan Malek ([EMAIL PROTECTED]). * * moved into proper i2c interface; * Brad Parker ([EMAIL PROTECTED]) * * (C) 2007 Montavista Software, Inc. * Vitaly Bordug <[EMAIL PROTECTED]> * * RPX lite specific parts of the i2c interface * Update: There actually isn't anything RPXLite-specific about this module. * This should work for most any CPM board. The console messages have been * changed to eliminate RPXLite references. * 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-8xx.c * Brad Parker ([EMAIL PROTECTED]) * * Parts from dbox2_i2c.c (cvs.tuxbox.org) * (C) 2000-2001 Tmbinc, Gillem ([EMAIL PROTECTED]) * * (C) 2007 Montavista Software, Inc. * Vitaly Bordug <[EMAIL PROTECTED]> * * Converted to of_platform_device. Renamed to i2c-cpm.c. * (C) 2007 Jochen Friedrich <[EMAIL PROTECTED]> */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/delay.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/stddef.h> #include <linux/i2c.h> #include <linux/io.h> #include <linux/time.h> #include <linux/dma-mapping.h> #include <linux/of_device.h> #include <linux/of_platform.h> #include <sysdev/fsl_soc.h> #ifdef CONFIG_CPM2 #include <asm/cpm2.h> #else #include <asm/commproc.h> #endif /* Try to define this if you have an older CPU (earlier than rev D4) */ /* However, better use a GPIO based bitbang driver in this case :/ */ #undef I2C_CHIP_ERRATA #define CPM_MAX_READ 513 #define CPM_MAXBD 4 struct cpm_i2c { char *base; struct of_device *ofdev; struct i2c_adapter adap; uint dp_addr; int reloc; int irq; int cp_command; #ifdef CONFIG_CPM2 i2c_cpm2_t __iomem *i2c; #else i2c8xx_t __iomem *i2c; #endif iic_t __iomem *iip; wait_queue_head_t iic_wait; struct mutex iic_mutex; /* Protects I2C CPM */ u_char *txbuf[CPM_MAXBD]; u_char *rxbuf[CPM_MAXBD]; u32 txdma[CPM_MAXBD]; u32 rxdma[CPM_MAXBD]; }; static irqreturn_t cpm_iic_interrupt(int irq, void *dev_id) { struct i2c_adapter *adap; struct cpm_i2c *cpm; #ifdef CONFIG_CPM2 i2c_cpm2_t __iomem *i2c; #else i2c8xx_t __iomem *i2c; #endif int i; printk ("%s:%d\n", __FUNCTION__,__LINE__); adap = (struct i2c_adapter *) dev_id; cpm = i2c_get_adapdata(adap); i2c = cpm->i2c; /* Clear interrupt. */ i = in_8(&i2c->i2c_i2cer); out_8(&i2c->i2c_i2cer, i); dev_dbg(&adap->dev, "Interrupt: %x\n", i); /* Get 'me going again. */ wake_up_interruptible(&cpm->iic_wait); return i ? IRQ_HANDLED : IRQ_NONE; } static void cpm_reset_iic_params(struct cpm_i2c *cpm) { iic_t __iomem *iip = cpm->iip; /* Set up the IIC parameters in the parameter ram. */ out_be16(&iip->iic_tbase, cpm->dp_addr); out_be16(&iip->iic_rbase, cpm->dp_addr + sizeof(cbd_t) * CPM_MAXBD); #ifdef CONFIG_CPM2 out_8(&iip->iic_tfcr, CPMFCR_EB); out_8(&iip->iic_rfcr, CPMFCR_EB); #else out_8(&iip->iic_tfcr, SMC_EB); out_8(&iip->iic_rfcr, SMC_EB); #endif out_be16(&iip->iic_mrblr, CPM_MAX_READ); out_be32(&iip->iic_rstate, 0); out_be32(&iip->iic_rdp, 0); out_be16(&iip->iic_rbptr, in_be16(&iip->iic_rbase)); out_be16(&iip->iic_rbc, 0); out_be32(&iip->iic_rxtmp, 0); out_be32(&iip->iic_tstate, 0); out_be32(&iip->iic_tdp, 0); out_be16(&iip->iic_tbptr, in_be16(&iip->iic_tbase)); out_be16(&iip->iic_tbc, 0); out_be32(&iip->iic_txtmp, 0); } static int cpm_iic_init(struct i2c_adapter *adap) { struct cpm_i2c *cpm = i2c_get_adapdata(adap); iic_t __iomem *iip = cpm->iip; #ifdef CONFIG_CPM2 i2c_cpm2_t __iomem *i2c = cpm->i2c; #else i2c8xx_t __iomem *i2c = cpm->i2c; #endif unsigned char brg; int ret, i, res; #ifdef CONFIG_CPM2 u32 v; #else u16 v; #endif printk("i2c-cpm: cpm_iic_init()\n"); ret = 0; init_waitqueue_head(&cpm->iic_wait); mutex_init(&cpm->iic_mutex); /* Initialize Tx/Rx parameters. */ cpm_reset_iic_params(cpm); printk("i2c-cpm: iip %p, dp_addr 0x%x\n", cpm->iip, cpm->dp_addr); printk("i2c-cpm: iic_tbase %d, iic_rbase %d\n", in_be16(&iip->iic_tbase), in_be16(&iip->iic_rbase)); v = cpm->cp_command | (CPM_CR_INIT_TRX << 8) | CPM_CR_FLG; #ifdef CONFIG_CPM2 out_be32(&cpmp->cp_cpcr, v); res = wait_event_timeout(cpm->iic_wait, !(in_be32(&cpmp->cp_cpcr) & CPM_CR_FLG), HZ * 10); #else out_be16(&cpmp->cp_cpcr, v); res = wait_event_timeout(cpm->iic_wait, !(in_be16(&cpmp->cp_cpcr) & CPM_CR_FLG), HZ * 10); #endif if (!res) return -EIO; /* Select an invalid address. Just make sure we don't use loopback mode */ out_8(&i2c->i2c_i2add, 0xfe); /* Make clock run at 60 kHz. */ brg = get_brgfreq() / (32 * 2 * 60000) - 3; out_8(&i2c->i2c_i2brg, brg); out_8(&i2c->i2c_i2mod, 0x00); out_8(&i2c->i2c_i2com, 0x01); /* Master mode */ /* Disable interrupts. */ out_8(&i2c->i2c_i2cmr, 0); out_8(&i2c->i2c_i2cer, 0xff); /* Allocate TX and RX buffers */ for (i = 0; i < CPM_MAXBD; i++) { cpm->rxbuf[i] = dma_alloc_coherent( NULL, CPM_MAX_READ + 1, &cpm->rxdma[i], GFP_KERNEL); if (!cpm->rxbuf[i]) { ret = -ENOMEM; goto out; } cpm->txbuf[i] = (unsigned char *)dma_alloc_coherent( NULL, CPM_MAX_READ + 1, &cpm->txdma[i], GFP_KERNEL); if (!cpm->txbuf[i]) { ret = -ENOMEM; goto out; } } /* Install interrupt handler. */ ret = request_irq(cpm->irq, cpm_iic_interrupt, 0, "cpm_i2c", adap); if (ret) goto out; return 0; out: for (i = 0; i < CPM_MAXBD; i++) { if (cpm->rxbuf[i]) dma_free_coherent(NULL, CPM_MAX_READ + 1, cpm->rxbuf[i], cpm->rxdma[i]); if (cpm->txbuf[i]) dma_free_coherent(NULL, CPM_MAX_READ + 1, cpm->txbuf[i], cpm->txdma[i]); } return ret; } static int cpm_iic_shutdown(struct i2c_adapter *adap) { struct cpm_i2c *cpm = i2c_get_adapdata(adap); int i; #ifdef CONFIG_CPM2 i2c_cpm2_t __iomem *i2c = cpm->i2c; #else i2c8xx_t __iomem *i2c = cpm->i2c; #endif /* Shut down IIC. */ out_8(&i2c->i2c_i2mod, in_8(&i2c->i2c_i2mod) | ~1); out_8(&i2c->i2c_i2cmr, 0); out_8(&i2c->i2c_i2cer, 0xff); for (i = 0; i < CPM_MAXBD; i++) { if (cpm->rxbuf[i]) dma_free_coherent(NULL, CPM_MAX_READ + 1, cpm->rxbuf[i], cpm->rxdma[i]); if (cpm->txbuf[i]) dma_free_coherent(NULL, CPM_MAX_READ + 1, cpm->txbuf[i], cpm->txdma[i]); } free_irq(cpm->irq, adap); return 0; } #define BD_SC_NAK (0x0004) /* NAK - did not respond */ #define BD_SC_OV (0x0002) /* OV - receive overrun */ #define CPM_CR_CLOSE_RXBD (0x0007) static void force_close(struct i2c_adapter *adap) { struct cpm_i2c *cpm = i2c_get_adapdata(adap); #ifdef CONFIG_CPM2 i2c_cpm2_t __iomem *i2c = cpm->i2c; u32 v; #else i2c8xx_t __iomem *i2c = cpm->i2c; u16 v; #endif dev_dbg(&adap->dev, "force_close()\n"); v = cpm->cp_command | (CPM_CR_CLOSE_RXBD << 8) | CPM_CR_FLG; #ifdef CONFIG_CPM2 out_be32(&cpmp->cp_cpcr, v); wait_event_timeout(cpm->iic_wait, !(in_be32(&cpmp->cp_cpcr) & CPM_CR_FLG), HZ * 5); #else out_be16(&cpmp->cp_cpcr, v); wait_event_timeout(cpm->iic_wait, !(in_be16(&cpmp->cp_cpcr) & CPM_CR_FLG), HZ * 5); #endif out_8(&i2c->i2c_i2cmr, 0x00); /* Disable all interrupts */ out_8(&i2c->i2c_i2cer, 0xff); } static void cpm_parse_message(struct i2c_adapter *adap, struct i2c_msg *pmsg, int num, int tx, int rx) { cbd_t *tbdf, *rbdf; u_char addr; u_char *tb; u_char *rb; struct cpm_i2c *cpm = i2c_get_adapdata(adap); iic_t __iomem *iip = cpm->iip; int i, dscan; printk ("%s:%d\n", __FUNCTION__,__LINE__); tbdf = (cbd_t *) cpm_muram_addr(in_be16(&iip->iic_tbase)); rbdf = (cbd_t *) cpm_muram_addr(in_be16(&iip->iic_rbase)); /* This chip can't do zero length writes. However, the i2c core uses them to scan for devices. The best we can do is to convert them into 1 byte reads */ dscan = ((pmsg->len == 0) && (num == 1)); addr = pmsg->addr << 1; if ((pmsg->flags & I2C_M_RD) || dscan) addr |= 1; tb = cpm->txbuf[tx]; rb = cpm->rxbuf[rx]; /* Align read buffer */ rb = (u_char *) (((ulong) rb + 1) & ~1); if ((pmsg->flags & I2C_M_RD) || dscan) { /* 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[0] = addr; /* Device address byte w/rw flag */ dev_dbg(&adap->dev, "cpm_iic_read(abyte=0x%x)\n", addr); tbdf[tx].cbd_bufaddr = cpm->txdma[tx]; if (dscan) tbdf[tx].cbd_datlen = 2; else tbdf[tx].cbd_datlen = pmsg->len + 1; tbdf[tx].cbd_sc = 0; if (!(pmsg->flags & I2C_M_NOSTART)) tbdf[tx].cbd_sc |= BD_IIC_START; if (tx + 1 == num) tbdf[tx].cbd_sc |= BD_SC_LAST | BD_SC_WRAP; rbdf[rx].cbd_datlen = 0; rbdf[rx].cbd_bufaddr = ((cpm->rxdma[rx] + 1) & ~1); rbdf[rx].cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT; if (rx + 1 == CPM_MAXBD) tbdf[rx].cbd_sc |= BD_SC_WRAP; eieio(); tbdf[tx].cbd_sc |= BD_SC_READY; } else { tb[0] = addr; /* Device address byte w/rw flag */ for (i = 0; i < pmsg->len; i++) tb[i+1] = pmsg->buf[i]; dev_dbg(&adap->dev, "cpm_iic_write(abyte=0x%x)\n", addr); tbdf[tx].cbd_bufaddr = cpm->txdma[tx]; tbdf[tx].cbd_datlen = pmsg->len + 1; tbdf[tx].cbd_sc = 0; if (!(pmsg->flags & I2C_M_NOSTART)) tbdf[tx].cbd_sc |= BD_IIC_START; if (tx + 1 == num) tbdf[tx].cbd_sc |= BD_SC_LAST | BD_SC_WRAP; eieio(); tbdf[tx].cbd_sc |= BD_SC_READY | BD_SC_INTRPT; dev_dbg(&adap->dev, "tx sc %d %04x\n", tx, tbdf[tx].cbd_sc); } } static int cpm_check_message(struct i2c_adapter *adap, struct i2c_msg *pmsg, int tx, int rx) { cbd_t *tbdf, *rbdf; u_char *tb; u_char *rb; struct cpm_i2c *cpm = i2c_get_adapdata(adap); iic_t __iomem *iip = cpm->iip; int i; printk ("%s:%d\n", __FUNCTION__,__LINE__); tbdf = (cbd_t *) cpm_muram_addr(in_be16(&iip->iic_tbase)); rbdf = (cbd_t *) cpm_muram_addr(in_be16(&iip->iic_rbase)); tb = cpm->txbuf[tx]; rb = cpm->rxbuf[rx]; /* Align read buffer */ rb = (u_char *) (((uint) rb + 1) & ~1); if (pmsg->flags & I2C_M_RD) { dev_dbg(&adap->dev, "rx sc %04x, rx sc %04x\n", tbdf[tx].cbd_sc, rbdf[rx].cbd_sc); if (tbdf[tx].cbd_sc & BD_SC_NAK) { dev_dbg(&adap->dev, "IIC read; no ack\n"); if (pmsg->flags & I2C_M_IGNORE_NAK) return 0; else return -EIO; } if (rbdf[rx].cbd_sc & BD_SC_EMPTY) { dev_dbg(&adap->dev, "IIC read; complete but rbuf empty\n"); return -EREMOTEIO; } if (rbdf[rx].cbd_sc & BD_SC_OV) { dev_dbg(&adap->dev, "IIC read; Overrun\n"); return -EREMOTEIO; } for (i = 0; i < pmsg->len; i++) pmsg->buf[i] = rb[i]; } else { dev_dbg(&adap->dev, "tx sc %d %04x\n", tx, tbdf[tx].cbd_sc); if (tbdf[tx].cbd_sc & BD_SC_NAK) { dev_dbg(&adap->dev, "IIC write; no ack\n"); if (pmsg->flags & I2C_M_IGNORE_NAK) return 0; else return -EIO; } } return 0; } static int cpm_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { struct cpm_i2c *cpm = i2c_get_adapdata(adap); #ifdef CONFIG_CPM2 i2c_cpm2_t __iomem *i2c = cpm->i2c; #else i2c8xx_t __iomem *i2c = cpm->i2c; #endif iic_t __iomem *iip = cpm->iip; struct i2c_msg *pmsg, *rmsg; int ret, i; int tptr; int rptr; cbd_t *tbdf, *rbdf; printk ("%s:%d\n", __FUNCTION__,__LINE__); if (num > CPM_MAXBD) return -EINVAL; /* Check if we have any oversized READ requests */ for (i = 0; i < num; i++) { pmsg = &msgs[i]; if (pmsg->len >= CPM_MAX_READ) return -EINVAL; } mutex_lock(&cpm->iic_mutex); /* Reset to use first buffer */ out_be16(&iip->iic_rbptr, in_be16(&iip->iic_rbase)); out_be16(&iip->iic_tbptr, in_be16(&iip->iic_tbase)); tbdf = (cbd_t *) cpm_muram_addr(in_be16(&iip->iic_tbase)); rbdf = (cbd_t *) cpm_muram_addr(in_be16(&iip->iic_rbase)); tptr = 0; rptr = 0; while (tptr < num) { pmsg = &msgs[tptr]; dev_dbg(&adap->dev, "i2c-algo-cpm.o: " "R: %d T: %d\n", rptr, tptr); cpm_parse_message(adap, pmsg, num, tptr, rptr); if (pmsg->flags & I2C_M_RD) rptr++; tptr++; } /* Start transfer now */ /* Chip bug, set enable here */ out_8(&i2c->i2c_i2cmr, 0x13); /* Enable some interupts */ out_8(&i2c->i2c_i2cer, 0xff); out_8(&i2c->i2c_i2mod, in_8(&i2c->i2c_i2mod) | 1); /* Enable */ /* Begin transmission */ out_8(&i2c->i2c_i2com, in_8(&i2c->i2c_i2com) | 0x80); tptr = 0; rptr = 0; while (tptr < num) { /* Check for outstanding messages */ dev_dbg(&adap->dev, "test ready.\n"); if (!(tbdf[tptr].cbd_sc & BD_SC_READY)) { dev_dbg(&adap->dev, "ready.\n"); rmsg = &msgs[tptr]; ret = cpm_check_message(adap, rmsg, tptr, rptr); tptr++; if (rmsg->flags & I2C_M_RD) rptr++; if (ret) { force_close(adap); mutex_unlock(&cpm->iic_mutex); return ret; } } else { dev_dbg(&adap->dev, "not ready.\n"); ret = wait_event_interruptible_timeout(cpm->iic_wait, !(tbdf[tptr].cbd_sc & BD_SC_READY), 1 * HZ); if (ret == 0) { force_close(adap); dev_dbg(&adap->dev, "IIC read: timeout!\n"); mutex_unlock(&cpm->iic_mutex); return -EREMOTEIO; } } } #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); out_8(&i2c->i2c_i2mod, in_8(&i2c->i2c_i2mod) | ~1); #endif mutex_unlock(&cpm->iic_mutex); return (num); } static u32 cpm_func(struct i2c_adapter *adap) { return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } /* -----exported algorithm data: ------------------------------------- */ static struct i2c_algorithm cpm_algo = { .master_xfer = cpm_xfer, .functionality = cpm_func, }; /* * registering functions to load algorithms at runtime */ int i2c_cpm_add_bus(struct i2c_adapter *adap) { int res; printk("i2c-cpm: hw routines for %s registered.\n", adap->name); /* register new adapter to i2c module... */ adap->algo = &cpm_algo; res = cpm_iic_init(adap); if (res) return res; return i2c_add_adapter(adap); } int i2c_cpm_del_bus(struct i2c_adapter *adap) { i2c_del_adapter(adap); return cpm_iic_shutdown(adap); } static const struct i2c_adapter cpm_ops = { .owner = THIS_MODULE, .name = "i2c-cpm", .id = I2C_HW_MPC8XX_EPON, .class = I2C_CLASS_HWMON, }; static int i2c_cpm_setup(struct cpm_i2c *i2c) { struct of_device *ofdev = i2c->ofdev; const u32 *data; int len; /* Pointer to Communication Processor */ i2c->irq = of_irq_to_resource(ofdev->node, 0, NULL); if (i2c->irq == NO_IRQ) return -EINVAL; i2c->iip = of_iomap(ofdev->node, 1); if (i2c->iip == NULL) return -EINVAL; #ifndef CONFIG_CPM2 /* Check for and use a microcode relocation patch. */ if (of_device_is_compatible(ofdev->node, "fsl,i2c-cpm1")) i2c->reloc = i2c->iip->iic_rpbase; /* Maybe should use ioremap instead? */ if (i2c->reloc) { iounmap(i2c->iip); i2c->iip = cpm_muram_addr(i2c->iip->iic_rpbase); } #endif i2c->i2c = of_iomap(ofdev->node, 0); if (i2c->i2c == NULL) return -EINVAL; /* Allocate space for two transmit and two receive buffer * descriptors in the DP ram. */ i2c->dp_addr = cpm_muram_alloc(sizeof(cbd_t) * 4, 8); if (!i2c->dp_addr) return -ENOMEM; data = of_get_property(ofdev->node, "fsl,cpm-command", &len); if (!data || len != 4) return -EINVAL; i2c->cp_command = *data; return 0; } static void i2c_cpm_release(struct cpm_i2c *i2c) { if (i2c->dp_addr) cpm_muram_free(i2c->dp_addr); if (i2c->i2c) iounmap(i2c->i2c); if ((i2c->iip) && (!i2c->reloc)) iounmap(i2c->iip); return; } static int i2c_cpm_probe(struct of_device *ofdev, const struct of_device_id *match) { int result; struct cpm_i2c *i2c; i2c = kzalloc(sizeof(struct cpm_i2c), GFP_KERNEL); if (!i2c) return -ENOMEM; i2c->ofdev = ofdev; result = i2c_cpm_setup(i2c); if (result) { printk(KERN_ERR "i2c-cpm: Unable to register resources\n"); goto out; } dev_set_drvdata(&ofdev->dev, i2c); i2c->adap = cpm_ops; i2c_set_adapdata(&i2c->adap, i2c); i2c->adap.dev.parent = &ofdev->dev; result = i2c_cpm_add_bus(&i2c->adap); if (result < 0) { printk(KERN_ERR "i2c-cpm: Unable to register with I2C\n"); goto out; } return 0; out: i2c_cpm_release(i2c); kfree(i2c); return result; } static int i2c_cpm_remove(struct of_device *ofdev) { struct cpm_i2c *i2c = dev_get_drvdata(&ofdev->dev); i2c_cpm_del_bus(&i2c->adap); dev_set_drvdata(&ofdev->dev, NULL); i2c_cpm_release(i2c); kfree(i2c); return 0; } static struct of_device_id i2c_cpm_match[] = { { .compatible = "fsl,cpm-i2c", }, {}, }; MODULE_DEVICE_TABLE(of, i2c_cpm_match); static struct of_platform_driver i2c_cpm_driver = { .name = "fsl-i2c-cpm", .match_table = i2c_cpm_match, .probe = i2c_cpm_probe, .remove = i2c_cpm_remove, }; static int __init i2c_cpm_init(void) { return of_register_platform_driver(&i2c_cpm_driver); } static void __exit i2c_cpm_exit(void) { of_unregister_platform_driver(&i2c_cpm_driver); } module_init(i2c_cpm_init); module_exit(i2c_cpm_exit); MODULE_AUTHOR("Dan Malek <[EMAIL PROTECTED]>"); MODULE_DESCRIPTION("I2C-Bus adapter routines for CPM boards"); MODULE_LICENSE("GPL");
_______________________________________________ Linuxppc-dev mailing list Linuxppc-dev@ozlabs.org https://ozlabs.org/mailman/listinfo/linuxppc-dev