From e32ad07cea1f198db1cc6c2055dd8471b562a535 Mon Sep 17 00:00:00 2001 From: Kalle Komierowski Date: Thu, 2 Feb 2012 16:05:46 +0100 Subject: ab8500_fg: Don't clear the CCMuxOffset bit The CCMuxOffset bit is not kept set. This will force the columb counter of the AB8500 to use the measure offset calibration. This should increase the accuracy of the fuel gauge. Signed-off-by: Kalle Komierowski Signed-off-by: Marcus Cooper Signed-off-by: Lee Jones Reviewed-by: Jonas ABERG --- drivers/power/ab8500_fg.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/power/ab8500_fg.c') diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index ed62ef788eb5..5a9f58d4c0fb 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -484,8 +484,9 @@ static int ab8500_fg_coulomb_counter(struct ab8500_fg *di, bool enable) di->flags.fg_enabled = true; } else { /* Clear any pending read requests */ - ret = abx500_set_register_interruptible(di->dev, - AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG, 0); + ret = abx500_mask_and_set_register_interruptible(di->dev, + AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG, + (RESET_ACCU | READ_REQ), 0); if (ret) goto cc_err; @@ -1403,8 +1404,7 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di) sleep_time = di->bat->fg_params->init_timer; /* Discard the first [x] seconds */ - if (di->init_cnt > - di->bat->fg_params->init_discard_time) { + if (di->init_cnt > di->bat->fg_params->init_discard_time) { ab8500_fg_calc_cap_discharge_voltage(di, true); ab8500_fg_check_capacity_limits(di, true); -- cgit v1.2.3 From b0284de05e07d56ff7de154d0c9263788755f5eb Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 30 Nov 2012 10:09:42 +0000 Subject: ab8500_bm: Rename battery management platform data to something more logical The platform specific battery management configuration data structure is currently called 'bat' short for 'battery'; however, it contains information for all components of the battery management group, rather than information pertaining to the battery itself - there are other structures for that. So, in keeping with its structure namesake 'abx500_bm_data', we rename it to 'bm' here. Using similar logic, we're also renaming 'bmdevs_of_probe' to the more device specific 'ab8500_bm_of_probe'. Signed-off-by: Lee Jones --- drivers/power/ab8500_fg.c | 96 +++++++++++++++++++++++------------------------ 1 file changed, 48 insertions(+), 48 deletions(-) (limited to 'drivers/power/ab8500_fg.c') diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index 5a9f58d4c0fb..4cf231375de3 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -173,7 +173,7 @@ struct inst_curr_result_list { * @avg_cap: Average capacity filter * @parent: Pointer to the struct ab8500 * @gpadc: Pointer to the struct gpadc - * @bat: Pointer to the abx500_bm platform data + * @bm: Platform specific battery management information * @fg_psy: Structure that holds the FG specific battery properties * @fg_wq: Work queue for running the FG algorithm * @fg_periodic_work: Work to run the FG algorithm periodically @@ -212,7 +212,7 @@ struct ab8500_fg { struct ab8500_fg_avg_cap avg_cap; struct ab8500 *parent; struct ab8500_gpadc *gpadc; - struct abx500_bm_data *bat; + struct abx500_bm_data *bm; struct power_supply fg_psy; struct workqueue_struct *fg_wq; struct delayed_work fg_periodic_work; @@ -355,7 +355,7 @@ static int ab8500_fg_is_low_curr(struct ab8500_fg *di, int curr) /* * We want to know if we're in low current mode */ - if (curr > -di->bat->fg_params->high_curr_threshold) + if (curr > -di->bm->fg_params->high_curr_threshold) return true; else return false; @@ -648,7 +648,7 @@ int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res) * 112.9nAh assumes 10mOhm, but fg_res is in 0.1mOhm */ val = (val * QLSB_NANO_AMP_HOURS_X10 * 36 * 4) / - (1000 * di->bat->fg_res); + (1000 * di->bm->fg_res); if (di->turn_off_fg) { dev_dbg(di->dev, "%s Disable FG\n", __func__); @@ -751,7 +751,7 @@ static void ab8500_fg_acc_cur_work(struct work_struct *work) * 112.9nAh assumes 10mOhm, but fg_res is in 0.1mOhm */ di->accu_charge = (val * QLSB_NANO_AMP_HOURS_X10) / - (100 * di->bat->fg_res); + (100 * di->bm->fg_res); /* * Convert to unit value in mA @@ -763,7 +763,7 @@ static void ab8500_fg_acc_cur_work(struct work_struct *work) * 112.9nAh assumes 10mOhm, but fg_res is in 0.1mOhm */ di->avg_curr = (val * QLSB_NANO_AMP_HOURS_X10 * 36) / - (1000 * di->bat->fg_res * (di->fg_samples / 4)); + (1000 * di->bm->fg_res * (di->fg_samples / 4)); di->flags.conv_done = true; @@ -815,8 +815,8 @@ static int ab8500_fg_volt_to_capacity(struct ab8500_fg *di, int voltage) struct abx500_v_to_cap *tbl; int cap = 0; - tbl = di->bat->bat_type[di->bat->batt_id].v_to_cap_tbl, - tbl_size = di->bat->bat_type[di->bat->batt_id].n_v_cap_tbl_elements; + tbl = di->bm->bat_type[di->bm->batt_id].v_to_cap_tbl, + tbl_size = di->bm->bat_type[di->bm->batt_id].n_v_cap_tbl_elements; for (i = 0; i < tbl_size; ++i) { if (voltage > tbl[i].voltage) @@ -867,8 +867,8 @@ static int ab8500_fg_battery_resistance(struct ab8500_fg *di) struct batres_vs_temp *tbl; int resist = 0; - tbl = di->bat->bat_type[di->bat->batt_id].batres_tbl; - tbl_size = di->bat->bat_type[di->bat->batt_id].n_batres_tbl_elements; + tbl = di->bm->bat_type[di->bm->batt_id].batres_tbl; + tbl_size = di->bm->bat_type[di->bm->batt_id].n_batres_tbl_elements; for (i = 0; i < tbl_size; ++i) { if (di->bat_temp / 10 > tbl[i].temp) @@ -889,11 +889,11 @@ static int ab8500_fg_battery_resistance(struct ab8500_fg *di) dev_dbg(di->dev, "%s Temp: %d battery internal resistance: %d" " fg resistance %d, total: %d (mOhm)\n", - __func__, di->bat_temp, resist, di->bat->fg_res / 10, - (di->bat->fg_res / 10) + resist); + __func__, di->bat_temp, resist, di->bm->fg_res / 10, + (di->bm->fg_res / 10) + resist); /* fg_res variable is in 0.1mOhm */ - resist += di->bat->fg_res / 10; + resist += di->bm->fg_res / 10; return resist; } @@ -1111,14 +1111,14 @@ static int ab8500_fg_capacity_level(struct ab8500_fg *di) percent = di->bat_cap.permille / 10; - if (percent <= di->bat->cap_levels->critical || + if (percent <= di->bm->cap_levels->critical || di->flags.low_bat) ret = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; - else if (percent <= di->bat->cap_levels->low) + else if (percent <= di->bm->cap_levels->low) ret = POWER_SUPPLY_CAPACITY_LEVEL_LOW; - else if (percent <= di->bat->cap_levels->normal) + else if (percent <= di->bm->cap_levels->normal) ret = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; - else if (percent <= di->bat->cap_levels->high) + else if (percent <= di->bm->cap_levels->high) ret = POWER_SUPPLY_CAPACITY_LEVEL_HIGH; else ret = POWER_SUPPLY_CAPACITY_LEVEL_FULL; @@ -1183,7 +1183,7 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init) di->bat_cap.prev_percent != (di->bat_cap.permille) / 10 && (di->bat_cap.permille / 10) < - di->bat->fg_params->maint_thres) { + di->bm->fg_params->maint_thres) { dev_dbg(di->dev, "battery reported full " "but capacity dropping: %d\n", @@ -1285,7 +1285,7 @@ static void ab8500_fg_algorithm_charging(struct ab8500_fg *di) switch (di->charge_state) { case AB8500_FG_CHARGE_INIT: di->fg_samples = SEC_TO_SAMPLE( - di->bat->fg_params->accu_charging); + di->bm->fg_params->accu_charging); ab8500_fg_coulomb_counter(di, true); ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_READOUT); @@ -1347,8 +1347,8 @@ static bool check_sysfs_capacity(struct ab8500_fg *di) cap_permille = ab8500_fg_convert_mah_to_permille(di, di->bat_cap.user_mah); - lower = di->bat_cap.permille - di->bat->fg_params->user_cap_limit * 10; - upper = di->bat_cap.permille + di->bat->fg_params->user_cap_limit * 10; + lower = di->bat_cap.permille - di->bm->fg_params->user_cap_limit * 10; + upper = di->bat_cap.permille + di->bm->fg_params->user_cap_limit * 10; if (lower < 0) lower = 0; @@ -1388,7 +1388,7 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di) case AB8500_FG_DISCHARGE_INIT: /* We use the FG IRQ to work on */ di->init_cnt = 0; - di->fg_samples = SEC_TO_SAMPLE(di->bat->fg_params->init_timer); + di->fg_samples = SEC_TO_SAMPLE(di->bm->fg_params->init_timer); ab8500_fg_coulomb_counter(di, true); ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_INITMEASURING); @@ -1401,17 +1401,17 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di) * samples to get an initial capacity. * Then go to READOUT */ - sleep_time = di->bat->fg_params->init_timer; + sleep_time = di->bm->fg_params->init_timer; /* Discard the first [x] seconds */ - if (di->init_cnt > di->bat->fg_params->init_discard_time) { + if (di->init_cnt > di->bm->fg_params->init_discard_time) { ab8500_fg_calc_cap_discharge_voltage(di, true); ab8500_fg_check_capacity_limits(di, true); } di->init_cnt += sleep_time; - if (di->init_cnt > di->bat->fg_params->init_total_time) + if (di->init_cnt > di->bm->fg_params->init_total_time) ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_READOUT_INIT); @@ -1426,7 +1426,7 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di) /* Intentional fallthrough */ case AB8500_FG_DISCHARGE_RECOVERY: - sleep_time = di->bat->fg_params->recovery_sleep_timer; + sleep_time = di->bm->fg_params->recovery_sleep_timer; /* * We should check the power consumption @@ -1438,9 +1438,9 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di) if (ab8500_fg_is_low_curr(di, di->inst_curr)) { if (di->recovery_cnt > - di->bat->fg_params->recovery_total_time) { + di->bm->fg_params->recovery_total_time) { di->fg_samples = SEC_TO_SAMPLE( - di->bat->fg_params->accu_high_curr); + di->bm->fg_params->accu_high_curr); ab8500_fg_coulomb_counter(di, true); ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_READOUT); @@ -1453,7 +1453,7 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di) di->recovery_cnt += sleep_time; } else { di->fg_samples = SEC_TO_SAMPLE( - di->bat->fg_params->accu_high_curr); + di->bm->fg_params->accu_high_curr); ab8500_fg_coulomb_counter(di, true); ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_READOUT); @@ -1462,7 +1462,7 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di) case AB8500_FG_DISCHARGE_READOUT_INIT: di->fg_samples = SEC_TO_SAMPLE( - di->bat->fg_params->accu_high_curr); + di->bm->fg_params->accu_high_curr); ab8500_fg_coulomb_counter(di, true); ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_READOUT); @@ -1509,9 +1509,9 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di) } di->high_curr_cnt += - di->bat->fg_params->accu_high_curr; + di->bm->fg_params->accu_high_curr; if (di->high_curr_cnt > - di->bat->fg_params->high_curr_time) + di->bm->fg_params->high_curr_time) di->recovery_needed = true; ab8500_fg_calc_cap_discharge_fg(di); @@ -1528,7 +1528,7 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di) ab8500_fg_calc_cap_discharge_voltage(di, true); di->fg_samples = SEC_TO_SAMPLE( - di->bat->fg_params->accu_high_curr); + di->bm->fg_params->accu_high_curr); ab8500_fg_coulomb_counter(di, true); ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_READOUT); @@ -1721,7 +1721,7 @@ static void ab8500_fg_low_bat_work(struct work_struct *work) vbat = ab8500_fg_bat_voltage(di); /* Check if LOW_BAT still fulfilled */ - if (vbat < di->bat->fg_params->lowbat_threshold) { + if (vbat < di->bm->fg_params->lowbat_threshold) { di->flags.low_bat = true; dev_warn(di->dev, "Battery voltage still LOW\n"); @@ -1779,8 +1779,8 @@ static int ab8500_fg_battok_init_hw_register(struct ab8500_fg *di) int ret; int new_val; - sel0 = di->bat->fg_params->battok_falling_th_sel0; - sel1 = di->bat->fg_params->battok_raising_th_sel1; + sel0 = di->bm->fg_params->battok_falling_th_sel0; + sel1 = di->bm->fg_params->battok_raising_th_sel1; cbp_sel0 = ab8500_fg_battok_calc(di, sel0); cbp_sel1 = ab8500_fg_battok_calc(di, sel1); @@ -1963,7 +1963,7 @@ static int ab8500_fg_get_property(struct power_supply *psy, di->bat_cap.max_mah); break; case POWER_SUPPLY_PROP_ENERGY_NOW: - if (di->flags.batt_unknown && !di->bat->chg_unknown_bat && + if (di->flags.batt_unknown && !di->bm->chg_unknown_bat && di->flags.batt_id_received) val->intval = ab8500_fg_convert_mah_to_uwh(di, di->bat_cap.max_mah); @@ -1978,21 +1978,21 @@ static int ab8500_fg_get_property(struct power_supply *psy, val->intval = di->bat_cap.max_mah; break; case POWER_SUPPLY_PROP_CHARGE_NOW: - if (di->flags.batt_unknown && !di->bat->chg_unknown_bat && + if (di->flags.batt_unknown && !di->bm->chg_unknown_bat && di->flags.batt_id_received) val->intval = di->bat_cap.max_mah; else val->intval = di->bat_cap.prev_mah; break; case POWER_SUPPLY_PROP_CAPACITY: - if (di->flags.batt_unknown && !di->bat->chg_unknown_bat && + if (di->flags.batt_unknown && !di->bm->chg_unknown_bat && di->flags.batt_id_received) val->intval = 100; else val->intval = di->bat_cap.prev_percent; break; case POWER_SUPPLY_PROP_CAPACITY_LEVEL: - if (di->flags.batt_unknown && !di->bat->chg_unknown_bat && + if (di->flags.batt_unknown && !di->bm->chg_unknown_bat && di->flags.batt_id_received) val->intval = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN; else @@ -2078,7 +2078,7 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data) if (!di->flags.batt_id_received) { const struct abx500_battery_type *b; - b = &(di->bat->bat_type[di->bat->batt_id]); + b = &(di->bm->bat_type[di->bm->batt_id]); di->flags.batt_id_received = true; @@ -2155,7 +2155,7 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di) AB8500_SYS_CTRL2_BLOCK, AB8500_LOW_BAT_REG, ab8500_volt_to_regval( - di->bat->fg_params->lowbat_threshold) << 1 | + di->bm->fg_params->lowbat_threshold) << 1 | LOW_BAT_ENABLE); if (ret) { dev_err(di->dev, "%s write failed\n", __func__); @@ -2457,10 +2457,10 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev) dev_err(&pdev->dev, "%s no mem for ab8500_fg\n", __func__); return -ENOMEM; } - di->bat = pdev->mfd_cell->platform_data; - if (!di->bat) { + di->bm = pdev->mfd_cell->platform_data; + if (!di->bm) { if (np) { - ret = bmdevs_of_probe(&pdev->dev, np, &di->bat); + ret = ab8500_bm_of_probe(&pdev->dev, np, &di->bm); if (ret) { dev_err(&pdev->dev, "failed to get battery information\n"); @@ -2491,11 +2491,11 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev) di->fg_psy.external_power_changed = ab8500_fg_external_power_changed; di->bat_cap.max_mah_design = MILLI_TO_MICRO * - di->bat->bat_type[di->bat->batt_id].charge_full_design; + di->bm->bat_type[di->bm->batt_id].charge_full_design; di->bat_cap.max_mah = di->bat_cap.max_mah_design; - di->vbat_nom = di->bat->bat_type[di->bat->batt_id].nominal_voltage; + di->vbat_nom = di->bm->bat_type[di->bm->batt_id].nominal_voltage; di->init_capacity = true; @@ -2549,7 +2549,7 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev) goto free_inst_curr_wq; } - di->fg_samples = SEC_TO_SAMPLE(di->bat->fg_params->init_timer); + di->fg_samples = SEC_TO_SAMPLE(di->bm->fg_params->init_timer); ab8500_fg_coulomb_counter(di, true); /* Initialize completion used to notify completion of inst current */ -- cgit v1.2.3 From 23a04f9f40f2b32ee593b768483105b1c776814d Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 29 Nov 2012 15:08:41 +0000 Subject: ab8500_bm: Always send platform specific battery information via pdata Currently the AB8500 battery management subsystem receives platform specific information via two different means depending on how the platform is booted. If DT is not enabled, a reference to a *_bm_data data structure containing each platform specific attribute is passed though platform_data. However, if DT is enabled, then platform_data is empty and the reference is gained though a DT specific probe function. There are two issues here 1) the same reference is being collected each time and 2) the DT way doesn't allow any provisions to select different platform specific attributes, which kind of defeats the object. Cc: Samuel Ortiz Signed-off-by: Lee Jones --- drivers/power/ab8500_fg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power/ab8500_fg.c') diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index 4cf231375de3..8bb9df92627d 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -2460,7 +2460,7 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev) di->bm = pdev->mfd_cell->platform_data; if (!di->bm) { if (np) { - ret = ab8500_bm_of_probe(&pdev->dev, np, &di->bm); + ret = ab8500_bm_of_probe(&pdev->dev, np, di->bm); if (ret) { dev_err(&pdev->dev, "failed to get battery information\n"); -- cgit v1.2.3 From 195c1c660c1868ac2024ad0ee3d5dce49bde4488 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 30 Nov 2012 10:56:51 +0000 Subject: ab8500_fg: Reorder obtainment of platform specific battery management data Now that we always pass platform specific battery management data through platform_data instead of obtaining it via different means depending the way be boot the system (DT or ATAGs); we need to re-jiggle the way we acquire it in the driver start-up functions. Now it is wrong for it to be missing, but we still allow Device Tree code to fiddle with it once we've confirmed it's there. Signed-off-by: Lee Jones --- drivers/power/ab8500_fg.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) (limited to 'drivers/power/ab8500_fg.c') diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index 8bb9df92627d..df681a80d8a4 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -2448,6 +2448,7 @@ static char *supply_interface[] = { static int __devinit ab8500_fg_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; + struct abx500_bm_data *plat = pdev->dev.platform_data; struct ab8500_fg *di; int i, irq; int ret = 0; @@ -2457,21 +2458,19 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev) dev_err(&pdev->dev, "%s no mem for ab8500_fg\n", __func__); return -ENOMEM; } - di->bm = pdev->mfd_cell->platform_data; - if (!di->bm) { - if (np) { - ret = ab8500_bm_of_probe(&pdev->dev, np, di->bm); - if (ret) { - dev_err(&pdev->dev, - "failed to get battery information\n"); - return ret; - } - } else { - dev_err(&pdev->dev, "missing dt node for ab8500_fg\n"); - return -EINVAL; + + if (!plat) { + dev_err(&pdev->dev, "no battery management data supplied\n"); + return -EINVAL; + } + di->bm = plat; + + if (np) { + ret = ab8500_bm_of_probe(&pdev->dev, np, di->bm); + if (ret) { + dev_err(&pdev->dev, "failed to get battery information\n"); + return ret; } - } else { - dev_info(&pdev->dev, "falling back to legacy platform data\n"); } mutex_init(&di->cc_lock); -- cgit v1.2.3 From 3988a4df3499e604a3f2ae979372d27fc5664f77 Mon Sep 17 00:00:00 2001 From: Johan Bjornstedt Date: Fri, 11 Jan 2013 13:12:50 +0000 Subject: ab8500_bm: Skip first CCEOC irq for instant current When enabling the CCEOC irq we might get false interrupt from ab8500-driver due to the latched value will be saved and interpreted as an IRQ when enabled Signed-off-by: Lee Jones Signed-off-by: Johan Bjornstedt Signed-off-by: Henrik Solver Reviewed-by: Karl KOMIEROWSKI Signed-off-by: Anton Vorontsov --- drivers/power/ab8500_fg.c | 63 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 8 deletions(-) (limited to 'drivers/power/ab8500_fg.c') diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index 3d05c73813c8..0f40c968286c 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -160,6 +160,7 @@ struct inst_curr_result_list { * @recovery_cnt: Counter for recovery mode * @high_curr_cnt: Counter for high current mode * @init_cnt: Counter for init mode + * @nbr_cceoc_irq_cnt Counter for number of CCEOC irqs received since enabled * @recovery_needed: Indicate if recovery is needed * @high_curr_mode: Indicate if we're in high current mode * @init_capacity: Indicate if initial capacity measuring should be done @@ -167,6 +168,7 @@ struct inst_curr_result_list { * @calib_state State during offset calibration * @discharge_state: Current discharge state * @charge_state: Current charge state + * @ab8500_fg_started Completion struct used for the instant current start * @ab8500_fg_complete Completion struct used for the instant current reading * @flags: Structure for information about events triggered * @bat_cap: Structure for battery capacity specific parameters @@ -199,6 +201,7 @@ struct ab8500_fg { int recovery_cnt; int high_curr_cnt; int init_cnt; + int nbr_cceoc_irq_cnt; bool recovery_needed; bool high_curr_mode; bool init_capacity; @@ -206,6 +209,7 @@ struct ab8500_fg { enum ab8500_fg_calibration_state calib_state; enum ab8500_fg_discharge_state discharge_state; enum ab8500_fg_charge_state charge_state; + struct completion ab8500_fg_started; struct completion ab8500_fg_complete; struct ab8500_fg_flags flags; struct ab8500_fg_battery_capacity bat_cap; @@ -524,13 +528,14 @@ cc_err: * Note: This is part "one" and has to be called before * ab8500_fg_inst_curr_finalize() */ - int ab8500_fg_inst_curr_start(struct ab8500_fg *di) +int ab8500_fg_inst_curr_start(struct ab8500_fg *di) { u8 reg_val; int ret; mutex_lock(&di->cc_lock); + di->nbr_cceoc_irq_cnt = 0; ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, AB8500_RTC_CC_CONF_REG, ®_val); if (ret < 0) @@ -558,6 +563,7 @@ cc_err: } /* Return and WFI */ + INIT_COMPLETION(di->ab8500_fg_started); INIT_COMPLETION(di->ab8500_fg_complete); enable_irq(di->irq); @@ -568,6 +574,17 @@ fail: return ret; } +/** + * ab8500_fg_inst_curr_started() - check if fg conversion has started + * @di: pointer to the ab8500_fg structure + * + * Returns 1 if conversion started, 0 if still waiting + */ +int ab8500_fg_inst_curr_started(struct ab8500_fg *di) +{ + return completion_done(&di->ab8500_fg_started); +} + /** * ab8500_fg_inst_curr_done() - check if fg conversion is done * @di: pointer to the ab8500_fg structure @@ -596,13 +613,15 @@ int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res) int timeout; if (!completion_done(&di->ab8500_fg_complete)) { - timeout = wait_for_completion_timeout(&di->ab8500_fg_complete, + timeout = wait_for_completion_timeout( + &di->ab8500_fg_complete, INS_CURR_TIMEOUT); dev_dbg(di->dev, "Finalize time: %d ms\n", ((INS_CURR_TIMEOUT - timeout) * 1000) / HZ); if (!timeout) { ret = -ETIME; disable_irq(di->irq); + di->nbr_cceoc_irq_cnt = 0; dev_err(di->dev, "completion timed out [%d]\n", __LINE__); goto fail; @@ -610,6 +629,7 @@ int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res) } disable_irq(di->irq); + di->nbr_cceoc_irq_cnt = 0; ret = abx500_mask_and_set_register_interruptible(di->dev, AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG, @@ -684,6 +704,7 @@ fail: int ab8500_fg_inst_curr_blocking(struct ab8500_fg *di) { int ret; + int timeout; int res = 0; ret = ab8500_fg_inst_curr_start(di); @@ -692,13 +713,32 @@ int ab8500_fg_inst_curr_blocking(struct ab8500_fg *di) return 0; } + /* Wait for CC to actually start */ + if (!completion_done(&di->ab8500_fg_started)) { + timeout = wait_for_completion_timeout( + &di->ab8500_fg_started, + INS_CURR_TIMEOUT); + dev_dbg(di->dev, "Start time: %d ms\n", + ((INS_CURR_TIMEOUT - timeout) * 1000) / HZ); + if (!timeout) { + ret = -ETIME; + dev_err(di->dev, "completion timed out [%d]\n", + __LINE__); + goto fail; + } + } + ret = ab8500_fg_inst_curr_finalize(di, &res); if (ret) { dev_err(di->dev, "Failed to finalize fg_inst\n"); return 0; } + dev_dbg(di->dev, "%s instant current: %d", __func__, res); return res; +fail: + mutex_unlock(&di->cc_lock); + return ret; } /** @@ -1523,8 +1563,6 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di) case AB8500_FG_DISCHARGE_WAKEUP: ab8500_fg_coulomb_counter(di, true); - di->inst_curr = ab8500_fg_inst_curr_blocking(di); - ab8500_fg_calc_cap_discharge_voltage(di, true); di->fg_samples = SEC_TO_SAMPLE( @@ -1641,8 +1679,6 @@ static void ab8500_fg_periodic_work(struct work_struct *work) fg_periodic_work.work); if (di->init_capacity) { - /* A dummy read that will return 0 */ - di->inst_curr = ab8500_fg_inst_curr_blocking(di); /* Get an initial capacity calculation */ ab8500_fg_calc_cap_discharge_voltage(di, true); ab8500_fg_check_capacity_limits(di, true); @@ -1828,7 +1864,13 @@ static void ab8500_fg_instant_work(struct work_struct *work) static irqreturn_t ab8500_fg_cc_data_end_handler(int irq, void *_di) { struct ab8500_fg *di = _di; - complete(&di->ab8500_fg_complete); + if (!di->nbr_cceoc_irq_cnt) { + di->nbr_cceoc_irq_cnt++; + complete(&di->ab8500_fg_started); + } else { + di->nbr_cceoc_irq_cnt = 0; + complete(&di->ab8500_fg_complete); + } return IRQ_HANDLED; } @@ -2551,7 +2593,11 @@ static int ab8500_fg_probe(struct platform_device *pdev) di->fg_samples = SEC_TO_SAMPLE(di->bm->fg_params->init_timer); ab8500_fg_coulomb_counter(di, true); - /* Initialize completion used to notify completion of inst current */ + /* + * Initialize completion used to notify completion and start + * of inst current + */ + init_completion(&di->ab8500_fg_started); init_completion(&di->ab8500_fg_complete); /* Register interrupts */ @@ -2571,6 +2617,7 @@ static int ab8500_fg_probe(struct platform_device *pdev) } di->irq = platform_get_irq_byname(pdev, "CCEOC"); disable_irq(di->irq); + di->nbr_cceoc_irq_cnt = 0; platform_set_drvdata(pdev, di); -- cgit v1.2.3 From 9a0bd070fd3582aed024a2f13a30b1de307db1e2 Mon Sep 17 00:00:00 2001 From: Jonas Aaberg Date: Tue, 15 Jan 2013 14:09:13 +0000 Subject: ab8500_fg: Replace msleep() with usleep_range() for greater accuracy Doing so provides a greater degree of accuracy when dealing with time-frames between 1us and 20ms. msleep() is only accurate for wake-ups greater than 20ms. Signed-off-by: Lee Jones Signed-off-by: Jonas ABERG Reviewed-by: Johan BJORNSTEDT Signed-off-by: Anton Vorontsov --- drivers/power/ab8500_fg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power/ab8500_fg.c') diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index 0f40c968286c..f3dbba8b3925 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -956,7 +956,7 @@ static int ab8500_fg_load_comp_volt_to_capacity(struct ab8500_fg *di) do { vbat += ab8500_fg_bat_voltage(di); i++; - msleep(5); + usleep_range(5000, 6000); } while (!ab8500_fg_inst_curr_done(di)); ab8500_fg_inst_curr_finalize(di, &di->inst_curr); -- cgit v1.2.3 From ea4024017831d61874351defe8f8c58ae73f8009 Mon Sep 17 00:00:00 2001 From: Marcus Cooper Date: Fri, 11 Jan 2013 13:12:54 +0000 Subject: ab8500_bm: Recharge condition not optimal for battery Today the battery recharge is determined with a voltage threshold. This voltage threshold is only valid when the battery is relaxed. In charging algorithm the voltage read is the loaded battery voltage and no compensation is done to get the relaxed voltage. When maintenance charging is not selected, this makes the recharging condition to almost immediately activate when there is a discharge present on the battery. Depending on which vendor the battery comes from this behavior can wear out the battery much faster than normal. The fuelgauge driver is responsible to monitor the actual battery capacity and is able to estimate the remaining capacity. It is better to use the remaining capacity as a limit to determine when battery should be recharged. Signed-off-by: Lee Jones Signed-off-by: Marcus Cooper Reviewed-by: Hakan BERG Reviewed-by: Jonas ABERG Signed-off-by: Anton Vorontsov --- drivers/power/ab8500_fg.c | 146 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 135 insertions(+), 11 deletions(-) (limited to 'drivers/power/ab8500_fg.c') diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index f3dbba8b3925..76fab6b5c541 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -113,6 +113,13 @@ struct ab8500_fg_avg_cap { int sum; }; +struct ab8500_fg_cap_scaling { + bool enable; + int cap_to_scale[2]; + int disable_cap_level; + int scaled_cap; +}; + struct ab8500_fg_battery_capacity { int max_mah_design; int max_mah; @@ -123,6 +130,7 @@ struct ab8500_fg_battery_capacity { int prev_percent; int prev_level; int user_mah; + struct ab8500_fg_cap_scaling cap_scale; }; struct ab8500_fg_flags { @@ -1166,6 +1174,99 @@ static int ab8500_fg_capacity_level(struct ab8500_fg *di) return ret; } +/** + * ab8500_fg_calculate_scaled_capacity() - Capacity scaling + * @di: pointer to the ab8500_fg structure + * + * Calculates the capacity to be shown to upper layers. Scales the capacity + * to have 100% as a reference from the actual capacity upon removal of charger + * when charging is in maintenance mode. + */ +static int ab8500_fg_calculate_scaled_capacity(struct ab8500_fg *di) +{ + struct ab8500_fg_cap_scaling *cs = &di->bat_cap.cap_scale; + int capacity = di->bat_cap.prev_percent; + + if (!cs->enable) + return capacity; + + /* + * As long as we are in fully charge mode scale the capacity + * to show 100%. + */ + if (di->flags.fully_charged) { + cs->cap_to_scale[0] = 100; + cs->cap_to_scale[1] = + max(capacity, di->bm->fg_params->maint_thres); + dev_dbg(di->dev, "Scale cap with %d/%d\n", + cs->cap_to_scale[0], cs->cap_to_scale[1]); + } + + /* Calculates the scaled capacity. */ + if ((cs->cap_to_scale[0] != cs->cap_to_scale[1]) + && (cs->cap_to_scale[1] > 0)) + capacity = min(100, + DIV_ROUND_CLOSEST(di->bat_cap.prev_percent * + cs->cap_to_scale[0], + cs->cap_to_scale[1])); + + if (di->flags.charging) { + if (capacity < cs->disable_cap_level) { + cs->disable_cap_level = capacity; + dev_dbg(di->dev, "Cap to stop scale lowered %d%%\n", + cs->disable_cap_level); + } else if (!di->flags.fully_charged) { + if (di->bat_cap.prev_percent >= + cs->disable_cap_level) { + dev_dbg(di->dev, "Disabling scaled capacity\n"); + cs->enable = false; + capacity = di->bat_cap.prev_percent; + } else { + dev_dbg(di->dev, + "Waiting in cap to level %d%%\n", + cs->disable_cap_level); + capacity = cs->disable_cap_level; + } + } + } + + return capacity; +} + +/** + * ab8500_fg_update_cap_scalers() - Capacity scaling + * @di: pointer to the ab8500_fg structure + * + * To be called when state change from charge<->discharge to update + * the capacity scalers. + */ +static void ab8500_fg_update_cap_scalers(struct ab8500_fg *di) +{ + struct ab8500_fg_cap_scaling *cs = &di->bat_cap.cap_scale; + + if (!cs->enable) + return; + if (di->flags.charging) { + di->bat_cap.cap_scale.disable_cap_level = + di->bat_cap.cap_scale.scaled_cap; + dev_dbg(di->dev, "Cap to stop scale at charge %d%%\n", + di->bat_cap.cap_scale.disable_cap_level); + } else { + if (cs->scaled_cap != 100) { + cs->cap_to_scale[0] = cs->scaled_cap; + cs->cap_to_scale[1] = di->bat_cap.prev_percent; + } else { + cs->cap_to_scale[0] = 100; + cs->cap_to_scale[1] = + max(di->bat_cap.prev_percent, + di->bm->fg_params->maint_thres); + } + + dev_dbg(di->dev, "Cap to scale at discharge %d/%d\n", + cs->cap_to_scale[0], cs->cap_to_scale[1]); + } +} + /** * ab8500_fg_check_capacity_limits() - Check if capacity has changed * @di: pointer to the ab8500_fg structure @@ -1214,16 +1315,24 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init) } else if (di->flags.fully_charged) { /* * We report 100% if algorithm reported fully charged - * unless capacity drops too much + * and show 100% during maintenance charging (scaling). */ if (di->flags.force_full) { di->bat_cap.prev_percent = di->bat_cap.permille / 10; di->bat_cap.prev_mah = di->bat_cap.mah; - } else if (!di->flags.force_full && - di->bat_cap.prev_percent != - (di->bat_cap.permille) / 10 && - (di->bat_cap.permille / 10) < - di->bm->fg_params->maint_thres) { + + changed = true; + + if (!di->bat_cap.cap_scale.enable && + di->bm->capacity_scaling) { + di->bat_cap.cap_scale.enable = true; + di->bat_cap.cap_scale.cap_to_scale[0] = 100; + di->bat_cap.cap_scale.cap_to_scale[1] = + di->bat_cap.prev_percent; + di->bat_cap.cap_scale.disable_cap_level = 100; + } + } else if ( di->bat_cap.prev_percent != + (di->bat_cap.permille) / 10) { dev_dbg(di->dev, "battery reported full " "but capacity dropping: %d\n", @@ -1272,6 +1381,14 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init) } if (changed) { + if (di->bm->capacity_scaling) { + di->bat_cap.cap_scale.scaled_cap = + ab8500_fg_calculate_scaled_capacity(di); + + dev_info(di->dev, "capacity=%d (%d)\n", + di->bat_cap.prev_percent, + di->bat_cap.cap_scale.scaled_cap); + } power_supply_changed(&di->fg_psy); if (di->flags.fully_charged && di->flags.force_full) { dev_dbg(di->dev, "Battery full, notifying.\n"); @@ -1337,7 +1454,7 @@ static void ab8500_fg_algorithm_charging(struct ab8500_fg *di) * Read the FG and calculate the new capacity */ mutex_lock(&di->cc_lock); - if (!di->flags.conv_done) { + if (!di->flags.conv_done && !di->flags.force_full) { /* Wasn't the CC IRQ that got us here */ mutex_unlock(&di->cc_lock); dev_dbg(di->dev, "%s CC conv not done\n", @@ -2027,7 +2144,9 @@ static int ab8500_fg_get_property(struct power_supply *psy, val->intval = di->bat_cap.prev_mah; break; case POWER_SUPPLY_PROP_CAPACITY: - if (di->flags.batt_unknown && !di->bm->chg_unknown_bat && + if (di->bm->capacity_scaling) + val->intval = di->bat_cap.cap_scale.scaled_cap; + else if (di->flags.batt_unknown && !di->bm->chg_unknown_bat && di->flags.batt_id_received) val->intval = 100; else @@ -2091,6 +2210,8 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data) break; di->flags.charging = false; di->flags.fully_charged = false; + if (di->bm->capacity_scaling) + ab8500_fg_update_cap_scalers(di); queue_work(di->fg_wq, &di->fg_work); break; case POWER_SUPPLY_STATUS_FULL: @@ -2103,10 +2224,13 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data) queue_work(di->fg_wq, &di->fg_work); break; case POWER_SUPPLY_STATUS_CHARGING: - if (di->flags.charging) + if (di->flags.charging && + !di->flags.fully_charged) break; di->flags.charging = true; di->flags.fully_charged = false; + if (di->bm->capacity_scaling) + ab8500_fg_update_cap_scalers(di); queue_work(di->fg_wq, &di->fg_work); break; }; @@ -2146,8 +2270,8 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data) case POWER_SUPPLY_PROP_TEMP: switch (ext->type) { case POWER_SUPPLY_TYPE_BATTERY: - if (di->flags.batt_id_received) - di->bat_temp = ret.intval; + if (di->flags.batt_id_received) + di->bat_temp = ret.intval; break; default: break; -- cgit v1.2.3 From 129d583b4432375a9559708882172320e4c8fb94 Mon Sep 17 00:00:00 2001 From: Rickard Andersson Date: Fri, 11 Jan 2013 13:12:55 +0000 Subject: ab8500_fg: Balance IRQ enable In case of time out error IRQ needs to be disabled otherwise we will get unbalanced enable/disable pairs. Signed-off-by: Lee Jones Signed-off-by: Rickard Andersson Reviewed-by: Jonas ABERG Signed-off-by: Anton Vorontsov --- drivers/power/ab8500_fg.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/power/ab8500_fg.c') diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index 76fab6b5c541..d2d14ea681a4 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -745,6 +745,7 @@ int ab8500_fg_inst_curr_blocking(struct ab8500_fg *di) dev_dbg(di->dev, "%s instant current: %d", __func__, res); return res; fail: + disable_irq(di->irq); mutex_unlock(&di->cc_lock); return ret; } -- cgit v1.2.3 From 6eaf8740dad8820ea708e83f14134401de1cff6e Mon Sep 17 00:00:00 2001 From: pender01 Date: Fri, 11 Jan 2013 13:12:59 +0000 Subject: ab8500_fg: Round capacity output Round the capacity values for better enduser experience. Signed-off-by: Lee Jones Signed-off-by: pender01 Reviewed-by: Jonas ABERG Tested-by: Marcus COOPER Signed-off-by: Anton Vorontsov --- drivers/power/ab8500_fg.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'drivers/power/ab8500_fg.c') diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index d2d14ea681a4..af0374620681 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -32,6 +32,7 @@ #include #include #include +#include #define MILLI_TO_MICRO 1000 #define FG_LSB_IN_MA 1627 @@ -1158,7 +1159,7 @@ static int ab8500_fg_capacity_level(struct ab8500_fg *di) { int ret, percent; - percent = di->bat_cap.permille / 10; + percent = DIV_ROUND_CLOSEST(di->bat_cap.permille, 10); if (percent <= di->bm->cap_levels->critical || di->flags.low_bat) @@ -1279,6 +1280,7 @@ static void ab8500_fg_update_cap_scalers(struct ab8500_fg *di) static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init) { bool changed = false; + int percent = DIV_ROUND_CLOSEST(di->bat_cap.permille, 10); di->bat_cap.level = ab8500_fg_capacity_level(di); @@ -1310,6 +1312,7 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init) dev_dbg(di->dev, "Battery low, set capacity to 0\n"); di->bat_cap.prev_percent = 0; di->bat_cap.permille = 0; + percent = 0; di->bat_cap.prev_mah = 0; di->bat_cap.mah = 0; changed = true; @@ -1319,7 +1322,7 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init) * and show 100% during maintenance charging (scaling). */ if (di->flags.force_full) { - di->bat_cap.prev_percent = di->bat_cap.permille / 10; + di->bat_cap.prev_percent = percent; di->bat_cap.prev_mah = di->bat_cap.mah; changed = true; @@ -1332,19 +1335,18 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init) di->bat_cap.prev_percent; di->bat_cap.cap_scale.disable_cap_level = 100; } - } else if ( di->bat_cap.prev_percent != - (di->bat_cap.permille) / 10) { + } else if (di->bat_cap.prev_percent != percent) { dev_dbg(di->dev, "battery reported full " "but capacity dropping: %d\n", - di->bat_cap.permille / 10); - di->bat_cap.prev_percent = di->bat_cap.permille / 10; + percent); + di->bat_cap.prev_percent = percent; di->bat_cap.prev_mah = di->bat_cap.mah; changed = true; } - } else if (di->bat_cap.prev_percent != di->bat_cap.permille / 10) { - if (di->bat_cap.permille / 10 == 0) { + } else if (di->bat_cap.prev_percent != percent) { + if (percent == 0) { /* * We will not report 0% unless we've got * the LOW_BAT IRQ, no matter what the FG @@ -1354,11 +1356,11 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init) di->bat_cap.permille = 1; di->bat_cap.prev_mah = 1; di->bat_cap.mah = 1; + percent = 1; changed = true; } else if (!(!di->flags.charging && - (di->bat_cap.permille / 10) > - di->bat_cap.prev_percent) || init) { + percent > di->bat_cap.prev_percent) || init) { /* * We do not allow reported capacity to go up * unless we're charging or if we're in init @@ -1366,9 +1368,9 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init) dev_dbg(di->dev, "capacity changed from %d to %d (%d)\n", di->bat_cap.prev_percent, - di->bat_cap.permille / 10, + percent, di->bat_cap.permille); - di->bat_cap.prev_percent = di->bat_cap.permille / 10; + di->bat_cap.prev_percent = percent; di->bat_cap.prev_mah = di->bat_cap.mah; changed = true; @@ -1376,7 +1378,7 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init) dev_dbg(di->dev, "capacity not allowed to go up since " "no charger is connected: %d to %d (%d)\n", di->bat_cap.prev_percent, - di->bat_cap.permille / 10, + percent, di->bat_cap.permille); } } -- cgit v1.2.3 From 8bcf3b39a8776630eeec51aa1180626d66dc51aa Mon Sep 17 00:00:00 2001 From: Hakan Berg Date: Fri, 11 Jan 2013 13:13:01 +0000 Subject: ab8500_fg: Added support for BATT_OVV Add support for the battery over-voltage situation Signed-off-by: Lee Jones Signed-off-by: Hakan Berg Reviewed-by: Karl KOMIEROWSKI Signed-off-by: Anton Vorontsov --- drivers/power/ab8500_fg.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers/power/ab8500_fg.c') diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index af0374620681..2917def7621c 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -1840,24 +1840,26 @@ static void ab8500_fg_check_hw_failure_work(struct work_struct *work) * If we have had a battery over-voltage situation, * check ovv-bit to see if it should be reset. */ - if (di->flags.bat_ovv) { - ret = abx500_get_register_interruptible(di->dev, - AB8500_CHARGER, AB8500_CH_STAT_REG, - ®_value); - if (ret < 0) { - dev_err(di->dev, "%s ab8500 read failed\n", __func__); - return; - } - if ((reg_value & BATT_OVV) != BATT_OVV) { - dev_dbg(di->dev, "Battery recovered from OVV\n"); - di->flags.bat_ovv = false; + ret = abx500_get_register_interruptible(di->dev, + AB8500_CHARGER, AB8500_CH_STAT_REG, + ®_value); + if (ret < 0) { + dev_err(di->dev, "%s ab8500 read failed\n", __func__); + return; + } + if ((reg_value & BATT_OVV) == BATT_OVV) { + if (!di->flags.bat_ovv) { + dev_dbg(di->dev, "Battery OVV\n"); + di->flags.bat_ovv = true; power_supply_changed(&di->fg_psy); - return; } - /* Not yet recovered from ovv, reschedule this test */ queue_delayed_work(di->fg_wq, &di->fg_check_hw_failure_work, round_jiffies(HZ)); + } else { + dev_dbg(di->dev, "Battery recovered from OVV\n"); + di->flags.bat_ovv = false; + power_supply_changed(&di->fg_psy); } } @@ -2037,8 +2039,6 @@ static irqreturn_t ab8500_fg_batt_ovv_handler(int irq, void *_di) struct ab8500_fg *di = _di; dev_dbg(di->dev, "Battery OVV\n"); - di->flags.bat_ovv = true; - power_supply_changed(&di->fg_psy); /* Schedule a new HW failure check */ queue_delayed_work(di->fg_wq, &di->fg_check_hw_failure_work, 0); -- cgit v1.2.3 From f902dadc123376b973e5f9cb216a22675a2a9181 Mon Sep 17 00:00:00 2001 From: Paer-Olof Haakansson Date: Fri, 11 Jan 2013 13:13:05 +0000 Subject: ab8500_fg: Overflow in current calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When calculating the average current the nominator will overflow when the charging current is high. Signed-off-by: Lee Jones Signed-off-by: Henrik Sölver Reviewed-by: Par-Olof HAKANSSON Reviewed-by: Jonas ABERG Tested-by: Par-Olof HAKANSSON Signed-off-by: Anton Vorontsov --- drivers/power/ab8500_fg.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/power/ab8500_fg.c') diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index 2917def7621c..570577c32ade 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -805,12 +805,9 @@ static void ab8500_fg_acc_cur_work(struct work_struct *work) /* * Convert to unit value in mA - * Full scale input voltage is - * 66.660mV => LSB = 66.660mV/(4096*res) = 1.627mA - * Given a 250ms conversion cycle time the LSB corresponds - * to 112.9 nAh. Convert to current by dividing by the conversion + * by dividing by the conversion * time in hours (= samples / (3600 * 4)h) - * 112.9nAh assumes 10mOhm, but fg_res is in 0.1mOhm + * and multiply with 1000 */ di->avg_curr = (val * QLSB_NANO_AMP_HOURS_X10 * 36) / (1000 * di->bm->fg_res * (di->fg_samples / 4)); @@ -821,6 +818,8 @@ static void ab8500_fg_acc_cur_work(struct work_struct *work) queue_work(di->fg_wq, &di->fg_work); + dev_dbg(di->dev, "fg_res: %d, fg_samples: %d, gasg: %d, accu_charge: %d \n", + di->bm->fg_res, di->fg_samples, val, di->accu_charge); return; exit: dev_err(di->dev, -- cgit v1.2.3 From 41ce25657bcd631235c123ace9d2f37c995e123d Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 11 Jan 2013 13:13:06 +0000 Subject: ab8500_fg: Remove pointless round_jiffies() call As HZ is a full-second, there is little point in rounding it. Let's save a few cycles by using HZ directly. Signed-off-by: Lee Jones Signed-off-by: Anton Vorontsov --- drivers/power/ab8500_fg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power/ab8500_fg.c') diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index 570577c32ade..cd03549b6227 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -1854,7 +1854,7 @@ static void ab8500_fg_check_hw_failure_work(struct work_struct *work) } /* Not yet recovered from ovv, reschedule this test */ queue_delayed_work(di->fg_wq, &di->fg_check_hw_failure_work, - round_jiffies(HZ)); + HZ); } else { dev_dbg(di->dev, "Battery recovered from OVV\n"); di->flags.bat_ovv = false; -- cgit v1.2.3 From 75f2a219ff11e6707903194009656820bba2d5c6 Mon Sep 17 00:00:00 2001 From: Hakan Berg Date: Thu, 10 May 2012 08:43:25 +0200 Subject: ab8500-fg: Adjust for RF bursts voltage drops Changed conditions for restarting low battery measurements counter and adjusted the interval between measurements to avoid RF burst induced voltage drops, and to shorten time to decide to shut down. Signed-off-by: Hakan Berg Signed-off-by: Martin Bergstrom Signed-off-by: Lee Jones Reviewed-by: Jonas ABERG Reviewed-by: Marcus COOPER --- drivers/power/ab8500_fg.c | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) (limited to 'drivers/power/ab8500_fg.c') diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index cd03549b6227..a0cbbd393fc9 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -43,7 +43,7 @@ #define NBR_AVG_SAMPLES 20 -#define LOW_BAT_CHECK_INTERVAL (2 * HZ) +#define LOW_BAT_CHECK_INTERVAL (HZ / 16) /* 62.5 ms */ #define VALID_CAPACITY_SEC (45 * 60) /* 45 minutes */ #define BATT_OK_MIN 2360 /* mV */ @@ -169,6 +169,7 @@ struct inst_curr_result_list { * @recovery_cnt: Counter for recovery mode * @high_curr_cnt: Counter for high current mode * @init_cnt: Counter for init mode + * @low_bat_cnt Counter for number of consecutive low battery measures * @nbr_cceoc_irq_cnt Counter for number of CCEOC irqs received since enabled * @recovery_needed: Indicate if recovery is needed * @high_curr_mode: Indicate if we're in high current mode @@ -210,6 +211,7 @@ struct ab8500_fg { int recovery_cnt; int high_curr_cnt; int init_cnt; + int low_bat_cnt; int nbr_cceoc_irq_cnt; bool recovery_needed; bool high_curr_mode; @@ -1879,25 +1881,29 @@ static void ab8500_fg_low_bat_work(struct work_struct *work) /* Check if LOW_BAT still fulfilled */ if (vbat < di->bm->fg_params->lowbat_threshold) { - di->flags.low_bat = true; - dev_warn(di->dev, "Battery voltage still LOW\n"); - - /* - * We need to re-schedule this check to be able to detect - * if the voltage increases again during charging - */ - queue_delayed_work(di->fg_wq, &di->fg_low_bat_work, - round_jiffies(LOW_BAT_CHECK_INTERVAL)); + /* Is it time to shut down? */ + if (di->low_bat_cnt < 1) { + di->flags.low_bat = true; + dev_warn(di->dev, "Shut down pending...\n"); + } else { + /* + * Else we need to re-schedule this check to be able to detect + * if the voltage increases again during charging or + * due to decreasing load. + */ + di->low_bat_cnt--; + dev_warn(di->dev, "Battery voltage still LOW\n"); + queue_delayed_work(di->fg_wq, &di->fg_low_bat_work, + round_jiffies(LOW_BAT_CHECK_INTERVAL)); + } } else { - di->flags.low_bat = false; + di->flags.low_bat_delay = false; + di->low_bat_cnt = 10; dev_warn(di->dev, "Battery voltage OK again\n"); } /* This is needed to dispatch LOW_BAT */ ab8500_fg_check_capacity_limits(di, false); - - /* Set this flag to check if LOW_BAT IRQ still occurs */ - di->flags.low_bat_delay = false; } /** @@ -2056,6 +2062,7 @@ static irqreturn_t ab8500_fg_lowbatf_handler(int irq, void *_di) { struct ab8500_fg *di = _di; + /* Initiate handling in ab8500_fg_low_bat_work() if not already initiated. */ if (!di->flags.low_bat_delay) { dev_warn(di->dev, "Battery voltage is below LOW threshold\n"); di->flags.low_bat_delay = true; @@ -2698,6 +2705,12 @@ static int ab8500_fg_probe(struct platform_device *pdev) INIT_DEFERRABLE_WORK(&di->fg_check_hw_failure_work, ab8500_fg_check_hw_failure_work); + /* Reset battery low voltage flag */ + di->flags.low_bat = false; + + /* Initialize low battery counter */ + di->low_bat_cnt = 10; + /* Initialize OVV, and other registers */ ret = ab8500_fg_init_hw_registers(di); if (ret) { -- cgit v1.2.3 From ffaa39d9edd7e3d620d14d574485e814edbf3950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Bergstr=C3=B6m?= Date: Fri, 4 May 2012 14:43:50 +0200 Subject: ab8500-fg: Go to INIT_RECOVERY when charger removed When the charger is removed we need to go to INIT_RECOVERY state instead of directly to RECOVERY state. Signed-off-by: Martin Bergstrom Signed-off-by: Lee Jones Reviewed-by: Marcus COOPER Reviewed-by: Jonas ABERG Tested-by: Jonas ABERG --- drivers/power/ab8500_fg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power/ab8500_fg.c') diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index a0cbbd393fc9..c791a5cef2b1 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -1641,7 +1641,7 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di) if (di->recovery_needed) { ab8500_fg_discharge_state_to(di, - AB8500_FG_DISCHARGE_RECOVERY); + AB8500_FG_DISCHARGE_INIT_RECOVERY); queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0); -- cgit v1.2.3 From 53ef1f590dc59f3c1478c68ea1f06a28f55ddccb Mon Sep 17 00:00:00 2001 From: Jonas Aaberg Date: Mon, 21 May 2012 16:05:01 +0200 Subject: ab8500-bm: Flush all work queues before suspending Flush all workqueues at suspend time to avoid suspending during work. Signed-off-by: Jonas Aaberg Signed-off-by: Lee Jones Reviewed-by: Marcus COOPER --- drivers/power/ab8500_fg.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/power/ab8500_fg.c') diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index c791a5cef2b1..f94c9661e47e 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -2570,6 +2570,11 @@ static int ab8500_fg_suspend(struct platform_device *pdev, struct ab8500_fg *di = platform_get_drvdata(pdev); flush_delayed_work(&di->fg_periodic_work); + flush_work(&di->fg_work); + flush_work(&di->fg_acc_cur_work); + flush_delayed_work(&di->fg_reinit_work); + flush_delayed_work(&di->fg_low_bat_work); + flush_delayed_work(&di->fg_check_hw_failure_work); /* * If the FG is enabled we will disable it before going to suspend -- cgit v1.2.3 From 1a793a10899487d40ac3071f52a6e231260d935d Mon Sep 17 00:00:00 2001 From: Rajkumar Kasirajan Date: Wed, 30 May 2012 14:54:28 +0530 Subject: ab8500-fg: Use correct battery charge full design If battery is not identified while fg probe, mah_max_design gets initialized with unknown battery's charge full design. Reinitialize mah_max_design if battery is identified after fg probe. Signed-off-by: Rajkumar Kasirajan Signed-off-by: Lee Jones Reviewed-by: Vijaya Kumar K-1 Reviewed-by: Marcus COOPER Reviewed-by: Olivier CLERGEAUD Reviewed-by: Arun MURTHY Reviewed-by: Rabin VINCENT Tested-by: Rupesh KUMAR Tested-by: Jonas ABERG --- drivers/power/ab8500_fg.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/power/ab8500_fg.c') diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index f94c9661e47e..25dae4c4b0ef 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -2250,7 +2250,8 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data) case POWER_SUPPLY_PROP_TECHNOLOGY: switch (ext->type) { case POWER_SUPPLY_TYPE_BATTERY: - if (!di->flags.batt_id_received) { + if (!di->flags.batt_id_received && + di->bm->batt_id != BATTERY_UNKNOWN) { const struct abx500_battery_type *b; b = &(di->bm->bat_type[di->bm->batt_id]); -- cgit v1.2.3