static irqreturn_t pm860x_vchg_handler(int irq, void *data)
{
	struct pm860x_charger_info *info = data;
	int vchg = 0;

	if (info->present)
		goto out;

	measure_vchg(info, &vchg);

	mutex_lock(&info->lock);
	if (!info->online) {
		int status;
		/* check if over-temp on pm8606 or not */
		status = pm860x_reg_read(info->i2c_8606, PM8606_FLAGS);
		if (status & OVER_TEMP_FLAG) {
			/* clear over temp flag and set auto recover */
			pm860x_set_bits(info->i2c_8606, PM8606_FLAGS,
					OVER_TEMP_FLAG, OVER_TEMP_FLAG);
			pm860x_set_bits(info->i2c_8606,
					PM8606_VSYS,
					OVTEMP_AUTORECOVER,
					OVTEMP_AUTORECOVER);
			dev_dbg(info->dev,
				"%s, pm8606 over-temp occure\n", __func__);
		}
	}

	if (vchg > VCHG_NORMAL_CHECK) {
		set_vchg_threshold(info, VCHG_OVP_LOW, 0);
		info->allowed = 0;
		dev_dbg(info->dev,
			"%s,pm8607 over-vchg occure,vchg = %dmv\n",
			__func__, vchg);
	} else if (vchg < VCHG_OVP_LOW) {
		set_vchg_threshold(info, VCHG_NORMAL_LOW,
				   VCHG_NORMAL_HIGH);
		info->allowed = 1;
		dev_dbg(info->dev,
			"%s,pm8607 over-vchg recover,vchg = %dmv\n",
			__func__, vchg);
	}
	mutex_unlock(&info->lock);

	dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed);
	set_charging_fsm(info);
out:
	return IRQ_HANDLED;
}
void pm860x_set_vchg_threshold(int min, int max)
{
	struct pm860x_charger_info *info = ginfo;
	set_vchg_threshold(info, min, max);
	pr_info( \
	"[%s][%s]min[%d]max[%d]\n", 
	__FILE__, __FUNCTION__, min, max);
	
}EXPORT_SYMBOL(pm860x_set_vchg_threshold);
static int start_fastcharge(struct pm860x_charger_info *info)
{
	int ret;

	dev_dbg(info->dev, "Start Fast-charging!\n");

	/* set fastcharge termination current & voltage, disable charging */
	ret = pm860x_reg_write(info->i2c, PM8607_CHG_CTRL1,
			       CC1_MODE_OFF | CC1_ITERM_60MA |
			       CC1_VFCHG_4_2V);
	if (ret < 0)
		goto out;
	ret = pm860x_reg_write(info->i2c_8606, PM8606_PREREGULATORA,
			       PREREG1_540MA | PREREG1_VSYS_4_5V);
	if (ret < 0)
		goto out;
	ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL2, 0x1f,
			      CC2_ICHG_500MA);
	if (ret < 0)
		goto out;
	/* set 270 minutes timeout */
	ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL3, (0xf << 4),
			      CC3_270MIN_TIMEOUT);
	if (ret < 0)
		goto out;
	/* set IBAT & TBAT monitor */
	ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL4,
			      CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN,
			      CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN);
	if (ret < 0)
		goto out;
	ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL6,
			      CC6_BAT_OV_EN | CC6_BAT_UV_EN |
			      CC6_UV_VBAT_SET,
			      CC6_BAT_OV_EN | CC6_BAT_UV_EN |
			      CC6_UV_VBAT_SET);
	if (ret < 0)
		goto out;
	ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL7,
			      CC7_BAT_REM_EN | CC7_IFSM_EN,
			      CC7_BAT_REM_EN | CC7_IFSM_EN);
	if (ret < 0)
		goto out;
	/* launch fast-charge */
	ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3,
			      CC1_MODE_FASTCHARGE);
	/* vchg threshold setting */
	set_vchg_threshold(info, VCHG_NORMAL_LOW, VCHG_NORMAL_HIGH);
out:
	return ret;
}
static int pm860x_charger_probe(struct platform_device *pdev)
{
	struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
	struct pm860x_charger_info *info;
	int ret;
	int count;
	int i;
	int j;

	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
	if (!info)
		return -ENOMEM;

	count = pdev->num_resources;
	for (i = 0, j = 0; i < count; i++) {
		info->irq[j] = platform_get_irq(pdev, i);
		if (info->irq[j] < 0)
			continue;
		j++;
	}
	info->irq_nums = j;

	info->chip = chip;
	info->i2c =
	    (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
	info->i2c_8606 =
	    (chip->id == CHIP_PM8607) ? chip->companion : chip->client;
	if (!info->i2c_8606) {
		dev_err(&pdev->dev, "Missed I2C address of 88PM8606!\n");
		ret = -EINVAL;
		goto out;
	}
	info->dev = &pdev->dev;

	/* set init value for the case we are not using battery */
	set_vchg_threshold(info, VCHG_NORMAL_LOW, VCHG_OVP_LOW);

	mutex_init(&info->lock);
	platform_set_drvdata(pdev, info);

	info->usb.name = "usb";
	info->usb.type = POWER_SUPPLY_TYPE_USB;
	info->usb.supplied_to = pm860x_supplied_to;
	info->usb.num_supplicants = ARRAY_SIZE(pm860x_supplied_to);
	info->usb.properties = pm860x_usb_props;
	info->usb.num_properties = ARRAY_SIZE(pm860x_usb_props);
	info->usb.get_property = pm860x_usb_get_prop;
	ret = power_supply_register(&pdev->dev, &info->usb);
	if (ret)
		goto out;

	pm860x_init_charger(info);

	for (i = 0; i < ARRAY_SIZE(info->irq); i++) {
		ret = request_threaded_irq(info->irq[i], NULL,
			pm860x_irq_descs[i].handler,
			IRQF_ONESHOT, pm860x_irq_descs[i].name, info);
		if (ret < 0) {
			dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
				info->irq[i], ret);
			goto out_irq;
		}
	}
	return 0;

out_irq:
	while (--i >= 0)
		free_irq(info->irq[i], info);
out:
	return ret;
}
static int start_fastcharge(struct pm860x_charger_info *info)
{
	unsigned char buf[6];
	int ret;

	dev_dbg(info->dev, "Start Fast-charging!\n");
	pr_info( \
	"charger:start_fastcharge\n");
	set_vbatt_threshold(info, 0, 0);
	set_vchg_threshold(info, VCHG_NORMAL_LOW, VCHG_NORMAL_HIGH);
	pm860x_calc_resistor();

	ret = pm860x_reg_write(info->i2c_8606, PM8606_PREREGULATORA,
			       PREREG1_1500MA | PREREG1_VSYS_4_5V);
	if (ret < 0)
		goto out;
	/* set fastcharge termination current & voltage, disable charging */
	ret = pm860x_reg_write(info->i2c, PM8607_CHG_CTRL1,
			       CC1_MODE_OFF | CC1_ITERM_60MA | CC1_VFCHG_4_2V);
	if (ret < 0)
		goto out;
	switch (info->charge_type) {
	case USB_CHARGER:
	pr_info("[%s][%s] charger:start_fastcharge:USB_CHARGER\n",
		__FILE__, __func__);
		break;
	case AC_STANDARD_CHARGER:
		ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL2, 0x1f,
				      CC2_ICHG_500MA);
	pr_info( \
	"charger:start_fastcharge:AC_STANDARD_CHARGER\n");
		break;
	case AC_OTHER_CHARGER:
		ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL2, 0x1f,
				      CC2_ICHG_1000MA);
	pr_info( \
	"charger:start_fastcharge:AC_OTHER_CHARGER\n");
		break;
	default:
		ret = -EINVAL;
		pr_info( \
		"charger:start_fastcharge:type none\n");
		break;
	}
	if (ret < 0)
		goto out;
	/* set 270 minutes timeout */
	ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL3, (0xf << 4),
			      CC3_270MIN_TIMEOUT);

	if (ret < 0)
		goto out;
	/* set IBAT & TBAT monitor */
	ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL4,
			      CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN,
			      CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN);
	if (ret < 0)
		goto out;
	ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL6,
			      CC6_BAT_OV_EN | CC6_BAT_UV_EN | CC6_UV_VBAT_SET,
			      CC6_BAT_OV_EN | CC6_BAT_UV_EN | CC6_UV_VBAT_SET);

	if (ret < 0)
		goto out;
	ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL7,
			      CC7_BAT_REM_EN | CC7_IFSM_EN,
			      CC7_BAT_REM_EN | CC7_IFSM_EN);
	if (ret < 0)
		goto out;
	/*hw fix workaround: disable BC_SHORT by setting in testpage,
	only occur before sanremo C1*/
	if((info->chip->chip_version <= PM8607_CHIP_C1) && !info->bc_short){
		info->bc_short = 1;/* disable bc_short mechanism*/
		buf[0] = buf[2] = 0x0;
		buf[1] = 0x60;
		buf[3] = 0xff;
		buf[4] = 0x9f;
		buf[5] = 0xfd;
		pm860x_page_bulk_write(info->i2c, 0xC8, 6, buf);
		pm860x_page_reg_write(info->i2c, 0xCF, 0x02);
	}
	/* trigger fastcharge */
	ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3,
			      CC1_MODE_FASTCHARGE);

out:
	return ret;
}
static __devinit int pm860x_charger_probe(struct platform_device *pdev)
{
	struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
	struct pm860x_platform_data *pdata = chip->dev->platform_data;
	struct pm860x_charger_info *info;
	int ret, i, j, count;

	info = kzalloc(sizeof(struct pm860x_charger_info), GFP_KERNEL);
	if (!info)
		return -ENOMEM;

	ret = device_create_file(&pdev->dev, &dev_attr_control);
	if (ret < 0)
		goto out;
	count = pdev->num_resources;
	for (i = 0, j = 0; i < count; i++) {
		info->irq[j] = platform_get_irq(pdev, i);
		if (info->irq[j] < 0)
			continue;
		j++;
	}
	ginfo = info;
	info->irq_nums = j;

	info->batdet = pdata->batt_det;

	pr_info( \
	"charger:pm860x_charger_probe:info->batdet=[%d]\n",
	info->batdet);

	info->chip = chip;
	info->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
	info->i2c_8606 = (chip->id == CHIP_PM8607) ? chip->companion
			: chip->client;
	if (!info->i2c_8606) {
		dev_err(&pdev->dev, "Missed I2C address of 88PM8606!\n");
		ret = -EINVAL;
		goto out_dev;
	}
	info->dev = &pdev->dev;
	
#ifdef CONFIG_ALTERNATE_CHARGER
	pm860x_registerChargerEventsCb(pm860x_default_chg_handler,0);
	pm860x_registerChargerEventsCb(pm860x_default_chg_handler,1);
	pm860x_registerChargerEventsCb(pm860x_default_chg_handler,2);
	pm860x_registerChargerEventsCb(pm860x_default_chg_handler,3);
	pm860x_registerChargerEventsCb(pm860x_default_chg_handler,4);
	//pm860x_registerChargerEventsCb(pm860x_default_chg_handler,5);
	pm860x_registerChargerEventsCb(m_spa_voltThresholdEvent,5);
	pm860x_registerChargerEventsCb(pm860x_default_chg_handler,6);
	pm860x_register_control_cb_func(spa_Charger_Ctrl);
#else
	ChargerEventsCb[0] = pm860x_charger_handler;
	ChargerEventsCb[1] = pm860x_done_handler;
	ChargerEventsCb[2] = pm860x_exception_handler;
	ChargerEventsCb[3] = pm860x_exception_handler;
	ChargerEventsCb[4] = pm860x_temp_handler;
	ChargerEventsCb[5] = pm860x_vbattery_handler;
	ChargerEventsCb[6] = pm860x_vchg_handler;
#endif
	/* set init value for the case we are not using battery*/
	set_vchg_threshold(info, VCHG_NORMAL_LOW, VCHG_OVP_LOW);

#if !defined(CONFIG_USB_VBUS_88PM860X)
	if (ChargerEventsCb[0]) {
		ret = request_threaded_irq(info->irq[0], NULL,
					   ChargerEventsCb[0],
					   IRQF_ONESHOT, "usb supply detect", info);
		if (ret < 0) {
			dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
				info->irq[0], ret);
			goto out_dev;
		}
	} else
		pr_info( \
		"pm860x_charger_probe:no callback for charger event[%d]\n",
		0);
#endif
	if (ChargerEventsCb[1]) {
		ret = request_threaded_irq(info->irq[1], NULL,
					   ChargerEventsCb[1],
					   IRQF_ONESHOT, "charge done", info);
		if (ret < 0) {
			dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
				info->irq[1], ret);
			goto out_irq1;
			}
	} else
		pr_info( \
		"pm860x_charger_probe:no callback for charger event[%d]\n",
		1);
		
	if (ChargerEventsCb[2]) {
		ret = request_threaded_irq(info->irq[2], NULL,
					   ChargerEventsCb[2],
					   IRQF_ONESHOT, "charge timeout", info);
		if (ret < 0) {
			dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
				info->irq[2], ret);
			goto out_irq2;
		}
	} else
		pr_info( \
		"pm860x_charger_probe:no callback for charger event[%d]\n",
		2);
		
	if (ChargerEventsCb[3]) {
		ret = request_threaded_irq(info->irq[3], NULL,
					   ChargerEventsCb[3],
					   IRQF_ONESHOT, "charge fault", info);
		if (ret < 0) {
			dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
				info->irq[3], ret);
			goto out_irq3;
		}
	} else
		pr_info( \
		"pm860x_charger_probe:no callback for charger event[%d]\n",
		3);

	if (ChargerEventsCb[4]) {	
		ret = request_threaded_irq(info->irq[4], NULL,
					   ChargerEventsCb[4],
					   IRQF_ONESHOT, "temperature", info);
		if (ret < 0) {
			dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
				info->irq[4], ret);
			goto out_irq4;
		}
	} else
		pr_info( \
		"pm860x_charger_probe:no callback for charger event[%d]\n",
		4);	
		
	if (ChargerEventsCb[5]) {		
		ret = request_threaded_irq(info->irq[5], NULL,
					   ChargerEventsCb[5],
					   IRQF_ONESHOT, "vbatt", info);
		if (ret < 0) {
			dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
				info->irq[5], ret);
			goto out_irq5;
		}
	} else
		pr_info( \
		"pm860x_charger_probe:no callback for charger event[%d]\n",
		5);
		
	if (ChargerEventsCb[6]) {
		ret = request_threaded_irq(info->irq[6], NULL,
					   ChargerEventsCb[6],
					   IRQF_ONESHOT, "vchg", info);
		if (ret < 0) {
			dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
				info->irq[6], ret);
			goto out_irq6;
		}
		if (info->irq_nums <= 6) {
			dev_err(chip->dev, "IRQ numbers aren't matched\n");
			goto out_nums;
		}
	} else
		pr_info( \
		"pm860x_charger_probe:no callback for charger event[%d]\n",
		6);		

	mutex_init(&info->lock);
	platform_set_drvdata(pdev, info);

#ifndef CONFIG_ALTERNATE_CHARGER
	info->usb.name = "usb";
	info->usb.type = POWER_SUPPLY_TYPE_USB;
	info->usb.supplied_to = pm860x_supplied_to;
	info->usb.num_supplicants = ARRAY_SIZE(pm860x_supplied_to);
	info->usb.properties = pm860x_usb_props;
	info->usb.num_properties = ARRAY_SIZE(pm860x_usb_props);
	info->usb.get_property = pm860x_usb_get_prop;
	ret = power_supply_register(&pdev->dev, &info->usb);
	if (ret)
		goto out_nums;

	info->ac.name = "ac";
	info->ac.type = POWER_SUPPLY_TYPE_MAINS;
	info->ac.supplied_to = pm860x_supplied_to;
	info->ac.num_supplicants = ARRAY_SIZE(pm860x_supplied_to);
	info->ac.properties = pm860x_ac_props;
	info->ac.num_properties = ARRAY_SIZE(pm860x_ac_props);
	info->ac.get_property = pm860x_ac_get_prop;
	ret = power_supply_register(&pdev->dev, &info->ac);
	if (ret)
		goto out_nums;

	pm860x_init_charger(info);
#endif
	INIT_WORK(&info->vbus_work, pm860x_vbus_work);

	device_init_wakeup(&pdev->dev, 1);
#ifdef	CONFIG_PROC_FS
	create_pm860x_power_proc_file();
#endif
	return 0;

out_nums:
	free_irq(info->irq[6], info);
out_irq6:
	free_irq(info->irq[5], info);
out_irq5:
	free_irq(info->irq[4], info);
out_irq4:
	free_irq(info->irq[3], info);
out_irq3:
	free_irq(info->irq[2], info);
out_irq2:
	free_irq(info->irq[1], info);
out_irq1:
#if !defined(CONFIG_USB_VBUS_88PM860X)
	free_irq(info->irq[0], info);
#endif
out_dev:
	device_remove_file(&pdev->dev, &dev_attr_control);
out:
	kfree(info);
	return ret;
}