Initial Contribution

msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142

Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/arch/arm/mach-msm/htc_battery.c b/arch/arm/mach-msm/htc_battery.c
new file mode 100644
index 0000000..d49c23e
--- /dev/null
+++ b/arch/arm/mach-msm/htc_battery.c
@@ -0,0 +1,771 @@
+/* arch/arm/mach-msm/htc_battery.c
+ *
+ * Copyright (C) 2008 HTC Corporation.
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/power_supply.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/wakelock.h>
+#include <asm/gpio.h>
+#include <mach/msm_rpcrouter.h>
+#include <mach/board.h>
+
+static struct wake_lock vbus_wake_lock;
+
+#define TRACE_BATT 0
+
+#if TRACE_BATT
+#define BATT(x...) printk(KERN_INFO "[BATT] " x)
+#else
+#define BATT(x...) do {} while (0)
+#endif
+
+/* rpc related */
+#define APP_BATT_PDEV_NAME		"rs30100001"
+#define APP_BATT_PROG			0x30100001
+#define APP_BATT_VER			0
+#define HTC_PROCEDURE_BATTERY_NULL	0
+#define HTC_PROCEDURE_GET_BATT_LEVEL	1
+#define HTC_PROCEDURE_GET_BATT_INFO	2
+#define HTC_PROCEDURE_GET_CABLE_STATUS	3
+#define HTC_PROCEDURE_SET_BATT_DELTA	4
+
+/* module debugger */
+#define HTC_BATTERY_DEBUG		1
+#define BATTERY_PREVENTION		1
+
+/* Enable this will shut down if no battery */
+#define ENABLE_BATTERY_DETECTION	0
+
+#define GPIO_BATTERY_DETECTION		21
+#define GPIO_BATTERY_CHARGER_EN		128
+
+/* Charge current selection */
+#define GPIO_BATTERY_CHARGER_CURRENT	129
+
+typedef enum {
+	DISABLE = 0,
+	ENABLE_SLOW_CHG,
+	ENABLE_FAST_CHG
+} batt_ctl_t;
+
+/* This order is the same as htc_power_supplies[]
+ * And it's also the same as htc_cable_status_update()
+ */
+typedef enum {
+	CHARGER_BATTERY = 0,
+	CHARGER_USB,
+	CHARGER_AC
+} charger_type_t;
+
+struct battery_info_reply {
+	u32 batt_id;		/* Battery ID from ADC */
+	u32 batt_vol;		/* Battery voltage from ADC */
+	u32 batt_temp;		/* Battery Temperature (C) from formula and ADC */
+	u32 batt_current;	/* Battery current from ADC */
+	u32 level;		/* formula */
+	u32 charging_source;	/* 0: no cable, 1:usb, 2:AC */
+	u32 charging_enabled;	/* 0: Disable, 1: Enable */
+	u32 full_bat;		/* Full capacity of battery (mAh) */
+};
+
+struct htc_battery_info {
+	int present;
+	unsigned long update_time;
+
+	/* lock to protect the battery info */
+	struct mutex lock;
+
+	/* lock held while calling the arm9 to query the battery info */
+	struct mutex rpc_lock;
+	struct battery_info_reply rep;
+};
+
+static struct msm_rpc_endpoint *endpoint;
+
+static struct htc_battery_info htc_batt_info;
+
+static unsigned int cache_time = 1000;
+
+static int htc_battery_initial = 0;
+
+static enum power_supply_property htc_battery_properties[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_CAPACITY,
+};
+
+static enum power_supply_property htc_power_properties[] = {
+	POWER_SUPPLY_PROP_ONLINE,
+};
+
+static char *supply_list[] = {
+	"battery",
+};
+
+/* HTC dedicated attributes */
+static ssize_t htc_battery_show_property(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf);
+
+static int htc_power_get_property(struct power_supply *psy, 
+				    enum power_supply_property psp,
+				    union power_supply_propval *val);
+
+static int htc_battery_get_property(struct power_supply *psy, 
+				    enum power_supply_property psp,
+				    union power_supply_propval *val);
+
+static struct power_supply htc_power_supplies[] = {
+	{
+		.name = "battery",
+		.type = POWER_SUPPLY_TYPE_BATTERY,
+		.properties = htc_battery_properties,
+		.num_properties = ARRAY_SIZE(htc_battery_properties),
+		.get_property = htc_battery_get_property,
+	},
+	{
+		.name = "usb",
+		.type = POWER_SUPPLY_TYPE_USB,
+		.supplied_to = supply_list,
+		.num_supplicants = ARRAY_SIZE(supply_list),
+		.properties = htc_power_properties,
+		.num_properties = ARRAY_SIZE(htc_power_properties),
+		.get_property = htc_power_get_property,
+	},
+	{
+		.name = "ac",
+		.type = POWER_SUPPLY_TYPE_MAINS,
+		.supplied_to = supply_list,
+		.num_supplicants = ARRAY_SIZE(supply_list),
+		.properties = htc_power_properties,
+		.num_properties = ARRAY_SIZE(htc_power_properties),
+		.get_property = htc_power_get_property,
+	},
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+#if defined(CONFIG_DEBUG_FS)
+int htc_battery_set_charging(batt_ctl_t ctl);
+static int batt_debug_set(void *data, u64 val)
+{
+	return htc_battery_set_charging((batt_ctl_t) val);
+}
+
+static int batt_debug_get(void *data, u64 *val)
+{
+	return -ENOSYS;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(batt_debug_fops, batt_debug_get, batt_debug_set, "%llu\n");
+static int __init batt_debug_init(void)
+{
+	struct dentry *dent;
+
+	dent = debugfs_create_dir("htc_battery", 0);
+	if (IS_ERR(dent))
+		return PTR_ERR(dent);
+
+	debugfs_create_file("charger_state", 0644, dent, NULL, &batt_debug_fops);
+
+	return 0;
+}
+
+device_initcall(batt_debug_init);
+#endif
+
+static int init_batt_gpio(void)
+{
+	if (gpio_request(GPIO_BATTERY_DETECTION, "batt_detect") < 0)
+		goto gpio_failed;
+	if (gpio_request(GPIO_BATTERY_CHARGER_EN, "charger_en") < 0)
+		goto gpio_failed;
+	if (gpio_request(GPIO_BATTERY_CHARGER_CURRENT, "charge_current") < 0)
+		goto gpio_failed;
+
+	return 0;
+
+gpio_failed:	
+	return -EINVAL;
+	
+}
+
+/* 
+ *	battery_charging_ctrl - battery charing control.
+ * 	@ctl:			battery control command
+ *
+ */
+static int battery_charging_ctrl(batt_ctl_t ctl)
+{
+	int result = 0;
+
+	switch (ctl) {
+	case DISABLE:
+		BATT("charger OFF\n");
+		/* 0 for enable; 1 disable */
+		result = gpio_direction_output(GPIO_BATTERY_CHARGER_EN, 1);
+		break;
+	case ENABLE_SLOW_CHG:
+		BATT("charger ON (SLOW)\n");
+		result = gpio_direction_output(GPIO_BATTERY_CHARGER_CURRENT, 0);
+		result = gpio_direction_output(GPIO_BATTERY_CHARGER_EN, 0);
+		break;
+	case ENABLE_FAST_CHG:
+		BATT("charger ON (FAST)\n");
+		result = gpio_direction_output(GPIO_BATTERY_CHARGER_CURRENT, 1);
+		result = gpio_direction_output(GPIO_BATTERY_CHARGER_EN, 0);
+		break;
+	default:
+		printk(KERN_ERR "Not supported battery ctr called.!\n");
+		result = -EINVAL;
+		break;
+	}
+	
+	return result;
+}
+
+int htc_battery_set_charging(batt_ctl_t ctl)
+{
+	int rc;
+	
+	if ((rc = battery_charging_ctrl(ctl)) < 0)
+		goto result;
+	
+	if (!htc_battery_initial) {
+		htc_batt_info.rep.charging_enabled = ctl & 0x3;
+	} else {
+		mutex_lock(&htc_batt_info.lock);
+		htc_batt_info.rep.charging_enabled = ctl & 0x3;
+		mutex_unlock(&htc_batt_info.lock);
+	}
+result:	
+	return rc;
+}
+
+int htc_battery_status_update(u32 curr_level)
+{
+	int notify;
+	if (!htc_battery_initial)
+		return 0;
+
+	mutex_lock(&htc_batt_info.lock);
+	notify = (htc_batt_info.rep.level != curr_level);
+	htc_batt_info.rep.level = curr_level;
+	mutex_unlock(&htc_batt_info.lock);
+
+	if (notify)
+		power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]);
+	return 0;
+}
+
+int htc_cable_status_update(int status)
+{
+	int rc = 0;
+	unsigned source;
+
+	if (!htc_battery_initial)
+		return 0;
+	
+	mutex_lock(&htc_batt_info.lock);
+	switch(status) {
+	case CHARGER_BATTERY:
+		BATT("cable NOT PRESENT\n");
+		htc_batt_info.rep.charging_source = CHARGER_BATTERY;
+		break;
+	case CHARGER_USB:
+		BATT("cable USB\n");
+		htc_batt_info.rep.charging_source = CHARGER_USB;
+		break;
+	case CHARGER_AC:
+		BATT("cable AC\n");
+		htc_batt_info.rep.charging_source = CHARGER_AC;
+		break;
+	default:
+		printk(KERN_ERR "%s: Not supported cable status received!\n",
+				__FUNCTION__);
+		rc = -EINVAL;
+	}
+	source = htc_batt_info.rep.charging_source;
+	mutex_unlock(&htc_batt_info.lock);
+
+	msm_hsusb_set_vbus_state(source == CHARGER_USB);
+	if (source == CHARGER_USB) {
+		wake_lock(&vbus_wake_lock);
+	} else {
+		/* give userspace some time to see the uevent and update
+		 * LED state or whatnot...
+		 */
+		wake_lock_timeout(&vbus_wake_lock, HZ / 2);
+	}
+
+	/* if the power source changes, all power supplies may change state */
+	power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]);
+	power_supply_changed(&htc_power_supplies[CHARGER_USB]);
+	power_supply_changed(&htc_power_supplies[CHARGER_AC]);
+
+	return rc;
+}
+
+static int htc_get_batt_info(struct battery_info_reply *buffer)
+{
+	struct rpc_request_hdr req;
+	
+	struct htc_get_batt_info_rep {
+		struct rpc_reply_hdr hdr;
+		struct battery_info_reply info;
+	} rep;
+	
+	int rc;
+
+	if (buffer == NULL) 
+		return -EINVAL;
+
+	rc = msm_rpc_call_reply(endpoint, HTC_PROCEDURE_GET_BATT_INFO,
+				&req, sizeof(req),
+				&rep, sizeof(rep),
+				5 * HZ);
+	if ( rc < 0 ) 
+		return rc;
+	
+	mutex_lock(&htc_batt_info.lock);
+	buffer->batt_id 		= be32_to_cpu(rep.info.batt_id);
+	buffer->batt_vol 		= be32_to_cpu(rep.info.batt_vol);
+	buffer->batt_temp 		= be32_to_cpu(rep.info.batt_temp);
+	buffer->batt_current 		= be32_to_cpu(rep.info.batt_current);
+	buffer->level 			= be32_to_cpu(rep.info.level);
+	buffer->charging_source 	= be32_to_cpu(rep.info.charging_source);
+	buffer->charging_enabled 	= be32_to_cpu(rep.info.charging_enabled);
+	buffer->full_bat 		= be32_to_cpu(rep.info.full_bat);
+	mutex_unlock(&htc_batt_info.lock);
+
+	return 0;
+}
+
+#if 0
+static int htc_get_cable_status(void)
+{
+	
+	struct rpc_request_hdr req;
+	
+	struct htc_get_cable_status_rep {
+		struct rpc_reply_hdr hdr;
+		int status;
+	} rep;
+
+	int rc;
+
+	rc = msm_rpc_call_reply(endpoint, HTC_PROCEDURE_GET_CABLE_STATUS,
+				&req, sizeof(req),
+				&rep, sizeof(rep),
+				5 * HZ);
+	if (rc < 0) 
+		return rc;
+
+	return be32_to_cpu(rep.status);
+}
+#endif
+
+/* -------------------------------------------------------------------------- */
+static int htc_power_get_property(struct power_supply *psy, 
+				    enum power_supply_property psp,
+				    union power_supply_propval *val)
+{
+	charger_type_t charger;
+	
+	mutex_lock(&htc_batt_info.lock);
+	charger = htc_batt_info.rep.charging_source;
+	mutex_unlock(&htc_batt_info.lock);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		if (psy->type == POWER_SUPPLY_TYPE_MAINS)
+			val->intval = (charger ==  CHARGER_AC ? 1 : 0);
+		else if (psy->type == POWER_SUPPLY_TYPE_USB)
+			val->intval = (charger ==  CHARGER_USB ? 1 : 0);
+		else
+			val->intval = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+	
+	return 0;
+}
+
+static int htc_battery_get_charging_status(void)
+{
+	u32 level;
+	charger_type_t charger;	
+	int ret;
+	
+	mutex_lock(&htc_batt_info.lock);
+	charger = htc_batt_info.rep.charging_source;
+	
+	switch (charger) {
+	case CHARGER_BATTERY:
+		ret = POWER_SUPPLY_STATUS_NOT_CHARGING;
+		break;
+	case CHARGER_USB:
+	case CHARGER_AC:
+		level = htc_batt_info.rep.level;
+		if (level == 100)
+			ret = POWER_SUPPLY_STATUS_FULL;
+		else
+			ret = POWER_SUPPLY_STATUS_CHARGING;
+		break;
+	default:
+		ret = POWER_SUPPLY_STATUS_UNKNOWN;
+	}
+	mutex_unlock(&htc_batt_info.lock);
+	return ret;
+}
+
+static int htc_battery_get_property(struct power_supply *psy, 
+				    enum power_supply_property psp,
+				    union power_supply_propval *val)
+{
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		val->intval = htc_battery_get_charging_status();
+		break;
+	case POWER_SUPPLY_PROP_HEALTH:
+		val->intval = POWER_SUPPLY_HEALTH_GOOD;
+		break;
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = htc_batt_info.present;
+		break;
+	case POWER_SUPPLY_PROP_TECHNOLOGY:
+		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		mutex_lock(&htc_batt_info.lock);
+		val->intval = htc_batt_info.rep.level;
+		mutex_unlock(&htc_batt_info.lock);
+		break;
+	default:		
+		return -EINVAL;
+	}
+	
+	return 0;
+}
+
+#define HTC_BATTERY_ATTR(_name)							\
+{										\
+	.attr = { .name = #_name, .mode = S_IRUGO, .owner = THIS_MODULE },	\
+	.show = htc_battery_show_property,					\
+	.store = NULL,								\
+}
+
+static struct device_attribute htc_battery_attrs[] = {
+	HTC_BATTERY_ATTR(batt_id),
+	HTC_BATTERY_ATTR(batt_vol),
+	HTC_BATTERY_ATTR(batt_temp),
+	HTC_BATTERY_ATTR(batt_current),
+	HTC_BATTERY_ATTR(charging_source),
+	HTC_BATTERY_ATTR(charging_enabled),
+	HTC_BATTERY_ATTR(full_bat),
+};
+
+enum {
+	BATT_ID = 0,
+	BATT_VOL,
+	BATT_TEMP,
+	BATT_CURRENT,
+	CHARGING_SOURCE,
+	CHARGING_ENABLED,
+	FULL_BAT,
+};
+
+static int htc_rpc_set_delta(unsigned delta)
+{
+	struct set_batt_delta_req {
+		struct rpc_request_hdr hdr;
+		uint32_t data;
+	} req;
+
+	req.data = cpu_to_be32(delta);
+	return msm_rpc_call(endpoint, HTC_PROCEDURE_SET_BATT_DELTA,
+			    &req, sizeof(req), 5 * HZ);
+}
+
+
+static ssize_t htc_battery_set_delta(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	int rc;
+	unsigned long delta = 0;
+	
+	delta = simple_strtoul(buf, NULL, 10);
+
+	if (delta > 100)
+		return -EINVAL;
+
+	mutex_lock(&htc_batt_info.rpc_lock);
+	rc = htc_rpc_set_delta(delta);
+	mutex_unlock(&htc_batt_info.rpc_lock);
+	if (rc < 0)
+		return rc;
+	return count;
+}
+
+static struct device_attribute htc_set_delta_attrs[] = {
+	__ATTR(delta, S_IWUSR | S_IWGRP, NULL, htc_battery_set_delta),
+};
+
+static int htc_battery_create_attrs(struct device * dev)
+{
+	int i, j, rc;
+	
+	for (i = 0; i < ARRAY_SIZE(htc_battery_attrs); i++) {
+		rc = device_create_file(dev, &htc_battery_attrs[i]);
+		if (rc)
+			goto htc_attrs_failed;
+	}
+
+	for (j = 0; j < ARRAY_SIZE(htc_set_delta_attrs); j++) {
+		rc = device_create_file(dev, &htc_set_delta_attrs[j]);
+		if (rc)
+			goto htc_delta_attrs_failed;
+	}
+	
+	goto succeed;
+	
+htc_attrs_failed:
+	while (i--)
+		device_remove_file(dev, &htc_battery_attrs[i]);
+htc_delta_attrs_failed:
+	while (j--)
+		device_remove_file(dev, &htc_set_delta_attrs[i]);
+succeed:	
+	return rc;
+}
+
+static ssize_t htc_battery_show_property(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	int i = 0;
+	const ptrdiff_t off = attr - htc_battery_attrs;
+	
+	/* rpc lock is used to prevent two threads from calling
+	 * into the get info rpc at the same time
+	 */
+
+	mutex_lock(&htc_batt_info.rpc_lock);
+	/* check cache time to decide if we need to update */
+	if (htc_batt_info.update_time &&
+            time_before(jiffies, htc_batt_info.update_time +
+                                msecs_to_jiffies(cache_time)))
+                goto dont_need_update;
+	
+	if (htc_get_batt_info(&htc_batt_info.rep) < 0)
+		printk(KERN_ERR "%s: rpc failed!!!\n", __FUNCTION__);
+	else
+		htc_batt_info.update_time = jiffies;
+dont_need_update:
+	mutex_unlock(&htc_batt_info.rpc_lock);
+
+	mutex_lock(&htc_batt_info.lock);
+	switch (off) {
+	case BATT_ID:
+		i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+			       htc_batt_info.rep.batt_id);
+		break;
+	case BATT_VOL:
+		i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+			       htc_batt_info.rep.batt_vol);
+		break;
+	case BATT_TEMP:
+		i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+			       htc_batt_info.rep.batt_temp);
+		break;
+	case BATT_CURRENT:
+		i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+			       htc_batt_info.rep.batt_current);
+		break;
+	case CHARGING_SOURCE:
+		i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+			       htc_batt_info.rep.charging_source);
+		break;
+	case CHARGING_ENABLED:
+		i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+			       htc_batt_info.rep.charging_enabled);
+		break;		
+	case FULL_BAT:
+		i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+			       htc_batt_info.rep.full_bat);
+		break;
+	default:
+		i = -EINVAL;
+	}	
+	mutex_unlock(&htc_batt_info.lock);
+	
+	return i;
+}
+
+static int htc_battery_probe(struct platform_device *pdev)
+{
+	int i, rc;
+
+	if (pdev->id != (APP_BATT_VER & RPC_VERSION_MAJOR_MASK))
+		return -EINVAL;
+
+	/* init battery gpio */
+	if ((rc = init_batt_gpio()) < 0) {
+		printk(KERN_ERR "%s: init battery gpio failed!\n", __FUNCTION__);
+		return rc;
+	}
+
+	/* init structure data member */
+	htc_batt_info.update_time 	= jiffies;
+	htc_batt_info.present 		= gpio_get_value(GPIO_BATTERY_DETECTION);
+	
+	/* init rpc */
+	endpoint = msm_rpc_connect(APP_BATT_PROG, APP_BATT_VER, 0);
+	if (IS_ERR(endpoint)) {
+		printk(KERN_ERR "%s: init rpc failed! rc = %ld\n",
+		       __FUNCTION__, PTR_ERR(endpoint));
+		return rc;
+	}
+
+	/* init power supplier framework */
+	for (i = 0; i < ARRAY_SIZE(htc_power_supplies); i++) {
+		rc = power_supply_register(&pdev->dev, &htc_power_supplies[i]);
+		if (rc)
+			printk(KERN_ERR "Failed to register power supply (%d)\n", rc);	
+	}
+
+	/* create htc detail attributes */
+	htc_battery_create_attrs(htc_power_supplies[CHARGER_BATTERY].dev);
+
+	/* After battery driver gets initialized, send rpc request to inquiry
+	 * the battery status in case of we lost some info
+	 */
+	htc_battery_initial = 1;
+
+	mutex_lock(&htc_batt_info.rpc_lock);
+	if (htc_get_batt_info(&htc_batt_info.rep) < 0)
+		printk(KERN_ERR "%s: get info failed\n", __FUNCTION__);
+
+	htc_cable_status_update(htc_batt_info.rep.charging_source);
+	battery_charging_ctrl(htc_batt_info.rep.charging_enabled ?
+			      ENABLE_SLOW_CHG : DISABLE);
+
+	if (htc_rpc_set_delta(1) < 0)
+		printk(KERN_ERR "%s: set delta failed\n", __FUNCTION__);
+	htc_batt_info.update_time = jiffies;
+	mutex_unlock(&htc_batt_info.rpc_lock);
+
+	if (htc_batt_info.rep.charging_enabled == 0)
+		battery_charging_ctrl(DISABLE);
+	
+	return 0;
+}
+
+static struct platform_driver htc_battery_driver = {
+	.probe	= htc_battery_probe,
+	.driver	= {
+		.name	= APP_BATT_PDEV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+/* batt_mtoa server definitions */
+#define BATT_MTOA_PROG				0x30100000
+#define BATT_MTOA_VERS				0
+#define RPC_BATT_MTOA_NULL			0
+#define RPC_BATT_MTOA_SET_CHARGING_PROC		1
+#define RPC_BATT_MTOA_CABLE_STATUS_UPDATE_PROC	2
+#define RPC_BATT_MTOA_LEVEL_UPDATE_PROC		3
+
+struct rpc_batt_mtoa_set_charging_args {
+	int enable;
+};
+
+struct rpc_batt_mtoa_cable_status_update_args {
+	int status;
+};
+
+struct rpc_dem_battery_update_args {
+	uint32_t level;
+};
+
+static int handle_battery_call(struct msm_rpc_server *server,
+			       struct rpc_request_hdr *req, unsigned len)
+{	
+	switch (req->procedure) {
+	case RPC_BATT_MTOA_NULL:
+		return 0;
+
+	case RPC_BATT_MTOA_SET_CHARGING_PROC: {
+		struct rpc_batt_mtoa_set_charging_args *args;
+		args = (struct rpc_batt_mtoa_set_charging_args *)(req + 1);
+		args->enable = be32_to_cpu(args->enable);
+		BATT("set_charging: enable=%d\n",args->enable);
+		htc_battery_set_charging(args->enable);
+		return 0;
+	}
+	case RPC_BATT_MTOA_CABLE_STATUS_UPDATE_PROC: {
+		struct rpc_batt_mtoa_cable_status_update_args *args;
+		args = (struct rpc_batt_mtoa_cable_status_update_args *)(req + 1);
+		args->status = be32_to_cpu(args->status);
+		BATT("cable_status_update: status=%d\n",args->status);
+		htc_cable_status_update(args->status);
+		return 0;
+	}
+	case RPC_BATT_MTOA_LEVEL_UPDATE_PROC: {
+		struct rpc_dem_battery_update_args *args;
+		args = (struct rpc_dem_battery_update_args *)(req + 1);
+		args->level = be32_to_cpu(args->level);
+		BATT("dem_battery_update: level=%d\n",args->level);
+		htc_battery_status_update(args->level);
+		return 0;
+	}
+	default:
+		printk(KERN_ERR "%s: program 0x%08x:%d: unknown procedure %d\n",
+		       __FUNCTION__, req->prog, req->vers, req->procedure);
+		return -ENODEV;
+	}
+}
+
+static struct msm_rpc_server battery_server = {
+	.prog = BATT_MTOA_PROG,
+	.vers = BATT_MTOA_VERS,
+	.rpc_call = handle_battery_call,
+};
+
+static int __init htc_battery_init(void)
+{
+	wake_lock_init(&vbus_wake_lock, WAKE_LOCK_SUSPEND, "vbus_present");
+	mutex_init(&htc_batt_info.lock);
+	mutex_init(&htc_batt_info.rpc_lock);
+	msm_rpc_create_server(&battery_server);
+	platform_driver_register(&htc_battery_driver);
+	return 0;
+}
+
+module_init(htc_battery_init);
+MODULE_DESCRIPTION("HTC Battery Driver");
+MODULE_LICENSE("GPL");
+