0001
0002
0003
0004
0005
0006
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);
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;
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
0315 flush_work(&bat_work);
0316 return 0;
0317 }
0318
0319 static int tosa_bat_resume(struct platform_device *dev)
0320 {
0321
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
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
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
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
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
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
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
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
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
0491
0492
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");