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 v3.14-rc1 390 lines 9.5 kB view raw
1/* 2 * Battery and Power Management code for the Sharp SL-5x00 3 * 4 * Copyright (C) 2009 Thomas Kunze 5 * 6 * based on tosa_battery.c 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/kernel.h> 14#include <linux/module.h> 15#include <linux/power_supply.h> 16#include <linux/delay.h> 17#include <linux/spinlock.h> 18#include <linux/interrupt.h> 19#include <linux/gpio.h> 20#include <linux/mfd/ucb1x00.h> 21 22#include <asm/mach/sharpsl_param.h> 23#include <asm/mach-types.h> 24#include <mach/collie.h> 25 26static DEFINE_MUTEX(bat_lock); /* protects gpio pins */ 27static struct work_struct bat_work; 28static struct ucb1x00 *ucb; 29 30struct collie_bat { 31 int status; 32 struct power_supply psy; 33 int full_chrg; 34 35 struct mutex work_lock; /* protects data */ 36 37 bool (*is_present)(struct collie_bat *bat); 38 int gpio_full; 39 int gpio_charge_on; 40 41 int technology; 42 43 int gpio_bat; 44 int adc_bat; 45 int adc_bat_divider; 46 int bat_max; 47 int bat_min; 48 49 int gpio_temp; 50 int adc_temp; 51 int adc_temp_divider; 52}; 53 54static struct collie_bat collie_bat_main; 55 56static unsigned long collie_read_bat(struct collie_bat *bat) 57{ 58 unsigned long value = 0; 59 60 if (bat->gpio_bat < 0 || bat->adc_bat < 0) 61 return 0; 62 mutex_lock(&bat_lock); 63 gpio_set_value(bat->gpio_bat, 1); 64 msleep(5); 65 ucb1x00_adc_enable(ucb); 66 value = ucb1x00_adc_read(ucb, bat->adc_bat, UCB_SYNC); 67 ucb1x00_adc_disable(ucb); 68 gpio_set_value(bat->gpio_bat, 0); 69 mutex_unlock(&bat_lock); 70 value = value * 1000000 / bat->adc_bat_divider; 71 72 return value; 73} 74 75static unsigned long collie_read_temp(struct collie_bat *bat) 76{ 77 unsigned long value = 0; 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 ucb1x00_adc_enable(ucb); 85 value = ucb1x00_adc_read(ucb, bat->adc_temp, UCB_SYNC); 86 ucb1x00_adc_disable(ucb); 87 gpio_set_value(bat->gpio_temp, 0); 88 mutex_unlock(&bat_lock); 89 90 value = value * 10000 / bat->adc_temp_divider; 91 92 return value; 93} 94 95static int collie_bat_get_property(struct power_supply *psy, 96 enum power_supply_property psp, 97 union power_supply_propval *val) 98{ 99 int ret = 0; 100 struct collie_bat *bat = container_of(psy, struct collie_bat, psy); 101 102 if (bat->is_present && !bat->is_present(bat) 103 && psp != POWER_SUPPLY_PROP_PRESENT) { 104 return -ENODEV; 105 } 106 107 switch (psp) { 108 case POWER_SUPPLY_PROP_STATUS: 109 val->intval = bat->status; 110 break; 111 case POWER_SUPPLY_PROP_TECHNOLOGY: 112 val->intval = bat->technology; 113 break; 114 case POWER_SUPPLY_PROP_VOLTAGE_NOW: 115 val->intval = collie_read_bat(bat); 116 break; 117 case POWER_SUPPLY_PROP_VOLTAGE_MAX: 118 if (bat->full_chrg == -1) 119 val->intval = bat->bat_max; 120 else 121 val->intval = bat->full_chrg; 122 break; 123 case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: 124 val->intval = bat->bat_max; 125 break; 126 case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: 127 val->intval = bat->bat_min; 128 break; 129 case POWER_SUPPLY_PROP_TEMP: 130 val->intval = collie_read_temp(bat); 131 break; 132 case POWER_SUPPLY_PROP_PRESENT: 133 val->intval = bat->is_present ? bat->is_present(bat) : 1; 134 break; 135 default: 136 ret = -EINVAL; 137 break; 138 } 139 return ret; 140} 141 142static void collie_bat_external_power_changed(struct power_supply *psy) 143{ 144 schedule_work(&bat_work); 145} 146 147static irqreturn_t collie_bat_gpio_isr(int irq, void *data) 148{ 149 pr_info("collie_bat_gpio irq\n"); 150 schedule_work(&bat_work); 151 return IRQ_HANDLED; 152} 153 154static void collie_bat_update(struct collie_bat *bat) 155{ 156 int old; 157 struct power_supply *psy = &bat->psy; 158 159 mutex_lock(&bat->work_lock); 160 161 old = bat->status; 162 163 if (bat->is_present && !bat->is_present(bat)) { 164 printk(KERN_NOTICE "%s not present\n", psy->name); 165 bat->status = POWER_SUPPLY_STATUS_UNKNOWN; 166 bat->full_chrg = -1; 167 } else if (power_supply_am_i_supplied(psy)) { 168 if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING) { 169 gpio_set_value(bat->gpio_charge_on, 1); 170 mdelay(15); 171 } 172 173 if (gpio_get_value(bat->gpio_full)) { 174 if (old == POWER_SUPPLY_STATUS_CHARGING || 175 bat->full_chrg == -1) 176 bat->full_chrg = collie_read_bat(bat); 177 178 gpio_set_value(bat->gpio_charge_on, 0); 179 bat->status = POWER_SUPPLY_STATUS_FULL; 180 } else { 181 gpio_set_value(bat->gpio_charge_on, 1); 182 bat->status = POWER_SUPPLY_STATUS_CHARGING; 183 } 184 } else { 185 gpio_set_value(bat->gpio_charge_on, 0); 186 bat->status = POWER_SUPPLY_STATUS_DISCHARGING; 187 } 188 189 if (old != bat->status) 190 power_supply_changed(psy); 191 192 mutex_unlock(&bat->work_lock); 193} 194 195static void collie_bat_work(struct work_struct *work) 196{ 197 collie_bat_update(&collie_bat_main); 198} 199 200 201static enum power_supply_property collie_bat_main_props[] = { 202 POWER_SUPPLY_PROP_STATUS, 203 POWER_SUPPLY_PROP_TECHNOLOGY, 204 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 205 POWER_SUPPLY_PROP_VOLTAGE_NOW, 206 POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 207 POWER_SUPPLY_PROP_VOLTAGE_MAX, 208 POWER_SUPPLY_PROP_PRESENT, 209 POWER_SUPPLY_PROP_TEMP, 210}; 211 212static enum power_supply_property collie_bat_bu_props[] = { 213 POWER_SUPPLY_PROP_STATUS, 214 POWER_SUPPLY_PROP_TECHNOLOGY, 215 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 216 POWER_SUPPLY_PROP_VOLTAGE_NOW, 217 POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 218 POWER_SUPPLY_PROP_VOLTAGE_MAX, 219 POWER_SUPPLY_PROP_PRESENT, 220}; 221 222static struct collie_bat collie_bat_main = { 223 .status = POWER_SUPPLY_STATUS_DISCHARGING, 224 .full_chrg = -1, 225 .psy = { 226 .name = "main-battery", 227 .type = POWER_SUPPLY_TYPE_BATTERY, 228 .properties = collie_bat_main_props, 229 .num_properties = ARRAY_SIZE(collie_bat_main_props), 230 .get_property = collie_bat_get_property, 231 .external_power_changed = collie_bat_external_power_changed, 232 .use_for_apm = 1, 233 }, 234 235 .gpio_full = COLLIE_GPIO_CO, 236 .gpio_charge_on = COLLIE_GPIO_CHARGE_ON, 237 238 .technology = POWER_SUPPLY_TECHNOLOGY_LIPO, 239 240 .gpio_bat = COLLIE_GPIO_MBAT_ON, 241 .adc_bat = UCB_ADC_INP_AD1, 242 .adc_bat_divider = 155, 243 .bat_max = 4310000, 244 .bat_min = 1551 * 1000000 / 414, 245 246 .gpio_temp = COLLIE_GPIO_TMP_ON, 247 .adc_temp = UCB_ADC_INP_AD0, 248 .adc_temp_divider = 10000, 249}; 250 251static struct collie_bat collie_bat_bu = { 252 .status = POWER_SUPPLY_STATUS_UNKNOWN, 253 .full_chrg = -1, 254 255 .psy = { 256 .name = "backup-battery", 257 .type = POWER_SUPPLY_TYPE_BATTERY, 258 .properties = collie_bat_bu_props, 259 .num_properties = ARRAY_SIZE(collie_bat_bu_props), 260 .get_property = collie_bat_get_property, 261 .external_power_changed = collie_bat_external_power_changed, 262 }, 263 264 .gpio_full = -1, 265 .gpio_charge_on = -1, 266 267 .technology = POWER_SUPPLY_TECHNOLOGY_LiMn, 268 269 .gpio_bat = COLLIE_GPIO_BBAT_ON, 270 .adc_bat = UCB_ADC_INP_AD1, 271 .adc_bat_divider = 155, 272 .bat_max = 3000000, 273 .bat_min = 1900000, 274 275 .gpio_temp = -1, 276 .adc_temp = -1, 277 .adc_temp_divider = -1, 278}; 279 280static struct gpio collie_batt_gpios[] = { 281 { COLLIE_GPIO_CO, GPIOF_IN, "main battery full" }, 282 { COLLIE_GPIO_MAIN_BAT_LOW, GPIOF_IN, "main battery low" }, 283 { COLLIE_GPIO_CHARGE_ON, GPIOF_OUT_INIT_LOW, "main charge on" }, 284 { COLLIE_GPIO_MBAT_ON, GPIOF_OUT_INIT_LOW, "main battery" }, 285 { COLLIE_GPIO_TMP_ON, GPIOF_OUT_INIT_LOW, "main battery temp" }, 286 { COLLIE_GPIO_BBAT_ON, GPIOF_OUT_INIT_LOW, "backup battery" }, 287}; 288 289#ifdef CONFIG_PM 290static int collie_bat_suspend(struct ucb1x00_dev *dev) 291{ 292 /* flush all pending status updates */ 293 flush_work(&bat_work); 294 return 0; 295} 296 297static int collie_bat_resume(struct ucb1x00_dev *dev) 298{ 299 /* things may have changed while we were away */ 300 schedule_work(&bat_work); 301 return 0; 302} 303#else 304#define collie_bat_suspend NULL 305#define collie_bat_resume NULL 306#endif 307 308static int collie_bat_probe(struct ucb1x00_dev *dev) 309{ 310 int ret; 311 312 if (!machine_is_collie()) 313 return -ENODEV; 314 315 ucb = dev->ucb; 316 317 ret = gpio_request_array(collie_batt_gpios, 318 ARRAY_SIZE(collie_batt_gpios)); 319 if (ret) 320 return ret; 321 322 mutex_init(&collie_bat_main.work_lock); 323 324 INIT_WORK(&bat_work, collie_bat_work); 325 326 ret = power_supply_register(&dev->ucb->dev, &collie_bat_main.psy); 327 if (ret) 328 goto err_psy_reg_main; 329 ret = power_supply_register(&dev->ucb->dev, &collie_bat_bu.psy); 330 if (ret) 331 goto err_psy_reg_bu; 332 333 ret = request_irq(gpio_to_irq(COLLIE_GPIO_CO), 334 collie_bat_gpio_isr, 335 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 336 "main full", &collie_bat_main); 337 if (!ret) { 338 schedule_work(&bat_work); 339 return 0; 340 } 341 power_supply_unregister(&collie_bat_bu.psy); 342err_psy_reg_bu: 343 power_supply_unregister(&collie_bat_main.psy); 344err_psy_reg_main: 345 346 /* see comment in collie_bat_remove */ 347 cancel_work_sync(&bat_work); 348 gpio_free_array(collie_batt_gpios, ARRAY_SIZE(collie_batt_gpios)); 349 return ret; 350} 351 352static void collie_bat_remove(struct ucb1x00_dev *dev) 353{ 354 free_irq(gpio_to_irq(COLLIE_GPIO_CO), &collie_bat_main); 355 356 power_supply_unregister(&collie_bat_bu.psy); 357 power_supply_unregister(&collie_bat_main.psy); 358 359 /* 360 * Now cancel the bat_work. We won't get any more schedules, 361 * since all sources (isr and external_power_changed) are 362 * unregistered now. 363 */ 364 cancel_work_sync(&bat_work); 365 gpio_free_array(collie_batt_gpios, ARRAY_SIZE(collie_batt_gpios)); 366} 367 368static struct ucb1x00_driver collie_bat_driver = { 369 .add = collie_bat_probe, 370 .remove = collie_bat_remove, 371 .suspend = collie_bat_suspend, 372 .resume = collie_bat_resume, 373}; 374 375static int __init collie_bat_init(void) 376{ 377 return ucb1x00_register_driver(&collie_bat_driver); 378} 379 380static void __exit collie_bat_exit(void) 381{ 382 ucb1x00_unregister_driver(&collie_bat_driver); 383} 384 385module_init(collie_bat_init); 386module_exit(collie_bat_exit); 387 388MODULE_LICENSE("GPL"); 389MODULE_AUTHOR("Thomas Kunze"); 390MODULE_DESCRIPTION("Collie battery driver");