diff options
Diffstat (limited to 'drivers/power/pda_power.c')
-rw-r--r-- | drivers/power/pda_power.c | 282 |
1 files changed, 202 insertions, 80 deletions
diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c index c058f28..c8aa55b 100644 --- a/drivers/power/pda_power.c +++ b/drivers/power/pda_power.c @@ -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,36 +73,44 @@ static char *pda_power_supplied_to[] = { "backup-battery", }; -static struct power_supply pda_power_supplies[] = { - { - .name = "ac", - .type = POWER_SUPPLY_TYPE_MAINS, - .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, - }, - { - .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_ac = { + .name = "ac", + .type = POWER_SUPPLY_TYPE_MAINS, + .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) { 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 (ret) { - dev_err(dev, "failed to register %s power supply\n", - pda_power_supplies[0].name); - goto supply0_failed; - } + 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_psy_ac.name); + goto ac_supply_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_psy_ac); + if (ret) { + dev_err(dev, "request ac irq failed\n"); + goto ac_irq_failed; + } + } else { + polling = 1; + } } - 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 (pdata->is_usb_online) { + ret = power_supply_register(&pdev->dev, &pda_psy_usb); if (ret) { - dev_err(dev, "request ac irq failed\n"); - goto ac_irq_failed; + 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]); - if (ret) { - dev_err(dev, "request usb irq failed\n"); - goto usb_irq_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: - 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) |