Skip to contentSkip to conversation

i2c: adds support for i2c bus on 8xx

RFC PATCH In review Vitaly Bordug 6 participants 12 messages Apr 20, 2007 – Apr 26, 2007

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

RFC PATCH 1/1 i2c: adds support for i2c bus on 8xx

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.

---
Jean,

The patch below may have rough edges but I'd appreciate you to take a look. It adds I2C capabilities of PQ series (mpc8xx mostly) or, more correctly, takes them off from the 2.4 kernel and makes it work.

Validated with quilt tree residing at
http://khali.linux-fr.org/devel/linux-2.6/jdelvare-i2c/

 arch/powerpc/boot/dts/mpc885ads.dts          |    7 
 arch/powerpc/platforms/8xx/mpc885ads_setup.c |   14 +
 arch/powerpc/sysdev/fsl_soc.c                |   61 +++
 drivers/i2c/algos/Kconfig                    |    2 
 drivers/i2c/algos/Makefile                   |    1 
 drivers/i2c/algos/i2c-algo-8xx.c             |  622 ++++++++++++++++++++++++++
 drivers/i2c/busses/Kconfig                   |    4 
 drivers/i2c/busses/i2c-rpx.c                 |  129 ++++-
 include/linux/i2c-algo-8xx.h                 |   29 +
 9 files changed, 822 insertions(+), 47 deletions(-)
arch/powerpc/boot/dts/mpc885ads.dts +7 -0
index 19d2d79..90e047a 100644
@@ -188,6 +188,13 @@
 				interrupts = <1d 3>;
 				interrupt-parent = <930>;
 			};
+			i2c@860 {
+				device_type = "i2c";
+				compatible = "fsl-i2c-cpm";
+				reg = <860 20 3c80 30>;
+				interrupts = <10 3>;
+				interrupt-parent = <930>;
+			};
 		};
 	};
 };
arch/powerpc/platforms/8xx/mpc885ads_setup.c +14 -0
index 9bd81c7..d32e066 100644
@@ -51,6 +51,7 @@ static void init_smc1_uart_ioports(struc
 static void init_smc2_uart_ioports(struct fs_uart_platform_info* fpi);
 static void init_scc3_ioports(struct fs_platform_info* ptr);
 static void init_irda_ioports(void);
+static void init_i2c_ioports(void);
 
 void __init mpc885ads_board_setup(void)
 {
@@ -120,6 +121,10 @@ #endif
 #ifdef CONFIG_8XX_SIR
 	init_irda_ioports();
 #endif
+
+#ifdef CONFIG_I2C_RPXLITE
+	init_i2c_ioports();
+#endif
 }
 
 
@@ -361,6 +366,15 @@ static void init_irda_ioports()
 	immr_unmap(cp);
 }
 
+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);
+}
+
 int platform_device_skip(const char *model, int id)
 {
 #ifdef CONFIG_MPC8xx_SECOND_ETH_SCC3
arch/powerpc/sysdev/fsl_soc.c +60 -1

71 lines not on this page · raw message

drivers/i2c/algos/Kconfig +2 -0
index 5889907..7d7fb87 100644
@@ -37,6 +37,8 @@ config I2C_ALGOPCA
 config I2C_ALGO8XX
 	tristate "MPC8xx CPM I2C interface"
 	depends on 8xx
+	help
+	  8xx I2C Algorithm
 
 config I2C_ALGO_SGI
 	tristate "I2C SGI interfaces"
drivers/i2c/algos/Makefile +1 -0
index cac1051..1bd3b37 100644
@@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_ALGOBIT)	+= i2c-algo-bi
 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
drivers/i2c/algos/i2c-algo-8xx.c new file +622 -0

622 lines not on this page · raw message

drivers/i2c/busses/Kconfig +2 -2
index 4afc795..83d9aef 100644
@@ -385,8 +385,8 @@ config I2C_PROSAVAGE
 	  will be called i2c-prosavage.
 
 config I2C_RPXLITE
-	tristate "Embedded Planet RPX Lite/Classic support"
-	depends on RPXLITE || RPXCLASSIC
+	tristate "Embedded Planet RPX Lite/Classic and Freescale 86x/885 ads support"
+	depends on RPXLITE || RPXCLASSIC || MPC86XADS || MPC885ADS
 	select I2C_ALGO8XX
 
 config I2C_S3C2410
drivers/i2c/busses/i2c-rpx.c +85 -44

173 lines not on this page · raw message

include/linux/i2c-algo-8xx.h new file +29 -0
new file mode 100644
index 0000000..a644512
@@ -0,0 +1,29 @@
+/* ------------------------------------------------------------------------- */
+/* i2c-algo-8xx.h i2c driver algorithms for MPX8XX CPM			     */
+/* ------------------------------------------------------------------------- */
+
+/* $Id$ */
+
+#ifndef I2C_ALGO_8XX_H
+#define I2C_ALGO_8XX_H
+
+#include <linux/i2c.h>
+#include <asm/8xx_immap.h>
+#include <asm/commproc.h>
+
+struct i2c_algo_8xx_data {
+	uint dp_addr;
+	int reloc;
+	int irq;
+	i2c8xx_t *i2c;
+	iic_t	*iip;
+	cpm8xx_t *cp;
+
+	u_char	temp[513];
+};
+
+extern int i2c_8xx_add_bus(struct i2c_adapter *);
+extern int i2c_8xx_del_bus(struct i2c_adapter *);
+
+#endif /* I2C_ALGO_8XX_H */
+
Signature · 1 line
Sincerely, Vitaly
#1 lore raw

arch/powerpc/boot/dts/mpc885ads.dts line 193

Quoted 1 line
arch/powerpc/boot/dts/mpc885ads.dts +1 -0
+				compatible = "fsl-i2c-cpm";

"fsl,i2c-cpm" instead.

Segher

#2 lore raw
In reply to Vitaly Bordug
Quoted 11 lines · Vitaly Bordug

+ i2c_dev =
platform_device_register_simple("fsl-i2c-cpm", i, &r[0], 3);

arch/powerpc/sysdev/fsl_soc.c +9 -0
+               if (IS_ERR(i2c_dev)) {
+                       ret = PTR_ERR(i2c_dev);
+                       goto err;
+               }
+
+               ret =
+                   platform_device_add_data(i2c_dev, &i2c_data,
+                                            sizeof(struct
+                                                   
Quoted 2 lines

fsl_i2c_platform_data));

This exposes a period of time where the device is registerd, but the platform data is not available. It might work for the everything-in-the kernel model where the device is added before the driver is registered, but is inherently racy.

You need to do alloc / add_resources / add_data / add.

milton

#3 lore raw
In reply to Vitaly Bordug

Hi Vitaly,

Quoted 3 lines · Vitaly Bordug

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.

Thanks for working on this. I was about to kill i2c-rpx because it's broken for so long:
http://lists.lm-sensors.org/pipermail/i2c/2007-March/000970.html

Quoted 7 lines

---
Jean,

The patch below may have rough edges but I'd appreciate you to take a look. It adds I2C capabilities of PQ series (mpc8xx mostly) or, more correctly, takes them off from the 2.4 kernel and makes it work.

OK. I can't comment on the platform-specific part I am not familiar with, but here's a quick review of the rest.

Quoted 4 lines

Validated with quilt tree residing at
http://khali.linux-fr.org/devel/linux-2.6/jdelvare-i2c/

Quoted 10 lines
 arch/powerpc/boot/dts/mpc885ads.dts          |    7 
 arch/powerpc/platforms/8xx/mpc885ads_setup.c |   14 +
 arch/powerpc/sysdev/fsl_soc.c                |   61 +++
 drivers/i2c/algos/Kconfig                    |    2 
 drivers/i2c/algos/Makefile                   |    1 
 drivers/i2c/algos/i2c-algo-8xx.c             |  622 ++++++++++++++++++++++++++
 drivers/i2c/busses/Kconfig                   |    4 
 drivers/i2c/busses/i2c-rpx.c                 |  129 ++++-
 include/linux/i2c-algo-8xx.h                 |   29 +
 9 files changed, 822 insertions(+), 47 deletions(-)

I wonder what's the point of having a separate i2c algorithm driver. We don't expect any other driver than i2c-rpx to ever use it, do we? In that case, all the code should be added to i2c-rpx directly, this will makes things more simple and more efficient.

Quoted 43 lines
arch/powerpc/boot/dts/mpc885ads.dts +7 -0
index 19d2d79..90e047a 100644
@@ -188,6 +188,13 @@
 				interrupts = <1d 3>;
 				interrupt-parent = <930>;
 			};
+			i2c@860 {
+				device_type = "i2c";
+				compatible = "fsl-i2c-cpm";
+				reg = <860 20 3c80 30>;
+				interrupts = <10 3>;
+				interrupt-parent = <930>;
+			};
 		};
 	};
 };
arch/powerpc/platforms/8xx/mpc885ads_setup.c +5 -0
index 9bd81c7..d32e066 100644
@@ -51,6 +51,7 @@ static void init_smc1_uart_ioports(struc
 static void init_smc2_uart_ioports(struct fs_uart_platform_info* fpi);
 static void init_scc3_ioports(struct fs_platform_info* ptr);
 static void init_irda_ioports(void);
+static void init_i2c_ioports(void);
 
 void __init mpc885ads_board_setup(void)
 {
@@ -120,6 +121,10 @@ #endif
 #ifdef CONFIG_8XX_SIR
 	init_irda_ioports();
 #endif
+
+#ifdef CONFIG_I2C_RPXLITE
+	init_i2c_ioports();
+#endif
 }
 
 
@@ -361,6 +366,15 @@ static void init_irda_ioports()
 	immr_unmap(cp);

arch/powerpc/platforms/8xx/mpc885ads_setup.c static void init_irda_ioports() · line 376

Quoted 10 lines
arch/powerpc/platforms/8xx/mpc885ads_setup.c +8 -0
 }
 
+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);
+}

If !CONFIG_I2C_RPXLITE, you define a static function which you never use. This is inefficient, and gcc will complain.

drivers/i2c/algos/i2c-algo-8xx.c

Quoted 111 lines
arch/powerpc/platforms/8xx/mpc885ads_setup.c +1 -0
+
 int platform_device_skip(const char *model, int id)
 {
 #ifdef CONFIG_MPC8xx_SECOND_ETH_SCC3
arch/powerpc/sysdev/fsl_soc.c +60 -1
index 419b688..7ecd537 100644
@@ -331,7 +331,7 @@ static int __init fsl_i2c_of_init(void)
 	for (np = NULL, i = 0;
 	     (np = of_find_compatible_node(np, "i2c", "fsl-i2c")) != NULL;
 	     i++) {
-		struct resource r[2];
+		struct resource r[3];
 		struct fsl_i2c_platform_data i2c_data;
 		const unsigned char *flags = NULL;
 
@@ -1215,4 +1215,63 @@ err:
 
 arch_initcall(fs_irda_of_init);
 
+static const char *i2c_regs = "regs";
+static const char *i2c_pram = "pram";
+static const char *i2c_irq = "interrupt";
+
+static int __init fsl_i2c_cpm_of_init(void)
+{
+	struct device_node *np;
+	unsigned int i;
+	struct platform_device *i2c_dev;
+	int ret;
+
+	for (np = NULL, i = 0;
+	     (np = of_find_compatible_node(np, "i2c", "fsl-i2c-cpm")) != NULL;
+	     i++) {
+		struct resource r[3];
+		struct fsl_i2c_platform_data i2c_data;
+
+		memset(&r, 0, sizeof(r));
+		memset(&i2c_data, 0, sizeof(i2c_data));
+
+		ret = of_address_to_resource(np, 0, &r[0]);
+		if (ret)
+			goto err;
+		r[0].name = i2c_regs;
+
+		ret = of_address_to_resource(np, 1, &r[1]);
+		if (ret)
+			goto err;
+		r[1].name = i2c_pram;
+
+		r[2].start = r[2].end = irq_of_parse_and_map(np, 0);
+		r[2].flags = IORESOURCE_IRQ;
+		r[2].name = i2c_irq;
+
+		i2c_dev = platform_device_register_simple("fsl-i2c-cpm", i, &r[0], 3);
+		if (IS_ERR(i2c_dev)) {
+			ret = PTR_ERR(i2c_dev);
+			goto err;
+		}
+
+		ret =
+		    platform_device_add_data(i2c_dev, &i2c_data,
+					     sizeof(struct
+						    fsl_i2c_platform_data));
+		if (ret)
+			goto unreg;
+	}
+
+	return 0;
+
+unreg:
+	platform_device_unregister(i2c_dev);
+err:
+	return ret;
+}
+
+arch_initcall(fsl_i2c_cpm_of_init);
+
+
 #endif /* CONFIG_8xx */
drivers/i2c/algos/Kconfig +2 -0
index 5889907..7d7fb87 100644
@@ -37,6 +37,8 @@ config I2C_ALGOPCA
 config I2C_ALGO8XX
 	tristate "MPC8xx CPM I2C interface"
 	depends on 8xx
+	help
+	  8xx I2C Algorithm
 
 config I2C_ALGO_SGI
 	tristate "I2C SGI interfaces"
drivers/i2c/algos/Makefile +1 -0
index cac1051..1bd3b37 100644
@@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_ALGOBIT)	+= i2c-algo-bi
 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
drivers/i2c/algos/i2c-algo-8xx.c new file +0 -0
new file mode 100644
index 0000000..b1f414a

General comment for this file: all the printks need a level (KERN_*) and some prefix so that the user knows they come from this driver. Or you can switch to pr_info/pr_debug or even dev_info/dev_dbg.

Quoted 16 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +15 -0
@@ -0,0 +1,622 @@
+/*
+ * i2c-algo-8xx.c i2x driver algorithms for MPC8XX CPM
+ * Copyright (c) 1999 Dan Malek ([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

drivers/i2c/algos/i2c-algo-8xx.c line 25

Quoted 10 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +10 -0
+    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 ([email protected])
+ */
+
+
+/* $Id: i2c-algo-8xx.c,v 1.15 2004/11/20 08:02:24 khali Exp $ */

Drop this.

Quoted 8 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +8 -0
+
+#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>

drivers/i2c/algos/i2c-algo-8xx.c line 43

Quoted 10 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +10 -0
+#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>
+
+#define CPM_MAX_READ	513
+#undef	I2C_CHIP_ERRATA /* Try ot define this if you have an older CPU(earlier than rev D4) */

Broken indentation, line too long, and typo in the comment.

drivers/i2c/algos/i2c-algo-8xx.c line 48

Quoted 5 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +5 -0
+
+static wait_queue_head_t iic_wait;
+static ushort r_tbase, r_rbase;
+
+int cpm_debug = 0;

Should be static and not initialized explicitly.

drivers/i2c/algos/i2c-algo-8xx.c line 49

Quoted 1 line
drivers/i2c/algos/i2c-algo-8xx.c new file +1 -0
+int cpm_scan = 0;

Please drop this, same can be done in a generic (and safer) way from user-space using i2c-dev + i2cdetect.

Quoted 36 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +36 -0
+
+static irqreturn_t cpm_iic_interrupt(int irq, void *dev_id)
+{
+	i2c8xx_t *i2c = (i2c8xx_t *) dev_id;
+	if (cpm_debug > 1)
+		printk("cpm_iic_interrupt(dev_id=%p)\n", 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 void cpm_iic_init(struct i2c_algo_8xx_data *cpm)
+{
+	iic_t *iip = cpm->iip;
+	i2c8xx_t *i2c = cpm->i2c;
+	unsigned char brg;
+
+	if (cpm_debug)
+		printk(KERN_DEBUG "cpm_iic_init()\n");
+
+	/* Initialize the parameter ram.
+	 * We need to make sure many things are initialized to zero,
+	 * especially in the case of a microcode patch.
+	 */

drivers/i2c/algos/i2c-algo-8xx.c line 95

Quoted 10 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +10 -0
+	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;

Maybe a memset on the whole structure would be more efficient? Looks to me like you're zeroing almost all fields.

Quoted 16 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +16 -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) {
+		printk("iip %p, dp_addr 0x%x\n", cpm->iip, cpm->dp_addr);
+		printk("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.
+	 */

drivers/i2c/algos/i2c-algo-8xx.c line 121

Quoted 10 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +10 -0
+	iip->iic_mrblr = CPM_MAX_READ;
+
+	/* Initialize Tx/Rx parameters.
+	 */
+	if (cpm->reloc == 0) {
+		cpm8xx_t *cp = cpm->cp;
+		u16 v = mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_INIT_TRX) | CPM_CR_FLG;
+
+		out_be16(&cp->cp_cpcr, v);
+		while (in_be16(&cp->cp_cpcr) & CPM_CR_FLG) ;

This could block, you need to add some form of timeout.

drivers/i2c/algos/i2c-algo-8xx.c line 133

Quoted 12 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +12 -0
+	} 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.

kHz (small k)

drivers/i2c/algos/i2c-algo-8xx.c line 135

Quoted 2 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +2 -0
+	 */
+	brg = (unsigned char)(ppc_proc_freq / (32 * 2 * 60000) - 3);

Useless cast.

Quoted 136 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +136 -0
+	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);
+
+	init_waitqueue_head(&iic_wait);
+
+	/* Install interrupt handler.
+	 */
+	if (cpm_debug) {
+		printk("%s[%d] Install ISR for IRQ %d\n",
+		       __func__, __LINE__, CPMVEC_I2C);
+	}
+	request_irq(cpm->irq, cpm_iic_interrupt, 0, "8xx_i2c", i2c);
+}
+
+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_algo_8xx_data *cpm)
+{
+	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);
+		while (in_be16(&cp->cp_cpcr) & CPM_CR_FLG) ;
+	}
+	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_algo_8xx_data *cpm, u_char abyte, char *buf, int count)
+{
+	iic_t *iip = cpm->iip;
+	i2c8xx_t *i2c = cpm->i2c;
+	cbd_t *tbdf, *rbdf;
+	u_char *tb;
+	unsigned long tmo;
+	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)
+		printk("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;
+
+	if (count > 16) {
+		/* 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 */
+

drivers/i2c/algos/i2c-algo-8xx.c line 281

Quoted 10 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +10 -0
+		/* Wait for IIC transfer */
+		res = wait_event_interruptible_timeout(iic_wait, 0, 1 * HZ);
+	} else {		/* busy wait for small transfers, its faster */
+		out_8(&i2c->i2c_i2cmr, 0x00);	/* Disable I2C 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 */
+
+		tmo = jiffies + 1 * HZ;
+		while (!(in_8(&i2c->i2c_i2cer) & 0x11 || time_after(jiffies, tmo))) ;/* Busy wait, with a timeout */

This could result in a one-second busy loop, not very friendly for other drivers. It should sleep while waiting. Line too long, please fold.

Quoted 34 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +34 -0
+	}
+
+	if ( res < 0) {
+		force_close(cpm);
+		if (cpm_debug)
+			printk("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) {
+		printk("tx sc %04x, rx sc %04x\n", tbdf->cbd_sc, rbdf->cbd_sc);
+	}
+
+	if (tbdf->cbd_sc & BD_SC_READY) {
+		printk("IIC read; complete but tbuf ready\n");
+		force_close(cpm);
+		printk("tx sc %04x, rx sc %04x\n", tbdf->cbd_sc, rbdf->cbd_sc);
+	}
+
+	if (tbdf->cbd_sc & BD_SC_NAK) {
+		if (cpm_debug)
+			printk("IIC read; no ack\n");
+		return -EREMOTEIO;
+	}
+
+	if (rbdf->cbd_sc & BD_SC_EMPTY) {
+		/* force_close(cpm); */
+		if (cpm_debug) {
+			printk("IIC read; complete but rbuf empty\n");

drivers/i2c/algos/i2c-algo-8xx.c line 325

Quoted 10 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +10 -0
+			printk("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)
+			printk("IIC read; Overrun\n");
+		return -EREMOTEIO;;

Doubled semi-colon.

Quoted 35 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +35 -0
+	}
+
+	if (cpm_debug)
+		printk("read %d bytes\n", rbdf->cbd_datlen);
+
+	if (rbdf->cbd_datlen < count) {
+		if (cpm_debug)
+			printk("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_algo_8xx_data *cpm, u_char abyte, char *buf, int count)
+{
+	iic_t *iip = cpm->iip;
+	i2c8xx_t *i2c = cpm->i2c;
+	cbd_t *tbdf;
+	u_char *tb;
+	unsigned long tmo;
+	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 */

drivers/i2c/algos/i2c-algo-8xx.c line 370

Quoted 10 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +10 -0
+
+	flush_dcache_range((unsigned long)tb, (unsigned long)(tb + 1));
+	flush_dcache_range((unsigned long)buf, (unsigned long)(buf + count));
+
+	if (cpm_debug)
+		printk("cpm_iic_write(abyte=0x%x)\n", abyte);
+
+	/* set up 2 descriptors */
+
+

Doubled blank line.

Quoted 16 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +16 -0
+	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;
+
+	if (count > 16) {
+		/* 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 */

drivers/i2c/algos/i2c-algo-8xx.c line 396

Quoted 10 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +10 -0
+
+		/* Wait for IIC transfer */
+		res = wait_event_interruptible_timeout(iic_wait, 0, 1 * HZ);
+	} else {		/* busy wait for small transfers, its faster */
+		out_8(&i2c->i2c_i2cmr, 0x00);	/* Disable I2C 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 */
+		tmo = jiffies + 1 * HZ;
+		while (!(in_8(&i2c->i2c_i2cer) & 0x12 || time_after(jiffies, tmo))) ;/* Busy wait, with a timeout */

Same as above, should sleep, and line to long.

Quoted 111 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +111 -0
+	}
+
+	if ( res < 0) {
+		force_close(cpm);
+		if (cpm_debug)
+			printk("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) {
+		printk("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)
+			printk("IIC write; no ack\n");
+		return 0;
+	}
+
+	if (tbdf->cbd_sc & BD_SC_READY) {
+		if (cpm_debug)
+			printk("IIC write; complete but tbuf ready\n");
+		return 0;
+	}
+
+	return count;
+}
+
+/* See if an IIC address exists..
+ * addr = 7 bit address, unshifted
+ */
+static int cpm_iic_tryaddress(struct i2c_algo_8xx_data *cpm, int addr)
+{
+	iic_t *iip = cpm->iip;
+	i2c8xx_t *i2c = cpm->i2c;
+	cbd_t *tbdf, *rbdf;
+	u_char *tb;
+	unsigned long len;
+	int res = 0;
+
+	if (cpm_debug > 1)
+		printk("cpm_iic_tryaddress(cpm=%p,addr=%d)\n", cpm, addr);
+
+	/* check for and use a microcode relocation patch */
+	if (cpm->reloc) {
+		cpm_reset_iic_params(iip);
+	}
+
+	if (cpm_debug && addr == 0) {
+		printk("iip %p, dp_addr 0x%x\n", cpm->iip, cpm->dp_addr);
+		printk("iic_tbase %d, r_tbase %d\n", iip->iic_tbase, r_tbase);
+	}
+
+	tbdf = (cbd_t *) cpm_dpram_addr(iip->iic_tbase);
+	rbdf = (cbd_t *) cpm_dpram_addr(iip->iic_rbase);
+
+	tb = cpm->temp;
+	tb = (u_char *) (((uint) tb + 15) & ~15);
+
+	/* do a simple read */
+	tb[0] = (addr << 1) | 1;	/* device address (+ read) */
+	len = 2;
+
+	flush_dcache_range((unsigned long)tb, (unsigned long)(tb + 2));
+
+	tbdf->cbd_bufaddr = __pa(tb);
+	tbdf->cbd_datlen = len;
+	tbdf->cbd_sc = BD_SC_READY | BD_SC_LAST | BD_SC_WRAP | BD_IIC_START;
+
+	rbdf->cbd_datlen = 0;
+	rbdf->cbd_bufaddr = __pa(tb + 2);
+	rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP | BD_SC_INTRPT;
+
+	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 */
+
+	if (cpm_debug > 1)
+		printk("about to sleep\n");
+
+	/* wait for IIC transfer */
+	res = wait_event_interruptible_timeout(iic_wait, 0, 1 * HZ);
+
+#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 ( res < 0) {
+		force_close(cpm);
+		if (cpm_debug)
+			printk("IIC tryaddress: timeout!\n");
+		return -EIO;
+	}
+
+	if (cpm_debug > 1)
+		printk("back from sleep\n");
+
+	if (tbdf->cbd_sc & BD_SC_NAK) {
+		if (cpm_debug > 1)
+			printk("IIC try; no ack\n");
+		return 0;

drivers/i2c/algos/i2c-algo-8xx.c line 517

Quoted 10 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +10 -0
+	}
+
+	if (tbdf->cbd_sc & BD_SC_READY) {
+		printk("IIC try; complete but tbuf ready\n");
+	}
+
+	return 1;
+}
+
+static int cpm_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)

Use *msgs instead of msgs[].

Quoted 13 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +13 -0
+{
+	struct i2c_algo_8xx_data *cpm = adap->algo_data;
+	struct i2c_msg *pmsg;
+	int i, ret;
+	u_char addr;
+
+	for (i = 0; i < num; i++) {
+		pmsg = &msgs[i];
+
+		if (cpm_debug)
+			printk("i2c-algo-8xx.o: "
+			       "#%d addr=0x%x flags=0x%x len=%d\n buf=%lx\n",
+			       i, pmsg->addr, pmsg->flags, pmsg->len,

drivers/i2c/algos/i2c-algo-8xx.c line 540

Quoted 10 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +10 -0
+			       (unsigned long)pmsg->buf);
+
+		addr = pmsg->addr << 1;
+		if (pmsg->flags & I2C_M_RD)
+			addr |= 1;
+		if (pmsg->flags & I2C_M_REV_DIR_ADDR)
+			addr ^= 1;
+
+		if (!(pmsg->flags & I2C_M_NOSTART)) {
+		}

Either something is missing here and you must add it, or this statement is useless and you should drop it.

Quoted 9 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +9 -0
+		if (pmsg->flags & I2C_M_RD) {
+			/* read bytes into buffer */
+			ret = cpm_iic_read(cpm, addr, pmsg->buf, pmsg->len);
+			if (cpm_debug)
+				printk("i2c-algo-8xx.o: read %d bytes\n", ret);
+			if (ret < pmsg->len) {
+				return (ret < 0) ? ret : -EREMOTEIO;
+			}
+		} else {

drivers/i2c/algos/i2c-algo-8xx.c line 559

Quoted 10 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +10 -0
+			/* write bytes from buffer */
+			ret = cpm_iic_write(cpm, addr, pmsg->buf, pmsg->len);
+			if (cpm_debug)
+				printk("i2c-algo-8xx.o: wrote %d\n", ret);
+			if (ret < pmsg->len) {
+				return (ret < 0) ? ret : -EREMOTEIO;
+			}
+		}
+	}
+	return (num);

Useless parentheses.

drivers/i2c/algos/i2c-algo-8xx.c line 560

Quoted 1 line
drivers/i2c/algos/i2c-algo-8xx.c new file +1 -0
+}

You do not appear to handle repeated start. I can tell because the code handles all messages the exact same way, be they the first, second or last message of a group. This means that you don't really implement the I2C protocol, but an approximation of it. It might be sufficient for some I2C chips, but others will break. Look in the specifications of your device for how this could be fixed.

drivers/i2c/algos/i2c-algo-8xx.c line 566

Quoted 6 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +6 -0
+
+static u32 cpm_func(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR |
+	    I2C_FUNC_PROTOCOL_MANGLING;
+}

I2C_FUNC_I2C is missing. I2C_FUNC_10BIT_ADDR isn't implemented so it shouldn't be advertised. I2C_FUNC_PROTOCOL_MANGLING should only be implemented if really needed, so I'd suggest that you drop it for now (together with I2C_M_REV_DIR_ADDR and I2C_M_NOSTART above).

Quoted 16 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +16 -0
+
+/* -----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)
+{
+	struct i2c_algo_8xx_data *cpm = adap->algo_data;
+	int i;
+

drivers/i2c/algos/i2c-algo-8xx.c line 592

Quoted 10 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +10 -0
+	if (cpm_debug)
+		printk("i2c-algo-8xx.o: hw routines for %s registered.\n",
+		       adap->name);
+
+	/* register new adapter to i2c module... */
+
+	adap->algo = &cpm_algo;
+
+	i2c_add_adapter(adap);
+	cpm_iic_init(cpm);

Should be in the reverse order, init first, register second.

drivers/i2c/algos/i2c-algo-8xx.c line 603

Quoted 11 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +11 -0
+
+	/* scan bus */
+	if (cpm_scan) {
+		printk(KERN_INFO " i2c-algo-8xx.o: scanning bus %s...\n",
+			adap->name);
+		for (i = 0; i < 128; i++) {
+			if (cpm_iic_tryaddress(cpm, i))
+				printk("(%02x)",i<<1);
+		}
+		printk("\n");
+	}

As explained above, please remove this.

drivers/i2c/algos/i2c-algo-8xx.c line 614

Quoted 11 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +11 -0
+
+	return 0;
+}
+
+int i2c_8xx_del_bus(struct i2c_adapter *adap)
+{
+	struct i2c_algo_8xx_data *cpm = adap->algo_data;
+
+	cpm_iic_shutdown(cpm);
+
+	return i2c_del_adapter(adap);

Should be in the reverse order, unregister first, shutdown second.

Quoted 13 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +8 -0
+}
+
+EXPORT_SYMBOL(i2c_8xx_add_bus);
+EXPORT_SYMBOL(i2c_8xx_del_bus);
+
+MODULE_AUTHOR("Brad Parker <[email protected]>");
+MODULE_DESCRIPTION("I2C-Bus MPC8XX algorithm");
+MODULE_LICENSE("GPL");
drivers/i2c/busses/Kconfig +0 -0
index 4afc795..83d9aef 100644

drivers/i2c/busses/Kconfig config I2C_PROSAVAGE · line 390

Quoted 8 lines
drivers/i2c/busses/Kconfig +2 -2
@@ -385,8 +385,8 @@ config I2C_PROSAVAGE
 	  will be called i2c-prosavage.
 
 config I2C_RPXLITE
-	tristate "Embedded Planet RPX Lite/Classic support"
-	depends on RPXLITE || RPXCLASSIC
+	tristate "Embedded Planet RPX Lite/Classic and Freescale 86x/885 ads support"
+	depends on RPXLITE || RPXCLASSIC || MPC86XADS || MPC885ADS
 	select I2C_ALGO8XX

Please add a help text.

Quoted 22 lines
drivers/i2c/busses/Kconfig +0 -0
 config I2C_S3C2410
drivers/i2c/busses/i2c-rpx.c +3 -2
index 8764df0..ebddd94 100644
@@ -14,88 +14,129 @@
 #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/mpc8xx.h>
 #include <asm/commproc.h>
+#include <asm/fs_pd.h>
 
 
-static void
-rpx_iic_init(struct i2c_algo_8xx_data *data)

drivers/i2c/busses/i2c-rpx.c line 36

Quoted 10 lines
drivers/i2c/busses/i2c-rpx.c +10 -0
+struct m8xx_i2c {
+	char *base;
+	struct device *dev;
+	struct i2c_adapter adap;
+	struct i2c_algo_8xx_data *algo_8xx;
+};
+
+static struct i2c_algo_8xx_data rpx_data;
+
+static struct i2c_adapter rpx_ops = {

Could be const?

drivers/i2c/busses/i2c-rpx.c line 38

Quoted 2 lines
drivers/i2c/busses/i2c-rpx.c +2 -0
+	.owner		= THIS_MODULE,
+	.name		= "m8xx",

Find a better name (e.g. "i2c-rpx").

drivers/i2c/busses/i2c-rpx.c line 50

Quoted 13 lines
drivers/i2c/busses/i2c-rpx.c +10 -1
+	.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 platform_device *pdev = to_platform_device(i2c->dev);
+
+	cp = data->cp = (cpm8xx_t *)immr_map(im_cpm);	/* pointer to Communication Processor */

Line too long.

drivers/i2c/busses/i2c-rpx.c line 53

Quoted 5 lines
drivers/i2c/busses/i2c-rpx.c +2 -2
-	cp = cpmp;	/* Get pointer to Communication Processor */
-	immap = (immap_t *)IMAP_ADDR;	/* and to internal registers */
+	data->irq = platform_get_irq_byname(pdev, "interrupt");
+  

No blank line between assignation and test please.

Quoted 36 lines
drivers/i2c/busses/i2c-rpx.c +11 -13
+	if (data->irq < 0)
+		return -EINVAL;
 
-	data->iip = (iic_t *)&cp->cp_dparam[PROFF_IIC];
+	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pram");
+	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;
-
-	/* Initialize Port B IIC pins.
-	*/
-	cp->cp_pbpar |= 0x00000030;
-	cp->cp_pbdir |= 0x00000030;
-	cp->cp_pbodr |= 0x00000030;
+	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
+	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);
 }

drivers/i2c/busses/i2c-rpx.c line 80

Quoted 10 lines
drivers/i2c/busses/i2c-rpx.c +2 -6
 
-static int rpx_install_isr(int irq, void (*func)(void *), void *data)
+static int i2c_rpx_probe(struct device *device)
 {
-	/* install interrupt handler */
-	cpm_install_handler(irq, func, data);
-
-	return 0;
-}
+	int result = 0;

Useless initialization.

drivers/i2c/busses/i2c-rpx.c line 87

Quoted 10 lines
drivers/i2c/busses/i2c-rpx.c +6 -3
+	struct m8xx_i2c *i2c;
+	struct platform_device *pdev = to_platform_device(device);
 
-static struct i2c_algo_8xx_data rpx_data = {
-	.setisr = rpx_install_isr
-};
+	if (!(i2c = kmalloc(sizeof(*i2c), GFP_KERNEL))) {
+		return -ENOMEM;
+	}
+	memset(i2c, 0, sizeof(*i2c));

Use kzalloc() instead.

Quoted 43 lines
drivers/i2c/busses/i2c-rpx.c +21 -13
+	i2c->dev = device;
+	i2c->algo_8xx = &rpx_data;
 
-static struct i2c_adapter rpx_ops = {
-	.owner		= THIS_MODULE,
-	.name		= "m8xx",
-	.id		= I2C_HW_MPC8XX_EPON,
-	.algo_data	= &rpx_data,
-};
+	rpx_iic_init(i2c);
 
-int __init i2c_rpx_init(void)
-{
-	printk(KERN_INFO "i2c-rpx: i2c MPC8xx driver\n");
+	dev_set_drvdata(device, 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 = &pdev->dev;
 
-	if (i2c_8xx_add_bus(&rpx_ops) < 0) {
+	if ((result = i2c_8xx_add_bus(&rpx_ops) < 0)) {
 		printk(KERN_ERR "i2c-rpx: Unable to register with I2C\n");
-		return -ENODEV;
+		kfree(i2c);
 	}
 
+	return result;
+}
+
+
+static int i2c_rpx_remove(struct device *device)
+{
+	struct m8xx_i2c *i2c = dev_get_drvdata(device);
+
+	i2c_8xx_del_bus(&i2c->adap);
+	dev_set_drvdata(device, NULL);
+
+	kfree(i2c);
 	return 0;
 }

drivers/i2c/busses/i2c-rpx.c line 126

Quoted 10 lines
drivers/i2c/busses/i2c-rpx.c +8 -1
 
-void __exit i2c_rpx_exit(void)
+
+/* Structure for a device driver */
+static struct device_driver i2c_rpx_driver = {
+	.name = "fsl-i2c-cpm",
+	.bus = &platform_bus_type,
+	.probe = i2c_rpx_probe,
+	.remove = i2c_rpx_remove,
+};

Why don't you declare it as a struct platform_driver, register it with platform_driver_register() and unregister it with platform_driver_unregister()?

Quoted 25 lines
drivers/i2c/busses/i2c-rpx.c +10 -3
+
+static int __init i2c_rpx_init(void)
 {
-	i2c_8xx_del_bus(&rpx_ops);
+	return driver_register(&i2c_rpx_driver);
 }
 
-MODULE_AUTHOR("Dan Malek <[email protected]>");
-MODULE_DESCRIPTION("I2C-Bus adapter routines for MPC8xx boards");
+static void __exit i2c_rpx_exit(void)
+{
+	driver_unregister(&i2c_rpx_driver);
+}
 
 module_init(i2c_rpx_init);
 module_exit(i2c_rpx_exit);
+
+MODULE_AUTHOR("Dan Malek <[email protected]>");
+MODULE_DESCRIPTION("I2C-Bus adapter routines for MPC8xx boards");
include/linux/i2c-algo-8xx.h new file +0 -0
new file mode 100644
index 0000000..a644512

include/linux/i2c-algo-8xx.h line 5

Quoted 5 lines
include/linux/i2c-algo-8xx.h new file +5 -0
@@ -0,0 +1,29 @@
+/* ------------------------------------------------------------------------- */
+/* i2c-algo-8xx.h i2c driver algorithms for MPX8XX CPM			     */
+/* ------------------------------------------------------------------------- */
+
+/* $Id$ */

Delete this.

Quoted 7 lines
include/linux/i2c-algo-8xx.h new file +7 -0
+
+#ifndef I2C_ALGO_8XX_H
+#define I2C_ALGO_8XX_H
+
+#include <linux/i2c.h>
+#include <asm/8xx_immap.h>
+#include <asm/commproc.h>

include/linux/i2c-algo-8xx.h line 22

Quoted 10 lines
include/linux/i2c-algo-8xx.h new file +10 -0
+
+struct i2c_algo_8xx_data {
+	uint dp_addr;
+	int reloc;
+	int irq;
+	i2c8xx_t *i2c;
+	iic_t	*iip;
+	cpm8xx_t *cp;
+
+	u_char	temp[513];

Shouldn't this be CPM_MAX_READ instead of a hard-coded value?

include/linux/i2c-algo-8xx.h line 23

Quoted 1 line
include/linux/i2c-algo-8xx.h new file +1 -0
+};

Alignment in this structure is inconsistent, some lines use tabs other use spaces. Please don't mix.

Quoted 6 lines
include/linux/i2c-algo-8xx.h new file +6 -0
+
+extern int i2c_8xx_add_bus(struct i2c_adapter *);
+extern int i2c_8xx_del_bus(struct i2c_adapter *);
+
+#endif /* I2C_ALGO_8XX_H */
+
Signature · 1 line
Jean Delvare
#4 lore raw
Quoted 12 lines · On Sat, 21 Apr 2007 09:57:07 +0200 Jean Delvare wrote:

Hi Vitaly,

Quoted 4 lines · Vitaly Bordug

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.

Thanks for working on this. I was about to kill i2c-rpx because it's broken for so long:
http://lists.lm-sensors.org/pipermail/i2c/2007-March/000970.html

Noticed this :)

Quoted 29 lines
Quoted 8 lines

---
Jean,

The patch below may have rough edges but I'd appreciate you to take a look. It adds I2C capabilities of PQ series (mpc8xx mostly) or, more correctly, takes them off from the 2.4 kernel and makes it work.

OK. I can't comment on the platform-specific part I am not familiar with, but here's a quick review of the rest.

Quoted 14 lines

Validated with quilt tree residing at
http://khali.linux-fr.org/devel/linux-2.6/jdelvare-i2c/

 arch/powerpc/boot/dts/mpc885ads.dts          |    7 
 arch/powerpc/platforms/8xx/mpc885ads_setup.c |   14 +
 arch/powerpc/sysdev/fsl_soc.c                |   61 +++
 drivers/i2c/algos/Kconfig                    |    2 
 drivers/i2c/algos/Makefile                   |    1 
 drivers/i2c/algos/i2c-algo-8xx.c             |  622
++++++++++++++++++++++++++
drivers/i2c/busses/Kconfig                   |    4
drivers/i2c/busses/i2c-rpx.c                 |  129 ++++-
include/linux/i2c-algo-8xx.h                 |   29 + 9 files
Quoted 1 line

changed, 822 insertions(+), 47 deletions(-)

Quoted 5 lines

I wonder what's the point of having a separate i2c algorithm driver. We don't expect any other driver than i2c-rpx to ever use it, do we? In that case, all the code should be added to i2c-rpx directly, this will makes things more simple and more efficient.

That is how it was back in 2.4 - if you see combine is a good move, I'm OK with it.
But what shouldn't be rpc then - basically rpx(lite) is 8xx-based target, so let's call it all mpc8xx then.

Quoted 45 lines
Quoted 45 lines
(quoted diff) +0 -0

b/arch/powerpc/boot/dts/mpc885ads.dts index 19d2d79..90e047a 100644

arch/powerpc/boot/dts/mpc885ads.dts +7 -0
@@ -188,6 +188,13 @@
 				interrupts = <1d 3>;
 				interrupt-parent = <930>;
 			};
+			i2c@860 {
+				device_type = "i2c";
+				compatible = "fsl-i2c-cpm";
+				reg = <860 20 3c80 30>;
+				interrupts = <10 3>;
+				interrupt-parent = <930>;
+			};
 		};
 	};
 };
(quoted diff) +0 -0

b/arch/powerpc/platforms/8xx/mpc885ads_setup.c index 9bd81c7..d32e066 100644 --- a/arch/powerpc/platforms/8xx/mpc885ads_setup.c +++ b/arch/powerpc/platforms/8xx/mpc885ads_setup.c @@ -51,6 +51,7 @@ static void init_smc1_uart_ioports(struc static void init_smc2_uart_ioports(struct fs_uart_platform_info* fpi); static void init_scc3_ioports(struct fs_platform_info* ptr); static void init_irda_ioports(void); +static void init_i2c_ioports(void);

void __init mpc885ads_board_setup(void)
{

arch/powerpc/platforms/8xx/mpc885ads_setup.c +4 -0
@@ -120,6 +121,10 @@ #endif
 #ifdef CONFIG_8XX_SIR
 	init_irda_ioports();
 #endif
+
+#ifdef CONFIG_I2C_RPXLITE
+	init_i2c_ioports();
+#endif
 }
 
 
@@ -361,6 +366,15 @@ static void init_irda_ioports()
 	immr_unmap(cp);
 }
 
Quoted 12 lines

arch/powerpc/platforms/8xx/mpc885ads_setup.c static void init_irda_ioports() · line 376

Quoted 8 lines
arch/powerpc/platforms/8xx/mpc885ads_setup.c +8 -0
+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);
+}

If !CONFIG_I2C_RPXLITE, you define a static function which you never use. This is inefficient, and gcc will complain.

yap, this one is board-specific anyway, will fix.

Quoted 108 lines
Quoted 108 lines
arch/powerpc/platforms/8xx/mpc885ads_setup.c +1 -0
+
 int platform_device_skip(const char *model, int id)
 {
 #ifdef CONFIG_MPC8xx_SECOND_ETH_SCC3
(quoted diff) +0 -0

b/arch/powerpc/sysdev/fsl_soc.c index 419b688..7ecd537 100644

arch/powerpc/sysdev/fsl_soc.c +32 -1
@@ -331,7 +331,7 @@ static int __init fsl_i2c_of_init(void)
 	for (np = NULL, i = 0;
 	     (np = of_find_compatible_node(np, "i2c",
 "fsl-i2c")) != NULL; i++) {
-		struct resource r[2];
+		struct resource r[3];
 		struct fsl_i2c_platform_data i2c_data;
 		const unsigned char *flags = NULL;
 
@@ -1215,4 +1215,63 @@ err:
 
 arch_initcall(fs_irda_of_init);
 
+static const char *i2c_regs = "regs";
+static const char *i2c_pram = "pram";
+static const char *i2c_irq = "interrupt";
+
+static int __init fsl_i2c_cpm_of_init(void)
+{
+	struct device_node *np;
+	unsigned int i;
+	struct platform_device *i2c_dev;
+	int ret;
+
+	for (np = NULL, i = 0;
+	     (np = of_find_compatible_node(np, "i2c",
 "fsl-i2c-cpm")) != NULL;
+	     i++) {
+		struct resource r[3];
+		struct fsl_i2c_platform_data i2c_data;
+
+		memset(&r, 0, sizeof(r));
+		memset(&i2c_data, 0, sizeof(i2c_data));
+
+		ret = of_address_to_resource(np, 0, &r[0]);
+		if (ret)
+			goto err;
+		r[0].name = i2c_regs;
+
+		ret = of_address_to_resource(np, 1, &r[1]);
+		if (ret)
+			goto err;
+		r[1].name = i2c_pram;
+
+		r[2].start = r[2].end = irq_of_parse_and_map(np,

0);

arch/powerpc/sysdev/fsl_soc.c +4 -0
+		r[2].flags = IORESOURCE_IRQ;
+		r[2].name = i2c_irq;
+
+		i2c_dev =

platform_device_register_simple("fsl-i2c-cpm", i, &r[0], 3);

arch/powerpc/sysdev/fsl_soc.c +9 -0
+		if (IS_ERR(i2c_dev)) {
+			ret = PTR_ERR(i2c_dev);
+			goto err;
+		}
+
+		ret =
+		    platform_device_add_data(i2c_dev, &i2c_data,
+					     sizeof(struct
+

fsl_i2c_platform_data));

arch/powerpc/sysdev/fsl_soc.c +15 -0
+		if (ret)
+			goto unreg;
+	}
+
+	return 0;
+
+unreg:
+	platform_device_unregister(i2c_dev);
+err:
+	return ret;
+}
+
+arch_initcall(fsl_i2c_cpm_of_init);
+
+
 #endif /* CONFIG_8xx */
drivers/i2c/algos/Kconfig +2 -0
index 5889907..7d7fb87 100644
@@ -37,6 +37,8 @@ config I2C_ALGOPCA
 config I2C_ALGO8XX
 	tristate "MPC8xx CPM I2C interface"
 	depends on 8xx
+	help
+	  8xx I2C Algorithm
 
 config I2C_ALGO_SGI
 	tristate "I2C SGI interfaces"
drivers/i2c/algos/Makefile +1 -0
index cac1051..1bd3b37 100644
@@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_ALGOBIT)	+= i2c-algo-bi
 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
 
Quoted 12 lines

drivers/i2c/algos/i2c-algo-8xx.c

Quoted 7 lines
drivers/i2c/algos/Makefile +0 -0
 ifeq ($(CONFIG_I2C_DEBUG_ALGO),y)
 EXTRA_CFLAGS += -DDEBUG
(quoted diff) +0 -0

b/drivers/i2c/algos/i2c-algo-8xx.c new file mode 100644 index 0000000..b1f414a

drivers/i2c/algos/i2c-algo-8xx.c new file +0 -0

General comment for this file: all the printks need a level (KERN_*) and some prefix so that the user knows they come from this driver. Or you can switch to pr_info/pr_debug or even dev_info/dev_dbg.

OK

Quoted 112 lines
Quoted 20 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +5 -0
@@ -0,0 +1,622 @@
+/*
+ * i2c-algo-8xx.c i2x driver algorithms for MPC8XX CPM
+ * Copyright (c) 1999 Dan Malek ([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

drivers/i2c/algos/i2c-algo-8xx.c new file +8 -0
+    (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

drivers/i2c/algos/i2c-algo-8xx.c line 25

Quoted 10 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +10 -0
+    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 ([email protected])
+ */
+
+
+/* $Id: i2c-algo-8xx.c,v 1.15 2004/11/20 08:02:24 khali Exp $ */

Drop this.

Quoted 18 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +18 -0
+
+#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>
+
+#define CPM_MAX_READ	513
+#undef	I2C_CHIP_ERRATA /* Try ot define this if you have an
Quoted 1 line

older CPU(earlier than rev D4) */

Broken indentation, line too long, and typo in the comment.

drivers/i2c/algos/i2c-algo-8xx.c line 48

Quoted 5 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +5 -0
+
+static wait_queue_head_t iic_wait;
+static ushort r_tbase, r_rbase;
+
+int cpm_debug = 0;

Should be static and not initialized explicitly.

Quoted 1 line

+int cpm_scan = 0;

Please drop this, same can be done in a generic (and safer) way from user-space using i2c-dev + i2cdetect.

Quoted 44 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +8 -0
+
+static irqreturn_t cpm_iic_interrupt(int irq, void *dev_id)
+{
+	i2c8xx_t *i2c = (i2c8xx_t *) dev_id;
+	if (cpm_debug > 1)
+		printk("cpm_iic_interrupt(dev_id=%p)\n", dev_id);
+#if 0
+	/* Chip errata, clear enable. This is not needed on rev D4

CPUs */

drivers/i2c/algos/i2c-algo-8xx.c new file +1 -0
+	/* This should probably be removed and replaced by

I2C_CHIP_ERRATA stuff */

drivers/i2c/algos/i2c-algo-8xx.c new file +25 -0
+	/* 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 void cpm_iic_init(struct i2c_algo_8xx_data *cpm)
+{
+	iic_t *iip = cpm->iip;
+	i2c8xx_t *i2c = cpm->i2c;
+	unsigned char brg;
+
+	if (cpm_debug)
+		printk(KERN_DEBUG "cpm_iic_init()\n");
+
+	/* Initialize the parameter ram.
+	 * We need to make sure many things are initialized to

zero,

drivers/i2c/algos/i2c-algo-8xx.c new file +7 -0
+	 * 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;
Quoted 9 lines

drivers/i2c/algos/i2c-algo-8xx.c line 95

Quoted 5 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +5 -0
+	iip->iic_tstate = 0;
+	iip->iic_tdp = 0;
+	iip->iic_tbptr = 0;
+	iip->iic_tbc = 0;
+	iip->iic_txtmp = 0;

Maybe a memset on the whole structure would be more efficient? Looks to me like you're zeroing almost all fields.

This can mangle with microcode patch, so if we'll do memset something could be messed.

Quoted 22 lines
Quoted 22 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +5 -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; +

drivers/i2c/algos/i2c-algo-8xx.c new file +2 -0
+	if (cpm_debug) {
+		printk("iip %p, dp_addr 0x%x\n", cpm->iip,

cpm->dp_addr);

drivers/i2c/algos/i2c-algo-8xx.c new file +1 -0
+		printk("iic_tbase %d, r_tbase %d\n",

iip->iic_tbase, r_tbase);

drivers/i2c/algos/i2c-algo-8xx.c new file +11 -0
+	}
+
+	iip->iic_tfcr = SMC_EB;
+	iip->iic_rfcr = SMC_EB;
+
+	/* Set maximum receive size.
+	 */
+	iip->iic_mrblr = CPM_MAX_READ;
+
+	/* Initialize Tx/Rx parameters.
+	 */
Quoted 9 lines

drivers/i2c/algos/i2c-algo-8xx.c

Quoted 6 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +3 -0
+	if (cpm->reloc == 0) {
+		cpm8xx_t *cp = cpm->cp;
+		u16 v = mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_INIT_TRX)

| CPM_CR_FLG; +

drivers/i2c/algos/i2c-algo-8xx.c new file +2 -0
+		out_be16(&cp->cp_cpcr, v);
+		while (in_be16(&cp->cp_cpcr) & CPM_CR_FLG) ;

This could block, you need to add some form of timeout.

OK. Here and below lies almost a copy of 2.4 code...

Quoted 176 lines

drivers/i2c/algos/i2c-algo-8xx.c line 133

Quoted 13 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +8 -0
+	} 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.

drivers/i2c/algos/i2c-algo-8xx.c new file +4 -0
+	 */
+	out_8(&i2c->i2c_i2add, 0xfe);
+
+	/* Make clock run at 60 KHz.

kHz (small k)

Quoted 3 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +2 -0
+	 */
+	brg = (unsigned char)(ppc_proc_freq / (32 * 2 * 60000) -

3);

Useless cast.

Quoted 154 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +19 -0
+	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);
+
+	init_waitqueue_head(&iic_wait);
+
+	/* Install interrupt handler.
+	 */
+	if (cpm_debug) {
+		printk("%s[%d] Install ISR for IRQ %d\n",
+		       __func__, __LINE__, CPMVEC_I2C);
+	}
+	request_irq(cpm->irq, cpm_iic_interrupt, 0, "8xx_i2c",

i2c); +}

drivers/i2c/algos/i2c-algo-8xx.c new file +37 -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) +

drivers/i2c/algos/i2c-algo-8xx.c new file +6 -0
+static void force_close(struct i2c_algo_8xx_data *cpm)
+{
+	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; +

drivers/i2c/algos/i2c-algo-8xx.c new file +7 -0
+		if (cpm_debug)
+			printk("force_close()\n");
+
+		out_be16(&cp->cp_cpcr, v);
+		while (in_be16(&cp->cp_cpcr) & CPM_CR_FLG) ;
+	}
+	out_8(&i2c->i2c_i2cmr, 0x00);	/* Disable all

interrupts */

drivers/i2c/algos/i2c-algo-8xx.c new file +8 -0
+	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_algo_8xx_data *cpm, u_char abyte, char

*buf, int count) +{

drivers/i2c/algos/i2c-algo-8xx.c new file +20 -0
+	iic_t *iip = cpm->iip;
+	i2c8xx_t *i2c = cpm->i2c;
+	cbd_t *tbdf, *rbdf;
+	u_char *tb;
+	unsigned long tmo;
+	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

drivers/i2c/algos/i2c-algo-8xx.c new file +1 -0
+	 * is just used for timing (and doesn't really have to

exist).

drivers/i2c/algos/i2c-algo-8xx.c new file +4 -0
+	 */
+	tb = cpm->temp;
+	tb = (u_char *) (((uint) tb + 15) & ~15);
+	tb[0] = abyte;		/* Device address byte w/rw

flag */ +

drivers/i2c/algos/i2c-algo-8xx.c new file +1 -0
+	flush_dcache_range((unsigned long)tb, (unsigned long)(tb +

1)); +

drivers/i2c/algos/i2c-algo-8xx.c new file +6 -0
+	if (cpm_debug)
+		printk("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; +

drivers/i2c/algos/i2c-algo-8xx.c new file +1 -0
+	iip->iic_mrblr = count + 1;	/* prevent excessive

read, +1

drivers/i2c/algos/i2c-algo-8xx.c new file +1 -0
+					   is needed otherwise

will the

drivers/i2c/algos/i2c-algo-8xx.c new file +1 -0
+					   RXB interrupt come too

early */ +

drivers/i2c/algos/i2c-algo-8xx.c new file +10 -0
+	/* 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;
+
+	if (count > 16) {
+		/* Chip bug, set enable here */
+		out_8(&i2c->i2c_i2cmr, 0x13);	/* Enable

some interupts */

drivers/i2c/algos/i2c-algo-8xx.c new file +2 -0
+		out_8(&i2c->i2c_i2cer, 0xff);
+		out_8(&i2c->i2c_i2mod, in_8(&i2c->i2c_i2mod) |

1); /* Enable */

drivers/i2c/algos/i2c-algo-8xx.c new file +1 -0
+		out_8(&i2c->i2c_i2com, in_8(&i2c->i2c_i2com) |

0x80); /* Begin transmission */ +

drivers/i2c/algos/i2c-algo-8xx.c new file +2 -0
+		/* Wait for IIC transfer */
+		res = wait_event_interruptible_timeout(iic_wait,

0, 1 * HZ);

drivers/i2c/algos/i2c-algo-8xx.c new file +1 -0
+	} else {		/* busy wait for small transfers,

its faster */

drivers/i2c/algos/i2c-algo-8xx.c new file +1 -0
+		out_8(&i2c->i2c_i2cmr, 0x00);	/* Disable

I2C interupts */

drivers/i2c/algos/i2c-algo-8xx.c new file +2 -0
+		out_8(&i2c->i2c_i2cer, 0xff);
+		out_8(&i2c->i2c_i2mod, in_8(&i2c->i2c_i2mod) |

1); /* Enable */

drivers/i2c/algos/i2c-algo-8xx.c new file +1 -0
+		out_8(&i2c->i2c_i2com, in_8(&i2c->i2c_i2com) |
Quoted 9 lines
Quoted 4 lines

0x80); /* Begin transmission */ +

drivers/i2c/algos/i2c-algo-8xx.c new file +2 -0
+		tmo = jiffies + 1 * HZ;
+		while (!(in_8(&i2c->i2c_i2cer) & 0x11 ||

time_after(jiffies, tmo))) ;/* Busy wait, with a timeout */

This could result in a one-second busy loop, not very friendly for other drivers. It should sleep while waiting. Line too long, please fold.

Can you please elaborate a little here (or just point to the similar code)? I assume we should not block here, handling timeout in a waitqueue...

Quoted 294 lines
Quoted 39 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +10 -0
+	}
+
+	if ( res < 0) {
+		force_close(cpm);
+		if (cpm_debug)
+			printk("IIC read: timeout!\n");
+		return -EIO;
+	}
+#ifdef I2C_CHIP_ERRATA
+	/* Chip errata, clear enable. This is not needed on rev D4

CPUs.

drivers/i2c/algos/i2c-algo-8xx.c new file +1 -0
+	   Disabling I2C too early may cause too short stop

condition */

drivers/i2c/algos/i2c-algo-8xx.c new file +5 -0
+	udelay(4);
+	out_8(&i2c->i2c_i2mod, in_8(&i2c->i2c_i2mod) | ~1);
+#endif
+	if (cpm_debug) {
+		printk("tx sc %04x, rx sc %04x\n", tbdf->cbd_sc,

rbdf->cbd_sc);

drivers/i2c/algos/i2c-algo-8xx.c new file +6 -0
+	}
+
+	if (tbdf->cbd_sc & BD_SC_READY) {
+		printk("IIC read; complete but tbuf ready\n");
+		force_close(cpm);
+		printk("tx sc %04x, rx sc %04x\n", tbdf->cbd_sc,

rbdf->cbd_sc);

drivers/i2c/algos/i2c-algo-8xx.c new file +12 -0
+	}
+
+	if (tbdf->cbd_sc & BD_SC_NAK) {
+		if (cpm_debug)
+			printk("IIC read; no ack\n");
+		return -EREMOTEIO;
+	}
+
+	if (rbdf->cbd_sc & BD_SC_EMPTY) {
+		/* force_close(cpm); */
+		if (cpm_debug) {
+			printk("IIC read; complete but rbuf

empty\n");

drivers/i2c/algos/i2c-algo-8xx.c line 325

Quoted 10 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +10 -0
+			printk("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)
+			printk("IIC read; Overrun\n");
+		return -EREMOTEIO;;

Doubled semi-colon.

Quoted 39 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +8 -0
+	}
+
+	if (cpm_debug)
+		printk("read %d bytes\n", rbdf->cbd_datlen);
+
+	if (rbdf->cbd_datlen < count) {
+		if (cpm_debug)
+			printk("IIC read; short, wanted %d got

%d\n",

drivers/i2c/algos/i2c-algo-8xx.c new file +12 -0
+			       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_algo_8xx_data *cpm, u_char abyte, char

*buf, int count) +{

drivers/i2c/algos/i2c-algo-8xx.c new file +14 -0
+	iic_t *iip = cpm->iip;
+	i2c8xx_t *i2c = cpm->i2c;
+	cbd_t *tbdf;
+	u_char *tb;
+	unsigned long tmo;
+	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 */ +

drivers/i2c/algos/i2c-algo-8xx.c new file +1 -0
+	flush_dcache_range((unsigned long)tb, (unsigned long)(tb +

1));

drivers/i2c/algos/i2c-algo-8xx.c line 370

Quoted 8 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +8 -0
+	flush_dcache_range((unsigned long)buf, (unsigned long)(buf
+ count)); +
+	if (cpm_debug)
+		printk("cpm_iic_write(abyte=0x%x)\n", abyte);
+
+	/* set up 2 descriptors */
+
+

Doubled blank line.

Quoted 33 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +9 -0
+	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; +

drivers/i2c/algos/i2c-algo-8xx.c new file +3 -0
+	if (count > 16) {
+		/* Chip bug, set enable here */
+		out_8(&i2c->i2c_i2cmr, 0x13);	/* Enable

some interupts */

drivers/i2c/algos/i2c-algo-8xx.c new file +2 -0
+		out_8(&i2c->i2c_i2cer, 0xff);
+		out_8(&i2c->i2c_i2mod, in_8(&i2c->i2c_i2mod) |

1); /* Enable */

drivers/i2c/algos/i2c-algo-8xx.c new file +1 -0
+		out_8(&i2c->i2c_i2com, in_8(&i2c->i2c_i2com) |

0x80); /* Begin transmission */ +

drivers/i2c/algos/i2c-algo-8xx.c new file +2 -0
+		/* Wait for IIC transfer */
+		res = wait_event_interruptible_timeout(iic_wait,

0, 1 * HZ);

drivers/i2c/algos/i2c-algo-8xx.c new file +1 -0
+	} else {		/* busy wait for small transfers,

its faster */

drivers/i2c/algos/i2c-algo-8xx.c new file +1 -0
+		out_8(&i2c->i2c_i2cmr, 0x00);	/* Disable

I2C interupts */

drivers/i2c/algos/i2c-algo-8xx.c new file +2 -0
+		out_8(&i2c->i2c_i2cer, 0xff);
+		out_8(&i2c->i2c_i2mod, in_8(&i2c->i2c_i2mod) |

1); /* Enable */

drivers/i2c/algos/i2c-algo-8xx.c new file +1 -0
+		out_8(&i2c->i2c_i2com, in_8(&i2c->i2c_i2com) |

0x80); /* Begin transmission */

drivers/i2c/algos/i2c-algo-8xx.c new file +2 -0
+		tmo = jiffies + 1 * HZ;
+		while (!(in_8(&i2c->i2c_i2cer) & 0x12 ||
Quoted 1 line

time_after(jiffies, tmo))) ;/* Busy wait, with a timeout */

Same as above, should sleep, and line to long.

Quoted 131 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +10 -0
+	}
+
+	if ( res < 0) {
+		force_close(cpm);
+		if (cpm_debug)
+			printk("IIC write: timeout!\n");
+		return -EIO;
+	}
+#ifdef I2C_CHIP_ERRATA
+	/* Chip errata, clear enable. This is not needed on rev D4

CPUs.

drivers/i2c/algos/i2c-algo-8xx.c new file +1 -0
+	   Disabling I2C too early may cause too short stop

condition */

drivers/i2c/algos/i2c-algo-8xx.c new file +17 -0
+	udelay(4);
+	out_8(&i2c->i2c_i2mod, in_8(&i2c->i2c_i2mod) | ~1);
+#endif
+	if (cpm_debug) {
+		printk("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)
+			printk("IIC write; no ack\n");
+		return 0;
+	}
+
+	if (tbdf->cbd_sc & BD_SC_READY) {
+		if (cpm_debug)
+			printk("IIC write; complete but tbuf

ready\n");

drivers/i2c/algos/i2c-algo-8xx.c new file +10 -0
+		return 0;
+	}
+
+	return count;
+}
+
+/* See if an IIC address exists..
+ * addr = 7 bit address, unshifted
+ */
+static int cpm_iic_tryaddress(struct i2c_algo_8xx_data *cpm, int

addr) +{

drivers/i2c/algos/i2c-algo-8xx.c new file +9 -0
+	iic_t *iip = cpm->iip;
+	i2c8xx_t *i2c = cpm->i2c;
+	cbd_t *tbdf, *rbdf;
+	u_char *tb;
+	unsigned long len;
+	int res = 0;
+
+	if (cpm_debug > 1)
+		printk("cpm_iic_tryaddress(cpm=%p,addr=%d)\n",

cpm, addr); +

drivers/i2c/algos/i2c-algo-8xx.c new file +7 -0
+	/* check for and use a microcode relocation patch */
+	if (cpm->reloc) {
+		cpm_reset_iic_params(iip);
+	}
+
+	if (cpm_debug && addr == 0) {
+		printk("iip %p, dp_addr 0x%x\n", cpm->iip,

cpm->dp_addr);

drivers/i2c/algos/i2c-algo-8xx.c new file +1 -0
+		printk("iic_tbase %d, r_tbase %d\n",

iip->iic_tbase, r_tbase);

drivers/i2c/algos/i2c-algo-8xx.c new file +10 -0
+	}
+
+	tbdf = (cbd_t *) cpm_dpram_addr(iip->iic_tbase);
+	rbdf = (cbd_t *) cpm_dpram_addr(iip->iic_rbase);
+
+	tb = cpm->temp;
+	tb = (u_char *) (((uint) tb + 15) & ~15);
+
+	/* do a simple read */
+	tb[0] = (addr << 1) | 1;	/* device address (+ read)

*/

drivers/i2c/algos/i2c-algo-8xx.c new file +3 -0
+	len = 2;
+
+	flush_dcache_range((unsigned long)tb, (unsigned long)(tb +

2)); +

drivers/i2c/algos/i2c-algo-8xx.c new file +3 -0
+	tbdf->cbd_bufaddr = __pa(tb);
+	tbdf->cbd_datlen = len;
+	tbdf->cbd_sc = BD_SC_READY | BD_SC_LAST | BD_SC_WRAP |

BD_IIC_START; +

drivers/i2c/algos/i2c-algo-8xx.c new file +5 -0
+	rbdf->cbd_datlen = 0;
+	rbdf->cbd_bufaddr = __pa(tb + 2);
+	rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP | BD_SC_INTRPT;
+
+	out_8(&i2c->i2c_i2cmr, 0x13);	/* Enable some

interupts */

drivers/i2c/algos/i2c-algo-8xx.c new file +2 -0
+	out_8(&i2c->i2c_i2cer, 0xff);
+	out_8(&i2c->i2c_i2mod, in_8(&i2c->i2c_i2mod) |

1); /* Enable */

drivers/i2c/algos/i2c-algo-8xx.c new file +1 -0
+	out_8(&i2c->i2c_i2com, in_8(&i2c->i2c_i2com) |

0x80); /* Begin transmission */ +

drivers/i2c/algos/i2c-algo-8xx.c new file +5 -0
+	if (cpm_debug > 1)
+		printk("about to sleep\n");
+
+	/* wait for IIC transfer */
+	res = wait_event_interruptible_timeout(iic_wait, 0, 1 *

HZ); +

drivers/i2c/algos/i2c-algo-8xx.c new file +2 -0
+#ifdef I2C_CHIP_ERRATA
+	/* Chip errata, clear enable. This is not needed on rev D4

CPUs.

drivers/i2c/algos/i2c-algo-8xx.c new file +1 -0
+	   Disabling I2C too early may cause too short stop

condition */

drivers/i2c/algos/i2c-algo-8xx.c new file +28 -0
+	udelay(4);
+	out_8(&i2c->i2c_i2mod, in_8(&i2c->i2c_i2mod) | ~1);
+#endif
+
+	if ( res < 0) {
+		force_close(cpm);
+		if (cpm_debug)
+			printk("IIC tryaddress: timeout!\n");
+		return -EIO;
+	}
+
+	if (cpm_debug > 1)
+		printk("back from sleep\n");
+
+	if (tbdf->cbd_sc & BD_SC_NAK) {
+		if (cpm_debug > 1)
+			printk("IIC try; no ack\n");
+		return 0;
+	}
+
+	if (tbdf->cbd_sc & BD_SC_READY) {
+		printk("IIC try; complete but tbuf ready\n");
+	}
+
+	return 1;
+}
+
+static int cpm_xfer(struct i2c_adapter *adap, struct i2c_msg
Quoted 1 line

msgs[], int num)

Use *msgs instead of msgs[].

Quoted 20 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +12 -0
+{
+	struct i2c_algo_8xx_data *cpm = adap->algo_data;
+	struct i2c_msg *pmsg;
+	int i, ret;
+	u_char addr;
+
+	for (i = 0; i < num; i++) {
+		pmsg = &msgs[i];
+
+		if (cpm_debug)
+			printk("i2c-algo-8xx.o: "
+			       "#%d addr=0x%x flags=0x%x len=%d\n

buf=%lx\n",

drivers/i2c/algos/i2c-algo-8xx.c new file +1 -0
+			       i, pmsg->addr, pmsg->flags,

pmsg->len,

drivers/i2c/algos/i2c-algo-8xx.c new file +5 -0
+			       (unsigned long)pmsg->buf);
+
+		addr = pmsg->addr << 1;
+		if (pmsg->flags & I2C_M_RD)
+			addr |= 1;
Quoted 9 lines

drivers/i2c/algos/i2c-algo-8xx.c line 540

Quoted 5 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +5 -0
+		if (pmsg->flags & I2C_M_REV_DIR_ADDR)
+			addr ^= 1;
+
+		if (!(pmsg->flags & I2C_M_NOSTART)) {
+		}

Either something is missing here and you must add it, or this statement is useless and you should drop it.

heh, correct.

Quoted 28 lines
Quoted 18 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +3 -0
+		if (pmsg->flags & I2C_M_RD) {
+			/* read bytes into buffer */
+			ret = cpm_iic_read(cpm, addr, pmsg->buf,

pmsg->len);

drivers/i2c/algos/i2c-algo-8xx.c new file +2 -0
+			if (cpm_debug)
+				printk("i2c-algo-8xx.o: read %d

bytes\n", ret);

drivers/i2c/algos/i2c-algo-8xx.c new file +6 -1
+			if (ret < pmsg->len) {
+				return (ret < 0) ? ret :
-EREMOTEIO;
+			}
+		} else {
+			/* write bytes from buffer */
+			ret = cpm_iic_write(cpm, addr, pmsg->buf,

pmsg->len);

drivers/i2c/algos/i2c-algo-8xx.c new file +2 -0
+			if (cpm_debug)
+				printk("i2c-algo-8xx.o: wrote

%d\n", ret);

drivers/i2c/algos/i2c-algo-8xx.c line 559

Quoted 7 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +6 -1
+			if (ret < pmsg->len) {
+				return (ret < 0) ? ret :
-EREMOTEIO;
+			}
+		}
+	}
+	return (num);

Useless parentheses.

Quoted 9 lines
Quoted 1 line

+}

You do not appear to handle repeated start. I can tell because the code handles all messages the exact same way, be they the first, second or last message of a group. This means that you don't really implement the I2C protocol, but an approximation of it. It might be sufficient for some I2C chips, but others will break. Look in the specifications of your device for how this could be fixed.

I doubt 8xx has a full-fledged i2c protocol stuff onboard, and basic code that were residing in 2.4 repo suite my needs quite well (afaict many others just don't care :)). I just think it is silly to drop the code already implemented and working even if it requires some efforts to bring it up to shape.

Quoted 116 lines

drivers/i2c/algos/i2c-algo-8xx.c line 566

Quoted 6 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +6 -0
+
+static u32 cpm_func(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR |
+	    I2C_FUNC_PROTOCOL_MANGLING;
+}

I2C_FUNC_I2C is missing. I2C_FUNC_10BIT_ADDR isn't implemented so it shouldn't be advertised. I2C_FUNC_PROTOCOL_MANGLING should only be implemented if really needed, so I'd suggest that you drop it for now (together with I2C_M_REV_DIR_ADDR and I2C_M_NOSTART above).

Quoted 19 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +17 -1
+
+/* -----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)
+{
+	struct i2c_algo_8xx_data *cpm = adap->algo_data;
+	int i;
+
+	if (cpm_debug)
+		printk("i2c-algo-8xx.o: hw routines for %s

registered.\n",

drivers/i2c/algos/i2c-algo-8xx.c line 592

Quoted 8 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +8 -0
+		       adap->name);
+
+	/* register new adapter to i2c module... */
+
+	adap->algo = &cpm_algo;
+
+	i2c_add_adapter(adap);
+	cpm_iic_init(cpm);

Should be in the reverse order, init first, register second.

drivers/i2c/algos/i2c-algo-8xx.c line 603

Quoted 12 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +4 -0
+
+	/* scan bus */
+	if (cpm_scan) {
+		printk(KERN_INFO " i2c-algo-8xx.o: scanning bus

%s...\n",

drivers/i2c/algos/i2c-algo-8xx.c new file +7 -0
+			adap->name);
+		for (i = 0; i < 128; i++) {
+			if (cpm_iic_tryaddress(cpm, i))
+				printk("(%02x)",i<<1);
+		}
+		printk("\n");
+	}

As explained above, please remove this.

drivers/i2c/algos/i2c-algo-8xx.c line 614

Quoted 11 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +11 -0
+
+	return 0;
+}
+
+int i2c_8xx_del_bus(struct i2c_adapter *adap)
+{
+	struct i2c_algo_8xx_data *cpm = adap->algo_data;
+
+	cpm_iic_shutdown(cpm);
+
+	return i2c_del_adapter(adap);

Should be in the reverse order, unregister first, shutdown second.

Quoted 20 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +8 -0
+}
+
+EXPORT_SYMBOL(i2c_8xx_add_bus);
+EXPORT_SYMBOL(i2c_8xx_del_bus);
+
+MODULE_AUTHOR("Brad Parker <[email protected]>");
+MODULE_DESCRIPTION("I2C-Bus MPC8XX algorithm");
+MODULE_LICENSE("GPL");
drivers/i2c/busses/Kconfig +1 -2
index 4afc795..83d9aef 100644
@@ -385,8 +385,8 @@ config I2C_PROSAVAGE
 	  will be called i2c-prosavage.
 
 config I2C_RPXLITE
-	tristate "Embedded Planet RPX Lite/Classic support"
-	depends on RPXLITE || RPXCLASSIC
+	tristate "Embedded Planet RPX Lite/Classic and Freescale

86x/885 ads support"

drivers/i2c/busses/Kconfig config I2C_PROSAVAGE · line 390

Quoted 2 lines
drivers/i2c/busses/Kconfig +1 -0
+	depends on RPXLITE || RPXCLASSIC || MPC86XADS || MPC885ADS
 	select I2C_ALGO8XX

Please add a help text.

Quoted 20 lines

config I2C_S3C2410

(quoted diff) +0 -0

b/drivers/i2c/busses/i2c-rpx.c index 8764df0..ebddd94 100644

drivers/i2c/busses/i2c-rpx.c +3 -0
@@ -14,88 +14,129 @@
 #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/mpc8xx.h>
 #include <asm/commproc.h>
+#include <asm/fs_pd.h>
 
 
Quoted 15 lines

drivers/i2c/busses/i2c-rpx.c line 36

Quoted 12 lines
drivers/i2c/busses/i2c-rpx.c +10 -2
-static void
-rpx_iic_init(struct i2c_algo_8xx_data *data)
+struct m8xx_i2c {
+	char *base;
+	struct device *dev;
+	struct i2c_adapter adap;
+	struct i2c_algo_8xx_data *algo_8xx;
+};
+
+static struct i2c_algo_8xx_data rpx_data;
+
+static struct i2c_adapter rpx_ops = {

Could be const?

prolly yes.

Quoted 5 lines

drivers/i2c/busses/i2c-rpx.c line 38

Quoted 2 lines
drivers/i2c/busses/i2c-rpx.c +2 -0
+	.owner		= THIS_MODULE,
+	.name		= "m8xx",

Find a better name (e.g. "i2c-rpx").

What about mpc8xx?

Quoted 136 lines
Quoted 13 lines
drivers/i2c/busses/i2c-rpx.c +8 -1
+	.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 platform_device *pdev =

to_platform_device(i2c->dev); +

drivers/i2c/busses/i2c-rpx.c +1 -0
+	cp = data->cp = (cpm8xx_t *)immr_map(im_cpm);	/*
Quoted 1 line

pointer to Communication Processor */

Line too long.

drivers/i2c/busses/i2c-rpx.c line 53

Quoted 7 lines
drivers/i2c/busses/i2c-rpx.c +0 -1
-	cp = cpmp;	/* Get pointer to Communication

Processor */

drivers/i2c/busses/i2c-rpx.c +0 -1
-	immap = (immap_t *)IMAP_ADDR;	/* and to internal

registers */

drivers/i2c/busses/i2c-rpx.c +2 -0
+	data->irq = platform_get_irq_byname(pdev, "interrupt");
+  

No blank line between assignation and test please.

Quoted 42 lines
drivers/i2c/busses/i2c-rpx.c +3 -1
+	if (data->irq < 0)
+		return -EINVAL;
 
-	data->iip = (iic_t *)&cp->cp_dparam[PROFF_IIC];
+	r = platform_get_resource_byname(pdev, IORESOURCE_MEM,

"pram");

drivers/i2c/busses/i2c-rpx.c +4 -1
+	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];

drivers/i2c/busses/i2c-rpx.c +1 -8
-	data->i2c = (i2c8xx_t *)&(immap->im_i2c);
-	data->cp = cp;
-
-	/* Initialize Port B IIC pins.
-	*/
-	cp->cp_pbpar |= 0x00000030;
-	cp->cp_pbdir |= 0x00000030;
-	cp->cp_pbodr |= 0x00000030;
+	r = platform_get_resource_byname(pdev, IORESOURCE_MEM,

"regs");

drivers/i2c/busses/i2c-rpx.c +3 -4
+	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 device *device)
{

drivers/i2c/busses/i2c-rpx.c line 80

Quoted 6 lines
drivers/i2c/busses/i2c-rpx.c +1 -5
-	/* install interrupt handler */
-	cpm_install_handler(irq, func, data);
-
-	return 0;
-}
+	int result = 0;

Useless initialization.

drivers/i2c/busses/i2c-rpx.c line 87

Quoted 10 lines
drivers/i2c/busses/i2c-rpx.c +6 -3
+	struct m8xx_i2c *i2c;
+	struct platform_device *pdev = to_platform_device(device);
 
-static struct i2c_algo_8xx_data rpx_data = {
-	.setisr = rpx_install_isr
-};
+	if (!(i2c = kmalloc(sizeof(*i2c), GFP_KERNEL))) {
+		return -ENOMEM;
+	}
+	memset(i2c, 0, sizeof(*i2c));

Use kzalloc() instead.

Quoted 45 lines
drivers/i2c/busses/i2c-rpx.c +8 -12
+	i2c->dev = device;
+	i2c->algo_8xx = &rpx_data;
 
-static struct i2c_adapter rpx_ops = {
-	.owner		= THIS_MODULE,
-	.name		= "m8xx",
-	.id		= I2C_HW_MPC8XX_EPON,
-	.algo_data	= &rpx_data,
-};
+	rpx_iic_init(i2c);
 
-int __init i2c_rpx_init(void)
-{
-	printk(KERN_INFO "i2c-rpx: i2c MPC8xx driver\n");
+	dev_set_drvdata(device, 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 = &pdev->dev;
 
-	if (i2c_8xx_add_bus(&rpx_ops) < 0) {
+	if ((result = i2c_8xx_add_bus(&rpx_ops) < 0)) {
 		printk(KERN_ERR "i2c-rpx: Unable to register with

I2C\n");

drivers/i2c/busses/i2c-rpx.c +13 -1
-		return -ENODEV;
+		kfree(i2c);
 	}
 
+	return result;
+}
+
+
+static int i2c_rpx_remove(struct device *device)
+{
+	struct m8xx_i2c *i2c = dev_get_drvdata(device);
+
+	i2c_8xx_del_bus(&i2c->adap);
+	dev_set_drvdata(device, NULL);
+
+	kfree(i2c);
 	return 0;
 }
Quoted 14 lines

drivers/i2c/busses/i2c-rpx.c line 126

Quoted 9 lines
drivers/i2c/busses/i2c-rpx.c +8 -1
-void __exit i2c_rpx_exit(void)
+
+/* Structure for a device driver */
+static struct device_driver i2c_rpx_driver = {
+	.name = "fsl-i2c-cpm",
+	.bus = &platform_bus_type,
+	.probe = i2c_rpx_probe,
+	.remove = i2c_rpx_remove,
+};

Why don't you declare it as a struct platform_driver, register it with platform_driver_register() and unregister it with platform_driver_unregister()?

Well. This stuff belongs to CPM1, of the mpc8xx family, but the target boards are different, and they may/should provide board specific inits and filling of platform data. With platform_driver_register we may end up with ifdef stuff here (which is evil).

Quoted 54 lines
Quoted 27 lines
drivers/i2c/busses/i2c-rpx.c +10 -3
+
+static int __init i2c_rpx_init(void)
 {
-	i2c_8xx_del_bus(&rpx_ops);
+	return driver_register(&i2c_rpx_driver);
 }
 
-MODULE_AUTHOR("Dan Malek <[email protected]>");
-MODULE_DESCRIPTION("I2C-Bus adapter routines for MPC8xx boards");
+static void __exit i2c_rpx_exit(void)
+{
+	driver_unregister(&i2c_rpx_driver);
+}
 
 module_init(i2c_rpx_init);
 module_exit(i2c_rpx_exit);
+
+MODULE_AUTHOR("Dan Malek <[email protected]>");
+MODULE_DESCRIPTION("I2C-Bus adapter routines for MPC8xx boards");
(quoted diff) +0 -0

b/include/linux/i2c-algo-8xx.h new file mode 100644 index 0000000..a644512

include/linux/i2c-algo-8xx.h new file +1 -1
@@ -0,0 +1,29 @@
+/*
-------------------------------------------------------------------------
Quoted 4 lines

*/ +/* i2c-algo-8xx.h i2c driver algorithms for MPX8XX
CPM */ +/*
-------------------------------------------------------------------------
*/ + +/* $Id$ */

Delete this.

Quoted 7 lines
include/linux/i2c-algo-8xx.h new file +7 -0
+
+#ifndef I2C_ALGO_8XX_H
+#define I2C_ALGO_8XX_H
+
+#include <linux/i2c.h>
+#include <asm/8xx_immap.h>
+#include <asm/commproc.h>

include/linux/i2c-algo-8xx.h line 22

Quoted 10 lines
include/linux/i2c-algo-8xx.h new file +10 -0
+
+struct i2c_algo_8xx_data {
+	uint dp_addr;
+	int reloc;
+	int irq;
+	i2c8xx_t *i2c;
+	iic_t	*iip;
+	cpm8xx_t *cp;
+
+	u_char	temp[513];

Shouldn't this be CPM_MAX_READ instead of a hard-coded value?

Quoted 5 lines
Quoted 1 line

+};

Alignment in this structure is inconsistent, some lines use tabs other use spaces. Please don't mix.

I'm OK with this and upper small nits - great thanks for review!

Quoted 8 lines
Quoted 6 lines
include/linux/i2c-algo-8xx.h new file +6 -0
+
+extern int i2c_8xx_add_bus(struct i2c_adapter *);
+extern int i2c_8xx_del_bus(struct i2c_adapter *);
+
+#endif /* I2C_ALGO_8XX_H */
+
Signature · 1 line
Sincerely, Vitaly
#5 lore raw

Hi Vitaly,

Quoted 9 lines · Vitaly Bordug
Quoted 4 lines · On Sat, 21 Apr 2007 09:57:07 +0200 Jean Delvare wrote:

I wonder what's the point of having a separate i2c algorithm driver. We don't expect any other driver than i2c-rpx to ever use it, do we? In that case, all the code should be added to i2c-rpx directly, this will makes things more simple and more efficient.

That is how it was back in 2.4 - if you see combine is a good move, I'm OK with it. But what shouldn't be rpc then - basically rpx(lite) is 8xx-based target, so let's call it all mpc8xx then.

Sure, I'm fine with a name change. If it makes more sense to name that driver i2c-mpc8xx, that's OK with me.

Quoted 10 lines
Quoted 6 lines

drivers/i2c/algos/i2c-algo-8xx.c line 281

Quoted 2 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +2 -0
+		tmo = jiffies + 1 * HZ;
+		while (!(in_8(&i2c->i2c_i2cer) & 0x11 || time_after(jiffies, tmo))) ;/* Busy wait, with a timeout */

This could result in a one-second busy loop, not very friendly for other drivers. It should sleep while waiting. Line too long, please fold.

Can you please elaborate a little here (or just point to the similar code)? I assume we should not block here, handling timeout in a waitqueue...

Blocking is not a problem. The problem is that you are keeping the CPU for yourself while waiting, for up to one full second. That's not acceptable. You should at least call schedule() or cond_resced() (I don't know the difference, I admit) and/or cpu_relax() as is done in i2c-mpc, i2c-ibm_iic and scx200_acb, or even sleep, as is done in i2c-omap. Search for "time_after" in these 4 drivers for examples. I believe that sleeping is more friendly.

Quoted 7 lines
Quoted 6 lines

You do not appear to handle repeated start. I can tell because the code handles all messages the exact same way, be they the first, second or last message of a group. This means that you don't really implement the I2C protocol, but an approximation of it. It might be sufficient for some I2C chips, but others will break. Look in the specifications of your device for how this could be fixed.

Quoted 5 lines

I doubt 8xx has a full-fledged i2c protocol stuff onboard, and basic code that were residing in 2.4 repo suite my needs quite well (afaict many others just don't care :)).
I just think it is silly to drop the code already implemented and working even if it requires some efforts to bring it up to shape.

Well as far as I can see, only the repeated start is missing, so it's not that far from a complete implementation. If the hardware can do it, you simply have to add it to the driver. If the hardware really doesn't do it (which would surprise me, but you never know), of course you cannot implement it in the driver and we'll have to live with (well, without) it. But that's definitely an issue to keep in mind if I2C chip drivers start failing when used together with this bus driver.

Quoted 11 lines
Quoted 4 lines
Quoted 1 line

+static struct i2c_adapter rpx_ops = {

Could be const?

prolly yes.

Quoted 5 lines

drivers/i2c/busses/i2c-rpx.c line 38

Quoted 2 lines
drivers/i2c/busses/i2c-rpx.c +2 -0
+	.owner		= THIS_MODULE,
+	.name		= "m8xx",

Find a better name (e.g. "i2c-rpx").

What about mpc8xx?

i2c-mpc8xx then, OK.

Quoted 12 lines
Quoted 11 lines

drivers/i2c/busses/i2c-rpx.c line 126

Quoted 7 lines
drivers/i2c/busses/i2c-rpx.c +7 -0
+/* Structure for a device driver */
+static struct device_driver i2c_rpx_driver = {
+	.name = "fsl-i2c-cpm",
+	.bus = &platform_bus_type,
+	.probe = i2c_rpx_probe,
+	.remove = i2c_rpx_remove,
+};

Why don't you declare it as a struct platform_driver, register it with platform_driver_register() and unregister it with platform_driver_unregister()?

Quoted 5 lines

Well. This stuff belongs to CPM1, of the mpc8xx family, but the target boards are different, and they may/should provide board specific inits and filling of platform data. With platform_driver_register we may end up with ifdef stuff here (which is evil).

I don't follow you here, sorry. Platform devices are declared by board-specific code which can include all the needed initialization. And device-specific data can be carried to the platform driver for further use. The platform device/driver infrastructure is meant to handle that kind of situation, so there really is no excuse that I can see not to use it. i2c-omap and i2c-mpc use it. As a matter of fact you _are_ declaring a platform driver (.bus = &platform_bus_type), just not using the standard way.

Signature · 1 line
Jean Delvare
#6 lore raw
Quoted 32 lines · Jean Delvare

Hi Vitaly,

Quoted 11 lines · Vitaly Bordug
Quoted 5 lines · On Sat, 21 Apr 2007 09:57:07 +0200 Jean Delvare wrote:

I wonder what's the point of having a separate i2c algorithm driver. We don't expect any other driver than i2c-rpx to ever use it, do we? In that case, all the code should be added to i2c-rpx directly, this will makes things more simple and more efficient.

That is how it was back in 2.4 - if you see combine is a good move, I'm OK with it. But what shouldn't be rpc then - basically rpx(lite) is 8xx-based target, so let's call it all mpc8xx then.

Sure, I'm fine with a name change. If it makes more sense to name that driver i2c-mpc8xx, that's OK with me.

Quoted 11 lines
Quoted 7 lines

drivers/i2c/algos/i2c-algo-8xx.c line 281

Quoted 3 lines
drivers/i2c/algos/i2c-algo-8xx.c new file +2 -0
+		tmo = jiffies + 1 * HZ;
+		while (!(in_8(&i2c->i2c_i2cer) & 0x11 || time_after(jiffies, tmo))) ;/* Busy wait, with a timeout */

This could result in a one-second busy loop, not very friendly for other drivers. It should sleep while waiting. Line too long, please fold.

Can you please elaborate a little here (or just point to the similar code)? I assume we should not block here, handling timeout in a waitqueue...

Quoted 9 lines

Blocking is not a problem. The problem is that you are keeping the CPU for yourself while waiting, for up to one full second. That's not acceptable. You should at least call schedule() or cond_resced() (I don't know the difference, I admit) and/or cpu_relax() as is done in i2c-mpc, i2c-ibm_iic and scx200_acb, or even sleep, as is done in i2c-omap. Search for "time_after" in these 4 drivers for examples. I believe that sleeping is more friendly.

OK, very clear, thanks.

Quoted 61 lines
Quoted 7 lines
Quoted 7 lines

You do not appear to handle repeated start. I can tell because the code handles all messages the exact same way, be they the first, second or last message of a group. This means that you don't really implement the I2C protocol, but an approximation of it. It might be sufficient for some I2C chips, but others will break. Look in the specifications of your device for how this could be fixed.

Quoted 6 lines

I doubt 8xx has a full-fledged i2c protocol stuff onboard, and basic code that were residing in 2.4 repo suite my needs quite well (afaict many others just don't care :)).
I just think it is silly to drop the code already implemented and working even if it requires some efforts to bring it up to shape.

Well as far as I can see, only the repeated start is missing, so it's not that far from a complete implementation. If the hardware can do it, you simply have to add it to the driver. If the hardware really doesn't do it (which would surprise me, but you never know), of course you cannot implement it in the driver and we'll have to live with (well, without) it. But that's definitely an issue to keep in mind if I2C chip drivers start failing when used together with this bus driver.

Quoted 7 lines
Quoted 5 lines
Quoted 2 lines

+static struct i2c_adapter rpx_ops = {

Could be const?

prolly yes.

Quoted 8 lines
Quoted 6 lines

drivers/i2c/busses/i2c-rpx.c line 38

Quoted 3 lines
drivers/i2c/busses/i2c-rpx.c +2 -0
+	.owner		= THIS_MODULE,
+	.name		= "m8xx",

Find a better name (e.g. "i2c-rpx").

What about mpc8xx?

i2c-mpc8xx then, OK.

Quoted 12 lines
Quoted 12 lines

drivers/i2c/busses/i2c-rpx.c line 126

Quoted 8 lines
drivers/i2c/busses/i2c-rpx.c +7 -0
+/* Structure for a device driver */
+static struct device_driver i2c_rpx_driver = {
+	.name = "fsl-i2c-cpm",
+	.bus = &platform_bus_type,
+	.probe = i2c_rpx_probe,
+	.remove = i2c_rpx_remove,
+};

Why don't you declare it as a struct platform_driver, register it with platform_driver_register() and unregister it with platform_driver_unregister()?

Quoted 6 lines

Well. This stuff belongs to CPM1, of the mpc8xx family, but the target boards are different, and they may/should provide board specific inits and filling of platform data. With platform_driver_register we may end up with ifdef stuff here (which is evil).

Quoted 10 lines

I don't follow you here, sorry. Platform devices are declared by board-specific code which can include all the needed initialization. And device-specific data can be carried to the platform driver for further use. The platform device/driver infrastructure is meant to handle that kind of situation, so there really is no excuse that I can see not to use it. i2c-omap and i2c-mpc use it. As a matter of fact you _are_ declaring a platform driver (.bus = &platform_bus_type), just not using the standard way.

Standard way here - platform devices got registered from elsewhere - from arch/ppc/ppc_sys.c if arch/ppc or from arch/powerpc/sysdev/fsl_soc.c if powerpc. Every way (powerpc is more flexible since is pulling the information from the firmware-passed device tree) fills in the resources and platform data, and is capable with device/drive bound you are talking about.

--
Thanks, Vitaly

#7 lore raw

Hi Vitaly,

Quoted 30 lines · Vitaly Bordug
Quoted 19 lines · Jean Delvare
Quoted 12 lines
Quoted 12 lines

drivers/i2c/busses/i2c-rpx.c line 126

Quoted 8 lines
drivers/i2c/busses/i2c-rpx.c +7 -0
+/* Structure for a device driver */
+static struct device_driver i2c_rpx_driver = {
+	.name = "fsl-i2c-cpm",
+	.bus = &platform_bus_type,
+	.probe = i2c_rpx_probe,
+	.remove = i2c_rpx_remove,
+};

Why don't you declare it as a struct platform_driver, register it with platform_driver_register() and unregister it with platform_driver_unregister()?

Quoted 6 lines

Well. This stuff belongs to CPM1, of the mpc8xx family, but the target boards are different, and they may/should provide board specific inits and filling of platform data. With platform_driver_register we may end up with ifdef stuff here (which is evil).

Quoted 10 lines

I don't follow you here, sorry. Platform devices are declared by board-specific code which can include all the needed initialization. And device-specific data can be carried to the platform driver for further use. The platform device/driver infrastructure is meant to handle that kind of situation, so there really is no excuse that I can see not to use it. i2c-omap and i2c-mpc use it. As a matter of fact you _are_ declaring a platform driver (.bus = &platform_bus_type), just not using the standard way.

Quoted 7 lines

Standard way here - platform devices got registered from elsewhere - from arch/ppc/ppc_sys.c if arch/ppc or from arch/powerpc/sysdev/fsl_soc.c if powerpc.
Every way (powerpc is more flexible since is pulling the information from the firmware-passed device tree) fills in the resources and platform data, and
is capable with device/drive bound you are talking about.

This doesn't explain why you can't use platform_driver_register(), which is the right way to register a platform driver.

Signature · 1 line
Jean Delvare
#8 lore raw
Quoted 41 lines · On Thu, 26 Apr 2007 17:54:38 +0200 Jean Delvare <[email protected]> wrote:

Hi Vitaly,

Quoted 30 lines · Vitaly Bordug
Quoted 19 lines · Jean Delvare
Quoted 12 lines
Quoted 12 lines

drivers/i2c/busses/i2c-rpx.c line 126

Quoted 8 lines
drivers/i2c/busses/i2c-rpx.c +7 -0
+/* Structure for a device driver */
+static struct device_driver i2c_rpx_driver = {
+	.name = "fsl-i2c-cpm",
+	.bus = &platform_bus_type,
+	.probe = i2c_rpx_probe,
+	.remove = i2c_rpx_remove,
+};

Why don't you declare it as a struct platform_driver, register it with platform_driver_register() and unregister it with platform_driver_unregister()?

Quoted 6 lines

Well. This stuff belongs to CPM1, of the mpc8xx family, but the target boards are different, and they may/should provide board specific inits and filling of platform data. With platform_driver_register we may end up with ifdef stuff here (which is evil).

Quoted 10 lines

I don't follow you here, sorry. Platform devices are declared by board-specific code which can include all the needed initialization. And device-specific data can be carried to the platform driver for further use. The platform device/driver infrastructure is meant to handle that kind of situation, so there really is no excuse that I can see not to use it. i2c-omap and i2c-mpc use it. As a matter of fact you _are_ declaring a platform driver (.bus = &platform_bus_type), just not using the standard way.

Quoted 7 lines

Standard way here - platform devices got registered from elsewhere - from arch/ppc/ppc_sys.c if arch/ppc or from arch/powerpc/sysdev/fsl_soc.c if powerpc.
Every way (powerpc is more flexible since is pulling the information from the firmware-passed device tree) fills in the resources and platform data, and
is capable with device/drive bound you are talking about.

Quoted 2 lines

This doesn't explain why you can't use platform_driver_register(), which is the right way to register a platform driver.

Ahm, I clearly confused myself (with device/driver stuff), sorry.

Anyway I think we'll look at of_device for this, to reduce extract from OF->insert platform device overhead.

Signature · 2 lines
Sincerely, 
Vitaly
#9 lore raw
In reply to Vitaly Bordug
Quoted 13 lines · Vitaly Bordug
arch/powerpc/platforms/8xx/mpc885ads_setup.c +1 -0
index 9bd81c7..d32e066 100644
@@ -51,6 +51,7 @@ static void init_smc1_uart_ioports(struc
 static void init_smc2_uart_ioports(struct fs_uart_platform_info* fpi);
 static void init_scc3_ioports(struct fs_platform_info* ptr);
 static void init_irda_ioports(void);
+static void init_i2c_ioports(void);
 
 void __init mpc885ads_board_setup(void)
 {

arch/powerpc/platforms/8xx/mpc885ads_setup.c #endif · line 127

Quoted 7 lines
arch/powerpc/platforms/8xx/mpc885ads_setup.c +4 -0
@@ -120,6 +121,10 @@ #endif
 #ifdef CONFIG_8XX_SIR
 	init_irda_ioports();
 #endif
+
+#ifdef CONFIG_I2C_RPXLITE
+	init_i2c_ioports();
+#endif

Does it hurt to always do it, even when the driver is not enabled? THat'd do away with an ifdef.

Also, if you move the static function up, you don't need a prototype. That goes for other stuff in this file too.

Quoted 5 lines
arch/powerpc/platforms/8xx/mpc885ads_setup.c +0 -0
 }
 
 
@@ -361,6 +366,15 @@ static void init_irda_ioports()
 	immr_unmap(cp);

arch/powerpc/platforms/8xx/mpc885ads_setup.c static void init_irda_ioports() · line 376

Quoted 10 lines
arch/powerpc/platforms/8xx/mpc885ads_setup.c +8 -0
 }
 
+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);
+}

Looks like you moved this out of the driver and into the platform code. What happens to other platforms where it's used?

Quoted 9 lines
arch/powerpc/platforms/8xx/mpc885ads_setup.c +1 -0
+
 int platform_device_skip(const char *model, int id)
 {
 #ifdef CONFIG_MPC8xx_SECOND_ETH_SCC3
arch/powerpc/sysdev/fsl_soc.c +0 -0
index 419b688..7ecd537 100644

arch/powerpc/sysdev/fsl_soc.c static int __init fsl_i2c_of_init(void) · line 334

Quoted 5 lines
arch/powerpc/sysdev/fsl_soc.c +1 -1
@@ -331,7 +331,7 @@ static int __init fsl_i2c_of_init(void)
 	for (np = NULL, i = 0;
 	     (np = of_find_compatible_node(np, "i2c", "fsl-i2c")) != NULL;
 	     i++) {
-		struct resource r[2];
+		struct resource r[3];

Why? No code that uses it has been changed. Is it a bugfix?

Quoted 54 lines
arch/powerpc/sysdev/fsl_soc.c +47 -0
 		struct fsl_i2c_platform_data i2c_data;
 		const unsigned char *flags = NULL;
 
@@ -1215,4 +1215,63 @@ err:
 
 arch_initcall(fs_irda_of_init);
 
+static const char *i2c_regs = "regs";
+static const char *i2c_pram = "pram";
+static const char *i2c_irq = "interrupt";
+
+static int __init fsl_i2c_cpm_of_init(void)
+{
+	struct device_node *np;
+	unsigned int i;
+	struct platform_device *i2c_dev;
+	int ret;
+
+	for (np = NULL, i = 0;
+	     (np = of_find_compatible_node(np, "i2c", "fsl-i2c-cpm")) != NULL;
+	     i++) {
+		struct resource r[3];
+		struct fsl_i2c_platform_data i2c_data;
+
+		memset(&r, 0, sizeof(r));
+		memset(&i2c_data, 0, sizeof(i2c_data));
+
+		ret = of_address_to_resource(np, 0, &r[0]);
+		if (ret)
+			goto err;
+		r[0].name = i2c_regs;
+
+		ret = of_address_to_resource(np, 1, &r[1]);
+		if (ret)
+			goto err;
+		r[1].name = i2c_pram;
+
+		r[2].start = r[2].end = irq_of_parse_and_map(np, 0);
+		r[2].flags = IORESOURCE_IRQ;
+		r[2].name = i2c_irq;
+
+		i2c_dev = platform_device_register_simple("fsl-i2c-cpm", i, &r[0], 3);
+		if (IS_ERR(i2c_dev)) {
+			ret = PTR_ERR(i2c_dev);
+			goto err;
+		}
+
+		ret =
+		    platform_device_add_data(i2c_dev, &i2c_data,
+					     sizeof(struct
+						    fsl_i2c_platform_data));
+		if (ret)
+			goto unreg;
+	}

arch/powerpc/sysdev/fsl_soc.c err: · line 1274

Quoted 10 lines
arch/powerpc/sysdev/fsl_soc.c +10 -0
+
+	return 0;
+
+unreg:
+	platform_device_unregister(i2c_dev);
+err:
+	return ret;
+}
+
+arch_initcall(fsl_i2c_cpm_of_init);

This could all be done with an of_platform driver instead, and avoid the above. (Someone else already suggested that I believe).

Quoted 891 lines
arch/powerpc/sysdev/fsl_soc.c +0 -0
 #endif /* CONFIG_8xx */
drivers/i2c/algos/Kconfig +2 -0
index 5889907..7d7fb87 100644
@@ -37,6 +37,8 @@ config I2C_ALGOPCA
 config I2C_ALGO8XX
 	tristate "MPC8xx CPM I2C interface"
 	depends on 8xx
+	help
+	  8xx I2C Algorithm
 
 config I2C_ALGO_SGI
 	tristate "I2C SGI interfaces"
drivers/i2c/algos/Makefile +1 -0
index cac1051..1bd3b37 100644
@@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_ALGOBIT)	+= i2c-algo-bi
 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
drivers/i2c/algos/i2c-algo-8xx.c new file +622 -0
new file mode 100644
index 0000000..b1f414a
@@ -0,0 +1,622 @@
+/*
+ * i2c-algo-8xx.c i2x driver algorithms for MPC8XX CPM
+ * Copyright (c) 1999 Dan Malek ([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.
+ *
+ * moved into proper i2c interface; separated out platform specific
+ * parts into i2c-rpx.c
+ * Brad Parker ([email protected])
+ */
+
+
+/* $Id: i2c-algo-8xx.c,v 1.15 2004/11/20 08:02:24 khali Exp $ */
+
+#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>
+
+#define CPM_MAX_READ	513
+#undef	I2C_CHIP_ERRATA /* Try ot define this if you have an older CPU(earlier than rev D4) */
+
+static wait_queue_head_t iic_wait;
+static ushort r_tbase, r_rbase;
+
+int cpm_debug = 0;
+int cpm_scan = 0;
+
+static irqreturn_t cpm_iic_interrupt(int irq, void *dev_id)
+{
+	i2c8xx_t *i2c = (i2c8xx_t *) dev_id;
+	if (cpm_debug > 1)
+		printk("cpm_iic_interrupt(dev_id=%p)\n", 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 void cpm_iic_init(struct i2c_algo_8xx_data *cpm)
+{
+	iic_t *iip = cpm->iip;
+	i2c8xx_t *i2c = cpm->i2c;
+	unsigned char brg;
+
+	if (cpm_debug)
+		printk(KERN_DEBUG "cpm_iic_init()\n");
+
+	/* 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) {
+		printk("iip %p, dp_addr 0x%x\n", cpm->iip, cpm->dp_addr);
+		printk("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;
+		u16 v = mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_INIT_TRX) | CPM_CR_FLG;
+
+		out_be16(&cp->cp_cpcr, v);
+		while (in_be16(&cp->cp_cpcr) & CPM_CR_FLG) ;
+	} 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 = (unsigned char)(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);
+
+	init_waitqueue_head(&iic_wait);
+
+	/* Install interrupt handler.
+	 */
+	if (cpm_debug) {
+		printk("%s[%d] Install ISR for IRQ %d\n",
+		       __func__, __LINE__, CPMVEC_I2C);
+	}
+	request_irq(cpm->irq, cpm_iic_interrupt, 0, "8xx_i2c", i2c);
+}
+
+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_algo_8xx_data *cpm)
+{
+	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);
+		while (in_be16(&cp->cp_cpcr) & CPM_CR_FLG) ;
+	}
+	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_algo_8xx_data *cpm, u_char abyte, char *buf, int count)
+{
+	iic_t *iip = cpm->iip;
+	i2c8xx_t *i2c = cpm->i2c;
+	cbd_t *tbdf, *rbdf;
+	u_char *tb;
+	unsigned long tmo;
+	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)
+		printk("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;
+
+	if (count > 16) {
+		/* 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);
+	} else {		/* busy wait for small transfers, its faster */
+		out_8(&i2c->i2c_i2cmr, 0x00);	/* Disable I2C 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 */
+
+		tmo = jiffies + 1 * HZ;
+		while (!(in_8(&i2c->i2c_i2cer) & 0x11 || time_after(jiffies, tmo))) ;/* Busy wait, with a timeout */
+	}
+
+	if ( res < 0) {
+		force_close(cpm);
+		if (cpm_debug)
+			printk("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) {
+		printk("tx sc %04x, rx sc %04x\n", tbdf->cbd_sc, rbdf->cbd_sc);
+	}
+
+	if (tbdf->cbd_sc & BD_SC_READY) {
+		printk("IIC read; complete but tbuf ready\n");
+		force_close(cpm);
+		printk("tx sc %04x, rx sc %04x\n", tbdf->cbd_sc, rbdf->cbd_sc);
+	}
+
+	if (tbdf->cbd_sc & BD_SC_NAK) {
+		if (cpm_debug)
+			printk("IIC read; no ack\n");
+		return -EREMOTEIO;
+	}
+
+	if (rbdf->cbd_sc & BD_SC_EMPTY) {
+		/* force_close(cpm); */
+		if (cpm_debug) {
+			printk("IIC read; complete but rbuf empty\n");
+			printk("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)
+			printk("IIC read; Overrun\n");
+		return -EREMOTEIO;;
+	}
+
+	if (cpm_debug)
+		printk("read %d bytes\n", rbdf->cbd_datlen);
+
+	if (rbdf->cbd_datlen < count) {
+		if (cpm_debug)
+			printk("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_algo_8xx_data *cpm, u_char abyte, char *buf, int count)
+{
+	iic_t *iip = cpm->iip;
+	i2c8xx_t *i2c = cpm->i2c;
+	cbd_t *tbdf;
+	u_char *tb;
+	unsigned long tmo;
+	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)
+		printk("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;
+
+	if (count > 16) {
+		/* 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);
+	} else {		/* busy wait for small transfers, its faster */
+		out_8(&i2c->i2c_i2cmr, 0x00);	/* Disable I2C 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 */
+		tmo = jiffies + 1 * HZ;
+		while (!(in_8(&i2c->i2c_i2cer) & 0x12 || time_after(jiffies, tmo))) ;/* Busy wait, with a timeout */
+	}
+
+	if ( res < 0) {
+		force_close(cpm);
+		if (cpm_debug)
+			printk("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) {
+		printk("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)
+			printk("IIC write; no ack\n");
+		return 0;
+	}
+
+	if (tbdf->cbd_sc & BD_SC_READY) {
+		if (cpm_debug)
+			printk("IIC write; complete but tbuf ready\n");
+		return 0;
+	}
+
+	return count;
+}
+
+/* See if an IIC address exists..
+ * addr = 7 bit address, unshifted
+ */
+static int cpm_iic_tryaddress(struct i2c_algo_8xx_data *cpm, int addr)
+{
+	iic_t *iip = cpm->iip;
+	i2c8xx_t *i2c = cpm->i2c;
+	cbd_t *tbdf, *rbdf;
+	u_char *tb;
+	unsigned long len;
+	int res = 0;
+
+	if (cpm_debug > 1)
+		printk("cpm_iic_tryaddress(cpm=%p,addr=%d)\n", cpm, addr);
+
+	/* check for and use a microcode relocation patch */
+	if (cpm->reloc) {
+		cpm_reset_iic_params(iip);
+	}
+
+	if (cpm_debug && addr == 0) {
+		printk("iip %p, dp_addr 0x%x\n", cpm->iip, cpm->dp_addr);
+		printk("iic_tbase %d, r_tbase %d\n", iip->iic_tbase, r_tbase);
+	}
+
+	tbdf = (cbd_t *) cpm_dpram_addr(iip->iic_tbase);
+	rbdf = (cbd_t *) cpm_dpram_addr(iip->iic_rbase);
+
+	tb = cpm->temp;
+	tb = (u_char *) (((uint) tb + 15) & ~15);
+
+	/* do a simple read */
+	tb[0] = (addr << 1) | 1;	/* device address (+ read) */
+	len = 2;
+
+	flush_dcache_range((unsigned long)tb, (unsigned long)(tb + 2));
+
+	tbdf->cbd_bufaddr = __pa(tb);
+	tbdf->cbd_datlen = len;
+	tbdf->cbd_sc = BD_SC_READY | BD_SC_LAST | BD_SC_WRAP | BD_IIC_START;
+
+	rbdf->cbd_datlen = 0;
+	rbdf->cbd_bufaddr = __pa(tb + 2);
+	rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP | BD_SC_INTRPT;
+
+	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 */
+
+	if (cpm_debug > 1)
+		printk("about to sleep\n");
+
+	/* wait for IIC transfer */
+	res = wait_event_interruptible_timeout(iic_wait, 0, 1 * HZ);
+
+#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 ( res < 0) {
+		force_close(cpm);
+		if (cpm_debug)
+			printk("IIC tryaddress: timeout!\n");
+		return -EIO;
+	}
+
+	if (cpm_debug > 1)
+		printk("back from sleep\n");
+
+	if (tbdf->cbd_sc & BD_SC_NAK) {
+		if (cpm_debug > 1)
+			printk("IIC try; no ack\n");
+		return 0;
+	}
+
+	if (tbdf->cbd_sc & BD_SC_READY) {
+		printk("IIC try; complete but tbuf ready\n");
+	}
+
+	return 1;
+}
+
+static int cpm_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+	struct i2c_algo_8xx_data *cpm = adap->algo_data;
+	struct i2c_msg *pmsg;
+	int i, ret;
+	u_char addr;
+
+	for (i = 0; i < num; i++) {
+		pmsg = &msgs[i];
+
+		if (cpm_debug)
+			printk("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_NOSTART)) {
+		}
+		if (pmsg->flags & I2C_M_RD) {
+			/* read bytes into buffer */
+			ret = cpm_iic_read(cpm, addr, pmsg->buf, pmsg->len);
+			if (cpm_debug)
+				printk("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(cpm, addr, pmsg->buf, pmsg->len);
+			if (cpm_debug)
+				printk("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_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR |
+	    I2C_FUNC_PROTOCOL_MANGLING;
+}
+
+/* -----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)
+{
+	struct i2c_algo_8xx_data *cpm = adap->algo_data;
+	int i;
+
+	if (cpm_debug)
+		printk("i2c-algo-8xx.o: hw routines for %s registered.\n",
+		       adap->name);
+
+	/* register new adapter to i2c module... */
+
+	adap->algo = &cpm_algo;
+
+	i2c_add_adapter(adap);
+	cpm_iic_init(cpm);
+
+	/* scan bus */
+	if (cpm_scan) {
+		printk(KERN_INFO " i2c-algo-8xx.o: scanning bus %s...\n",
+			adap->name);
+		for (i = 0; i < 128; i++) {
+			if (cpm_iic_tryaddress(cpm, i))
+				printk("(%02x)",i<<1);
+		}
+		printk("\n");
+	}
+
+	return 0;
+}
+
+int i2c_8xx_del_bus(struct i2c_adapter *adap)
+{
+	struct i2c_algo_8xx_data *cpm = adap->algo_data;
+
+	cpm_iic_shutdown(cpm);
+
+	return i2c_del_adapter(adap);
+}
+
+EXPORT_SYMBOL(i2c_8xx_add_bus);
+EXPORT_SYMBOL(i2c_8xx_del_bus);
+
+MODULE_AUTHOR("Brad Parker <[email protected]>");
+MODULE_DESCRIPTION("I2C-Bus MPC8XX algorithm");
+MODULE_LICENSE("GPL");
drivers/i2c/busses/Kconfig +2 -2
index 4afc795..83d9aef 100644
@@ -385,8 +385,8 @@ config I2C_PROSAVAGE
 	  will be called i2c-prosavage.
 
 config I2C_RPXLITE
-	tristate "Embedded Planet RPX Lite/Classic support"
-	depends on RPXLITE || RPXCLASSIC
+	tristate "Embedded Planet RPX Lite/Classic and Freescale 86x/885 ads support"
+	depends on RPXLITE || RPXCLASSIC || MPC86XADS || MPC885ADS
 	select I2C_ALGO8XX
 
 config I2C_S3C2410
drivers/i2c/busses/i2c-rpx.c +85 -44
index 8764df0..ebddd94 100644
@@ -14,88 +14,129 @@
 #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/mpc8xx.h>
 #include <asm/commproc.h>
+#include <asm/fs_pd.h>
 
 
-static void
-rpx_iic_init(struct i2c_algo_8xx_data *data)
+struct m8xx_i2c {
+	char *base;
+	struct device *dev;
+	struct i2c_adapter adap;
+	struct i2c_algo_8xx_data *algo_8xx;
+};
+
+static struct i2c_algo_8xx_data rpx_data;
+
+static struct i2c_adapter rpx_ops = {
+	.owner		= THIS_MODULE,
+	.name		= "m8xx",
+	.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 platform_device *pdev = to_platform_device(i2c->dev);
+
+	cp = data->cp = (cpm8xx_t *)immr_map(im_cpm);	/* pointer to Communication Processor */
 
-	cp = cpmp;	/* Get pointer to Communication Processor */
-	immap = (immap_t *)IMAP_ADDR;	/* and to internal registers */
+	data->irq = platform_get_irq_byname(pdev, "interrupt");
+  
+	if (data->irq < 0)
+		return -EINVAL;
 
-	data->iip = (iic_t *)&cp->cp_dparam[PROFF_IIC];
+	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pram");
+	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;
-
-	/* Initialize Port B IIC pins.
-	*/
-	cp->cp_pbpar |= 0x00000030;
-	cp->cp_pbdir |= 0x00000030;
-	cp->cp_pbodr |= 0x00000030;
+	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
+	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 device *device)
 {
-	/* install interrupt handler */
-	cpm_install_handler(irq, func, data);
-
-	return 0;
-}
+	int result = 0;
+	struct m8xx_i2c *i2c;
+	struct platform_device *pdev = to_platform_device(device);
 
-static struct i2c_algo_8xx_data rpx_data = {
-	.setisr = rpx_install_isr
-};
+	if (!(i2c = kmalloc(sizeof(*i2c), GFP_KERNEL))) {
+		return -ENOMEM;
+	}
+	memset(i2c, 0, sizeof(*i2c));
+	i2c->dev = device;
+	i2c->algo_8xx = &rpx_data;
 
-static struct i2c_adapter rpx_ops = {
-	.owner		= THIS_MODULE,
-	.name		= "m8xx",
-	.id		= I2C_HW_MPC8XX_EPON,
-	.algo_data	= &rpx_data,
-};
+	rpx_iic_init(i2c);
 
-int __init i2c_rpx_init(void)
-{
-	printk(KERN_INFO "i2c-rpx: i2c MPC8xx driver\n");
+	dev_set_drvdata(device, 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 = &pdev->dev;
 
-	if (i2c_8xx_add_bus(&rpx_ops) < 0) {
+	if ((result = i2c_8xx_add_bus(&rpx_ops) < 0)) {
 		printk(KERN_ERR "i2c-rpx: Unable to register with I2C\n");
-		return -ENODEV;
+		kfree(i2c);
 	}
 
+	return result;
+}
+
+
+static int i2c_rpx_remove(struct device *device)
+{
+	struct m8xx_i2c *i2c = dev_get_drvdata(device);
+
+	i2c_8xx_del_bus(&i2c->adap);
+	dev_set_drvdata(device, NULL);
+
+	kfree(i2c);
 	return 0;
 }
 
-void __exit i2c_rpx_exit(void)
+
+/* Structure for a device driver */
+static struct device_driver i2c_rpx_driver = {
+	.name = "fsl-i2c-cpm",
+	.bus = &platform_bus_type,
+	.probe = i2c_rpx_probe,
+	.remove = i2c_rpx_remove,
+};
+
+static int __init i2c_rpx_init(void)
 {
-	i2c_8xx_del_bus(&rpx_ops);
+	return driver_register(&i2c_rpx_driver);
 }
 
-MODULE_AUTHOR("Dan Malek <[email protected]>");
-MODULE_DESCRIPTION("I2C-Bus adapter routines for MPC8xx boards");
+static void __exit i2c_rpx_exit(void)
+{
+	driver_unregister(&i2c_rpx_driver);
+}
 
 module_init(i2c_rpx_init);
 module_exit(i2c_rpx_exit);
+
+MODULE_AUTHOR("Dan Malek <[email protected]>");
+MODULE_DESCRIPTION("I2C-Bus adapter routines for MPC8xx boards");
include/linux/i2c-algo-8xx.h new file +29 -0
new file mode 100644
index 0000000..a644512
@@ -0,0 +1,29 @@
+/* ------------------------------------------------------------------------- */
+/* i2c-algo-8xx.h i2c driver algorithms for MPX8XX CPM			     */
+/* ------------------------------------------------------------------------- */
+
+/* $Id$ */
+
+#ifndef I2C_ALGO_8XX_H
+#define I2C_ALGO_8XX_H
+
+#include <linux/i2c.h>
+#include <asm/8xx_immap.h>
+#include <asm/commproc.h>
+
+struct i2c_algo_8xx_data {
+	uint dp_addr;
+	int reloc;
+	int irq;
+	i2c8xx_t *i2c;
+	iic_t	*iip;
+	cpm8xx_t *cp;
+
+	u_char	temp[513];
+};
+
+extern int i2c_8xx_add_bus(struct i2c_adapter *);
+extern int i2c_8xx_del_bus(struct i2c_adapter *);
+
+#endif /* I2C_ALGO_8XX_H */
+
Signature · 7 lines
-- 
Sincerely, Vitaly

_______________________________________________
Linuxppc-dev mailing list
[email protected]
https://ozlabs.org/mailman/listinfo/linuxppc-dev
#10 lore raw
Quoted 13 lines · Olof Johansson
Quoted 10 lines · Vitaly Bordug
arch/powerpc/platforms/8xx/mpc885ads_setup.c +1 -0
index 9bd81c7..d32e066 100644
@@ -51,6 +51,7 @@ static void init_smc1_uart_ioports(struc
 static void init_smc2_uart_ioports(struct fs_uart_platform_info* fpi);
 static void init_scc3_ioports(struct fs_platform_info* ptr);
 static void init_irda_ioports(void);
+static void init_i2c_ioports(void);
 
Quoted 16 lines

arch/powerpc/platforms/8xx/mpc885ads_setup.c #endif · line 128

Quoted 11 lines
arch/powerpc/platforms/8xx/mpc885ads_setup.c +4 -0
 void __init mpc885ads_board_setup(void)
 {
@@ -120,6 +121,10 @@ #endif
 #ifdef CONFIG_8XX_SIR
 	init_irda_ioports();
 #endif
+
+#ifdef CONFIG_I2C_RPXLITE
+	init_i2c_ioports();
+#endif
 

Does it hurt to always do it, even when the driver is not enabled? THat'd do away with an ifdef.

Well it will hurt - 8xx has conflicting io pin configurations, and nothing should be set up "just in case".

Quoted 11 lines

Also, if you move the static function up, you don't need a prototype. That goes for other stuff in this file too.

Quoted 7 lines
arch/powerpc/platforms/8xx/mpc885ads_setup.c +0 -0
 }
 
@@ -361,6 +366,15 @@ static void init_irda_ioports()
 	immr_unmap(cp);
 }
 
Quoted 14 lines

arch/powerpc/platforms/8xx/mpc885ads_setup.c static void init_irda_ioports()

Quoted 9 lines
arch/powerpc/platforms/8xx/mpc885ads_setup.c +8 -0
+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);
+}
 

Looks like you moved this out of the driver and into the platform code. What happens to other platforms where it's used?

i2c && 8xx combo never work with 2.6 at least in mainstream. That's why related stuff were scheduled to removal by Jean even, before I came up with this stuff.

Quoted 9 lines
Quoted 9 lines
arch/powerpc/platforms/8xx/mpc885ads_setup.c +1 -0
+
 int platform_device_skip(const char *model, int id)
 {

#ifdef CONFIG_MPC8xx_SECOND_ETH_SCC3

arch/powerpc/sysdev/fsl_soc.c +0 -0
index 419b688..7ecd537 100644
@@ -331,7 +331,7 @@ static int __init fsl_i2c_of_init(void)
Quoted 10 lines

arch/powerpc/sysdev/fsl_soc.c static int __init fsl_i2c_of_init(void) · line 337

Quoted 6 lines
arch/powerpc/sysdev/fsl_soc.c +1 -1
 	for (np = NULL, i = 0;
 	     (np = of_find_compatible_node(np, "i2c", "fsl-i2c")) != NULL;
 	     i++) {
-		struct resource r[2];
+		struct resource r[3];
 

Why? No code that uses it has been changed. Is it a bugfix?

maybe something stray...

Quoted 60 lines
Quoted 60 lines
arch/powerpc/sysdev/fsl_soc.c +53 -0
 		struct fsl_i2c_platform_data i2c_data;
 		const unsigned char *flags = NULL;
@@ -1215,4 +1215,63 @@ err:
 
 arch_initcall(fs_irda_of_init);
 
+static const char *i2c_regs = "regs";
+static const char *i2c_pram = "pram";
+static const char *i2c_irq = "interrupt";
+
+static int __init fsl_i2c_cpm_of_init(void)
+{
+	struct device_node *np;
+	unsigned int i;
+	struct platform_device *i2c_dev;
+	int ret;
+
+	for (np = NULL, i = 0;
+	     (np = of_find_compatible_node(np, "i2c", "fsl-i2c-cpm")) != NULL;
+	     i++) {
+		struct resource r[3];
+		struct fsl_i2c_platform_data i2c_data;
+
+		memset(&r, 0, sizeof(r));
+		memset(&i2c_data, 0, sizeof(i2c_data));
+
+		ret = of_address_to_resource(np, 0, &r[0]);
+		if (ret)
+			goto err;
+		r[0].name = i2c_regs;
+
+		ret = of_address_to_resource(np, 1, &r[1]);
+		if (ret)
+			goto err;
+		r[1].name = i2c_pram;
+
+		r[2].start = r[2].end = irq_of_parse_and_map(np, 0);
+		r[2].flags = IORESOURCE_IRQ;
+		r[2].name = i2c_irq;
+
+		i2c_dev = platform_device_register_simple("fsl-i2c-cpm", i, &r[0], 3);
+		if (IS_ERR(i2c_dev)) {
+			ret = PTR_ERR(i2c_dev);
+			goto err;
+		}
+
+		ret =
+		    platform_device_add_data(i2c_dev, &i2c_data,
+					     sizeof(struct
+						    fsl_i2c_platform_data));
+		if (ret)
+			goto unreg;
+	}
+
+	return 0;
+
+unreg:
+	platform_device_unregister(i2c_dev);
+err:
Quoted 10 lines

arch/powerpc/sysdev/fsl_soc.c err:

Quoted 5 lines
arch/powerpc/sysdev/fsl_soc.c +4 -0
+	return ret;
+}
+
+arch_initcall(fsl_i2c_cpm_of_init);
 

This could all be done with an of_platform driver instead, and avoid the above. (Someone else already suggested that I believe).

I know i know. But it was decided, while both ppc/ and powerpc/ wander around, platform devices way is preferrable. It is apparent why - so far only mpc885 is alive in arch/powerpc, and it is not going to change soon for 8xx. OTOH, some stuff from arch/ppc might use it/add BSP configuration etc.

Having some devices on of_device and some on pdev look kinda messy. Thanks,
Vitaly

#11 lore raw
Quoted 2 lines · Vitaly Bordug

i2c && 8xx combo never work with 2.6 at least in mainstream. That's why related stuff were scheduled to removal by Jean even, before I came up with this stuff.

[snip]

Quoted 6 lines
Quoted 4 lines

This could all be done with an of_platform driver instead, and avoid the above. (Someone else already suggested that I believe).

I know i know. But it was decided, while both ppc/ and powerpc/ wander around, platform devices way is preferrable.

The idea is to avoid regressions in arch/ppc; maintaining feature parity isn't necessary. If, as you say above, 8xx i2c was already broken in 2.6 (even in arch/ppc), then I see no reason not to go to an of_device right away.

Quoted 1 line

It is apparent why - so far only mpc885 is alive in arch/powerpc, and it is not going to change soon for 8xx.

Actually, I plan to do some arch/powerpc 8xx work over the next couple months, so hopefully it will change soon. :-)

-Scott