static int axp_battery_event(struct notifier_block *nb, unsigned long event,
				void *data)
{
	struct axp_charger *charger =
		container_of(nb, struct axp_charger, nb);

	axp_charger_update_state(charger);

	switch (event) {
	case AXP19_IRQ_BATIN:
	case AXP19_IRQ_ACIN:
	case AXP19_IRQ_USBIN:
		axp_set_bits(charger->master,AXP19_CHARGE_CONTROL1,0x80);
		break;
	case AXP19_IRQ_BATRE:
	case AXP19_IRQ_ACOV:
	case AXP19_IRQ_ACRE:
	case AXP19_IRQ_USBOV:
	case AXP19_IRQ_USBRE:
    case AXP19_IRQ_TEMOV:
	case AXP19_IRQ_TEMLO:
		axp_clr_bits(charger->master,AXP19_CHARGE_CONTROL1,0x80);
		break;
    default:
		break;
	}

	return 0;
}
static int axp81x_resume(struct platform_device *dev)
{
	struct axp_charger *charger = platform_get_drvdata(dev);
	int pre_rest_vol;
	uint8_t val,tmp;

	axp_enable_irq(charger);
	pre_rest_vol = charger->rest_vol;
	axp_read(charger->master, AXP81X_CAP,&val);
	charger->rest_vol = val & 0x7f;
	if(charger->rest_vol - pre_rest_vol){
		printk("battery vol change: %d->%d \n", pre_rest_vol, charger->rest_vol);
		pre_rest_vol = charger->rest_vol;
		axp_write(charger->master,AXP81X_DATA_BUFFER1,charger->rest_vol | 0x80);
	}
#if defined (CONFIG_AXP_CHGCHANGE)
	if(axp81x_config.pmu_runtime_chgcur == 0)
		axp_clr_bits(charger->master,AXP81X_CHARGE_CONTROL1,0x80);
	else
		axp_set_bits(charger->master,AXP81X_CHARGE_CONTROL1,0x80);
	printk("pmu_runtime_chgcur = %d\n", axp81x_config.pmu_runtime_chgcur);
	if(axp81x_config.pmu_runtime_chgcur >= 300000 && axp81x_config.pmu_runtime_chgcur <= 2550000){
		tmp = (axp81x_config.pmu_runtime_chgcur -200001)/150000;
		charger->chgcur = tmp *150000 + 300000;
		axp_update(charger->master, AXP81X_CHARGE_CONTROL1, tmp,0x0F);
	}else if(axp81x_config.pmu_runtime_chgcur < 300000){
		axp_clr_bits(axp_charger->master, AXP81X_CHARGE_CONTROL1,0x0F);
	}else{
		axp_set_bits(axp_charger->master, AXP81X_CHARGE_CONTROL1,0x0F);
	}
#endif
	charger->disvbat = 0;
	charger->disibat = 0;
	axp_charger_update_state(charger);
	axp_charger_update(charger, &axp81x_config);
	power_supply_changed(&charger->batt);
	power_supply_changed(&charger->ac);
	power_supply_changed(&charger->usb);
	schedule_delayed_work(&charger->work, charger->interval);
	return 0;
}
static void axp_charging_monitor(struct work_struct *work)
{
	struct axp_charger *charger;
	uint8_t	val,temp_val[4];
	int	pre_rest_vol,pre_bat_curr_dir;
	unsigned long power_sply = 0;

	charger = container_of(work, struct axp_charger, work.work);
	pre_rest_vol = charger->rest_vol;
	pre_bat_curr_dir = charger->bat_current_direction;
	axp_charger_update_state(charger);
	axp_charger_update(charger, &axp81x_config);
	axp_read(charger->master, AXP81X_CAP,&val);

	spin_lock(&charger->charger_lock);
	charger->rest_vol	= (int)	(val & 0x7F);
	spin_unlock(&charger->charger_lock);

	if (axp_debug & DEBUG_SPLY) {
		DBG_PSY_MSG(DEBUG_SPLY, "charger->ic_temp = %d\n",charger->ic_temp);
		DBG_PSY_MSG(DEBUG_SPLY, "charger->bat_temp = %d\n",charger->bat_temp);
		DBG_PSY_MSG(DEBUG_SPLY, "charger->vbat = %d\n",charger->vbat);
		DBG_PSY_MSG(DEBUG_SPLY, "charger->ibat = %d\n",charger->ibat);
		DBG_PSY_MSG(DEBUG_SPLY, "charger->ocv = %d\n",charger->ocv);
		DBG_PSY_MSG(DEBUG_SPLY, "charger->disvbat = %d\n",charger->disvbat);
		DBG_PSY_MSG(DEBUG_SPLY, "charger->disibat = %d\n",charger->disibat);
		power_sply = charger->disvbat * charger->disibat;
		if (0 != power_sply)
			power_sply = power_sply/1000;
		DBG_PSY_MSG(DEBUG_SPLY, "power_sply = %ld mW\n",power_sply);
		DBG_PSY_MSG(DEBUG_SPLY, "charger->rest_vol = %d\n",charger->rest_vol);
		axp_reads(charger->master,0xba,2,temp_val);
		DBG_PSY_MSG(DEBUG_SPLY, "Axp Rdc = %d\n",(((temp_val[0] & 0x1f) <<8) + temp_val[1])*10742/10000);
		axp_reads(charger->master,0xe0,2,temp_val);
		DBG_PSY_MSG(DEBUG_SPLY, "Axp batt_max_cap = %d\n",(((temp_val[0] & 0x7f) <<8) + temp_val[1])*1456/1000);
		axp_reads(charger->master,0xe2,2,temp_val);
		DBG_PSY_MSG(DEBUG_SPLY, "Axp coulumb_counter = %d\n",(((temp_val[0] & 0x7f) <<8) + temp_val[1])*1456/1000);
		axp_read(charger->master,0xb8,temp_val);
		DBG_PSY_MSG(DEBUG_SPLY, "Axp REG_B8 = %x\n",temp_val[0]);
		axp_reads(charger->master,0xe4,2,temp_val);
		DBG_PSY_MSG(DEBUG_SPLY, "Axp OCV_percentage = %d\n",(temp_val[0] & 0x7f));
		DBG_PSY_MSG(DEBUG_SPLY, "Axp Coulumb_percentage = %d\n",(temp_val[1] & 0x7f));
		DBG_PSY_MSG(DEBUG_SPLY, "charger->is_on = %d\n",charger->is_on);
		DBG_PSY_MSG(DEBUG_SPLY, "charger->bat_current_direction = %d\n",charger->bat_current_direction);
		DBG_PSY_MSG(DEBUG_SPLY, "charger->charge_on = %d\n",charger->charge_on);
		DBG_PSY_MSG(DEBUG_SPLY, "charger->ext_valid = %d\n",charger->ext_valid);
		DBG_PSY_MSG(DEBUG_SPLY, "pmu_runtime_chgcur           = %d\n",axp81x_config.pmu_runtime_chgcur);
		DBG_PSY_MSG(DEBUG_SPLY, "pmu_earlysuspend_chgcur   = %d\n",axp81x_config.pmu_earlysuspend_chgcur);
		DBG_PSY_MSG(DEBUG_SPLY, "pmu_suspend_chgcur        = %d\n",axp81x_config.pmu_suspend_chgcur);
		DBG_PSY_MSG(DEBUG_SPLY, "pmu_shutdown_chgcur       = %d\n\n\n",axp81x_config.pmu_shutdown_chgcur);
	}

	/* if battery volume changed, inform uevent */
	if((charger->rest_vol - pre_rest_vol) || (charger->bat_current_direction != pre_bat_curr_dir)){	
		axp_reads(charger->master,0xe2,2,temp_val);
		axp_reads(charger->master,0xe4,2,(temp_val+2));
		DBG_PSY_MSG(DEBUG_SPLY, "battery vol change: %d->%d \n", pre_rest_vol, charger->rest_vol);
		DBG_PSY_MSG(DEBUG_SPLY, "for test %d %d %d %d %d %d\n",charger->vbat,charger->ocv,charger->ibat,
			(temp_val[2] & 0x7f),(temp_val[3] & 0x7f),(((temp_val[0] & 0x7f) <<8) + temp_val[1])*1456/1000);
		pre_rest_vol = charger->rest_vol;
		power_supply_changed(&charger->batt);
	}
	/* reschedule for the next time */
	schedule_delayed_work(&charger->work, charger->interval);
}
static int axp_battery_probe(struct platform_device *pdev)
{
	struct axp_charger *charger;
	struct axp_supply_init_data *pdata = pdev->dev.platform_data;
	int ret;
	uint8_t val;

	if (pdata == NULL)
		return -EINVAL;
	if (pdata->chgcur > 2550000 ||
	pdata->chgvol < 4100000 ||
	pdata->chgvol > 4350000){
		printk("charger milliamp is too high or target voltage is over range\n");
		return -EINVAL;
	}
	if (pdata->chgpretime < 40 || pdata->chgpretime >70 ||
	pdata->chgcsttime < 360 || pdata->chgcsttime > 720){
		printk("prechaging time or constant current charging time is over range\n");
		return -EINVAL;
	}
	charger = kzalloc(sizeof(*charger), GFP_KERNEL);
	if (charger == NULL)
		return -ENOMEM;

	spin_lock_init(&charger->charger_lock);

	spin_lock(&charger->charger_lock);
	charger->master 	= pdev->dev.parent;
	charger->chgcur		= pdata->chgcur;
	charger->chgvol     	= pdata->chgvol;
	charger->chgend       	= pdata->chgend;
	charger->sample_time  	= pdata->sample_time;
	charger->chgen        	= pdata->chgen;
	charger->chgpretime   	= pdata->chgpretime;
	charger->chgcsttime 	= pdata->chgcsttime;
	charger->battery_info 	= pdata->battery_info;
	charger->disvbat	= 0;
	charger->disibat	= 0;
	spin_unlock(&charger->charger_lock);

	ret = axp81x_init(charger);
	if (ret) {
		goto err_ps_register;
	}

	axp_battery_setup_psy(charger);
	ret = power_supply_register(&pdev->dev, &charger->batt);
	if (ret)
		goto err_ps_register;

	ret = power_supply_register(&pdev->dev, &charger->ac);
	if (ret){
		power_supply_unregister(&charger->batt);
		goto err_ps_register;
	}

	ret = power_supply_register(&pdev->dev, &charger->usb);
	if (ret){
		power_supply_unregister(&charger->ac);
		power_supply_unregister(&charger->batt);
		goto err_ps_register;
	}
	ret = axp_charger_create_attrs(&charger->batt);
	if(ret){
		printk("cat notaxp_charger_create_attrs!!!===\n ");
		return ret;
	}
	platform_set_drvdata(pdev, charger);

	axp_charger_update_state((struct axp_charger *)charger);
	axp_read(charger->master, AXP81X_CAP,&val);
	spin_lock(&charger->charger_lock);
	charger->rest_vol = (int) (val & 0x7F);
	spin_unlock(&charger->charger_lock);
	printk("now_rest_vol = %d\n",(val & 0x7F));

	spin_lock(&charger->charger_lock);
	charger->interval = msecs_to_jiffies(10 * 1000);
	spin_unlock(&charger->charger_lock);
	INIT_DELAYED_WORK(&charger->work, axp_charging_monitor);
	schedule_delayed_work(&charger->work, charger->interval);

	ret = axp_irq_init(charger, pdev);
	if(ret){
		printk("cat notaxp_charger_create_attrs!!!===\n ");
		return ret;
	}

	axp_charger = charger;
#ifdef CONFIG_HAS_EARLYSUSPEND
	axp_early_suspend.suspend = axp_earlysuspend;
	axp_early_suspend.resume = axp_lateresume;
	axp_early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 2;
	register_early_suspend(&axp_early_suspend);
#endif

	class_register(&axppower_class);

	return ret;
err_ps_register:
	axp81x_exit(charger);
	cancel_delayed_work_sync(&charger->work);
	kfree(charger);
	return ret;
}
static int axp_battery_get_property(struct power_supply *psy,
           enum power_supply_property psp,
           union power_supply_propval *val)
{
	struct axp_charger *charger;
	int ret = 0;

	charger = container_of(psy, struct axp_charger, batt);

	switch (psp) {
	case POWER_SUPPLY_PROP_STATUS:
		axp_battery_check_status(charger, val);
		break;
	case POWER_SUPPLY_PROP_HEALTH:
		axp_battery_check_health(charger, val);
		break;
	case POWER_SUPPLY_PROP_TECHNOLOGY:
		val->intval = charger->battery_info->technology;
		break;
	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
		val->intval = charger->battery_info->voltage_max_design;
		break;
	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
		val->intval = charger->battery_info->voltage_min_design;
		break;
	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
		//val->intval = charger->ocv * 1000;
		val->intval = charger->vbat * 1000;
		break;
	case POWER_SUPPLY_PROP_CURRENT_NOW:
		val->intval = charger->ibat * 1000;
		break;
	case POWER_SUPPLY_PROP_MODEL_NAME:
		val->strval = charger->batt.name;
	break;
	case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
		val->intval = charger->battery_info->energy_full_design;
		//  DBG_PSY_MSG("POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:%d\n",val->intval);
		break;
	case POWER_SUPPLY_PROP_CAPACITY:
		val->intval = charger->rest_vol;
		break;
	case POWER_SUPPLY_PROP_ONLINE:
	{
	/* in order to get hardware state, we must update charger state now.
	 * by sunny at 2012-12-23 11:06:15.
	 */
		axp_charger_update_state(charger);
		val->intval = charger->bat_current_direction;
		if (charger->bat_temp > 50 || -5 < charger->bat_temp)
			val->intval = 0;
		break;
	}
	case POWER_SUPPLY_PROP_PRESENT:
		val->intval = charger->bat_det;
		break;
	case POWER_SUPPLY_PROP_TEMP:
		val->intval =  charger->bat_temp * 10;
		break;
	default:
		ret = -EINVAL;
		break;
	}

	return ret;
}