Commit e0e04bff authored by Benjamin Herrenschmidt's avatar Benjamin Herrenschmidt Committed by Linus Torvalds

[PATCH] ppc64: Fix G5 low level i2c code

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: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 482003f2
......@@ -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 low_i2c_host *host, reg_t reg, u8 val)
*/
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 low_i2c_host *host, int state, int rw, int
{
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 low_i2c_host *host, u8 addr, u8 subaddr,
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 low_i2c_host *host, u8 addr, u8 subaddr,
/* 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 low_i2c_host *host, u8 addr, u8 subaddr,
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 device_node *np)
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 device_node *np)
}
/* 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 device_node *np)
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;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment