Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/drivers/i2c/busses/i2c-keywest.c b/drivers/i2c/busses/i2c-keywest.c
new file mode 100644
index 0000000..dd0d4c4
--- /dev/null
+++ b/drivers/i2c/busses/i2c-keywest.c
@@ -0,0 +1,763 @@
+/*
+    i2c Support for Apple Keywest I2C Bus Controller
+
+    Copyright (c) 2001 Benjamin Herrenschmidt <benh@kernel.crashing.org>
+
+    Original work by
+    
+    Copyright (c) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
+
+    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.
+
+    Changes:
+
+    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:
+
+     - Dumb mode : not implemented, probably direct tweaking of lines
+     - Standard mode : simple i2c transaction of type
+         S Addr R/W A Data A Data ... T
+     - Standard sub mode : combined 8 bit subaddr write with data read
+         S Addr R/W A SubAddr A Data A Data ... T
+     - Combined mode : Subaddress and Data sequences appended with no stop
+         S Addr R/W A SubAddr S Addr R/W A Data A Data ... T
+
+    Currently, this driver uses only Standard mode for i2c xfer, and
+    smbus byte & quick transfers ; and uses StandardSub mode for
+    other smbus transfers instead of combined as we need that for the
+    sound driver to be happy
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/timer.h>
+#include <linux/spinlock.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+#include <asm/pmac_low_i2c.h>
+
+#include "i2c-keywest.h"
+
+#undef POLLED_MODE
+
+/* 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 */
+
+static int probe;
+
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_DESCRIPTION("I2C driver for Apple's Keywest");
+MODULE_LICENSE("GPL");
+module_param(probe, bool, 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, KW_I2C_CTL_STOP);
+	iface->state = state_stop;
+	iface->result = result;
+}
+
+/* Main state machine for standard & standard sub mode */
+static void
+handle_interrupt(struct keywest_iface *iface, u8 isr)
+{
+	int ack;
+	
+	if (isr == 0) {
+		if (iface->state != state_stop) {
+			pr_debug("KW: Timeout !\n");
+			do_stop(iface, -EIO);
+		}
+		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 */
+			}
+		}
+		return;
+	}
+
+	if (isr & KW_I2C_IRQ_ADDR) {
+		ack = read_reg(reg_status);
+		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) {
+			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--;
+			}
+		}
+		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--;
+			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);
+		}
+	}
+
+	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_irqsave(&iface->lock, flags);
+	del_timer(&iface->timeout_timer);
+	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;
+}
+
+static void
+keywest_timeout(unsigned long data)
+{
+	struct keywest_iface *iface = (struct keywest_iface *)data;
+	unsigned long flags;
+
+	pr_debug("timeout !\n");
+	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
+ */
+static s32
+keywest_smbus_xfer(	struct i2c_adapter*	adap,
+			u16			addr,
+			unsigned short		flags,
+			char			read_write,
+			u8			command,
+			int			size,
+			union i2c_smbus_data*	data)
+{
+	struct keywest_chan* chan = i2c_get_adapdata(adap);
+	struct keywest_iface* iface = chan->iface;
+	int len;
+	u8* buffer;
+	u16 cur_word;
+	int rc = 0;
+
+	if (iface->state == state_dead)
+		return -ENXIO;
+		
+	/* Prepare datas & select mode */
+	iface->cur_mode &= ~KW_I2C_MODE_MODE_MASK;
+	switch (size) {
+        case I2C_SMBUS_QUICK:
+	    	len = 0;
+	    	buffer = NULL;
+	    	iface->cur_mode |= KW_I2C_MODE_STANDARD;
+	    	break;
+        case I2C_SMBUS_BYTE:
+	    	len = 1;
+	    	buffer = &data->byte;
+	    	iface->cur_mode |= KW_I2C_MODE_STANDARD;
+	    	break;
+        case I2C_SMBUS_BYTE_DATA:
+	    	len = 1;
+	    	buffer = &data->byte;
+	    	iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
+	    	break;
+        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:
+	    	len = data->block[0];
+	    	buffer = &data->block[1];
+	    	iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
+		break;
+        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;
+
+	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);
+
+	iface->data = buffer;
+	iface->datalen = len;
+	iface->state = state_addr;
+	iface->result = 0;
+	iface->read_write = read_write;
+	
+	/* Setup channel & clear pending irqs */
+	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 */
+	write_reg(reg_addr,
+		(addr << 1) | ((read_write == I2C_SMBUS_READ) ? 0x01 : 0x00));
+
+	/* Set up the sub address */
+	if ((iface->cur_mode & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_STANDARDSUB
+	    || (iface->cur_mode & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_COMBINED)
+		write_reg(reg_subaddr, command);
+
+#ifndef POLLED_MODE
+	/* Arm timeout */
+	iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+	add_timer(&iface->timeout_timer);
+#endif
+
+	/* Start sending address & enable interrupt*/
+	write_reg(reg_control, KW_I2C_CTL_XADDR);
+	write_reg(reg_ier, KW_I2C_IRQ_MASK);
+
+#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);
+
+	if (rc == 0 && size == I2C_SMBUS_WORD_DATA && read_write == I2C_SMBUS_READ)
+	    	data->word = le16_to_cpu(cur_word);
+	
+	/* Release sem */
+	pmac_low_i2c_unlock(iface->node);
+	
+	return rc;
+}
+
+/*
+ * Generic i2c master transfer entrypoint
+ */
+static int
+keywest_xfer(	struct i2c_adapter *adap,
+		struct i2c_msg *msgs, 
+		int num)
+{
+	struct keywest_chan* chan = i2c_get_adapdata(adap);
+	struct keywest_iface* iface = chan->iface;
+	struct i2c_msg *pmsg;
+	int i, completed;
+	int rc = 0;
+
+	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;
+	iface->cur_mode |= KW_I2C_MODE_STANDARD;
+
+	completed = 0;
+	for (i = 0; rc >= 0 && i < num;) {
+		u8 addr;
+		
+		pmsg = &msgs[i++];
+		addr = pmsg->addr;
+		if (pmsg->flags & I2C_M_TEN) {
+			printk(KERN_ERR "i2c-keywest: 10 bits addr not supported !\n");
+			rc = -EINVAL;
+			break;
+		}
+		pr_debug("xfer: chan: %d, doing %s %d bytes to 0x%02x - %d of %d messages\n",
+		     chan->chan_no,
+		     pmsg->flags & I2C_M_RD ? "read" : "write",
+                     pmsg->len, addr, i, num);
+    
+		/* 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_status, 0);
+		
+		iface->data = pmsg->buf;
+		iface->datalen = pmsg->len;
+		iface->state = state_addr;
+		iface->result = 0;
+		if (pmsg->flags & I2C_M_RD)
+			iface->read_write = I2C_SMBUS_READ;
+		else
+			iface->read_write = I2C_SMBUS_WRITE;
+
+		/* Set up address and r/w bit */
+		if (pmsg->flags & I2C_M_REV_DIR_ADDR)
+			addr ^= 1;		
+		write_reg(reg_addr,
+			(addr << 1) |
+			((iface->read_write == I2C_SMBUS_READ) ? 0x01 : 0x00));
+
+#ifndef POLLED_MODE
+		/* Arm timeout */
+		iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+		add_timer(&iface->timeout_timer);
+#endif
+
+		/* Start sending address & enable interrupt*/
+		write_reg(reg_ier, KW_I2C_IRQ_MASK);
+		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)
+			completed++;
+		pr_debug("transfer done, result: %d\n", rc);
+	}
+
+	/* Release sem */
+	pmac_low_i2c_unlock(iface->node);
+
+	return completed;
+}
+
+static u32
+keywest_func(struct i2c_adapter * adapter)
+{
+	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+	       I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+	       I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+/* For now, we only handle combined mode (smbus) */
+static struct i2c_algorithm keywest_algorithm = {
+	.name		= "Keywest i2c",
+	.id		= I2C_ALGO_SMBUS,
+	.smbus_xfer	= keywest_smbus_xfer,
+	.master_xfer	= keywest_xfer,
+	.functionality	= keywest_func,
+};
+
+
+static int
+create_iface(struct device_node *np, struct device *dev)
+{
+	unsigned long steps;
+	unsigned bsteps, tsize, i, nchan, addroffset;
+	struct keywest_iface* iface;
+	u32 *psteps, *prate;
+	int rc;
+
+	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 (np->parent->name[0] == 'u') {
+		nchan = 2;
+		addroffset = 3;
+	} else {
+		addroffset = 0;
+		nchan = 1;
+	}
+
+	tsize = sizeof(struct keywest_iface) +
+		(sizeof(struct keywest_chan) + 4) * nchan;
+	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);
+	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;
+	iface->irq = np->intrs[0].line;
+	iface->channels = (struct keywest_chan *)
+		(((unsigned long)(iface + 1) + 3UL) & ~3UL);
+	iface->base = ioremap(np->addrs[0].address + addroffset,
+						np->addrs[0].size);
+	if (!iface->base) {
+		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 = (u32 *)get_property(np, "AAPL,i2c-rate", NULL);
+	if (prate) switch(*prate) {
+	case 100:
+		iface->cur_mode = KW_I2C_MODE_100KHZ;
+		break;
+	case 50:
+		iface->cur_mode = KW_I2C_MODE_50KHZ;
+		break;
+	case 25:
+		iface->cur_mode = KW_I2C_MODE_25KHZ;
+		break;
+	default:
+		printk(KERN_WARNING "i2c-keywest: unknown rate %ldKhz, using 100KHz\n",
+		       (long)*prate);
+	}
+	
+	/* Select standard mode by default */
+	iface->cur_mode |= KW_I2C_MODE_STANDARD;
+	
+	/* Write mode */
+	write_reg(reg_mode, iface->cur_mode);
+	
+	/* Switch interrupts off & clear them*/
+	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, SA_INTERRUPT, "keywest i2c", iface);
+	if (rc) {
+		printk(KERN_ERR "i2c-keywest: can't get IRQ %d !\n", iface->irq);
+		iounmap(iface->base);
+		kfree(iface);
+		pmac_low_i2c_unlock(np);
+		return -ENODEV;
+	}
+#endif /* POLLED_MODE */
+
+	pmac_low_i2c_unlock(np);
+	dev_set_drvdata(dev, iface);
+	
+	for (i=0; i<nchan; i++) {
+		struct keywest_chan* chan = &iface->channels[i];
+		u8 addr;
+		
+		sprintf(chan->adapter.name, "%s %d", np->parent->name, i);
+		chan->iface = iface;
+		chan->chan_no = i;
+		chan->adapter.id = I2C_ALGO_SMBUS;
+		chan->adapter.algo = &keywest_algorithm;
+		chan->adapter.algo_data = NULL;
+		chan->adapter.client_register = NULL;
+		chan->adapter.client_unregister = NULL;
+		i2c_set_adapdata(&chan->adapter, chan);
+		chan->adapter.dev.parent = dev;
+
+		rc = i2c_add_adapter(&chan->adapter);
+		if (rc) {
+			printk("i2c-keywest.c: Adapter %s registration failed\n",
+				chan->adapter.name);
+			i2c_set_adapdata(&chan->adapter, NULL);
+		}
+		if (probe) {
+			printk("Probe: ");
+			for (addr = 0x00; addr <= 0x7f; addr++) {
+				if (i2c_smbus_xfer(&chan->adapter,addr,
+				    0,0,0,I2C_SMBUS_QUICK,NULL) >= 0)
+					printk("%02x ", addr);
+			}
+			printk("\n");
+		}
+	}
+
+	printk(KERN_INFO "Found KeyWest i2c on \"%s\", %d channel%s, stepping: %d bits\n",
+		np->parent->name, nchan, nchan > 1 ? "s" : "", bsteps);
+		
+	return 0;
+}
+
+static int
+dispose_iface(struct device *dev)
+{
+	struct keywest_iface *iface = dev_get_drvdata(dev);
+	int i, rc;
+	
+	/* Make sure we stop all activity */
+	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);
+		msleep(100);
+		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);
+#endif /* POLLED_MODE */
+
+	pmac_low_i2c_unlock(iface->node);
+
+	/* Release all channels */
+	for (i=0; i<iface->chan_count; i++) {
+		struct keywest_chan* chan = &iface->channels[i];
+		if (i2c_get_adapdata(&chan->adapter) == NULL)
+			continue;
+		rc = i2c_del_adapter(&chan->adapter);
+		i2c_set_adapdata(&chan->adapter, NULL);
+		/* We aren't that prepared to deal with this... */
+		if (rc)
+			printk("i2c-keywest.c: i2c_del_adapter failed, that's bad !\n");
+	}
+	iounmap(iface->base);
+	dev_set_drvdata(dev, NULL);
+	of_node_put(iface->node);
+	kfree(iface);
+
+	return 0;
+}
+
+static int
+create_iface_macio(struct macio_dev* dev, const struct of_match *match)
+{
+	return create_iface(dev->ofdev.node, &dev->ofdev.dev);
+}
+
+static int
+dispose_iface_macio(struct macio_dev* dev)
+{
+	return dispose_iface(&dev->ofdev.dev);
+}
+
+static int
+create_iface_of_platform(struct of_device* dev, const struct of_match *match)
+{
+	return create_iface(dev->node, &dev->dev);
+}
+
+static int
+dispose_iface_of_platform(struct of_device* dev)
+{
+	return dispose_iface(&dev->dev);
+}
+
+static struct of_match i2c_keywest_match[] = 
+{
+	{
+	.name 		= OF_ANY_MATCH,
+	.type		= "i2c",
+	.compatible	= "keywest"
+	},
+	{},
+};
+
+static struct macio_driver i2c_keywest_macio_driver = 
+{
+	.name 		= "i2c-keywest",
+	.match_table	= i2c_keywest_match,
+	.probe		= create_iface_macio,
+	.remove		= dispose_iface_macio
+};
+
+static struct of_platform_driver i2c_keywest_of_platform_driver = 
+{
+	.name 		= "i2c-keywest",
+	.match_table	= i2c_keywest_match,
+	.probe		= create_iface_of_platform,
+	.remove		= dispose_iface_of_platform
+};
+
+static int __init
+i2c_keywest_init(void)
+{
+	of_register_driver(&i2c_keywest_of_platform_driver);
+	macio_register_driver(&i2c_keywest_macio_driver);
+
+	return 0;
+}
+
+static void __exit
+i2c_keywest_cleanup(void)
+{
+	of_unregister_driver(&i2c_keywest_of_platform_driver);
+	macio_unregister_driver(&i2c_keywest_macio_driver);
+}
+
+module_init(i2c_keywest_init);
+module_exit(i2c_keywest_cleanup);