All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] [POWERPC] i2c: adds support for i2c bus on 8xx
@ 2007-10-13 21:41 Jochen Friedrich
  0 siblings, 0 replies; 3+ messages in thread
From: Jochen Friedrich @ 2007-10-13 21:41 UTC (permalink / raw)
  To: linuxppc-embedded; +Cc: Carsten Juttner

[-- Attachment #1: Type: text/plain, Size: 1288 bytes --]

Using the port of 2.4 code from Vitaly Bordug <vitb@kernel.crashing.org>
and the actual algorithm used by the i2c driver of the DBox code on
cvs.tuxboc.org from Tmbinc, Gillem (htoa@gmx.net). Renamed
i2c-algo-8xx.c to i2c-algo-cpm.c and i2c-rpx.c to i2c-8xx-of.c. Added
original i2c-rpx.c as i2c-8xx.c for pre-OF (arch ppc) devices.

Signed-off-by: Jochen Friedrich <jochen@scram.de>
---
This can be pulled from git://git.bocc.de/dbox2.git i2c

 arch/powerpc/platforms/8xx/mpc885ads_setup.c |    6 +
 arch/powerpc/sysdev/fsl_soc.c                |   15 +
 drivers/i2c/algos/Kconfig                    |   12 +
 drivers/i2c/algos/Makefile                   |    1 +
 drivers/i2c/algos/i2c-algo-cpm.c             |  562 ++++++++++++++++++++++++++
 drivers/i2c/busses/Kconfig                   |   16 +
 drivers/i2c/busses/Makefile                  |    2 +
 drivers/i2c/busses/i2c-8xx-of.c              |  170 ++++++++
 drivers/i2c/busses/i2c-8xx.c                 |  105 +++++
 include/linux/i2c-algo-cpm.h                 |   34 ++
 10 files changed, 923 insertions(+), 0 deletions(-)
 create mode 100644 drivers/i2c/algos/i2c-algo-cpm.c
 create mode 100644 drivers/i2c/busses/i2c-8xx-of.c
 create mode 100644 drivers/i2c/busses/i2c-8xx.c
 create mode 100644 include/linux/i2c-algo-cpm.h

[-- Attachment #2: b654609590d9aa07d46749783b2cf01eba5f422b.diff --]
[-- Type: text/x-patch, Size: 26775 bytes --]

diff --git a/arch/powerpc/platforms/8xx/mpc885ads_setup.c b/arch/powerpc/platforms/8xx/mpc885ads_setup.c
index 2cf1b6a..ecc81ba 100644
--- a/arch/powerpc/platforms/8xx/mpc885ads_setup.c
+++ b/arch/powerpc/platforms/8xx/mpc885ads_setup.c
@@ -175,6 +175,12 @@ static void __init init_ioports(void)
 
 	/* Set FEC1 and FEC2 to MII mode */
 	clrbits32(&mpc8xx_immr->im_cpm.cp_cptr, 0x00000180);
+
+#ifdef CONFIG_I2C_8XX_OF
+	setbits32(&mpc8xx_immr->im_cpm.cp_pbpar, 0x00000030);
+	setbits32(&mpc8xx_immr->im_cpm.cp_pbdir, 0x00000030);
+	setbits16(&mpc8xx_immr->im_cpm.cp_pbodr, 0x0030);
+#endif
 }
 
 static void __init mpc885ads_setup_arch(void)
diff --git a/arch/powerpc/sysdev/fsl_soc.c b/arch/powerpc/sysdev/fsl_soc.c
index 3ace747..0fda67c 100644
--- a/arch/powerpc/sysdev/fsl_soc.c
+++ b/arch/powerpc/sysdev/fsl_soc.c
@@ -28,6 +28,7 @@
 #include <linux/fsl_devices.h>
 #include <linux/fs_enet_pd.h>
 #include <linux/fs_uart_pd.h>
+#include <linux/of_platform.h>
 
 #include <asm/system.h>
 #include <asm/atomic.h>
@@ -1210,6 +1211,20 @@ err:
 
 arch_initcall(cpm_smc_uart_of_init);
 
+static int __init fsl_i2c_cpm_of_init(void)
+{
+	struct device_node *np = NULL;
+	/*
+	 * Register all the devices which type is "i2c-cpm"
+	 */
+	while ((np = of_find_compatible_node(np, "i2c", "fsl,i2c-cpm")) != NULL)
+		of_platform_device_create(np, "fsl-i2c-cpm", NULL);
+	return 0;
+}
+
+arch_initcall(fsl_i2c_cpm_of_init);
+
+
 #endif /* CONFIG_8xx */
 #endif /* CONFIG_PPC_CPM_NEW_BINDING */
 
diff --git a/drivers/i2c/algos/Kconfig b/drivers/i2c/algos/Kconfig
index 014dfa5..2419051 100644
--- a/drivers/i2c/algos/Kconfig
+++ b/drivers/i2c/algos/Kconfig
@@ -14,6 +14,18 @@ config I2C_ALGOBIT
 	  This support is also available as a module.  If so, the module 
 	  will be called i2c-algo-bit.
 
+config I2C_ALGOCPM
+	tristate "I2C MPC8xx CPM and MPC8260 CPM2 interfaces"
+	depends on ( 8xx || 8260 ) && I2C
+	help
+	  CPM I2C Algorithm, supports the CPM I2C interface for mpc8xx 
+	  and mpc8260 CPUs. Say Y if you own a CPU of this class and wish
+	  to use the I2C interface and say Y to the specific driver for the
+	  particular CPU.
+
+	  This support is also available as a module.  If so, the module
+	  will be called i2c-algo-cpm.
+
 config I2C_ALGOPCF
 	tristate "I2C PCF 8584 interfaces"
 	help
diff --git a/drivers/i2c/algos/Makefile b/drivers/i2c/algos/Makefile
index cac1051..b5fe28c 100644
--- a/drivers/i2c/algos/Makefile
+++ b/drivers/i2c/algos/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_ALGOBIT)	+= i2c-algo-bit.o
 obj-$(CONFIG_I2C_ALGOPCF)	+= i2c-algo-pcf.o
 obj-$(CONFIG_I2C_ALGOPCA)	+= i2c-algo-pca.o
 obj-$(CONFIG_I2C_ALGO_SGI)	+= i2c-algo-sgi.o
+obj-$(CONFIG_I2C_ALGOCPM)	+= i2c-algo-cpm.o
 
 ifeq ($(CONFIG_I2C_DEBUG_ALGO),y)
 EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/i2c/algos/i2c-algo-cpm.c b/drivers/i2c/algos/i2c-algo-cpm.c
new file mode 100644
index 0000000..e287f1a
--- /dev/null
+++ b/drivers/i2c/algos/i2c-algo-cpm.c
@@ -0,0 +1,562 @@
+/*
+ * i2c-algo-cpm.c i2x driver algorithms for MPC8XX CPM and MPC8260 CPM2
+ * 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.
+ *
+ * moved into proper i2c interface; separated out platform specific
+ * parts into i2c-8xx.c
+ * Brad Parker (brad@heeltoe.com)
+ *
+ * Parts from dbox2_i2c.c (cvs.tuxbox.org)
+ * (C) 2000-2001 Tmbinc, Gillem (htoa@gmx.net)
+ *
+ * (C) 2007 Montavista Software, Inc.
+ * Vitaly Bordug <vitb@kernel.crashing.org>
+ *
+ * (C) 2007 Jochen Friedrich <jochen@scram.de>
+ */
+
+#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/i2c.h>
+#include <linux/i2c-algo-cpm.h>
+#include <linux/io.h>
+#include <linux/time.h>
+#include <linux/dma-mapping.h>
+#include <asm/mpc8xx.h>
+
+/* Try to define this if you have an older CPU (earlier than rev D4) */
+#undef	I2C_CHIP_ERRATA
+
+static int cpm_debug = 1;
+#ifdef DEBUG
+module_param(cpm_debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(cpm_debug, "debug level - 0 off; 1 normal");
+#endif
+
+static irqreturn_t cpm_iic_interrupt(int irq, void *dev_id)
+{
+	struct i2c_adapter *adap;
+	struct i2c_algo_cpm_data *cpm;
+	i2c8xx_t *i2c;
+	int i;
+
+	adap = (struct i2c_adapter *) dev_id;
+	cpm = adap->algo_data;
+	i2c = cpm->i2c;
+
+	/* Clear interrupt.
+	 */
+	i = in_8(&i2c->i2c_i2cer);
+	out_8(&i2c->i2c_i2cer, i);
+
+	if (cpm_debug)
+		dev_dbg(&adap->dev, "Interrupt: %x\n", i);
+
+	/* Get 'me going again.
+	 */
+	wake_up_interruptible(&cpm->iic_wait);
+
+	return IRQ_HANDLED;
+}
+
+static int cpm_iic_init(struct i2c_adapter *adap)
+{
+	struct i2c_algo_cpm_data *cpm = adap->algo_data;
+	iic_t *iip = cpm->iip;
+	i2c8xx_t *i2c = cpm->i2c;
+	unsigned char brg;
+	int ret, i;
+
+	if (cpm_debug)
+		dev_dbg(&adap->dev, "cpm_iic_init()\n");
+
+	ret = 0;
+	init_waitqueue_head(&cpm->iic_wait);
+	mutex_init(&cpm->iic_mutex);
+	/* Initialize the parameter ram.
+	 * We need to make sure many things are initialized to zero,
+	 * especially in the case of a microcode patch.
+	 */
+	out_be32(&iip->iic_rstate, 0);
+	out_be32(&iip->iic_rdp, 0);
+	out_be16(&iip->iic_rbptr, 0);
+	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, 0);
+	out_be16(&iip->iic_tbc, 0);
+	out_be32(&iip->iic_txtmp, 0);
+
+	/* 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);
+
+	if (cpm_debug) {
+		dev_dbg(&adap->dev, "iip %p, dp_addr 0x%x\n", cpm->iip,
+			cpm->dp_addr);
+		dev_dbg(&adap->dev, "iic_tbase %d, iic_rbase %d\n",
+			in_be16(&iip->iic_tbase), in_be16(&iip->iic_rbase));
+	}
+
+	out_8(&iip->iic_tfcr, SMC_EB);
+	out_8(&iip->iic_rfcr, SMC_EB);
+
+	/* Set maximum receive size.
+	 */
+	out_be16(&iip->iic_mrblr, CPM_MAX_READ);
+
+	/* Initialize Tx/Rx parameters.
+	 */
+	if (cpm->reloc == 0) {
+		cpm8xx_t *cp = cpm->cp;
+		int res;
+
+		u16 v = mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_INIT_TRX) | CPM_CR_FLG;
+
+		out_be16(&cp->cp_cpcr, v);
+		res = wait_event_timeout(cpm->iic_wait,
+					 !(in_be16(&cp->cp_cpcr) & CPM_CR_FLG),
+					 HZ * 10);
+		if (!res)
+			return -EIO;
+
+	} 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.
+	 */
+	out_8(&i2c->i2c_i2add, 0xfe);
+
+	/* Make clock run at 60 kHz.
+	 */
+	/* brg = ppc_proc_freq / (32 * 2 * 60000) - 3; */
+	brg = 7;
+	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] = (unsigned char *)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.
+	 */
+	if (request_irq(cpm->irq, cpm_iic_interrupt, 0, "8xx_i2c", adap)) {
+		ret = -EIO;
+		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 i2c_algo_cpm_data *cpm = adap->algo_data;
+	int i;
+
+	i2c8xx_t *i2c = cpm->i2c;
+
+	/* 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);
+}
+
+static void cpm_reset_iic_params(struct i2c_algo_cpm_data *cpm)
+{
+	iic_t *iip = cpm->iip;
+
+	out_be16(&iip->iic_tbase, cpm->dp_addr);
+	out_be16(&iip->iic_rbase, cpm->dp_addr + sizeof(cbd_t) * CPM_MAXBD);
+
+	out_8(&iip->iic_tfcr, SMC_EB);
+	out_8(&iip->iic_rfcr, SMC_EB);
+
+	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);
+}
+
+#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_adapter *adap)
+{
+	struct i2c_algo_cpm_data *cpm = adap->algo_data;
+	i2c8xx_t *i2c = cpm->i2c;
+	if (cpm->reloc == 0) {	/* micro code disabled */
+		cpm8xx_t *cp = cpm->cp;
+		u16 v =
+		    mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_CLOSE_RXBD) | CPM_CR_FLG;
+
+		if (cpm_debug)
+			printk(KERN_INFO "force_close()\n");
+
+		out_be16(&cp->cp_cpcr, v);
+		wait_event_timeout(cpm->iic_wait,
+				   !(in_be16(&cp->cp_cpcr) & CPM_CR_FLG),
+				   HZ * 5);
+	}
+	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 i2c_algo_cpm_data *cpm = adap->algo_data;
+	iic_t *iip = cpm->iip;
+	int i;
+
+	tbdf = (cbd_t *) cpm_dpram_addr(in_be16(&iip->iic_tbase));
+	rbdf = (cbd_t *) cpm_dpram_addr(in_be16(&iip->iic_rbase));
+
+	addr = pmsg->addr << 1;
+	if (pmsg->flags & I2C_M_RD)
+		addr |= 1;
+	if (pmsg->flags & I2C_M_REV_DIR_ADDR)
+		addr ^= 1;
+
+	tb = cpm->txbuf[tx];
+	rb = cpm->rxbuf[rx];
+
+	/* Align read buffer */
+	rb = (u_char *) (((uint) rb + 1) & ~1);
+
+	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).
+		 */
+		tb[0] = addr;		/* Device address byte w/rw flag */
+
+		if (cpm_debug)
+			dev_dbg(&adap->dev, "cpm_iic_read(abyte=0x%x)\n", addr);
+		tbdf[tx].cbd_bufaddr = cpm->txdma[tx];
+		if (pmsg->len == 0)
+			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];
+		rbdf[rx].cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT;
+		if (rx + 1 == CPM_MAXBD)
+			tbdf[rx].cbd_sc |= BD_SC_WRAP;
+
+		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];
+
+		if (cpm_debug)
+			dev_dbg(&adap->dev, "cpm_iic_write(abyte=0x%x)\n",
+				addr);
+		tbdf[tx].cbd_bufaddr = cpm->txdma[tx];
+		if (pmsg->len == 0)
+			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;
+		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 i2c_algo_cpm_data *cpm = adap->algo_data;
+	iic_t *iip = cpm->iip;
+	int i;
+
+	tbdf = (cbd_t *) cpm_dpram_addr(in_be16(&iip->iic_tbase));
+	rbdf = (cbd_t *) cpm_dpram_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) {
+		if (cpm_debug)
+			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) {
+			if (cpm_debug)
+				dev_dbg(&adap->dev, "IIC read; no ack\n");
+			if (pmsg->flags & I2C_M_IGNORE_NAK)
+				return 0;
+			else
+				return -EREMOTEIO;
+		}
+		if (rbdf[rx].cbd_sc & BD_SC_EMPTY) {
+			if (cpm_debug) {
+				dev_dbg(&adap->dev,
+					"IIC read; complete but rbuf empty\n");
+			}
+			return -EREMOTEIO;
+		}
+		if (rbdf[rx].cbd_sc & BD_SC_OV) {
+			if (cpm_debug)
+				dev_dbg(&adap->dev, "IIC read; Overrun\n");
+			return -EREMOTEIO;
+		}
+		for (i = 0; i < pmsg->len; i++)
+			pmsg->buf[i] = rb[i];
+	} else {
+		if (cpm_debug)
+			dev_dbg(&adap->dev, "tx sc %d %04x\n",
+				tx, tbdf[tx].cbd_sc);
+		if (tbdf[tx].cbd_sc & BD_SC_NAK) {
+			if (cpm_debug)
+				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 i2c_algo_cpm_data *cpm = adap->algo_data;
+	i2c8xx_t *i2c = cpm->i2c;
+	iic_t *iip = cpm->iip;
+	struct i2c_msg *pmsg, *rmsg;
+	int ret, i;
+	int tptr;
+	int rptr;
+	cbd_t *tbdf, *rbdf;
+
+	if (num > CPM_MAXBD)
+		return -EREMOTEIO;
+
+	/* Check if we have any oversized READ requests */
+	for (i = 0; i < num; i++) {
+		pmsg = &msgs[i];
+		if (pmsg->len >= CPM_MAX_READ)
+			return -EREMOTEIO;
+	}
+
+	mutex_lock(&cpm->iic_mutex);
+
+	/* check for and use a microcode relocation patch */
+	if (cpm->reloc)
+		cpm_reset_iic_params(cpm);
+
+	/* 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_dpram_addr(in_be16(&iip->iic_tbase));
+	rbdf = (cbd_t *) cpm_dpram_addr(in_be16(&iip->iic_rbase));
+
+	tptr = 0;
+	rptr = 0;
+
+	while (tptr < num) {
+		pmsg = &msgs[tptr];
+		if (cpm_debug)
+			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 -EREMOTEIO;
+			}
+		} 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);
+				if (cpm_debug)
+					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;
+
+	if (cpm_debug)
+		dev_dbg(&adap->dev,
+			"i2c-algo-cpm.o: 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);
+}
+EXPORT_SYMBOL(i2c_cpm_add_bus);
+
+int i2c_cpm_del_bus(struct i2c_adapter *adap)
+{
+	i2c_del_adapter(adap);
+
+	return cpm_iic_shutdown(adap);
+}
+EXPORT_SYMBOL(i2c_cpm_del_bus);
+
+MODULE_AUTHOR("Brad Parker <brad@heeltoe.com>");
+MODULE_DESCRIPTION("I2C-Bus CPM algorithm");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 9f3a4cd..1dcc294 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -4,6 +4,22 @@
 
 menu "I2C Hardware Bus support"
 
+config I2C_8XX
+	tristate "MPC8xx CPM oldstyle (no OF)"
+	depends on 8xx && I2C && !OF
+	select I2C_ALGOCPM
+	help
+	  This supports the use of the I2C interface on Freescale MPC8xx
+	  processors.
+
+config I2C_8XX_OF
+	tristate "MPC8xx CPM with Open Firmware"
+	depends on 8xx && I2C && OF
+	select I2C_ALGOCPM
+	help
+	  This supports the use of the I2C interface on Freescale MPC8xx
+	  processors.
+
 config I2C_ALI1535
 	tristate "ALI 1535"
 	depends on PCI
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 5b752e4..586e263 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -2,6 +2,8 @@
 # Makefile for the i2c bus drivers.
 #
 
+obj-$(CONFIG_I2C_8XX)		+= i2c-8xx.o
+obj-$(CONFIG_I2C_8XX_OF)	+= i2c-8xx-of.o
 obj-$(CONFIG_I2C_ALI1535)	+= i2c-ali1535.o
 obj-$(CONFIG_I2C_ALI1563)	+= i2c-ali1563.o
 obj-$(CONFIG_I2C_ALI15X3)	+= i2c-ali15x3.o
diff --git a/drivers/i2c/busses/i2c-8xx-of.c b/drivers/i2c/busses/i2c-8xx-of.c
new file mode 100644
index 0000000..f2e87bd
--- /dev/null
+++ b/drivers/i2c/busses/i2c-8xx-of.c
@@ -0,0 +1,170 @@
+/*
+ * Embedded Planet RPX Lite MPC8xx CPM I2C interface.
+ * Copyright (c) 1999 Dan Malek (dmalek@jlc.net).
+ *
+ * moved into proper i2c interface;
+ * Brad Parker (brad@heeltoe.com)
+ *
+ * (C) 2007 Montavista Software, Inc.
+ * Vitaly Bordug <vitb@kernel.crashing.org>
+ *
+ * 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 8xx board.  The console messages have been
+ * changed to eliminate RPXLite references.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/stddef.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-cpm.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <asm/mpc8xx.h>
+#include <asm/commproc.h>
+#include <asm/fs_pd.h>
+
+
+struct m8xx_i2c {
+	char *base;
+	struct of_device *ofdev;
+	struct i2c_adapter adap;
+	struct i2c_algo_cpm_data *algo_8xx;
+};
+
+static struct i2c_algo_cpm_data m8xx_data;
+
+static const struct i2c_adapter m8xx_ops = {
+	.owner		= THIS_MODULE,
+	.name		= "i2c-8xx-of",
+	.id		= I2C_HW_MPC8XX_EPON,
+	.algo_data	= &m8xx_data,
+	.dev.parent	= &platform_bus,
+	.class		= I2C_CLASS_HWMON,
+};
+
+static int m8xx_iic_init(struct m8xx_i2c *i2c)
+{
+	cpm8xx_t *cp;
+	struct resource r;
+	struct i2c_algo_cpm_data *data = i2c->algo_8xx;
+	struct of_device *ofdev = i2c->ofdev;
+	struct device_node *np = ofdev->node;
+
+	/* Pointer to Communication Processor
+	 */
+	cp = (cpm8xx_t *)immr_map(im_cpm);
+	data->cp = cp;
+
+	data->irq = irq_of_parse_and_map(np, 0);
+	if (data->irq < 0)
+		return -EINVAL;
+
+	if (of_address_to_resource(np, 1, &r))
+		return -EINVAL;
+
+	data->iip = ioremap(r.start, r.end - r.start + 1);
+	if (data->iip == NULL)
+		return -EINVAL;
+
+	/* Check for and use a microcode relocation patch.
+	 */
+	data->reloc = data->iip->iic_rpbase;
+	if (data->reloc)
+		data->iip = (iic_t *)&cp->cp_dpmem[data->iip->iic_rpbase];
+
+	if (of_address_to_resource(np, 0, &r))
+		return -EINVAL;
+
+	data->i2c = ioremap(r.start, r.end - r.start + 1);
+	if (data->i2c == NULL)
+		return -EINVAL;
+
+	/* Allocate space for two transmit and two receive buffer
+	 * descriptors in the DP ram.
+	 */
+	data->dp_addr = cpm_dpalloc(sizeof(cbd_t) * 4, 8);
+	if (!data->dp_addr)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int i2c_8xx_probe(struct of_device *ofdev,
+			 const struct of_device_id *match)
+{
+	int result;
+	struct m8xx_i2c *i2c;
+
+	i2c = kzalloc(sizeof(*i2c), GFP_KERNEL);
+	if (!i2c)
+		return -ENOMEM;
+
+	i2c->ofdev = ofdev;
+	i2c->algo_8xx = &m8xx_data;
+
+	m8xx_iic_init(i2c);
+
+	dev_set_drvdata(&ofdev->dev, i2c);
+
+	i2c->adap = m8xx_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-8xx-of: Unable to register with I2C\n");
+		kfree(i2c);
+	}
+
+	return result;
+}
+
+static int i2c_8xx_remove(struct of_device *ofdev)
+{
+	struct m8xx_i2c *i2c = dev_get_drvdata(&ofdev->dev);
+
+	i2c_cpm_del_bus(&i2c->adap);
+	dev_set_drvdata(&ofdev->dev, NULL);
+
+	kfree(i2c);
+	return 0;
+}
+
+static struct of_device_id i2c_8xx_match[] = {
+	{
+		.type = "i2c",
+		.compatible = "fsl,i2c-cpm",
+	},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, i2c_8xx_match);
+
+static struct of_platform_driver i2c_8xx_driver = {
+	.name		= "fsl-i2c-cpm",
+	.match_table	= i2c_8xx_match,
+	.probe		= i2c_8xx_probe,
+	.remove		= i2c_8xx_remove,
+};
+
+static int __init i2c_8xx_init(void)
+{
+	return of_register_platform_driver(&i2c_8xx_driver);
+}
+
+static void __exit i2c_8xx_exit(void)
+{
+	of_unregister_platform_driver(&i2c_8xx_driver);
+}
+
+module_init(i2c_8xx_init);
+module_exit(i2c_8xx_exit);
+
+MODULE_AUTHOR("Dan Malek <dmalek@jlc.net>");
+MODULE_DESCRIPTION("I2C-Bus adapter routines for MPC8xx boards");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-8xx.c b/drivers/i2c/busses/i2c-8xx.c
new file mode 100644
index 0000000..0e2fa56
--- /dev/null
+++ b/drivers/i2c/busses/i2c-8xx.c
@@ -0,0 +1,105 @@
+/*
+ * Embedded Planet RPX Lite MPC8xx CPM I2C interface.
+ * Copyright (c) 1999 Dan Malek (dmalek@jlc.net).
+ *
+ * moved into proper i2c interface;
+ * Brad Parker (brad@heeltoe.com)
+ *
+ * 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 8xx board.  The console messages have been
+ * changed to eliminate RPXLite references.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/stddef.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-cpm.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <asm/mpc8xx.h>
+#include <asm/commproc.h>
+
+static int
+m8xx_iic_init(struct i2c_algo_cpm_data *data)
+{
+	cpm8xx_t *cp;
+	immap_t *immap;
+
+	immap = (immap_t *)IMAP_ADDR;	/* and to internal registers */
+	/* Get pointer to Communication Processor */
+	cp = (cpm8xx_t *)&immap->im_cpm;
+
+	data->iip = (iic_t *)&cp->cp_dparam[PROFF_IIC];
+
+	/* Check for and use a microcode relocation patch.
+	*/
+	data->reloc = in_be16(&data->iip->iic_rpbase);
+	if (data->reloc)
+		data->iip = (iic_t *)&cp->cp_dpmem[data->reloc];
+
+	data->i2c = (i2c8xx_t *)&(immap->im_i2c);
+	data->cp = cp;
+
+	/* Initialize Port B IIC pins.
+	*/
+	setbits32(&cp->cp_pbpar, 0x00000030);
+	setbits32(&cp->cp_pbdir, 0x00000030);
+	setbits16(&cp->cp_pbodr, 0x00000030);
+
+	/* Allocate space for transmit and receive buffer
+	 * descriptors in the DP ram.
+	 */
+	data->dp_addr = cpm_dpalloc(sizeof(cbd_t) * 2 * CPM_MAXBD, 8);
+	if (!data->dp_addr)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static struct i2c_algo_cpm_data m8xx_data = {
+	.irq = CPM_IRQ_OFFSET + CPMVEC_I2C,
+};
+
+static struct i2c_adapter m8xx_ops = {
+	.owner		= THIS_MODULE,
+	.name		= "m8xx",
+	.id		= I2C_HW_MPC8XX_EPON,
+	.algo_data	= &m8xx_data,
+	.dev.parent	= &platform_bus,
+	.class		= I2C_CLASS_HWMON,
+};
+
+int __init i2c_8xx_init(void)
+{
+	int ret;
+
+	printk(KERN_INFO "i2c-8xx: i2c MPC8xx driver\n");
+
+	/* reset hardware to sane state */
+	ret = m8xx_iic_init(&m8xx_data);
+	if (ret)
+		return ret;
+
+	if (i2c_cpm_add_bus(&m8xx_ops) < 0) {
+		printk(KERN_ERR "i2c-8xx: Unable to register with I2C\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+void __exit i2c_8xx_exit(void)
+{
+	i2c_cpm_del_bus(&m8xx_ops);
+}
+
+module_init(i2c_8xx_init);
+module_exit(i2c_8xx_exit);
+
+MODULE_AUTHOR("Dan Malek <dmalek@jlc.net>");
+MODULE_DESCRIPTION("I2C-Bus adapter routines for MPC8xx boards");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/i2c-algo-cpm.h b/include/linux/i2c-algo-cpm.h
new file mode 100644
index 0000000..2192b7d
--- /dev/null
+++ b/include/linux/i2c-algo-cpm.h
@@ -0,0 +1,34 @@
+/* ------------------------------------------------------------------------- */
+/* i2c-algo-cpm.h i2c driver algorithms for MPX8XX CPM or MPC8250 CPM2       */
+/* ------------------------------------------------------------------------- */
+
+#ifndef I2C_ALGO_CPM_H
+#define I2C_ALGO_CPM_H
+
+#include <linux/i2c.h>
+#include <asm/8xx_immap.h>
+#include <asm/commproc.h>
+
+#define CPM_MAX_READ    513
+#define CPM_MAXBD       4
+
+struct i2c_algo_cpm_data {
+	uint dp_addr;
+	int reloc;
+	int irq;
+	i2c8xx_t *i2c;
+	iic_t *iip;
+	cpm8xx_t *cp;
+	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];
+};
+
+extern int i2c_cpm_add_bus(struct i2c_adapter *);
+extern int i2c_cpm_del_bus(struct i2c_adapter *);
+
+#endif /* I2C_ALGO_CPM_H */
+


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH] [POWERPC] i2c: adds support for i2c bus on 8xx
@ 2007-05-08  6:31 ` Vitaly Bordug
  0 siblings, 0 replies; 3+ messages in thread
From: Vitaly Bordug @ 2007-05-08  6:31 UTC (permalink / raw)
  To: Jean Delvare; +Cc: linuxppc-dev, linux-kernel, linuxppc-dev


Utilized devicetree to store I2C data, ported i2c-algo-8xx.c from 2.4
approach(which remains nearly intact), refined i2c-rpx.c. I2C functionality
has been validated on mpc885ads with EEPROM access.

Interface has been reworked to of_device as well as other feedback
addressed. Repeated start ability is missing from the implementation.

Signed-off-by: Vitaly Bordug <vitb@kernel.crashing.org>

---

 arch/powerpc/platforms/8xx/mpc885ads_setup.c |   15 +
 arch/powerpc/sysdev/fsl_soc.c                |   15 +
 drivers/i2c/algos/Kconfig                    |    2 
 drivers/i2c/algos/Makefile                   |    1 
 drivers/i2c/algos/i2c-algo-8xx.c             |  520 ++++++++++++++++++++++++++
 drivers/i2c/busses/Kconfig                   |    2 
 drivers/i2c/busses/i2c-rpx.c                 |  143 +++++--
 include/linux/i2c-algo-8xx.h                 |   29 +
 8 files changed, 683 insertions(+), 44 deletions(-)

diff --git a/arch/powerpc/platforms/8xx/mpc885ads_setup.c b/arch/powerpc/platforms/8xx/mpc885ads_setup.c
index a57b577..40d12a2 100644
--- a/arch/powerpc/platforms/8xx/mpc885ads_setup.c
+++ b/arch/powerpc/platforms/8xx/mpc885ads_setup.c
@@ -51,6 +51,17 @@ static void init_smc1_uart_ioports(struct fs_uart_platform_info* fpi);
 static void init_smc2_uart_ioports(struct fs_uart_platform_info* fpi);
 static void init_scc3_ioports(struct fs_platform_info* ptr);
 
+#ifdef CONFIG_I2C_RPXLITE
+static void init_i2c_ioports()
+{
+	cpm8xx_t *cp = (cpm8xx_t *)immr_map(im_cpm);
+
+	setbits32(&cp->cp_pbpar, 0x00000030);
+	setbits32(&cp->cp_pbdir, 0x00000030);
+	setbits16(&cp->cp_pbodr, 0x0030);
+}
+#endif
+
 void __init mpc885ads_board_setup(void)
 {
 	cpm8xx_t *cp;
@@ -115,6 +126,10 @@ void __init mpc885ads_board_setup(void)
 	immr_unmap(io_port);
 
 #endif
+
+#ifdef CONFIG_I2C_RPXLITE
+	init_i2c_ioports();
+#endif
 }
 
 
diff --git a/arch/powerpc/sysdev/fsl_soc.c b/arch/powerpc/sysdev/fsl_soc.c
index 8a123c7..be60db7 100644
--- a/arch/powerpc/sysdev/fsl_soc.c
+++ b/arch/powerpc/sysdev/fsl_soc.c
@@ -33,6 +33,7 @@
 #include <asm/irq.h>
 #include <asm/time.h>
 #include <asm/prom.h>
+#include <asm/of_platform.h>
 #include <sysdev/fsl_soc.h>
 #include <mm/mmu_decl.h>
 #include <asm/cpm2.h>
@@ -1102,4 +1103,18 @@ err:
 
 arch_initcall(cpm_smc_uart_of_init);
 
+static int __init fsl_i2c_cpm_of_init(void)
+{
+	struct device_node *np = NULL;
+	/*
+	 * Register all the devices which type is "i2c-cpm"
+	 */
+	while ((np = of_find_compatible_node(np, "i2c", "fsl,i2c-cpm")) != NULL)
+		of_platform_device_create(np, "fsl-i2c-cpm", NULL);
+	return 0;
+}
+
+arch_initcall(fsl_i2c_cpm_of_init);
+
+
 #endif /* CONFIG_8xx */
diff --git a/drivers/i2c/algos/Kconfig b/drivers/i2c/algos/Kconfig
index af02034..11e37ff 100644
--- a/drivers/i2c/algos/Kconfig
+++ b/drivers/i2c/algos/Kconfig
@@ -41,6 +41,8 @@ config I2C_ALGOPCA
 config I2C_ALGO8XX
 	tristate "MPC8xx CPM I2C interface"
 	depends on 8xx && I2C
+	help
+	  8xx I2C Algorithm,supports the CPM I2C interface for mpc8xx CPUs.
 
 config I2C_ALGO_SGI
 	tristate "I2C SGI interfaces"
diff --git a/drivers/i2c/algos/Makefile b/drivers/i2c/algos/Makefile
index cac1051..1bd3b37 100644
--- a/drivers/i2c/algos/Makefile
+++ b/drivers/i2c/algos/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_ALGOBIT)	+= i2c-algo-bit.o
 obj-$(CONFIG_I2C_ALGOPCF)	+= i2c-algo-pcf.o
 obj-$(CONFIG_I2C_ALGOPCA)	+= i2c-algo-pca.o
 obj-$(CONFIG_I2C_ALGO_SGI)	+= i2c-algo-sgi.o
+obj-$(CONFIG_I2C_ALGO8XX)	+= i2c-algo-8xx.o
 
 ifeq ($(CONFIG_I2C_DEBUG_ALGO),y)
 EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/i2c/algos/i2c-algo-8xx.c b/drivers/i2c/algos/i2c-algo-8xx.c
new file mode 100644
index 0000000..01ffcee
--- /dev/null
+++ b/drivers/i2c/algos/i2c-algo-8xx.c
@@ -0,0 +1,520 @@
+/*
+ * i2c-algo-8xx.c i2x driver algorithms for MPC8XX CPM
+ * 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.
+ *
+ * moved into proper i2c interface; separated out platform specific
+ * parts into i2c-rpx.c
+ * Brad Parker (brad@heeltoe.com)
+ * 
+ * (C) 2007 Montavista Software, Inc.
+ * Vitaly Bordug <vitb@kernel.crashing.org>
+ */
+
+#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/i2c.h>
+#include <linux/i2c-algo-8xx.h>
+#include <asm/io.h>
+#include <asm/cacheflush.h>
+#include <asm/time.h>
+#include <asm/mpc8xx.h>
+
+/* Try to define this if you have an older CPU (earlier than rev D4) */
+#undef	I2C_CHIP_ERRATA
+
+static wait_queue_head_t iic_wait;
+static ushort r_tbase, r_rbase;
+
+static int cpm_debug;
+
+static irqreturn_t cpm_iic_interrupt(int irq, void *dev_id)
+{
+	i2c8xx_t *i2c = (i2c8xx_t *) dev_id;
+
+#if 0
+	/* Chip errata, clear enable. This is not needed on rev D4 CPUs */
+	/* This should probably be removed and replaced by I2C_CHIP_ERRATA stuff */
+	/* Someone with a buggy CPU needs to confirm that */
+	out_8(&i2c->i2c_i2mod, in_8(&i2c->i2c_i2mod) | ~1);
+#endif
+	/* Clear interrupt.
+	 */
+	out_8(&i2c->i2c_i2cer, 0xff);
+
+	/* Get 'me going again.
+	 */
+	wake_up_interruptible(&iic_wait);
+
+	return IRQ_HANDLED;
+}
+
+static int cpm_iic_init(struct i2c_adapter *adap)
+{
+	struct i2c_algo_8xx_data *cpm = adap->algo_data;
+	iic_t *iip = cpm->iip;
+	i2c8xx_t *i2c = cpm->i2c;
+	unsigned char brg;
+
+	if (cpm_debug)
+		dev_dbg(adap->dev, "cpm_iic_init()\n");
+
+	init_waitqueue_head(&iic_wait);
+
+	/* 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->dp_addr;
+	iip->iic_rbase = r_rbase = cpm->dp_addr + sizeof(cbd_t) * 2;
+
+	if (cpm_debug) {
+		dev_dbg(adap->dev, "iip %p, dp_addr 0x%x\n", cpm->iip,
+			cpm->dp_addr);
+		dev_dbg(adap->dev, "iic_tbase %d, r_tbase %d\n", iip->iic_tbase,
+			r_tbase);
+	}
+
+	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->reloc == 0) {
+		cpm8xx_t *cp = cpm->cp;
+		int res;
+
+		u16 v = mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_INIT_TRX) | CPM_CR_FLG;
+
+		out_be16(&cp->cp_cpcr, v);
+		res = wait_event_timeout(iic_wait,
+					 !(in_be16(&cp->cp_cpcr) & CPM_CR_FLG),
+					 HZ * 10);
+		if (!res)
+			return -EIO;
+
+	} 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.
+	 */
+	out_8(&i2c->i2c_i2add, 0xfe);
+
+	/* Make clock run at 60 kHz.
+	 */
+	brg = ppc_proc_freq / (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);
+
+	/* Install interrupt handler.
+	 */
+	request_irq(cpm->irq, cpm_iic_interrupt, 0, "8xx_i2c", i2c);
+
+	return 0;
+}
+
+static int cpm_iic_shutdown(struct i2c_algo_8xx_data *cpm)
+{
+	i2c8xx_t *i2c = cpm->i2c;
+
+	/* 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);
+
+	return (0);
+}
+
+static void cpm_reset_iic_params(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_adapter *adap)
+{
+	struct i2c_algo_8xx_data *cpm = adap->algo_data;
+	i2c8xx_t *i2c = cpm->i2c;
+	if (cpm->reloc == 0) {	/* micro code disabled */
+		cpm8xx_t *cp = cpm->cp;
+		u16 v =
+		    mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_CLOSE_RXBD) | CPM_CR_FLG;
+
+		if (cpm_debug)
+			printk("force_close()\n");
+
+		out_be16(&cp->cp_cpcr, v);
+		wait_event_timeout(iic_wait,
+				   !(in_be16(&cp->cp_cpcr) & CPM_CR_FLG),
+				   HZ * 5);
+	}
+	out_8(&i2c->i2c_i2cmr, 0x00);	/* Disable all interrupts */
+	out_8(&i2c->i2c_i2cer, 0xff);
+}
+
+/* Read from IIC...
+ * abyte = address byte, with r/w flag already set
+ */
+static int
+cpm_iic_read(struct i2c_adapter *adap, u_char abyte, char *buf, int count)
+{
+	struct i2c_algo_8xx_data *cpm = adap->algo_data;
+	iic_t *iip = cpm->iip;
+	i2c8xx_t *i2c = cpm->i2c;
+	cbd_t *tbdf, *rbdf;
+	u_char *tb;
+	int res = 0;
+
+	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 *) cpm_dpram_addr(iip->iic_tbase);
+	rbdf = (cbd_t *) cpm_dpram_addr(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)
+		dev_dbg(adap->dev, "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;
+
+	/* 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 */
+	out_8(&i2c->i2c_i2com, in_8(&i2c->i2c_i2com) | 0x80);	/* Begin transmission */
+
+	/* Wait for IIC transfer */
+	res = wait_event_interruptible_timeout(iic_wait, 0, 1 * HZ);
+
+	if (res < 0) {
+		force_close(adap);
+		if (cpm_debug)
+			dev_dbg(adap->dev, "IIC read: timeout!\n");
+		return -EIO;
+	}
+#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
+	if (cpm_debug) {
+		dev_dbg(adap->dev, "tx sc %04x, rx sc %04x\n",
+			tbdf->cbd_sc, rbdf->cbd_sc);
+	}
+
+	if (tbdf->cbd_sc & BD_SC_READY) {
+		dev_dbg(adap->dev, "IIC read; complete but tbuf ready\n");
+		force_close(adap);
+		dev_dbg(adap->dev, "tx sc %04x, rx sc %04x\n",
+			tbdf->cbd_sc, rbdf->cbd_sc);
+	}
+
+	if (tbdf->cbd_sc & BD_SC_NAK) {
+		if (cpm_debug)
+			dev_dbg(adap->dev, "IIC read; no ack\n");
+		return -EREMOTEIO;
+	}
+
+	if (rbdf->cbd_sc & BD_SC_EMPTY) {
+		/* force_close(adap); */
+		if (cpm_debug) {
+			dev_dbg(adap->dev,
+				"IIC read; complete but rbuf empty\n");
+			dev_dbg(adap->dev, "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)
+			dev_dbg(adap->dev, "IIC read; Overrun\n");
+		return -EREMOTEIO;
+	}
+
+	if (cpm_debug)
+		dev_dbg(adap->dev, "read %d bytes\n", rbdf->cbd_datlen);
+
+	if (rbdf->cbd_datlen < count) {
+		if (cpm_debug)
+			dev_dbg(adap->dev,
+				"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_adapter *adap, u_char abyte, char *buf, int count)
+{
+	struct i2c_algo_8xx_data *cpm = adap->algo_data;
+	iic_t *iip = cpm->iip;
+	i2c8xx_t *i2c = cpm->i2c;
+	cbd_t *tbdf;
+	u_char *tb;
+	int res = 0;
+
+	/* 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)
+		dev_dbg(adap->dev, "cpm_iic_write(abyte=0x%x)\n", abyte);
+
+	/* set up 2 descriptors */
+
+	tbdf = (cbd_t *) cpm_dpram_addr(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 */
+	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 */
+	out_8(&i2c->i2c_i2com, in_8(&i2c->i2c_i2com) | 0x80);	/* Begin transmission */
+
+	/* Wait for IIC transfer */
+	res = wait_event_interruptible_timeout(iic_wait, 0, 1 * HZ);
+
+	if (res < 0) {
+		force_close(adap);
+		if (cpm_debug)
+			dev_dbg(adap->dev, "IIC write: timeout!\n");
+		return -EIO;
+	}
+#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
+	if (cpm_debug) {
+		dev_dbg(adap->dev, "tx0 sc %04x, tx1 sc %04x\n",
+			tbdf[0].cbd_sc, tbdf[1].cbd_sc);
+	}
+
+	if (tbdf->cbd_sc & BD_SC_NAK) {
+		if (cpm_debug)
+			dev_dbg(adap->dev, "IIC write; no ack\n");
+		return 0;
+	}
+
+	if (tbdf->cbd_sc & BD_SC_READY) {
+		if (cpm_debug)
+			dev_dbg(adap->dev,
+				"IIC write; complete but tbuf ready\n");
+		return 0;
+	}
+
+	return count;
+}
+
+static int cpm_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+	struct i2c_msg *pmsg;
+	int i, ret;
+	u_char addr;
+
+	for (i = 0; i < num; i++) {
+		pmsg = &msgs[i];
+
+		if (cpm_debug)
+			dev_dbg(adap->dev, "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_RD) {
+			/* read bytes into buffer */
+			ret = cpm_iic_read(adap, addr, pmsg->buf, pmsg->len);
+			if (cpm_debug)
+				dev_dbg(adap->dev,
+					"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)
+				dev_dbg(adap->dev, "i2c-algo-8xx.o: wrote %d\n",
+					ret);
+			if (ret < pmsg->len) {
+				return (ret < 0) ? ret : -EREMOTEIO;
+			}
+		}
+	}
+	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_8xx_add_bus(struct i2c_adapter *adap)
+{
+	int res;
+
+	if (cpm_debug)
+		dev_dbg(adap->dev,
+			"i2c-algo-8xx.o: 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_8xx_del_bus(struct i2c_adapter *adap)
+{
+	struct i2c_algo_8xx_data *cpm = adap->algo_data;
+
+	i2c_del_adapter(adap);
+
+	return cpm_iic_shutdown(cpm);
+}
+
+EXPORT_SYMBOL(i2c_8xx_add_bus);
+EXPORT_SYMBOL(i2c_8xx_del_bus);
+
+MODULE_AUTHOR("Brad Parker <brad@heeltoe.com>");
+MODULE_DESCRIPTION("I2C-Bus MPC8XX algorithm");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index ece31d2..df29fb5 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -365,7 +365,7 @@ config I2C_PROSAVAGE
 
 config I2C_RPXLITE
 	tristate "Embedded Planet RPX Lite/Classic support"
-	depends on (RPXLITE || RPXCLASSIC) && I2C
+	depends on (RPXLITE || RPXCLASSIC || MPC86XADS || MPC885ADS) && I2C
 	select I2C_ALGO8XX
 
 config I2C_S3C2410
diff --git a/drivers/i2c/busses/i2c-rpx.c b/drivers/i2c/busses/i2c-rpx.c
index 8764df0..97cbd2c 100644
--- a/drivers/i2c/busses/i2c-rpx.c
+++ b/drivers/i2c/busses/i2c-rpx.c
@@ -5,6 +5,9 @@
  * moved into proper i2c interface;
  * Brad Parker (brad@heeltoe.com)
  *
+ * (C) 2007 Montavista Software, Inc.
+ * Vitaly Bordug <vitb@kernel.crashing.org>
+ *
  * 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 8xx board.  The console messages have been 
@@ -14,88 +17,142 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/io.h>
 #include <linux/stddef.h>
+#include <linux/platform_device.h>
 #include <linux/i2c.h>
 #include <linux/i2c-algo-8xx.h>
+#include <asm/of_device.h>
+#include <asm/of_platform.h>
 #include <asm/mpc8xx.h>
 #include <asm/commproc.h>
+#include <asm/fs_pd.h>
+
+
+struct m8xx_i2c {
+	char *base;
+	struct of_device *ofdev;
+	struct i2c_adapter adap;
+	struct i2c_algo_8xx_data *algo_8xx;
+};
 
+static struct i2c_algo_8xx_data rpx_data;
 
-static void
-rpx_iic_init(struct i2c_algo_8xx_data *data)
+static const struct i2c_adapter rpx_ops = {
+	.owner		= THIS_MODULE,
+	.name		= "i2c-rpx",
+	.id		= I2C_HW_MPC8XX_EPON,
+	.algo_data	= &rpx_data,
+};
+
+static int rpx_iic_init(struct m8xx_i2c *i2c)
 {
 	volatile cpm8xx_t *cp;
-	volatile immap_t *immap;
+	struct resource r;
+	struct i2c_algo_8xx_data *data = i2c->algo_8xx;
+	struct of_device *ofdev = i2c->ofdev;
+	struct device_node *np = ofdev->node;
+
+	/* Pointer to Communication Processor 
+	 */
+	cp = data->cp = (cpm8xx_t *)immr_map(im_cpm);
 
-	cp = cpmp;	/* Get pointer to Communication Processor */
-	immap = (immap_t *)IMAP_ADDR;	/* and to internal registers */
+	data->irq = irq_of_parse_and_map(np, 0);
+  	if (data->irq < 0)
+		return -EINVAL;
 
-	data->iip = (iic_t *)&cp->cp_dparam[PROFF_IIC];
+	if (of_address_to_resource(np, 1, &r))
+		return -EINVAL;
+
+	data->iip = ioremap(r.start, r.end - r.start + 1);
+	if (data->iip == NULL)
+		return -EINVAL;
 
 	/* Check for and use a microcode relocation patch.
-	*/
+	 */
 	if ((data->reloc = data->iip->iic_rpbase))
 		data->iip = (iic_t *)&cp->cp_dpmem[data->iip->iic_rpbase];
 		
-	data->i2c = (i2c8xx_t *)&(immap->im_i2c);
-	data->cp = cp;
+	if (of_address_to_resource(np, 0, &r))
+		return -EINVAL;
 
-	/* Initialize Port B IIC pins.
-	*/
-	cp->cp_pbpar |= 0x00000030;
-	cp->cp_pbdir |= 0x00000030;
-	cp->cp_pbodr |= 0x00000030;
+	data->i2c = ioremap(r.start, r.end - r.start + 1);
+	if (data->i2c == NULL)
+		return -EINVAL;
 
 	/* 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 = (i2c8xx_t *)&(((immap_t *)IMAP_ADDR)->im_i2c);
 }
 
-static int rpx_install_isr(int irq, void (*func)(void *), void *data)
+static int i2c_rpx_probe(struct of_device* ofdev, const struct of_device_id *match)
 {
-	/* install interrupt handler */
-	cpm_install_handler(irq, func, data);
+	int result;
+	struct m8xx_i2c *i2c;
 
-	return 0;
-}
+	if (!(i2c = kzalloc(sizeof(*i2c), GFP_KERNEL))) {
+		return -ENOMEM;
+	}
+	i2c->ofdev = ofdev;
+	i2c->algo_8xx = &rpx_data;
 
-static struct i2c_algo_8xx_data rpx_data = {
-	.setisr = rpx_install_isr
-};
+	rpx_iic_init(i2c);
 
-static struct i2c_adapter rpx_ops = {
-	.owner		= THIS_MODULE,
-	.name		= "m8xx",
-	.id		= I2C_HW_MPC8XX_EPON,
-	.algo_data	= &rpx_data,
-};
-
-int __init i2c_rpx_init(void)
-{
-	printk(KERN_INFO "i2c-rpx: i2c MPC8xx driver\n");
+	dev_set_drvdata(&ofdev->dev, i2c);
 
-	/* reset hardware to sane state */
-	rpx_iic_init(&rpx_data);
+	i2c->adap = rpx_ops;
+	i2c_set_adapdata(&i2c->adap, i2c);
+	i2c->adap.dev.parent = &ofdev->dev;
 
-	if (i2c_8xx_add_bus(&rpx_ops) < 0) {
+	if ((result = i2c_8xx_add_bus(&i2c->adap) < 0)) {
 		printk(KERN_ERR "i2c-rpx: Unable to register with I2C\n");
-		return -ENODEV;
+		kfree(i2c);
 	}
 
+	return result;
+}
+
+static int i2c_rpx_remove(struct of_device* ofdev)
+{
+	struct m8xx_i2c *i2c = dev_get_drvdata(&ofdev->dev);
+
+	i2c_8xx_del_bus(&i2c->adap);
+	dev_set_drvdata(&ofdev->dev, NULL);
+
+	kfree(i2c);
 	return 0;
 }
 
-void __exit i2c_rpx_exit(void)
+static struct of_device_id i2c_rpx_match[] = {
+	{
+		.type = "i2c",
+		.compatible = "fsl,i2c-cpm",
+	},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, i2c_rpx_match);
+
+static struct of_platform_driver i2c_rpx_driver = {
+	.name		= "fsl-i2c-cpm",
+	.match_table	= i2c_rpx_match,
+	.probe		= i2c_rpx_probe,
+	.remove		= i2c_rpx_remove,
+};
+
+static int __init i2c_rpx_init(void)
 {
-	i2c_8xx_del_bus(&rpx_ops);
+	return of_register_platform_driver(&i2c_rpx_driver);
 }
 
-MODULE_AUTHOR("Dan Malek <dmalek@jlc.net>");
-MODULE_DESCRIPTION("I2C-Bus adapter routines for MPC8xx boards");
+static void __exit i2c_rpx_exit(void)
+{
+	of_unregister_platform_driver(&i2c_rpx_driver);
+}
 
 module_init(i2c_rpx_init);
 module_exit(i2c_rpx_exit);
+
+MODULE_AUTHOR("Dan Malek <dmalek@jlc.net>");
+MODULE_DESCRIPTION("I2C-Bus adapter routines for MPC8xx boards");
diff --git a/include/linux/i2c-algo-8xx.h b/include/linux/i2c-algo-8xx.h
new file mode 100644
index 0000000..c63a739
--- /dev/null
+++ b/include/linux/i2c-algo-8xx.h
@@ -0,0 +1,29 @@
+/* ------------------------------------------------------------------------- */
+/* i2c-algo-8xx.h i2c driver algorithms for MPX8XX CPM			     */
+/* ------------------------------------------------------------------------- */
+
+#ifndef I2C_ALGO_8XX_H
+#define I2C_ALGO_8XX_H
+
+#include <linux/i2c.h>
+#include <asm/8xx_immap.h>
+#include <asm/commproc.h>
+
+#define CPM_MAX_READ    513
+
+struct i2c_algo_8xx_data {
+	uint dp_addr;
+	int reloc;
+	int irq;
+	i2c8xx_t *i2c;
+	iic_t *iip;
+	cpm8xx_t *cp;
+
+	u_char temp[CPM_MAX_READ];
+};
+
+extern int i2c_8xx_add_bus(struct i2c_adapter *);
+extern int i2c_8xx_del_bus(struct i2c_adapter *);
+
+#endif /* I2C_ALGO_8XX_H */
+

^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH] [POWERPC] i2c: adds support for i2c bus on 8xx
@ 2007-05-08  6:31 ` Vitaly Bordug
  0 siblings, 0 replies; 3+ messages in thread
From: Vitaly Bordug @ 2007-05-08  6:31 UTC (permalink / raw)
  To: Jean Delvare; +Cc: linuxppc-dev, linux-kernel


Utilized devicetree to store I2C data, ported i2c-algo-8xx.c from 2.4
approach(which remains nearly intact), refined i2c-rpx.c. I2C functionality
has been validated on mpc885ads with EEPROM access.

Interface has been reworked to of_device as well as other feedback
addressed. Repeated start ability is missing from the implementation.

Signed-off-by: Vitaly Bordug <vitb@kernel.crashing.org>

---

 arch/powerpc/platforms/8xx/mpc885ads_setup.c |   15 +
 arch/powerpc/sysdev/fsl_soc.c                |   15 +
 drivers/i2c/algos/Kconfig                    |    2 
 drivers/i2c/algos/Makefile                   |    1 
 drivers/i2c/algos/i2c-algo-8xx.c             |  520 ++++++++++++++++++++++++++
 drivers/i2c/busses/Kconfig                   |    2 
 drivers/i2c/busses/i2c-rpx.c                 |  143 +++++--
 include/linux/i2c-algo-8xx.h                 |   29 +
 8 files changed, 683 insertions(+), 44 deletions(-)

diff --git a/arch/powerpc/platforms/8xx/mpc885ads_setup.c b/arch/powerpc/platforms/8xx/mpc885ads_setup.c
index a57b577..40d12a2 100644
--- a/arch/powerpc/platforms/8xx/mpc885ads_setup.c
+++ b/arch/powerpc/platforms/8xx/mpc885ads_setup.c
@@ -51,6 +51,17 @@ static void init_smc1_uart_ioports(struct fs_uart_platform_info* fpi);
 static void init_smc2_uart_ioports(struct fs_uart_platform_info* fpi);
 static void init_scc3_ioports(struct fs_platform_info* ptr);
 
+#ifdef CONFIG_I2C_RPXLITE
+static void init_i2c_ioports()
+{
+	cpm8xx_t *cp = (cpm8xx_t *)immr_map(im_cpm);
+
+	setbits32(&cp->cp_pbpar, 0x00000030);
+	setbits32(&cp->cp_pbdir, 0x00000030);
+	setbits16(&cp->cp_pbodr, 0x0030);
+}
+#endif
+
 void __init mpc885ads_board_setup(void)
 {
 	cpm8xx_t *cp;
@@ -115,6 +126,10 @@ void __init mpc885ads_board_setup(void)
 	immr_unmap(io_port);
 
 #endif
+
+#ifdef CONFIG_I2C_RPXLITE
+	init_i2c_ioports();
+#endif
 }
 
 
diff --git a/arch/powerpc/sysdev/fsl_soc.c b/arch/powerpc/sysdev/fsl_soc.c
index 8a123c7..be60db7 100644
--- a/arch/powerpc/sysdev/fsl_soc.c
+++ b/arch/powerpc/sysdev/fsl_soc.c
@@ -33,6 +33,7 @@
 #include <asm/irq.h>
 #include <asm/time.h>
 #include <asm/prom.h>
+#include <asm/of_platform.h>
 #include <sysdev/fsl_soc.h>
 #include <mm/mmu_decl.h>
 #include <asm/cpm2.h>
@@ -1102,4 +1103,18 @@ err:
 
 arch_initcall(cpm_smc_uart_of_init);
 
+static int __init fsl_i2c_cpm_of_init(void)
+{
+	struct device_node *np = NULL;
+	/*
+	 * Register all the devices which type is "i2c-cpm"
+	 */
+	while ((np = of_find_compatible_node(np, "i2c", "fsl,i2c-cpm")) != NULL)
+		of_platform_device_create(np, "fsl-i2c-cpm", NULL);
+	return 0;
+}
+
+arch_initcall(fsl_i2c_cpm_of_init);
+
+
 #endif /* CONFIG_8xx */
diff --git a/drivers/i2c/algos/Kconfig b/drivers/i2c/algos/Kconfig
index af02034..11e37ff 100644
--- a/drivers/i2c/algos/Kconfig
+++ b/drivers/i2c/algos/Kconfig
@@ -41,6 +41,8 @@ config I2C_ALGOPCA
 config I2C_ALGO8XX
 	tristate "MPC8xx CPM I2C interface"
 	depends on 8xx && I2C
+	help
+	  8xx I2C Algorithm,supports the CPM I2C interface for mpc8xx CPUs.
 
 config I2C_ALGO_SGI
 	tristate "I2C SGI interfaces"
diff --git a/drivers/i2c/algos/Makefile b/drivers/i2c/algos/Makefile
index cac1051..1bd3b37 100644
--- a/drivers/i2c/algos/Makefile
+++ b/drivers/i2c/algos/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_ALGOBIT)	+= i2c-algo-bit.o
 obj-$(CONFIG_I2C_ALGOPCF)	+= i2c-algo-pcf.o
 obj-$(CONFIG_I2C_ALGOPCA)	+= i2c-algo-pca.o
 obj-$(CONFIG_I2C_ALGO_SGI)	+= i2c-algo-sgi.o
+obj-$(CONFIG_I2C_ALGO8XX)	+= i2c-algo-8xx.o
 
 ifeq ($(CONFIG_I2C_DEBUG_ALGO),y)
 EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/i2c/algos/i2c-algo-8xx.c b/drivers/i2c/algos/i2c-algo-8xx.c
new file mode 100644
index 0000000..01ffcee
--- /dev/null
+++ b/drivers/i2c/algos/i2c-algo-8xx.c
@@ -0,0 +1,520 @@
+/*
+ * i2c-algo-8xx.c i2x driver algorithms for MPC8XX CPM
+ * 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.
+ *
+ * moved into proper i2c interface; separated out platform specific
+ * parts into i2c-rpx.c
+ * Brad Parker (brad@heeltoe.com)
+ * 
+ * (C) 2007 Montavista Software, Inc.
+ * Vitaly Bordug <vitb@kernel.crashing.org>
+ */
+
+#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/i2c.h>
+#include <linux/i2c-algo-8xx.h>
+#include <asm/io.h>
+#include <asm/cacheflush.h>
+#include <asm/time.h>
+#include <asm/mpc8xx.h>
+
+/* Try to define this if you have an older CPU (earlier than rev D4) */
+#undef	I2C_CHIP_ERRATA
+
+static wait_queue_head_t iic_wait;
+static ushort r_tbase, r_rbase;
+
+static int cpm_debug;
+
+static irqreturn_t cpm_iic_interrupt(int irq, void *dev_id)
+{
+	i2c8xx_t *i2c = (i2c8xx_t *) dev_id;
+
+#if 0
+	/* Chip errata, clear enable. This is not needed on rev D4 CPUs */
+	/* This should probably be removed and replaced by I2C_CHIP_ERRATA stuff */
+	/* Someone with a buggy CPU needs to confirm that */
+	out_8(&i2c->i2c_i2mod, in_8(&i2c->i2c_i2mod) | ~1);
+#endif
+	/* Clear interrupt.
+	 */
+	out_8(&i2c->i2c_i2cer, 0xff);
+
+	/* Get 'me going again.
+	 */
+	wake_up_interruptible(&iic_wait);
+
+	return IRQ_HANDLED;
+}
+
+static int cpm_iic_init(struct i2c_adapter *adap)
+{
+	struct i2c_algo_8xx_data *cpm = adap->algo_data;
+	iic_t *iip = cpm->iip;
+	i2c8xx_t *i2c = cpm->i2c;
+	unsigned char brg;
+
+	if (cpm_debug)
+		dev_dbg(adap->dev, "cpm_iic_init()\n");
+
+	init_waitqueue_head(&iic_wait);
+
+	/* 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->dp_addr;
+	iip->iic_rbase = r_rbase = cpm->dp_addr + sizeof(cbd_t) * 2;
+
+	if (cpm_debug) {
+		dev_dbg(adap->dev, "iip %p, dp_addr 0x%x\n", cpm->iip,
+			cpm->dp_addr);
+		dev_dbg(adap->dev, "iic_tbase %d, r_tbase %d\n", iip->iic_tbase,
+			r_tbase);
+	}
+
+	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->reloc == 0) {
+		cpm8xx_t *cp = cpm->cp;
+		int res;
+
+		u16 v = mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_INIT_TRX) | CPM_CR_FLG;
+
+		out_be16(&cp->cp_cpcr, v);
+		res = wait_event_timeout(iic_wait,
+					 !(in_be16(&cp->cp_cpcr) & CPM_CR_FLG),
+					 HZ * 10);
+		if (!res)
+			return -EIO;
+
+	} 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.
+	 */
+	out_8(&i2c->i2c_i2add, 0xfe);
+
+	/* Make clock run at 60 kHz.
+	 */
+	brg = ppc_proc_freq / (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);
+
+	/* Install interrupt handler.
+	 */
+	request_irq(cpm->irq, cpm_iic_interrupt, 0, "8xx_i2c", i2c);
+
+	return 0;
+}
+
+static int cpm_iic_shutdown(struct i2c_algo_8xx_data *cpm)
+{
+	i2c8xx_t *i2c = cpm->i2c;
+
+	/* 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);
+
+	return (0);
+}
+
+static void cpm_reset_iic_params(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_adapter *adap)
+{
+	struct i2c_algo_8xx_data *cpm = adap->algo_data;
+	i2c8xx_t *i2c = cpm->i2c;
+	if (cpm->reloc == 0) {	/* micro code disabled */
+		cpm8xx_t *cp = cpm->cp;
+		u16 v =
+		    mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_CLOSE_RXBD) | CPM_CR_FLG;
+
+		if (cpm_debug)
+			printk("force_close()\n");
+
+		out_be16(&cp->cp_cpcr, v);
+		wait_event_timeout(iic_wait,
+				   !(in_be16(&cp->cp_cpcr) & CPM_CR_FLG),
+				   HZ * 5);
+	}
+	out_8(&i2c->i2c_i2cmr, 0x00);	/* Disable all interrupts */
+	out_8(&i2c->i2c_i2cer, 0xff);
+}
+
+/* Read from IIC...
+ * abyte = address byte, with r/w flag already set
+ */
+static int
+cpm_iic_read(struct i2c_adapter *adap, u_char abyte, char *buf, int count)
+{
+	struct i2c_algo_8xx_data *cpm = adap->algo_data;
+	iic_t *iip = cpm->iip;
+	i2c8xx_t *i2c = cpm->i2c;
+	cbd_t *tbdf, *rbdf;
+	u_char *tb;
+	int res = 0;
+
+	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 *) cpm_dpram_addr(iip->iic_tbase);
+	rbdf = (cbd_t *) cpm_dpram_addr(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)
+		dev_dbg(adap->dev, "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;
+
+	/* 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 */
+	out_8(&i2c->i2c_i2com, in_8(&i2c->i2c_i2com) | 0x80);	/* Begin transmission */
+
+	/* Wait for IIC transfer */
+	res = wait_event_interruptible_timeout(iic_wait, 0, 1 * HZ);
+
+	if (res < 0) {
+		force_close(adap);
+		if (cpm_debug)
+			dev_dbg(adap->dev, "IIC read: timeout!\n");
+		return -EIO;
+	}
+#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
+	if (cpm_debug) {
+		dev_dbg(adap->dev, "tx sc %04x, rx sc %04x\n",
+			tbdf->cbd_sc, rbdf->cbd_sc);
+	}
+
+	if (tbdf->cbd_sc & BD_SC_READY) {
+		dev_dbg(adap->dev, "IIC read; complete but tbuf ready\n");
+		force_close(adap);
+		dev_dbg(adap->dev, "tx sc %04x, rx sc %04x\n",
+			tbdf->cbd_sc, rbdf->cbd_sc);
+	}
+
+	if (tbdf->cbd_sc & BD_SC_NAK) {
+		if (cpm_debug)
+			dev_dbg(adap->dev, "IIC read; no ack\n");
+		return -EREMOTEIO;
+	}
+
+	if (rbdf->cbd_sc & BD_SC_EMPTY) {
+		/* force_close(adap); */
+		if (cpm_debug) {
+			dev_dbg(adap->dev,
+				"IIC read; complete but rbuf empty\n");
+			dev_dbg(adap->dev, "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)
+			dev_dbg(adap->dev, "IIC read; Overrun\n");
+		return -EREMOTEIO;
+	}
+
+	if (cpm_debug)
+		dev_dbg(adap->dev, "read %d bytes\n", rbdf->cbd_datlen);
+
+	if (rbdf->cbd_datlen < count) {
+		if (cpm_debug)
+			dev_dbg(adap->dev,
+				"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_adapter *adap, u_char abyte, char *buf, int count)
+{
+	struct i2c_algo_8xx_data *cpm = adap->algo_data;
+	iic_t *iip = cpm->iip;
+	i2c8xx_t *i2c = cpm->i2c;
+	cbd_t *tbdf;
+	u_char *tb;
+	int res = 0;
+
+	/* 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)
+		dev_dbg(adap->dev, "cpm_iic_write(abyte=0x%x)\n", abyte);
+
+	/* set up 2 descriptors */
+
+	tbdf = (cbd_t *) cpm_dpram_addr(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 */
+	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 */
+	out_8(&i2c->i2c_i2com, in_8(&i2c->i2c_i2com) | 0x80);	/* Begin transmission */
+
+	/* Wait for IIC transfer */
+	res = wait_event_interruptible_timeout(iic_wait, 0, 1 * HZ);
+
+	if (res < 0) {
+		force_close(adap);
+		if (cpm_debug)
+			dev_dbg(adap->dev, "IIC write: timeout!\n");
+		return -EIO;
+	}
+#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
+	if (cpm_debug) {
+		dev_dbg(adap->dev, "tx0 sc %04x, tx1 sc %04x\n",
+			tbdf[0].cbd_sc, tbdf[1].cbd_sc);
+	}
+
+	if (tbdf->cbd_sc & BD_SC_NAK) {
+		if (cpm_debug)
+			dev_dbg(adap->dev, "IIC write; no ack\n");
+		return 0;
+	}
+
+	if (tbdf->cbd_sc & BD_SC_READY) {
+		if (cpm_debug)
+			dev_dbg(adap->dev,
+				"IIC write; complete but tbuf ready\n");
+		return 0;
+	}
+
+	return count;
+}
+
+static int cpm_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+	struct i2c_msg *pmsg;
+	int i, ret;
+	u_char addr;
+
+	for (i = 0; i < num; i++) {
+		pmsg = &msgs[i];
+
+		if (cpm_debug)
+			dev_dbg(adap->dev, "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_RD) {
+			/* read bytes into buffer */
+			ret = cpm_iic_read(adap, addr, pmsg->buf, pmsg->len);
+			if (cpm_debug)
+				dev_dbg(adap->dev,
+					"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)
+				dev_dbg(adap->dev, "i2c-algo-8xx.o: wrote %d\n",
+					ret);
+			if (ret < pmsg->len) {
+				return (ret < 0) ? ret : -EREMOTEIO;
+			}
+		}
+	}
+	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_8xx_add_bus(struct i2c_adapter *adap)
+{
+	int res;
+
+	if (cpm_debug)
+		dev_dbg(adap->dev,
+			"i2c-algo-8xx.o: 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_8xx_del_bus(struct i2c_adapter *adap)
+{
+	struct i2c_algo_8xx_data *cpm = adap->algo_data;
+
+	i2c_del_adapter(adap);
+
+	return cpm_iic_shutdown(cpm);
+}
+
+EXPORT_SYMBOL(i2c_8xx_add_bus);
+EXPORT_SYMBOL(i2c_8xx_del_bus);
+
+MODULE_AUTHOR("Brad Parker <brad@heeltoe.com>");
+MODULE_DESCRIPTION("I2C-Bus MPC8XX algorithm");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index ece31d2..df29fb5 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -365,7 +365,7 @@ config I2C_PROSAVAGE
 
 config I2C_RPXLITE
 	tristate "Embedded Planet RPX Lite/Classic support"
-	depends on (RPXLITE || RPXCLASSIC) && I2C
+	depends on (RPXLITE || RPXCLASSIC || MPC86XADS || MPC885ADS) && I2C
 	select I2C_ALGO8XX
 
 config I2C_S3C2410
diff --git a/drivers/i2c/busses/i2c-rpx.c b/drivers/i2c/busses/i2c-rpx.c
index 8764df0..97cbd2c 100644
--- a/drivers/i2c/busses/i2c-rpx.c
+++ b/drivers/i2c/busses/i2c-rpx.c
@@ -5,6 +5,9 @@
  * moved into proper i2c interface;
  * Brad Parker (brad@heeltoe.com)
  *
+ * (C) 2007 Montavista Software, Inc.
+ * Vitaly Bordug <vitb@kernel.crashing.org>
+ *
  * 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 8xx board.  The console messages have been 
@@ -14,88 +17,142 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/io.h>
 #include <linux/stddef.h>
+#include <linux/platform_device.h>
 #include <linux/i2c.h>
 #include <linux/i2c-algo-8xx.h>
+#include <asm/of_device.h>
+#include <asm/of_platform.h>
 #include <asm/mpc8xx.h>
 #include <asm/commproc.h>
+#include <asm/fs_pd.h>
+
+
+struct m8xx_i2c {
+	char *base;
+	struct of_device *ofdev;
+	struct i2c_adapter adap;
+	struct i2c_algo_8xx_data *algo_8xx;
+};
 
+static struct i2c_algo_8xx_data rpx_data;
 
-static void
-rpx_iic_init(struct i2c_algo_8xx_data *data)
+static const struct i2c_adapter rpx_ops = {
+	.owner		= THIS_MODULE,
+	.name		= "i2c-rpx",
+	.id		= I2C_HW_MPC8XX_EPON,
+	.algo_data	= &rpx_data,
+};
+
+static int rpx_iic_init(struct m8xx_i2c *i2c)
 {
 	volatile cpm8xx_t *cp;
-	volatile immap_t *immap;
+	struct resource r;
+	struct i2c_algo_8xx_data *data = i2c->algo_8xx;
+	struct of_device *ofdev = i2c->ofdev;
+	struct device_node *np = ofdev->node;
+
+	/* Pointer to Communication Processor 
+	 */
+	cp = data->cp = (cpm8xx_t *)immr_map(im_cpm);
 
-	cp = cpmp;	/* Get pointer to Communication Processor */
-	immap = (immap_t *)IMAP_ADDR;	/* and to internal registers */
+	data->irq = irq_of_parse_and_map(np, 0);
+  	if (data->irq < 0)
+		return -EINVAL;
 
-	data->iip = (iic_t *)&cp->cp_dparam[PROFF_IIC];
+	if (of_address_to_resource(np, 1, &r))
+		return -EINVAL;
+
+	data->iip = ioremap(r.start, r.end - r.start + 1);
+	if (data->iip == NULL)
+		return -EINVAL;
 
 	/* Check for and use a microcode relocation patch.
-	*/
+	 */
 	if ((data->reloc = data->iip->iic_rpbase))
 		data->iip = (iic_t *)&cp->cp_dpmem[data->iip->iic_rpbase];
 		
-	data->i2c = (i2c8xx_t *)&(immap->im_i2c);
-	data->cp = cp;
+	if (of_address_to_resource(np, 0, &r))
+		return -EINVAL;
 
-	/* Initialize Port B IIC pins.
-	*/
-	cp->cp_pbpar |= 0x00000030;
-	cp->cp_pbdir |= 0x00000030;
-	cp->cp_pbodr |= 0x00000030;
+	data->i2c = ioremap(r.start, r.end - r.start + 1);
+	if (data->i2c == NULL)
+		return -EINVAL;
 
 	/* 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 = (i2c8xx_t *)&(((immap_t *)IMAP_ADDR)->im_i2c);
 }
 
-static int rpx_install_isr(int irq, void (*func)(void *), void *data)
+static int i2c_rpx_probe(struct of_device* ofdev, const struct of_device_id *match)
 {
-	/* install interrupt handler */
-	cpm_install_handler(irq, func, data);
+	int result;
+	struct m8xx_i2c *i2c;
 
-	return 0;
-}
+	if (!(i2c = kzalloc(sizeof(*i2c), GFP_KERNEL))) {
+		return -ENOMEM;
+	}
+	i2c->ofdev = ofdev;
+	i2c->algo_8xx = &rpx_data;
 
-static struct i2c_algo_8xx_data rpx_data = {
-	.setisr = rpx_install_isr
-};
+	rpx_iic_init(i2c);
 
-static struct i2c_adapter rpx_ops = {
-	.owner		= THIS_MODULE,
-	.name		= "m8xx",
-	.id		= I2C_HW_MPC8XX_EPON,
-	.algo_data	= &rpx_data,
-};
-
-int __init i2c_rpx_init(void)
-{
-	printk(KERN_INFO "i2c-rpx: i2c MPC8xx driver\n");
+	dev_set_drvdata(&ofdev->dev, i2c);
 
-	/* reset hardware to sane state */
-	rpx_iic_init(&rpx_data);
+	i2c->adap = rpx_ops;
+	i2c_set_adapdata(&i2c->adap, i2c);
+	i2c->adap.dev.parent = &ofdev->dev;
 
-	if (i2c_8xx_add_bus(&rpx_ops) < 0) {
+	if ((result = i2c_8xx_add_bus(&i2c->adap) < 0)) {
 		printk(KERN_ERR "i2c-rpx: Unable to register with I2C\n");
-		return -ENODEV;
+		kfree(i2c);
 	}
 
+	return result;
+}
+
+static int i2c_rpx_remove(struct of_device* ofdev)
+{
+	struct m8xx_i2c *i2c = dev_get_drvdata(&ofdev->dev);
+
+	i2c_8xx_del_bus(&i2c->adap);
+	dev_set_drvdata(&ofdev->dev, NULL);
+
+	kfree(i2c);
 	return 0;
 }
 
-void __exit i2c_rpx_exit(void)
+static struct of_device_id i2c_rpx_match[] = {
+	{
+		.type = "i2c",
+		.compatible = "fsl,i2c-cpm",
+	},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, i2c_rpx_match);
+
+static struct of_platform_driver i2c_rpx_driver = {
+	.name		= "fsl-i2c-cpm",
+	.match_table	= i2c_rpx_match,
+	.probe		= i2c_rpx_probe,
+	.remove		= i2c_rpx_remove,
+};
+
+static int __init i2c_rpx_init(void)
 {
-	i2c_8xx_del_bus(&rpx_ops);
+	return of_register_platform_driver(&i2c_rpx_driver);
 }
 
-MODULE_AUTHOR("Dan Malek <dmalek@jlc.net>");
-MODULE_DESCRIPTION("I2C-Bus adapter routines for MPC8xx boards");
+static void __exit i2c_rpx_exit(void)
+{
+	of_unregister_platform_driver(&i2c_rpx_driver);
+}
 
 module_init(i2c_rpx_init);
 module_exit(i2c_rpx_exit);
+
+MODULE_AUTHOR("Dan Malek <dmalek@jlc.net>");
+MODULE_DESCRIPTION("I2C-Bus adapter routines for MPC8xx boards");
diff --git a/include/linux/i2c-algo-8xx.h b/include/linux/i2c-algo-8xx.h
new file mode 100644
index 0000000..c63a739
--- /dev/null
+++ b/include/linux/i2c-algo-8xx.h
@@ -0,0 +1,29 @@
+/* ------------------------------------------------------------------------- */
+/* i2c-algo-8xx.h i2c driver algorithms for MPX8XX CPM			     */
+/* ------------------------------------------------------------------------- */
+
+#ifndef I2C_ALGO_8XX_H
+#define I2C_ALGO_8XX_H
+
+#include <linux/i2c.h>
+#include <asm/8xx_immap.h>
+#include <asm/commproc.h>
+
+#define CPM_MAX_READ    513
+
+struct i2c_algo_8xx_data {
+	uint dp_addr;
+	int reloc;
+	int irq;
+	i2c8xx_t *i2c;
+	iic_t *iip;
+	cpm8xx_t *cp;
+
+	u_char temp[CPM_MAX_READ];
+};
+
+extern int i2c_8xx_add_bus(struct i2c_adapter *);
+extern int i2c_8xx_del_bus(struct i2c_adapter *);
+
+#endif /* I2C_ALGO_8XX_H */
+

^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2007-10-13 21:41 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-10-13 21:41 [PATCH] [POWERPC] i2c: adds support for i2c bus on 8xx Jochen Friedrich
  -- strict thread matches above, loose matches on Subject: below --
2007-05-08  6:31 Vitaly Bordug
2007-05-08  6:31 ` Vitaly Bordug

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.