X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fpower%2Fapm_power.c;h=a4892275659dc02ffb1d12b85d0774a86bda8b08;hb=e90a4e475a8b34adbefe189c9d0932fa0b7b750f;hp=39a90a6f0f803110964260d02a167c1256a6394e;hpb=939ab20152390c8ccccfa6fac0830405ca91d903;p=linux-2.6-omap-h63xx.git diff --git a/drivers/power/apm_power.c b/drivers/power/apm_power.c index 39a90a6f0f8..a4892275659 100644 --- a/drivers/power/apm_power.c +++ b/drivers/power/apm_power.c @@ -13,6 +13,7 @@ #include #include + #define PSY_PROP(psy, prop, val) psy->get_property(psy, \ POWER_SUPPLY_PROP_##prop, val) @@ -21,73 +22,194 @@ #define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val) +static DEFINE_MUTEX(apm_mutex); static struct power_supply *main_battery; +enum apm_source { + SOURCE_ENERGY, + SOURCE_CHARGE, + SOURCE_VOLTAGE, +}; + +struct find_bat_param { + struct power_supply *main; + struct power_supply *bat; + struct power_supply *max_charge_bat; + struct power_supply *max_energy_bat; + union power_supply_propval full; + int max_charge; + int max_energy; +}; + +static int __find_main_battery(struct device *dev, void *data) +{ + struct find_bat_param *bp = (struct find_bat_param *)data; + + bp->bat = dev_get_drvdata(dev); + + if (bp->bat->use_for_apm) { + /* nice, we explicitly asked to report this battery. */ + bp->main = bp->bat; + return 1; + } + + if (!PSY_PROP(bp->bat, CHARGE_FULL_DESIGN, &bp->full) || + !PSY_PROP(bp->bat, CHARGE_FULL, &bp->full)) { + if (bp->full.intval > bp->max_charge) { + bp->max_charge_bat = bp->bat; + bp->max_charge = bp->full.intval; + } + } else if (!PSY_PROP(bp->bat, ENERGY_FULL_DESIGN, &bp->full) || + !PSY_PROP(bp->bat, ENERGY_FULL, &bp->full)) { + if (bp->full.intval > bp->max_energy) { + bp->max_energy_bat = bp->bat; + bp->max_energy = bp->full.intval; + } + } + return 0; +} + static void find_main_battery(void) { - struct device *dev; - struct power_supply *bat, *batm; - union power_supply_propval full; - int max_charge = 0; + struct find_bat_param bp; + int error; + memset(&bp, 0, sizeof(struct find_bat_param)); main_battery = NULL; - batm = NULL; - list_for_each_entry(dev, &power_supply_class->devices, node) { - bat = dev_get_drvdata(dev); - /* If none of battery devices cantains 'use_for_apm' flag, - choice one with maximum design charge */ - if (!PSY_PROP(bat, CHARGE_FULL_DESIGN, &full)) { - if (full.intval > max_charge) { - batm = bat; - max_charge = full.intval; - } - } + bp.main = main_battery; + + error = class_for_each_device(power_supply_class, &bp, + __find_main_battery); + if (error) { + main_battery = bp.main; + return; + } - if (bat->use_for_apm) - main_battery = bat; + if ((bp.max_energy_bat && bp.max_charge_bat) && + (bp.max_energy_bat != bp.max_charge_bat)) { + /* try guess battery with more capacity */ + if (!PSY_PROP(bp.max_charge_bat, VOLTAGE_MAX_DESIGN, + &bp.full)) { + if (bp.max_energy > bp.max_charge * bp.full.intval) + main_battery = bp.max_energy_bat; + else + main_battery = bp.max_charge_bat; + } else if (!PSY_PROP(bp.max_energy_bat, VOLTAGE_MAX_DESIGN, + &bp.full)) { + if (bp.max_charge > bp.max_energy / bp.full.intval) + main_battery = bp.max_charge_bat; + else + main_battery = bp.max_energy_bat; + } else { + /* give up, choice any */ + main_battery = bp.max_energy_bat; + } + } else if (bp.max_charge_bat) { + main_battery = bp.max_charge_bat; + } else if (bp.max_energy_bat) { + main_battery = bp.max_energy_bat; + } else { + /* give up, try the last if any */ + main_battery = bp.bat; } - if (!main_battery) - main_battery = batm; } -static int calculate_time(int status) +static int do_calculate_time(int status, enum apm_source source) { - union power_supply_propval charge_full, charge_empty; - union power_supply_propval charge, I; + union power_supply_propval full; + union power_supply_propval empty; + union power_supply_propval cur; + union power_supply_propval I; + enum power_supply_property full_prop; + enum power_supply_property full_design_prop; + enum power_supply_property empty_prop; + enum power_supply_property empty_design_prop; + enum power_supply_property cur_avg_prop; + enum power_supply_property cur_now_prop; - if (MPSY_PROP(CHARGE_FULL, &charge_full)) { - /* if battery can't report this property, use design value */ - if (MPSY_PROP(CHARGE_FULL_DESIGN, &charge_full)) + if (MPSY_PROP(CURRENT_AVG, &I)) { + /* if battery can't report average value, use momentary */ + if (MPSY_PROP(CURRENT_NOW, &I)) return -1; } - if (MPSY_PROP(CHARGE_EMPTY, &charge_empty)) { - /* if battery can't report this property, use design value */ - if (MPSY_PROP(CHARGE_EMPTY_DESIGN, &charge_empty)) - charge_empty.intval = 0; + if (!I.intval) + return 0; + + switch (source) { + case SOURCE_CHARGE: + full_prop = POWER_SUPPLY_PROP_CHARGE_FULL; + full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN; + empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY; + empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY; + cur_avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG; + cur_now_prop = POWER_SUPPLY_PROP_CHARGE_NOW; + break; + case SOURCE_ENERGY: + full_prop = POWER_SUPPLY_PROP_ENERGY_FULL; + full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN; + empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY; + empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY; + cur_avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG; + cur_now_prop = POWER_SUPPLY_PROP_ENERGY_NOW; + break; + case SOURCE_VOLTAGE: + full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX; + full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN; + empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN; + empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN; + cur_avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG; + cur_now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW; + break; + default: + printk(KERN_ERR "Unsupported source: %d\n", source); + return -1; } - if (MPSY_PROP(CHARGE_AVG, &charge)) { - /* if battery can't report average value, use momentary */ - if (MPSY_PROP(CHARGE_NOW, &charge)) + if (_MPSY_PROP(full_prop, &full)) { + /* if battery can't report this property, use design value */ + if (_MPSY_PROP(full_design_prop, &full)) return -1; } - if (MPSY_PROP(CURRENT_AVG, &I)) { + if (_MPSY_PROP(empty_prop, &empty)) { + /* if battery can't report this property, use design value */ + if (_MPSY_PROP(empty_design_prop, &empty)) + empty.intval = 0; + } + + if (_MPSY_PROP(cur_avg_prop, &cur)) { /* if battery can't report average value, use momentary */ - if (MPSY_PROP(CURRENT_NOW, &I)) + if (_MPSY_PROP(cur_now_prop, &cur)) return -1; } if (status == POWER_SUPPLY_STATUS_CHARGING) - return ((charge.intval - charge_full.intval) * 60L) / - I.intval; + return ((cur.intval - full.intval) * 60L) / I.intval; else - return -((charge.intval - charge_empty.intval) * 60L) / - I.intval; + return -((cur.intval - empty.intval) * 60L) / I.intval; } -static int calculate_capacity(int using_charge) +static int calculate_time(int status) +{ + int time; + + time = do_calculate_time(status, SOURCE_ENERGY); + if (time != -1) + return time; + + time = do_calculate_time(status, SOURCE_CHARGE); + if (time != -1) + return time; + + time = do_calculate_time(status, SOURCE_VOLTAGE); + if (time != -1) + return time; + + return -1; +} + +static int calculate_capacity(enum apm_source source) { enum power_supply_property full_prop, empty_prop; enum power_supply_property full_design_prop, empty_design_prop; @@ -95,20 +217,33 @@ static int calculate_capacity(int using_charge) union power_supply_propval empty, full, cur; int ret; - if (using_charge) { + switch (source) { + case SOURCE_CHARGE: full_prop = POWER_SUPPLY_PROP_CHARGE_FULL; empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY; full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN; empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN; now_prop = POWER_SUPPLY_PROP_CHARGE_NOW; avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG; - } else { + break; + case SOURCE_ENERGY: full_prop = POWER_SUPPLY_PROP_ENERGY_FULL; empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY; full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN; empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN; now_prop = POWER_SUPPLY_PROP_ENERGY_NOW; avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG; + case SOURCE_VOLTAGE: + full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX; + empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN; + full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN; + empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN; + now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW; + avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG; + break; + default: + printk(KERN_ERR "Unsupported source: %d\n", source); + return -1; } if (_MPSY_PROP(full_prop, &full)) { @@ -148,10 +283,10 @@ static void apm_battery_apm_get_power_status(struct apm_power_info *info) union power_supply_propval status; union power_supply_propval capacity, time_to_full, time_to_empty; - down(&power_supply_class->sem); + mutex_lock(&apm_mutex); find_main_battery(); if (!main_battery) { - up(&power_supply_class->sem); + mutex_unlock(&apm_mutex); return; } @@ -175,10 +310,12 @@ static void apm_battery_apm_get_power_status(struct apm_power_info *info) info->battery_life = capacity.intval; } else { /* try calculate using energy */ - info->battery_life = calculate_capacity(0); + info->battery_life = calculate_capacity(SOURCE_ENERGY); /* if failed try calculate using charge instead */ if (info->battery_life == -1) - info->battery_life = calculate_capacity(1); + info->battery_life = calculate_capacity(SOURCE_CHARGE); + if (info->battery_life == -1) + info->battery_life = calculate_capacity(SOURCE_VOLTAGE); } /* charging status */ @@ -200,22 +337,20 @@ static void apm_battery_apm_get_power_status(struct apm_power_info *info) info->units = APM_UNITS_MINS; if (status.intval == POWER_SUPPLY_STATUS_CHARGING) { - if (MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full)) { - if (MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full)) - info->time = calculate_time(status.intval); - else - info->time = time_to_full.intval / 60; - } + if (!MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full) || + !MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full)) + info->time = time_to_full.intval / 60; + else + info->time = calculate_time(status.intval); } else { - if (MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty)) { - if (MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty)) - info->time = calculate_time(status.intval); - else - info->time = time_to_empty.intval / 60; - } + if (!MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty) || + !MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty)) + info->time = time_to_empty.intval / 60; + else + info->time = calculate_time(status.intval); } - up(&power_supply_class->sem); + mutex_unlock(&apm_mutex); } static int __init apm_battery_init(void)