collie_battery.c (12027B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Battery and Power Management code for the Sharp SL-5x00 4 * 5 * Copyright (C) 2009 Thomas Kunze 6 * 7 * based on tosa_battery.c 8 */ 9#include <linux/kernel.h> 10#include <linux/module.h> 11#include <linux/power_supply.h> 12#include <linux/delay.h> 13#include <linux/spinlock.h> 14#include <linux/interrupt.h> 15#include <linux/gpio/driver.h> 16#include <linux/gpio/machine.h> 17#include <linux/gpio/consumer.h> 18#include <linux/mfd/ucb1x00.h> 19 20#include <asm/mach/sharpsl_param.h> 21#include <asm/mach-types.h> 22#include <mach/collie.h> 23 24static DEFINE_MUTEX(bat_lock); /* protects gpio pins */ 25static struct work_struct bat_work; 26static struct ucb1x00 *ucb; 27 28struct collie_bat { 29 int status; 30 struct power_supply *psy; 31 int full_chrg; 32 33 struct mutex work_lock; /* protects data */ 34 35 bool (*is_present)(struct collie_bat *bat); 36 struct gpio_desc *gpio_full; 37 struct gpio_desc *gpio_charge_on; 38 39 int technology; 40 41 struct gpio_desc *gpio_bat; 42 int adc_bat; 43 int adc_bat_divider; 44 int bat_max; 45 int bat_min; 46 47 struct gpio_desc *gpio_temp; 48 int adc_temp; 49 int adc_temp_divider; 50}; 51 52static struct collie_bat collie_bat_main; 53 54static unsigned long collie_read_bat(struct collie_bat *bat) 55{ 56 unsigned long value = 0; 57 58 if (!bat->gpio_bat || bat->adc_bat < 0) 59 return 0; 60 mutex_lock(&bat_lock); 61 gpiod_set_value(bat->gpio_bat, 1); 62 msleep(5); 63 ucb1x00_adc_enable(ucb); 64 value = ucb1x00_adc_read(ucb, bat->adc_bat, UCB_SYNC); 65 ucb1x00_adc_disable(ucb); 66 gpiod_set_value(bat->gpio_bat, 0); 67 mutex_unlock(&bat_lock); 68 value = value * 1000000 / bat->adc_bat_divider; 69 70 return value; 71} 72 73static unsigned long collie_read_temp(struct collie_bat *bat) 74{ 75 unsigned long value = 0; 76 if (!bat->gpio_temp || bat->adc_temp < 0) 77 return 0; 78 79 mutex_lock(&bat_lock); 80 gpiod_set_value(bat->gpio_temp, 1); 81 msleep(5); 82 ucb1x00_adc_enable(ucb); 83 value = ucb1x00_adc_read(ucb, bat->adc_temp, UCB_SYNC); 84 ucb1x00_adc_disable(ucb); 85 gpiod_set_value(bat->gpio_temp, 0); 86 mutex_unlock(&bat_lock); 87 88 value = value * 10000 / bat->adc_temp_divider; 89 90 return value; 91} 92 93static int collie_bat_get_property(struct power_supply *psy, 94 enum power_supply_property psp, 95 union power_supply_propval *val) 96{ 97 int ret = 0; 98 struct collie_bat *bat = power_supply_get_drvdata(psy); 99 100 if (bat->is_present && !bat->is_present(bat) 101 && psp != POWER_SUPPLY_PROP_PRESENT) { 102 return -ENODEV; 103 } 104 105 switch (psp) { 106 case POWER_SUPPLY_PROP_STATUS: 107 val->intval = bat->status; 108 break; 109 case POWER_SUPPLY_PROP_TECHNOLOGY: 110 val->intval = bat->technology; 111 break; 112 case POWER_SUPPLY_PROP_VOLTAGE_NOW: 113 val->intval = collie_read_bat(bat); 114 break; 115 case POWER_SUPPLY_PROP_VOLTAGE_MAX: 116 if (bat->full_chrg == -1) 117 val->intval = bat->bat_max; 118 else 119 val->intval = bat->full_chrg; 120 break; 121 case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: 122 val->intval = bat->bat_max; 123 break; 124 case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: 125 val->intval = bat->bat_min; 126 break; 127 case POWER_SUPPLY_PROP_TEMP: 128 val->intval = collie_read_temp(bat); 129 break; 130 case POWER_SUPPLY_PROP_PRESENT: 131 val->intval = bat->is_present ? bat->is_present(bat) : 1; 132 break; 133 default: 134 ret = -EINVAL; 135 break; 136 } 137 return ret; 138} 139 140static void collie_bat_external_power_changed(struct power_supply *psy) 141{ 142 schedule_work(&bat_work); 143} 144 145static irqreturn_t collie_bat_gpio_isr(int irq, void *data) 146{ 147 pr_info("collie_bat_gpio irq\n"); 148 schedule_work(&bat_work); 149 return IRQ_HANDLED; 150} 151 152static void collie_bat_update(struct collie_bat *bat) 153{ 154 int old; 155 struct power_supply *psy = bat->psy; 156 157 mutex_lock(&bat->work_lock); 158 159 old = bat->status; 160 161 if (bat->is_present && !bat->is_present(bat)) { 162 printk(KERN_NOTICE "%s not present\n", psy->desc->name); 163 bat->status = POWER_SUPPLY_STATUS_UNKNOWN; 164 bat->full_chrg = -1; 165 } else if (power_supply_am_i_supplied(psy)) { 166 if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING) { 167 gpiod_set_value(bat->gpio_charge_on, 1); 168 mdelay(15); 169 } 170 171 if (gpiod_get_value(bat->gpio_full)) { 172 if (old == POWER_SUPPLY_STATUS_CHARGING || 173 bat->full_chrg == -1) 174 bat->full_chrg = collie_read_bat(bat); 175 176 gpiod_set_value(bat->gpio_charge_on, 0); 177 bat->status = POWER_SUPPLY_STATUS_FULL; 178 } else { 179 gpiod_set_value(bat->gpio_charge_on, 1); 180 bat->status = POWER_SUPPLY_STATUS_CHARGING; 181 } 182 } else { 183 gpiod_set_value(bat->gpio_charge_on, 0); 184 bat->status = POWER_SUPPLY_STATUS_DISCHARGING; 185 } 186 187 if (old != bat->status) 188 power_supply_changed(psy); 189 190 mutex_unlock(&bat->work_lock); 191} 192 193static void collie_bat_work(struct work_struct *work) 194{ 195 collie_bat_update(&collie_bat_main); 196} 197 198 199static enum power_supply_property collie_bat_main_props[] = { 200 POWER_SUPPLY_PROP_STATUS, 201 POWER_SUPPLY_PROP_TECHNOLOGY, 202 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 203 POWER_SUPPLY_PROP_VOLTAGE_NOW, 204 POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 205 POWER_SUPPLY_PROP_VOLTAGE_MAX, 206 POWER_SUPPLY_PROP_PRESENT, 207 POWER_SUPPLY_PROP_TEMP, 208}; 209 210static enum power_supply_property collie_bat_bu_props[] = { 211 POWER_SUPPLY_PROP_STATUS, 212 POWER_SUPPLY_PROP_TECHNOLOGY, 213 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 214 POWER_SUPPLY_PROP_VOLTAGE_NOW, 215 POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 216 POWER_SUPPLY_PROP_VOLTAGE_MAX, 217 POWER_SUPPLY_PROP_PRESENT, 218}; 219 220static const struct power_supply_desc collie_bat_main_desc = { 221 .name = "main-battery", 222 .type = POWER_SUPPLY_TYPE_BATTERY, 223 .properties = collie_bat_main_props, 224 .num_properties = ARRAY_SIZE(collie_bat_main_props), 225 .get_property = collie_bat_get_property, 226 .external_power_changed = collie_bat_external_power_changed, 227 .use_for_apm = 1, 228}; 229 230static struct collie_bat collie_bat_main = { 231 .status = POWER_SUPPLY_STATUS_DISCHARGING, 232 .full_chrg = -1, 233 .psy = NULL, 234 235 .gpio_full = NULL, 236 .gpio_charge_on = NULL, 237 238 .technology = POWER_SUPPLY_TECHNOLOGY_LIPO, 239 240 .gpio_bat = NULL, 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 = NULL, 247 .adc_temp = UCB_ADC_INP_AD0, 248 .adc_temp_divider = 10000, 249}; 250 251static const struct power_supply_desc collie_bat_bu_desc = { 252 .name = "backup-battery", 253 .type = POWER_SUPPLY_TYPE_BATTERY, 254 .properties = collie_bat_bu_props, 255 .num_properties = ARRAY_SIZE(collie_bat_bu_props), 256 .get_property = collie_bat_get_property, 257 .external_power_changed = collie_bat_external_power_changed, 258}; 259 260static struct collie_bat collie_bat_bu = { 261 .status = POWER_SUPPLY_STATUS_UNKNOWN, 262 .full_chrg = -1, 263 .psy = NULL, 264 265 .gpio_full = NULL, 266 .gpio_charge_on = NULL, 267 268 .technology = POWER_SUPPLY_TECHNOLOGY_LiMn, 269 270 .gpio_bat = NULL, 271 .adc_bat = UCB_ADC_INP_AD1, 272 .adc_bat_divider = 155, 273 .bat_max = 3000000, 274 .bat_min = 1900000, 275 276 .gpio_temp = NULL, 277 .adc_temp = -1, 278 .adc_temp_divider = -1, 279}; 280 281/* Obtained but unused GPIO */ 282static struct gpio_desc *collie_mbat_low; 283 284#ifdef CONFIG_PM 285static int wakeup_enabled; 286 287static int collie_bat_suspend(struct ucb1x00_dev *dev) 288{ 289 /* flush all pending status updates */ 290 flush_work(&bat_work); 291 292 if (device_may_wakeup(&dev->ucb->dev) && 293 collie_bat_main.status == POWER_SUPPLY_STATUS_CHARGING) 294 wakeup_enabled = !enable_irq_wake(gpiod_to_irq(collie_bat_main.gpio_full)); 295 else 296 wakeup_enabled = 0; 297 298 return 0; 299} 300 301static int collie_bat_resume(struct ucb1x00_dev *dev) 302{ 303 if (wakeup_enabled) 304 disable_irq_wake(gpiod_to_irq(collie_bat_main.gpio_full)); 305 306 /* things may have changed while we were away */ 307 schedule_work(&bat_work); 308 return 0; 309} 310#else 311#define collie_bat_suspend NULL 312#define collie_bat_resume NULL 313#endif 314 315static int collie_bat_probe(struct ucb1x00_dev *dev) 316{ 317 int ret; 318 struct power_supply_config psy_main_cfg = {}, psy_bu_cfg = {}; 319 struct gpio_chip *gc = &dev->ucb->gpio; 320 321 if (!machine_is_collie()) 322 return -ENODEV; 323 324 ucb = dev->ucb; 325 326 /* Obtain all the main battery GPIOs */ 327 collie_bat_main.gpio_full = gpiod_get(&dev->ucb->dev, 328 "main battery full", 329 GPIOD_IN); 330 if (IS_ERR(collie_bat_main.gpio_full)) 331 return PTR_ERR(collie_bat_main.gpio_full); 332 333 collie_mbat_low = gpiod_get(&dev->ucb->dev, 334 "main battery low", 335 GPIOD_IN); 336 if (IS_ERR(collie_mbat_low)) { 337 ret = PTR_ERR(collie_mbat_low); 338 goto err_put_gpio_full; 339 } 340 341 collie_bat_main.gpio_charge_on = gpiod_get(&dev->ucb->dev, 342 "main charge on", 343 GPIOD_OUT_LOW); 344 if (IS_ERR(collie_bat_main.gpio_charge_on)) { 345 ret = PTR_ERR(collie_bat_main.gpio_charge_on); 346 goto err_put_mbat_low; 347 } 348 349 /* COLLIE_GPIO_MBAT_ON = GPIO 7 on the UCB (TC35143) */ 350 collie_bat_main.gpio_bat = gpiochip_request_own_desc(gc, 351 7, 352 "main battery", 353 GPIO_ACTIVE_HIGH, 354 GPIOD_OUT_LOW); 355 if (IS_ERR(collie_bat_main.gpio_bat)) { 356 ret = PTR_ERR(collie_bat_main.gpio_bat); 357 goto err_put_gpio_charge_on; 358 } 359 360 /* COLLIE_GPIO_TMP_ON = GPIO 9 on the UCB (TC35143) */ 361 collie_bat_main.gpio_temp = gpiochip_request_own_desc(gc, 362 9, 363 "main battery temp", 364 GPIO_ACTIVE_HIGH, 365 GPIOD_OUT_LOW); 366 if (IS_ERR(collie_bat_main.gpio_temp)) { 367 ret = PTR_ERR(collie_bat_main.gpio_temp); 368 goto err_free_gpio_bat; 369 } 370 371 /* 372 * Obtain the backup battery COLLIE_GPIO_BBAT_ON which is 373 * GPIO 8 on the UCB (TC35143) 374 */ 375 collie_bat_bu.gpio_bat = gpiochip_request_own_desc(gc, 376 8, 377 "backup battery", 378 GPIO_ACTIVE_HIGH, 379 GPIOD_OUT_LOW); 380 if (IS_ERR(collie_bat_bu.gpio_bat)) { 381 ret = PTR_ERR(collie_bat_bu.gpio_bat); 382 goto err_free_gpio_temp; 383 } 384 385 mutex_init(&collie_bat_main.work_lock); 386 387 INIT_WORK(&bat_work, collie_bat_work); 388 389 psy_main_cfg.drv_data = &collie_bat_main; 390 collie_bat_main.psy = power_supply_register(&dev->ucb->dev, 391 &collie_bat_main_desc, 392 &psy_main_cfg); 393 if (IS_ERR(collie_bat_main.psy)) { 394 ret = PTR_ERR(collie_bat_main.psy); 395 goto err_psy_reg_main; 396 } 397 398 psy_bu_cfg.drv_data = &collie_bat_bu; 399 collie_bat_bu.psy = power_supply_register(&dev->ucb->dev, 400 &collie_bat_bu_desc, 401 &psy_bu_cfg); 402 if (IS_ERR(collie_bat_bu.psy)) { 403 ret = PTR_ERR(collie_bat_bu.psy); 404 goto err_psy_reg_bu; 405 } 406 407 ret = request_irq(gpio_to_irq(COLLIE_GPIO_CO), 408 collie_bat_gpio_isr, 409 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 410 "main full", &collie_bat_main); 411 if (ret) 412 goto err_irq; 413 414 device_init_wakeup(&ucb->dev, 1); 415 schedule_work(&bat_work); 416 417 return 0; 418 419err_irq: 420 power_supply_unregister(collie_bat_bu.psy); 421err_psy_reg_bu: 422 power_supply_unregister(collie_bat_main.psy); 423err_psy_reg_main: 424 /* see comment in collie_bat_remove */ 425 cancel_work_sync(&bat_work); 426 gpiochip_free_own_desc(collie_bat_bu.gpio_bat); 427err_free_gpio_temp: 428 gpiochip_free_own_desc(collie_bat_main.gpio_temp); 429err_free_gpio_bat: 430 gpiochip_free_own_desc(collie_bat_main.gpio_bat); 431err_put_gpio_charge_on: 432 gpiod_put(collie_bat_main.gpio_charge_on); 433err_put_mbat_low: 434 gpiod_put(collie_mbat_low); 435err_put_gpio_full: 436 gpiod_put(collie_bat_main.gpio_full); 437 438 return ret; 439} 440 441static void collie_bat_remove(struct ucb1x00_dev *dev) 442{ 443 free_irq(gpio_to_irq(COLLIE_GPIO_CO), &collie_bat_main); 444 power_supply_unregister(collie_bat_bu.psy); 445 power_supply_unregister(collie_bat_main.psy); 446 447 /* These are obtained from the machine */ 448 gpiod_put(collie_bat_main.gpio_full); 449 gpiod_put(collie_mbat_low); 450 gpiod_put(collie_bat_main.gpio_charge_on); 451 /* These are directly from the UCB so let's free them */ 452 gpiochip_free_own_desc(collie_bat_main.gpio_bat); 453 gpiochip_free_own_desc(collie_bat_main.gpio_temp); 454 gpiochip_free_own_desc(collie_bat_bu.gpio_bat); 455 /* 456 * Now cancel the bat_work. We won't get any more schedules, 457 * since all sources (isr and external_power_changed) are 458 * unregistered now. 459 */ 460 cancel_work_sync(&bat_work); 461} 462 463static struct ucb1x00_driver collie_bat_driver = { 464 .add = collie_bat_probe, 465 .remove = collie_bat_remove, 466 .suspend = collie_bat_suspend, 467 .resume = collie_bat_resume, 468}; 469 470static int __init collie_bat_init(void) 471{ 472 return ucb1x00_register_driver(&collie_bat_driver); 473} 474 475static void __exit collie_bat_exit(void) 476{ 477 ucb1x00_unregister_driver(&collie_bat_driver); 478} 479 480module_init(collie_bat_init); 481module_exit(collie_bat_exit); 482 483MODULE_LICENSE("GPL"); 484MODULE_AUTHOR("Thomas Kunze"); 485MODULE_DESCRIPTION("Collie battery driver");