Merge git://git.infradead.org/battery-2.6

* 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.
This commit is contained in:
Linus Torvalds 2008-02-02 15:13:05 +11:00
commit ae9458d6a0
9 changed files with 301 additions and 153 deletions

View file

@ -87,6 +87,10 @@ batteries use voltage for very approximated calculation of capacity.
Battery driver also can use this attribute just to inform userspace Battery driver also can use this attribute just to inform userspace
about maximal and minimal voltage thresholds of a given battery. 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 CHARGE_FULL_DESIGN, CHARGE_EMPTY_DESIGN - design charge values, when
battery considered full/empty. 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. ENERGY_FULL, ENERGY_EMPTY - same as above but for energy.
CAPACITY - capacity in percents. CAPACITY - capacity in percents.
CAPACITY_LEVEL - capacity level. This corresponds to
POWER_SUPPLY_CAPACITY_LEVEL_*.
TEMP - temperature of the power supply. TEMP - temperature of the power supply.
TEMP_AMBIENT - ambient temperature. TEMP_AMBIENT - ambient temperature.

View file

@ -3053,7 +3053,6 @@ M: cbou@mail.ru
P: David Woodhouse P: David Woodhouse
M: dwmw2@infradead.org M: dwmw2@infradead.org
L: linux-kernel@vger.kernel.org L: linux-kernel@vger.kernel.org
L: kernel-discuss@handhelds.org
T: git git.infradead.org/battery-2.6.git T: git git.infradead.org/battery-2.6.git
S: Maintained S: Maintained

View file

@ -13,7 +13,7 @@
#include <linux/power_supply.h> #include <linux/power_supply.h>
#include <linux/apm-emulation.h> #include <linux/apm-emulation.h>
static DEFINE_MUTEX(apm_mutex);
#define PSY_PROP(psy, prop, val) psy->get_property(psy, \ #define PSY_PROP(psy, prop, val) psy->get_property(psy, \
POWER_SUPPLY_PROP_##prop, val) 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) #define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val)
static DEFINE_MUTEX(apm_mutex);
static struct power_supply *main_battery; static struct power_supply *main_battery;
enum apm_source {
SOURCE_ENERGY,
SOURCE_CHARGE,
SOURCE_VOLTAGE,
};
struct find_bat_param { struct find_bat_param {
struct power_supply *main; struct power_supply *main;
struct power_supply *bat; 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 full;
union power_supply_propval empty; union power_supply_propval empty;
@ -126,20 +133,37 @@ static int calculate_time(int status, int using_charge)
return -1; return -1;
} }
if (using_charge) { if (!I.intval)
return 0;
switch (source) {
case SOURCE_CHARGE:
full_prop = POWER_SUPPLY_PROP_CHARGE_FULL; full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN; full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY; empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY; empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
cur_avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG; cur_avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
cur_now_prop = POWER_SUPPLY_PROP_CHARGE_NOW; cur_now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
} else { break;
case SOURCE_ENERGY:
full_prop = POWER_SUPPLY_PROP_ENERGY_FULL; full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN; full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY; empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY; empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
cur_avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG; cur_avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
cur_now_prop = POWER_SUPPLY_PROP_ENERGY_NOW; 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)) { 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; 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_prop, empty_prop;
enum power_supply_property full_design_prop, empty_design_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; union power_supply_propval empty, full, cur;
int ret; int ret;
if (using_charge) { switch (source) {
case SOURCE_CHARGE:
full_prop = POWER_SUPPLY_PROP_CHARGE_FULL; full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY; empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN; full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN; empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN;
now_prop = POWER_SUPPLY_PROP_CHARGE_NOW; now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG; avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
} else { break;
case SOURCE_ENERGY:
full_prop = POWER_SUPPLY_PROP_ENERGY_FULL; full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY; empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN; full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN; empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN;
now_prop = POWER_SUPPLY_PROP_ENERGY_NOW; now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG; 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)) { 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; info->battery_life = capacity.intval;
} else { } else {
/* try calculate using energy */ /* 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 failed try calculate using charge instead */
if (info->battery_life == -1) 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 */ /* 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 (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
if (!MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full) || 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; info->time = time_to_full.intval / 60;
} else { else
info->time = calculate_time(status.intval, 0); info->time = calculate_time(status.intval);
if (info->time == -1)
info->time = calculate_time(status.intval, 1);
}
} else { } else {
if (!MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty) || 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; info->time = time_to_empty.intval / 60;
} else { else
info->time = calculate_time(status.intval, 0); info->time = calculate_time(status.intval);
if (info->time == -1)
info->time = calculate_time(status.intval, 1);
}
} }
mutex_unlock(&apm_mutex); mutex_unlock(&apm_mutex);

View file

@ -226,14 +226,6 @@ static int olpc_bat_get_property(struct power_supply *psy,
return ret; return ret;
val->intval = ec_byte; val->intval = ec_byte;
break; 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: case POWER_SUPPLY_PROP_TEMP:
ret = olpc_ec_cmd(EC_BAT_TEMP, NULL, 0, (void *)&ec_word, 2); ret = olpc_ec_cmd(EC_BAT_TEMP, NULL, 0, (void *)&ec_word, 2);
if (ret) if (ret)
@ -265,7 +257,6 @@ static enum power_supply_property olpc_bat_props[] = {
POWER_SUPPLY_PROP_VOLTAGE_AVG, POWER_SUPPLY_PROP_VOLTAGE_AVG,
POWER_SUPPLY_PROP_CURRENT_AVG, POWER_SUPPLY_PROP_CURRENT_AVG,
POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_TEMP_AMBIENT, POWER_SUPPLY_PROP_TEMP_AMBIENT,
POWER_SUPPLY_PROP_MANUFACTURER, POWER_SUPPLY_PROP_MANUFACTURER,

View file

@ -32,6 +32,18 @@ static struct pda_power_pdata *pdata;
static struct resource *ac_irq, *usb_irq; static struct resource *ac_irq, *usb_irq;
static struct timer_list charger_timer; static struct timer_list charger_timer;
static struct timer_list supply_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, static int pda_power_get_property(struct power_supply *psy,
enum power_supply_property psp, enum power_supply_property psp,
@ -61,36 +73,44 @@ static char *pda_power_supplied_to[] = {
"backup-battery", "backup-battery",
}; };
static struct power_supply pda_power_supplies[] = { static struct power_supply pda_psy_ac = {
{ .name = "ac",
.name = "ac", .type = POWER_SUPPLY_TYPE_MAINS,
.type = POWER_SUPPLY_TYPE_MAINS, .supplied_to = pda_power_supplied_to,
.supplied_to = pda_power_supplied_to, .num_supplicants = ARRAY_SIZE(pda_power_supplied_to),
.num_supplicants = ARRAY_SIZE(pda_power_supplied_to), .properties = pda_power_props,
.properties = pda_power_props, .num_properties = ARRAY_SIZE(pda_power_props),
.num_properties = ARRAY_SIZE(pda_power_props), .get_property = pda_power_get_property,
.get_property = pda_power_get_property,
},
{
.name = "usb",
.type = POWER_SUPPLY_TYPE_USB,
.supplied_to = pda_power_supplied_to,
.num_supplicants = ARRAY_SIZE(pda_power_supplied_to),
.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,
.num_supplicants = ARRAY_SIZE(pda_power_supplied_to),
.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) static void update_charger(void)
{ {
if (!pdata->set_charge) if (!pdata->set_charge)
return; return;
if (pdata->is_ac_online && pdata->is_ac_online()) { if (new_ac_status > 0) {
dev_dbg(dev, "charger on (AC)\n"); dev_dbg(dev, "charger on (AC)\n");
pdata->set_charge(PDA_POWER_CHARGE_AC); 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"); dev_dbg(dev, "charger on (USB)\n");
pdata->set_charge(PDA_POWER_CHARGE_USB); pdata->set_charge(PDA_POWER_CHARGE_USB);
} else { } 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(); update_charger();
/* Okay, charger set. Now wait a bit before notifying supplicants, /*
* charge power should stabilize. */ * Okay, charger set. Now wait a bit before notifying supplicants,
supply_timer.data = power_supply_ptr; * charge power should stabilize.
*/
mod_timer(&supply_timer, mod_timer(&supply_timer,
jiffies + msecs_to_jiffies(pdata->wait_for_charger)); 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) static irqreturn_t power_changed_isr(int irq, void *power_supply)
{ {
/* Wait a bit before reading ac/usb line status and setting charger, if (power_supply == &pda_psy_ac)
* because ac/usb status readings may lag from irq. */ ac_status = PDA_PSY_TO_CHANGE;
charger_timer.data = (unsigned long)power_supply; 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, mod_timer(&charger_timer,
jiffies + msecs_to_jiffies(pdata->wait_for_status)); jiffies + msecs_to_jiffies(pdata->wait_for_status));
return IRQ_HANDLED; 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) static int pda_power_probe(struct platform_device *pdev)
{ {
int ret = 0; int ret = 0;
@ -142,6 +209,7 @@ static int pda_power_probe(struct platform_device *pdev)
pdata = pdev->dev.platform_data; pdata = pdev->dev.platform_data;
update_status();
update_charger(); update_charger();
if (!pdata->wait_for_status) if (!pdata->wait_for_status)
@ -150,93 +218,147 @@ static int pda_power_probe(struct platform_device *pdev)
if (!pdata->wait_for_charger) if (!pdata->wait_for_charger)
pdata->wait_for_charger = 500; pdata->wait_for_charger = 500;
if (!pdata->polling_interval)
pdata->polling_interval = 2000;
setup_timer(&charger_timer, charger_timer_func, 0); setup_timer(&charger_timer, charger_timer_func, 0);
setup_timer(&supply_timer, supply_timer_func, 0); setup_timer(&supply_timer, supply_timer_func, 0);
ac_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ac"); ac_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ac");
usb_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "usb"); 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) { if (pdata->supplied_to) {
pda_power_supplies[0].supplied_to = pdata->supplied_to; pda_psy_ac.supplied_to = pdata->supplied_to;
pda_power_supplies[1].supplied_to = pdata->supplied_to; pda_psy_ac.num_supplicants = pdata->num_supplicants;
pda_power_supplies[0].num_supplicants = pdata->num_supplicants; pda_psy_usb.supplied_to = pdata->supplied_to;
pda_power_supplies[1].num_supplicants = pdata->num_supplicants; pda_psy_usb.num_supplicants = pdata->num_supplicants;
} }
ret = power_supply_register(&pdev->dev, &pda_power_supplies[0]); if (pdata->is_ac_online) {
if (ret) { ret = power_supply_register(&pdev->dev, &pda_psy_ac);
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;
}
if (ac_irq) {
ret = request_irq(ac_irq->start, power_changed_isr,
get_irq_flags(ac_irq), ac_irq->name,
&pda_power_supplies[0]);
if (ret) { if (ret) {
dev_err(dev, "request ac irq failed\n"); dev_err(dev, "failed to register %s power supply\n",
goto ac_irq_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_psy_ac);
if (ret) {
dev_err(dev, "request ac irq failed\n");
goto ac_irq_failed;
}
} else {
polling = 1;
} }
} }
if (usb_irq) { if (pdata->is_usb_online) {
ret = request_irq(usb_irq->start, power_changed_isr, ret = power_supply_register(&pdev->dev, &pda_psy_usb);
get_irq_flags(usb_irq), usb_irq->name,
&pda_power_supplies[1]);
if (ret) { if (ret) {
dev_err(dev, "request usb irq failed\n"); dev_err(dev, "failed to register %s power supply\n",
goto usb_irq_failed; 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_psy_usb);
if (ret) {
dev_err(dev, "request usb irq failed\n");
goto usb_irq_failed;
}
} else {
polling = 1;
} }
} }
goto success; 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));
}
if (ac_irq || usb_irq)
device_init_wakeup(&pdev->dev, 1);
return 0;
usb_irq_failed: usb_irq_failed:
if (ac_irq) if (pdata->is_usb_online)
free_irq(ac_irq->start, &pda_power_supplies[0]); 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: ac_irq_failed:
power_supply_unregister(&pda_power_supplies[1]); if (pdata->is_ac_online)
supply1_failed: power_supply_unregister(&pda_psy_ac);
power_supply_unregister(&pda_power_supplies[0]); ac_supply_failed:
supply0_failed:
noirqs:
wrongid: wrongid:
success:
return ret; return ret;
} }
static int pda_power_remove(struct platform_device *pdev) static int pda_power_remove(struct platform_device *pdev)
{ {
if (usb_irq) if (pdata->is_usb_online && usb_irq)
free_irq(usb_irq->start, &pda_power_supplies[1]); free_irq(usb_irq->start, &pda_psy_usb);
if (ac_irq) if (pdata->is_ac_online && ac_irq)
free_irq(ac_irq->start, &pda_power_supplies[0]); free_irq(ac_irq->start, &pda_psy_ac);
if (polling)
del_timer_sync(&polling_timer);
del_timer_sync(&charger_timer); del_timer_sync(&charger_timer);
del_timer_sync(&supply_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; 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 = { static struct platform_driver pda_power_pdrv = {
.driver = { .driver = {
.name = "pda-power", .name = "pda-power",
}, },
.probe = pda_power_probe, .probe = pda_power_probe,
.remove = pda_power_remove, .remove = pda_power_remove,
.suspend = pda_power_suspend,
.resume = pda_power_resume,
}; };
static int __init pda_power_init(void) static int __init pda_power_init(void)

View file

@ -10,8 +10,11 @@
* You may use this code as per GPL version 2 * You may use this code as per GPL version 2
*/ */
#include <linux/kernel.h>
#include <linux/power_supply.h> #include <linux/power_supply.h>
#include "power_supply.h"
/* Battery specific LEDs triggers. */ /* Battery specific LEDs triggers. */
static void power_supply_update_bat_leds(struct power_supply *psy) static void power_supply_update_bat_leds(struct power_supply *psy)
@ -46,28 +49,20 @@ static int power_supply_create_bat_triggers(struct power_supply *psy)
{ {
int rc = 0; int rc = 0;
psy->charging_full_trig_name = kmalloc(strlen(psy->name) + psy->charging_full_trig_name = kasprintf(GFP_KERNEL,
sizeof("-charging-or-full"), GFP_KERNEL); "%s-charging-or-full", psy->name);
if (!psy->charging_full_trig_name) if (!psy->charging_full_trig_name)
goto charging_full_failed; goto charging_full_failed;
psy->charging_trig_name = kmalloc(strlen(psy->name) + psy->charging_trig_name = kasprintf(GFP_KERNEL,
sizeof("-charging"), GFP_KERNEL); "%s-charging", psy->name);
if (!psy->charging_trig_name) if (!psy->charging_trig_name)
goto charging_failed; goto charging_failed;
psy->full_trig_name = kmalloc(strlen(psy->name) + psy->full_trig_name = kasprintf(GFP_KERNEL, "%s-full", psy->name);
sizeof("-full"), GFP_KERNEL);
if (!psy->full_trig_name) if (!psy->full_trig_name)
goto full_failed; goto full_failed;
strcpy(psy->charging_full_trig_name, psy->name);
strcat(psy->charging_full_trig_name, "-charging-or-full");
strcpy(psy->charging_trig_name, psy->name);
strcat(psy->charging_trig_name, "-charging");
strcpy(psy->full_trig_name, psy->name);
strcat(psy->full_trig_name, "-full");
led_trigger_register_simple(psy->charging_full_trig_name, led_trigger_register_simple(psy->charging_full_trig_name,
&psy->charging_full_trig); &psy->charging_full_trig);
led_trigger_register_simple(psy->charging_trig_name, led_trigger_register_simple(psy->charging_trig_name,
@ -118,14 +113,10 @@ static int power_supply_create_gen_triggers(struct power_supply *psy)
{ {
int rc = 0; int rc = 0;
psy->online_trig_name = kmalloc(strlen(psy->name) + sizeof("-online"), psy->online_trig_name = kasprintf(GFP_KERNEL, "%s-online", psy->name);
GFP_KERNEL);
if (!psy->online_trig_name) if (!psy->online_trig_name)
goto online_failed; goto online_failed;
strcpy(psy->online_trig_name, psy->name);
strcat(psy->online_trig_name, "-online");
led_trigger_register_simple(psy->online_trig_name, &psy->online_trig); led_trigger_register_simple(psy->online_trig_name, &psy->online_trig);
goto success; goto success;

View file

@ -14,6 +14,8 @@
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/power_supply.h> #include <linux/power_supply.h>
#include "power_supply.h"
/* /*
* This is because the name "current" breaks the device attr macro. * This is because the name "current" breaks the device attr macro.
* The "current" word resolves to "(get_current())" so instead of * The "current" word resolves to "(get_current())" so instead of
@ -46,10 +48,8 @@ static ssize_t power_supply_show_property(struct device *dev,
"Unspecified failure" "Unspecified failure"
}; };
static char *technology_text[] = { static char *technology_text[] = {
"Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd" "Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd",
}; "LiMn"
static char *capacity_level_text[] = {
"Unknown", "Critical", "Low", "Normal", "High", "Full"
}; };
ssize_t ret; ssize_t ret;
struct power_supply *psy = dev_get_drvdata(dev); struct power_supply *psy = dev_get_drvdata(dev);
@ -71,9 +71,6 @@ static ssize_t power_supply_show_property(struct device *dev,
return sprintf(buf, "%s\n", health_text[value.intval]); return sprintf(buf, "%s\n", health_text[value.intval]);
else if (off == POWER_SUPPLY_PROP_TECHNOLOGY) else if (off == POWER_SUPPLY_PROP_TECHNOLOGY)
return sprintf(buf, "%s\n", technology_text[value.intval]); return sprintf(buf, "%s\n", technology_text[value.intval]);
else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL)
return sprintf(buf, "%s\n",
capacity_level_text[value.intval]);
else if (off >= POWER_SUPPLY_PROP_MODEL_NAME) else if (off >= POWER_SUPPLY_PROP_MODEL_NAME)
return sprintf(buf, "%s\n", value.strval); return sprintf(buf, "%s\n", value.strval);
@ -88,6 +85,8 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(present), POWER_SUPPLY_ATTR(present),
POWER_SUPPLY_ATTR(online), POWER_SUPPLY_ATTR(online),
POWER_SUPPLY_ATTR(technology), POWER_SUPPLY_ATTR(technology),
POWER_SUPPLY_ATTR(voltage_max),
POWER_SUPPLY_ATTR(voltage_min),
POWER_SUPPLY_ATTR(voltage_max_design), POWER_SUPPLY_ATTR(voltage_max_design),
POWER_SUPPLY_ATTR(voltage_min_design), POWER_SUPPLY_ATTR(voltage_min_design),
POWER_SUPPLY_ATTR(voltage_now), POWER_SUPPLY_ATTR(voltage_now),
@ -159,8 +158,7 @@ dynamics_failed:
&power_supply_attrs[psy->properties[j]]); &power_supply_attrs[psy->properties[j]]);
statics_failed: statics_failed:
while (i--) while (i--)
device_remove_file(psy->dev, device_remove_file(psy->dev, &power_supply_static_attrs[i]);
&power_supply_static_attrs[psy->properties[i]]);
succeed: succeed:
return rc; return rc;
} }
@ -170,8 +168,7 @@ void power_supply_remove_attrs(struct power_supply *psy)
int i; int i;
for (i = 0; i < ARRAY_SIZE(power_supply_static_attrs); i++) for (i = 0; i < ARRAY_SIZE(power_supply_static_attrs); i++)
device_remove_file(psy->dev, device_remove_file(psy->dev, &power_supply_static_attrs[i]);
&power_supply_static_attrs[i]);
for (i = 0; i < psy->num_properties; i++) for (i = 0; i < psy->num_properties; i++)
device_remove_file(psy->dev, device_remove_file(psy->dev,

View file

@ -26,6 +26,7 @@ struct pda_power_pdata {
unsigned int wait_for_status; /* msecs, default is 500 */ unsigned int wait_for_status; /* msecs, default is 500 */
unsigned int wait_for_charger; /* msecs, default is 500 */ unsigned int wait_for_charger; /* msecs, default is 500 */
unsigned int polling_interval; /* msecs, default is 2000 */
}; };
#endif /* __PDA_POWER_H__ */ #endif /* __PDA_POWER_H__ */

View file

@ -54,15 +54,7 @@ enum {
POWER_SUPPLY_TECHNOLOGY_LIPO, POWER_SUPPLY_TECHNOLOGY_LIPO,
POWER_SUPPLY_TECHNOLOGY_LiFe, POWER_SUPPLY_TECHNOLOGY_LiFe,
POWER_SUPPLY_TECHNOLOGY_NiCd, POWER_SUPPLY_TECHNOLOGY_NiCd,
}; POWER_SUPPLY_TECHNOLOGY_LiMn,
enum {
POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN = 0,
POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL,
POWER_SUPPLY_CAPACITY_LEVEL_LOW,
POWER_SUPPLY_CAPACITY_LEVEL_NORMAL,
POWER_SUPPLY_CAPACITY_LEVEL_HIGH,
POWER_SUPPLY_CAPACITY_LEVEL_FULL,
}; };
enum power_supply_property { enum power_supply_property {
@ -72,6 +64,8 @@ enum power_supply_property {
POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
POWER_SUPPLY_PROP_VOLTAGE_MIN,
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_VOLTAGE_NOW,
@ -91,7 +85,6 @@ enum power_supply_property {
POWER_SUPPLY_PROP_ENERGY_NOW, POWER_SUPPLY_PROP_ENERGY_NOW,
POWER_SUPPLY_PROP_ENERGY_AVG, POWER_SUPPLY_PROP_ENERGY_AVG,
POWER_SUPPLY_PROP_CAPACITY, /* in percents! */ POWER_SUPPLY_PROP_CAPACITY, /* in percents! */
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_TEMP_AMBIENT, POWER_SUPPLY_PROP_TEMP_AMBIENT,
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,