apm_power: improve battery finding algorithm
[linux-2.6/x86.git] / drivers / power / apm_power.c
blob3928e7cdddc13331c5176491df1f7891896cc4fb
1 /*
2 * Copyright © 2007 Anton Vorontsov <cbou@mail.ru>
3 * Copyright © 2007 Eugeny Boger <eugenyboger@dgap.mipt.ru>
5 * Author: Eugeny Boger <eugenyboger@dgap.mipt.ru>
7 * Use consistent with the GNU GPL is permitted,
8 * provided that this copyright notice is
9 * preserved in its entirety in all copies and derived works.
12 #include <linux/module.h>
13 #include <linux/power_supply.h>
14 #include <linux/apm-emulation.h>
16 #define PSY_PROP(psy, prop, val) psy->get_property(psy, \
17 POWER_SUPPLY_PROP_##prop, val)
19 #define _MPSY_PROP(prop, val) main_battery->get_property(main_battery, \
20 prop, val)
22 #define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val)
24 static struct power_supply *main_battery;
26 static void find_main_battery(void)
28 struct device *dev;
29 struct power_supply *bat = NULL;
30 struct power_supply *max_charge_bat = NULL;
31 struct power_supply *max_energy_bat = NULL;
32 union power_supply_propval full;
33 int max_charge = 0;
34 int max_energy = 0;
36 main_battery = NULL;
38 list_for_each_entry(dev, &power_supply_class->devices, node) {
39 bat = dev_get_drvdata(dev);
41 if (bat->use_for_apm) {
42 /* nice, we explicitly asked to report this battery. */
43 main_battery = bat;
44 return;
47 if (!PSY_PROP(bat, CHARGE_FULL_DESIGN, &full) ||
48 !PSY_PROP(bat, CHARGE_FULL, &full)) {
49 if (full.intval > max_charge) {
50 max_charge_bat = bat;
51 max_charge = full.intval;
53 } else if (!PSY_PROP(bat, ENERGY_FULL_DESIGN, &full) ||
54 !PSY_PROP(bat, ENERGY_FULL, &full)) {
55 if (full.intval > max_energy) {
56 max_energy_bat = bat;
57 max_energy = full.intval;
62 if ((max_energy_bat && max_charge_bat) &&
63 (max_energy_bat != max_charge_bat)) {
64 /* try guess battery with more capacity */
65 if (!PSY_PROP(max_charge_bat, VOLTAGE_MAX_DESIGN, &full)) {
66 if (max_energy > max_charge * full.intval)
67 main_battery = max_energy_bat;
68 else
69 main_battery = max_charge_bat;
70 } else if (!PSY_PROP(max_energy_bat, VOLTAGE_MAX_DESIGN,
71 &full)) {
72 if (max_charge > max_energy / full.intval)
73 main_battery = max_charge_bat;
74 else
75 main_battery = max_energy_bat;
76 } else {
77 /* give up, choice any */
78 main_battery = max_energy_bat;
80 } else if (max_charge_bat) {
81 main_battery = max_charge_bat;
82 } else if (max_energy_bat) {
83 main_battery = max_energy_bat;
84 } else {
85 /* give up, try the last if any */
86 main_battery = bat;
90 static int calculate_time(int status)
92 union power_supply_propval charge_full, charge_empty;
93 union power_supply_propval charge, I;
95 if (MPSY_PROP(CHARGE_FULL, &charge_full)) {
96 /* if battery can't report this property, use design value */
97 if (MPSY_PROP(CHARGE_FULL_DESIGN, &charge_full))
98 return -1;
101 if (MPSY_PROP(CHARGE_EMPTY, &charge_empty)) {
102 /* if battery can't report this property, use design value */
103 if (MPSY_PROP(CHARGE_EMPTY_DESIGN, &charge_empty))
104 charge_empty.intval = 0;
107 if (MPSY_PROP(CHARGE_AVG, &charge)) {
108 /* if battery can't report average value, use momentary */
109 if (MPSY_PROP(CHARGE_NOW, &charge))
110 return -1;
113 if (MPSY_PROP(CURRENT_AVG, &I)) {
114 /* if battery can't report average value, use momentary */
115 if (MPSY_PROP(CURRENT_NOW, &I))
116 return -1;
119 if (status == POWER_SUPPLY_STATUS_CHARGING)
120 return ((charge.intval - charge_full.intval) * 60L) /
121 I.intval;
122 else
123 return -((charge.intval - charge_empty.intval) * 60L) /
124 I.intval;
127 static int calculate_capacity(int using_charge)
129 enum power_supply_property full_prop, empty_prop;
130 enum power_supply_property full_design_prop, empty_design_prop;
131 enum power_supply_property now_prop, avg_prop;
132 union power_supply_propval empty, full, cur;
133 int ret;
135 if (using_charge) {
136 full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
137 empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
138 full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
139 empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN;
140 now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
141 avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
142 } else {
143 full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
144 empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
145 full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
146 empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN;
147 now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
148 avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
151 if (_MPSY_PROP(full_prop, &full)) {
152 /* if battery can't report this property, use design value */
153 if (_MPSY_PROP(full_design_prop, &full))
154 return -1;
157 if (_MPSY_PROP(avg_prop, &cur)) {
158 /* if battery can't report average value, use momentary */
159 if (_MPSY_PROP(now_prop, &cur))
160 return -1;
163 if (_MPSY_PROP(empty_prop, &empty)) {
164 /* if battery can't report this property, use design value */
165 if (_MPSY_PROP(empty_design_prop, &empty))
166 empty.intval = 0;
169 if (full.intval - empty.intval)
170 ret = ((cur.intval - empty.intval) * 100L) /
171 (full.intval - empty.intval);
172 else
173 return -1;
175 if (ret > 100)
176 return 100;
177 else if (ret < 0)
178 return 0;
180 return ret;
183 static void apm_battery_apm_get_power_status(struct apm_power_info *info)
185 union power_supply_propval status;
186 union power_supply_propval capacity, time_to_full, time_to_empty;
188 down(&power_supply_class->sem);
189 find_main_battery();
190 if (!main_battery) {
191 up(&power_supply_class->sem);
192 return;
195 /* status */
197 if (MPSY_PROP(STATUS, &status))
198 status.intval = POWER_SUPPLY_STATUS_UNKNOWN;
200 /* ac line status */
202 if ((status.intval == POWER_SUPPLY_STATUS_CHARGING) ||
203 (status.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) ||
204 (status.intval == POWER_SUPPLY_STATUS_FULL))
205 info->ac_line_status = APM_AC_ONLINE;
206 else
207 info->ac_line_status = APM_AC_OFFLINE;
209 /* battery life (i.e. capacity, in percents) */
211 if (MPSY_PROP(CAPACITY, &capacity) == 0) {
212 info->battery_life = capacity.intval;
213 } else {
214 /* try calculate using energy */
215 info->battery_life = calculate_capacity(0);
216 /* if failed try calculate using charge instead */
217 if (info->battery_life == -1)
218 info->battery_life = calculate_capacity(1);
221 /* charging status */
223 if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
224 info->battery_status = APM_BATTERY_STATUS_CHARGING;
225 } else {
226 if (info->battery_life > 50)
227 info->battery_status = APM_BATTERY_STATUS_HIGH;
228 else if (info->battery_life > 5)
229 info->battery_status = APM_BATTERY_STATUS_LOW;
230 else
231 info->battery_status = APM_BATTERY_STATUS_CRITICAL;
233 info->battery_flag = info->battery_status;
235 /* time */
237 info->units = APM_UNITS_MINS;
239 if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
240 if (!MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full) ||
241 !MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full))
242 info->time = time_to_full.intval / 60;
243 else
244 info->time = calculate_time(status.intval);
245 } else {
246 if (!MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty) ||
247 !MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty))
248 info->time = time_to_empty.intval / 60;
249 else
250 info->time = calculate_time(status.intval);
253 up(&power_supply_class->sem);
256 static int __init apm_battery_init(void)
258 printk(KERN_INFO "APM Battery Driver\n");
260 apm_get_power_status = apm_battery_apm_get_power_status;
261 return 0;
264 static void __exit apm_battery_exit(void)
266 apm_get_power_status = NULL;
269 module_init(apm_battery_init);
270 module_exit(apm_battery_exit);
272 MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>");
273 MODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
274 MODULE_LICENSE("GPL");