diff --git a/drivers/usb/gadget/charger.c b/drivers/usb/gadget/charger.c
new file mode 100644
index 0000000..3ca0180
--- /dev/null
+++ b/drivers/usb/gadget/charger.c
@@ -0,0 +1,547 @@
+/*
+ * usb charger.c -- USB charger driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/extcon.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/usb_charger.h>
+
+#define DEFAULT_CUR_PROTECT	(50)
+#define DEFAULT_SDP_CUR_LIMIT	(500 - DEFAULT_CUR_PROTECT)
+#define DEFAULT_DCP_CUR_LIMIT	(1500 - DEFAULT_CUR_PROTECT)
+#define DEFAULT_CDP_CUR_LIMIT	(1500 - DEFAULT_CUR_PROTECT)
+#define DEFAULT_ACA_CUR_LIMIT	(1500 - DEFAULT_CUR_PROTECT)
+
+static LIST_HEAD(usb_charger_list);
+static DEFINE_MUTEX(usb_charger_list_lock);
+
+/*
+ * usb_charger_find_by_name - Get the usb charger device by name.
+ * @name - usb charger device name.
+ *
+ * notes: when this function walks the list and returns a charger
+ * it's dropped the lock which means that something else could come
+ * along and delete the charger before we dereference the pointer.
+ * It's very unlikely but it's a possibility so you should take care
+ * of it.
+ * Thus when you get the usb charger by name, you should call
+ * put_usb_charger() to derease the reference count of the usb charger.
+ *
+ * return the instance of usb charger device.
+ */
+struct usb_charger *usb_charger_find_by_name(char *name)
+{
+	struct usb_charger *uchger;
+
+	if (!name)
+		return ERR_PTR(-EINVAL);
+
+	mutex_lock(&usb_charger_list_lock);
+	list_for_each_entry(uchger, &usb_charger_list, entry) {
+		if (!strcmp(uchger->name, name)) {
+			get_usb_charger(uchger);
+			mutex_unlock(&usb_charger_list_lock);
+			return uchger;
+		}
+	}
+	mutex_unlock(&usb_charger_list_lock);
+
+	return NULL;
+}
+
+/*
+ * usb_charger_register_notify() - Register a notifiee to get notified by
+ * 		any attach status changes from the usb charger type detection.
+ * @uchger - the usb charger device which is monitored.
+ * @nb - a notifier block to be registered.
+ */
+void usb_charger_register_notify(struct usb_charger *uchger,
+				 struct notifier_block *nb)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&uchger->lock, flags);
+	raw_notifier_chain_register(&uchger->uchger_nh, nb);
+	spin_unlock_irqrestore(&uchger->lock, flags);
+}
+
+/*
+ * usb_charger_unregister_notify() - Unregister a notifiee from the usb charger.
+ * @uchger - the usb charger device which is monitored.
+ * @nb - a notifier block to be unregistered.
+ */
+void usb_charger_unregister_notify(struct usb_charger *uchger,
+				   struct notifier_block *nb)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&uchger->lock, flags);
+	raw_notifier_chain_unregister(&uchger->uchger_nh, nb);
+	spin_unlock_irqrestore(&uchger->lock, flags);
+}
+
+/*
+ * usb_charger_register_extcon_notifier() - Register a notifiee of the usb
+ * 		charger to get notified by any attach status changes from
+ * 		the extcon device.
+ * @uchger - the usb charger device.
+ * @edev - the extcon device.
+ * @extcon_id - extcon id.
+ */
+int usb_charger_register_extcon_notifier(struct usb_charger *uchger,
+					 struct extcon_dev *edev,
+					 unsigned int extcon_id)
+{
+	if (!uchger || !edev)
+		return -EINVAL;
+
+	return extcon_register_notifier(edev, extcon_id, &uchger->extcon_nb.nb);
+}
+
+/*
+ * usb_charger_unregister_extcon_notifier() - Unregister a notifiee of the
+ * 		 usb charger from the extcon device.
+ * @uchger - the usb charger device.
+ * @edev - the extcon device.
+ * @extcon_id - extcon id.
+ */
+int usb_charger_unregister_extcon_notifier(struct usb_charger *uchger,
+					   struct extcon_dev *edev,
+					   unsigned int extcon_id)
+{
+	if (!uchger || !edev)
+		return -EINVAL;
+
+	return extcon_unregister_notifier(edev, extcon_id, &uchger->extcon_nb.nb);
+}
+
+/*
+ * usb_charger_register_gadget_notifier() - Register a notifiee of the usb
+ * 		charger to get notified by any attach status changes from
+ * 		the usb gadget device.
+ * @uchger - the usb charger device.
+ */
+int usb_charger_register_gadget_notifier(struct usb_charger *uchger)
+{
+	struct usb_gadget *ugadget = uchger->gadget;
+
+	return usb_gadget_register_notify(ugadget, &uchger->gadget_nb);
+}
+
+/*
+ * usb_charger_unregister_gadget_notifier() - Unregister a notifiee of the usb
+ * 		charger from the usb gadget device.
+ * @uchger - the usb charger device.
+ */
+int usb_charger_unregister_gadget_notifier(struct usb_charger *uchger)
+{
+	struct usb_gadget *ugadget = uchger->gadget;
+
+	return usb_gadget_unregister_notify(ugadget, &uchger->gadget_nb);
+}
+
+/*
+ * usb_charger_set_cur_limit() - Set the current limitation.
+ * @uchger - the usb charger device.
+ * @xxx_cur - the current limit by different charger type.
+ *
+ */
+int usb_charger_set_cur_limit(struct usb_charger *uchger,
+			      struct usb_charger_cur_limit *cur_limit_set)
+{
+	if (!uchger || !cur_limit_set)
+		return -EINVAL;
+
+	uchger->cur_limit.sdp_cur_limit = cur_limit_set->sdp_cur_limit;
+	uchger->cur_limit.dcp_cur_limit = cur_limit_set->dcp_cur_limit;
+	uchger->cur_limit.cdp_cur_limit = cur_limit_set->cdp_cur_limit;
+	uchger->cur_limit.aca_cur_limit = cur_limit_set->aca_cur_limit;
+	return 0;
+}
+
+/*
+ * usb_charger_get_cur_limit() - Get the current limitation by different usb
+ * 		charger type.
+ * @uchger - the usb charger device.
+ * @type - the usb charger type.
+ *
+ * return the current limitation to set.
+ */
+static unsigned int
+usb_charger_get_cur_limit(struct usb_charger *uchger)
+{
+	enum usb_charger_type uchger_type = uchger->type;
+	unsigned int cur_limit;
+
+	switch (uchger_type) {
+	case SDP_TYPE:
+		cur_limit = uchger->cur_limit.sdp_cur_limit;
+		break;
+	case DCP_TYPE:
+		cur_limit = uchger->cur_limit.dcp_cur_limit;
+		break;
+	case CDP_TYPE:
+		cur_limit = uchger->cur_limit.cdp_cur_limit;
+		break;
+	case ACA_TYPE:
+		cur_limit = uchger->cur_limit.aca_cur_limit;
+		break;
+	default:
+		return 0;
+	}
+
+	return cur_limit;
+}
+
+/*
+ * usb_charger_detect_type() - Get the usb charger type by the callback which is
+ * 		implemented by user.
+ * @uchger - the usb charger device.
+ *
+ * return the usb charger type.
+ */
+enum usb_charger_type
+usb_charger_detect_type(struct usb_charger *uchger)
+{
+	if (uchger->gadget && uchger->gadget->ops
+	    && uchger->gadget->ops->get_charger_type)
+		uchger->type =
+			uchger->gadget->ops->get_charger_type(uchger->gadget);
+	else
+		uchger->type = UNKNOWN_TYPE;
+
+	return uchger->type;
+}
+
+/*
+ * usb_charger_notifier_others() - It will notify other device registered on
+ * 		usb charger.
+ * @uchger - the usb charger device.
+ *
+ */
+static void
+usb_charger_notify_others(struct usb_charger *uchger,
+			  enum usb_charger_state state)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&uchger->lock, flags);
+	uchger->state = state;
+
+	switch (state) {
+	case USB_CHARGER_PRESENT:
+		usb_charger_detect_type(uchger);
+		raw_notifier_call_chain(&uchger->uchger_nh,
+			usb_charger_get_cur_limit(uchger),
+			uchger);
+		break;
+	case USB_CHARGER_REMOVE:
+		uchger->type = UNKNOWN_TYPE;
+		raw_notifier_call_chain(&uchger->uchger_nh, 0, uchger);
+		break;
+	default:
+		dev_warn(&uchger->dev, "Unknown USB charger state: %d\n",
+			 state);
+		break;
+	}
+	spin_unlock_irqrestore(&uchger->lock, flags);
+}
+
+/*
+ * usb_charger_plug_by_extcon() - The notifier call function which is registered
+ * 		on the extcon device.
+ * @nb - thr notifier block that notified by extcon device.
+ * @state - the extcon device state changed.
+ * @data - here specify a extcon device.
+ *
+ * return the notify flag.
+ */
+static int
+usb_charger_plug_by_extcon(struct notifier_block *nb,
+			   unsigned long state, void *data)
+{
+	struct usb_charger_nb *extcon_nb =
+		container_of(nb, struct usb_charger_nb, nb);
+	struct usb_charger *uchger = extcon_nb->uchger;
+	enum usb_charger_state uchger_state;
+
+	if (!uchger)
+		return NOTIFY_BAD;
+
+	/* Report event to power to setting the current limitation
+	 * for this usb charger when one usb charger is added or removed
+	 * with detecting by extcon device.
+	 */
+	if (state)
+		uchger_state = USB_CHARGER_PRESENT;
+	else
+		uchger_state = USB_CHARGER_REMOVE;
+
+	usb_charger_notify_others(uchger, uchger_state);
+
+	return NOTIFY_OK;
+}
+
+/*
+ * usb_charger_plug_by_gadget() - Set the usb charger current limitation
+ * 		according to the usb gadget device state.
+ * @data - here specify a usb charger device.
+ *
+ */
+static int
+usb_charger_plug_by_gadget(struct notifier_block *nb,
+			   unsigned long state, void *data)
+{
+	struct usb_gadget *gadget = (struct usb_gadget *)data;
+	struct usb_charger *uchger = gadget->uchger;
+	enum usb_charger_state uchger_state;
+
+	if (!uchger)
+		return NOTIFY_BAD;
+
+	/* Report event to power to setting the current limitation
+	 * for this usb charger when one usb charger state is changed
+	 * with detecting by usb gadget state.
+	 */
+	if (uchger->old_gadget_state != state) {
+		uchger->old_gadget_state = state;
+
+		if (state >= USB_STATE_ATTACHED)
+			uchger_state = USB_CHARGER_PRESENT;
+		else if (state == USB_STATE_NOTATTACHED)
+			uchger_state = USB_CHARGER_REMOVE;
+		else
+			uchger_state = USB_CHARGER_DEFAULT;
+
+		usb_charger_notify_others(uchger, uchger_state);
+	}
+
+	return NOTIFY_OK;
+}
+
+static int devm_uchger_dev_match(struct device *dev, void *res, void *data)
+{
+	struct usb_charger **r = res;
+
+	if (WARN_ON(!r || !*r))
+		return 0;
+
+	return *r == data;
+}
+
+static void usb_charger_release(struct device *dev)
+{
+	struct usb_charger *uchger = dev_get_drvdata(dev);
+
+	if (!atomic_dec_and_test(&uchger->count)) {
+		dev_err(dev, "The usb charger is still in use\n");
+		return;
+	}
+
+	kfree(uchger->name);
+	kfree(uchger);
+}
+
+/*
+ * usb_charger_unregister() - Unregister a usb charger device.
+ * @uchger - the usb charger device.
+ *
+ */
+int usb_charger_unregister(struct usb_charger *uchger)
+{
+	if (!uchger)
+		return -EINVAL;
+
+	mutex_lock(&usb_charger_list_lock);
+	list_del(&uchger->entry);
+	mutex_unlock(&usb_charger_list_lock);
+
+	device_unregister(&uchger->dev);
+	return 0;
+}
+
+static void devm_uchger_dev_unreg(struct device *dev, void *res)
+{
+	usb_charger_unregister(*(struct usb_charger **)res);
+}
+
+void devm_usb_charger_unregister(struct device *dev,
+				 struct usb_charger *uchger)
+{
+	devres_release(dev, devm_uchger_dev_unreg,
+		       devm_uchger_dev_match, uchger);
+}
+
+/*
+ * usb_charger_register() - Register a new usb charger device.
+ * @uchger - the new usb charger device.
+ *
+ */
+int usb_charger_register(struct device *dev, struct usb_charger *uchger)
+{
+	static atomic_t uchger_no = ATOMIC_INIT(-1);
+	struct usb_charger *tmp;
+	int ret;
+
+	if (!uchger) {
+		dev_err(dev, "no device provided for charger\n");
+		return -EINVAL;
+	}
+
+	uchger->dev.parent = dev;
+	uchger->dev.release = usb_charger_release;
+	dev_set_name(&uchger->dev, "usb-chger%lu",
+		     (unsigned long)atomic_inc_return(&uchger_no));
+
+	ret = device_register(&uchger->dev);
+	if (ret) {
+		put_device(&uchger->dev);
+		return ret;
+	}
+
+	dev_set_drvdata(&uchger->dev, uchger);
+
+	mutex_lock(&usb_charger_list_lock);
+	list_for_each_entry(tmp, &usb_charger_list, entry) {
+		if (!(strcmp(tmp->name, uchger->name))) {
+			mutex_unlock(&usb_charger_list_lock);
+			ret = -EEXIST;
+			goto out;
+		}
+	}
+	list_add_tail(&uchger->entry, &usb_charger_list);
+	mutex_unlock(&usb_charger_list_lock);
+
+	return 0;
+
+out:
+	dev_err(dev, "Failed to register usb charger (%s)\n",
+		uchger->name);
+	device_unregister(&uchger->dev);
+	put_device(&uchger->dev);
+	return ret;
+}
+
+int devm_usb_charger_register(struct device *dev,
+			      struct usb_charger *uchger)
+{
+	struct usb_charger **ptr;
+	int ret;
+
+	ptr = devres_alloc(devm_uchger_dev_unreg, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr)
+		return -ENOMEM;
+
+	ret = usb_charger_register(dev, uchger);
+	if (ret) {
+		devres_free(ptr);
+		return ret;
+	}
+
+	*ptr = uchger;
+	devres_add(dev, ptr);
+
+	return 0;
+}
+
+int usb_charger_init(struct usb_gadget *ugadget)
+{
+	struct usb_charger *uchger;
+	struct extcon_dev *edev;
+	char buf[100];
+	char *str;
+	int ret;
+
+	if (!ugadget)
+		return -EINVAL;
+
+	uchger = devm_kzalloc(&ugadget->dev, sizeof(struct usb_charger),
+			      GFP_KERNEL);
+	if (!uchger)
+		return -ENOMEM;
+
+	sprintf(buf, "usb-charger.%s", ugadget->name);
+	str = devm_kzalloc(&ugadget->dev, sizeof(char) * (strlen(buf) + 1),
+			   GFP_KERNEL);
+	if (!str)
+		return -ENOMEM;
+
+	strcpy(str, buf);
+	uchger->name = str;
+	uchger->type = UNKNOWN_TYPE;
+	uchger->state = USB_CHARGER_DEFAULT;
+	uchger->cur_limit.sdp_cur_limit = DEFAULT_SDP_CUR_LIMIT;
+	uchger->cur_limit.dcp_cur_limit = DEFAULT_DCP_CUR_LIMIT;
+	uchger->cur_limit.cdp_cur_limit = DEFAULT_CDP_CUR_LIMIT;
+	uchger->cur_limit.aca_cur_limit = DEFAULT_ACA_CUR_LIMIT;
+
+	atomic_set(&uchger->count, 1);
+	spin_lock_init(&uchger->lock);
+	INIT_LIST_HEAD(&uchger->entry);
+	RAW_INIT_NOTIFIER_HEAD(&uchger->uchger_nh);
+
+	/* register a notifier on a extcon device if it is exsited */
+	edev = extcon_get_edev_by_phandle(ugadget->dev.parent, 0);
+	if (!IS_ERR_OR_NULL(edev)) {
+		uchger->extcon_dev = edev;
+		uchger->extcon_nb.nb.notifier_call = usb_charger_plug_by_extcon;
+		uchger->extcon_nb.uchger = uchger;
+		usb_charger_register_extcon_notifier(uchger, edev, EXTCON_USB);
+	}
+
+	/* register a notifier on a usb gadget device */
+	uchger->gadget = ugadget;
+	ugadget->uchger = uchger;
+	uchger->old_gadget_state = ugadget->state;
+	uchger->gadget_nb.notifier_call = usb_charger_plug_by_gadget;
+	usb_charger_register_gadget_notifier(uchger);
+
+	ret = usb_charger_register(&ugadget->dev, uchger);
+	if (ret)
+		goto reg_fail;
+
+	return 0;
+
+reg_fail:
+	if (uchger->extcon_dev)
+		usb_charger_unregister_extcon_notifier(uchger,
+				uchger->extcon_dev, EXTCON_USB);
+
+	usb_charger_unregister_gadget_notifier(uchger);
+	return ret;
+}
+
+int usb_charger_exit(struct usb_gadget *ugadget)
+{
+	struct usb_charger *uchger = ugadget->uchger;
+
+	if (!uchger)
+		return -EINVAL;
+
+	if (uchger->extcon_dev)
+		usb_charger_unregister_extcon_notifier(uchger,
+				uchger->extcon_dev, EXTCON_USB);
+
+	usb_charger_unregister_gadget_notifier(uchger);
+	return usb_charger_unregister(uchger);
+}
+
+MODULE_AUTHOR("Baolin Wang <baolin.wang@linaro.org>");
+MODULE_DESCRIPTION("USB charger driver");
diff --git a/include/linux/usb/usb_charger.h b/include/linux/usb/usb_charger.h
new file mode 100644
index 0000000..da4d3c9
--- /dev/null
+++ b/include/linux/usb/usb_charger.h
@@ -0,0 +1,101 @@
+#ifndef __LINUX_USB_CHARGER_H__
+#define __LINUX_USB_CHARGER_H__
+
+#include <linux/device.h>
+#include <linux/notifier.h>
+#include <linux/sysfs.h>
+#include <linux/usb/ch9.h>
+
+/* USB charger type:
+ * SDP (Standard Downstream Port)
+ * DCP (Dedicated Charging Port)
+ * CDP (Charging Downstream Port)
+ * ACA (Accessory Charger Adapters)
+ */
+enum usb_charger_type {
+	UNKNOWN_TYPE,
+	SDP_TYPE,
+	DCP_TYPE,
+	CDP_TYPE,
+	ACA_TYPE,
+};
+
+/* USB charger state */
+enum usb_charger_state {
+	USB_CHARGER_DEFAULT,
+	USB_CHARGER_PRESENT,
+	USB_CHARGER_REMOVE,
+};
+
+/* Current limitation by charger type */
+struct usb_charger_cur_limit {
+	unsigned int sdp_cur_limit;
+	unsigned int dcp_cur_limit;
+	unsigned int cdp_cur_limit;
+	unsigned int aca_cur_limit;
+};
+
+struct usb_charger_nb {
+	struct notifier_block	nb;
+	struct usb_charger	*uchger;
+};
+
+struct usb_charger {
+	/* Internal data. Please do not set. */
+	const char		*name;
+	struct device		dev;
+	struct raw_notifier_head	uchger_nh;
+	struct list_head	entry;
+	spinlock_t		lock;
+	enum usb_charger_type	type;
+	enum usb_charger_state	state;
+	atomic_t		count;
+
+	/* For supporting extcon usb gpio */
+	struct extcon_dev	*extcon_dev;
+	struct usb_charger_nb	extcon_nb;
+
+	/* For supporting usb gadget */
+	struct usb_gadget	*gadget;
+	enum usb_device_state	old_gadget_state;
+	struct notifier_block	gadget_nb;
+
+	/* Current limitation */
+	struct usb_charger_cur_limit	cur_limit;
+};
+
+extern struct usb_charger *usb_charger_find_by_name(char *name);
+
+extern void usb_charger_register_notify(struct usb_charger *uchger,
+					struct notifier_block *nb);
+extern void usb_charger_unregister_notify(struct usb_charger *uchger,
+					  struct notifier_block *nb);
+
+extern int usb_charger_register_extcon_notifier(struct usb_charger *uchger,
+						struct extcon_dev *edev,
+						unsigned int extcon_id);
+extern int usb_charger_unregister_extcon_notifier(struct usb_charger *uchger,
+						  struct extcon_dev *edev,
+						  unsigned int extcon_id);
+
+extern int usb_charger_register_gadget_notifier(struct usb_charger *uchger);
+extern int usb_charger_unregister_gadget_notifier(struct usb_charger *uchger);
+
+extern int usb_charger_set_cur_limit(struct usb_charger *uchger,
+			      struct usb_charger_cur_limit *cur_limit_set);
+extern enum usb_charger_type usb_charger_detect_type(struct usb_charger *uchger);
+
+extern int usb_charger_init(struct usb_gadget *ugadget);
+extern int usb_charger_exit(struct usb_gadget *ugadget);
+
+static inline void get_usb_charger(struct usb_charger *uchger)
+{
+	atomic_inc(&uchger->count);
+}
+
+static inline void put_usb_charger(struct usb_charger *uchger)
+{
+	atomic_dec(&uchger->count);
+}
+
+#endif /* __LINUX_USB_CHARGER_H__ */
