ppc32: Update PowerMac i2c management

Create a low-level synchronous implementation suitable for use
by the early boot platform code or other places where the
asynchronous driver isn't useable. This also exports the locks
used by the real driver to avoid collisions.
Use this new implementation to properly setup the clock chip
at boot on Apple latest laptops
parent e37fe183
......@@ -17,7 +17,8 @@ ifeq ($(CONFIG_APUS),y)
obj-$(CONFIG_PCI) += apus_pci.o
endif
obj-$(CONFIG_PPC_PMAC) += pmac_pic.o pmac_setup.o pmac_time.o \
pmac_feature.o pmac_pci.o pmac_sleep.o
pmac_feature.o pmac_pci.o pmac_sleep.o \
pmac_low_i2c.o
obj-$(CONFIG_PPC_CHRP) += chrp_setup.o chrp_time.o chrp_pci.o
obj-$(CONFIG_PPC_PREP) += prep_pci.o prep_time.o prep_setup.o
ifeq ($(CONFIG_PPC_PMAC),y)
......
......@@ -41,6 +41,7 @@
#include <asm/pmac_feature.h>
#include <asm/dbdma.h>
#include <asm/pci-bridge.h>
#include <asm/pmac_low_i2c.h>
#undef DEBUG_FEATURE
......@@ -2601,6 +2602,99 @@ set_initial_features(void)
MACIO_BIC(HEATHROW_FCR, HRW_SOUND_POWER_N);
}
/* Hack for bumping clock speed on the new PowerBooks and the
* iBook G4. This implements the "platform-do-clockspreading" OF
* property. For safety, we also check the product ID in the
* device-tree to make reasonably sure we won't set wrong values
* in the clock chip.
*
* Of course, ultimately, we have to implement a real parser for
* the platform-do-* stuff...
*/
while (machine_is_compatible("PowerBook5,2") ||
machine_is_compatible("PowerBook5,3") ||
machine_is_compatible("PowerBook6,2") ||
machine_is_compatible("PowerBook6,3")) {
struct device_node *ui2c = of_find_node_by_type(NULL, "i2c");
struct device_node *dt = of_find_node_by_name(NULL, "device-tree");
u8 buffer[9];
u32 *productID;
int i, rc, changed = 0;
if (dt == NULL)
break;
productID = (u32 *)get_property(dt, "pid#", NULL);
if (productID == NULL)
break;
while(ui2c) {
struct device_node *p = of_get_parent(ui2c);
if (p && !strcmp(p->name, "uni-n"))
break;
ui2c = of_find_node_by_type(np, "i2c");
}
if (ui2c == NULL)
break;
DBG("Trying to bump clock speed for PID: %08x...\n", *productID);
rc = pmac_low_i2c_open(ui2c, 1);
if (rc != 0)
break;
pmac_low_i2c_setmode(ui2c, pmac_low_i2c_mode_combined);
rc = pmac_low_i2c_xfer(ui2c, 0xd2 | pmac_low_i2c_read, 0x80, buffer, 9);
DBG("read result: %d,", rc);
if (rc != 0) {
pmac_low_i2c_close(ui2c);
break;
}
for (i=0; i<9; i++)
DBG(" %02x", buffer[i]);
DBG("\n");
switch(*productID) {
case 0x1182: /* AlBook 12" rev 2 */
case 0x1183: /* iBook G4 12" */
buffer[0] = (buffer[0] & 0x8f) | 0x70;
buffer[2] = (buffer[2] & 0x7f) | 0x00;
buffer[5] = (buffer[5] & 0x80) | 0x31;
buffer[6] = (buffer[6] & 0x40) | 0xb0;
buffer[7] = (buffer[7] & 0x00) | 0xc0;
buffer[8] = (buffer[8] & 0x00) | 0x30;
changed = 1;
break;
case 0x3142: /* AlBook 15" (ATI M10) */
case 0x3143: /* AlBook 17" (ATI M10) */
buffer[0] = (buffer[0] & 0xaf) | 0x50;
buffer[2] = (buffer[2] & 0x7f) | 0x00;
buffer[5] = (buffer[5] & 0x80) | 0x31;
buffer[6] = (buffer[6] & 0x40) | 0xb0;
buffer[7] = (buffer[7] & 0x00) | 0xd0;
buffer[8] = (buffer[8] & 0x00) | 0x30;
changed = 1;
break;
default:
DBG("i2c-hwclock: Machine model not handled\n");
break;
}
if (!changed) {
pmac_low_i2c_close(ui2c);
break;
}
pmac_low_i2c_setmode(ui2c, pmac_low_i2c_mode_stdsub);
rc = pmac_low_i2c_xfer(ui2c, 0xd2 | pmac_low_i2c_write, 0x80, buffer, 9);
DBG("write result: %d,", rc);
pmac_low_i2c_setmode(ui2c, pmac_low_i2c_mode_combined);
rc = pmac_low_i2c_xfer(ui2c, 0xd2 | pmac_low_i2c_read, 0x80, buffer, 9);
DBG("read result: %d,", rc);
if (rc != 0) {
pmac_low_i2c_close(ui2c);
break;
}
for (i=0; i<9; i++)
DBG(" %02x", buffer[i]);
pmac_low_i2c_close(ui2c);
break;
}
#endif /* CONFIG_POWER4 */
/* On all machines, switch modem & serial ports off */
np = find_devices("ch-a");
......@@ -2627,6 +2721,9 @@ pmac_feature_init(void)
return;
}
/* Setup low-level i2c stuffs */
pmac_init_low_i2c();
/* Probe machine type */
if (probe_motherboard())
printk(KERN_WARNING "Unknown PowerMac !\n");
......
/*
* arch/ppc/platforms/pmac_low_i2c.c
*
* Copyright (C) 2003 Ben. Herrenschmidt (benh@kernel.crashing.org)
*
* 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 file contains some low-level i2c access routines that
* need to be used by various bits of the PowerMac platform code
* at times where the real asynchronous & interrupt driven driver
* cannot be used. The API borrows some semantics from the darwin
* driver in order to ease the implementation of the platform
* properties parser
*/
#include <linux/config.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/adb.h>
#include <linux/pmu.h>
#include <asm/keylargo.h>
#include <asm/uninorth.h>
#include <asm/io.h>
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/pmac_low_i2c.h>
#define MAX_LOW_I2C_HOST 4
#if 1
#define DBG(x...) do {\
printk(KERN_DEBUG "KW:" x); \
} while(0)
#else
#define DBGG(x...)
#endif
struct low_i2c_host;
typedef int (*low_i2c_func_t)(struct low_i2c_host *host, u8 addr, u8 sub, u8 *data, int len);
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 */
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 */
int bsteps; /* And register stepping */
int speed; /* And speed */
};
static struct low_i2c_host low_i2c_hosts[MAX_LOW_I2C_HOST];
/* No locking is necessary on allocation, we are running way before
* anything can race with us
*/
static struct low_i2c_host *find_low_i2c_host(struct device_node *np)
{
int i;
for (i = 0; i < MAX_LOW_I2C_HOST; i++)
if (low_i2c_hosts[i].np == np)
return &low_i2c_hosts[i];
return NULL;
}
/*
*
* i2c-keywest implementation (UniNorth, U2, U3, Keylargo's)
*
*/
/*
* Keywest i2c definitions borrowed from drivers/i2c/i2c-keywest.h,
* should be moved somewhere in include/asm-ppc/
*/
/* Register indices */
typedef enum {
reg_mode = 0,
reg_control,
reg_status,
reg_isr,
reg_ier,
reg_addr,
reg_subaddr,
reg_data
} reg_t;
/* Mode register */
#define KW_I2C_MODE_100KHZ 0x00
#define KW_I2C_MODE_50KHZ 0x01
#define KW_I2C_MODE_25KHZ 0x02
#define KW_I2C_MODE_DUMB 0x00
#define KW_I2C_MODE_STANDARD 0x04
#define KW_I2C_MODE_STANDARDSUB 0x08
#define KW_I2C_MODE_COMBINED 0x0C
#define KW_I2C_MODE_MODE_MASK 0x0C
#define KW_I2C_MODE_CHAN_MASK 0xF0
/* Control register */
#define KW_I2C_CTL_AAK 0x01
#define KW_I2C_CTL_XADDR 0x02
#define KW_I2C_CTL_STOP 0x04
#define KW_I2C_CTL_START 0x08
/* Status register */
#define KW_I2C_STAT_BUSY 0x01
#define KW_I2C_STAT_LAST_AAK 0x02
#define KW_I2C_STAT_LAST_RW 0x04
#define KW_I2C_STAT_SDA 0x08
#define KW_I2C_STAT_SCL 0x10
/* IER & ISR registers */
#define KW_I2C_IRQ_DATA 0x01
#define KW_I2C_IRQ_ADDR 0x02
#define KW_I2C_IRQ_STOP 0x04
#define KW_I2C_IRQ_START 0x08
#define KW_I2C_IRQ_MASK 0x0F
/* State machine states */
enum {
state_idle,
state_addr,
state_read,
state_write,
state_stop,
state_dead
};
#define WRONG_STATE(name) do {\
printk(KERN_DEBUG "KW: wrong state. Got %s, state: %s (isr: %02x)\n", \
name, __kw_state_names[state], isr); \
} while(0)
static const char *__kw_state_names[] = {
"state_idle",
"state_addr",
"state_read",
"state_write",
"state_stop",
"state_dead"
};
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));
}
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);
(void)__kw_read_reg(host, reg_subaddr);
}
#define kw_write_reg(reg, val) __kw_write_reg(host, reg, val)
#define kw_read_reg(reg) __kw_read_reg(host, reg)
/* Don't schedule, the g5 fan controller is too
* timing sensitive
*/
static u8 kw_wait_interrupt(struct low_i2c_host* host)
{
int i;
u8 isr;
for (i = 0; i < 200000; i++) {
isr = kw_read_reg(reg_isr) & KW_I2C_IRQ_MASK;
if (isr != 0)
return isr;
udelay(1);
}
return isr;
}
static int kw_handle_interrupt(struct low_i2c_host *host, int state, int rw, int *rc, u8 **data, int *len, u8 isr)
{
u8 ack;
if (isr == 0) {
if (state != state_stop) {
DBG("KW: Timeout !\n");
*rc = -EIO;
goto stop;
}
if (state == state_stop) {
ack = kw_read_reg(reg_status);
if (!(ack & KW_I2C_STAT_BUSY)) {
state = state_idle;
kw_write_reg(reg_ier, 0x00);
}
}
return state;
}
if (isr & KW_I2C_IRQ_ADDR) {
ack = kw_read_reg(reg_status);
if (state != state_addr) {
kw_write_reg(reg_isr, KW_I2C_IRQ_ADDR);
WRONG_STATE("KW_I2C_IRQ_ADDR");
*rc = -EIO;
goto stop;
}
if ((ack & KW_I2C_STAT_LAST_AAK) == 0) {
*rc = -ENODEV;
DBG("KW: NAK on address\n");
return state_stop;
} else {
if (rw) {
state = state_read;
if (*len > 1)
kw_write_reg(reg_control, KW_I2C_CTL_AAK);
} else {
state = state_write;
kw_write_reg(reg_data, **data);
(*data)++; (*len)--;
}
}
kw_write_reg(reg_isr, KW_I2C_IRQ_ADDR);
}
if (isr & KW_I2C_IRQ_DATA) {
if (state == state_read) {
**data = kw_read_reg(reg_data);
(*data)++; (*len)--;
kw_write_reg(reg_isr, KW_I2C_IRQ_DATA);
if ((*len) == 0)
state = state_stop;
else if ((*len) == 1)
kw_write_reg(reg_control, 0);
} else if (state == state_write) {
ack = kw_read_reg(reg_status);
if ((ack & KW_I2C_STAT_LAST_AAK) == 0) {
DBG("KW: nack on data write\n");
*rc = -EIO;
goto stop;
} else if (*len) {
kw_write_reg(reg_data, **data);
(*data)++; (*len)--;
} else {
kw_write_reg(reg_control, KW_I2C_CTL_STOP);
state = state_stop;
*rc = 0;
}
kw_write_reg(reg_isr, KW_I2C_IRQ_DATA);
} else {
kw_write_reg(reg_isr, KW_I2C_IRQ_DATA);
WRONG_STATE("KW_I2C_IRQ_DATA");
if (state != state_stop) {
*rc = -EIO;
goto stop;
}
}
}
if (isr & KW_I2C_IRQ_STOP) {
kw_write_reg(reg_isr, KW_I2C_IRQ_STOP);
if (state != state_stop) {
WRONG_STATE("KW_I2C_IRQ_STOP");
*rc = -EIO;
}
return state_idle;
}
if (isr & KW_I2C_IRQ_START)
kw_write_reg(reg_isr, KW_I2C_IRQ_START);
return state;
stop:
kw_write_reg(reg_control, KW_I2C_CTL_STOP);
return state_stop;
}
static int keywest_low_i2c_func(struct low_i2c_host *host, u8 addr, u8 subaddr, u8 *data, int len)
{
u8 mode_reg = host->speed;
int state = state_addr;
int rc = 0;
/* Setup mode & subaddress if any */
switch(host->mode) {
case pmac_low_i2c_mode_dumb:
printk(KERN_ERR "low_i2c: Dumb mode not supported !\n");
return -EINVAL;
case pmac_low_i2c_mode_std:
mode_reg |= KW_I2C_MODE_STANDARD;
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;
}
/* Setup channel & clear pending irqs */
kw_write_reg(reg_isr, kw_read_reg(reg_isr));
kw_write_reg(reg_mode, mode_reg | (host->channel << 4));
kw_write_reg(reg_status, 0);
/* Set up address and r/w bit */
kw_write_reg(reg_addr, addr);
/* Start sending address & disable interrupt*/
kw_write_reg(reg_ier, 0 /*KW_I2C_IRQ_MASK*/);
kw_write_reg(reg_control, KW_I2C_CTL_XADDR);
/* State machine, to turn into an interrupt handler */
while(state != state_idle) {
u8 isr = kw_wait_interrupt(host);
state = kw_handle_interrupt(host, state, addr & 1, &rc, &data, &len, isr);
}
return rc;
}
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;
struct device_node *parent;
if (host == NULL) {
printk(KERN_ERR "low_i2c: Can't allocate host for %s\n",
np->full_name);
return;
}
memset(host, 0, sizeof(*host));
init_MUTEX(&host->mutex);
host->np = of_node_get(np);
psteps = (unsigned long *)get_property(np, "AAPL,address-step", NULL);
steps = psteps ? (*psteps) : 0x10;
for (host->bsteps = 0; (steps & 0x01) == 0; host->bsteps++)
steps >>= 1;
parent = of_get_parent(np);
host->num_channels = 1;
if (parent && parent->name[0] == 'u') {
host->num_channels = 2;
aoffset = 3;
}
/* Select interface rate */
host->speed = KW_I2C_MODE_100KHZ;
prate = (unsigned long *)get_property(np, "AAPL,i2c-rate", NULL);
if (prate) switch(*prate) {
case 100:
host->speed = KW_I2C_MODE_100KHZ;
break;
case 50:
host->speed = KW_I2C_MODE_50KHZ;
break;
case 25:
host->speed = KW_I2C_MODE_25KHZ;
break;
}
host->mode = pmac_low_i2c_mode_std;
host->base = (unsigned long)ioremap(np->addrs[0].address + aoffset,
np->addrs[0].size);
host->func = keywest_low_i2c_func;
}
/*
*
* PMU implementation
*
*/
#ifdef CONFIG_ADB_PMU
static int pmu_low_i2c_func(struct low_i2c_host *host, u8 addr, u8 sub, u8 *data, int len)
{
// TODO
return -ENODEV;
}
static void pmu_low_i2c_add(struct device_node *np)
{
struct low_i2c_host *host = find_low_i2c_host(NULL);
if (host == NULL) {
printk(KERN_ERR "low_i2c: Can't allocate host for %s\n",
np->full_name);
return;
}
memset(host, 0, sizeof(*host));
init_MUTEX(&host->mutex);
host->np = of_node_get(np);
host->num_channels = 3;
host->mode = pmac_low_i2c_mode_std;
host->func = pmu_low_i2c_func;
}
#endif /* CONFIG_ADB_PMU */
void __init pmac_init_low_i2c(void)
{
struct device_node *np;
/* Probe keywest-i2c busses */
np = of_find_compatible_node(NULL, "i2c", "keywest-i2c");
while(np) {
keywest_low_i2c_add(np);
np = of_find_compatible_node(np, "i2c", "keywest-i2c");
}
#ifdef CONFIG_ADB_PMU
/* Probe PMU busses */
np = of_find_node_by_name(NULL, "via-pmu");
if (np)
pmu_low_i2c_add(np);
#endif /* CONFIG_ADB_PMU */
/* TODO: Add CUDA support as well */
}
int pmac_low_i2c_lock(struct device_node *np)
{
struct low_i2c_host *host = find_low_i2c_host(np);
if (!host)
return -ENODEV;
down(&host->mutex);
return 0;
}
EXPORT_SYMBOL(pmac_low_i2c_lock);
int pmac_low_i2c_unlock(struct device_node *np)
{
struct low_i2c_host *host = find_low_i2c_host(np);
if (!host)
return -ENODEV;
up(&host->mutex);
return 0;
}
EXPORT_SYMBOL(pmac_low_i2c_unlock);
int pmac_low_i2c_open(struct device_node *np, int channel)
{
struct low_i2c_host *host = find_low_i2c_host(np);
if (!host)
return -ENODEV;
if (channel >= host->num_channels)
return -EINVAL;
down(&host->mutex);
host->is_open = 1;
host->channel = channel;
return 0;
}
EXPORT_SYMBOL(pmac_low_i2c_open);
int pmac_low_i2c_close(struct device_node *np)
{
struct low_i2c_host *host = find_low_i2c_host(np);
if (!host)
return -ENODEV;
host->is_open = 0;
up(&host->mutex);
return 0;
}
EXPORT_SYMBOL(pmac_low_i2c_close);
int pmac_low_i2c_setmode(struct device_node *np, int mode)
{
struct low_i2c_host *host = find_low_i2c_host(np);
if (!host)
return -ENODEV;
WARN_ON(!host->is_open);
host->mode = mode;
return 0;
}
EXPORT_SYMBOL(pmac_low_i2c_setmode);
int pmac_low_i2c_xfer(struct device_node *np, u8 addrdir, u8 subaddr, u8 *data, int len)
{
struct low_i2c_host *host = find_low_i2c_host(np);
if (!host)
return -ENODEV;
WARN_ON(!host->is_open);
return host->func(host, addrdir, subaddr, data, len);
}
EXPORT_SYMBOL(pmac_low_i2c_xfer);
......@@ -469,10 +469,6 @@ pmac_restart(char *cmd)
struct adb_request req;
#endif /* CONFIG_ADB_CUDA */
#ifdef CONFIG_NVRAM
pmac_nvram_update();
#endif
switch (sys_ctrler) {
#ifdef CONFIG_ADB_CUDA
case SYS_CTRLER_CUDA:
......@@ -498,10 +494,6 @@ pmac_power_off(void)
struct adb_request req;
#endif /* CONFIG_ADB_CUDA */
#ifdef CONFIG_NVRAM
pmac_nvram_update();
#endif
switch (sys_ctrler) {
#ifdef CONFIG_ADB_CUDA
case SYS_CTRLER_CUDA:
......@@ -637,11 +629,6 @@ pmac_init(unsigned long r3, unsigned long r4, unsigned long r5,
ppc_md.get_rtc_time = pmac_get_rtc_time;
ppc_md.calibrate_decr = pmac_calibrate_decr;
#ifdef CONFIG_NVRAM
ppc_md.nvram_read_val = pmac_nvram_read_byte;
ppc_md.nvram_write_val = pmac_nvram_write_byte;
#endif
ppc_md.find_end_of_memory = pmac_find_end_of_memory;
ppc_md.feature_call = pmac_do_feature_call;
......@@ -685,6 +672,14 @@ pmac_declare_of_platform_devices(void)
break;
}
}
np = find_devices("u3");
if (np) {
for (np = np->child; np != NULL; np = np->sibling)
if (strncmp(np->name, "i2c", 3) == 0) {
of_platform_device_create(np, "u3-i2c");
break;
}
}
np = find_devices("valkyrie");
if (np)
......
......@@ -26,6 +26,9 @@
2001/12/13 BenH New implementation
2001/12/15 BenH Add support for "byte" and "quick"
transfers. Add i2c_xfer routine.
2003/09/21 BenH Rework state machine with Paulus help
2004/01/21 BenH Merge in Greg KH changes, polled mode is back
2004/02/05 BenH Merge 64 bits fixes from the g5 ppc64 tree
My understanding of the various modes supported by keywest are:
......@@ -67,9 +70,35 @@
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/pmac_feature.h>
#include <asm/pmac_low_i2c.h>
#include "i2c-keywest.h"
/* Currently, we don't deal with the weird interrupt cascade of the G5
* machines with the ppc64 kernel, so use Polled mode on these
*/
#ifdef CONFIG_PPC64
#define POLLED_MODE
#else
#undef POLLED_MODE
#endif
/* Some debug macros */
#define WRONG_STATE(name) do {\
pr_debug("KW: wrong state. Got %s, state: %s (isr: %02x)\n", \
name, __kw_state_names[iface->state], isr); \
} while(0)
#ifdef DEBUG
static const char *__kw_state_names[] = {
"state_idle",
"state_addr",
"state_read",
"state_write",
"state_stop",
"state_dead"
};
#endif /* DEBUG */
MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
MODULE_DESCRIPTION("I2C driver for Apple's Keywest");
......@@ -78,121 +107,154 @@ MODULE_PARM(probe, "i");
static int probe = 0;
#ifdef POLLED_MODE
/* Don't schedule, the g5 fan controller is too
* timing sensitive
*/
static u8
wait_interrupt(struct keywest_iface* iface)
{
int i;
u8 isr;
for (i = 0; i < 200000; i++) {
isr = read_reg(reg_isr) & KW_I2C_IRQ_MASK;
if (isr != 0)
return isr;
udelay(10);
}
return isr;
}
#endif /* POLLED_MODE */
static void
do_stop(struct keywest_iface* iface, int result)
{
write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_STOP);
write_reg(reg_control, KW_I2C_CTL_STOP);
iface->state = state_stop;
iface->result = result;
}
/* Main state machine for standard & standard sub mode */
static int
static void
handle_interrupt(struct keywest_iface *iface, u8 isr)
{
int ack;
int rearm_timer = 1;
pr_debug("handle_interrupt(), got: %x, status: %x, state: %d\n",
isr, read_reg(reg_status), iface->state);
if (isr == 0 && iface->state != state_stop) {
do_stop(iface, -1);
return rearm_timer;
}
if (isr & KW_I2C_IRQ_STOP && iface->state != state_stop) {
iface->result = -1;
iface->state = state_stop;
}
switch(iface->state) {
case state_addr:
if (!(isr & KW_I2C_IRQ_ADDR)) {
do_stop(iface, -1);
break;
}
ack = read_reg(reg_status);
pr_debug("ack on set address: %x\n", ack);
if ((ack & KW_I2C_STAT_LAST_AAK) == 0) {
do_stop(iface, -1);
break;
if (isr == 0) {
if (iface->state != state_stop) {
pr_debug("KW: Timeout !\n");
do_stop(iface, -EIO);
}
/* Handle rw "quick" mode */
if (iface->datalen == 0)
do_stop(iface, 0);
else if (iface->read_write == I2C_SMBUS_READ) {
iface->state = state_read;
if (iface->datalen > 1)
write_reg(reg_control, read_reg(reg_control)
| KW_I2C_CTL_AAK);
} else {
iface->state = state_write;
pr_debug("write byte: %x\n", *(iface->data));
write_reg(reg_data, *(iface->data++));
iface->datalen--;
}
break;
case state_read:
if (!(isr & KW_I2C_IRQ_DATA)) {
do_stop(iface, -1);
break;
}
*(iface->data++) = read_reg(reg_data);
pr_debug("read byte: %x\n", *(iface->data-1));
iface->datalen--;
if (iface->datalen == 0)
iface->state = state_stop;
else
write_reg(reg_control, 0);
break;
case state_write:
if (!(isr & KW_I2C_IRQ_DATA)) {
do_stop(iface, -1);
break;
if (iface->state == state_stop) {
ack = read_reg(reg_status);
if (!(ack & KW_I2C_STAT_BUSY)) {
iface->state = state_idle;
write_reg(reg_ier, 0x00);
#ifndef POLLED_MODE
complete(&iface->complete);
#endif /* POLLED_MODE */
}
}
/* Check ack status */
return;
}
if (isr & KW_I2C_IRQ_ADDR) {
ack = read_reg(reg_status);
pr_debug("ack on data write: %x\n", ack);
if (iface->state != state_addr) {
write_reg(reg_isr, KW_I2C_IRQ_ADDR);
WRONG_STATE("KW_I2C_IRQ_ADDR");
do_stop(iface, -EIO);
return;
}
if ((ack & KW_I2C_STAT_LAST_AAK) == 0) {
do_stop(iface, -1);
break;
iface->state = state_stop;
iface->result = -ENODEV;
pr_debug("KW: NAK on address\n");
} else {
/* Handle rw "quick" mode */
if (iface->datalen == 0) {
do_stop(iface, 0);
} else if (iface->read_write == I2C_SMBUS_READ) {
iface->state = state_read;
if (iface->datalen > 1)
write_reg(reg_control, KW_I2C_CTL_AAK);
} else {
iface->state = state_write;
write_reg(reg_data, *(iface->data++));
iface->datalen--;
}
}
if (iface->datalen) {
pr_debug("write byte: %x\n", *(iface->data));
write_reg(reg_data, *(iface->data++));
write_reg(reg_isr, KW_I2C_IRQ_ADDR);
}
if (isr & KW_I2C_IRQ_DATA) {
if (iface->state == state_read) {
*(iface->data++) = read_reg(reg_data);
write_reg(reg_isr, KW_I2C_IRQ_DATA);
iface->datalen--;
} else
do_stop(iface, 0);
break;
case state_stop:
if (!(isr & KW_I2C_IRQ_STOP) && (++iface->stopretry) < 10)
do_stop(iface, -1);
else {
rearm_timer = 0;
iface->state = state_idle;
write_reg(reg_control, 0x00);
write_reg(reg_ier, 0x00);
complete(&iface->complete);
if (iface->datalen == 0)
iface->state = state_stop;
else if (iface->datalen == 1)
write_reg(reg_control, 0);
} else if (iface->state == state_write) {
/* Check ack status */
ack = read_reg(reg_status);
if ((ack & KW_I2C_STAT_LAST_AAK) == 0) {
pr_debug("KW: nack on data write (%x): %x\n",
iface->data[-1], ack);
do_stop(iface, -EIO);
} else if (iface->datalen) {
write_reg(reg_data, *(iface->data++));
iface->datalen--;
} else {
write_reg(reg_control, KW_I2C_CTL_STOP);
iface->state = state_stop;
iface->result = 0;
}
write_reg(reg_isr, KW_I2C_IRQ_DATA);
} else {
write_reg(reg_isr, KW_I2C_IRQ_DATA);
WRONG_STATE("KW_I2C_IRQ_DATA");
if (iface->state != state_stop)
do_stop(iface, -EIO);
}
break;
}
write_reg(reg_isr, isr);
return rearm_timer;
if (isr & KW_I2C_IRQ_STOP) {
write_reg(reg_isr, KW_I2C_IRQ_STOP);
if (iface->state != state_stop) {
WRONG_STATE("KW_I2C_IRQ_STOP");
iface->result = -EIO;
}
iface->state = state_idle;
write_reg(reg_ier, 0x00);
#ifndef POLLED_MODE
complete(&iface->complete);
#endif /* POLLED_MODE */
}
if (isr & KW_I2C_IRQ_START)
write_reg(reg_isr, KW_I2C_IRQ_START);
}
#ifndef POLLED_MODE
/* Interrupt handler */
static irqreturn_t
keywest_irq(int irq, void *dev_id, struct pt_regs *regs)
{
struct keywest_iface *iface = (struct keywest_iface *)dev_id;
unsigned long flags;
spin_lock(&iface->lock);
spin_lock_irqsave(&iface->lock, flags);
del_timer(&iface->timeout_timer);
if (handle_interrupt(iface, read_reg(reg_isr)))
mod_timer(&iface->timeout_timer, jiffies + POLL_TIMEOUT);
spin_unlock(&iface->lock);
handle_interrupt(iface, read_reg(reg_isr));
if (iface->state != state_idle) {
iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
add_timer(&iface->timeout_timer);
}
spin_unlock_irqrestore(&iface->lock, flags);
return IRQ_HANDLED;
}
......@@ -200,14 +262,20 @@ static void
keywest_timeout(unsigned long data)
{
struct keywest_iface *iface = (struct keywest_iface *)data;
unsigned long flags;
pr_debug("timeout !\n");
spin_lock_irq(&iface->lock);
if (handle_interrupt(iface, read_reg(reg_isr)))
mod_timer(&iface->timeout_timer, jiffies + POLL_TIMEOUT);
spin_unlock(&iface->lock);
spin_lock_irqsave(&iface->lock, flags);
handle_interrupt(iface, read_reg(reg_isr));
if (iface->state != state_idle) {
iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
add_timer(&iface->timeout_timer);
}
spin_unlock_irqrestore(&iface->lock, flags);
}
#endif /* POLLED_MODE */
/*
* SMBUS-type transfer entrypoint
*/
......@@ -228,46 +296,54 @@ keywest_smbus_xfer( struct i2c_adapter* adap,
int rc = 0;
if (iface->state == state_dead)
return -1;
return -ENXIO;
/* Prepare datas & select mode */
iface->cur_mode &= ~KW_I2C_MODE_MODE_MASK;
switch (size) {
case I2C_SMBUS_QUICK:
case I2C_SMBUS_QUICK:
len = 0;
buffer = NULL;
iface->cur_mode |= KW_I2C_MODE_STANDARD;
break;
case I2C_SMBUS_BYTE:
case I2C_SMBUS_BYTE:
len = 1;
buffer = &data->byte;
iface->cur_mode |= KW_I2C_MODE_STANDARD;
break;
case I2C_SMBUS_BYTE_DATA:
case I2C_SMBUS_BYTE_DATA:
len = 1;
buffer = &data->byte;
iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
break;
case I2C_SMBUS_WORD_DATA:
case I2C_SMBUS_WORD_DATA:
len = 2;
cur_word = cpu_to_le16(data->word);
buffer = (u8 *)&cur_word;
iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
break;
case I2C_SMBUS_BLOCK_DATA:
case I2C_SMBUS_BLOCK_DATA:
len = data->block[0];
buffer = &data->block[1];
iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
break;
default:
default:
return -1;
}
/* Turn a standardsub read into a combined mode access */
if (read_write == I2C_SMBUS_READ
&& (iface->cur_mode & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_STANDARDSUB) {
iface->cur_mode &= ~KW_I2C_MODE_MODE_MASK;
iface->cur_mode |= KW_I2C_MODE_COMBINED;
}
/* Original driver had this limitation */
if (len > 32)
len = 32;
down(&iface->sem);
if (pmac_low_i2c_lock(iface->node))
return -ENXIO;
pr_debug("chan: %d, addr: 0x%x, transfer len: %d, read: %d\n",
chan->chan_no, addr, len, read_write == I2C_SMBUS_READ);
......@@ -276,12 +352,11 @@ keywest_smbus_xfer( struct i2c_adapter* adap,
iface->datalen = len;
iface->state = state_addr;
iface->result = 0;
iface->stopretry = 0;
iface->read_write = read_write;
/* Setup channel & clear pending irqs */
write_reg(reg_mode, iface->cur_mode | (chan->chan_no << 4));
write_reg(reg_isr, read_reg(reg_isr));
write_reg(reg_mode, iface->cur_mode | (chan->chan_no << 4));
write_reg(reg_status, 0);
/* Set up address and r/w bit */
......@@ -293,15 +368,31 @@ keywest_smbus_xfer( struct i2c_adapter* adap,
|| (iface->cur_mode & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_COMBINED)
write_reg(reg_subaddr, command);
#ifndef POLLED_MODE
/* Arm timeout */
mod_timer(&iface->timeout_timer, jiffies + POLL_TIMEOUT);
iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
add_timer(&iface->timeout_timer);
#endif
/* Start sending address & enable interrupt*/
write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_XADDR);
write_reg(reg_control, KW_I2C_CTL_XADDR);
write_reg(reg_ier, KW_I2C_IRQ_MASK);
/* Wait interrupt operations completion */
#ifdef POLLED_MODE
pr_debug("using polled mode...\n");
/* State machine, to turn into an interrupt handler */
while(iface->state != state_idle) {
unsigned long flags;
u8 isr = wait_interrupt(iface);
spin_lock_irqsave(&iface->lock, flags);
handle_interrupt(iface, isr);
spin_unlock_irqrestore(&iface->lock, flags);
}
#else /* POLLED_MODE */
pr_debug("using interrupt mode...\n");
wait_for_completion(&iface->complete);
#endif /* POLLED_MODE */
rc = iface->result;
pr_debug("transfer done, result: %d\n", rc);
......@@ -310,7 +401,7 @@ keywest_smbus_xfer( struct i2c_adapter* adap,
data->word = le16_to_cpu(cur_word);
/* Release sem */
up(&iface->sem);
pmac_low_i2c_unlock(iface->node);
return rc;
}
......@@ -329,7 +420,11 @@ keywest_xfer( struct i2c_adapter *adap,
int i, completed;
int rc = 0;
down(&iface->sem);
if (iface->state == state_dead)
return -ENXIO;
if (pmac_low_i2c_lock(iface->node))
return -ENXIO;
/* Set adapter to standard mode */
iface->cur_mode &= ~KW_I2C_MODE_MODE_MASK;
......@@ -360,7 +455,6 @@ keywest_xfer( struct i2c_adapter *adap,
iface->datalen = pmsg->len;
iface->state = state_addr;
iface->result = 0;
iface->stopretry = 0;
if (pmsg->flags & I2C_M_RD)
iface->read_write = I2C_SMBUS_READ;
else
......@@ -373,15 +467,27 @@ keywest_xfer( struct i2c_adapter *adap,
(addr << 1) |
((iface->read_write == I2C_SMBUS_READ) ? 0x01 : 0x00));
#ifndef POLLED_MODE
/* Arm timeout */
mod_timer(&iface->timeout_timer, jiffies + POLL_TIMEOUT);
iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
add_timer(&iface->timeout_timer);
#endif
/* Start sending address & enable interrupt*/
write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_XADDR);
write_reg(reg_ier, KW_I2C_IRQ_MASK);
/* Wait interrupt operations completion */
write_reg(reg_control, KW_I2C_CTL_XADDR);
#ifdef POLLED_MODE
pr_debug("using polled mode...\n");
/* State machine, to turn into an interrupt handler */
while(iface->state != state_idle) {
u8 isr = wait_interrupt(iface);
handle_interrupt(iface, isr);
}
#else /* POLLED_MODE */
pr_debug("using interrupt mode...\n");
wait_for_completion(&iface->complete);
#endif /* POLLED_MODE */
rc = iface->result;
if (rc == 0)
......@@ -390,7 +496,7 @@ keywest_xfer( struct i2c_adapter *adap,
}
/* Release sem */
up(&iface->sem);
pmac_low_i2c_unlock(iface->node);
return completed;
}
......@@ -416,19 +522,23 @@ static struct i2c_algorithm keywest_algorithm = {
static int
create_iface(struct device_node *np, struct device *dev)
{
unsigned long steps, *psteps, *prate;
unsigned long steps;
unsigned bsteps, tsize, i, nchan, addroffset;
struct keywest_iface* iface;
u32 *psteps, *prate;
int rc;
psteps = (unsigned long *)get_property(np, "AAPL,address-step", NULL);
if (pmac_low_i2c_lock(np))
return -ENODEV;
psteps = (u32 *)get_property(np, "AAPL,address-step", NULL);
steps = psteps ? (*psteps) : 0x10;
/* Hrm... maybe we can be smarter here */
for (bsteps = 0; (steps & 0x01) == 0; bsteps++)
steps >>= 1;
if (!strcmp(np->parent->name, "uni-n")) {
if (np->parent->name[0] == 'u') {
nchan = 2;
addroffset = 3;
} else {
......@@ -441,12 +551,13 @@ create_iface(struct device_node *np, struct device *dev)
iface = (struct keywest_iface *) kmalloc(tsize, GFP_KERNEL);
if (iface == NULL) {
printk(KERN_ERR "i2c-keywest: can't allocate inteface !\n");
pmac_low_i2c_unlock(np);
return -ENOMEM;
}
memset(iface, 0, tsize);
init_MUTEX(&iface->sem);
spin_lock_init(&iface->lock);
init_completion(&iface->complete);
iface->node = of_node_get(np);
iface->bsteps = bsteps;
iface->chan_count = nchan;
iface->state = state_idle;
......@@ -458,16 +569,19 @@ create_iface(struct device_node *np, struct device *dev)
if (iface->base == 0) {
printk(KERN_ERR "i2c-keywest: can't map inteface !\n");
kfree(iface);
pmac_low_i2c_unlock(np);
return -ENOMEM;
}
#ifndef POLLED_MODE
init_timer(&iface->timeout_timer);
iface->timeout_timer.function = keywest_timeout;
iface->timeout_timer.data = (unsigned long)iface;
#endif
/* Select interface rate */
iface->cur_mode = 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:
iface->cur_mode = KW_I2C_MODE_100KHZ;
......@@ -480,11 +594,11 @@ create_iface(struct device_node *np, struct device *dev)
break;
default:
printk(KERN_WARNING "i2c-keywest: unknown rate %ldKhz, using 100KHz\n",
*prate);
(long)*prate);
}
/* Select standard sub mode */
iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
/* Select standard mode by default */
iface->cur_mode |= KW_I2C_MODE_STANDARD;
/* Write mode */
write_reg(reg_mode, iface->cur_mode);
......@@ -493,14 +607,17 @@ create_iface(struct device_node *np, struct device *dev)
write_reg(reg_ier, 0x00);
write_reg(reg_isr, KW_I2C_IRQ_MASK);
#ifndef POLLED_MODE
/* Request chip interrupt */
rc = request_irq(iface->irq, keywest_irq, 0, "keywest i2c", iface);
rc = request_irq(iface->irq, keywest_irq, SA_INTERRUPT, "keywest i2c", iface);
if (rc) {
printk(KERN_ERR "i2c-keywest: can't get IRQ %d !\n", iface->irq);
iounmap((void *)iface->base);
kfree(iface);
pmac_low_i2c_unlock(np);
return -ENODEV;
}
#endif /* POLLED_MODE */
dev_set_drvdata(dev, iface);
......@@ -539,6 +656,7 @@ create_iface(struct device_node *np, struct device *dev)
printk(KERN_INFO "Found KeyWest i2c on \"%s\", %d channel%s, stepping: %d bits\n",
np->parent->name, nchan, nchan > 1 ? "s" : "", bsteps);
pmac_low_i2c_unlock(np);
return 0;
}
......@@ -549,8 +667,10 @@ dispose_iface(struct device *dev)
int i, rc;
/* Make sure we stop all activity */
down(&iface->sem);
if (pmac_low_i2c_lock(iface->node))
return -ENODEV;
#ifndef POLLED_MODE
spin_lock_irq(&iface->lock);
while (iface->state != state_idle) {
spin_unlock_irq(&iface->lock);
......@@ -558,10 +678,14 @@ dispose_iface(struct device *dev)
schedule_timeout(HZ/10);
spin_lock_irq(&iface->lock);
}
#endif /* POLLED_MODE */
iface->state = state_dead;
#ifndef POLLED_MODE
spin_unlock_irq(&iface->lock);
free_irq(iface->irq, iface);
up(&iface->sem);
#endif /* POLLED_MODE */
pmac_low_i2c_unlock(iface->node);
/* Release all channels */
for (i=0; i<iface->chan_count; i++) {
......@@ -576,6 +700,7 @@ dispose_iface(struct device *dev)
}
iounmap((void *)iface->base);
dev_set_drvdata(dev, NULL);
of_node_put(iface->node);
kfree(iface);
return 0;
......@@ -634,8 +759,8 @@ static struct of_platform_driver i2c_keywest_of_platform_driver =
static int __init
i2c_keywest_init(void)
{
macio_register_driver(&i2c_keywest_macio_driver);
of_register_driver(&i2c_keywest_of_platform_driver);
macio_register_driver(&i2c_keywest_macio_driver);
return 0;
}
......@@ -643,8 +768,8 @@ i2c_keywest_init(void)
static void __exit
i2c_keywest_cleanup(void)
{
macio_unregister_driver(&i2c_keywest_macio_driver);
of_unregister_driver(&i2c_keywest_of_platform_driver);
macio_unregister_driver(&i2c_keywest_macio_driver);
}
module_init(i2c_keywest_init);
......
......@@ -51,20 +51,19 @@ typedef enum {
/* Physical interface */
struct keywest_iface
{
struct device_node *node;
unsigned long base;
unsigned bsteps;
int irq;
struct semaphore sem;
spinlock_t lock;
struct keywest_chan* channels;
struct keywest_chan *channels;
unsigned chan_count;
u8 cur_mode;
char read_write;
u8* data;
u8 *data;
unsigned datalen;
int state;
int result;
int stopretry;
struct timer_list timeout_timer;
struct completion complete;
};
......@@ -98,8 +97,7 @@ static inline void __write_reg(struct keywest_iface *iface, reg_t reg, u8 val)
{
out_8(((volatile u8 *)iface->base)
+ (((unsigned)reg) << iface->bsteps), val);
(void)__read_reg(iface, reg);
udelay(10);
(void)__read_reg(iface, reg_subaddr);
}
#define write_reg(reg, val) __write_reg(iface, reg, val)
......
/*
* include/asm-ppc/pmac_low_i2c.h
*
* Copyright (C) 2003 Ben. Herrenschmidt (benh@kernel.crashing.org)
*
* 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.
*
*/
#ifndef __PMAC_LOW_I2C_H__
#define __PMAC_LOW_I2C_H__
/* i2c mode (based on the platform functions format) */
enum {
pmac_low_i2c_mode_dumb = 1,
pmac_low_i2c_mode_std = 2,
pmac_low_i2c_mode_stdsub = 3,
pmac_low_i2c_mode_combined = 4,
};
/* RW bit in address */
enum {
pmac_low_i2c_read = 0x01,
pmac_low_i2c_write = 0x00
};
/* Init, called early during boot */
extern void pmac_init_low_i2c(void);
/* Locking functions exposed to i2c-keywest */
int pmac_low_i2c_lock(struct device_node *np);
int pmac_low_i2c_unlock(struct device_node *np);
/* Access functions for platform code */
int pmac_low_i2c_open(struct device_node *np, int channel);
int pmac_low_i2c_close(struct device_node *np);
int pmac_low_i2c_setmode(struct device_node *np, int mode);
int pmac_low_i2c_xfer(struct device_node *np, u8 addrdir, u8 subaddr, u8 *data, int len);
#endif /* __PMAC_LOW_I2C_H__ */
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