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-6000x
0004  *
0005  * Copyright (c) 2005 Dirk Opfer
0006  * Copyright (c) 2008 Dmitry Baryshkov
0007  */
0008 #include <linux/kernel.h>
0009 #include <linux/module.h>
0010 #include <linux/power_supply.h>
0011 #include <linux/wm97xx.h>
0012 #include <linux/delay.h>
0013 #include <linux/spinlock.h>
0014 #include <linux/interrupt.h>
0015 #include <linux/gpio/consumer.h>
0016 
0017 #include <asm/mach-types.h>
0018 
0019 static DEFINE_MUTEX(bat_lock); /* protects gpio pins */
0020 static struct work_struct bat_work;
0021 
0022 struct tosa_bat {
0023     int status;
0024     struct power_supply *psy;
0025     int full_chrg;
0026 
0027     struct mutex work_lock; /* protects data */
0028 
0029     bool (*is_present)(struct tosa_bat *bat);
0030     struct gpio_desc *gpiod_full;
0031     struct gpio_desc *gpiod_charge_off;
0032 
0033     int technology;
0034 
0035     struct gpio_desc *gpiod_bat;
0036     int adc_bat;
0037     int adc_bat_divider;
0038     int bat_max;
0039     int bat_min;
0040 
0041     struct gpio_desc *gpiod_temp;
0042     int adc_temp;
0043     int adc_temp_divider;
0044 };
0045 
0046 static struct gpio_desc *jacket_detect;
0047 static struct tosa_bat tosa_bat_main;
0048 static struct tosa_bat tosa_bat_jacket;
0049 
0050 static unsigned long tosa_read_bat(struct tosa_bat *bat)
0051 {
0052     unsigned long value = 0;
0053 
0054     if (!bat->gpiod_bat || bat->adc_bat < 0)
0055         return 0;
0056 
0057     mutex_lock(&bat_lock);
0058     gpiod_set_value(bat->gpiod_bat, 1);
0059     msleep(5);
0060     value = wm97xx_read_aux_adc(dev_get_drvdata(bat->psy->dev.parent),
0061             bat->adc_bat);
0062     gpiod_set_value(bat->gpiod_bat, 0);
0063     mutex_unlock(&bat_lock);
0064 
0065     value = value * 1000000 / bat->adc_bat_divider;
0066 
0067     return value;
0068 }
0069 
0070 static unsigned long tosa_read_temp(struct tosa_bat *bat)
0071 {
0072     unsigned long value = 0;
0073 
0074     if (!bat->gpiod_temp || bat->adc_temp < 0)
0075         return 0;
0076 
0077     mutex_lock(&bat_lock);
0078     gpiod_set_value(bat->gpiod_temp, 1);
0079     msleep(5);
0080     value = wm97xx_read_aux_adc(dev_get_drvdata(bat->psy->dev.parent),
0081             bat->adc_temp);
0082     gpiod_set_value(bat->gpiod_temp, 0);
0083     mutex_unlock(&bat_lock);
0084 
0085     value = value * 10000 / bat->adc_temp_divider;
0086 
0087     return value;
0088 }
0089 
0090 static int tosa_bat_get_property(struct power_supply *psy,
0091                 enum power_supply_property psp,
0092                 union power_supply_propval *val)
0093 {
0094     int ret = 0;
0095     struct tosa_bat *bat = power_supply_get_drvdata(psy);
0096 
0097     if (bat->is_present && !bat->is_present(bat)
0098             && psp != POWER_SUPPLY_PROP_PRESENT) {
0099         return -ENODEV;
0100     }
0101 
0102     switch (psp) {
0103     case POWER_SUPPLY_PROP_STATUS:
0104         val->intval = bat->status;
0105         break;
0106     case POWER_SUPPLY_PROP_TECHNOLOGY:
0107         val->intval = bat->technology;
0108         break;
0109     case POWER_SUPPLY_PROP_VOLTAGE_NOW:
0110         val->intval = tosa_read_bat(bat);
0111         break;
0112     case POWER_SUPPLY_PROP_VOLTAGE_MAX:
0113         if (bat->full_chrg == -1)
0114             val->intval = bat->bat_max;
0115         else
0116             val->intval = bat->full_chrg;
0117         break;
0118     case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
0119         val->intval = bat->bat_max;
0120         break;
0121     case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
0122         val->intval = bat->bat_min;
0123         break;
0124     case POWER_SUPPLY_PROP_TEMP:
0125         val->intval = tosa_read_temp(bat);
0126         break;
0127     case POWER_SUPPLY_PROP_PRESENT:
0128         val->intval = bat->is_present ? bat->is_present(bat) : 1;
0129         break;
0130     default:
0131         ret = -EINVAL;
0132         break;
0133     }
0134     return ret;
0135 }
0136 
0137 static bool tosa_jacket_bat_is_present(struct tosa_bat *bat)
0138 {
0139     return gpiod_get_value(jacket_detect) == 0;
0140 }
0141 
0142 static void tosa_bat_external_power_changed(struct power_supply *psy)
0143 {
0144     schedule_work(&bat_work);
0145 }
0146 
0147 static irqreturn_t tosa_bat_gpio_isr(int irq, void *data)
0148 {
0149     pr_info("tosa_bat_gpio irq\n");
0150     schedule_work(&bat_work);
0151     return IRQ_HANDLED;
0152 }
0153 
0154 static void tosa_bat_update(struct tosa_bat *bat)
0155 {
0156     int old;
0157     struct power_supply *psy = bat->psy;
0158 
0159     mutex_lock(&bat->work_lock);
0160 
0161     old = bat->status;
0162 
0163     if (bat->is_present && !bat->is_present(bat)) {
0164         printk(KERN_NOTICE "%s not present\n", psy->desc->name);
0165         bat->status = POWER_SUPPLY_STATUS_UNKNOWN;
0166         bat->full_chrg = -1;
0167     } else if (power_supply_am_i_supplied(psy)) {
0168         if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING) {
0169             gpiod_set_value(bat->gpiod_charge_off, 0);
0170             mdelay(15);
0171         }
0172 
0173         if (gpiod_get_value(bat->gpiod_full)) {
0174             if (old == POWER_SUPPLY_STATUS_CHARGING ||
0175                     bat->full_chrg == -1)
0176                 bat->full_chrg = tosa_read_bat(bat);
0177 
0178             gpiod_set_value(bat->gpiod_charge_off, 1);
0179             bat->status = POWER_SUPPLY_STATUS_FULL;
0180         } else {
0181             gpiod_set_value(bat->gpiod_charge_off, 0);
0182             bat->status = POWER_SUPPLY_STATUS_CHARGING;
0183         }
0184     } else {
0185         gpiod_set_value(bat->gpiod_charge_off, 1);
0186         bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
0187     }
0188 
0189     if (old != bat->status)
0190         power_supply_changed(psy);
0191 
0192     mutex_unlock(&bat->work_lock);
0193 }
0194 
0195 static void tosa_bat_work(struct work_struct *work)
0196 {
0197     tosa_bat_update(&tosa_bat_main);
0198     tosa_bat_update(&tosa_bat_jacket);
0199 }
0200 
0201 
0202 static enum power_supply_property tosa_bat_main_props[] = {
0203     POWER_SUPPLY_PROP_STATUS,
0204     POWER_SUPPLY_PROP_TECHNOLOGY,
0205     POWER_SUPPLY_PROP_VOLTAGE_NOW,
0206     POWER_SUPPLY_PROP_VOLTAGE_MAX,
0207     POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
0208     POWER_SUPPLY_PROP_TEMP,
0209     POWER_SUPPLY_PROP_PRESENT,
0210 };
0211 
0212 static enum power_supply_property tosa_bat_bu_props[] = {
0213     POWER_SUPPLY_PROP_STATUS,
0214     POWER_SUPPLY_PROP_TECHNOLOGY,
0215     POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
0216     POWER_SUPPLY_PROP_VOLTAGE_NOW,
0217     POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
0218     POWER_SUPPLY_PROP_PRESENT,
0219 };
0220 
0221 static const struct power_supply_desc tosa_bat_main_desc = {
0222     .name       = "main-battery",
0223     .type       = POWER_SUPPLY_TYPE_BATTERY,
0224     .properties = tosa_bat_main_props,
0225     .num_properties = ARRAY_SIZE(tosa_bat_main_props),
0226     .get_property   = tosa_bat_get_property,
0227     .external_power_changed = tosa_bat_external_power_changed,
0228     .use_for_apm    = 1,
0229 };
0230 
0231 static const struct power_supply_desc tosa_bat_jacket_desc = {
0232     .name       = "jacket-battery",
0233     .type       = POWER_SUPPLY_TYPE_BATTERY,
0234     .properties = tosa_bat_main_props,
0235     .num_properties = ARRAY_SIZE(tosa_bat_main_props),
0236     .get_property   = tosa_bat_get_property,
0237     .external_power_changed = tosa_bat_external_power_changed,
0238 };
0239 
0240 static const struct power_supply_desc tosa_bat_bu_desc = {
0241     .name       = "backup-battery",
0242     .type       = POWER_SUPPLY_TYPE_BATTERY,
0243     .properties = tosa_bat_bu_props,
0244     .num_properties = ARRAY_SIZE(tosa_bat_bu_props),
0245     .get_property   = tosa_bat_get_property,
0246     .external_power_changed = tosa_bat_external_power_changed,
0247 };
0248 
0249 static struct tosa_bat tosa_bat_main = {
0250     .status = POWER_SUPPLY_STATUS_DISCHARGING,
0251     .full_chrg = -1,
0252     .psy = NULL,
0253 
0254     .gpiod_full = NULL,
0255     .gpiod_charge_off = NULL,
0256 
0257     .technology = POWER_SUPPLY_TECHNOLOGY_LIPO,
0258 
0259     .gpiod_bat = NULL,
0260     .adc_bat = WM97XX_AUX_ID3,
0261     .adc_bat_divider = 414,
0262     .bat_max = 4310000,
0263     .bat_min = 1551 * 1000000 / 414,
0264 
0265     .gpiod_temp = NULL,
0266     .adc_temp = WM97XX_AUX_ID2,
0267     .adc_temp_divider = 10000,
0268 };
0269 
0270 static struct tosa_bat tosa_bat_jacket = {
0271     .status = POWER_SUPPLY_STATUS_DISCHARGING,
0272     .full_chrg = -1,
0273     .psy = NULL,
0274 
0275     .is_present = tosa_jacket_bat_is_present,
0276     .gpiod_full = NULL,
0277     .gpiod_charge_off = NULL,
0278 
0279     .technology = POWER_SUPPLY_TECHNOLOGY_LIPO,
0280 
0281     .gpiod_bat = NULL,
0282     .adc_bat = WM97XX_AUX_ID3,
0283     .adc_bat_divider = 414,
0284     .bat_max = 4310000,
0285     .bat_min = 1551 * 1000000 / 414,
0286 
0287     .gpiod_temp = NULL,
0288     .adc_temp = WM97XX_AUX_ID2,
0289     .adc_temp_divider = 10000,
0290 };
0291 
0292 static struct tosa_bat tosa_bat_bu = {
0293     .status = POWER_SUPPLY_STATUS_UNKNOWN,
0294     .full_chrg = -1,
0295     .psy = NULL,
0296 
0297     .gpiod_full = NULL,
0298     .gpiod_charge_off = NULL,
0299 
0300     .technology = POWER_SUPPLY_TECHNOLOGY_LiMn,
0301 
0302     .gpiod_bat = NULL,
0303     .adc_bat = WM97XX_AUX_ID4,
0304     .adc_bat_divider = 1266,
0305 
0306     .gpiod_temp = NULL,
0307     .adc_temp = -1,
0308     .adc_temp_divider = -1,
0309 };
0310 
0311 #ifdef CONFIG_PM
0312 static int tosa_bat_suspend(struct platform_device *dev, pm_message_t state)
0313 {
0314     /* flush all pending status updates */
0315     flush_work(&bat_work);
0316     return 0;
0317 }
0318 
0319 static int tosa_bat_resume(struct platform_device *dev)
0320 {
0321     /* things may have changed while we were away */
0322     schedule_work(&bat_work);
0323     return 0;
0324 }
0325 #else
0326 #define tosa_bat_suspend NULL
0327 #define tosa_bat_resume NULL
0328 #endif
0329 
0330 static int tosa_bat_probe(struct platform_device *pdev)
0331 {
0332     int ret;
0333     struct power_supply_config main_psy_cfg = {},
0334                    jacket_psy_cfg = {},
0335                    bu_psy_cfg = {};
0336     struct device *dev = &pdev->dev;
0337     struct gpio_desc *dummy;
0338 
0339     if (!machine_is_tosa())
0340         return -ENODEV;
0341 
0342     /* Main charging control GPIOs */
0343     tosa_bat_main.gpiod_charge_off = devm_gpiod_get(dev, "main charge off", GPIOD_OUT_HIGH);
0344     if (IS_ERR(tosa_bat_main.gpiod_charge_off))
0345         return dev_err_probe(dev, PTR_ERR(tosa_bat_main.gpiod_charge_off),
0346                      "no main charger GPIO\n");
0347     tosa_bat_jacket.gpiod_charge_off = devm_gpiod_get(dev, "jacket charge off", GPIOD_OUT_HIGH);
0348     if (IS_ERR(tosa_bat_jacket.gpiod_charge_off))
0349         return dev_err_probe(dev, PTR_ERR(tosa_bat_jacket.gpiod_charge_off),
0350                      "no jacket charger GPIO\n");
0351 
0352     /* Per-battery output check (routes battery voltage to ADC) */
0353     tosa_bat_main.gpiod_bat = devm_gpiod_get(dev, "main battery", GPIOD_OUT_LOW);
0354     if (IS_ERR(tosa_bat_main.gpiod_bat))
0355         return dev_err_probe(dev, PTR_ERR(tosa_bat_main.gpiod_bat),
0356                      "no main battery GPIO\n");
0357     tosa_bat_jacket.gpiod_bat = devm_gpiod_get(dev, "jacket battery", GPIOD_OUT_LOW);
0358     if (IS_ERR(tosa_bat_jacket.gpiod_bat))
0359         return dev_err_probe(dev, PTR_ERR(tosa_bat_jacket.gpiod_bat),
0360                      "no jacket battery GPIO\n");
0361     tosa_bat_bu.gpiod_bat = devm_gpiod_get(dev, "backup battery", GPIOD_OUT_LOW);
0362     if (IS_ERR(tosa_bat_bu.gpiod_bat))
0363         return dev_err_probe(dev, PTR_ERR(tosa_bat_bu.gpiod_bat),
0364                      "no backup battery GPIO\n");
0365 
0366     /* Battery full detect GPIOs (using PXA SoC GPIOs) */
0367     tosa_bat_main.gpiod_full = devm_gpiod_get(dev, "main battery full", GPIOD_IN);
0368     if (IS_ERR(tosa_bat_main.gpiod_full))
0369         return dev_err_probe(dev, PTR_ERR(tosa_bat_main.gpiod_full),
0370                      "no main battery full GPIO\n");
0371     tosa_bat_jacket.gpiod_full = devm_gpiod_get(dev, "jacket battery full", GPIOD_IN);
0372     if (IS_ERR(tosa_bat_jacket.gpiod_full))
0373         return dev_err_probe(dev, PTR_ERR(tosa_bat_jacket.gpiod_full),
0374                      "no jacket battery full GPIO\n");
0375 
0376     /* Battery temperature GPIOs (routes thermistor voltage to ADC) */
0377     tosa_bat_main.gpiod_temp = devm_gpiod_get(dev, "main battery temp", GPIOD_OUT_LOW);
0378     if (IS_ERR(tosa_bat_main.gpiod_temp))
0379         return dev_err_probe(dev, PTR_ERR(tosa_bat_main.gpiod_temp),
0380                      "no main battery temp GPIO\n");
0381     tosa_bat_jacket.gpiod_temp = devm_gpiod_get(dev, "jacket battery temp", GPIOD_OUT_LOW);
0382     if (IS_ERR(tosa_bat_jacket.gpiod_temp))
0383         return dev_err_probe(dev, PTR_ERR(tosa_bat_jacket.gpiod_temp),
0384                      "no jacket battery temp GPIO\n");
0385 
0386     /* Jacket detect GPIO */
0387     jacket_detect = devm_gpiod_get(dev, "jacket detect", GPIOD_IN);
0388     if (IS_ERR(jacket_detect))
0389         return dev_err_probe(dev, PTR_ERR(jacket_detect),
0390                      "no jacket detect GPIO\n");
0391 
0392     /* Battery low indication GPIOs (not used, we just request them) */
0393     dummy = devm_gpiod_get(dev, "main battery low", GPIOD_IN);
0394     if (IS_ERR(dummy))
0395         return dev_err_probe(dev, PTR_ERR(dummy),
0396                      "no main battery low GPIO\n");
0397     dummy = devm_gpiod_get(dev, "jacket battery low", GPIOD_IN);
0398     if (IS_ERR(dummy))
0399         return dev_err_probe(dev, PTR_ERR(dummy),
0400                      "no jacket battery low GPIO\n");
0401 
0402     /* Battery switch GPIO (not used just requested) */
0403     dummy = devm_gpiod_get(dev, "battery switch", GPIOD_OUT_LOW);
0404     if (IS_ERR(dummy))
0405         return dev_err_probe(dev, PTR_ERR(dummy),
0406                      "no battery switch GPIO\n");
0407 
0408     mutex_init(&tosa_bat_main.work_lock);
0409     mutex_init(&tosa_bat_jacket.work_lock);
0410 
0411     INIT_WORK(&bat_work, tosa_bat_work);
0412 
0413     main_psy_cfg.drv_data = &tosa_bat_main;
0414     tosa_bat_main.psy = power_supply_register(dev,
0415                           &tosa_bat_main_desc,
0416                           &main_psy_cfg);
0417     if (IS_ERR(tosa_bat_main.psy)) {
0418         ret = PTR_ERR(tosa_bat_main.psy);
0419         goto err_psy_reg_main;
0420     }
0421 
0422     jacket_psy_cfg.drv_data = &tosa_bat_jacket;
0423     tosa_bat_jacket.psy = power_supply_register(dev,
0424                             &tosa_bat_jacket_desc,
0425                             &jacket_psy_cfg);
0426     if (IS_ERR(tosa_bat_jacket.psy)) {
0427         ret = PTR_ERR(tosa_bat_jacket.psy);
0428         goto err_psy_reg_jacket;
0429     }
0430 
0431     bu_psy_cfg.drv_data = &tosa_bat_bu;
0432     tosa_bat_bu.psy = power_supply_register(dev, &tosa_bat_bu_desc,
0433                         &bu_psy_cfg);
0434     if (IS_ERR(tosa_bat_bu.psy)) {
0435         ret = PTR_ERR(tosa_bat_bu.psy);
0436         goto err_psy_reg_bu;
0437     }
0438 
0439     ret = request_irq(gpiod_to_irq(tosa_bat_main.gpiod_full),
0440                 tosa_bat_gpio_isr,
0441                 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
0442                 "main full", &tosa_bat_main);
0443     if (ret)
0444         goto err_req_main;
0445 
0446     ret = request_irq(gpiod_to_irq(tosa_bat_jacket.gpiod_full),
0447                 tosa_bat_gpio_isr,
0448                 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
0449                 "jacket full", &tosa_bat_jacket);
0450     if (ret)
0451         goto err_req_jacket;
0452 
0453     ret = request_irq(gpiod_to_irq(jacket_detect),
0454                 tosa_bat_gpio_isr,
0455                 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
0456                 "jacket detect", &tosa_bat_jacket);
0457     if (!ret) {
0458         schedule_work(&bat_work);
0459         return 0;
0460     }
0461 
0462     free_irq(gpiod_to_irq(tosa_bat_jacket.gpiod_full), &tosa_bat_jacket);
0463 err_req_jacket:
0464     free_irq(gpiod_to_irq(tosa_bat_main.gpiod_full), &tosa_bat_main);
0465 err_req_main:
0466     power_supply_unregister(tosa_bat_bu.psy);
0467 err_psy_reg_bu:
0468     power_supply_unregister(tosa_bat_jacket.psy);
0469 err_psy_reg_jacket:
0470     power_supply_unregister(tosa_bat_main.psy);
0471 err_psy_reg_main:
0472 
0473     /* see comment in tosa_bat_remove */
0474     cancel_work_sync(&bat_work);
0475 
0476     return ret;
0477 }
0478 
0479 static int tosa_bat_remove(struct platform_device *dev)
0480 {
0481     free_irq(gpiod_to_irq(jacket_detect), &tosa_bat_jacket);
0482     free_irq(gpiod_to_irq(tosa_bat_jacket.gpiod_full), &tosa_bat_jacket);
0483     free_irq(gpiod_to_irq(tosa_bat_main.gpiod_full), &tosa_bat_main);
0484 
0485     power_supply_unregister(tosa_bat_bu.psy);
0486     power_supply_unregister(tosa_bat_jacket.psy);
0487     power_supply_unregister(tosa_bat_main.psy);
0488 
0489     /*
0490      * Now cancel the bat_work.  We won't get any more schedules,
0491      * since all sources (isr and external_power_changed) are
0492      * unregistered now.
0493      */
0494     cancel_work_sync(&bat_work);
0495     return 0;
0496 }
0497 
0498 static struct platform_driver tosa_bat_driver = {
0499     .driver.name    = "wm97xx-battery",
0500     .driver.owner   = THIS_MODULE,
0501     .probe      = tosa_bat_probe,
0502     .remove     = tosa_bat_remove,
0503     .suspend    = tosa_bat_suspend,
0504     .resume     = tosa_bat_resume,
0505 };
0506 
0507 module_platform_driver(tosa_bat_driver);
0508 
0509 MODULE_LICENSE("GPL");
0510 MODULE_AUTHOR("Dmitry Baryshkov");
0511 MODULE_DESCRIPTION("Tosa battery driver");
0512 MODULE_ALIAS("platform:wm97xx-battery");