summaryrefslogtreecommitdiff
path: root/arch/arm/mach-msm/htc_battery.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-msm/htc_battery.c')
-rw-r--r--arch/arm/mach-msm/htc_battery.c769
1 files changed, 769 insertions, 0 deletions
diff --git a/arch/arm/mach-msm/htc_battery.c b/arch/arm/mach-msm/htc_battery.c
new file mode 100644
index 000000000000..7320edbff1a9
--- /dev/null
+++ b/arch/arm/mach-msm/htc_battery.c
@@ -0,0 +1,769 @@
+/* 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:00000000"
+#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;
+
+ /* 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");
+