Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.23-rc9 255 lines 6.5 kB view raw
1/* 2 * Common power driver for PDAs and phones with one or two external 3 * power supplies (AC/USB) connected to main and backup batteries, 4 * and optional builtin charger. 5 * 6 * Copyright © 2007 Anton Vorontsov <cbou@mail.ru> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 */ 12 13#include <linux/module.h> 14#include <linux/platform_device.h> 15#include <linux/interrupt.h> 16#include <linux/power_supply.h> 17#include <linux/pda_power.h> 18#include <linux/timer.h> 19#include <linux/jiffies.h> 20 21static inline unsigned int get_irq_flags(struct resource *res) 22{ 23 unsigned int flags = IRQF_DISABLED | IRQF_SHARED; 24 25 flags |= res->flags & IRQF_TRIGGER_MASK; 26 27 return flags; 28} 29 30static struct device *dev; 31static struct pda_power_pdata *pdata; 32static struct resource *ac_irq, *usb_irq; 33static struct timer_list charger_timer; 34static struct timer_list supply_timer; 35 36static int pda_power_get_property(struct power_supply *psy, 37 enum power_supply_property psp, 38 union power_supply_propval *val) 39{ 40 switch (psp) { 41 case POWER_SUPPLY_PROP_ONLINE: 42 if (psy->type == POWER_SUPPLY_TYPE_MAINS) 43 val->intval = pdata->is_ac_online ? 44 pdata->is_ac_online() : 0; 45 else 46 val->intval = pdata->is_usb_online ? 47 pdata->is_usb_online() : 0; 48 break; 49 default: 50 return -EINVAL; 51 } 52 return 0; 53} 54 55static enum power_supply_property pda_power_props[] = { 56 POWER_SUPPLY_PROP_ONLINE, 57}; 58 59static char *pda_power_supplied_to[] = { 60 "main-battery", 61 "backup-battery", 62}; 63 64static struct power_supply pda_power_supplies[] = { 65 { 66 .name = "ac", 67 .type = POWER_SUPPLY_TYPE_MAINS, 68 .supplied_to = pda_power_supplied_to, 69 .num_supplicants = ARRAY_SIZE(pda_power_supplied_to), 70 .properties = pda_power_props, 71 .num_properties = ARRAY_SIZE(pda_power_props), 72 .get_property = pda_power_get_property, 73 }, 74 { 75 .name = "usb", 76 .type = POWER_SUPPLY_TYPE_USB, 77 .supplied_to = pda_power_supplied_to, 78 .num_supplicants = ARRAY_SIZE(pda_power_supplied_to), 79 .properties = pda_power_props, 80 .num_properties = ARRAY_SIZE(pda_power_props), 81 .get_property = pda_power_get_property, 82 }, 83}; 84 85static void update_charger(void) 86{ 87 if (!pdata->set_charge) 88 return; 89 90 if (pdata->is_ac_online && pdata->is_ac_online()) { 91 dev_dbg(dev, "charger on (AC)\n"); 92 pdata->set_charge(PDA_POWER_CHARGE_AC); 93 } else if (pdata->is_usb_online && pdata->is_usb_online()) { 94 dev_dbg(dev, "charger on (USB)\n"); 95 pdata->set_charge(PDA_POWER_CHARGE_USB); 96 } else { 97 dev_dbg(dev, "charger off\n"); 98 pdata->set_charge(0); 99 } 100} 101 102static void supply_timer_func(unsigned long power_supply_ptr) 103{ 104 void *power_supply = (void *)power_supply_ptr; 105 106 power_supply_changed(power_supply); 107} 108 109static void charger_timer_func(unsigned long power_supply_ptr) 110{ 111 update_charger(); 112 113 /* Okay, charger set. Now wait a bit before notifying supplicants, 114 * charge power should stabilize. */ 115 supply_timer.data = power_supply_ptr; 116 mod_timer(&supply_timer, 117 jiffies + msecs_to_jiffies(pdata->wait_for_charger)); 118} 119 120static irqreturn_t power_changed_isr(int irq, void *power_supply) 121{ 122 /* Wait a bit before reading ac/usb line status and setting charger, 123 * because ac/usb status readings may lag from irq. */ 124 charger_timer.data = (unsigned long)power_supply; 125 mod_timer(&charger_timer, 126 jiffies + msecs_to_jiffies(pdata->wait_for_status)); 127 return IRQ_HANDLED; 128} 129 130static int pda_power_probe(struct platform_device *pdev) 131{ 132 int ret = 0; 133 134 dev = &pdev->dev; 135 136 if (pdev->id != -1) { 137 dev_err(dev, "it's meaningless to register several " 138 "pda_powers; use id = -1\n"); 139 ret = -EINVAL; 140 goto wrongid; 141 } 142 143 pdata = pdev->dev.platform_data; 144 145 update_charger(); 146 147 if (!pdata->wait_for_status) 148 pdata->wait_for_status = 500; 149 150 if (!pdata->wait_for_charger) 151 pdata->wait_for_charger = 500; 152 153 setup_timer(&charger_timer, charger_timer_func, 0); 154 setup_timer(&supply_timer, supply_timer_func, 0); 155 156 ac_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ac"); 157 usb_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "usb"); 158 if (!ac_irq && !usb_irq) { 159 dev_err(dev, "no ac/usb irq specified\n"); 160 ret = -ENODEV; 161 goto noirqs; 162 } 163 164 if (pdata->supplied_to) { 165 pda_power_supplies[0].supplied_to = pdata->supplied_to; 166 pda_power_supplies[1].supplied_to = pdata->supplied_to; 167 pda_power_supplies[0].num_supplicants = pdata->num_supplicants; 168 pda_power_supplies[1].num_supplicants = pdata->num_supplicants; 169 } 170 171 ret = power_supply_register(&pdev->dev, &pda_power_supplies[0]); 172 if (ret) { 173 dev_err(dev, "failed to register %s power supply\n", 174 pda_power_supplies[0].name); 175 goto supply0_failed; 176 } 177 178 ret = power_supply_register(&pdev->dev, &pda_power_supplies[1]); 179 if (ret) { 180 dev_err(dev, "failed to register %s power supply\n", 181 pda_power_supplies[1].name); 182 goto supply1_failed; 183 } 184 185 if (ac_irq) { 186 ret = request_irq(ac_irq->start, power_changed_isr, 187 get_irq_flags(ac_irq), ac_irq->name, 188 &pda_power_supplies[0]); 189 if (ret) { 190 dev_err(dev, "request ac irq failed\n"); 191 goto ac_irq_failed; 192 } 193 } 194 195 if (usb_irq) { 196 ret = request_irq(usb_irq->start, power_changed_isr, 197 get_irq_flags(usb_irq), usb_irq->name, 198 &pda_power_supplies[1]); 199 if (ret) { 200 dev_err(dev, "request usb irq failed\n"); 201 goto usb_irq_failed; 202 } 203 } 204 205 goto success; 206 207usb_irq_failed: 208 if (ac_irq) 209 free_irq(ac_irq->start, &pda_power_supplies[0]); 210ac_irq_failed: 211 power_supply_unregister(&pda_power_supplies[1]); 212supply1_failed: 213 power_supply_unregister(&pda_power_supplies[0]); 214supply0_failed: 215noirqs: 216wrongid: 217success: 218 return ret; 219} 220 221static int pda_power_remove(struct platform_device *pdev) 222{ 223 if (usb_irq) 224 free_irq(usb_irq->start, &pda_power_supplies[1]); 225 if (ac_irq) 226 free_irq(ac_irq->start, &pda_power_supplies[0]); 227 del_timer_sync(&charger_timer); 228 del_timer_sync(&supply_timer); 229 power_supply_unregister(&pda_power_supplies[1]); 230 power_supply_unregister(&pda_power_supplies[0]); 231 return 0; 232} 233 234static struct platform_driver pda_power_pdrv = { 235 .driver = { 236 .name = "pda-power", 237 }, 238 .probe = pda_power_probe, 239 .remove = pda_power_remove, 240}; 241 242static int __init pda_power_init(void) 243{ 244 return platform_driver_register(&pda_power_pdrv); 245} 246 247static void __exit pda_power_exit(void) 248{ 249 platform_driver_unregister(&pda_power_pdrv); 250} 251 252module_init(pda_power_init); 253module_exit(pda_power_exit); 254MODULE_LICENSE("GPL"); 255MODULE_AUTHOR("Anton Vorontsov <cbou@mail.ru>");