Merge changes I84443121,I147eab37 into msm-3.0

* changes:
  power_supply: Add driver for LTC4088 Charger
  power: core: add power supply APIs
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 7c8dfea..3dfa68e 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -338,6 +338,13 @@
 	help
 	  Say Y here to enable support for pm8921 chip bms subdevice
 
+config LTC4088_CHARGER
+	tristate "LTC4088 Charger driver"
+	depends on GPIOLIB
+	help
+	  Say Y here to enable support for ltc4088 chip charger. It controls the
+	  operations through GPIO pins.
+
 config PM8921_BMS
 	select PM8XXX_CCADC
 	tristate "PM8921 Battery Monitoring System driver"
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index e168590..03db839 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -48,3 +48,4 @@
 obj-$(CONFIG_PM8XXX_CCADC)	+= pm8xxx-ccadc.o
 obj-$(CONFIG_PM8921_BMS)	+= pm8921-bms.o
 obj-$(CONFIG_PM8921_CHARGER)	+= pm8921-charger.o
+obj-$(CONFIG_LTC4088_CHARGER)	+= ltc4088-charger.o
diff --git a/drivers/power/ltc4088-charger.c b/drivers/power/ltc4088-charger.c
new file mode 100644
index 0000000..dbc75cd
--- /dev/null
+++ b/drivers/power/ltc4088-charger.c
@@ -0,0 +1,338 @@
+/*
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#define pr_fmt(fmt)	"%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/power/ltc4088-charger.h>
+
+#define MAX_CURRENT_UA(n)	(n)
+#define MAX_CURRENT_MA(n)	(n * MAX_CURRENT_UA(1000))
+
+/**
+ * ltc4088_max_current - A typical current values supported by the charger
+ * @LTC4088_MAX_CURRENT_100mA:  100mA current
+ * @LTC4088_MAX_CURRENT_500mA:  500mA current
+ * @LTC4088_MAX_CURRENT_1A:     1A current
+ */
+enum ltc4088_max_current {
+	LTC4088_MAX_CURRENT_100mA = 100,
+	LTC4088_MAX_CURRENT_500mA = 500,
+	LTC4088_MAX_CURRENT_1A = 1000,
+};
+
+/**
+ * struct ltc4088_chg_chip - Device information
+ * @dev:			Device pointer to access the parent
+ * @lock:			Enable mutual exclusion
+ * @usb_psy:			USB device information
+ * @gpio_mode_select_d0:	GPIO #pin for D0 charger line
+ * @gpio_mode_select_d1:	GPIO #pin for D1 charger line
+ * @gpio_mode_select_d2:	GPIO #pin for D2 charger line
+ * @max_current:		Maximum current that is supplied at this time
+ */
+struct ltc4088_chg_chip {
+	struct device		*dev;
+	struct mutex		lock;
+	struct power_supply	usb_psy;
+	unsigned int		gpio_mode_select_d0;
+	unsigned int		gpio_mode_select_d1;
+	unsigned int		gpio_mode_select_d2;
+	unsigned int		max_current;
+};
+
+static enum power_supply_property pm_power_props[] = {
+	POWER_SUPPLY_PROP_CURRENT_MAX,
+	POWER_SUPPLY_PROP_ONLINE,
+};
+
+static char *pm_power_supplied_to[] = {
+	"battery",
+};
+
+static int ltc4088_set_charging(struct ltc4088_chg_chip *chip, bool enable)
+{
+	mutex_lock(&chip->lock);
+
+	if (enable) {
+		gpio_set_value_cansleep(chip->gpio_mode_select_d2, 0);
+	} else {
+		/* When disabling charger, set the max current to 0 also */
+		chip->max_current = 0;
+		gpio_set_value_cansleep(chip->gpio_mode_select_d0, 1);
+		gpio_set_value_cansleep(chip->gpio_mode_select_d1, 1);
+		gpio_set_value_cansleep(chip->gpio_mode_select_d2, 1);
+	}
+
+	mutex_unlock(&chip->lock);
+
+	return 0;
+}
+
+static void ltc4088_set_max_current(struct ltc4088_chg_chip *chip, int value)
+{
+	mutex_lock(&chip->lock);
+
+	/* If current is less than 100mA, we can not support that granularity */
+	if (value <  MAX_CURRENT_MA(LTC4088_MAX_CURRENT_100mA)) {
+		chip->max_current = 0;
+		gpio_set_value_cansleep(chip->gpio_mode_select_d0, 1);
+		gpio_set_value_cansleep(chip->gpio_mode_select_d1, 1);
+	} else if (value <  MAX_CURRENT_MA(LTC4088_MAX_CURRENT_500mA)) {
+		chip->max_current = MAX_CURRENT_MA(LTC4088_MAX_CURRENT_100mA);
+		gpio_set_value_cansleep(chip->gpio_mode_select_d0, 0);
+		gpio_set_value_cansleep(chip->gpio_mode_select_d1, 0);
+	} else if (value <  MAX_CURRENT_MA(LTC4088_MAX_CURRENT_1A)) {
+		chip->max_current = MAX_CURRENT_MA(LTC4088_MAX_CURRENT_500mA);
+		gpio_set_value_cansleep(chip->gpio_mode_select_d0, 0);
+		gpio_set_value_cansleep(chip->gpio_mode_select_d1, 1);
+	} else {
+		chip->max_current = MAX_CURRENT_MA(LTC4088_MAX_CURRENT_1A);
+		gpio_set_value_cansleep(chip->gpio_mode_select_d0, 1);
+		gpio_set_value_cansleep(chip->gpio_mode_select_d1, 0);
+	}
+
+	mutex_unlock(&chip->lock);
+}
+
+static void ltc4088_set_charging_off(struct ltc4088_chg_chip *chip)
+{
+	gpio_set_value_cansleep(chip->gpio_mode_select_d0, 1);
+	gpio_set_value_cansleep(chip->gpio_mode_select_d1, 1);
+}
+
+static int ltc4088_set_initial_state(struct ltc4088_chg_chip *chip)
+{
+	int rc;
+
+	rc = gpio_request(chip->gpio_mode_select_d0, "ltc4088_D0");
+	if (rc) {
+		pr_err("gpio request failed for GPIO %d\n",
+				chip->gpio_mode_select_d0);
+		return rc;
+	}
+
+	rc = gpio_request(chip->gpio_mode_select_d1, "ltc4088_D1");
+	if (rc) {
+		pr_err("gpio request failed for GPIO %d\n",
+				chip->gpio_mode_select_d1);
+		goto gpio_err_d0;
+	}
+
+	rc = gpio_request(chip->gpio_mode_select_d2, "ltc4088_D2");
+	if (rc) {
+		pr_err("gpio request failed for GPIO %d\n",
+				chip->gpio_mode_select_d2);
+		goto gpio_err_d1;
+	}
+
+	rc = gpio_direction_output(chip->gpio_mode_select_d0, 0);
+	if (rc) {
+		pr_err("failed to set direction for GPIO %d\n",
+				chip->gpio_mode_select_d0);
+		goto gpio_err_d2;
+	}
+
+	rc = gpio_direction_output(chip->gpio_mode_select_d1, 0);
+	if (rc) {
+		pr_err("failed to set direction for GPIO %d\n",
+				chip->gpio_mode_select_d1);
+		goto gpio_err_d2;
+	}
+
+	rc = gpio_direction_output(chip->gpio_mode_select_d2, 1);
+	if (rc) {
+		pr_err("failed to set direction for GPIO %d\n",
+				chip->gpio_mode_select_d2);
+		goto gpio_err_d2;
+	}
+
+	return 0;
+
+gpio_err_d2:
+	gpio_free(chip->gpio_mode_select_d2);
+gpio_err_d1:
+	gpio_free(chip->gpio_mode_select_d1);
+gpio_err_d0:
+	gpio_free(chip->gpio_mode_select_d0);
+	return rc;
+}
+
+static int pm_power_get_property(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  union power_supply_propval *val)
+{
+	struct ltc4088_chg_chip *chip;
+
+	if (psy->type == POWER_SUPPLY_TYPE_USB) {
+		chip = container_of(psy, struct ltc4088_chg_chip,
+						usb_psy);
+		switch (psp) {
+		case POWER_SUPPLY_PROP_ONLINE:
+			if (chip->max_current)
+				val->intval = 1;
+			else
+				val->intval = 0;
+			break;
+		case POWER_SUPPLY_PROP_CURRENT_MAX:
+			val->intval = chip->max_current;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+static int pm_power_set_property(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  const union power_supply_propval *val)
+{
+	struct ltc4088_chg_chip *chip;
+
+	if (psy->type == POWER_SUPPLY_TYPE_USB) {
+		chip = container_of(psy, struct ltc4088_chg_chip,
+						usb_psy);
+		switch (psp) {
+		case POWER_SUPPLY_PROP_ONLINE:
+			ltc4088_set_charging(chip, val->intval);
+			break;
+		case POWER_SUPPLY_PROP_CURRENT_MAX:
+			ltc4088_set_max_current(chip, val->intval);
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+static int __devinit ltc4088_charger_probe(struct platform_device *pdev)
+{
+	int rc;
+	struct ltc4088_chg_chip *chip;
+	const struct ltc4088_charger_platform_data *pdata
+			= pdev->dev.platform_data;
+
+	if (!pdata) {
+		pr_err("missing platform data\n");
+		return -EINVAL;
+	}
+
+	chip = kzalloc(sizeof(struct ltc4088_chg_chip),
+					GFP_KERNEL);
+	if (!chip) {
+		pr_err("Cannot allocate pm_chg_chip\n");
+		return -ENOMEM;
+	}
+
+	chip->dev = &pdev->dev;
+
+	if (pdata->gpio_mode_select_d0 < 0 ||
+		 pdata->gpio_mode_select_d1 < 0 ||
+		 pdata->gpio_mode_select_d2 < 0) {
+		pr_err("Invalid platform data supplied\n");
+		rc = -EINVAL;
+		goto free_chip;
+	}
+
+	mutex_init(&chip->lock);
+
+	chip->gpio_mode_select_d0 = pdata->gpio_mode_select_d0;
+	chip->gpio_mode_select_d1 = pdata->gpio_mode_select_d1;
+	chip->gpio_mode_select_d2 = pdata->gpio_mode_select_d2;
+
+	chip->usb_psy.name = "usb",
+	chip->usb_psy.type = POWER_SUPPLY_TYPE_USB,
+	chip->usb_psy.supplied_to = pm_power_supplied_to,
+	chip->usb_psy.num_supplicants = ARRAY_SIZE(pm_power_supplied_to),
+	chip->usb_psy.properties = pm_power_props,
+	chip->usb_psy.num_properties = ARRAY_SIZE(pm_power_props),
+	chip->usb_psy.get_property = pm_power_get_property,
+	chip->usb_psy.set_property = pm_power_set_property,
+
+	rc = power_supply_register(chip->dev, &chip->usb_psy);
+	if (rc < 0) {
+		pr_err("power_supply_register usb failed rc = %d\n", rc);
+		goto free_chip;
+	}
+
+	platform_set_drvdata(pdev, chip);
+
+	rc = ltc4088_set_initial_state(chip);
+	if (rc < 0) {
+		pr_err("setting initial state failed rc = %d\n", rc);
+		goto unregister_usb;
+	}
+
+	return 0;
+
+unregister_usb:
+	platform_set_drvdata(pdev, NULL);
+	power_supply_unregister(&chip->usb_psy);
+free_chip:
+	kfree(chip);
+
+	return rc;
+}
+
+static int __devexit ltc4088_charger_remove(struct platform_device *pdev)
+{
+	struct ltc4088_chg_chip *chip = platform_get_drvdata(pdev);
+
+	ltc4088_set_charging_off(chip);
+
+	gpio_free(chip->gpio_mode_select_d2);
+	gpio_free(chip->gpio_mode_select_d1);
+	gpio_free(chip->gpio_mode_select_d0);
+
+	power_supply_unregister(&chip->usb_psy);
+
+	platform_set_drvdata(pdev, NULL);
+	mutex_destroy(&chip->lock);
+	kfree(chip);
+
+	return 0;
+}
+
+static struct platform_driver ltc4088_charger_driver = {
+	.probe	= ltc4088_charger_probe,
+	.remove	= __devexit_p(ltc4088_charger_remove),
+	.driver	= {
+		.name	= LTC4088_CHARGER_DEV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init ltc4088_charger_init(void)
+{
+	return platform_driver_register(&ltc4088_charger_driver);
+}
+
+static void __exit ltc4088_charger_exit(void)
+{
+	platform_driver_unregister(&ltc4088_charger_driver);
+}
+
+subsys_initcall(ltc4088_charger_init);
+module_exit(ltc4088_charger_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("LTC4088 charger/battery driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" LTC4088_CHARGER_DEV_NAME);
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c
index 03810ce..a184ab6 100644
--- a/drivers/power/power_supply_core.c
+++ b/drivers/power/power_supply_core.c
@@ -25,6 +25,44 @@
 
 static struct device_type power_supply_dev_type;
 
+/**
+ * power_supply_set_current_limit - set current limit
+ * @psy:	the power supply to control
+ * @limit:	current limit in uA from the power supply.
+ *		0 will disable the power supply.
+ *
+ * This function will set a maximum supply current from a source
+ * and it will disable the charger when limit is 0.
+ */
+int power_supply_set_current_limit(struct power_supply *psy, int limit)
+{
+	const union power_supply_propval ret = {limit,};
+
+	if (psy->set_property)
+		return psy->set_property(psy, POWER_SUPPLY_PROP_CURRENT_MAX,
+								&ret);
+
+	return -ENXIO;
+}
+EXPORT_SYMBOL_GPL(power_supply_set_current_limit);
+
+/**
+ * power_supply_set_charging_by - set charging state of the charger
+ * @psy:	the power supply to control
+ * @enable:	enables or disables the charger
+ */
+int power_supply_set_charging_by(struct power_supply *psy, bool enable)
+{
+	const union power_supply_propval ret = {enable,};
+
+	if (psy->set_property)
+		return psy->set_property(psy, POWER_SUPPLY_PROP_ONLINE,
+								&ret);
+
+	return -ENXIO;
+}
+EXPORT_SYMBOL_GPL(power_supply_set_charging_by);
+
 static int __power_supply_changed_work(struct device *dev, void *data)
 {
 	struct power_supply *psy = (struct power_supply *)data;
diff --git a/include/linux/power/ltc4088-charger.h b/include/linux/power/ltc4088-charger.h
new file mode 100644
index 0000000..7a0bacf
--- /dev/null
+++ b/include/linux/power/ltc4088-charger.h
@@ -0,0 +1,30 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#ifndef LTC4088_CHARGER_H_
+#define LTC4088_CHARGER_H_
+
+#define LTC4088_CHARGER_DEV_NAME    "ltc4088-charger"
+
+/**
+ * struct ltc4088_charger_platform_data - platform data for LTC4088 charger
+ * @gpio_mode_select_d0: GPIO #pin for D0 charger line
+ * @gpio_mode_select_d1: GPIO #pin for D1 charger line
+ * @gpio_mode_select_d2: GPIO #pin for D2 charger line
+ */
+struct ltc4088_charger_platform_data {
+	unsigned int	gpio_mode_select_d0;
+	unsigned int	gpio_mode_select_d1;
+	unsigned int	gpio_mode_select_d2;
+};
+
+#endif /* LTC4088_CHARGER_H_ */
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 2287c321..cab042b 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -157,6 +157,8 @@
 				     enum power_supply_property psp);
 	void (*external_power_changed)(struct power_supply *psy);
 	void (*set_charged)(struct power_supply *psy);
+	int (*set_current_limit)(struct power_supply *psy, int limit);
+	int (*set_charging_by)(struct power_supply *psy, bool enable);
 
 	/* For APM emulation, think legacy userspace. */
 	int use_for_apm;
@@ -205,6 +207,8 @@
 extern void power_supply_changed(struct power_supply *psy);
 extern int power_supply_am_i_supplied(struct power_supply *psy);
 extern int power_supply_set_battery_charged(struct power_supply *psy);
+extern int power_supply_set_current_limit(struct power_supply *psy, int limit);
+extern int power_supply_set_charging_by(struct power_supply *psy, bool enable);
 
 #if defined(CONFIG_POWER_SUPPLY) || defined(CONFIG_POWER_SUPPLY_MODULE)
 extern int power_supply_is_system_supplied(void);