
From: Benjamin Herrenschmidt <benh@kernel.crashing.org>

The code in pmac_low_i2c.c is a low level synchronous version of the i2c
keywest driver for use by platform code early during boot or during
sleep/wakeup cycles to communicate with some motherboard chips, typically
clock chips.  It wasn't used on g5 until now, which is good because it wasn't
64 bits clean :)

This patch fixes it, and also remove the use of udelay() since it can be used
for synchronizing the HW timebase, and so must operate when it's frozen (and
our implementation of udelay uses that timebase).

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/arch/ppc64/kernel/pmac_low_i2c.c |   46 ++++++++++++++++++-------------
 1 files changed, 28 insertions(+), 18 deletions(-)

diff -puN arch/ppc64/kernel/pmac_low_i2c.c~ppc64-fix-g5-low-level-i2c-code arch/ppc64/kernel/pmac_low_i2c.c
--- 25/arch/ppc64/kernel/pmac_low_i2c.c~ppc64-fix-g5-low-level-i2c-code	2004-11-09 00:08:46.525618056 -0800
+++ 25-akpm/arch/ppc64/kernel/pmac_low_i2c.c	2004-11-09 00:08:46.529617448 -0800
@@ -16,9 +16,10 @@
  *  properties parser
  */
 
+#undef DEBUG
+
 #include <linux/config.h>
 #include <linux/types.h>
-#include <linux/delay.h>
 #include <linux/sched.h>
 #include <linux/init.h>
 #include <linux/module.h>
@@ -33,12 +34,12 @@
 
 #define MAX_LOW_I2C_HOST	4
 
-#if 1
+#ifdef DEBUG
 #define DBG(x...) do {\
 		printk(KERN_DEBUG "KW:" x);	\
 	} while(0)
 #else
-#define DBGG(x...)
+#define DBG(x...)
 #endif
 
 struct low_i2c_host;
@@ -50,11 +51,11 @@ struct low_i2c_host
 	struct device_node	*np;		/* OF device node */
 	struct semaphore	mutex;		/* Access mutex for use by i2c-keywest */
 	low_i2c_func_t		func;		/* Access function */
-	unsigned		is_open : 1;	/* Poor man's access control */
+	unsigned int		is_open : 1;	/* Poor man's access control */
 	int			mode;		/* Current mode */
 	int			channel;	/* Current channel */
 	int			num_channels;	/* Number of channels */
-	unsigned long		base;		/* For keywest-i2c, base address */
+	void __iomem		*base;		/* For keywest-i2c, base address */
 	int			bsteps;		/* And register stepping */
 	int			speed;		/* And speed */
 };
@@ -154,14 +155,12 @@ static const char *__kw_state_names[] = 
 
 static inline u8 __kw_read_reg(struct low_i2c_host *host, reg_t reg)
 {
-	return in_8(((volatile u8 *)host->base)
-		+ (((unsigned)reg) << host->bsteps));
+	return readb(host->base + (((unsigned int)reg) << host->bsteps));
 }
 
 static inline void __kw_write_reg(struct low_i2c_host *host, reg_t reg, u8 val)
 {
-	out_8(((volatile u8 *)host->base)
-		+ (((unsigned)reg) << host->bsteps), val);
+	writeb(val, host->base + (((unsigned)reg) << host->bsteps));
 	(void)__kw_read_reg(host, reg_subaddr);
 }
 
@@ -174,14 +173,19 @@ static inline void __kw_write_reg(struct
  */
 static u8 kw_wait_interrupt(struct low_i2c_host* host)
 {
-	int i;
+	int i, j;
 	u8 isr;
 	
-	for (i = 0; i < 200000; i++) {
+	for (i = 0; i < 100000; i++) {
 		isr = kw_read_reg(reg_isr) & KW_I2C_IRQ_MASK;
 		if (isr != 0)
 			return isr;
-		udelay(1);
+
+		/* This code is used with the timebase frozen, we cannot rely
+		 * on udelay ! For now, just use a bogus loop
+		 */
+		for (j = 1; j < 10000; j++)
+			mb();
 	}
 	return isr;
 }
@@ -190,6 +194,8 @@ static int kw_handle_interrupt(struct lo
 {
 	u8 ack;
 
+	DBG("kw_handle_interrupt(%s, isr: %x)\n", __kw_state_names[state], isr);
+
 	if (isr == 0) {
 		if (state != state_stop) {
 			DBG("KW: Timeout !\n");
@@ -301,11 +307,9 @@ static int keywest_low_i2c_func(struct l
 		break;
 	case pmac_low_i2c_mode_stdsub:
 		mode_reg |= KW_I2C_MODE_STANDARDSUB;
-		kw_write_reg(reg_subaddr, subaddr);
 		break;
 	case pmac_low_i2c_mode_combined:
 		mode_reg |= KW_I2C_MODE_COMBINED;
-		kw_write_reg(reg_subaddr, subaddr);
 		break;
 	}
 
@@ -317,6 +321,11 @@ static int keywest_low_i2c_func(struct l
 	/* Set up address and r/w bit */
 	kw_write_reg(reg_addr, addr);
 
+	/* Set up the sub address */
+	if ((mode_reg & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_STANDARDSUB
+	    || (mode_reg & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_COMBINED)
+		kw_write_reg(reg_subaddr, subaddr);
+
 	/* Start sending address & disable interrupt*/
 	kw_write_reg(reg_ier, 0 /*KW_I2C_IRQ_MASK*/);
 	kw_write_reg(reg_control, KW_I2C_CTL_XADDR);
@@ -333,7 +342,7 @@ static int keywest_low_i2c_func(struct l
 static void keywest_low_i2c_add(struct device_node *np)
 {
 	struct low_i2c_host	*host = find_low_i2c_host(NULL);
-	unsigned long		*psteps, *prate, steps, aoffset = 0;
+	u32			*psteps, *prate, steps, aoffset = 0;
 	struct device_node	*parent;
 
 	if (host == NULL) {
@@ -345,7 +354,7 @@ static void keywest_low_i2c_add(struct d
 
 	init_MUTEX(&host->mutex);
 	host->np = of_node_get(np);	
-	psteps = (unsigned long *)get_property(np, "AAPL,address-step", NULL);
+	psteps = (u32 *)get_property(np, "AAPL,address-step", NULL);
 	steps = psteps ? (*psteps) : 0x10;
 	for (host->bsteps = 0; (steps & 0x01) == 0; host->bsteps++)
 		steps >>= 1;
@@ -357,7 +366,7 @@ static void keywest_low_i2c_add(struct d
 	}
 	/* Select interface rate */
 	host->speed = KW_I2C_MODE_100KHZ;
-	prate = (unsigned long *)get_property(np, "AAPL,i2c-rate", NULL);
+	prate = (u32 *)get_property(np, "AAPL,i2c-rate", NULL);
 	if (prate) switch(*prate) {
 	case 100:
 		host->speed = KW_I2C_MODE_100KHZ;
@@ -369,8 +378,9 @@ static void keywest_low_i2c_add(struct d
 		host->speed = KW_I2C_MODE_25KHZ;
 		break;
 	}	
+
 	host->mode = pmac_low_i2c_mode_std;
-	host->base = (unsigned long)ioremap(np->addrs[0].address + aoffset,
+	host->base = ioremap(np->addrs[0].address + aoffset,
 						np->addrs[0].size);
 	host->func = keywest_low_i2c_func;
 }
_
