xref: /openbmc/linux/drivers/power/supply/apm_power.c (revision 0cce284537fb42d9c28b9b31038ffc9b464555f5)
1*8c0984e5SSebastian Reichel /*
2*8c0984e5SSebastian Reichel  * Copyright © 2007 Anton Vorontsov <cbou@mail.ru>
3*8c0984e5SSebastian Reichel  * Copyright © 2007 Eugeny Boger <eugenyboger@dgap.mipt.ru>
4*8c0984e5SSebastian Reichel  *
5*8c0984e5SSebastian Reichel  * Author: Eugeny Boger <eugenyboger@dgap.mipt.ru>
6*8c0984e5SSebastian Reichel  *
7*8c0984e5SSebastian Reichel  * Use consistent with the GNU GPL is permitted,
8*8c0984e5SSebastian Reichel  * provided that this copyright notice is
9*8c0984e5SSebastian Reichel  * preserved in its entirety in all copies and derived works.
10*8c0984e5SSebastian Reichel  */
11*8c0984e5SSebastian Reichel 
12*8c0984e5SSebastian Reichel #include <linux/module.h>
13*8c0984e5SSebastian Reichel #include <linux/device.h>
14*8c0984e5SSebastian Reichel #include <linux/power_supply.h>
15*8c0984e5SSebastian Reichel #include <linux/apm-emulation.h>
16*8c0984e5SSebastian Reichel 
17*8c0984e5SSebastian Reichel 
18*8c0984e5SSebastian Reichel #define PSY_PROP(psy, prop, val) (power_supply_get_property(psy, \
19*8c0984e5SSebastian Reichel 			 POWER_SUPPLY_PROP_##prop, val))
20*8c0984e5SSebastian Reichel 
21*8c0984e5SSebastian Reichel #define _MPSY_PROP(prop, val) (power_supply_get_property(main_battery, \
22*8c0984e5SSebastian Reichel 							 prop, val))
23*8c0984e5SSebastian Reichel 
24*8c0984e5SSebastian Reichel #define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val)
25*8c0984e5SSebastian Reichel 
26*8c0984e5SSebastian Reichel static DEFINE_MUTEX(apm_mutex);
27*8c0984e5SSebastian Reichel static struct power_supply *main_battery;
28*8c0984e5SSebastian Reichel 
29*8c0984e5SSebastian Reichel enum apm_source {
30*8c0984e5SSebastian Reichel 	SOURCE_ENERGY,
31*8c0984e5SSebastian Reichel 	SOURCE_CHARGE,
32*8c0984e5SSebastian Reichel 	SOURCE_VOLTAGE,
33*8c0984e5SSebastian Reichel };
34*8c0984e5SSebastian Reichel 
35*8c0984e5SSebastian Reichel struct find_bat_param {
36*8c0984e5SSebastian Reichel 	struct power_supply *main;
37*8c0984e5SSebastian Reichel 	struct power_supply *bat;
38*8c0984e5SSebastian Reichel 	struct power_supply *max_charge_bat;
39*8c0984e5SSebastian Reichel 	struct power_supply *max_energy_bat;
40*8c0984e5SSebastian Reichel 	union power_supply_propval full;
41*8c0984e5SSebastian Reichel 	int max_charge;
42*8c0984e5SSebastian Reichel 	int max_energy;
43*8c0984e5SSebastian Reichel };
44*8c0984e5SSebastian Reichel 
__find_main_battery(struct device * dev,void * data)45*8c0984e5SSebastian Reichel static int __find_main_battery(struct device *dev, void *data)
46*8c0984e5SSebastian Reichel {
47*8c0984e5SSebastian Reichel 	struct find_bat_param *bp = (struct find_bat_param *)data;
48*8c0984e5SSebastian Reichel 
49*8c0984e5SSebastian Reichel 	bp->bat = dev_get_drvdata(dev);
50*8c0984e5SSebastian Reichel 
51*8c0984e5SSebastian Reichel 	if (bp->bat->desc->use_for_apm) {
52*8c0984e5SSebastian Reichel 		/* nice, we explicitly asked to report this battery. */
53*8c0984e5SSebastian Reichel 		bp->main = bp->bat;
54*8c0984e5SSebastian Reichel 		return 1;
55*8c0984e5SSebastian Reichel 	}
56*8c0984e5SSebastian Reichel 
57*8c0984e5SSebastian Reichel 	if (!PSY_PROP(bp->bat, CHARGE_FULL_DESIGN, &bp->full) ||
58*8c0984e5SSebastian Reichel 			!PSY_PROP(bp->bat, CHARGE_FULL, &bp->full)) {
59*8c0984e5SSebastian Reichel 		if (bp->full.intval > bp->max_charge) {
60*8c0984e5SSebastian Reichel 			bp->max_charge_bat = bp->bat;
61*8c0984e5SSebastian Reichel 			bp->max_charge = bp->full.intval;
62*8c0984e5SSebastian Reichel 		}
63*8c0984e5SSebastian Reichel 	} else if (!PSY_PROP(bp->bat, ENERGY_FULL_DESIGN, &bp->full) ||
64*8c0984e5SSebastian Reichel 			!PSY_PROP(bp->bat, ENERGY_FULL, &bp->full)) {
65*8c0984e5SSebastian Reichel 		if (bp->full.intval > bp->max_energy) {
66*8c0984e5SSebastian Reichel 			bp->max_energy_bat = bp->bat;
67*8c0984e5SSebastian Reichel 			bp->max_energy = bp->full.intval;
68*8c0984e5SSebastian Reichel 		}
69*8c0984e5SSebastian Reichel 	}
70*8c0984e5SSebastian Reichel 	return 0;
71*8c0984e5SSebastian Reichel }
72*8c0984e5SSebastian Reichel 
find_main_battery(void)73*8c0984e5SSebastian Reichel static void find_main_battery(void)
74*8c0984e5SSebastian Reichel {
75*8c0984e5SSebastian Reichel 	struct find_bat_param bp;
76*8c0984e5SSebastian Reichel 	int error;
77*8c0984e5SSebastian Reichel 
78*8c0984e5SSebastian Reichel 	memset(&bp, 0, sizeof(struct find_bat_param));
79*8c0984e5SSebastian Reichel 	main_battery = NULL;
80*8c0984e5SSebastian Reichel 	bp.main = main_battery;
81*8c0984e5SSebastian Reichel 
82*8c0984e5SSebastian Reichel 	error = class_for_each_device(power_supply_class, NULL, &bp,
83*8c0984e5SSebastian Reichel 				      __find_main_battery);
84*8c0984e5SSebastian Reichel 	if (error) {
85*8c0984e5SSebastian Reichel 		main_battery = bp.main;
86*8c0984e5SSebastian Reichel 		return;
87*8c0984e5SSebastian Reichel 	}
88*8c0984e5SSebastian Reichel 
89*8c0984e5SSebastian Reichel 	if ((bp.max_energy_bat && bp.max_charge_bat) &&
90*8c0984e5SSebastian Reichel 			(bp.max_energy_bat != bp.max_charge_bat)) {
91*8c0984e5SSebastian Reichel 		/* try guess battery with more capacity */
92*8c0984e5SSebastian Reichel 		if (!PSY_PROP(bp.max_charge_bat, VOLTAGE_MAX_DESIGN,
93*8c0984e5SSebastian Reichel 			      &bp.full)) {
94*8c0984e5SSebastian Reichel 			if (bp.max_energy > bp.max_charge * bp.full.intval)
95*8c0984e5SSebastian Reichel 				main_battery = bp.max_energy_bat;
96*8c0984e5SSebastian Reichel 			else
97*8c0984e5SSebastian Reichel 				main_battery = bp.max_charge_bat;
98*8c0984e5SSebastian Reichel 		} else if (!PSY_PROP(bp.max_energy_bat, VOLTAGE_MAX_DESIGN,
99*8c0984e5SSebastian Reichel 								  &bp.full)) {
100*8c0984e5SSebastian Reichel 			if (bp.max_charge > bp.max_energy / bp.full.intval)
101*8c0984e5SSebastian Reichel 				main_battery = bp.max_charge_bat;
102*8c0984e5SSebastian Reichel 			else
103*8c0984e5SSebastian Reichel 				main_battery = bp.max_energy_bat;
104*8c0984e5SSebastian Reichel 		} else {
105*8c0984e5SSebastian Reichel 			/* give up, choice any */
106*8c0984e5SSebastian Reichel 			main_battery = bp.max_energy_bat;
107*8c0984e5SSebastian Reichel 		}
108*8c0984e5SSebastian Reichel 	} else if (bp.max_charge_bat) {
109*8c0984e5SSebastian Reichel 		main_battery = bp.max_charge_bat;
110*8c0984e5SSebastian Reichel 	} else if (bp.max_energy_bat) {
111*8c0984e5SSebastian Reichel 		main_battery = bp.max_energy_bat;
112*8c0984e5SSebastian Reichel 	} else {
113*8c0984e5SSebastian Reichel 		/* give up, try the last if any */
114*8c0984e5SSebastian Reichel 		main_battery = bp.bat;
115*8c0984e5SSebastian Reichel 	}
116*8c0984e5SSebastian Reichel }
117*8c0984e5SSebastian Reichel 
do_calculate_time(int status,enum apm_source source)118*8c0984e5SSebastian Reichel static int do_calculate_time(int status, enum apm_source source)
119*8c0984e5SSebastian Reichel {
120*8c0984e5SSebastian Reichel 	union power_supply_propval full;
121*8c0984e5SSebastian Reichel 	union power_supply_propval empty;
122*8c0984e5SSebastian Reichel 	union power_supply_propval cur;
123*8c0984e5SSebastian Reichel 	union power_supply_propval I;
124*8c0984e5SSebastian Reichel 	enum power_supply_property full_prop;
125*8c0984e5SSebastian Reichel 	enum power_supply_property full_design_prop;
126*8c0984e5SSebastian Reichel 	enum power_supply_property empty_prop;
127*8c0984e5SSebastian Reichel 	enum power_supply_property empty_design_prop;
128*8c0984e5SSebastian Reichel 	enum power_supply_property cur_avg_prop;
129*8c0984e5SSebastian Reichel 	enum power_supply_property cur_now_prop;
130*8c0984e5SSebastian Reichel 
131*8c0984e5SSebastian Reichel 	if (MPSY_PROP(CURRENT_AVG, &I)) {
132*8c0984e5SSebastian Reichel 		/* if battery can't report average value, use momentary */
133*8c0984e5SSebastian Reichel 		if (MPSY_PROP(CURRENT_NOW, &I))
134*8c0984e5SSebastian Reichel 			return -1;
135*8c0984e5SSebastian Reichel 	}
136*8c0984e5SSebastian Reichel 
137*8c0984e5SSebastian Reichel 	if (!I.intval)
138*8c0984e5SSebastian Reichel 		return 0;
139*8c0984e5SSebastian Reichel 
140*8c0984e5SSebastian Reichel 	switch (source) {
141*8c0984e5SSebastian Reichel 	case SOURCE_CHARGE:
142*8c0984e5SSebastian Reichel 		full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
143*8c0984e5SSebastian Reichel 		full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
144*8c0984e5SSebastian Reichel 		empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
145*8c0984e5SSebastian Reichel 		empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
146*8c0984e5SSebastian Reichel 		cur_avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
147*8c0984e5SSebastian Reichel 		cur_now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
148*8c0984e5SSebastian Reichel 		break;
149*8c0984e5SSebastian Reichel 	case SOURCE_ENERGY:
150*8c0984e5SSebastian Reichel 		full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
151*8c0984e5SSebastian Reichel 		full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
152*8c0984e5SSebastian Reichel 		empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
153*8c0984e5SSebastian Reichel 		empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
154*8c0984e5SSebastian Reichel 		cur_avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
155*8c0984e5SSebastian Reichel 		cur_now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
156*8c0984e5SSebastian Reichel 		break;
157*8c0984e5SSebastian Reichel 	case SOURCE_VOLTAGE:
158*8c0984e5SSebastian Reichel 		full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
159*8c0984e5SSebastian Reichel 		full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
160*8c0984e5SSebastian Reichel 		empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
161*8c0984e5SSebastian Reichel 		empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
162*8c0984e5SSebastian Reichel 		cur_avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
163*8c0984e5SSebastian Reichel 		cur_now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
164*8c0984e5SSebastian Reichel 		break;
165*8c0984e5SSebastian Reichel 	default:
166*8c0984e5SSebastian Reichel 		printk(KERN_ERR "Unsupported source: %d\n", source);
167*8c0984e5SSebastian Reichel 		return -1;
168*8c0984e5SSebastian Reichel 	}
169*8c0984e5SSebastian Reichel 
170*8c0984e5SSebastian Reichel 	if (_MPSY_PROP(full_prop, &full)) {
171*8c0984e5SSebastian Reichel 		/* if battery can't report this property, use design value */
172*8c0984e5SSebastian Reichel 		if (_MPSY_PROP(full_design_prop, &full))
173*8c0984e5SSebastian Reichel 			return -1;
174*8c0984e5SSebastian Reichel 	}
175*8c0984e5SSebastian Reichel 
176*8c0984e5SSebastian Reichel 	if (_MPSY_PROP(empty_prop, &empty)) {
177*8c0984e5SSebastian Reichel 		/* if battery can't report this property, use design value */
178*8c0984e5SSebastian Reichel 		if (_MPSY_PROP(empty_design_prop, &empty))
179*8c0984e5SSebastian Reichel 			empty.intval = 0;
180*8c0984e5SSebastian Reichel 	}
181*8c0984e5SSebastian Reichel 
182*8c0984e5SSebastian Reichel 	if (_MPSY_PROP(cur_avg_prop, &cur)) {
183*8c0984e5SSebastian Reichel 		/* if battery can't report average value, use momentary */
184*8c0984e5SSebastian Reichel 		if (_MPSY_PROP(cur_now_prop, &cur))
185*8c0984e5SSebastian Reichel 			return -1;
186*8c0984e5SSebastian Reichel 	}
187*8c0984e5SSebastian Reichel 
188*8c0984e5SSebastian Reichel 	if (status == POWER_SUPPLY_STATUS_CHARGING)
189*8c0984e5SSebastian Reichel 		return ((cur.intval - full.intval) * 60L) / I.intval;
190*8c0984e5SSebastian Reichel 	else
191*8c0984e5SSebastian Reichel 		return -((cur.intval - empty.intval) * 60L) / I.intval;
192*8c0984e5SSebastian Reichel }
193*8c0984e5SSebastian Reichel 
calculate_time(int status)194*8c0984e5SSebastian Reichel static int calculate_time(int status)
195*8c0984e5SSebastian Reichel {
196*8c0984e5SSebastian Reichel 	int time;
197*8c0984e5SSebastian Reichel 
198*8c0984e5SSebastian Reichel 	time = do_calculate_time(status, SOURCE_ENERGY);
199*8c0984e5SSebastian Reichel 	if (time != -1)
200*8c0984e5SSebastian Reichel 		return time;
201*8c0984e5SSebastian Reichel 
202*8c0984e5SSebastian Reichel 	time = do_calculate_time(status, SOURCE_CHARGE);
203*8c0984e5SSebastian Reichel 	if (time != -1)
204*8c0984e5SSebastian Reichel 		return time;
205*8c0984e5SSebastian Reichel 
206*8c0984e5SSebastian Reichel 	time = do_calculate_time(status, SOURCE_VOLTAGE);
207*8c0984e5SSebastian Reichel 	if (time != -1)
208*8c0984e5SSebastian Reichel 		return time;
209*8c0984e5SSebastian Reichel 
210*8c0984e5SSebastian Reichel 	return -1;
211*8c0984e5SSebastian Reichel }
212*8c0984e5SSebastian Reichel 
calculate_capacity(enum apm_source source)213*8c0984e5SSebastian Reichel static int calculate_capacity(enum apm_source source)
214*8c0984e5SSebastian Reichel {
215*8c0984e5SSebastian Reichel 	enum power_supply_property full_prop, empty_prop;
216*8c0984e5SSebastian Reichel 	enum power_supply_property full_design_prop, empty_design_prop;
217*8c0984e5SSebastian Reichel 	enum power_supply_property now_prop, avg_prop;
218*8c0984e5SSebastian Reichel 	union power_supply_propval empty, full, cur;
219*8c0984e5SSebastian Reichel 	int ret;
220*8c0984e5SSebastian Reichel 
221*8c0984e5SSebastian Reichel 	switch (source) {
222*8c0984e5SSebastian Reichel 	case SOURCE_CHARGE:
223*8c0984e5SSebastian Reichel 		full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
224*8c0984e5SSebastian Reichel 		empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
225*8c0984e5SSebastian Reichel 		full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
226*8c0984e5SSebastian Reichel 		empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN;
227*8c0984e5SSebastian Reichel 		now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
228*8c0984e5SSebastian Reichel 		avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
229*8c0984e5SSebastian Reichel 		break;
230*8c0984e5SSebastian Reichel 	case SOURCE_ENERGY:
231*8c0984e5SSebastian Reichel 		full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
232*8c0984e5SSebastian Reichel 		empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
233*8c0984e5SSebastian Reichel 		full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
234*8c0984e5SSebastian Reichel 		empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN;
235*8c0984e5SSebastian Reichel 		now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
236*8c0984e5SSebastian Reichel 		avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
237*8c0984e5SSebastian Reichel 		break;
238*8c0984e5SSebastian Reichel 	case SOURCE_VOLTAGE:
239*8c0984e5SSebastian Reichel 		full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
240*8c0984e5SSebastian Reichel 		empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
241*8c0984e5SSebastian Reichel 		full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
242*8c0984e5SSebastian Reichel 		empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
243*8c0984e5SSebastian Reichel 		now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
244*8c0984e5SSebastian Reichel 		avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
245*8c0984e5SSebastian Reichel 		break;
246*8c0984e5SSebastian Reichel 	default:
247*8c0984e5SSebastian Reichel 		printk(KERN_ERR "Unsupported source: %d\n", source);
248*8c0984e5SSebastian Reichel 		return -1;
249*8c0984e5SSebastian Reichel 	}
250*8c0984e5SSebastian Reichel 
251*8c0984e5SSebastian Reichel 	if (_MPSY_PROP(full_prop, &full)) {
252*8c0984e5SSebastian Reichel 		/* if battery can't report this property, use design value */
253*8c0984e5SSebastian Reichel 		if (_MPSY_PROP(full_design_prop, &full))
254*8c0984e5SSebastian Reichel 			return -1;
255*8c0984e5SSebastian Reichel 	}
256*8c0984e5SSebastian Reichel 
257*8c0984e5SSebastian Reichel 	if (_MPSY_PROP(avg_prop, &cur)) {
258*8c0984e5SSebastian Reichel 		/* if battery can't report average value, use momentary */
259*8c0984e5SSebastian Reichel 		if (_MPSY_PROP(now_prop, &cur))
260*8c0984e5SSebastian Reichel 			return -1;
261*8c0984e5SSebastian Reichel 	}
262*8c0984e5SSebastian Reichel 
263*8c0984e5SSebastian Reichel 	if (_MPSY_PROP(empty_prop, &empty)) {
264*8c0984e5SSebastian Reichel 		/* if battery can't report this property, use design value */
265*8c0984e5SSebastian Reichel 		if (_MPSY_PROP(empty_design_prop, &empty))
266*8c0984e5SSebastian Reichel 			empty.intval = 0;
267*8c0984e5SSebastian Reichel 	}
268*8c0984e5SSebastian Reichel 
269*8c0984e5SSebastian Reichel 	if (full.intval - empty.intval)
270*8c0984e5SSebastian Reichel 		ret =  ((cur.intval - empty.intval) * 100L) /
271*8c0984e5SSebastian Reichel 		       (full.intval - empty.intval);
272*8c0984e5SSebastian Reichel 	else
273*8c0984e5SSebastian Reichel 		return -1;
274*8c0984e5SSebastian Reichel 
275*8c0984e5SSebastian Reichel 	if (ret > 100)
276*8c0984e5SSebastian Reichel 		return 100;
277*8c0984e5SSebastian Reichel 	else if (ret < 0)
278*8c0984e5SSebastian Reichel 		return 0;
279*8c0984e5SSebastian Reichel 
280*8c0984e5SSebastian Reichel 	return ret;
281*8c0984e5SSebastian Reichel }
282*8c0984e5SSebastian Reichel 
apm_battery_apm_get_power_status(struct apm_power_info * info)283*8c0984e5SSebastian Reichel static void apm_battery_apm_get_power_status(struct apm_power_info *info)
284*8c0984e5SSebastian Reichel {
285*8c0984e5SSebastian Reichel 	union power_supply_propval status;
286*8c0984e5SSebastian Reichel 	union power_supply_propval capacity, time_to_full, time_to_empty;
287*8c0984e5SSebastian Reichel 
288*8c0984e5SSebastian Reichel 	mutex_lock(&apm_mutex);
289*8c0984e5SSebastian Reichel 	find_main_battery();
290*8c0984e5SSebastian Reichel 	if (!main_battery) {
291*8c0984e5SSebastian Reichel 		mutex_unlock(&apm_mutex);
292*8c0984e5SSebastian Reichel 		return;
293*8c0984e5SSebastian Reichel 	}
294*8c0984e5SSebastian Reichel 
295*8c0984e5SSebastian Reichel 	/* status */
296*8c0984e5SSebastian Reichel 
297*8c0984e5SSebastian Reichel 	if (MPSY_PROP(STATUS, &status))
298*8c0984e5SSebastian Reichel 		status.intval = POWER_SUPPLY_STATUS_UNKNOWN;
299*8c0984e5SSebastian Reichel 
300*8c0984e5SSebastian Reichel 	/* ac line status */
301*8c0984e5SSebastian Reichel 
302*8c0984e5SSebastian Reichel 	if ((status.intval == POWER_SUPPLY_STATUS_CHARGING) ||
303*8c0984e5SSebastian Reichel 	    (status.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) ||
304*8c0984e5SSebastian Reichel 	    (status.intval == POWER_SUPPLY_STATUS_FULL))
305*8c0984e5SSebastian Reichel 		info->ac_line_status = APM_AC_ONLINE;
306*8c0984e5SSebastian Reichel 	else
307*8c0984e5SSebastian Reichel 		info->ac_line_status = APM_AC_OFFLINE;
308*8c0984e5SSebastian Reichel 
309*8c0984e5SSebastian Reichel 	/* battery life (i.e. capacity, in percents) */
310*8c0984e5SSebastian Reichel 
311*8c0984e5SSebastian Reichel 	if (MPSY_PROP(CAPACITY, &capacity) == 0) {
312*8c0984e5SSebastian Reichel 		info->battery_life = capacity.intval;
313*8c0984e5SSebastian Reichel 	} else {
314*8c0984e5SSebastian Reichel 		/* try calculate using energy */
315*8c0984e5SSebastian Reichel 		info->battery_life = calculate_capacity(SOURCE_ENERGY);
316*8c0984e5SSebastian Reichel 		/* if failed try calculate using charge instead */
317*8c0984e5SSebastian Reichel 		if (info->battery_life == -1)
318*8c0984e5SSebastian Reichel 			info->battery_life = calculate_capacity(SOURCE_CHARGE);
319*8c0984e5SSebastian Reichel 		if (info->battery_life == -1)
320*8c0984e5SSebastian Reichel 			info->battery_life = calculate_capacity(SOURCE_VOLTAGE);
321*8c0984e5SSebastian Reichel 	}
322*8c0984e5SSebastian Reichel 
323*8c0984e5SSebastian Reichel 	/* charging status */
324*8c0984e5SSebastian Reichel 
325*8c0984e5SSebastian Reichel 	if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
326*8c0984e5SSebastian Reichel 		info->battery_status = APM_BATTERY_STATUS_CHARGING;
327*8c0984e5SSebastian Reichel 	} else {
328*8c0984e5SSebastian Reichel 		if (info->battery_life > 50)
329*8c0984e5SSebastian Reichel 			info->battery_status = APM_BATTERY_STATUS_HIGH;
330*8c0984e5SSebastian Reichel 		else if (info->battery_life > 5)
331*8c0984e5SSebastian Reichel 			info->battery_status = APM_BATTERY_STATUS_LOW;
332*8c0984e5SSebastian Reichel 		else
333*8c0984e5SSebastian Reichel 			info->battery_status = APM_BATTERY_STATUS_CRITICAL;
334*8c0984e5SSebastian Reichel 	}
335*8c0984e5SSebastian Reichel 	info->battery_flag = info->battery_status;
336*8c0984e5SSebastian Reichel 
337*8c0984e5SSebastian Reichel 	/* time */
338*8c0984e5SSebastian Reichel 
339*8c0984e5SSebastian Reichel 	info->units = APM_UNITS_MINS;
340*8c0984e5SSebastian Reichel 
341*8c0984e5SSebastian Reichel 	if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
342*8c0984e5SSebastian Reichel 		if (!MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full) ||
343*8c0984e5SSebastian Reichel 				!MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full))
344*8c0984e5SSebastian Reichel 			info->time = time_to_full.intval / 60;
345*8c0984e5SSebastian Reichel 		else
346*8c0984e5SSebastian Reichel 			info->time = calculate_time(status.intval);
347*8c0984e5SSebastian Reichel 	} else {
348*8c0984e5SSebastian Reichel 		if (!MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty) ||
349*8c0984e5SSebastian Reichel 			      !MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty))
350*8c0984e5SSebastian Reichel 			info->time = time_to_empty.intval / 60;
351*8c0984e5SSebastian Reichel 		else
352*8c0984e5SSebastian Reichel 			info->time = calculate_time(status.intval);
353*8c0984e5SSebastian Reichel 	}
354*8c0984e5SSebastian Reichel 
355*8c0984e5SSebastian Reichel 	mutex_unlock(&apm_mutex);
356*8c0984e5SSebastian Reichel }
357*8c0984e5SSebastian Reichel 
apm_battery_init(void)358*8c0984e5SSebastian Reichel static int __init apm_battery_init(void)
359*8c0984e5SSebastian Reichel {
360*8c0984e5SSebastian Reichel 	printk(KERN_INFO "APM Battery Driver\n");
361*8c0984e5SSebastian Reichel 
362*8c0984e5SSebastian Reichel 	apm_get_power_status = apm_battery_apm_get_power_status;
363*8c0984e5SSebastian Reichel 	return 0;
364*8c0984e5SSebastian Reichel }
365*8c0984e5SSebastian Reichel 
apm_battery_exit(void)366*8c0984e5SSebastian Reichel static void __exit apm_battery_exit(void)
367*8c0984e5SSebastian Reichel {
368*8c0984e5SSebastian Reichel 	apm_get_power_status = NULL;
369*8c0984e5SSebastian Reichel }
370*8c0984e5SSebastian Reichel 
371*8c0984e5SSebastian Reichel module_init(apm_battery_init);
372*8c0984e5SSebastian Reichel module_exit(apm_battery_exit);
373*8c0984e5SSebastian Reichel 
374*8c0984e5SSebastian Reichel MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>");
375*8c0984e5SSebastian Reichel MODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
376*8c0984e5SSebastian Reichel MODULE_LICENSE("GPL");
377