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; }
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); }
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; }
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; }