root/drivers/power/supply/tosa_battery.c

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

DEFINITIONS

This source file includes following definitions.
  1. tosa_read_bat
  2. tosa_read_temp
  3. tosa_bat_get_property
  4. tosa_jacket_bat_is_present
  5. tosa_bat_external_power_changed
  6. tosa_bat_gpio_isr
  7. tosa_bat_update
  8. tosa_bat_work
  9. tosa_bat_suspend
  10. tosa_bat_resume
  11. tosa_bat_probe
  12. tosa_bat_remove

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

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