Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ae9458d6 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* git://git.infradead.org/battery-2.6:
  apm_power: check I.intval for zero value, we use it as the divisor
  MAINTAINERS: remove kernel-discuss@handhelds.org list
  pda_power: implement polling
  pda_power: various cleanups
  apm_power: support using VOLTAGE_* properties for apm calculations
  pda_power: add suspend/resume support
  power_supply: add few more values and props
  pda_power: only register available psu
  power: fix incorrect unregistration in power_supply_create_attrs error path
  power: remove POWER_SUPPLY_PROP_CAPACITY_LEVEL
  [BATTERY] power_supply_leds: use kasprintf
  [BATTERY] Every file should include the headers containing the prototypes for its global functions.
parents 63e9b66e e91926e9
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -87,6 +87,10 @@ batteries use voltage for very approximated calculation of capacity.
Battery driver also can use this attribute just to inform userspace
about maximal and minimal voltage thresholds of a given battery.

VOLTAGE_MAX, VOLTAGE_MIN - same as _DESIGN voltage values except that
these ones should be used if hardware could only guess (measure and
retain) the thresholds of a given power supply.

CHARGE_FULL_DESIGN, CHARGE_EMPTY_DESIGN - design charge values, when
battery considered full/empty.

@@ -100,8 +104,6 @@ age)". I.e. these attributes represents real thresholds, not design values.
ENERGY_FULL, ENERGY_EMPTY - same as above but for energy.

CAPACITY - capacity in percents.
CAPACITY_LEVEL - capacity level. This corresponds to
POWER_SUPPLY_CAPACITY_LEVEL_*.

TEMP - temperature of the power supply.
TEMP_AMBIENT - ambient temperature.
+0 −1
Original line number Diff line number Diff line
@@ -3053,7 +3053,6 @@ M: cbou@mail.ru
P:	David Woodhouse
M:	dwmw2@infradead.org
L:	linux-kernel@vger.kernel.org
L:	kernel-discuss@handhelds.org
T:	git git.infradead.org/battery-2.6.git
S:	Maintained

+73 −21
Original line number Diff line number Diff line
@@ -13,7 +13,7 @@
#include <linux/power_supply.h>
#include <linux/apm-emulation.h>

static DEFINE_MUTEX(apm_mutex);

#define PSY_PROP(psy, prop, val) psy->get_property(psy, \
			 POWER_SUPPLY_PROP_##prop, val)

@@ -22,8 +22,15 @@ static DEFINE_MUTEX(apm_mutex);

#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;
@@ -107,7 +114,7 @@ static void find_main_battery(void)
	}
}

static int calculate_time(int status, int using_charge)
static int do_calculate_time(int status, enum apm_source source)
{
	union power_supply_propval full;
	union power_supply_propval empty;
@@ -126,20 +133,37 @@ static int calculate_time(int status, int using_charge)
			return -1;
	}

	if (using_charge) {
	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;
	} else {
		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(full_prop, &full)) {
@@ -166,7 +190,26 @@ static int calculate_time(int status, int using_charge)
		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;
@@ -174,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)) {
@@ -254,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 */
@@ -280,22 +338,16 @@ static void apm_battery_apm_get_power_status(struct apm_power_info *info)

	if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
		if (!MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full) ||
				!MPSY_PROP(TIME_TO_FULL_NOW, &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, 0);
			if (info->time == -1)
				info->time = calculate_time(status.intval, 1);
		}
		else
			info->time = calculate_time(status.intval);
	} else {
		if (!MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty) ||
			      !MPSY_PROP(TIME_TO_EMPTY_NOW, &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, 0);
			if (info->time == -1)
				info->time = calculate_time(status.intval, 1);
		}
		else
			info->time = calculate_time(status.intval);
	}

	mutex_unlock(&apm_mutex);
+0 −9
Original line number Diff line number Diff line
@@ -226,14 +226,6 @@ static int olpc_bat_get_property(struct power_supply *psy,
			return ret;
		val->intval = ec_byte;
		break;
	case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
		if (ec_byte & BAT_STAT_FULL)
			val->intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
		else if (ec_byte & BAT_STAT_LOW)
			val->intval = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
		else
			val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
		break;
	case POWER_SUPPLY_PROP_TEMP:
		ret = olpc_ec_cmd(EC_BAT_TEMP, NULL, 0, (void *)&ec_word, 2);
		if (ret)
@@ -265,7 +257,6 @@ static enum power_supply_property olpc_bat_props[] = {
	POWER_SUPPLY_PROP_VOLTAGE_AVG,
	POWER_SUPPLY_PROP_CURRENT_AVG,
	POWER_SUPPLY_PROP_CAPACITY,
	POWER_SUPPLY_PROP_CAPACITY_LEVEL,
	POWER_SUPPLY_PROP_TEMP,
	POWER_SUPPLY_PROP_TEMP_AMBIENT,
	POWER_SUPPLY_PROP_MANUFACTURER,
+202 −80
Original line number Diff line number Diff line
@@ -32,6 +32,18 @@ static struct pda_power_pdata *pdata;
static struct resource *ac_irq, *usb_irq;
static struct timer_list charger_timer;
static struct timer_list supply_timer;
static struct timer_list polling_timer;
static int polling;

enum {
	PDA_PSY_OFFLINE = 0,
	PDA_PSY_ONLINE = 1,
	PDA_PSY_TO_CHANGE,
};
static int new_ac_status = -1;
static int new_usb_status = -1;
static int ac_status = -1;
static int usb_status = -1;

static int pda_power_get_property(struct power_supply *psy,
				  enum power_supply_property psp,
@@ -61,8 +73,7 @@ static char *pda_power_supplied_to[] = {
	"backup-battery",
};

static struct power_supply pda_power_supplies[] = {
	{
static struct power_supply pda_psy_ac = {
	.name = "ac",
	.type = POWER_SUPPLY_TYPE_MAINS,
	.supplied_to = pda_power_supplied_to,
@@ -70,8 +81,9 @@ static struct power_supply pda_power_supplies[] = {
	.properties = pda_power_props,
	.num_properties = ARRAY_SIZE(pda_power_props),
	.get_property = pda_power_get_property,
	},
	{
};

static struct power_supply pda_psy_usb = {
	.name = "usb",
	.type = POWER_SUPPLY_TYPE_USB,
	.supplied_to = pda_power_supplied_to,
@@ -79,18 +91,26 @@ static struct power_supply pda_power_supplies[] = {
	.properties = pda_power_props,
	.num_properties = ARRAY_SIZE(pda_power_props),
	.get_property = pda_power_get_property,
	},
};

static void update_status(void)
{
	if (pdata->is_ac_online)
		new_ac_status = !!pdata->is_ac_online();

	if (pdata->is_usb_online)
		new_usb_status = !!pdata->is_usb_online();
}

static void update_charger(void)
{
	if (!pdata->set_charge)
		return;

	if (pdata->is_ac_online && pdata->is_ac_online()) {
	if (new_ac_status > 0) {
		dev_dbg(dev, "charger on (AC)\n");
		pdata->set_charge(PDA_POWER_CHARGE_AC);
	} else if (pdata->is_usb_online && pdata->is_usb_online()) {
	} else if (new_usb_status > 0) {
		dev_dbg(dev, "charger on (USB)\n");
		pdata->set_charge(PDA_POWER_CHARGE_USB);
	} else {
@@ -99,34 +119,81 @@ static void update_charger(void)
	}
}

static void supply_timer_func(unsigned long power_supply_ptr)
static void supply_timer_func(unsigned long unused)
{
	void *power_supply = (void *)power_supply_ptr;
	if (ac_status == PDA_PSY_TO_CHANGE) {
		ac_status = new_ac_status;
		power_supply_changed(&pda_psy_ac);
	}

	power_supply_changed(power_supply);
	if (usb_status == PDA_PSY_TO_CHANGE) {
		usb_status = new_usb_status;
		power_supply_changed(&pda_psy_usb);
	}
}

static void charger_timer_func(unsigned long power_supply_ptr)
static void psy_changed(void)
{
	update_charger();

	/* Okay, charger set. Now wait a bit before notifying supplicants,
	 * charge power should stabilize. */
	supply_timer.data = power_supply_ptr;
	/*
	 * Okay, charger set. Now wait a bit before notifying supplicants,
	 * charge power should stabilize.
	 */
	mod_timer(&supply_timer,
		  jiffies + msecs_to_jiffies(pdata->wait_for_charger));
}

static void charger_timer_func(unsigned long unused)
{
	update_status();
	psy_changed();
}

static irqreturn_t power_changed_isr(int irq, void *power_supply)
{
	/* Wait a bit before reading ac/usb line status and setting charger,
	 * because ac/usb status readings may lag from irq. */
	charger_timer.data = (unsigned long)power_supply;
	if (power_supply == &pda_psy_ac)
		ac_status = PDA_PSY_TO_CHANGE;
	else if (power_supply == &pda_psy_usb)
		usb_status = PDA_PSY_TO_CHANGE;
	else
		return IRQ_NONE;

	/*
	 * Wait a bit before reading ac/usb line status and setting charger,
	 * because ac/usb status readings may lag from irq.
	 */
	mod_timer(&charger_timer,
		  jiffies + msecs_to_jiffies(pdata->wait_for_status));

	return IRQ_HANDLED;
}

static void polling_timer_func(unsigned long unused)
{
	int changed = 0;

	dev_dbg(dev, "polling...\n");

	update_status();

	if (!ac_irq && new_ac_status != ac_status) {
		ac_status = PDA_PSY_TO_CHANGE;
		changed = 1;
	}

	if (!usb_irq && new_usb_status != usb_status) {
		usb_status = PDA_PSY_TO_CHANGE;
		changed = 1;
	}

	if (changed)
		psy_changed();

	mod_timer(&polling_timer,
		  jiffies + msecs_to_jiffies(pdata->polling_interval));
}

static int pda_power_probe(struct platform_device *pdev)
{
	int ret = 0;
@@ -142,6 +209,7 @@ static int pda_power_probe(struct platform_device *pdev)

	pdata = pdev->dev.platform_data;

	update_status();
	update_charger();

	if (!pdata->wait_for_status)
@@ -150,86 +218,138 @@ static int pda_power_probe(struct platform_device *pdev)
	if (!pdata->wait_for_charger)
		pdata->wait_for_charger = 500;

	if (!pdata->polling_interval)
		pdata->polling_interval = 2000;

	setup_timer(&charger_timer, charger_timer_func, 0);
	setup_timer(&supply_timer, supply_timer_func, 0);

	ac_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ac");
	usb_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "usb");
	if (!ac_irq && !usb_irq) {
		dev_err(dev, "no ac/usb irq specified\n");
		ret = -ENODEV;
		goto noirqs;
	}

	if (pdata->supplied_to) {
		pda_power_supplies[0].supplied_to = pdata->supplied_to;
		pda_power_supplies[1].supplied_to = pdata->supplied_to;
		pda_power_supplies[0].num_supplicants = pdata->num_supplicants;
		pda_power_supplies[1].num_supplicants = pdata->num_supplicants;
		pda_psy_ac.supplied_to = pdata->supplied_to;
		pda_psy_ac.num_supplicants = pdata->num_supplicants;
		pda_psy_usb.supplied_to = pdata->supplied_to;
		pda_psy_usb.num_supplicants = pdata->num_supplicants;
	}

	ret = power_supply_register(&pdev->dev, &pda_power_supplies[0]);
	if (pdata->is_ac_online) {
		ret = power_supply_register(&pdev->dev, &pda_psy_ac);
		if (ret) {
			dev_err(dev, "failed to register %s power supply\n",
			pda_power_supplies[0].name);
		goto supply0_failed;
	}

	ret = power_supply_register(&pdev->dev, &pda_power_supplies[1]);
	if (ret) {
		dev_err(dev, "failed to register %s power supply\n",
			pda_power_supplies[1].name);
		goto supply1_failed;
				pda_psy_ac.name);
			goto ac_supply_failed;
		}

		if (ac_irq) {
			ret = request_irq(ac_irq->start, power_changed_isr,
					  get_irq_flags(ac_irq), ac_irq->name,
				  &pda_power_supplies[0]);
					  &pda_psy_ac);
			if (ret) {
				dev_err(dev, "request ac irq failed\n");
				goto ac_irq_failed;
			}
		} else {
			polling = 1;
		}
	}

	if (pdata->is_usb_online) {
		ret = power_supply_register(&pdev->dev, &pda_psy_usb);
		if (ret) {
			dev_err(dev, "failed to register %s power supply\n",
				pda_psy_usb.name);
			goto usb_supply_failed;
		}

		if (usb_irq) {
			ret = request_irq(usb_irq->start, power_changed_isr,
				  get_irq_flags(usb_irq), usb_irq->name,
				  &pda_power_supplies[1]);
					  get_irq_flags(usb_irq),
					  usb_irq->name, &pda_psy_usb);
			if (ret) {
				dev_err(dev, "request usb irq failed\n");
				goto usb_irq_failed;
			}
		} else {
			polling = 1;
		}
	}

	if (polling) {
		dev_dbg(dev, "will poll for status\n");
		setup_timer(&polling_timer, polling_timer_func, 0);
		mod_timer(&polling_timer,
			  jiffies + msecs_to_jiffies(pdata->polling_interval));
	}

	goto success;
	if (ac_irq || usb_irq)
		device_init_wakeup(&pdev->dev, 1);

	return 0;

usb_irq_failed:
	if (ac_irq)
		free_irq(ac_irq->start, &pda_power_supplies[0]);
	if (pdata->is_usb_online)
		power_supply_unregister(&pda_psy_usb);
usb_supply_failed:
	if (pdata->is_ac_online && ac_irq)
		free_irq(ac_irq->start, &pda_psy_ac);
ac_irq_failed:
	power_supply_unregister(&pda_power_supplies[1]);
supply1_failed:
	power_supply_unregister(&pda_power_supplies[0]);
supply0_failed:
noirqs:
	if (pdata->is_ac_online)
		power_supply_unregister(&pda_psy_ac);
ac_supply_failed:
wrongid:
success:
	return ret;
}

static int pda_power_remove(struct platform_device *pdev)
{
	if (usb_irq)
		free_irq(usb_irq->start, &pda_power_supplies[1]);
	if (ac_irq)
		free_irq(ac_irq->start, &pda_power_supplies[0]);
	if (pdata->is_usb_online && usb_irq)
		free_irq(usb_irq->start, &pda_psy_usb);
	if (pdata->is_ac_online && ac_irq)
		free_irq(ac_irq->start, &pda_psy_ac);

	if (polling)
		del_timer_sync(&polling_timer);
	del_timer_sync(&charger_timer);
	del_timer_sync(&supply_timer);
	power_supply_unregister(&pda_power_supplies[1]);
	power_supply_unregister(&pda_power_supplies[0]);

	if (pdata->is_usb_online)
		power_supply_unregister(&pda_psy_usb);
	if (pdata->is_ac_online)
		power_supply_unregister(&pda_psy_ac);

	return 0;
}

#ifdef CONFIG_PM
static int pda_power_suspend(struct platform_device *pdev, pm_message_t state)
{
	if (device_may_wakeup(&pdev->dev)) {
		if (ac_irq)
			enable_irq_wake(ac_irq->start);
		if (usb_irq)
			enable_irq_wake(usb_irq->start);
	}

	return 0;
}

static int pda_power_resume(struct platform_device *pdev)
{
	if (device_may_wakeup(&pdev->dev)) {
		if (usb_irq)
			disable_irq_wake(usb_irq->start);
		if (ac_irq)
			disable_irq_wake(ac_irq->start);
	}

	return 0;
}
#else
#define pda_power_suspend NULL
#define pda_power_resume NULL
#endif /* CONFIG_PM */

static struct platform_driver pda_power_pdrv = {
	.driver = {
@@ -237,6 +357,8 @@ static struct platform_driver pda_power_pdrv = {
	},
	.probe = pda_power_probe,
	.remove = pda_power_remove,
	.suspend = pda_power_suspend,
	.resume = pda_power_resume,
};

static int __init pda_power_init(void)
Loading