i2c: adds support for i2c bus on Freescale CPM1/CPM2 controllers
Subsystems (2)
Jochen Friedrich
maintainerThis driver uses the port of 2.4 code from Vitaly Bordug <[email protected]> and the actual algorithm used by the i2c driver of the DBox code on cvs.tuxboc.org from Felix Domke ([email protected]) and Gillem ([email protected]) converted to an of_platform_driver. Tested on CPM1 (MPC823 on dbox2 hardware) and CPM2 (MPC8272).
--- drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-cpm.c | 728 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 739 insertions(+), 0 deletions(-) create mode 100644 drivers/i2c/busses/i2c-cpm.c
drivers/i2c/busses/Kconfig +10 -0
@@ -114,6 +114,16 @@ config I2C_BLACKFIN_TWI_CLK_KHZ help The unit of the TWI clock is kHz. +config I2C_CPM + tristate "Freescale CPM1 or CPM2 (MPC8xx/826x)" + depends on (CPM1 || CPM2) && PPC_OF + help + This supports the use of the I2C interface on Freescale + processors with CPM1 or CPM2. + + This driver can also be built as a module. If so, the module + will be called i2c-cpm. + config I2C_DAVINCI tristate "DaVinci I2C driver" depends on ARCH_DAVINCI
drivers/i2c/busses/Makefile +1 -0
@@ -11,6 +11,7 @@ obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o obj-$(CONFIG_I2C_AT91) += i2c-at91.o obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o +obj-$(CONFIG_I2C_CPM) += i2c-cpm.o obj-$(CONFIG_I2C_DAVINCI) += i2c-davinci.o obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
drivers/i2c/busses/i2c-cpm.c new file +728 -0
728 lines not on this page · raw message
Signature · 1 line
1.5.4.5
Hi Jochen,
I just had a debug session with a MPC8260 + eeprom and got some questions:
Quoted 394 lines
This driver uses the port of 2.4 code from Vitaly Bordug <[email protected]> and the actual algorithm used by the i2c driver of the DBox code on cvs.tuxboc.org from Felix Domke ([email protected]) and Gillem ([email protected]) converted to an
of_platform_driver. Tested on CPM1 (MPC823 on dbox2 hardware) and CPM2 (MPC8272).Signed-off-by Jochen Friedrich <[email protected]>--- drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-cpm.c | 728 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 739 insertions(+), 0 deletions(-) create mode 100644 drivers/i2c/busses/i2c-cpm.cdrivers/i2c/busses/Kconfig +10
-0@@ -114,6 +114,16 @@ config I2C_BLACKFIN_TWI_CLK_KHZ help The unit of the TWI clock is kHz. +config I2C_CPM + tristate "Freescale CPM1 or CPM2 (MPC8xx/826x)" + depends on (CPM1 || CPM2) && PPC_OF + help + This supports the use of the I2C interface on Freescale + processors with CPM1 or CPM2. + + This driver can also be built as a module. If so, the module + will be called i2c-cpm. + config I2C_DAVINCI tristate "DaVinci I2C driver" depends on ARCH_DAVINCIdrivers/i2c/busses/Makefile +1
-0@@ -11,6 +11,7 @@ obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o obj-$(CONFIG_I2C_AT91) += i2c-at91.o obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o +obj-$(CONFIG_I2C_CPM) += i2c-cpm.o obj-$(CONFIG_I2C_DAVINCI) += i2c-davinci.o obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o obj-$(CONFIG_I2C_GPIO) += i2c-gpio.odrivers/i2c/busses/i2c-cpm.c new file +340
-0@@ -0,0 +1,728 @@ +/* +* 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]> +* +* Parts from dbox2_i2c.c (cvs.tuxbox.org) +* (C) 2000-2001 Felix Domke ([email protected]), Gillem ([email protected]) +* +* Converted to of_platform_device. Renamed to i2c-cpm.c. +* (C) 2007,2008 Jochen Friedrich <[email protected]> +* +* 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. +*/ + +#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/stddef.h> +#include <linux/i2c.h> +#include <linux/io.h> +#include <linux/dma-mapping.h> +#include <linux/of_device.h> +#include <linux/of_platform.h> +#include <linux/of_i2c.h> +#include <sysdev/fsl_soc.h> +#include <asm/cpm.h> + +/* 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 + +#define CPM_CR_INIT_TRX (0x00) +#define CPM_CR_CLOSE_RX_BD (0x07) + +#define I2C_EB (0x10)/* Big endian mode */ +#define I2C_EB_CPM2 (0x30)/* Big endian mode, memory snoop */ + +#define DPRAM_BASE ((u8 __iomem __force *)cpm_muram_addr(0)) + +/* I2C parameter RAM. */ +struct i2c_ram { +ushort rbase;/* Rx Buffer descriptor base address */ +ushort tbase;/* Tx Buffer descriptor base address */ +u_char rfcr;/* Rx function code */ +u_char tfcr;/* Tx function code */ +ushort mrblr;/* Max receive buffer length */ +uint rstate;/* Internal */ +uint rdp;/* Internal */ +ushort rbptr;/* Rx Buffer descriptor pointer */ +ushort rbc;/* Internal */ +uint rxtmp;/* Internal */ +uint tstate;/* Internal */ +uint tdp;/* Internal */ +ushort tbptr;/* Tx Buffer descriptor pointer */ +ushort tbc;/* Internal */ +uint txtmp;/* Internal */ +char res1[4 ];/* Reserved */ +ushort rpbase;/* Relocation pointer */ +char res2[2 ];/* Reserved */ +}; + +/* I2C Registers */ +struct i2c_reg { +u8 i2mod; +u8 res1[3 ]; +u8 i2add; +u8 res2[3 ]; +u8 i2brg; +u8 res3[3 ]; +u8 i2com; +u8 res4[3 ]; +u8 i2cer; +u8 res5[3 ]; +u8 i2cmr; +}; + +struct cpm_i2c { +char *base; +struct of_device *ofdev; +struct i2c_adapter adap; +uint dp_addr; +int version;/* CPM1=1, CPM2=2 */ +int irq; +int cp_command; +struct i2c_reg __iomem *i2c_reg; +struct i2c_ram __iomem *i2c_ram; +u16 i2c_addr; +wait_queue_head_t i2c_wait; +struct mutex i2c_mutex;/* Protects struct i2c_ram */ +cbd_t __iomem *tbase; +cbd_t __iomem *rbase; +u_char *txbuf[CPM_MAXBD ]; +u_char *rxbuf[CPM_MAXBD ]; +u32 txdma[CPM_MAXBD ]; +u32 rxdma[CPM_MAXBD ]; +}; + +static irqreturn_t cpm_i2c_interrupt (int irq,void *dev_id) +{ +struct cpm_i2c *cpm; +struct i2c_reg __iomem *i2c_reg; +struct i2c_adapter *adap = dev_id; +int i; + + cpm =i2c_get_adapdata (dev_id); + i2c_reg = cpm->i2c_reg; + +/* Clear interrupt. */ + i =in_8 (&i2c_reg->i2cer); +out_8 (&i2c_reg->i2cer, i); + +dev_dbg (&adap->dev,"Interrupt: %x\n" , i); + +/* Get me going again. */ +wake_up_interruptible (&cpm->i2c_wait); + +return i ?IRQ_HANDLED :IRQ_NONE ; +} + +static void cpm_reset_i2c_params (struct cpm_i2c *cpm) +{ +struct i2c_ram __iomem *i2c_ram = cpm->i2c_ram; + +/* Set up the IIC parameters in the parameter ram. */ +out_be16 (&i2c_ram->tbase, (u8 __iomem *)cpm->tbase -DPRAM_BASE ); +out_be16 (&i2c_ram->rbase, (u8 __iomem *)cpm->rbase -DPRAM_BASE ); + +if (cpm->version ==1 ) { +out_8 (&i2c_ram->tfcr,I2C_EB ); +out_8 (&i2c_ram->rfcr,I2C_EB ); + }else { +out_8 (&i2c_ram->tfcr,I2C_EB_CPM2 ); +out_8 (&i2c_ram->rfcr,I2C_EB_CPM2 ); + } + +out_be16 (&i2c_ram->mrblr,CPM_MAX_READ ); + +out_be32 (&i2c_ram->rstate,0 ); +out_be32 (&i2c_ram->rdp,0 ); +out_be16 (&i2c_ram->rbptr,0 ); +out_be16 (&i2c_ram->rbc,0 ); +out_be32 (&i2c_ram->rxtmp,0 ); +out_be32 (&i2c_ram->tstate,0 ); +out_be32 (&i2c_ram->tdp,0 ); +out_be16 (&i2c_ram->tbptr,0 ); +out_be16 (&i2c_ram->tbc,0 ); +out_be32 (&i2c_ram->txtmp,0 ); +} + +static void cpm_i2c_force_close (struct i2c_adapter *adap) +{ +struct cpm_i2c *cpm =i2c_get_adapdata (adap); +struct i2c_reg __iomem *i2c_reg = cpm->i2c_reg; + +dev_dbg (&adap->dev,"cpm_i2c_force_close()\n" ); + +cpm_command (cpm->cp_command,CPM_CR_CLOSE_RX_BD ); + +out_8 (&i2c_reg->i2cmr,0x00 );/* Disable all interrupts */ +out_8 (&i2c_reg->i2cer,0xff ); +} + +static void cpm_i2c_parse_message (struct i2c_adapter *adap, +struct i2c_msg *pmsg,int num,int tx,int rx) +{ +cbd_t __iomem *tbdf; +cbd_t __iomem *rbdf; +u_char addr; +u_char *tb; +u_char *rb; +struct cpm_i2c *cpm =i2c_get_adapdata (adap); + + tbdf = cpm->tbase + tx; + rbdf = cpm->rbase + rx; + + addr = pmsg->addr <<1 ; +if (pmsg->flags &I2C_M_RD ) + addr |=1 ; + + tb = cpm->txbuf[tx]; + rb = cpm->rxbuf[rx]; + +/* Align read buffer */ + rb = (u_char *) (((ulong ) rb +1 ) & ~1 ); + + tb[0 ] = addr;/* Device address byte w/rw flag */ + +out_be16 (&tbdf->cbd_datlen, pmsg->len +1 ); +out_be16 (&tbdf->cbd_sc,0 ); + +if (!(pmsg->flags &I2C_M_NOSTART )) +setbits16 (&tbdf->cbd_sc,BD_I2C_START ); + +if (tx +1 == num) +setbits16 (&tbdf->cbd_sc,BD_SC_LAST |BD_SC_WRAP ); + +if (pmsg->flags &I2C_M_RD ) { +/* +* 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). +*/ + +dev_dbg (&adap->dev,"cpm_i2c_read(abyte=0x%x)\n" , addr); + +out_be16 (&rbdf->cbd_datlen,0 ); +out_be16 (&rbdf->cbd_sc,BD_SC_EMPTY |BD_SC_INTRPT ); + +if (rx +1 ==CPM_MAXBD ) +setbits16 (&rbdf->cbd_sc,BD_SC_WRAP ); + +eieio (); +setbits16 (&tbdf->cbd_sc,BD_SC_READY ); + }else { +dev_dbg (&adap->dev,"cpm_iic_write(abyte=0x%x)\n" , addr); + +memcpy (tb+1 , pmsg->buf, pmsg->len); + +eieio (); +setbits16 (&tbdf->cbd_sc,BD_SC_READY |BD_SC_INTRPT ); + } +} + +static int cpm_i2c_check_message (struct i2c_adapter *adap, +struct i2c_msg *pmsg,int tx,int rx) +{ +cbd_t __iomem *tbdf; +cbd_t __iomem *rbdf; +u_char *tb; +u_char *rb; +struct cpm_i2c *cpm =i2c_get_adapdata (adap); + + tbdf = cpm->tbase + tx; + rbdf = cpm->rbase + rx; + + tb = cpm->txbuf[tx]; + rb = cpm->rxbuf[rx]; + +/* Align read buffer */ + rb = (u_char *) (((uint ) rb +1 ) & ~1 ); + +eieio (); +if (pmsg->flags &I2C_M_RD ) { +dev_dbg (&adap->dev,"tx sc 0x%04x, rx sc 0x%04x\n" , +in_be16 (&tbdf->cbd_sc),in_be16 (&rbdf->cbd_sc)); + +if (in_be16 (&tbdf->cbd_sc) &BD_SC_NAK ) { +dev_err (&adap->dev,"IIC read; No ack\n" ); +return -EIO ; + } +if (in_be16 (&rbdf->cbd_sc) &BD_SC_EMPTY ) { +dev_err (&adap->dev, +"IIC read; complete but rbuf empty\n" ); +return -EREMOTEIO ; + } +if (in_be16 (&rbdf->cbd_sc) &BD_SC_OV ) { +dev_err (&adap->dev,"IIC read; Overrun\n" ); +return -EREMOTEIO ; + } +memcpy (pmsg->buf, rb, pmsg->len); + }else { +dev_dbg (&adap->dev,"tx sc %d 0x%04x\n" , tx, +in_be16 (&tbdf->cbd_sc)); + +if (in_be16 (&tbdf->cbd_sc) &BD_SC_NAK ) { +dev_err (&adap->dev,"IIC write; No ack\n" ); +return -EIO ; + } +if (in_be16 (&tbdf->cbd_sc) &BD_SC_UN ) { +dev_err (&adap->dev,"IIC write; Underrun\n" ); +return -EIO ; + } +if (in_be16 (&tbdf->cbd_sc) &BD_SC_CL ) { +dev_err (&adap->dev,"IIC write; Collision\n" ); +return -EIO ; + } + } +return 0 ; +} + +static int cpm_i2c_xfer (struct i2c_adapter *adap,struct i2c_msg *msgs,int num) +{ +struct cpm_i2c *cpm =i2c_get_adapdata (adap); +struct i2c_reg __iomem *i2c_reg = cpm->i2c_reg; +struct i2c_ram __iomem *i2c_ram = cpm->i2c_ram; +struct i2c_msg *pmsg; +int ret, i; +int tptr; +int rptr; +cbd_t __iomem *tbdf; +cbd_t __iomem *rbdf; + +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->i2c_mutex); + +/* Reset to use first buffer */ +out_be16 (&i2c_ram->rbptr,in_be16 (&i2c_ram->rbase)); +out_be16 (&i2c_ram->tbptr,in_be16 (&i2c_ram->tbase)); + + tbdf = cpm->tbase; + rbdf = cpm->rbase; + + tptr =0 ; + rptr =0 ; + +while (tptr < num) { + pmsg = &msgs[tptr];
drivers/i2c/busses/i2c-cpm.c line 350
Quoted 10 lines
drivers/i2c/busses/i2c-cpm.c new file +10
-0+dev_dbg (&adap->dev,"R: %d T: %d\n" , rptr, tptr); + +cpm_i2c_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_reg->i2cmr,0x13 );/* Enable RX/TX/Error interupts */
Hardcoded value. Some OR-ed defines should be more readable.
drivers/i2c/busses/i2c-cpm.c line 352
Quoted 2 lines
drivers/i2c/busses/i2c-cpm.c new file +2
-0+out_8 (&i2c_reg->i2cer,0xff );/* Clear interrupt status */ +setbits8 (&i2c_reg->i2mod,1 );/* Enable */
Hardcoded value.
drivers/i2c/busses/i2c-cpm.c line 354
Quoted 2 lines
drivers/i2c/busses/i2c-cpm.c new file +2
-0+/* Begin transmission */ +setbits8 (&i2c_reg->i2com,0x80 );
Hardcoded value. (I also wonder if 0x81 might be more suitable, as it keeps the "be-a-master"-bit set. Still, both values work with my setup.)
Quoted 28 lines
drivers/i2c/busses/i2c-cpm.c new file +28
-0+ + tptr =0 ; + rptr =0 ; + +while (tptr < num) { +/* Check for outstanding messages */ +dev_dbg (&adap->dev,"test ready.\n" ); + pmsg = &msgs[tptr]; +if (pmsg->flags &I2C_M_RD ) + ret =wait_event_interruptible_timeout (cpm->i2c_wait, + !(in_be16 (&rbdf[rptr].cbd_sc) &BD_SC_EMPTY ), +1 *HZ ); +else + ret =wait_event_interruptible_timeout (cpm->i2c_wait, + !(in_be16 (&tbdf[tptr].cbd_sc) &BD_SC_READY ), +1 *HZ ); +if (ret ==0 ) { + ret = -EREMOTEIO ; +dev_dbg (&adap->dev,"I2C read: timeout!\n" ); + goto out_err; + } +if (ret >0 ) { +dev_dbg (&adap->dev,"ready.\n" ); + ret =cpm_i2c_check_message (adap, pmsg, tptr, rptr); + tptr++; +if (pmsg->flags &I2C_M_RD ) + rptr++; +if (ret)
drivers/i2c/busses/i2c-cpm.c line 392
Quoted 10 lines
drivers/i2c/busses/i2c-cpm.c new file +10
-0+ goto out_err; + } + } +#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 ); +clrbits8 (&i2c_reg->i2mod,1 );
I was unable to find the corresponding errata document, still I wonder if it is a 0 which should have been written? The text says "clear" and according to the reference manual, this means the bit should be 0.
drivers/i2c/busses/i2c-cpm.c line 403
Quoted 11 lines
drivers/i2c/busses/i2c-cpm.c new file +11
-0+#endif +mutex_unlock (&cpm->i2c_mutex); +return (num); + +out_err: +cpm_i2c_force_close (adap); +#ifdef I2C_CHIP_ERRATA +/* +* Chip errata, clear enable. This is not needed on rev D4 CPUs. +*/ +clrbits8 (&i2c_reg->i2mod,1 );
write 0? see above.
Quoted 147 lines
drivers/i2c/busses/i2c-cpm.c new file +147
-0+#endif +mutex_unlock (&cpm->i2c_mutex); +return ret; +} + +static u32 cpm_i2c_func (struct i2c_adapter *adap) +{ +return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK ); +} + +/* -----exported algorithm data: ------------------------------------- */ + +static const struct i2c_algorithm cpm_i2c_algo = { + .master_xfer = cpm_i2c_xfer, + .functionality = cpm_i2c_func, +}; + +static const struct i2c_adapter cpm_ops = { + .owner =THIS_MODULE , + .name ="i2c-cpm" , + .algo = &cpm_i2c_algo, + .class =I2C_CLASS_HWMON , +}; + +static int __devinit cpm_i2c_setup (struct cpm_i2c *cpm) +{ +struct of_device *ofdev = cpm->ofdev; +const u32 *data; +int len, ret, i; +void __iomem *i2c_base; +cbd_t __iomem *tbdf; +cbd_t __iomem *rbdf; +unsigned char brg; + +dev_dbg (&cpm->ofdev->dev,"cpm_i2c_setup()\n" ); + +init_waitqueue_head (&cpm->i2c_wait); +mutex_init (&cpm->i2c_mutex); + + cpm->irq =of_irq_to_resource (ofdev->node,0 ,NULL ); +if (cpm->irq ==NO_IRQ ) +return -EINVAL ; + +/* Install interrupt handler. */ + ret =request_irq (cpm->irq, cpm_i2c_interrupt,0 ,"cpm_i2c" , + &cpm->adap); +if (ret) + goto out_irq; + +/* IIC parameter RAM */ + i2c_base =of_iomap (ofdev->node,1 ); +if (i2c_base ==NULL ) { + ret = -EINVAL ; + goto out_irq; + } + +if (of_device_is_compatible (ofdev->node,"fsl,cpm1-i2c" )) { + +/* Check for and use a microcode relocation patch. */ + cpm->i2c_ram = i2c_base; + cpm->i2c_addr =in_be16 (&cpm->i2c_ram->rpbase); + +/* +* Maybe should use cpm_muram_alloc instead of hardcoding +* this in micropatch.c +*/ +if (cpm->i2c_addr) { + cpm->i2c_ram =cpm_muram_addr (cpm->i2c_addr); +iounmap (i2c_base); + } + + cpm->version =1 ; + + }else if (of_device_is_compatible (ofdev->node,"fsl,cpm2-i2c" )) { + cpm->i2c_addr =cpm_muram_alloc (sizeof (struct i2c_ram ),64 ); + cpm->i2c_ram =cpm_muram_addr (cpm->i2c_addr); +out_be16 (i2c_base, cpm->i2c_addr); +iounmap (i2c_base); + + cpm->version =2 ; + + }else { +iounmap (i2c_base); + ret = -EINVAL ; + goto out_irq; + } + +/* I2C control/status registers */ + cpm->i2c_reg =of_iomap (ofdev->node,0 ); +if (cpm->i2c_reg ==NULL ) { + ret = -EINVAL ; + goto out_ram; + } + + data =of_get_property (ofdev->node,"fsl,cpm-command" , &len); +if (!data || len !=4 ) { + ret = -EINVAL ; + goto out_reg; + } + cpm->cp_command = *data; + + data =of_get_property (ofdev->node,"linux,i2c-class" , &len); +if (data && len ==4 ) + cpm->adap.class = *data; + +/* +* Allocate space for CPM_MAXBD transmit and receive buffer +* descriptors in the DP ram. +*/ + cpm->dp_addr =cpm_muram_alloc (sizeof (cbd_t) *2 *CPM_MAXBD ,8 ); +if (!cpm->dp_addr) { + ret = -ENOMEM ; + goto out_reg; + } + + cpm->tbase =cpm_muram_addr (cpm->dp_addr); + cpm->rbase =cpm_muram_addr (cpm->dp_addr +sizeof (cbd_t) *CPM_MAXBD ); + +/* Allocate TX and RX buffers */ + + tbdf = cpm->tbase; + rbdf = cpm->rbase; + +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_muram; + } +out_be32 (&rbdf[i].cbd_bufaddr, ((cpm->rxdma[i] +1 ) & ~1 )); + + 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_muram; + } +out_be32 (&tbdf[i].cbd_bufaddr, cpm->txdma[i]); + } + +/* Initialize Tx/Rx parameters. */ + +cpm_reset_i2c_params (cpm); + +dev_dbg (&cpm->ofdev->dev,"i2c_ram %p, i2c_addr 0x%04x\n" , + cpm->i2c_ram, cpm->i2c_addr);
drivers/i2c/busses/i2c-cpm.c line 560
Quoted 10 lines
drivers/i2c/busses/i2c-cpm.c new file +10
-0+dev_dbg (&cpm->ofdev->dev,"tbase 0x%04x, rbase 0x%04x\n" , + (u8 __iomem *)cpm->tbase -DPRAM_BASE , + (u8 __iomem *)cpm->rbase -DPRAM_BASE ); + +cpm_command (cpm->cp_command,CPM_CR_INIT_TRX ); + +/* +* Select an invalid address. Just make sure we don't use loopback mode +*/ +out_8 (&cpm->i2c_reg->i2add,0xfe );
Maybe 0x7f << 1 is a tiny bit more obvious?
drivers/i2c/busses/i2c-cpm.c line 568
Quoted 8 lines
drivers/i2c/busses/i2c-cpm.c new file +8
-0+ +/* Make clock run at 60 kHz. */ + + brg =get_brgfreq () / (32 *2 *60000 ) -3 ; +out_8 (&cpm->i2c_reg->i2brg, brg); + +out_8 (&cpm->i2c_reg->i2mod,0x00 ); +out_8 (&cpm->i2c_reg->i2com,0x01 );/* Master mode */
Hardcoded value.
Quoted 25 lines
drivers/i2c/busses/i2c-cpm.c new file +25
-0+ +/* Disable interrupts. */ +out_8 (&cpm->i2c_reg->i2cmr,0 ); +out_8 (&cpm->i2c_reg->i2cer,0xff ); + +return 0 ; + +out_muram: +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]); + } +cpm_muram_free (cpm->dp_addr); +out_reg: +iounmap (cpm->i2c_reg); +out_ram: +if ((cpm->version ==1 ) && (!cpm->i2c_addr)) +iounmap (cpm->i2c_ram); +if (cpm->version ==2 ) +cpm_muram_free (cpm->i2c_addr); +out_irq:
drivers/i2c/busses/i2c-cpm.c line 603
Quoted 10 lines
drivers/i2c/busses/i2c-cpm.c new file +10
-0+free_irq (cpm->irq, &cpm->adap); +return ret; +} + +static void cpm_i2c_shutdown (struct cpm_i2c *cpm) +{ +int i; + +/* Shut down I2C. */ +clrbits8 (&cpm->i2c_reg->i2mod,1 );
write 0? see above.
Quoted 135 lines
drivers/i2c/busses/i2c-cpm.c new file +125
-0+ +/* Disable interrupts */ +out_8 (&cpm->i2c_reg->i2cmr,0 ); +out_8 (&cpm->i2c_reg->i2cer,0xff ); + +free_irq (cpm->irq, &cpm->adap); + +/* Free all memory */ +for (i =0 ; i <CPM_MAXBD ; i++) { +dma_free_coherent (NULL ,CPM_MAX_READ +1 , + cpm->rxbuf[i], cpm->rxdma[i]); +dma_free_coherent (NULL ,CPM_MAX_READ +1 , + cpm->txbuf[i], cpm->txdma[i]); + } + +cpm_muram_free (cpm->dp_addr); +iounmap (cpm->i2c_reg); + +if ((cpm->version ==1 ) && (!cpm->i2c_addr)) +iounmap (cpm->i2c_ram); +if (cpm->version ==2 ) +cpm_muram_free (cpm->i2c_addr); + +return ; +} + +static int __devinit cpm_i2c_probe (struct of_device *ofdev, +const struct of_device_id *match) +{ +int result; +struct cpm_i2c *cpm; + + cpm =kzalloc (sizeof (struct cpm_i2c ),GFP_KERNEL ); +if (!cpm) +return -ENOMEM ; + + cpm->ofdev = ofdev; + +dev_set_drvdata (&ofdev->dev, cpm); + + cpm->adap = cpm_ops; +i2c_set_adapdata (&cpm->adap, cpm); + cpm->adap.dev.parent = &ofdev->dev; + + result =cpm_i2c_setup (cpm); +if (result) { +dev_err (&ofdev->dev,"Unable to init hardware\n" ); + goto out_free; + } + +/* register new adapter to i2c module... */ + + result =i2c_add_adapter (&cpm->adap); +if (result <0 ) { +dev_err (&ofdev->dev,"Unable to register with I2C\n" ); + goto out_shut; + } + +dev_dbg (&ofdev->dev,"hw routines for %s registered.\n" , + cpm->adap.name); + +/* +* register OF I2C devices +*/ +of_register_i2c_devices (&cpm->adap, ofdev->node); + +return 0 ; +out_shut: +cpm_i2c_shutdown (cpm); +out_free: +dev_set_drvdata (&ofdev->dev,NULL ); +kfree (cpm); + +return result; +} + +static int __devexit cpm_i2c_remove(struct of_device *ofdev) +{ +struct cpm_i2c *cpm =dev_get_drvdata (&ofdev->dev); + +i2c_del_adapter (&cpm->adap); + +cpm_i2c_shutdown (cpm); + +dev_set_drvdata (&ofdev->dev,NULL ); +kfree (cpm); + +return 0 ; +} + +static const struct of_device_id cpm_i2c_match[] = { + { + .compatible ="fsl,cpm-i2c" , + }, + {}, +}; + +MODULE_DEVICE_TABLE (of, cpm_i2c_match); + +static struct of_platform_driver cpm_i2c_driver = { + .match_table = cpm_i2c_match, + .probe = cpm_i2c_probe, + .remove =__devexit_p (cpm_i2c_remove), + .driver = { + .name ="fsl-i2c-cpm" , + .owner =THIS_MODULE , + } +}; + +static int __init cpm_i2c_init (void ) +{ +return of_register_platform_driver (&cpm_i2c_driver); +} + +static void __exit cpm_i2c_exit(void) +{ +of_unregister_platform_driver (&cpm_i2c_driver); +} + +module_init (cpm_i2c_init); +module_exit (cpm_i2c_exit); + +MODULE_AUTHOR ("Jochen Friedrich <[email protected]>" ); +MODULE_DESCRIPTION ("I2C-Bus adapter routines for CPM boards" ); +MODULE_LICENSE ("GPL" );Signature · 9 lines
1.5.4.5 _______________________________________________ i2c mailing list [email protected] http://lists.lm-sensors.org/mailman/listinfo/i2c
In general, the driver works well for me (just had to disable broadcast responses; eeprom at 00-1f \o/).
Kind regards,
Wolfram
Signature · 2 lines
Dipl.-Ing. Wolfram Sang | http://www.pengutronix.de Pengutronix - Linux Solutions for Science and Industry
_______________________________________________
Linuxppc-dev mailing list
[email protected]
https://ozlabs.org/mailman/listinfo/linuxppc-dev
Jochen Friedrich
maintainerHi Wolfram,
Quoted 5 lines
drivers/i2c/busses/i2c-cpm.c line 354
Quoted 2 lines
drivers/i2c/busses/i2c-cpm.c new file +2
-0+/* Begin transmission */ +setbits8 (&i2c_reg->i2com,0x80 );Hardcoded value. (I also wonder if
0x81might be more suitable, as it keeps the "be-a-master"-bit set. Still, both values work with my setup.)
Quoted 10 lines
drivers/i2c/busses/i2c-cpm.c line 392
Quoted 7 lines
drivers/i2c/busses/i2c-cpm.c new file +7
-0+#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 ); +clrbits8 (&i2c_reg->i2mod,1 );I was unable to find the corresponding errata document, still I wonder if it is a 0 which should have been written? The text says "clear" and according to the reference manual, this means the bit should be 0.
setbits8() and clrbits8() use a bitmask as second argument.
setbits8(&i2c_reg->i2com, 0x80) will set bit 7 on the i2com register but leave bit 0 untouched. Likewise, clrbits8(&i2c_reg->i2mod, 1) will clear bit 0.
Thanks,
Jochen
Quoted 1 line · Jochen Friedrich
setbits8()andclrbits8()use a bitmask as second argument.
Ouch! Guess my mind wanted to read in_8 and out_8 for some reason... Sorry and thanks for clarifying!
Wolfram
Signature · 2 lines
Dipl.-Ing. Wolfram Sang | http://www.pengutronix.de Pengutronix - Linux Solutions for Science and Industry