[PATCH] ppc64: Fix G5 low level i2c code

Benjamin Herrenschmidt benh at kernel.crashing.org
Tue Nov 9 18:53:45 EST 2004


Hi !

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 at kernel.crashing.org>

Index: linux-work/arch/ppc64/kernel/pmac_low_i2c.c
===================================================================
--- linux-work.orig/arch/ppc64/kernel/pmac_low_i2c.c	2004-11-09 14:56:12.224427352 +1100
+++ linux-work/arch/ppc64/kernel/pmac_low_i2c.c	2004-11-09 16:26:21.187140032 +1100
@@ -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 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 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 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 @@
 {
 	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 @@
 		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 @@
 	/* 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 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 @@
 
 	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 @@
 	}
 	/* 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 @@
 		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;
 }





More information about the Linuxppc64-dev mailing list