Index: openezx/arch/arm/mach-pxa/ezx.c
===================================================================
--- openezx.orig/arch/arm/mach-pxa/ezx.c	2008-12-09 21:55:11.000000000 +0800
+++ openezx/arch/arm/mach-pxa/ezx.c	2008-12-21 14:19:20.000000000 +0800
@@ -20,6 +20,7 @@
 #include <linux/gpio.h>
 #include <linux/spi/spi.h>
 #include <linux/mfd/ezx-pcap.h>
+#include <linux/mfd/ezx-power.h>
 #include <linux/spi/mmc_spi.h>
 #include <linux/irq.h>
 #include <linux/leds.h>
@@ -809,6 +810,32 @@
 	.id   = -1,
 };
 
+static struct power_define_t e6_power_define[LAST_POWER] = {
+	[BLUETOOTH_POWER] = {
+		.pcap_reg = PCAP_REG_VREG2,
+		.power_control_index = 1,
+		.power_value_index = -1,
+		.power_value_mask = 0,
+		.valid = 1,
+	},
+	[VIBRATOR_POWER] = {
+		.pcap_reg = PCAP_REG_AUXVREG,
+		.power_control_index = 19,
+		.power_value_index = 20,
+		.power_value_mask = 3,
+		.valid = 1,
+	},
+};
+
+static struct platform_device ezx_e6_power_device = {
+	.name   = "ezx-power",
+	.id     = 0,
+	.dev    = {
+		.platform_data = e6_power_define,
+	},
+};
+
+
 /* UDC */
 static void ezx_udc_command(int cmd)
 {
@@ -1326,6 +1353,7 @@
 
 	platform_device_register(&pcap_ts_device);
 	platform_device_register(&pcap_rtc_device);
+	platform_device_register(&ezx_e6_power_device);
 
 	platform_device_register(&gen2_bp_device);
 
@@ -1373,6 +1401,7 @@
 
 	pxa_set_keypad_info(&e2_keypad_platform_data);
 	platform_device_register(&pcap_rtc_device);
+	platform_device_register(&ezx_e6_power_device);
 
 	platform_device_register(&gen2_bp_device);
 
Index: openezx/drivers/mfd/Kconfig
===================================================================
--- openezx.orig/drivers/mfd/Kconfig	2008-12-06 23:29:06.000000000 +0800
+++ openezx/drivers/mfd/Kconfig	2008-12-09 21:55:11.000000000 +0800
@@ -160,6 +160,12 @@
 	  This enables the PCAP ASIC present on EZX Phones. This is
 	  needed for MMC, TouchScreen, Sound, USB, etc..
 
+config EZX_POWER
+	tristate "EZX POWER Support"
+	depends on EZX_PCAP
+	help
+	  This enables the simple power set api
+
 endmenu
 
 menu "Multimedia Capabilities Port drivers"
Index: openezx/drivers/mfd/Makefile
===================================================================
--- openezx.orig/drivers/mfd/Makefile	2008-12-06 23:29:06.000000000 +0800
+++ openezx/drivers/mfd/Makefile	2008-12-09 21:55:11.000000000 +0800
@@ -22,6 +22,7 @@
 obj-$(CONFIG_MFD_CORE)		+= mfd-core.o
 
 obj-$(CONFIG_EZX_PCAP)		+= ezx-pcap.o
+obj-$(CONFIG_EZX_POWER)		+= ezx-power.o
 
 obj-$(CONFIG_MCP)		+= mcp-core.o
 obj-$(CONFIG_MCP_SA11X0)	+= mcp-sa11x0.o
Index: openezx/drivers/mfd/ezx-power.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ openezx/drivers/mfd/ezx-power.c	2008-12-09 21:55:11.000000000 +0800
@@ -0,0 +1,529 @@
+/*
+ *  Powermanager driver Motorola EZX phones
+ *
+ *  Copyright (c) 2008 guiming zhuo <gmzhuo@gmail.com>
+ *
+ *  Based on Motorola's rtc.c Copyright (c) 2003-2005 Motorola
+ *
+ *  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/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/proc_fs.h>
+#include <linux/mfd/ezx-pcap.h>
+#include <linux/mfd/ezx-eoc.h>
+#include <linux/mfd/ezx-power.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+
+static int powerc_major;
+
+/* Bits in EMU one-chip's power control register 0. */
+#define EMU_VCHRG_MASK              0x00000007
+#define EMU_ICHRG_MASK              0x00000078
+#define EMU_ICHRG_SHIFT             3
+#define EMU_ICHRG_TR_MASK           0x00000380
+#define EMU_ICHRG_TR_SHIFT          7
+#define EMU_FET_OVRD_MASK           0x00000400
+#define EMU_FET_CTRL_MASK           0x00000800
+
+/*! Below 3.0V, the initial power path will be set to dual-path mode
+ * if a charger is attached). Otherwise, current-share mode will be
+ * selected. */
+#define POWER_PATH_BATT_THRESHOLD   0x01A4
+
+/* The safest way to detect a charger early in powerup is to look at
+ * its output. Anything above 4.0V on the charger line will be treated
+ * as a charger attached. */
+#define POWER_PATH_CHRG_THRESHOLD   0x0074
+
+/* The power control register will be set up like this depending
+ * on battery voltage. The curr share init value is the one originally
+ * specified by the hardware team. */
+#define POWER_PATH_INIT_CURR_SHARE  0x0000C01
+#define POWER_PATH_INIT_DUAL_PATH   0x0000801
+
+void charger_init(void)
+{
+	int batt;
+	int charger;
+
+	ezx_pcap_do_general_adc(PCAP_ADC_BANK_0, PCAP_ADC_CH_BATT, &batt);
+	ezx_pcap_do_general_adc(PCAP_ADC_BANK_0,
+				PCAP_ADC_CH_MOBPORTB, &charger);
+
+	if ((charger > POWER_PATH_CHRG_THRESHOLD)
+	    && (batt < POWER_PATH_BATT_THRESHOLD))
+		eoc_reg_write(EOC_REG_POWER_CONTROL_0,
+			      POWER_PATH_INIT_DUAL_PATH);
+	else
+		eoc_reg_write(EOC_REG_POWER_CONTROL_0,
+			      POWER_PATH_INIT_CURR_SHARE);
+
+}
+
+int power_ic_charger_set_charge_voltage(int charge_voltage)
+{
+	return eoc_set_reg_mask(EOC_REG_POWER_CONTROL_0,
+				EMU_VCHRG_MASK, charge_voltage);
+}
+
+int power_ic_charger_set_charge_current(int charge_current)
+{
+	int mask;
+
+	int setup = (charge_current << EMU_ICHRG_SHIFT) & EMU_ICHRG_MASK;
+	if (charge_current != 0)
+		mask = EMU_ICHRG_MASK | EMU_ICHRG_TR_MASK;
+	else
+		mask = EMU_ICHRG_MASK;
+
+	return eoc_set_reg_mask(EOC_REG_POWER_CONTROL_0, mask, setup);
+}
+
+int power_ic_charger_set_trickle_current(int charge_current)
+{
+	int mask;
+
+	int setup = (charge_current << EMU_ICHRG_TR_SHIFT) & EMU_ICHRG_TR_MASK;
+
+	if (charge_current != 0)
+		mask = EMU_ICHRG_MASK | EMU_ICHRG_TR_MASK;
+	else
+		mask = EMU_ICHRG_TR_MASK;
+
+	return eoc_set_reg_mask(EOC_REG_POWER_CONTROL_0, mask, setup);
+}
+
+int power_ic_charger_set_power_path(int path)
+{
+	/* Both FET override and FET control are used to control path. */
+	int mask = EMU_FET_OVRD_MASK | EMU_FET_CTRL_MASK;
+	int setup;
+
+	switch (path) {
+	case POWER_IC_CHARGER_POWER_DUAL_PATH:
+		setup = 0;
+		break;
+
+	case POWER_IC_CHARGER_POWER_CURRENT_SHARE:
+		setup = EMU_FET_CTRL_MASK;
+		break;
+
+	case POWER_IC_CHARGER_POWER_DUAL_PATH_SW_OVERRIDE:
+		setup = EMU_FET_OVRD_MASK;
+		break;
+
+	case POWER_IC_CHARGER_POWER_CURRENT_SHARE_SW_OVERRIDE:
+		setup = EMU_FET_OVRD_MASK | EMU_FET_CTRL_MASK;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return eoc_set_reg_mask(EOC_REG_POWER_CONTROL_0, mask, setup);
+}
+
+int charger_ioctl(unsigned int cmd, unsigned long arg)
+{
+	switch (cmd) {
+	case POWER_IOCTL_CHARGER_SET_CHARGE_VOLTAGE:
+		return power_ic_charger_set_charge_voltage((int)arg);
+		break;
+
+	case POWER_IOCTL_CHARGER_SET_CHARGE_CURRENT:
+		return power_ic_charger_set_charge_current((int)arg);
+		break;
+
+	case POWER_IOCTL_CHARGER_SET_TRICKLE_CURRENT:
+		return power_ic_charger_set_trickle_current((int)arg);
+		break;
+
+	case POWER_IOCTL_CHARGER_SET_POWER_PATH:
+		return power_ic_charger_set_power_path((int)arg);
+		break;
+
+	default:
+		return -ENOTTY;
+		break;
+	}
+	return 0;
+}
+
+void set_device_power_on(struct power_define_t *power_table,
+	    enum device_power_t type, int on)
+{
+	unsigned int tmp;
+
+	if (type >= LAST_POWER)
+		return;
+	if (power_table[type].valid == 0
+	    || power_table[type].power_control_index == -1)
+		return;
+	on = (on != 0) ? 1 : 0;
+	ezx_pcap_read(power_table[type].pcap_reg, &tmp);
+	tmp &= ~(1 << power_table[type].power_control_index);
+	tmp |= (on << power_table[type].power_control_index);
+	ezx_pcap_write(power_table[type].pcap_reg, tmp);
+}
+
+void set_device_power_value(struct power_define_t *power_table,
+	    enum device_power_t type, int value)
+{
+	unsigned int tmp;
+
+	if (type >= LAST_POWER)
+		return;
+	if (power_table[type].valid == 0
+	    || power_table[type].power_value_index == -1
+	    || power_table[type].power_value_mask == 0)
+		return;
+
+	ezx_pcap_read(power_table[type].pcap_reg, &tmp);
+	tmp &= ~(power_table[type].power_value_mask
+		 << power_table[type].power_value_index);
+	tmp |= ((value & power_table[type].power_value_mask)
+		<< power_table[type].power_value_index);
+	ezx_pcap_write(power_table[type].pcap_reg, tmp);
+}
+
+int powerc_open(struct inode *inode, struct file *filp)
+{
+	struct power_dev_t *dev;	/* device information */
+
+	/*  Find the device */
+	dev = container_of(inode->i_cdev, struct power_dev_t, cdev);
+
+	/* and use filp->private_data to point to the device data */
+	filp->private_data = dev;
+
+	return 0;		/* success */
+}
+
+int powerc_ioctl(struct inode *inode, struct file *filp,
+		 unsigned int cmd, unsigned long arg)
+{
+
+	struct power_dev_t *dev;
+	struct power_control_t control;
+
+	/* don't even decode wrong cmds: better returning  ENOTTY than EFAULT */
+	if (_IOC_TYPE(cmd) != POWERC_IOC_MAGIC)
+		return -ENOTTY;
+	if (_IOC_NR(cmd) > POWERC_IOC_MAXNR)
+		return -ENOTTY;
+
+	switch (cmd) {
+	case POWER_IOCTL_CHARGER_SET_POWER_PATH:
+		return power_ic_charger_set_power_path(arg);
+		break;
+
+	case POWER_IOCTL_SET_POWER:
+		dev = filp->private_data;
+		copy_from_user(&control, (void *)arg, sizeof(control));
+		switch (control.control_type) {
+		case CONTROL_ONOFF:
+			set_device_power_on(dev->power_table, control.type,
+					    control.value);
+			break;
+		case CONTROL_VALUE:
+			set_device_power_value(dev->power_table, control.type,
+					       control.value);
+			break;
+		}
+		break;
+	}
+
+	return charger_ioctl(cmd, arg);
+}
+
+struct file_operations power_fops = {
+	.ioctl = powerc_ioctl,
+	.open = powerc_open,
+};
+
+static ssize_t ezx_power_show_charge_voltage(struct device *dev,
+					     struct device_attribute *attr,
+					     char *buf)
+{
+	int value;
+	eoc_reg_read(EOC_REG_POWER_CONTROL_0, &value);
+	return sprintf(buf, "%d\n", value & EMU_VCHRG_MASK);
+}
+
+static ssize_t ezx_power_set_charge_voltage(struct device *dev,
+					    struct device_attribute *attr,
+					    const char *buf, size_t size)
+{
+	int value;
+	sscanf(buf, "%d", &value);
+	power_ic_charger_set_charge_voltage(value);
+	return size;
+}
+
+static ssize_t ezx_power_show_charge_current(struct device *dev,
+					     struct device_attribute *attr,
+					     char *buf)
+{
+	int value;
+	eoc_reg_read(EOC_REG_POWER_CONTROL_0, &value);
+	return sprintf(buf, "%d\n",
+		       ((value & EMU_ICHRG_MASK) >> EMU_ICHRG_SHIFT));
+}
+
+static ssize_t ezx_power_set_charge_current(struct device *dev,
+					    struct device_attribute *attr,
+					    const char *buf, size_t size)
+{
+	int value;
+	sscanf(buf, "%d", &value);
+	power_ic_charger_set_charge_current(value);
+	return size;
+}
+
+static ssize_t ezx_power_show_trickle_current(struct device *dev,
+					      struct device_attribute *attr,
+					      char *buf)
+{
+	int value;
+	eoc_reg_read(EOC_REG_POWER_CONTROL_0, &value);
+	return sprintf(buf, "%d\n",
+		       ((value & EMU_ICHRG_TR_MASK) >> EMU_ICHRG_TR_SHIFT));
+}
+
+static ssize_t ezx_power_set_trickle_current(struct device *dev,
+					     struct device_attribute *attr,
+					     const char *buf, size_t size)
+{
+	int value;
+	sscanf(buf, "%d", &value);
+	power_ic_charger_set_trickle_current(value);
+	return size;
+}
+
+char *power_descript[LAST_POWER] = {
+	"BLUETOOTH",
+	"VIBRATOR",
+};
+
+static ssize_t ezx_power_show_control(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	unsigned int index, onoff, value, val;
+	char *p = buf;
+	struct power_define_t *power_table = dev->platform_data;
+
+	for (index = 0; index < LAST_POWER; index++) {
+		if (power_table[index].valid == 0)
+			continue;
+
+		ezx_pcap_read(power_table[index].pcap_reg, &val);
+		onoff = val & (1 << power_table[index].power_control_index);
+		if (power_table[index].power_value_index >= 0)
+			value = (val >> power_table[index].power_value_index)
+			    & power_table[index].power_value_mask;
+		else
+			value = 0;
+
+		p += sprintf(p, "%u %s %d %s\n", index, onoff ? "on" : "off",
+			value, power_descript[index]);
+	}
+	return p - buf;
+}
+
+static ssize_t ezx_power_set_control(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t size)
+{
+	int index, value;
+	char onoff[10];
+	char *p = (char *)buf;
+	struct power_define_t *power_table = dev->platform_data;
+
+	while (p < (buf + size)) {
+		if ((sscanf(p, "%u %s %u", &index, onoff, &value) != 3) ||
+		    index < 0 || index >= LAST_POWER)
+			return -EINVAL;
+		p = strchr(p, '\n');
+		if (!p)
+			break;
+		p++;
+	}
+
+	p = (char *)buf;
+	while (p < (buf + size)) {
+		if ((sscanf(p, "%u %s %u", &index, onoff, &value) != 3) ||
+		    index < 0 || index >= LAST_POWER)
+			return -EINVAL;
+		if (power_table[index].valid == 0)
+			continue;
+
+		if (strcmp(onoff, "on") == 0)
+			set_device_power_on(dev->platform_data, index, 1);
+		else
+			set_device_power_on(dev->platform_data, index, 0);
+
+		set_device_power_value(dev->platform_data, index, value);
+
+		p = strchr(p, '\n');
+		if (!p)
+			break;
+		p++;
+	}
+
+	return size;
+}
+
+static DEVICE_ATTR(charge_voltage, 0600,
+		   ezx_power_show_charge_voltage, ezx_power_set_charge_voltage);
+
+static DEVICE_ATTR(charge_current, 0600,
+		   ezx_power_show_charge_current, ezx_power_set_charge_current);
+
+static DEVICE_ATTR(charge_trickle, 0600,
+		   ezx_power_show_trickle_current,
+		   ezx_power_set_trickle_current);
+
+static DEVICE_ATTR(power_control, 0600,
+		   ezx_power_show_control, ezx_power_set_control);
+
+static int ezx_power_setup_sysfs(struct device *pdev, int create)
+{
+	int ret = 0;
+
+	if (!create)
+		goto remove_all;
+
+	ret = device_create_file(pdev, &dev_attr_charge_voltage);
+	if (ret)
+		goto ret;
+	ret = device_create_file(pdev, &dev_attr_charge_current);
+	if (ret)
+		goto fail1;
+	ret = device_create_file(pdev, &dev_attr_charge_trickle);
+	if (ret)
+		goto fail2;
+	ret = device_create_file(pdev, &dev_attr_power_control);
+	if (ret)
+		goto fail3;
+
+	goto ret;
+
+remove_all:
+	device_remove_file(pdev, &dev_attr_power_control);
+fail3:	device_remove_file(pdev, &dev_attr_charge_trickle);
+fail2:	device_remove_file(pdev, &dev_attr_charge_current);
+fail1:	device_remove_file(pdev, &dev_attr_charge_voltage);
+ret:	return ret;
+}
+
+static int ezx_power_remove(struct platform_device *pdev)
+{
+	struct power_dev_t *dev;
+
+	dev = platform_get_drvdata(pdev);
+	cdev_del(&dev->cdev);
+	kfree(dev);
+	ezx_power_setup_sysfs(&pdev->dev, 0);
+
+	return 0;
+}
+
+static int __init ezx_power_probe(struct platform_device *pdev)
+{
+	struct power_dev_t *dev;
+	int err;
+	dev_t devno;
+
+	devno = MKDEV(powerc_major, pdev->id);
+
+	dev = kmalloc(sizeof(struct power_dev_t), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	dev->power_table = pdev->dev.platform_data;
+	cdev_init(&dev->cdev, 0);
+	dev->cdev.owner = THIS_MODULE;
+	dev->cdev.ops = 0;
+	err = cdev_add(&dev->cdev, devno, 1);
+	if (err) {
+		kfree(dev);
+		return err;
+	}
+
+	platform_set_drvdata(pdev, dev);
+	ezx_power_setup_sysfs(&pdev->dev, 1);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int ezx_power_suspend(struct platform_device *dev, pm_message_t state)
+{
+	return 0;
+}
+
+static int ezx_power_resume(struct platform_device *dev)
+{
+
+	return 0;
+}
+#else
+
+#define ezx_power_suspend NULL
+#define ezx_power_resume  NULL
+
+#endif
+
+static struct platform_driver ezxpower_driver = {
+	.probe = ezx_power_probe,
+	.remove = ezx_power_remove,
+	.suspend = ezx_power_suspend,
+	.resume = ezx_power_resume,
+	.driver = {
+		   .name = "ezx-power",
+		   .owner = THIS_MODULE,
+		   },
+};
+
+static int __init ezx_power_init(void)
+{
+	int ret;
+	dev_t dev;
+
+	ret = platform_driver_register(&ezxpower_driver);
+	if (ret)
+		return ret;
+
+	ret = alloc_chrdev_region(&dev, 0, 255, "power");
+	powerc_major = MAJOR(dev);
+
+	if (ret)
+		platform_driver_unregister(&ezxpower_driver);
+
+
+	return ret;
+}
+
+static void __exit ezx_power_exit(void)
+{
+	platform_driver_unregister(&ezxpower_driver);
+}
+
+module_init(ezx_power_init);
+module_exit(ezx_power_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("guiming zhuo <gmzhuo@gmail.com>");
+MODULE_DESCRIPTION("Motorola ezx machine power manager driver");
Index: openezx/include/linux/mfd/ezx-power.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ openezx/include/linux/mfd/ezx-power.h	2008-12-09 21:55:11.000000000 +0800
@@ -0,0 +1,64 @@
+#ifndef EZX_POWER_H
+#define EZX_POWER_H
+
+#include <linux/ioctl.h>
+#include <linux/cdev.h>
+
+enum device_power_t{
+    BLUETOOTH_POWER,
+    VIBRATOR_POWER,
+    LAST_POWER,
+};
+
+enum device_power_control_t{
+    CONTROL_ONOFF,
+    CONTROL_VALUE,
+};
+
+struct power_define_t{
+    unsigned int pcap_reg;
+    int power_control_index;
+    int power_value_index;
+    unsigned int power_value_mask;
+    int valid;
+};
+
+struct power_control_t{
+	enum device_power_t type;
+	enum device_power_control_t control_type;
+	int value;
+};
+
+void set_device_power_on(struct power_define_t *power_table,
+	enum device_power_t type, int on);
+void set_device_power_value(struct power_define_t *power_table,
+	enum device_power_t type, int value);
+
+#define POWERC_IOC_MAGIC  'P'
+
+#define POWER_IOCTL_CHARGER_SET_CHARGE_VOLTAGE _IOWR(POWERC_IOC_MAGIC, 1, int)
+
+#define POWER_IOCTL_CHARGER_SET_CHARGE_CURRENT _IOWR(POWERC_IOC_MAGIC, 2, int)
+
+#define POWER_IOCTL_CHARGER_SET_TRICKLE_CURRENT _IOWR(POWERC_IOC_MAGIC, 3, int)
+
+#define POWER_IOCTL_CHARGER_SET_POWER_PATH _IOWR(POWERC_IOC_MAGIC, 4, int)
+
+#define POWER_IOCTL_SET_POWER _IOWR(POWERC_IOC_MAGIC, 5, struct power_control_t)
+
+#define POWERC_IOC_MAXNR 5
+
+enum POWER_IC_CHARGER_POWER_PATH_T{
+	POWER_IC_CHARGER_POWER_DUAL_PATH,
+	POWER_IC_CHARGER_POWER_CURRENT_SHARE,
+	POWER_IC_CHARGER_POWER_DUAL_PATH_SW_OVERRIDE,
+	POWER_IC_CHARGER_POWER_CURRENT_SHARE_SW_OVERRIDE,
+};
+
+struct power_dev_t{
+	struct power_define_t *power_table;
+	struct cdev cdev;
+};
+
+
+#endif

