apm_power: calculate to_full/to_empty time using energy
[linux-2.6/kmemtrace.git] / drivers / power / apm_power.c
blobbbf3ee10da04ad433027963d9831379827cb715f
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, int using_charge)
92 union power_supply_propval full;
93 union power_supply_propval empty;
94 union power_supply_propval cur;
95 union power_supply_propval I;
96 enum power_supply_property full_prop;
97 enum power_supply_property full_design_prop;
98 enum power_supply_property empty_prop;
99 enum power_supply_property empty_design_prop;
100 enum power_supply_property cur_avg_prop;
101 enum power_supply_property cur_now_prop;
103 if (MPSY_PROP(CURRENT_AVG, &I)) {
104 /* if battery can't report average value, use momentary */
105 if (MPSY_PROP(CURRENT_NOW, &I))
106 return -1;
109 if (using_charge) {
110 full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
111 full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
112 empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
113 empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
114 cur_avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
115 cur_now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
116 } else {
117 full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
118 full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
119 empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
120 empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
121 cur_avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
122 cur_now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
125 if (_MPSY_PROP(full_prop, &full)) {
126 /* if battery can't report this property, use design value */
127 if (_MPSY_PROP(full_design_prop, &full))
128 return -1;
131 if (_MPSY_PROP(empty_prop, &empty)) {
132 /* if battery can't report this property, use design value */
133 if (_MPSY_PROP(empty_design_prop, &empty))
134 empty.intval = 0;
137 if (_MPSY_PROP(cur_avg_prop, &cur)) {
138 /* if battery can't report average value, use momentary */
139 if (_MPSY_PROP(cur_now_prop, &cur))
140 return -1;
143 if (status == POWER_SUPPLY_STATUS_CHARGING)
144 return ((cur.intval - full.intval) * 60L) / I.intval;
145 else
146 return -((cur.intval - empty.intval) * 60L) / I.intval;
149 static int calculate_capacity(int using_charge)
151 enum power_supply_property full_prop, empty_prop;
152 enum power_supply_property full_design_prop, empty_design_prop;
153 enum power_supply_property now_prop, avg_prop;
154 union power_supply_propval empty, full, cur;
155 int ret;
157 if (using_charge) {
158 full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
159 empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
160 full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
161 empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN;
162 now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
163 avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
164 } else {
165 full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
166 empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
167 full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
168 empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN;
169 now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
170 avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
173 if (_MPSY_PROP(full_prop, &full)) {
174 /* if battery can't report this property, use design value */
175 if (_MPSY_PROP(full_design_prop, &full))
176 return -1;
179 if (_MPSY_PROP(avg_prop, &cur)) {
180 /* if battery can't report average value, use momentary */
181 if (_MPSY_PROP(now_prop, &cur))
182 return -1;
185 if (_MPSY_PROP(empty_prop, &empty)) {
186 /* if battery can't report this property, use design value */
187 if (_MPSY_PROP(empty_design_prop, &empty))
188 empty.intval = 0;
191 if (full.intval - empty.intval)
192 ret = ((cur.intval - empty.intval) * 100L) /
193 (full.intval - empty.intval);
194 else
195 return -1;
197 if (ret > 100)
198 return 100;
199 else if (ret < 0)
200 return 0;
202 return ret;
205 static void apm_battery_apm_get_power_status(struct apm_power_info *info)
207 union power_supply_propval status;
208 union power_supply_propval capacity, time_to_full, time_to_empty;
210 down(&power_supply_class->sem);
211 find_main_battery();
212 if (!main_battery) {
213 up(&power_supply_class->sem);
214 return;
217 /* status */
219 if (MPSY_PROP(STATUS, &status))
220 status.intval = POWER_SUPPLY_STATUS_UNKNOWN;
222 /* ac line status */
224 if ((status.intval == POWER_SUPPLY_STATUS_CHARGING) ||
225 (status.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) ||
226 (status.intval == POWER_SUPPLY_STATUS_FULL))
227 info->ac_line_status = APM_AC_ONLINE;
228 else
229 info->ac_line_status = APM_AC_OFFLINE;
231 /* battery life (i.e. capacity, in percents) */
233 if (MPSY_PROP(CAPACITY, &capacity) == 0) {
234 info->battery_life = capacity.intval;
235 } else {
236 /* try calculate using energy */
237 info->battery_life = calculate_capacity(0);
238 /* if failed try calculate using charge instead */
239 if (info->battery_life == -1)
240 info->battery_life = calculate_capacity(1);
243 /* charging status */
245 if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
246 info->battery_status = APM_BATTERY_STATUS_CHARGING;
247 } else {
248 if (info->battery_life > 50)
249 info->battery_status = APM_BATTERY_STATUS_HIGH;
250 else if (info->battery_life > 5)
251 info->battery_status = APM_BATTERY_STATUS_LOW;
252 else
253 info->battery_status = APM_BATTERY_STATUS_CRITICAL;
255 info->battery_flag = info->battery_status;
257 /* time */
259 info->units = APM_UNITS_MINS;
261 if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
262 if (!MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full) ||
263 !MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full)) {
264 info->time = time_to_full.intval / 60;
265 } else {
266 info->time = calculate_time(status.intval, 0);
267 if (info->time == -1)
268 info->time = calculate_time(status.intval, 1);
270 } else {
271 if (!MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty) ||
272 !MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty)) {
273 info->time = time_to_empty.intval / 60;
274 } else {
275 info->time = calculate_time(status.intval, 0);
276 if (info->time == -1)
277 info->time = calculate_time(status.intval, 1);
281 up(&power_supply_class->sem);
284 static int __init apm_battery_init(void)
286 printk(KERN_INFO "APM Battery Driver\n");
288 apm_get_power_status = apm_battery_apm_get_power_status;
289 return 0;
292 static void __exit apm_battery_exit(void)
294 apm_get_power_status = NULL;
297 module_init(apm_battery_init);
298 module_exit(apm_battery_exit);
300 MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>");
301 MODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
302 MODULE_LICENSE("GPL");