Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

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