コード例 #1
0
static int p3_get_bat_level(struct power_supply *bat_ps)
{
	struct battery_data *battery = container_of(bat_ps,
				struct battery_data, psy_battery);
	int fg_soc;
	int fg_vfsoc;
	int fg_vcell;
	int fg_current;
	int avg_current;
	int recover_flag = 0;

	recover_flag = fg_check_cap_corruption();

	/* check VFcapacity every five minutes */
	if (!(battery->fg_chk_cnt++ % 10)) {
		fg_check_vf_fullcap_range();
		battery->fg_chk_cnt = 1;
	}

	fg_soc = get_fuelgauge_value(FG_LEVEL);
	if (fg_soc < 0) {
		pr_info("Can't read soc!!!");
		fg_soc = battery->info.level;
	}

	if (!check_jig_on() && !max17042_chip_data->info.low_batt_comp_flag) {
		if (((fg_soc+5) < max17042_chip_data->info.previous_repsoc) ||
			(fg_soc > (max17042_chip_data->info.previous_repsoc+5)))
			battery->fg_skip = 1;
	}

	/* skip one time (maximum 30 seconds) because of corruption. */
	if (battery->fg_skip) {
		pr_info("%s: skip update until corruption check "
			"is done (fg_skip_cnt:%d)\n",
			__func__, ++battery->fg_skip_cnt);
		fg_soc = battery->info.level;
		if (recover_flag || battery->fg_skip_cnt > 10) {
			battery->fg_skip = 0;
			battery->fg_skip_cnt = 0;
		}
	}

	if (battery->low_batt_boot_flag) {
		fg_soc = 0;

		if (check_ta_conn(battery) && !check_UV_charging_case()) {
			fg_adjust_capacity();
			battery->low_batt_boot_flag = 0;
		}

		if (!check_ta_conn(battery))
			battery->low_batt_boot_flag = 0;
	}

	fg_vcell = get_fuelgauge_value(FG_VOLTAGE);
	if (fg_vcell < 0) {
		pr_info("Can't read vcell!!!");
		fg_vcell = battery->info.batt_vol;
	} else
		battery->info.batt_vol = fg_vcell;

	fg_current = get_fuelgauge_value(FG_CURRENT);
	avg_current = get_fuelgauge_value(FG_CURRENT_AVG);
	fg_vfsoc = get_fuelgauge_value(FG_VF_SOC);

	// Algorithm for reducing time to fully charged (from MAXIM)
	if(battery->info.charging_enabled &&  // Charging is enabled
		!battery->info.batt_is_recharging &&  // Not Recharging
		battery->info.charging_source == CHARGER_AC &&  // Only AC (Not USB cable)
		!battery->is_first_check &&  // Skip when first check after boot up
		(fg_vfsoc>70 && (fg_current>20 && fg_current<250) &&
		(avg_current>20 && avg_current<260))) {

		if(battery->full_check_flag == 2) {
			pr_info("%s: force fully charged SOC !! (%d)", __func__, battery->full_check_flag);
			fg_set_full_charged();
			fg_soc = get_fuelgauge_value(FG_LEVEL);
		}
		else if(battery->full_check_flag < 2)
			pr_info("%s: full_check_flag (%d)", __func__, battery->full_check_flag);

		if(battery->full_check_flag++ > 10000)  // prevent overflow
			battery->full_check_flag = 3;
	}
	else
		battery->full_check_flag = 0;

	if (battery->info.charging_source == CHARGER_AC &&
		battery->info.batt_improper_ta == 0) {
		if (is_over_abs_time(battery)) {
			fg_soc = 100;
			battery->info.batt_is_full = 1;
			pr_info("%s: charging time is over", __func__);
			pr_info("%s: fg_vcell = %d, fg_soc = %d,"
				" is_full = %d\n",
				__func__, fg_vcell, fg_soc,
				battery->info.batt_is_full);
			p3_set_chg_en(battery, 0);
			goto __end__;
		}
	}

	if (fg_vcell <= battery->pdata->recharge_voltage) {
		if (battery->info.batt_is_full &&
			!battery->info.charging_enabled) {
			if (++battery->recharging_cnt > 1) {
				pr_info("recharging(under full)");
				battery->info.batt_is_recharging = 1;
				p3_set_chg_en(battery, 1);
				battery->recharging_cnt = 0;
				pr_info("%s: fg_vcell = %d, fg_soc = %d,"
					" is_recharging = %d\n",
					__func__, fg_vcell, fg_soc,
					battery->info.batt_is_recharging);
			}
		} else
			battery->recharging_cnt = 0;
	} else
		battery->recharging_cnt = 0;

	if (fg_soc > 100)
		fg_soc = 100;

	/*  Checks vcell level and tries to compensate SOC if needed.*/
	/*  If jig cable is connected, then skip low batt compensation check. */
	if (!check_jig_on() && !battery->info.charging_enabled)
		fg_soc = p3_low_batt_compensation(fg_soc, fg_vcell, fg_current);

__end__:
	pr_debug("fg_vcell = %d, fg_soc = %d, is_full = %d",
		fg_vcell, fg_soc, battery->info.batt_is_full);

	if(battery->is_first_check)
		battery->is_first_check = false;

	if (battery->info.batt_is_full &&
		(battery->info.charging_source != CHARGER_USB))
		fg_soc = 100;
#if 0 // not used
	else {
		if (fg_soc >= 100)
			fg_soc = 99;
	}
#endif
	return fg_soc;
}
コード例 #2
0
static void sec_bat_discharge_reason(struct chg_data *chg)
{
    int discharge_reason;
    ktime_t ktime;
    struct timespec cur_time;
    union power_supply_propval value;
    bool vdc_status;
    int recover_flag = 0;

    if (chg->pdata && chg->pdata->psy_fuelgauge &&
            chg->pdata->psy_fuelgauge->get_property) {
        chg->pdata->psy_fuelgauge->get_property(chg->pdata->psy_fuelgauge,
                                                POWER_SUPPLY_PROP_VOLTAGE_NOW, &value);
        chg->bat_info.batt_vcell = value.intval;

        chg->pdata->psy_fuelgauge->get_property(chg->pdata->psy_fuelgauge,
                                                POWER_SUPPLY_PROP_CAPACITY, &value);
        chg->bat_info.batt_soc = value.intval;
    }

#ifdef CONFIG_BATTERY_MAX17042
    // Corruption check algorithm
    if(chg->pdata && chg->pdata->get_jig_status &&
            !chg->pdata->get_jig_status()) {  // Not using Jig.
        if(chg->pdata && chg->pdata->fuelgauge_cb)
            recover_flag = chg->pdata->fuelgauge_cb(REQ_CAP_CORRUPTION_CHECK, 0, 0);
    }

    // Get recoverd values if recover flag is set.
    if(recover_flag) {
        if (chg->pdata && chg->pdata->psy_fuelgauge &&
                chg->pdata->psy_fuelgauge->get_property) {
            chg->pdata->psy_fuelgauge->get_property(chg->pdata->psy_fuelgauge,
                                                    POWER_SUPPLY_PROP_VOLTAGE_NOW, &value);
            chg->bat_info.batt_vcell = value.intval;

            chg->pdata->psy_fuelgauge->get_property(chg->pdata->psy_fuelgauge,
                                                    POWER_SUPPLY_PROP_CAPACITY, &value);
            chg->bat_info.batt_soc = value.intval;
        }
    }

    // VF FullCap range check algorithm.
    if(!(chk_cnt++ % 10)) {  // every 5 minutes
        if(chg->pdata && chg->pdata->fuelgauge_cb)
            recover_flag = chg->pdata->fuelgauge_cb(REQ_VF_FULLCAP_RANGE_CHECK, 0, 0);
        chk_cnt = 1;
    }

    // SOC gauging restart algorithm
    if(chg->low_batt_boot_flag) {
        // 1. Check charger connection status
        if(chg->pdata && chg->pdata->pmic_charger &&
                chg->pdata->pmic_charger->get_connection_status)
            vdc_status = chg->pdata->pmic_charger->get_connection_status();

        // 2. Restart SOC gauging after (vcell >= threshold) condition satisfied.
        if(vdc_status &&  !check_UV_charging_case(chg)) {
            if(chg->pdata && chg->pdata->fuelgauge_cb)
                chg->pdata->fuelgauge_cb(REQ_ADJUST_CAPACITY_RESTART, 0, 0);
            chg->low_batt_boot_flag = false;
        }

        // If charger removed, then clear flag.
        if(!vdc_status)
            chg->low_batt_boot_flag = false;
    }

    // Start low battery compensation algorithm
    if(!chg->charging && ((chg->bat_info.batt_vcell/1000) <= chg->check_start_vol)) {
        if(chg->pdata && chg->pdata->get_jig_status &&
                !chg->pdata->get_jig_status()) {  // Not using Jig.
            if (chg->pdata && chg->pdata->fuelgauge_cb)
                chg->pdata->fuelgauge_cb(REQ_LOW_BATTERY_COMPENSATION, 1, 0);
        }
    }
#endif

    discharge_reason = chg->bat_info.dis_reason & 0xf;

    if (discharge_reason == DISCONNECT_BAT_FULL &&
            chg->bat_info.batt_vcell < RECHARGE_COND_VOLTAGE) {
        chg->bat_info.dis_reason &= ~DISCONNECT_BAT_FULL;
        chg->is_recharging = true;
    }

    if (discharge_reason == DISCONNECT_TEMP_OVERHEAT &&
            chg->bat_info.batt_temp <=
            HIGH_RECOVER_TEMP)
        chg->bat_info.dis_reason &= ~DISCONNECT_TEMP_OVERHEAT;

    if (discharge_reason == DISCONNECT_TEMP_FREEZE &&
            chg->bat_info.batt_temp >=
            LOW_RECOVER_TEMP)
        chg->bat_info.dis_reason &= ~DISCONNECT_TEMP_FREEZE;

    if (discharge_reason == DISCONNECT_OVER_TIME &&
            chg->bat_info.batt_vcell < RECHARGE_COND_VOLTAGE)
        chg->bat_info.dis_reason &= ~DISCONNECT_OVER_TIME;

    if (chg->set_batt_full)
        chg->bat_info.dis_reason |= DISCONNECT_BAT_FULL;

    if (chg->bat_info.batt_health != POWER_SUPPLY_HEALTH_GOOD)
        chg->bat_info.dis_reason |= chg->bat_info.batt_health ==
                                    POWER_SUPPLY_HEALTH_OVERHEAT ?
                                    DISCONNECT_TEMP_OVERHEAT : DISCONNECT_TEMP_FREEZE;

    ktime = alarm_get_elapsed_realtime();
    cur_time = ktime_to_timespec(ktime);

    if (chg->discharging_time &&
            cur_time.tv_sec > chg->discharging_time) {
        pr_info("%s : cur_time = %d, timeout = %d\n", __func__, cur_time.tv_sec, chg->discharging_time);
        chg->set_charge_timeout = true;
//		chg->bat_info.dis_reason |= DISCONNECT_OVER_TIME;  // for GED (crespo).
        chg->bat_info.dis_reason |= DISCONNECT_BAT_FULL;
        // set battery full (expiration of absolute timer)
        sec_bat_set_status
        (&chg->callbacks, CHARGING_STATUS_FULL);
    }

    pr_debug("%s : Current Voltage : %d\n			\
		Current time : %ld  discharging_time : %ld\n	\
		discharging reason : %d\n",			\
             __func__, chg->bat_info.batt_vcell, cur_time.tv_sec,
             chg->discharging_time, chg->bat_info.dis_reason);
}
コード例 #3
0
static int __devinit p3_bat_probe(struct platform_device *pdev)
{
	struct p3_battery_platform_data *pdata = dev_get_platdata(&pdev->dev);
	struct battery_data *battery;
	int ret;
	unsigned long trigger;
	int irq_num;

	battery = kzalloc(sizeof(*battery), GFP_KERNEL);
	if (!battery)
		return -ENOMEM;

	battery->pdata = pdata;
	if (!battery->pdata) {
		pr_err("%s : No platform data\n", __func__);
		return -EINVAL;
	}

	battery->pdata->init_charger_gpio();

	platform_set_drvdata(pdev, battery);
	test_batterydata = battery;

	battery->present = 1;
	battery->info.level = 100;
	battery->info.charging_source = CHARGER_BATTERY;
	battery->info.batt_health = POWER_SUPPLY_HEALTH_GOOD;
	battery->is_first_check = true;

	battery->psy_battery.name = "battery";
	battery->psy_battery.type = POWER_SUPPLY_TYPE_BATTERY;
	battery->psy_battery.properties = p3_battery_properties;
	battery->psy_battery.num_properties = ARRAY_SIZE(p3_battery_properties);
	battery->psy_battery.get_property = p3_bat_get_property;

	battery->psy_usb.name = "usb";
	battery->psy_usb.type = POWER_SUPPLY_TYPE_USB;
	battery->psy_usb.supplied_to = supply_list;
	battery->psy_usb.num_supplicants = ARRAY_SIZE(supply_list);
	battery->psy_usb.properties = p3_power_properties;
	battery->psy_usb.num_properties = ARRAY_SIZE(p3_power_properties);
	battery->psy_usb.get_property = p3_usb_get_property;

	battery->psy_ac.name = "ac";
	battery->psy_ac.type = POWER_SUPPLY_TYPE_MAINS;
	battery->psy_ac.supplied_to = supply_list;
	battery->psy_ac.num_supplicants = ARRAY_SIZE(supply_list);
	battery->psy_ac.properties = p3_power_properties;
	battery->psy_ac.num_properties = ARRAY_SIZE(p3_power_properties);
	battery->psy_ac.get_property = p3_ac_get_property;

	mutex_init(&battery->work_lock);

	wake_lock_init(&battery->vbus_wake_lock, WAKE_LOCK_SUSPEND,
		"vbus wake lock");
	wake_lock_init(&battery->work_wake_lock, WAKE_LOCK_SUSPEND,
		"batt_work wake lock");
	wake_lock_init(&battery->cable_wake_lock, WAKE_LOCK_SUSPEND,
		"temp wake lock");
	wake_lock_init(&battery->fullcharge_wake_lock, WAKE_LOCK_SUSPEND,
		"fullcharge wake lock");
#ifdef __TEST_DEVICE_DRIVER__
	wake_lock_init(&battery->wake_lock_for_dev, WAKE_LOCK_SUSPEND,
		"test mode wake lock");
#endif /* __TEST_DEVICE_DRIVER__ */

	INIT_WORK(&battery->battery_work, p3_bat_work);
	INIT_WORK(&battery->cable_work, p3_cable_work);
	INIT_DELAYED_WORK(&battery->fuelgauge_work, fuelgauge_work_handler);
	INIT_DELAYED_WORK(&battery->fullcharging_work,
			fullcharging_work_handler);
	INIT_DELAYED_WORK(&battery->full_comp_work, full_comp_work_handler);
	INIT_DELAYED_WORK(&battery->TA_work, p3_TA_work_handler);
	battery->p3_TA_workqueue = create_singlethread_workqueue(
		"p3_TA_workqueue");
	if (!battery->p3_TA_workqueue) {
		pr_err("Failed to create single workqueue\n");
		ret = -ENOMEM;
		goto err_workqueue_init;
	}

	battery->last_poll = alarm_get_elapsed_realtime();
	alarm_init(&battery->alarm, ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,
		p3_battery_alarm);

	ret = power_supply_register(&pdev->dev, &battery->psy_battery);
	if (ret) {
		pr_err("Failed to register battery power supply.\n");
		goto err_battery_psy_register;
	}

	ret = power_supply_register(&pdev->dev, &battery->psy_usb);
	if (ret) {
		pr_err("Failed to register USB power supply.\n");
		goto err_usb_psy_register;
	}

	ret = power_supply_register(&pdev->dev, &battery->psy_ac);
	if (ret) {
		pr_err("Failed to register AC power supply.\n");
		goto err_ac_psy_register;
	}

	/* create sec detail attributes */
	p3_bat_create_attrs(battery->psy_battery.dev);

#ifdef __TEST_DEVICE_DRIVER__
	sec_batt_test_create_attrs(battery->psy_ac.dev);
#endif /* __TEST_DEVICE_DRIVER__ */

	battery->p3_battery_initial = 1;
	battery->low_batt_boot_flag = 0;

	battery->connect_irq = gpio_to_irq(pdata->charger.connect_line);
	if (check_ta_conn(battery))
		trigger = IRQF_TRIGGER_HIGH;
	else
		trigger = IRQF_TRIGGER_LOW;

	if (request_irq(battery->connect_irq, p3_TA_interrupt_handler, trigger,
			"TA_CON intr", battery)) {
		pr_err("p3_TA_interrupt_handler register failed!\n");
		goto err_charger_irq;
	}
	disable_irq(battery->connect_irq);

	// Get initial cable status and enable connection irq.
	p3_get_cable_status(battery);
	battery->previous_cable_status = battery->current_cable_status;
	enable_irq(battery->connect_irq);

	if (check_ta_conn(battery) && check_UV_charging_case())
		battery->low_batt_boot_flag = 1;

	mutex_lock(&battery->work_lock);
	fg_alert_init();
	mutex_unlock(&battery->work_lock);

	p3_bat_status_update(&battery->psy_battery);

	p3_cable_check_status(battery);

	/* before enable fullcharge interrupt, check fullcharge */
	if (battery->info.charging_source == CHARGER_AC
		&& battery->info.charging_enabled
		&& gpio_get_value(pdata->charger.fullcharge_line) == 1)
		p3_cable_charging(battery);

	/* Request IRQ */
	irq_num = gpio_to_irq(max17042_chip_data->pdata->fuel_alert_line);
	if (request_irq(irq_num, low_battery_isr,
			IRQF_TRIGGER_FALLING, "FUEL_ALRT irq", battery))
		pr_err("Can NOT request irq 'IRQ_FUEL_ALERT' %d ", irq_num);

#ifdef CONFIG_SAMSUNG_LPM_MODE
	lpm_mode_check(battery);
#endif

	return 0;

err_charger_irq:
	alarm_cancel(&battery->alarm);
	power_supply_unregister(&battery->psy_ac);
err_ac_psy_register:
	power_supply_unregister(&battery->psy_usb);
err_usb_psy_register:
	power_supply_unregister(&battery->psy_battery);
err_battery_psy_register:
	destroy_workqueue(battery->p3_TA_workqueue);
err_workqueue_init:
	wake_lock_destroy(&battery->vbus_wake_lock);
	wake_lock_destroy(&battery->work_wake_lock);
	wake_lock_destroy(&battery->cable_wake_lock);
	wake_lock_destroy(&battery->fullcharge_wake_lock);
	mutex_destroy(&battery->work_lock);
	kfree(battery);

	return ret;
}
コード例 #4
0
static __devinit int sec_battery_probe(struct platform_device *pdev)
{
    struct sec_battery_platform_data *pdata = pdev->dev.platform_data;
    struct chg_data *chg;
    int ret = 0;

    pr_info("%s : Samsung Battery Driver Loading\n", __func__);

    chg = kzalloc(sizeof(*chg), GFP_KERNEL);
    if (!chg)
        return -ENOMEM;

    chg->pdata = pdata;

    if (!chg->pdata || !chg->pdata->adc_table) {
        pr_err("%s : No platform data & adc_table supplied\n", __func__);
        ret = -EINVAL;
        goto err_bat_table;
    }

    chg->psy_bat.name = "battery",
                 chg->psy_bat.type = POWER_SUPPLY_TYPE_BATTERY,
                              chg->psy_bat.properties = sec_battery_props,
                                           chg->psy_bat.num_properties = ARRAY_SIZE(sec_battery_props),
                                                        chg->psy_bat.get_property = sec_bat_get_property,

                                                                     chg->psy_usb.name = "usb",
                                                                                  chg->psy_usb.type = POWER_SUPPLY_TYPE_USB,
                                                                                               chg->psy_usb.supplied_to = supply_list,
                                                                                                            chg->psy_usb.num_supplicants = ARRAY_SIZE(supply_list),
                                                                                                                         chg->psy_usb.properties = sec_power_properties,
                                                                                                                                      chg->psy_usb.num_properties = ARRAY_SIZE(sec_power_properties),
                                                                                                                                                   chg->psy_usb.get_property = sec_usb_get_property,

                                                                                                                                                                chg->psy_ac.name = "ac",
                                                                                                                                                                            chg->psy_ac.type = POWER_SUPPLY_TYPE_MAINS,
                                                                                                                                                                                        chg->psy_ac.supplied_to = supply_list,
                                                                                                                                                                                                    chg->psy_ac.num_supplicants = ARRAY_SIZE(supply_list),
                                                                                                                                                                                                                chg->psy_ac.properties = sec_power_properties,
                                                                                                                                                                                                                            chg->psy_ac.num_properties = ARRAY_SIZE(sec_power_properties),
                                                                                                                                                                                                                                        chg->psy_ac.get_property = sec_ac_get_property,

                                                                                                                                                                                                                                                    chg->present = 1;
    chg->polling_interval = POLLING_INTERVAL;
    chg->bat_info.batt_health = POWER_SUPPLY_HEALTH_GOOD;
    chg->bat_info.batt_is_full = false;
    chg->set_charge_timeout = false;
    chg->bat_info.batt_improper_ta = false;
    chg->is_recharging = false;
#ifdef CONFIG_BATTERY_MAX17042
    // Get battery type from fuelgauge driver.
    if(chg->pdata && chg->pdata->fuelgauge_cb)
        chg->battery_type = (battery_type_t)chg->pdata->fuelgauge_cb(
                                REQ_TEST_MODE_INTERFACE, TEST_MODE_BATTERY_TYPE_CHECK, 0);

    // Check UV charging case.
    if(chg->pdata && chg->pdata->pmic_charger &&
            chg->pdata->pmic_charger->get_connection_status) {
        if(chg->pdata->pmic_charger->get_connection_status() &&
                check_UV_charging_case(chg))
            chg->low_batt_boot_flag = true;
    }
    else
        chg->low_batt_boot_flag = false;

    // init delayed work
    INIT_DELAYED_WORK(&chg->full_chg_work, full_comp_work_handler);

    // Init low batt check threshold values.
    if(chg->battery_type == SDI_BATTERY_TYPE)
        chg->check_start_vol = 3550;  // Under 3.55V
    else if(chg->battery_type == ATL_BATTERY_TYPE)
        chg->check_start_vol = 3450;  // Under 3.45V
#endif

    chg->cable_status = CABLE_TYPE_NONE;
    chg->charging_status = CHARGING_STATUS_NONE;

    mutex_init(&chg->mutex);

    platform_set_drvdata(pdev, chg);

    wake_lock_init(&chg->vbus_wake_lock, WAKE_LOCK_SUSPEND,
                   "vbus_present");
    wake_lock_init(&chg->work_wake_lock, WAKE_LOCK_SUSPEND,
                   "sec_battery_work");

    INIT_WORK(&chg->bat_work, sec_bat_work);

    chg->monitor_wqueue =
        create_freezeable_workqueue(dev_name(&pdev->dev));
    if (!chg->monitor_wqueue) {
        pr_err("Failed to create freezeable workqueue\n");
        ret = -ENOMEM;
        goto err_wake_lock;
    }

    chg->last_poll = alarm_get_elapsed_realtime();
    alarm_init(&chg->alarm, ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,
               sec_battery_alarm);

    /* init power supplier framework */
    ret = power_supply_register(&pdev->dev, &chg->psy_bat);
    if (ret) {
        pr_err("Failed to register power supply psy_bat\n");
        goto err_wqueue;
    }

    ret = power_supply_register(&pdev->dev, &chg->psy_usb);
    if (ret) {
        pr_err("Failed to register power supply psy_usb\n");
        goto err_supply_unreg_bat;
    }

    ret = power_supply_register(&pdev->dev, &chg->psy_ac);
    if (ret) {
        pr_err("Failed to register power supply psy_ac\n");
        goto err_supply_unreg_usb;
    }

    sec_bat_create_attrs(chg->psy_bat.dev);

    chg->callbacks.set_cable = sec_bat_set_cable;
    chg->callbacks.set_status = sec_bat_set_status;
    chg->callbacks.force_update = sec_bat_force_update;
    if (chg->pdata->register_callbacks)
        chg->pdata->register_callbacks(&chg->callbacks);

    wake_lock(&chg->work_wake_lock);
    queue_work(chg->monitor_wqueue, &chg->bat_work);

    p1_lpm_mode_check(chg);

    return 0;

err_supply_unreg_ac:
    power_supply_unregister(&chg->psy_ac);
err_supply_unreg_usb:
    power_supply_unregister(&chg->psy_usb);
err_supply_unreg_bat:
    power_supply_unregister(&chg->psy_bat);
err_wqueue:
    destroy_workqueue(chg->monitor_wqueue);
    cancel_work_sync(&chg->bat_work);
    alarm_cancel(&chg->alarm);
err_wake_lock:
    wake_lock_destroy(&chg->work_wake_lock);
    wake_lock_destroy(&chg->vbus_wake_lock);
    mutex_destroy(&chg->mutex);
err_bat_table:
    kfree(chg);
    return ret;
}