Index: linux-2.6.21/arch/arm/mach-pxa/ezx-eoc.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.21/arch/arm/mach-pxa/ezx-eoc.c	2007-09-11 20:14:33.000000000 -0300
@@ -0,0 +1,267 @@
+/*
+ *  EZX EOC Driver for Motorola EZX phones
+ *
+ *  Copyright (C) 2007 Alex Zhang <celeber2@gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+
+#include <asm/arch/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/ezx.h>
+
+#include "ezx-eoc.h"
+
+#if 1
+#define EOC_DBG printk
+#else
+#define EOC_DBG(x, args...)
+#endif
+
+#define EOC_REG_ADDR_SIZE  1
+#define EOC_REG_DATA_SIZE  3
+
+struct ezx_eoc_platform_data *pdata;
+static int eoc_func = EOC_FUNC_USB_NET;
+static const char eoc_i2c_driver_name[] = "ezx-eoc";
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = {
+    0x17,        /* Address for version 2.0 and above  */
+   // 0x7C,      /* Address for versions prior too 2.0 */
+	I2C_CLIENT_END
+};
+
+/* I2C Magic */
+I2C_CLIENT_INSMOD;
+
+static int ezx_eoc_attach_adapter(struct i2c_adapter *adapter);
+static int ezx_eoc_detect(struct i2c_adapter *adapter, int address, int kind);
+static int ezx_eoc_detach_client(struct i2c_client *client);
+
+static struct i2c_client *eoc_i2c_client = NULL;
+
+static struct i2c_driver eoc_i2c_driver = {
+	.driver = {
+		.name	= (char *)eoc_i2c_driver_name,
+	},
+	.id		= I2C_DRIVERID_EEPROM,
+	.attach_adapter	= ezx_eoc_attach_adapter,
+	.detach_client	= ezx_eoc_detach_client,
+};
+
+int eoc_reg_read(int reg, unsigned int *reg_value)
+{
+    unsigned char reg_num = reg;
+    unsigned char value[EOC_REG_DATA_SIZE];
+    int retval;
+
+    struct i2c_msg msgs[2] =
+    {
+        { 0, 0, EOC_REG_ADDR_SIZE, &reg_num },
+        { 0, I2C_M_RD, EOC_REG_DATA_SIZE, value }
+    };
+
+    /* check if we have initialized */ /*not necessary --WM
+    if (eoc_i2c_client == NULL)
+    {
+        EOC_DBG("eoc_reg_read: not initialized\n");
+        return -EINVAL;
+    }
+*/
+	msgs[0].addr = msgs[1].addr = eoc_i2c_client->addr;
+
+    /* transfer message to client */
+    retval = i2c_transfer(eoc_i2c_client->adapter, msgs, 2);
+    if (retval >= 0)
+    {
+		*reg_value  = (value[2] <<  0);
+        *reg_value |= (value[1] <<  8);
+        *reg_value |= (value[0] << 16);
+    }
+    return retval;
+}
+EXPORT_SYMBOL_GPL(eoc_reg_read);
+
+int eoc_reg_write(int reg, unsigned int reg_value)
+{
+    unsigned char value[EOC_REG_ADDR_SIZE + EOC_REG_DATA_SIZE];
+    int retval;
+
+    /* check if we have initialized */ /*not necessary --WM
+    if (eoc_i2c_client == NULL)
+    {
+        EOC_DBG("eoc_reg_write: not initialized\n");
+        return -EINVAL;
+    }
+*/
+    /* Copy the data into a buffer into the correct format */
+    value[0] = reg;
+    value[1] = (reg_value >> 16) & 0xFF;
+    value[2] = (reg_value >>  8) & 0xFF;
+    value[3] = (reg_value >>  0) & 0xFF;
+
+    /* Write the data to the EOC */
+    retval = i2c_master_send (eoc_i2c_client, value, EOC_REG_ADDR_SIZE + EOC_REG_DATA_SIZE);
+
+    return retval;
+}
+EXPORT_SYMBOL_GPL(eoc_reg_write);
+
+static void eoc_switch_to_usb(void)
+{
+	pxa_gpio_mode(GPIO34_USB_P2_2_MD);
+	pxa_gpio_mode(GPIO35_USB_P2_1_MD);
+	pxa_gpio_mode(GPIO36_USB_P2_4_MD);
+	pxa_gpio_mode(GPIO39_USB_P2_6_MD);
+	pxa_gpio_mode(GPIO40_USB_P2_5_MD);
+	pxa_gpio_mode(GPIO53_USB_P2_3_MD);
+	EOC_DBG("ALEX;*********************************************emu_switch_to_usb;\n");
+}
+
+static void eoc_switch_to_nothing(void)
+{
+	pxa_gpio_mode(GPIO34_TXENB | GPIO_OUT);
+	set_GPIO(GPIO34_TXENB);
+	pxa_gpio_mode(GPIO35_XRXD  | GPIO_IN);
+	pxa_gpio_mode(GPIO36_VMOUT | GPIO_IN);
+	pxa_gpio_mode(GPIO39_VPOUT | GPIO_IN);
+	pxa_gpio_mode(GPIO40_VPIN  | GPIO_IN);
+	pxa_gpio_mode(GPIO53_VMIN  | GPIO_IN);
+}
+
+static void eoc_switch_to_default(void)
+{
+	switch (eoc_func) {
+	case EOC_FUNC_USB_NET:
+		eoc_switch_to_usb();
+		break;
+	case EOC_FUNC_NOTHING:
+		eoc_switch_to_nothing();
+		break;
+	}
+}
+
+
+static int ezx_eoc_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_probe(adapter, &addr_data, ezx_eoc_detect);
+}
+
+/* This function is called by i2c_probe */
+static int ezx_eoc_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+	struct i2c_client *new_client;
+	int err = 0;
+
+	if (!(new_client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL)))
+		return -ENOMEM;
+
+	new_client->addr = address;
+	new_client->adapter = adapter;
+	new_client->driver = &eoc_i2c_driver;
+	new_client->flags = 0;
+	strlcpy(new_client->name, eoc_i2c_driver_name, I2C_NAME_SIZE);
+
+	if ((err = i2c_attach_client(new_client))) {
+		kfree(new_client);
+		return err;
+	}
+
+	eoc_i2c_client = new_client;
+
+	if (pdata && pdata->init)
+		pdata->init();
+
+	eoc_switch_to_default();
+
+	return 0;
+}
+
+static int ezx_eoc_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	err = i2c_detach_client(client);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+static int __init ezx_eoc_probe(struct platform_device *dev)
+{
+	int ret;
+
+	 pdata = dev->dev.platform_data;
+
+	ret = i2c_add_driver(&eoc_i2c_driver);
+	if (ret != 0)
+		return -EINVAL;
+	
+	/* 
+	 * I think we should save platform_data and call init and eoc_switch
+	 * from ezx_eoc_detect, after client is setup.
+	 * And there is no need for all the "check if initialised" checks if 
+	 * you assure that you only call read/write after the client is set.
+	 * Probably, this was causing the crash on i2c-core too.
+	 * --WM
+	 */
+
+	/* FIXME: should set udc_info here -WM */
+	return 0;
+}
+
+static int ezx_eoc_remove(struct platform_device *dev)
+{
+	return i2c_del_driver(&eoc_i2c_driver);
+}
+
+static int ezx_eoc_suspend(struct platform_device *dev, pm_message_t state)
+{
+	eoc_switch_to_nothing();
+	return 0;
+}
+
+static int ezx_eoc_resume(struct platform_device *dev)
+{
+	eoc_switch_to_default();
+	return 0;
+}
+
+static struct platform_driver ezx_eoc_driver = {
+	.probe		= ezx_eoc_probe,
+	.remove		= ezx_eoc_remove,
+	.suspend	= ezx_eoc_suspend,
+	.resume		= ezx_eoc_resume,
+	.driver		= {
+		.name	= "ezx-eoc",
+		.owner	= THIS_MODULE,
+	},
+};
+
+int __init ezx_eoc_init(void)
+{
+	return platform_driver_register(&ezx_eoc_driver);
+}
+
+void ezx_eoc_exit(void)
+{
+	return platform_driver_unregister(&ezx_eoc_driver);
+}
+
+MODULE_AUTHOR("Alex Zhang <celeber2@gmail.com>");
+MODULE_DESCRIPTION("EZX EOC I2C driver");
+MODULE_LICENSE("GPL");
+
+/* doesnt module_init work?? -WM */
+/*late_initcall(ezx_eoc_init);*/
+module_init(ezx_eoc_init);
+module_exit(ezx_eoc_exit);
Index: linux-2.6.21/arch/arm/mach-pxa/ezx-eoc.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.21/arch/arm/mach-pxa/ezx-eoc.h	2007-09-09 21:46:27.000000000 -0300
@@ -0,0 +1,33 @@
+/*
+ *  linux/arch/arm/mach-pxa/ezx-eoc.h
+ *
+ * Copyright (C) Alex Zhang <celeber2@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __EZX_EOC_H__
+#define __EZX_EOC_H__
+
+enum {
+    POWER_IC_REG_EOC_INT_STATUS,
+    POWER_IC_REG_EOC_INT_MASK,
+    POWER_IC_REG_EOC_INT_SENSE,
+    POWER_IC_REG_EOC_POWER_CONTROL_0,
+    POWER_IC_REG_EOC_POWER_CONTROL_1,
+    POWER_IC_REG_EOC_CONN_CONTROL,
+	POWER_IC_REG_EOC_NUM
+};
+
+enum {
+	EOC_FUNC_NOTHING,
+	EOC_FUNC_USB_NET,
+};
+
+struct ezx_eoc_platform_data {
+	int (*init)(void);
+};
+
+#endif /* __EZX_EOC_H__ */
Index: linux-2.6.21/arch/arm/mach-pxa/Kconfig
===================================================================
--- linux-2.6.21.orig/arch/arm/mach-pxa/Kconfig	2007-09-09 21:46:27.000000000 -0300
+++ linux-2.6.21/arch/arm/mach-pxa/Kconfig	2007-09-09 21:46:27.000000000 -0300
@@ -108,6 +108,12 @@
 config EZX_PCAP
 	bool "PCAP Support"
 
+config EZX_EOC
+	tristate "EOC i2c driver of Motorola EZX phones"
+	depends on I2C && EXPERIMENTAL
+	help
+	  EOC i2c driver of Motorola EZX phones
+
 config EZX_EMU
 	bool "Motorola Enchanced Mini Usb"
 	depends on EZX_PCAP
Index: linux-2.6.21/arch/arm/mach-pxa/Makefile
===================================================================
--- linux-2.6.21.orig/arch/arm/mach-pxa/Makefile	2007-09-09 21:46:27.000000000 -0300
+++ linux-2.6.21/arch/arm/mach-pxa/Makefile	2007-09-09 21:46:27.000000000 -0300
@@ -27,6 +27,7 @@
 obj-$(CONFIG_EZX_BP)		+= ezx-bp.o
 obj-$(CONFIG_EZX_PCAP)		+= ezx-pcap.o
 obj-$(CONFIG_EZX_EMU)		+= ezx-emu.o
+obj-$(CONFIG_EZX_EOC)		+= ezx-eoc.o
 
 # Support for blinky lights
 led-y := leds.o