zte's code,first commit

Change-Id: I9a04da59e459a9bc0d67f101f700d9d7dc8d681b
diff --git a/ap/os/linux/linux-3.4.x/drivers/power/xc5071_charger.c b/ap/os/linux/linux-3.4.x/drivers/power/xc5071_charger.c
new file mode 100644
index 0000000..b20b8a4
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/power/xc5071_charger.c
@@ -0,0 +1,1066 @@
+/*
+ * Driver for the TI zx234502 battery charger.
+ *
+ * Author: Mark A. Greer <mgreer@animalcreek.com>
+ *
+ * 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/interrupt.h>
+#include <linux/delay.h>
+#include <linux/of_irq.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/power_supply.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/kthread.h>
+//#include <linux/mutex.h>
+#include <linux/semaphore.h>
+
+#include <linux/power/aw3215_charger.h>
+#include <linux/mfd/zx234290.h>
+
+#include <mach/gpio.h>
+#include <mach/pcu.h>
+#include <mach/zx29_usb.h>
+#include <linux/workqueue.h>
+
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <asm/uaccess.h>
+#include <linux/wakelock.h>
+
+#define USB_IN		GPIO_HIGH
+#define USB_OUT		GPIO_LOW
+#define IRQ_TYPE_USB_OUT	IRQ_TYPE_LEVEL_LOW
+#define IRQ_TYPE_USB_IN		IRQ_TYPE_LEVEL_HIGH
+
+#define CHG_START	GPIO_LOW
+#define CHG_STOP	GPIO_HIGH
+
+#define CHG_EN_TYPE		GPIO_HIGH
+#define CHG_DISEN_TYPE	GPIO_LOW
+/*
+ * The FAULT register is latched by the zx234502 (except for NTC_FAULT)
+ * so the first read after a fault returns the latched value and subsequent
+ * reads return the current value.  In order to return the fault status
+ * to the user, have the interrupt handler save the reg's value and retrieve
+ * it in the appropriate health/status routine.  Each routine has its own
+ * flag indicating whether it should use the value stored by the last run
+ * of the interrupt handler or do an actual reg read.  That way each routine
+ * can report back whatever fault may have occured.
+ */
+ enum chg_stop_reason{
+	CHG_STOP_REASON_NO = 0,
+	CHG_STOP_REASON_TEMP= 1,
+	CHG_STOP_REASON_FULL = 2,
+	CHG_STOP_DEFALT= 0xff
+};
+struct aw3215_dev_info {
+	struct device			*dev;
+	struct power_supply	charger;
+	struct power_supply	battery;
+
+	kernel_ulong_t		model;
+	unsigned int			chgin_irq;
+	unsigned int			chgstate_irq;
+	struct task_struct		*chgin_irq_thread;
+	struct task_struct		*chgstate_irq_thread;
+
+	u8					watchdog;
+
+	struct aw3215_platform_data 	*pdata;
+	unsigned int					chgin_type;
+	unsigned int					chgstate_type;
+	bool							chg_en;
+	unsigned int					chg_state;
+	struct semaphore		chgin_sem;
+	struct semaphore		chgstate_sem;	
+	enum chg_stop_reason	stopchg_flag;
+	struct timer_list		changed_timer;
+	struct wake_lock		wlock_chgfull;
+
+};
+
+struct aw3215_dev_info *g_bdi = NULL;
+
+/* Charger power supply property routines */
+
+static int aw3215_charger_get_charge_type(struct aw3215_dev_info *bdi,union power_supply_propval *val)
+{
+	val->intval = bdi->charger.type;
+	return 0;
+}
+
+static int aw3215_charger_get_status(struct aw3215_dev_info *bdi,union power_supply_propval *val)
+{
+#if 1
+	val->intval = bdi->chg_state;
+#else
+	if(false==bdi->chg_en){
+		val->intval=POWER_SUPPLY_STATUS_NOT_CHARGING;/*diaable chg*/
+		return 0;
+	}
+	if (USB_IN==bdi->chgin_type) {
+		if(CHG_STOP==bdi->chgstate_type)
+			val->intval= POWER_SUPPLY_STATUS_FULL;
+		else if (CHG_START==bdi->chgstate_type)
+			val->intval= POWER_SUPPLY_STATUS_CHARGING;
+		else
+			val->intval=POWER_SUPPLY_STATUS_NOT_CHARGING;/*diaable chg*/
+	} else {
+		val->intval=POWER_SUPPLY_STATUS_DISCHARGING;/*usb not insert*/
+	}
+#endif
+	return 0;
+}
+
+static int aw3215_charger_get_health(struct aw3215_dev_info *bdi,union power_supply_propval *val)
+{
+	val->intval = POWER_SUPPLY_HEALTH_GOOD;
+
+	return 0;
+}
+
+int aw3215_charger_get_online(struct aw3215_dev_info *bdi,union power_supply_propval *val)
+{
+	if (USB_IN==bdi->chgin_type) {
+		val->intval= 1;
+	} else {
+		val->intval=0;/*usb not insert*/
+	}
+
+	return 0;
+}EXPORT_SYMBOL (aw3215_charger_get_online);
+
+
+static int aw3215_charger_get_charger_enabled(struct aw3215_dev_info *bdi,union power_supply_propval *val)
+{
+	val->intval = bdi->chg_en;
+
+	return 0;
+}
+static int aw3215_charger_get_voltage_max(struct aw3215_dev_info *bdi,union power_supply_propval *val)
+{
+	val->intval = 4200;
+
+	return 0;
+}
+
+static int aw3215_charger_set_voltage(struct aw3215_dev_info *bdi,const union power_supply_propval *val)
+{
+	return 0;
+}
+static int aw3215_charger_set_charger_config(struct aw3215_dev_info *bdi,const union power_supply_propval *val)
+{
+	int ret = 0;
+	int gpio_state = 0;
+
+	if (val->intval==1) {
+		if (bdi->chg_en)
+			return 0;
+		gpio_state = CHG_EN_TYPE ;/*gpio low en chg*/
+		printk("mmi start chg\n");
+	} else {
+		if (!bdi->chg_en)
+			return 0;
+		gpio_state = CHG_DISEN_TYPE ;/*gpio high stop chg*/
+		printk("mmi stop chg\n");
+	}
+
+	disable_irq(bdi->chgstate_irq);
+
+	if (gpio_state == CHG_EN_TYPE) {
+		bdi->chg_en = true;		/*(~gpio_state)*/
+		bdi->chgstate_type = CHG_START;
+		bdi->stopchg_flag = CHG_STOP_REASON_NO;
+		gpio_set_value(bdi->pdata->gpio_chgen, gpio_state);
+
+		if (bdi->chgin_type == USB_IN) {
+			bdi->chg_state = POWER_SUPPLY_STATUS_CHARGING;
+			/* start charging in 5.3ms after enable */
+			if (gpio_get_value(bdi->pdata->gpio_chgstate))
+				mdelay(7);
+			if (gpio_get_value(bdi->pdata->gpio_chgstate))
+				printk(KERN_INFO "chg still not chargin"); /* should not go here */
+
+			irq_set_irq_type(bdi->chgstate_irq, IRQ_TYPE_LEVEL_HIGH);
+
+		}
+	} else {
+		bdi->chg_en = false;
+		bdi->chgstate_type = CHG_STOP;
+		bdi->stopchg_flag = CHG_STOP_REASON_TEMP;
+		if (2==val->intval){/*chg full stop*/
+			bdi->stopchg_flag = CHG_STOP_REASON_FULL;
+			bdi->chg_state = POWER_SUPPLY_STATUS_FULL;
+			printk("mmi full stop chg\n");
+	
+		}
+		else if (bdi->chgin_type == USB_IN)
+			bdi->chg_state = POWER_SUPPLY_STATUS_NOT_CHARGING;
+
+		gpio_set_value(bdi->pdata->gpio_chgen, gpio_state);
+		/* charger state changes from 0 to 1 in 0.12ms */
+		if (!gpio_get_value(bdi->pdata->gpio_chgstate))
+			udelay(500);
+
+		irq_set_irq_type(bdi->chgstate_irq, IRQ_TYPE_LEVEL_LOW);
+
+	}
+
+	enable_irq(bdi->chgstate_irq);
+
+	power_supply_changed(&bdi->charger);
+
+	return ret;
+}
+
+
+static int aw3215_charger_get_property(struct power_supply *psy,enum power_supply_property psp, union power_supply_propval *val)
+{
+	struct aw3215_dev_info *bdi = container_of(psy, struct aw3215_dev_info, charger);
+	int ret;
+
+	//dev_dbg(bdi->dev, "prop: %d\n", psp);
+
+	//pm_runtime_get_sync(bdi->dev);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_PC1_AC2:
+		ret = aw3215_charger_get_charge_type(bdi, val);
+		break;
+
+	case POWER_SUPPLY_PROP_STATUS:
+		ret = aw3215_charger_get_status(bdi, val);
+		break;
+	case POWER_SUPPLY_PROP_HEALTH:
+		ret = aw3215_charger_get_health(bdi, val);
+		break;
+	case POWER_SUPPLY_PROP_ONLINE:
+		ret = aw3215_charger_get_online(bdi, val);
+		break;
+
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+		ret = aw3215_charger_get_voltage_max(bdi, val);
+		break;
+
+	case POWER_SUPPLY_PROP_CHARGE_ENABLED:
+		ret = aw3215_charger_get_charger_enabled(bdi, val);
+		break;
+	default:
+		ret = -ENODATA;
+	}
+
+	//pm_runtime_put_sync(bdi->dev);
+	return ret;
+}
+
+static int aw3215_charger_set_property(struct power_supply *psy,enum power_supply_property psp,
+		const union power_supply_propval *val)
+{
+	struct aw3215_dev_info *bdi =
+			container_of(psy, struct aw3215_dev_info, charger);
+	int ret;
+
+	//dev_dbg(bdi->dev, "prop: %d\n", psp);
+
+	//pm_runtime_get_sync(bdi->dev);
+
+	switch (psp) {
+#if 0
+	case POWER_SUPPLY_PROP_CURRENT_NOW:
+		ret = zx234502_charger_set_current(bdi, val);
+		break;
+#endif
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+		ret = aw3215_charger_set_voltage(bdi, val);
+		break;
+
+    case POWER_SUPPLY_PROP_CHARGE_ENABLED:
+        ret = aw3215_charger_set_charger_config(bdi, val);
+        break;
+	default:
+		ret = -EINVAL;
+	}
+
+	//pm_runtime_put_sync(bdi->dev);
+	return ret;
+}
+
+static int aw3215_charger_property_is_writeable(struct power_supply *psy,enum power_supply_property psp)
+{
+	int ret;
+
+	switch (psp)
+	{
+    	//case POWER_SUPPLY_PROP_CURRENT_NOW:
+    	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+	case POWER_SUPPLY_PROP_CHARGE_ENABLED:
+    		ret = 1;
+    		break;
+	default:
+		ret = 0;
+		break;
+	}
+
+	return ret;
+}
+
+static enum power_supply_property aw3215_charger_properties[] = {
+	POWER_SUPPLY_PROP_PC1_AC2,
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_ONLINE,
+	//POWER_SUPPLY_PROP_CURRENT_NOW,
+	//POWER_SUPPLY_PROP_CURRENT_MAX,
+	//POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_VOLTAGE_MAX,
+	POWER_SUPPLY_PROP_CHARGE_ENABLED,
+};
+
+static char *aw3215_charger_supplied_to[] = {
+	"main-battery",
+};
+
+static void aw3215_charger_init(struct power_supply *charger)
+{
+	charger->name = "charger";
+	charger->type = POWER_SUPPLY_PCAC_UNKNOWN;
+	charger->properties = aw3215_charger_properties;
+	charger->num_properties = ARRAY_SIZE(aw3215_charger_properties);
+	charger->supplied_to = aw3215_charger_supplied_to;
+	//charger->num_supplies = ARRAY_SIZE(aw3215_charger_supplied_to);
+	charger->get_property = aw3215_charger_get_property;
+	charger->set_property = aw3215_charger_set_property;
+	charger->property_is_writeable = aw3215_charger_property_is_writeable;
+}
+
+/* Battery power supply property routines */
+
+static int aw3215_battery_get_health(struct aw3215_dev_info *bdi,union power_supply_propval *val)
+{
+	val->intval = POWER_SUPPLY_HEALTH_GOOD;
+
+	return 0;
+}
+
+static int aw3215_battery_get_online(struct aw3215_dev_info *bdi,union power_supply_propval *val)
+{
+	val->intval = 1;/*bat on*/
+
+	return 0;
+}
+
+static int aw3215_battery_set_online(struct aw3215_dev_info *bdi,const union power_supply_propval *val)
+{
+	return 0;
+}
+
+
+static int aw3215_battery_get_property(struct power_supply *psy,enum power_supply_property psp,
+		union power_supply_propval *val)
+{
+	struct aw3215_dev_info *bdi =
+			container_of(psy, struct aw3215_dev_info, battery);
+	int ret;
+
+	//dev_dbg(bdi->dev, "prop: %d\n", psp);
+
+	//pm_runtime_get_sync(bdi->dev);
+
+	switch (psp) {
+
+	case POWER_SUPPLY_PROP_HEALTH:
+		ret = aw3215_battery_get_health(bdi, val);
+		break;
+	case POWER_SUPPLY_PROP_ONLINE:
+		ret = aw3215_battery_get_online(bdi, val);
+		break;
+
+	case POWER_SUPPLY_PROP_TEMP:
+		val->intval = get_adc2_voltage();
+		ret = 0;
+		break;
+
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		//#ifdef CONFIG_ZX234290_ADC
+		val->intval = get_adc1_voltage() - 15;
+		ret = 0;
+		break;
+	default:
+		ret = -ENODATA;
+	}
+
+	//pm_runtime_put_sync(bdi->dev);
+	return ret;
+}
+
+static int aw3215_battery_set_property(struct power_supply *psy,enum power_supply_property psp,
+		const union power_supply_propval *val)
+{
+	struct aw3215_dev_info *bdi =
+			container_of(psy, struct aw3215_dev_info, battery);
+	int ret;
+
+	//dev_dbg(bdi->dev, "prop: %d\n", psp);
+
+	//pm_runtime_put_sync(bdi->dev);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		ret = aw3215_battery_set_online(bdi, val);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	//pm_runtime_put_sync(bdi->dev);
+	return ret;
+}
+
+static int aw3215_battery_property_is_writeable(struct power_supply *psy,enum power_supply_property psp)
+{
+	int ret;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		ret = 1;
+		break;
+	default:
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static enum power_supply_property aw3215_battery_properties[] = {
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	//POWER_SUPPLY_PROP_CAPACITY,
+};
+
+static void aw3215_battery_init(struct power_supply *battery)
+{
+	battery->name = "battery";
+	battery->type = POWER_SUPPLY_PCAC_UNKNOWN;
+	battery->properties = aw3215_battery_properties;
+	battery->num_properties = ARRAY_SIZE(aw3215_battery_properties);
+	battery->get_property = aw3215_battery_get_property;
+	battery->set_property = aw3215_battery_set_property;
+	battery->property_is_writeable = aw3215_battery_property_is_writeable;
+}
+
+#if 1
+static irqreturn_t aw3215_charger_in_irq_primary_handler(int irq, struct aw3215_dev_info * bdi)
+{
+	disable_irq_nosync(irq);
+	//pcu_int_clear(irq);
+	pcu_clr_irq_pending(irq);
+	up(&bdi->chgin_sem);
+	
+	return IRQ_HANDLED;
+}
+static irqreturn_t aw3215_charger_state_irq_primary_handler(int irq, struct aw3215_dev_info * bdi)
+{
+	disable_irq_nosync(irq);
+	//pcu_int_clear(irq);
+	pcu_clr_irq_pending(irq);
+	up(&bdi->chgstate_sem);
+	
+	return IRQ_HANDLED;
+}
+
+#endif
+
+static irqreturn_t aw3215_chgin_irq_handler_thread(void *data)
+{
+	struct aw3215_dev_info *bdi = data;
+	struct aw3215_platform_data *pdata = bdi->pdata;
+	int g_gpio_in =0;
+	struct sched_param param = { .sched_priority = 2 };
+
+	param.sched_priority= 31;
+	sched_setscheduler(current, SCHED_FIFO, &param);
+
+	while(1)
+	{
+		down(&bdi->chgin_sem);
+
+		g_gpio_in = gpio_get_value(pdata->gpio_chgin);
+
+		/*charging status*/
+		if (g_gpio_in == USB_IN) {
+			if (bdi->chgin_type == USB_IN) {
+				printk(KERN_INFO"chg usb in err\n");
+			} 
+			else if (bdi->stopchg_flag == CHG_STOP_REASON_TEMP){
+				bdi->chgin_type = USB_IN;
+				printk(KERN_INFO"chg usb in temp err\n");
+				bdi->chg_state = POWER_SUPPLY_STATUS_NOT_CHARGING;
+				dwc_otg_chg_inform(0);/*usb in*/
+			}
+			else{
+				bdi->chgin_type = USB_IN;
+
+				disable_irq(bdi->chgstate_irq);
+				//gpio_set_value(pdata->gpio_chgen, CHG_EN_TYPE);
+				//bdi->chg_en = true;
+				//bdi->chgstate_type = CHG_START;
+
+				/* start charging in 5.3ms after enable */
+				if (gpio_get_value(pdata->gpio_chgstate))
+					mdelay(7);
+				if (bdi->chgstate_type == gpio_get_value(pdata->gpio_chgstate))
+					printk(KERN_INFO "chg still not chargin"); /* should not go here */
+
+				bdi->chg_state = POWER_SUPPLY_STATUS_CHARGING;
+
+				irq_set_irq_type(bdi->chgstate_irq, IRQ_TYPE_LEVEL_HIGH);
+
+				enable_irq(bdi->chgstate_irq);
+
+				printk(KERN_INFO"chg usb in\n");
+				dwc_otg_chg_inform(0);/*usb in*/
+
+				//irq_set_irq_type(bdi->chgin_irq, IRQ_TYPE_LEVEL_HIGH);		
+			}
+			irq_set_irq_type(bdi->chgin_irq, IRQ_TYPE_USB_OUT/*IRQ_TYPE_LEVEL_HIGH*/);
+		} else {
+			bdi->chg_state = POWER_SUPPLY_STATUS_DISCHARGING;
+			if (bdi->chgin_type == USB_OUT) {
+				printk(KERN_INFO"chg usb out err\n");
+			} else {
+				bdi->chgin_type = USB_OUT;
+				//SINT32 Usb_plug = DISCONNECTED_FROM_HOST;
+				printk(KERN_INFO"chg usb out\n");
+				dwc_otg_chg_inform(1);/*usb out*/
+				//irq_set_irq_type(bdi->chgin_irq, IRQ_TYPE_LEVEL_LOW);
+			}
+			
+			irq_set_irq_type(bdi->chgin_irq, IRQ_TYPE_USB_IN/*IRQ_TYPE_LEVEL_LOW*/);
+		}
+
+		power_supply_changed(&bdi->charger);
+		power_supply_changed(&bdi->battery);
+		enable_irq(bdi->chgin_irq);
+
+	}
+	return 0;
+}
+
+static irqreturn_t aw3215_chgstate_irq_handler_thread(void *data)
+{
+	struct aw3215_dev_info *bdi = data;
+	struct aw3215_platform_data *pdata = bdi->pdata;
+	//unsigned int state_rcd = bdi->chg_state;
+	int g_gpio_state=CHG_STOP;
+	uint adc1_v= 0;
+	struct sched_param param = { .sched_priority = 2 };
+
+	param.sched_priority= 31;
+	sched_setscheduler(current, SCHED_FIFO, &param);
+
+	while(1)
+	{
+		down(&bdi->chgstate_sem);
+
+		g_gpio_state = gpio_get_value(pdata->gpio_chgstate);
+
+		/*charging status*/
+		if (g_gpio_state == CHG_START) {	/*low charging*/
+			bdi->chg_state = POWER_SUPPLY_STATUS_CHARGING;
+
+			if (bdi->chgstate_type == CHG_START) {
+				printk(KERN_INFO"chg chging err!\n");
+			} else {
+				bdi->chgstate_type = CHG_START;
+
+				//irq_set_irq_type(bdi->chgstate_irq, IRQ_TYPE_LEVEL_HIGH);
+					power_supply_changed(&bdi->charger);
+					power_supply_changed(&bdi->battery);
+				printk(KERN_INFO"chg charging\n");
+			}
+			irq_set_irq_type(bdi->chgstate_irq, IRQ_TYPE_LEVEL_HIGH);
+		} else {/*high stop charging*/
+			if (bdi->chgstate_type == CHG_STOP) {
+				printk(KERN_INFO"chg full err!\n");
+			} else {
+				if ((bdi->chgin_type == USB_IN)&&(bdi->stopchg_flag == CHG_STOP_REASON_TEMP)){
+					bdi->chg_state = POWER_SUPPLY_STATUS_NOT_CHARGING;	
+					power_supply_changed(&bdi->charger);
+					power_supply_changed(&bdi->battery);
+				}
+				else if(bdi->chgin_type == USB_IN){
+					bdi->chg_state = POWER_SUPPLY_STATUS_FULL;
+					wake_lock(&(bdi->wlock_chgfull)); 
+					mod_timer(&bdi->changed_timer, jiffies + msecs_to_jiffies(200));			
+				}
+				else{
+					bdi->chg_state = POWER_SUPPLY_STATUS_DISCHARGING;
+						power_supply_changed(&bdi->charger);
+						power_supply_changed(&bdi->battery);
+					}
+					
+				bdi->chgstate_type = CHG_STOP;
+
+				printk(KERN_INFO "chg %s %s stop\n",
+				(bdi->chg_state == POWER_SUPPLY_STATUS_FULL) ? "full" : " ",
+				(bdi->chg_state == POWER_SUPPLY_STATUS_NOT_CHARGING) ? "temp error":"discharging");
+
+			}
+			irq_set_irq_type(bdi->chgstate_irq, IRQ_TYPE_LEVEL_LOW);
+
+		}
+		enable_irq(bdi->chgstate_irq);
+
+	}
+	return 0;
+}
+
+
+static int aw3215_setup_pdata(struct aw3215_dev_info *bdi,
+		struct aw3215_platform_data *pdata)
+{
+	int ret;
+
+	  //chg en
+	if (!gpio_is_valid(pdata->gpio_chgen))
+		return -1;
+
+	ret = gpio_request(pdata->gpio_chgen, "chg_en");
+	if (ret < 0)
+		goto out;
+	ret = zx29_gpio_config(pdata->gpio_chgen, pdata->gpio_chgen_gpio_sel);
+	ret = gpio_direction_output(pdata->gpio_chgen, CHG_EN_TYPE);
+	bdi->chg_en =true;
+	bdi->stopchg_flag=CHG_STOP_DEFALT;
+
+    //chg termination current ctrl
+	if (!gpio_is_valid(pdata->gpio_chgctrl)){
+		printk("chg chgctrl gpio undifined\n");
+		return 0;
+	}
+
+	ret = gpio_request(pdata->gpio_chgctrl, "chg_ctrl");
+	if (ret < 0)
+		goto out;
+	ret = zx29_gpio_config(pdata->gpio_chgctrl, pdata->gpio_chgctrl_gpio_sel);
+	if (ret < 0)
+		goto out;
+	ret = gpio_direction_output(pdata->gpio_chgctrl, 0);
+	if (ret < 0)
+		goto out;
+
+	return 0;
+out:
+	//gpio_free(pdata->gpio_int);
+	return -1;
+}
+
+
+static int aw3215_init_state(struct aw3215_dev_info *bdi)
+{
+	struct aw3215_platform_data *pdata;
+	int ret = 0;
+	unsigned int g_gpio_in,g_gpio_state;
+	unsigned int chgin_irq_type=IRQ_TYPE_NONE;
+	unsigned int chgstate_irq_type=IRQ_TYPE_NONE;
+
+	pdata = bdi->pdata;
+	/*chgin*/
+	if (!gpio_is_valid(pdata->gpio_chgin))
+		goto error;
+
+	ret = gpio_request(pdata->gpio_chgin, "chg_usbin");
+	if (ret < 0)
+		goto error;
+
+	zx29_gpio_pd_pu_set(pdata->gpio_chgin, IO_CFG_PULL_DISABLE);
+
+	ret = gpio_direction_input(pdata->gpio_chgin);
+	if (ret < 0)
+		goto error;
+	ret = zx29_gpio_config(pdata->gpio_chgin,pdata->gpio_chgin_fun_sel);
+	if (ret < 0)
+		goto error;
+
+	bdi->chgin_irq= gpio_to_irq(pdata->gpio_chgin);
+
+	/*chg state*/
+    if (!gpio_is_valid(pdata->gpio_chgstate))
+		goto error;
+
+	ret = gpio_request(pdata->gpio_chgstate, "chg_state");
+	if (ret < 0)
+		goto error;
+
+	zx29_gpio_pd_pu_set(pdata->gpio_chgstate, IO_CFG_PULL_DISABLE);
+	ret = gpio_direction_input(pdata->gpio_chgstate);
+	if (ret < 0)
+		goto error;
+	ret = zx29_gpio_config(pdata->gpio_chgstate,pdata->gpio_chgstate_fun_sel);
+	if (ret < 0)
+		goto error;
+
+	bdi->chgstate_irq= gpio_to_irq(pdata->gpio_chgstate);
+
+	mdelay(20);/*?*/
+	g_gpio_in = gpio_get_value(pdata->gpio_chgin);
+	g_gpio_state = gpio_get_value(pdata->gpio_chgstate);
+
+	if ( USB_IN == g_gpio_in){
+		bdi->chgin_type = USB_IN;
+		chgin_irq_type=IRQ_TYPE_USB_OUT;/*IRQ_TYPE_LEVEL_HIGH*/
+		if(CHG_START == g_gpio_state){
+			bdi->chgstate_type=CHG_START ;
+			bdi->chg_state=POWER_SUPPLY_STATUS_CHARGING;
+			chgstate_irq_type=IRQ_TYPE_LEVEL_HIGH;			
+		}
+		else{
+			bdi->chgstate_type =CHG_STOP ;	
+			chgstate_irq_type = IRQ_TYPE_LEVEL_LOW;	
+			bdi->chg_state=POWER_SUPPLY_STATUS_FULL;			
+		}
+		printk(KERN_INFO"init usb in\n");
+		dwc_otg_chg_inform(0);/*usb in*/	
+	}
+	else{
+		bdi->chgin_type = USB_OUT;
+		bdi->chg_state=POWER_SUPPLY_STATUS_DISCHARGING;
+		chgin_irq_type = IRQ_TYPE_USB_IN/*IRQ_TYPE_LEVEL_LOW*/;
+		if(CHG_START == g_gpio_state){
+			bdi->chgstate_type=CHG_START ;
+			chgstate_irq_type=IRQ_TYPE_LEVEL_HIGH;		
+			printk(KERN_INFO"init chg_state err\n");
+		}
+		else{
+			bdi->chgstate_type =CHG_STOP ;	
+			chgstate_irq_type = IRQ_TYPE_LEVEL_LOW;	
+		}
+		printk(KERN_INFO"init usb out\n");
+		dwc_otg_chg_inform(1);/*usb out*/		
+	}
+	irq_set_irq_type(bdi->chgin_irq, chgin_irq_type);
+	irq_set_irq_type(bdi->chgstate_irq, chgstate_irq_type);
+
+	return 0;
+error:
+	printk(KERN_INFO"chg gpio  error ret = %d\n",ret);
+	return -1;
+}
+
+
+
+static void aw3215_charge_typedet(T_TYPE_USB_DETECT chg_type)
+{
+	//u8 ret;
+	#ifdef DBG_CHARGE
+	//printk(KERN_INFO"charge type is %d in\n",chg_type);
+	#endif
+
+	if(TYPE_ADAPTER == chg_type){
+		printk(KERN_INFO"chg type DC\n");
+		g_bdi->charger.type = POWER_SUPPLY_PCAC__AC;
+	}
+	else{
+		printk(KERN_INFO"chg type PC\n");
+		g_bdi->charger.type = POWER_SUPPLY_PCAC__PC;
+	}
+
+}
+
+
+#if defined(CONFIG_DEBUG_FS)
+static ssize_t debugfs_regs_write(struct file *file, const char __user *buf,size_t nbytes, loff_t *ppos)
+{
+	unsigned int val1, val2;
+	//u8 reg, value;
+	int ret = 0;
+	char *kern_buf;
+	//struct seq_file	*s = file->private_data;
+	//struct aw3215_dev_info *aw3215 = s->private;
+
+	kern_buf = kzalloc(nbytes, GFP_KERNEL);
+
+	if (!kern_buf) {
+		printk(KERN_INFO "aw3215_charger: Failed to allocate buffer\n");
+		return -ENOMEM;
+	}
+
+	if (copy_from_user(kern_buf, (void  __user *)buf, nbytes)) {
+		kfree(kern_buf);
+		return -ENOMEM;
+	}
+	printk(KERN_INFO "%s input str=%s,nbytes=%d \n", __func__, kern_buf,nbytes);
+
+	ret = sscanf(kern_buf, "%x:%x", &val1, &val2);
+	if (ret < 2) {
+		printk(KERN_INFO "sgm40561_charger: failed to read user buf, ret=%d, input 0x%x:0x%x\n",
+				ret, val1, val2);
+		kfree(kern_buf);
+		return -EINVAL;
+	}
+	kfree(kern_buf);
+
+	return ret ? ret : nbytes;
+}
+
+static int debugfs_regs_show(struct seq_file *s, void *v)
+{
+	//int i;
+	int ret=0;
+	//int curr = 0;
+	struct aw3215_dev_info *aw3215 = s->private;
+
+	/*charger type*/
+	if((int)aw3215->charger.type == POWER_SUPPLY_PCAC__PC){
+		seq_printf(s, "charger type is  PC\n");
+	}
+	else if((int)aw3215->charger.type == POWER_SUPPLY_PCAC__AC){
+		seq_printf(s, "charger type is  AC\n");
+	}
+	else
+	seq_printf(s, "charger type is  unknow = %d\n",aw3215->charger.type);
+
+	seq_printf(s, "mmi charger config state = %d\n",aw3215->chg_en);
+	seq_printf(s, "chg in state = %s\n",(aw3215->chgin_type==USB_IN)? "USB_IN":"USB_OUT");
+	seq_printf(s, "chg_state  state = %s\n",aw3215->chgstate_type ? "CHG_STOP": "CHG_START");
+
+	return ret;
+}
+
+#define DEBUGFS_FILE_ENTRY(name) \
+static int debugfs_##name##_open(struct inode *inode, struct file *file) \
+{\
+return single_open(file, debugfs_##name##_show, inode->i_private); \
+}\
+\
+static const struct file_operations debugfs_##name##_fops = { \
+.owner= THIS_MODULE, \
+.open= debugfs_##name##_open, \
+.write=debugfs_##name##_write, \
+.read= seq_read, \
+.llseek= seq_lseek, \
+.release= single_release, \
+}
+
+DEBUGFS_FILE_ENTRY(regs);
+
+static struct dentry *g_charger_root;
+
+static void debugfs_charger_init(struct aw3215_dev_info  *aw3215)
+{
+	struct dentry *root;
+	struct dentry *node;
+	//int i;
+
+	if(!aw3215)
+		return;
+
+	//create root
+	root = debugfs_create_dir("charger_zx29", NULL);
+	if (!root)	{
+		dev_err(aw3215->dev, "debugfs_create_dir err=%ld\n", IS_ERR(root));
+		goto err;
+	}
+
+	//print regs;
+	node = debugfs_create_file("regs", S_IRUGO | S_IWUGO, root, aw3215,  &debugfs_regs_fops);
+	if (!node){
+		dev_err(aw3215->dev, "debugfs_create_dir err=%ld\n", IS_ERR(node));
+		goto err;
+	}
+
+	g_charger_root = (void *)root;
+	return;
+err:
+	dev_err(aw3215->dev, "debugfs_charger_init err\n");
+}
+
+#endif
+
+static void aw3215_changed_timer_function(unsigned long data)
+{
+	struct aw3215_dev_info *bdi = (void *)data;
+	power_supply_changed(&bdi->charger);
+	power_supply_changed(&bdi->battery);
+	//printk("chg timer callback\n");
+	del_timer(&bdi->changed_timer);
+	wake_unlock(&(bdi->wlock_chgfull));
+	printk("chg timer callback end\n");
+
+}
+
+static int __devinit aw3215_charger_probe(struct platform_device *pdev)
+{
+	struct aw3215_platform_data *pdata = pdev->dev.platform_data;
+	struct device *dev = &pdev->dev;
+	struct aw3215_dev_info *bdi;
+	unsigned long flag;
+	int ret;
+	//int i;  /*when err try 3 times*/
+
+	bdi = devm_kzalloc(dev, sizeof(*bdi), GFP_KERNEL);
+	if (!bdi) {
+		dev_err(dev, "Can't alloc bdi struct\n");
+		return -ENOMEM;
+	}
+	bdi->dev = dev;
+	bdi->pdata = pdata;
+
+	//printk(KERN_INFO "charger probe.\n");
+
+	bdi->chg_state = POWER_SUPPLY_STATUS_UNKNOWN;
+	bdi->charger.type = POWER_SUPPLY_TYPE_UNKNOWN;
+
+	g_bdi = bdi;
+
+	ret = aw3215_setup_pdata(bdi, pdata);
+	if (ret) {
+		dev_err(dev, "Can't get irq info\n");
+		return -EINVAL;
+	}
+
+	aw3215_charger_init(&bdi->charger);
+
+	ret = power_supply_register(dev, &bdi->charger);
+	if (ret) {
+		dev_err(dev, "Can't register charger\n");
+		goto out2;
+	}
+	//printk(KERN_INFO "aw3215_probe power_supply_register charger ok.\n");
+
+	aw3215_battery_init(&bdi->battery);
+
+	ret = power_supply_register(dev, &bdi->battery);
+	if (ret) {
+		dev_err(dev, "Can't register battery\n");
+		goto out1;
+	}
+	//printk(KERN_INFO "aw3215_probe power_supply_register battery ok.\n");
+	sema_init(&bdi->chgin_sem, 0);
+	sema_init(&bdi->chgstate_sem, 0);
+
+	dwc_chg_Regcallback(aw3215_charge_typedet);/*register for usb*/
+	aw3215_init_state(bdi);
+	init_timer(&bdi->changed_timer);
+	bdi->changed_timer.function = aw3215_changed_timer_function;
+	bdi->changed_timer.data = (unsigned long)bdi;
+	wake_lock_init(&(bdi->wlock_chgfull), WAKE_LOCK_SUSPEND, "aw3215_wake_lock_chgfull");
+
+/*chg in*/
+	ret = request_irq(bdi->chgin_irq, aw3215_charger_in_irq_primary_handler,IRQF_NO_THREAD, "aw3215-chgin", bdi);
+	if (ret < 0) {
+		dev_err(dev, "Can't set up irq handler\n");
+		if (bdi->pdata->gpio_chgin)
+			gpio_free(bdi->pdata->gpio_chgin);
+
+		goto out3;
+	}
+	irq_set_irq_wake(bdi->chgin_irq, 1);
+	bdi->chgin_irq_thread = kthread_run(aw3215_chgin_irq_handler_thread, bdi, "aw3215-chgin");
+	BUG_ON(IS_ERR(bdi->chgin_irq_thread));
+
+/*chg state*/
+	ret = request_irq(bdi->chgstate_irq, aw3215_charger_state_irq_primary_handler,IRQF_NO_THREAD, "aw3215-chgstate", bdi);
+	if (ret < 0) {
+		dev_err(dev, "Can't set up irq handler\n");
+		if (bdi->pdata->gpio_chgstate)
+			gpio_free(bdi->pdata->gpio_chgstate);
+
+		goto out3;
+	}
+	irq_set_irq_wake(bdi->chgstate_irq, 1);
+	bdi->chgstate_irq_thread = kthread_run(aw3215_chgstate_irq_handler_thread, bdi, "aw3215-chgstate");
+	BUG_ON(IS_ERR(bdi->chgstate_irq_thread));
+
+#if defined(CONFIG_DEBUG_FS)
+	debugfs_charger_init(bdi);
+#endif
+
+#ifdef DBG_CHARGE
+	//printk(KERN_INFO "aw3215_probe end.\n");
+#endif
+
+	return 0;
+
+out1:
+	power_supply_unregister(&bdi->battery);
+out2:
+	//pm_runtime_disable(dev);
+	power_supply_unregister(&bdi->charger);
+out3:
+
+	return ret;
+
+}
+
+static int aw3215_charger_remove(struct platform_device *pdev)
+{
+	struct aw3215_platform_data *pdata = pdev->dev.platform_data;
+
+	power_supply_unregister(&(g_bdi->battery));
+	power_supply_unregister(&(g_bdi->charger));
+
+	pm_runtime_disable(g_bdi->dev);
+
+	if (pdata->gpio_chgctrl)
+		gpio_free(pdata->gpio_chgctrl);
+	if (pdata->gpio_chgen)
+		gpio_free(pdata->gpio_chgen);
+	if (pdata->gpio_chgin)
+		gpio_free(pdata->gpio_chgin);
+	if (pdata->gpio_chgstate)
+		gpio_free(pdata->gpio_chgstate);
+
+
+#if defined(CONFIG_DEBUG_FS)
+	if(g_charger_root){
+		//printk(KERN_INFO "aw3215_device_exit:debugfs_remove_recursive \n");
+		debugfs_remove_recursive(g_charger_root);
+	}
+#endif
+
+	return 0;
+}
+
+
+static struct platform_driver zx29_charger_driver = {
+	.probe		= aw3215_charger_probe,
+	.remove		= __devexit_p(aw3215_charger_remove),
+	.driver		= {
+		.name	= "aw3215-charger",
+		.owner	= THIS_MODULE,
+	},
+};
+
+//module_platform_driver(zx29_charger_driver);
+
+
+static int __init zx29_charger_init(void)
+{
+	return platform_driver_register(&zx29_charger_driver);
+}
+
+static void __exit zx29_charger_exit(void)
+{
+	platform_driver_unregister(&zx29_charger_driver);
+}
+
+module_init(zx29_charger_init);
+module_exit(zx29_charger_exit);
+
+
+MODULE_AUTHOR("Mark A. Greer <mgreer@animalcreek.com>");
+MODULE_DESCRIPTION("AW3215 Charger Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:zx29_charger");
+