Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Battery and Power Management code for the Sharp SL-5x00
0004  *
0005  * Copyright (C) 2009 Thomas Kunze
0006  *
0007  * based on tosa_battery.c
0008  */
0009 #include <linux/kernel.h>
0010 #include <linux/module.h>
0011 #include <linux/power_supply.h>
0012 #include <linux/delay.h>
0013 #include <linux/spinlock.h>
0014 #include <linux/interrupt.h>
0015 #include <linux/gpio/driver.h>
0016 #include <linux/gpio/machine.h>
0017 #include <linux/gpio/consumer.h>
0018 #include <linux/mfd/ucb1x00.h>
0019 
0020 #include <asm/mach/sharpsl_param.h>
0021 #include <asm/mach-types.h>
0022 #include <mach/collie.h>
0023 
0024 static DEFINE_MUTEX(bat_lock); /* protects gpio pins */
0025 static struct work_struct bat_work;
0026 static struct ucb1x00 *ucb;
0027 
0028 struct collie_bat {
0029     int status;
0030     struct power_supply *psy;
0031     int full_chrg;
0032 
0033     struct mutex work_lock; /* protects data */
0034 
0035     bool (*is_present)(struct collie_bat *bat);
0036     struct gpio_desc *gpio_full;
0037     struct gpio_desc *gpio_charge_on;
0038 
0039     int technology;
0040 
0041     struct gpio_desc *gpio_bat;
0042     int adc_bat;
0043     int adc_bat_divider;
0044     int bat_max;
0045     int bat_min;
0046 
0047     struct gpio_desc *gpio_temp;
0048     int adc_temp;
0049     int adc_temp_divider;
0050 };
0051 
0052 static struct collie_bat collie_bat_main;
0053 
0054 static unsigned long collie_read_bat(struct collie_bat *bat)
0055 {
0056     unsigned long value = 0;
0057 
0058     if (!bat->gpio_bat || bat->adc_bat < 0)
0059         return 0;
0060     mutex_lock(&bat_lock);
0061     gpiod_set_value(bat->gpio_bat, 1);
0062     msleep(5);
0063     ucb1x00_adc_enable(ucb);
0064     value = ucb1x00_adc_read(ucb, bat->adc_bat, UCB_SYNC);
0065     ucb1x00_adc_disable(ucb);
0066     gpiod_set_value(bat->gpio_bat, 0);
0067     mutex_unlock(&bat_lock);
0068     value = value * 1000000 / bat->adc_bat_divider;
0069 
0070     return value;
0071 }
0072 
0073 static unsigned long collie_read_temp(struct collie_bat *bat)
0074 {
0075     unsigned long value = 0;
0076     if (!bat->gpio_temp || bat->adc_temp < 0)
0077         return 0;
0078 
0079     mutex_lock(&bat_lock);
0080     gpiod_set_value(bat->gpio_temp, 1);
0081     msleep(5);
0082     ucb1x00_adc_enable(ucb);
0083     value = ucb1x00_adc_read(ucb, bat->adc_temp, UCB_SYNC);
0084     ucb1x00_adc_disable(ucb);
0085     gpiod_set_value(bat->gpio_temp, 0);
0086     mutex_unlock(&bat_lock);
0087 
0088     value = value * 10000 / bat->adc_temp_divider;
0089 
0090     return value;
0091 }
0092 
0093 static int collie_bat_get_property(struct power_supply *psy,
0094                 enum power_supply_property psp,
0095                 union power_supply_propval *val)
0096 {
0097     int ret = 0;
0098     struct collie_bat *bat = power_supply_get_drvdata(psy);
0099 
0100     if (bat->is_present && !bat->is_present(bat)
0101             && psp != POWER_SUPPLY_PROP_PRESENT) {
0102         return -ENODEV;
0103     }
0104 
0105     switch (psp) {
0106     case POWER_SUPPLY_PROP_STATUS:
0107         val->intval = bat->status;
0108         break;
0109     case POWER_SUPPLY_PROP_TECHNOLOGY:
0110         val->intval = bat->technology;
0111         break;
0112     case POWER_SUPPLY_PROP_VOLTAGE_NOW:
0113         val->intval = collie_read_bat(bat);
0114         break;
0115     case POWER_SUPPLY_PROP_VOLTAGE_MAX:
0116         if (bat->full_chrg == -1)
0117             val->intval = bat->bat_max;
0118         else
0119             val->intval = bat->full_chrg;
0120         break;
0121     case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
0122         val->intval = bat->bat_max;
0123         break;
0124     case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
0125         val->intval = bat->bat_min;
0126         break;
0127     case POWER_SUPPLY_PROP_TEMP:
0128         val->intval = collie_read_temp(bat);
0129         break;
0130     case POWER_SUPPLY_PROP_PRESENT:
0131         val->intval = bat->is_present ? bat->is_present(bat) : 1;
0132         break;
0133     default:
0134         ret = -EINVAL;
0135         break;
0136     }
0137     return ret;
0138 }
0139 
0140 static void collie_bat_external_power_changed(struct power_supply *psy)
0141 {
0142     schedule_work(&bat_work);
0143 }
0144 
0145 static irqreturn_t collie_bat_gpio_isr(int irq, void *data)
0146 {
0147     pr_info("collie_bat_gpio irq\n");
0148     schedule_work(&bat_work);
0149     return IRQ_HANDLED;
0150 }
0151 
0152 static void collie_bat_update(struct collie_bat *bat)
0153 {
0154     int old;
0155     struct power_supply *psy = bat->psy;
0156 
0157     mutex_lock(&bat->work_lock);
0158 
0159     old = bat->status;
0160 
0161     if (bat->is_present && !bat->is_present(bat)) {
0162         printk(KERN_NOTICE "%s not present\n", psy->desc->name);
0163         bat->status = POWER_SUPPLY_STATUS_UNKNOWN;
0164         bat->full_chrg = -1;
0165     } else if (power_supply_am_i_supplied(psy)) {
0166         if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING) {
0167             gpiod_set_value(bat->gpio_charge_on, 1);
0168             mdelay(15);
0169         }
0170 
0171         if (gpiod_get_value(bat->gpio_full)) {
0172             if (old == POWER_SUPPLY_STATUS_CHARGING ||
0173                     bat->full_chrg == -1)
0174                 bat->full_chrg = collie_read_bat(bat);
0175 
0176             gpiod_set_value(bat->gpio_charge_on, 0);
0177             bat->status = POWER_SUPPLY_STATUS_FULL;
0178         } else {
0179             gpiod_set_value(bat->gpio_charge_on, 1);
0180             bat->status = POWER_SUPPLY_STATUS_CHARGING;
0181         }
0182     } else {
0183         gpiod_set_value(bat->gpio_charge_on, 0);
0184         bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
0185     }
0186 
0187     if (old != bat->status)
0188         power_supply_changed(psy);
0189 
0190     mutex_unlock(&bat->work_lock);
0191 }
0192 
0193 static void collie_bat_work(struct work_struct *work)
0194 {
0195     collie_bat_update(&collie_bat_main);
0196 }
0197 
0198 
0199 static enum power_supply_property collie_bat_main_props[] = {
0200     POWER_SUPPLY_PROP_STATUS,
0201     POWER_SUPPLY_PROP_TECHNOLOGY,
0202     POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
0203     POWER_SUPPLY_PROP_VOLTAGE_NOW,
0204     POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
0205     POWER_SUPPLY_PROP_VOLTAGE_MAX,
0206     POWER_SUPPLY_PROP_PRESENT,
0207     POWER_SUPPLY_PROP_TEMP,
0208 };
0209 
0210 static enum power_supply_property collie_bat_bu_props[] = {
0211     POWER_SUPPLY_PROP_STATUS,
0212     POWER_SUPPLY_PROP_TECHNOLOGY,
0213     POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
0214     POWER_SUPPLY_PROP_VOLTAGE_NOW,
0215     POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
0216     POWER_SUPPLY_PROP_VOLTAGE_MAX,
0217     POWER_SUPPLY_PROP_PRESENT,
0218 };
0219 
0220 static const struct power_supply_desc collie_bat_main_desc = {
0221     .name       = "main-battery",
0222     .type       = POWER_SUPPLY_TYPE_BATTERY,
0223     .properties = collie_bat_main_props,
0224     .num_properties = ARRAY_SIZE(collie_bat_main_props),
0225     .get_property   = collie_bat_get_property,
0226     .external_power_changed = collie_bat_external_power_changed,
0227     .use_for_apm    = 1,
0228 };
0229 
0230 static struct collie_bat collie_bat_main = {
0231     .status = POWER_SUPPLY_STATUS_DISCHARGING,
0232     .full_chrg = -1,
0233     .psy = NULL,
0234 
0235     .gpio_full = NULL,
0236     .gpio_charge_on = NULL,
0237 
0238     .technology = POWER_SUPPLY_TECHNOLOGY_LIPO,
0239 
0240     .gpio_bat = NULL,
0241     .adc_bat = UCB_ADC_INP_AD1,
0242     .adc_bat_divider = 155,
0243     .bat_max = 4310000,
0244     .bat_min = 1551 * 1000000 / 414,
0245 
0246     .gpio_temp = NULL,
0247     .adc_temp = UCB_ADC_INP_AD0,
0248     .adc_temp_divider = 10000,
0249 };
0250 
0251 static const struct power_supply_desc collie_bat_bu_desc = {
0252     .name       = "backup-battery",
0253     .type       = POWER_SUPPLY_TYPE_BATTERY,
0254     .properties = collie_bat_bu_props,
0255     .num_properties = ARRAY_SIZE(collie_bat_bu_props),
0256     .get_property   = collie_bat_get_property,
0257     .external_power_changed = collie_bat_external_power_changed,
0258 };
0259 
0260 static struct collie_bat collie_bat_bu = {
0261     .status = POWER_SUPPLY_STATUS_UNKNOWN,
0262     .full_chrg = -1,
0263     .psy = NULL,
0264 
0265     .gpio_full = NULL,
0266     .gpio_charge_on = NULL,
0267 
0268     .technology = POWER_SUPPLY_TECHNOLOGY_LiMn,
0269 
0270     .gpio_bat = NULL,
0271     .adc_bat = UCB_ADC_INP_AD1,
0272     .adc_bat_divider = 155,
0273     .bat_max = 3000000,
0274     .bat_min = 1900000,
0275 
0276     .gpio_temp = NULL,
0277     .adc_temp = -1,
0278     .adc_temp_divider = -1,
0279 };
0280 
0281 /* Obtained but unused GPIO */
0282 static struct gpio_desc *collie_mbat_low;
0283 
0284 #ifdef CONFIG_PM
0285 static int wakeup_enabled;
0286 
0287 static int collie_bat_suspend(struct ucb1x00_dev *dev)
0288 {
0289     /* flush all pending status updates */
0290     flush_work(&bat_work);
0291 
0292     if (device_may_wakeup(&dev->ucb->dev) &&
0293         collie_bat_main.status == POWER_SUPPLY_STATUS_CHARGING)
0294         wakeup_enabled = !enable_irq_wake(gpiod_to_irq(collie_bat_main.gpio_full));
0295     else
0296         wakeup_enabled = 0;
0297 
0298     return 0;
0299 }
0300 
0301 static int collie_bat_resume(struct ucb1x00_dev *dev)
0302 {
0303     if (wakeup_enabled)
0304         disable_irq_wake(gpiod_to_irq(collie_bat_main.gpio_full));
0305 
0306     /* things may have changed while we were away */
0307     schedule_work(&bat_work);
0308     return 0;
0309 }
0310 #else
0311 #define collie_bat_suspend NULL
0312 #define collie_bat_resume NULL
0313 #endif
0314 
0315 static int collie_bat_probe(struct ucb1x00_dev *dev)
0316 {
0317     int ret;
0318     struct power_supply_config psy_main_cfg = {}, psy_bu_cfg = {};
0319     struct gpio_chip *gc = &dev->ucb->gpio;
0320 
0321     if (!machine_is_collie())
0322         return -ENODEV;
0323 
0324     ucb = dev->ucb;
0325 
0326     /* Obtain all the main battery GPIOs */
0327     collie_bat_main.gpio_full = gpiod_get(&dev->ucb->dev,
0328                           "main battery full",
0329                           GPIOD_IN);
0330     if (IS_ERR(collie_bat_main.gpio_full))
0331         return PTR_ERR(collie_bat_main.gpio_full);
0332 
0333     collie_mbat_low = gpiod_get(&dev->ucb->dev,
0334                     "main battery low",
0335                     GPIOD_IN);
0336     if (IS_ERR(collie_mbat_low)) {
0337         ret = PTR_ERR(collie_mbat_low);
0338         goto err_put_gpio_full;
0339     }
0340 
0341     collie_bat_main.gpio_charge_on = gpiod_get(&dev->ucb->dev,
0342                            "main charge on",
0343                            GPIOD_OUT_LOW);
0344     if (IS_ERR(collie_bat_main.gpio_charge_on)) {
0345         ret = PTR_ERR(collie_bat_main.gpio_charge_on);
0346         goto err_put_mbat_low;
0347     }
0348 
0349     /* COLLIE_GPIO_MBAT_ON = GPIO 7 on the UCB (TC35143) */
0350     collie_bat_main.gpio_bat = gpiochip_request_own_desc(gc,
0351                         7,
0352                         "main battery",
0353                         GPIO_ACTIVE_HIGH,
0354                         GPIOD_OUT_LOW);
0355     if (IS_ERR(collie_bat_main.gpio_bat)) {
0356         ret = PTR_ERR(collie_bat_main.gpio_bat);
0357         goto err_put_gpio_charge_on;
0358     }
0359 
0360     /* COLLIE_GPIO_TMP_ON = GPIO 9 on the UCB (TC35143) */
0361     collie_bat_main.gpio_temp = gpiochip_request_own_desc(gc,
0362                         9,
0363                         "main battery temp",
0364                         GPIO_ACTIVE_HIGH,
0365                         GPIOD_OUT_LOW);
0366     if (IS_ERR(collie_bat_main.gpio_temp)) {
0367         ret = PTR_ERR(collie_bat_main.gpio_temp);
0368         goto err_free_gpio_bat;
0369     }
0370 
0371     /*
0372      * Obtain the backup battery COLLIE_GPIO_BBAT_ON which is
0373      * GPIO 8 on the UCB (TC35143)
0374      */
0375     collie_bat_bu.gpio_bat = gpiochip_request_own_desc(gc,
0376                         8,
0377                         "backup battery",
0378                         GPIO_ACTIVE_HIGH,
0379                         GPIOD_OUT_LOW);
0380     if (IS_ERR(collie_bat_bu.gpio_bat)) {
0381         ret = PTR_ERR(collie_bat_bu.gpio_bat);
0382         goto err_free_gpio_temp;
0383     }
0384 
0385     mutex_init(&collie_bat_main.work_lock);
0386 
0387     INIT_WORK(&bat_work, collie_bat_work);
0388 
0389     psy_main_cfg.drv_data = &collie_bat_main;
0390     collie_bat_main.psy = power_supply_register(&dev->ucb->dev,
0391                             &collie_bat_main_desc,
0392                             &psy_main_cfg);
0393     if (IS_ERR(collie_bat_main.psy)) {
0394         ret = PTR_ERR(collie_bat_main.psy);
0395         goto err_psy_reg_main;
0396     }
0397 
0398     psy_bu_cfg.drv_data = &collie_bat_bu;
0399     collie_bat_bu.psy = power_supply_register(&dev->ucb->dev,
0400                           &collie_bat_bu_desc,
0401                           &psy_bu_cfg);
0402     if (IS_ERR(collie_bat_bu.psy)) {
0403         ret = PTR_ERR(collie_bat_bu.psy);
0404         goto err_psy_reg_bu;
0405     }
0406 
0407     ret = request_irq(gpio_to_irq(COLLIE_GPIO_CO),
0408                 collie_bat_gpio_isr,
0409                 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
0410                 "main full", &collie_bat_main);
0411     if (ret)
0412         goto err_irq;
0413 
0414     device_init_wakeup(&ucb->dev, 1);
0415     schedule_work(&bat_work);
0416 
0417     return 0;
0418 
0419 err_irq:
0420     power_supply_unregister(collie_bat_bu.psy);
0421 err_psy_reg_bu:
0422     power_supply_unregister(collie_bat_main.psy);
0423 err_psy_reg_main:
0424     /* see comment in collie_bat_remove */
0425     cancel_work_sync(&bat_work);
0426     gpiochip_free_own_desc(collie_bat_bu.gpio_bat);
0427 err_free_gpio_temp:
0428     gpiochip_free_own_desc(collie_bat_main.gpio_temp);
0429 err_free_gpio_bat:
0430     gpiochip_free_own_desc(collie_bat_main.gpio_bat);
0431 err_put_gpio_charge_on:
0432     gpiod_put(collie_bat_main.gpio_charge_on);
0433 err_put_mbat_low:
0434     gpiod_put(collie_mbat_low);
0435 err_put_gpio_full:
0436     gpiod_put(collie_bat_main.gpio_full);
0437 
0438     return ret;
0439 }
0440 
0441 static void collie_bat_remove(struct ucb1x00_dev *dev)
0442 {
0443     free_irq(gpio_to_irq(COLLIE_GPIO_CO), &collie_bat_main);
0444     power_supply_unregister(collie_bat_bu.psy);
0445     power_supply_unregister(collie_bat_main.psy);
0446 
0447     /* These are obtained from the machine */
0448     gpiod_put(collie_bat_main.gpio_full);
0449     gpiod_put(collie_mbat_low);
0450     gpiod_put(collie_bat_main.gpio_charge_on);
0451     /* These are directly from the UCB so let's free them */
0452     gpiochip_free_own_desc(collie_bat_main.gpio_bat);
0453     gpiochip_free_own_desc(collie_bat_main.gpio_temp);
0454     gpiochip_free_own_desc(collie_bat_bu.gpio_bat);
0455     /*
0456      * Now cancel the bat_work.  We won't get any more schedules,
0457      * since all sources (isr and external_power_changed) are
0458      * unregistered now.
0459      */
0460     cancel_work_sync(&bat_work);
0461 }
0462 
0463 static struct ucb1x00_driver collie_bat_driver = {
0464     .add        = collie_bat_probe,
0465     .remove     = collie_bat_remove,
0466     .suspend    = collie_bat_suspend,
0467     .resume     = collie_bat_resume,
0468 };
0469 
0470 static int __init collie_bat_init(void)
0471 {
0472     return ucb1x00_register_driver(&collie_bat_driver);
0473 }
0474 
0475 static void __exit collie_bat_exit(void)
0476 {
0477     ucb1x00_unregister_driver(&collie_bat_driver);
0478 }
0479 
0480 module_init(collie_bat_init);
0481 module_exit(collie_bat_exit);
0482 
0483 MODULE_LICENSE("GPL");
0484 MODULE_AUTHOR("Thomas Kunze");
0485 MODULE_DESCRIPTION("Collie battery driver");