root/drivers/power/supply/collie_battery.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. collie_read_bat
  2. collie_read_temp
  3. collie_bat_get_property
  4. collie_bat_external_power_changed
  5. collie_bat_gpio_isr
  6. collie_bat_update
  7. collie_bat_work
  8. collie_bat_suspend
  9. collie_bat_resume
  10. collie_bat_probe
  11. collie_bat_remove
  12. collie_bat_init
  13. collie_bat_exit

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

/* [<][>][^][v][top][bottom][index][help] */