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.35-rc3 486 lines 12 kB view raw
1/* 2 * Battery and Power Management code for the Sharp SL-6000x 3 * 4 * Copyright (c) 2005 Dirk Opfer 5 * Copyright (c) 2008 Dmitry Baryshkov 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 * 11 */ 12#include <linux/kernel.h> 13#include <linux/module.h> 14#include <linux/power_supply.h> 15#include <linux/wm97xx.h> 16#include <linux/delay.h> 17#include <linux/spinlock.h> 18#include <linux/interrupt.h> 19#include <linux/gpio.h> 20 21#include <asm/mach-types.h> 22#include <mach/tosa.h> 23 24static DEFINE_MUTEX(bat_lock); /* protects gpio pins */ 25static struct work_struct bat_work; 26 27struct tosa_bat { 28 int status; 29 struct power_supply psy; 30 int full_chrg; 31 32 struct mutex work_lock; /* protects data */ 33 34 bool (*is_present)(struct tosa_bat *bat); 35 int gpio_full; 36 int gpio_charge_off; 37 38 int technology; 39 40 int gpio_bat; 41 int adc_bat; 42 int adc_bat_divider; 43 int bat_max; 44 int bat_min; 45 46 int gpio_temp; 47 int adc_temp; 48 int adc_temp_divider; 49}; 50 51static struct tosa_bat tosa_bat_main; 52static struct tosa_bat tosa_bat_jacket; 53 54static unsigned long tosa_read_bat(struct tosa_bat *bat) 55{ 56 unsigned long value = 0; 57 58 if (bat->gpio_bat < 0 || bat->adc_bat < 0) 59 return 0; 60 61 mutex_lock(&bat_lock); 62 gpio_set_value(bat->gpio_bat, 1); 63 msleep(5); 64 value = wm97xx_read_aux_adc(dev_get_drvdata(bat->psy.dev->parent), 65 bat->adc_bat); 66 gpio_set_value(bat->gpio_bat, 0); 67 mutex_unlock(&bat_lock); 68 69 value = value * 1000000 / bat->adc_bat_divider; 70 71 return value; 72} 73 74static unsigned long tosa_read_temp(struct tosa_bat *bat) 75{ 76 unsigned long value = 0; 77 78 if (bat->gpio_temp < 0 || bat->adc_temp < 0) 79 return 0; 80 81 mutex_lock(&bat_lock); 82 gpio_set_value(bat->gpio_temp, 1); 83 msleep(5); 84 value = wm97xx_read_aux_adc(dev_get_drvdata(bat->psy.dev->parent), 85 bat->adc_temp); 86 gpio_set_value(bat->gpio_temp, 0); 87 mutex_unlock(&bat_lock); 88 89 value = value * 10000 / bat->adc_temp_divider; 90 91 return value; 92} 93 94static int tosa_bat_get_property(struct power_supply *psy, 95 enum power_supply_property psp, 96 union power_supply_propval *val) 97{ 98 int ret = 0; 99 struct tosa_bat *bat = container_of(psy, struct tosa_bat, psy); 100 101 if (bat->is_present && !bat->is_present(bat) 102 && psp != POWER_SUPPLY_PROP_PRESENT) { 103 return -ENODEV; 104 } 105 106 switch (psp) { 107 case POWER_SUPPLY_PROP_STATUS: 108 val->intval = bat->status; 109 break; 110 case POWER_SUPPLY_PROP_TECHNOLOGY: 111 val->intval = bat->technology; 112 break; 113 case POWER_SUPPLY_PROP_VOLTAGE_NOW: 114 val->intval = tosa_read_bat(bat); 115 break; 116 case POWER_SUPPLY_PROP_VOLTAGE_MAX: 117 if (bat->full_chrg == -1) 118 val->intval = bat->bat_max; 119 else 120 val->intval = bat->full_chrg; 121 break; 122 case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: 123 val->intval = bat->bat_max; 124 break; 125 case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: 126 val->intval = bat->bat_min; 127 break; 128 case POWER_SUPPLY_PROP_TEMP: 129 val->intval = tosa_read_temp(bat); 130 break; 131 case POWER_SUPPLY_PROP_PRESENT: 132 val->intval = bat->is_present ? bat->is_present(bat) : 1; 133 break; 134 default: 135 ret = -EINVAL; 136 break; 137 } 138 return ret; 139} 140 141static bool tosa_jacket_bat_is_present(struct tosa_bat *bat) 142{ 143 return gpio_get_value(TOSA_GPIO_JACKET_DETECT) == 0; 144} 145 146static void tosa_bat_external_power_changed(struct power_supply *psy) 147{ 148 schedule_work(&bat_work); 149} 150 151static irqreturn_t tosa_bat_gpio_isr(int irq, void *data) 152{ 153 pr_info("tosa_bat_gpio irq: %d\n", gpio_get_value(irq_to_gpio(irq))); 154 schedule_work(&bat_work); 155 return IRQ_HANDLED; 156} 157 158static void tosa_bat_update(struct tosa_bat *bat) 159{ 160 int old; 161 struct power_supply *psy = &bat->psy; 162 163 mutex_lock(&bat->work_lock); 164 165 old = bat->status; 166 167 if (bat->is_present && !bat->is_present(bat)) { 168 printk(KERN_NOTICE "%s not present\n", psy->name); 169 bat->status = POWER_SUPPLY_STATUS_UNKNOWN; 170 bat->full_chrg = -1; 171 } else if (power_supply_am_i_supplied(psy)) { 172 if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING) { 173 gpio_set_value(bat->gpio_charge_off, 0); 174 mdelay(15); 175 } 176 177 if (gpio_get_value(bat->gpio_full)) { 178 if (old == POWER_SUPPLY_STATUS_CHARGING || 179 bat->full_chrg == -1) 180 bat->full_chrg = tosa_read_bat(bat); 181 182 gpio_set_value(bat->gpio_charge_off, 1); 183 bat->status = POWER_SUPPLY_STATUS_FULL; 184 } else { 185 gpio_set_value(bat->gpio_charge_off, 0); 186 bat->status = POWER_SUPPLY_STATUS_CHARGING; 187 } 188 } else { 189 gpio_set_value(bat->gpio_charge_off, 1); 190 bat->status = POWER_SUPPLY_STATUS_DISCHARGING; 191 } 192 193 if (old != bat->status) 194 power_supply_changed(psy); 195 196 mutex_unlock(&bat->work_lock); 197} 198 199static void tosa_bat_work(struct work_struct *work) 200{ 201 tosa_bat_update(&tosa_bat_main); 202 tosa_bat_update(&tosa_bat_jacket); 203} 204 205 206static enum power_supply_property tosa_bat_main_props[] = { 207 POWER_SUPPLY_PROP_STATUS, 208 POWER_SUPPLY_PROP_TECHNOLOGY, 209 POWER_SUPPLY_PROP_VOLTAGE_NOW, 210 POWER_SUPPLY_PROP_VOLTAGE_MAX, 211 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 212 POWER_SUPPLY_PROP_TEMP, 213 POWER_SUPPLY_PROP_PRESENT, 214}; 215 216static enum power_supply_property tosa_bat_bu_props[] = { 217 POWER_SUPPLY_PROP_STATUS, 218 POWER_SUPPLY_PROP_TECHNOLOGY, 219 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 220 POWER_SUPPLY_PROP_VOLTAGE_NOW, 221 POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 222 POWER_SUPPLY_PROP_PRESENT, 223}; 224 225static struct tosa_bat tosa_bat_main = { 226 .status = POWER_SUPPLY_STATUS_DISCHARGING, 227 .full_chrg = -1, 228 .psy = { 229 .name = "main-battery", 230 .type = POWER_SUPPLY_TYPE_BATTERY, 231 .properties = tosa_bat_main_props, 232 .num_properties = ARRAY_SIZE(tosa_bat_main_props), 233 .get_property = tosa_bat_get_property, 234 .external_power_changed = tosa_bat_external_power_changed, 235 .use_for_apm = 1, 236 }, 237 238 .gpio_full = TOSA_GPIO_BAT0_CRG, 239 .gpio_charge_off = TOSA_GPIO_CHARGE_OFF, 240 241 .technology = POWER_SUPPLY_TECHNOLOGY_LIPO, 242 243 .gpio_bat = TOSA_GPIO_BAT0_V_ON, 244 .adc_bat = WM97XX_AUX_ID3, 245 .adc_bat_divider = 414, 246 .bat_max = 4310000, 247 .bat_min = 1551 * 1000000 / 414, 248 249 .gpio_temp = TOSA_GPIO_BAT1_TH_ON, 250 .adc_temp = WM97XX_AUX_ID2, 251 .adc_temp_divider = 10000, 252}; 253 254static struct tosa_bat tosa_bat_jacket = { 255 .status = POWER_SUPPLY_STATUS_DISCHARGING, 256 .full_chrg = -1, 257 .psy = { 258 .name = "jacket-battery", 259 .type = POWER_SUPPLY_TYPE_BATTERY, 260 .properties = tosa_bat_main_props, 261 .num_properties = ARRAY_SIZE(tosa_bat_main_props), 262 .get_property = tosa_bat_get_property, 263 .external_power_changed = tosa_bat_external_power_changed, 264 }, 265 266 .is_present = tosa_jacket_bat_is_present, 267 .gpio_full = TOSA_GPIO_BAT1_CRG, 268 .gpio_charge_off = TOSA_GPIO_CHARGE_OFF_JC, 269 270 .technology = POWER_SUPPLY_TECHNOLOGY_LIPO, 271 272 .gpio_bat = TOSA_GPIO_BAT1_V_ON, 273 .adc_bat = WM97XX_AUX_ID3, 274 .adc_bat_divider = 414, 275 .bat_max = 4310000, 276 .bat_min = 1551 * 1000000 / 414, 277 278 .gpio_temp = TOSA_GPIO_BAT0_TH_ON, 279 .adc_temp = WM97XX_AUX_ID2, 280 .adc_temp_divider = 10000, 281}; 282 283static struct tosa_bat tosa_bat_bu = { 284 .status = POWER_SUPPLY_STATUS_UNKNOWN, 285 .full_chrg = -1, 286 287 .psy = { 288 .name = "backup-battery", 289 .type = POWER_SUPPLY_TYPE_BATTERY, 290 .properties = tosa_bat_bu_props, 291 .num_properties = ARRAY_SIZE(tosa_bat_bu_props), 292 .get_property = tosa_bat_get_property, 293 .external_power_changed = tosa_bat_external_power_changed, 294 }, 295 296 .gpio_full = -1, 297 .gpio_charge_off = -1, 298 299 .technology = POWER_SUPPLY_TECHNOLOGY_LiMn, 300 301 .gpio_bat = TOSA_GPIO_BU_CHRG_ON, 302 .adc_bat = WM97XX_AUX_ID4, 303 .adc_bat_divider = 1266, 304 305 .gpio_temp = -1, 306 .adc_temp = -1, 307 .adc_temp_divider = -1, 308}; 309 310static struct { 311 int gpio; 312 char *name; 313 bool output; 314 int value; 315} gpios[] = { 316 { TOSA_GPIO_CHARGE_OFF, "main charge off", 1, 1 }, 317 { TOSA_GPIO_CHARGE_OFF_JC, "jacket charge off", 1, 1 }, 318 { TOSA_GPIO_BAT_SW_ON, "battery switch", 1, 0 }, 319 { TOSA_GPIO_BAT0_V_ON, "main battery", 1, 0 }, 320 { TOSA_GPIO_BAT1_V_ON, "jacket battery", 1, 0 }, 321 { TOSA_GPIO_BAT1_TH_ON, "main battery temp", 1, 0 }, 322 { TOSA_GPIO_BAT0_TH_ON, "jacket battery temp", 1, 0 }, 323 { TOSA_GPIO_BU_CHRG_ON, "backup battery", 1, 0 }, 324 { TOSA_GPIO_BAT0_CRG, "main battery full", 0, 0 }, 325 { TOSA_GPIO_BAT1_CRG, "jacket battery full", 0, 0 }, 326 { TOSA_GPIO_BAT0_LOW, "main battery low", 0, 0 }, 327 { TOSA_GPIO_BAT1_LOW, "jacket battery low", 0, 0 }, 328 { TOSA_GPIO_JACKET_DETECT, "jacket detect", 0, 0 }, 329}; 330 331#ifdef CONFIG_PM 332static int tosa_bat_suspend(struct platform_device *dev, pm_message_t state) 333{ 334 /* flush all pending status updates */ 335 flush_scheduled_work(); 336 return 0; 337} 338 339static int tosa_bat_resume(struct platform_device *dev) 340{ 341 /* things may have changed while we were away */ 342 schedule_work(&bat_work); 343 return 0; 344} 345#else 346#define tosa_bat_suspend NULL 347#define tosa_bat_resume NULL 348#endif 349 350static int __devinit tosa_bat_probe(struct platform_device *dev) 351{ 352 int ret; 353 int i; 354 355 if (!machine_is_tosa()) 356 return -ENODEV; 357 358 for (i = 0; i < ARRAY_SIZE(gpios); i++) { 359 ret = gpio_request(gpios[i].gpio, gpios[i].name); 360 if (ret) { 361 i--; 362 goto err_gpio; 363 } 364 365 if (gpios[i].output) 366 ret = gpio_direction_output(gpios[i].gpio, 367 gpios[i].value); 368 else 369 ret = gpio_direction_input(gpios[i].gpio); 370 371 if (ret) 372 goto err_gpio; 373 } 374 375 mutex_init(&tosa_bat_main.work_lock); 376 mutex_init(&tosa_bat_jacket.work_lock); 377 378 INIT_WORK(&bat_work, tosa_bat_work); 379 380 ret = power_supply_register(&dev->dev, &tosa_bat_main.psy); 381 if (ret) 382 goto err_psy_reg_main; 383 ret = power_supply_register(&dev->dev, &tosa_bat_jacket.psy); 384 if (ret) 385 goto err_psy_reg_jacket; 386 ret = power_supply_register(&dev->dev, &tosa_bat_bu.psy); 387 if (ret) 388 goto err_psy_reg_bu; 389 390 ret = request_irq(gpio_to_irq(TOSA_GPIO_BAT0_CRG), 391 tosa_bat_gpio_isr, 392 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 393 "main full", &tosa_bat_main); 394 if (ret) 395 goto err_req_main; 396 397 ret = request_irq(gpio_to_irq(TOSA_GPIO_BAT1_CRG), 398 tosa_bat_gpio_isr, 399 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 400 "jacket full", &tosa_bat_jacket); 401 if (ret) 402 goto err_req_jacket; 403 404 ret = request_irq(gpio_to_irq(TOSA_GPIO_JACKET_DETECT), 405 tosa_bat_gpio_isr, 406 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 407 "jacket detect", &tosa_bat_jacket); 408 if (!ret) { 409 schedule_work(&bat_work); 410 return 0; 411 } 412 413 free_irq(gpio_to_irq(TOSA_GPIO_BAT1_CRG), &tosa_bat_jacket); 414err_req_jacket: 415 free_irq(gpio_to_irq(TOSA_GPIO_BAT0_CRG), &tosa_bat_main); 416err_req_main: 417 power_supply_unregister(&tosa_bat_bu.psy); 418err_psy_reg_bu: 419 power_supply_unregister(&tosa_bat_jacket.psy); 420err_psy_reg_jacket: 421 power_supply_unregister(&tosa_bat_main.psy); 422err_psy_reg_main: 423 424 /* see comment in tosa_bat_remove */ 425 flush_scheduled_work(); 426 427 i--; 428err_gpio: 429 for (; i >= 0; i--) 430 gpio_free(gpios[i].gpio); 431 432 return ret; 433} 434 435static int __devexit tosa_bat_remove(struct platform_device *dev) 436{ 437 int i; 438 439 free_irq(gpio_to_irq(TOSA_GPIO_JACKET_DETECT), &tosa_bat_jacket); 440 free_irq(gpio_to_irq(TOSA_GPIO_BAT1_CRG), &tosa_bat_jacket); 441 free_irq(gpio_to_irq(TOSA_GPIO_BAT0_CRG), &tosa_bat_main); 442 443 power_supply_unregister(&tosa_bat_bu.psy); 444 power_supply_unregister(&tosa_bat_jacket.psy); 445 power_supply_unregister(&tosa_bat_main.psy); 446 447 /* 448 * now flush all pending work. 449 * we won't get any more schedules, since all 450 * sources (isr and external_power_changed) 451 * are unregistered now. 452 */ 453 flush_scheduled_work(); 454 455 for (i = ARRAY_SIZE(gpios) - 1; i >= 0; i--) 456 gpio_free(gpios[i].gpio); 457 458 return 0; 459} 460 461static struct platform_driver tosa_bat_driver = { 462 .driver.name = "wm97xx-battery", 463 .driver.owner = THIS_MODULE, 464 .probe = tosa_bat_probe, 465 .remove = __devexit_p(tosa_bat_remove), 466 .suspend = tosa_bat_suspend, 467 .resume = tosa_bat_resume, 468}; 469 470static int __init tosa_bat_init(void) 471{ 472 return platform_driver_register(&tosa_bat_driver); 473} 474 475static void __exit tosa_bat_exit(void) 476{ 477 platform_driver_unregister(&tosa_bat_driver); 478} 479 480module_init(tosa_bat_init); 481module_exit(tosa_bat_exit); 482 483MODULE_LICENSE("GPL"); 484MODULE_AUTHOR("Dmitry Baryshkov"); 485MODULE_DESCRIPTION("Tosa battery driver"); 486MODULE_ALIAS("platform:wm97xx-battery");