linux/drivers/power/jz4740-battery.c
<<
>>
Prefs
   1/*
   2 * Battery measurement code for Ingenic JZ SOC.
   3 *
   4 * Copyright (C) 2009 Jiejing Zhang <kzjeef@gmail.com>
   5 * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
   6 *
   7 * based on tosa_battery.c
   8 *
   9 * Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.com>
  10*
  11 * This program is free software; you can redistribute it and/or modify
  12 * it under the terms of the GNU General Public License version 2 as
  13 * published by the Free Software Foundation.
  14 *
  15 */
  16
  17#include <linux/interrupt.h>
  18#include <linux/kernel.h>
  19#include <linux/module.h>
  20#include <linux/platform_device.h>
  21#include <linux/slab.h>
  22#include <linux/io.h>
  23
  24#include <linux/delay.h>
  25#include <linux/gpio.h>
  26#include <linux/mfd/core.h>
  27#include <linux/power_supply.h>
  28
  29#include <linux/power/jz4740-battery.h>
  30#include <linux/jz4740-adc.h>
  31
  32struct jz_battery {
  33        struct jz_battery_platform_data *pdata;
  34        struct platform_device *pdev;
  35
  36        void __iomem *base;
  37
  38        int irq;
  39        int charge_irq;
  40
  41        const struct mfd_cell *cell;
  42
  43        int status;
  44        long voltage;
  45
  46        struct completion read_completion;
  47
  48        struct power_supply battery;
  49        struct delayed_work work;
  50
  51        struct mutex lock;
  52};
  53
  54static inline struct jz_battery *psy_to_jz_battery(struct power_supply *psy)
  55{
  56        return container_of(psy, struct jz_battery, battery);
  57}
  58
  59static irqreturn_t jz_battery_irq_handler(int irq, void *devid)
  60{
  61        struct jz_battery *battery = devid;
  62
  63        complete(&battery->read_completion);
  64        return IRQ_HANDLED;
  65}
  66
  67static long jz_battery_read_voltage(struct jz_battery *battery)
  68{
  69        long t;
  70        unsigned long val;
  71        long voltage;
  72
  73        mutex_lock(&battery->lock);
  74
  75        INIT_COMPLETION(battery->read_completion);
  76
  77        enable_irq(battery->irq);
  78        battery->cell->enable(battery->pdev);
  79
  80        t = wait_for_completion_interruptible_timeout(&battery->read_completion,
  81                HZ);
  82
  83        if (t > 0) {
  84                val = readw(battery->base) & 0xfff;
  85
  86                if (battery->pdata->info.voltage_max_design <= 2500000)
  87                        val = (val * 78125UL) >> 7UL;
  88                else
  89                        val = ((val * 924375UL) >> 9UL) + 33000;
  90                voltage = (long)val;
  91        } else {
  92                voltage = t ? t : -ETIMEDOUT;
  93        }
  94
  95        battery->cell->disable(battery->pdev);
  96        disable_irq(battery->irq);
  97
  98        mutex_unlock(&battery->lock);
  99
 100        return voltage;
 101}
 102
 103static int jz_battery_get_capacity(struct power_supply *psy)
 104{
 105        struct jz_battery *jz_battery = psy_to_jz_battery(psy);
 106        struct power_supply_info *info = &jz_battery->pdata->info;
 107        long voltage;
 108        int ret;
 109        int voltage_span;
 110
 111        voltage = jz_battery_read_voltage(jz_battery);
 112
 113        if (voltage < 0)
 114                return voltage;
 115
 116        voltage_span = info->voltage_max_design - info->voltage_min_design;
 117        ret = ((voltage - info->voltage_min_design) * 100) / voltage_span;
 118
 119        if (ret > 100)
 120                ret = 100;
 121        else if (ret < 0)
 122                ret = 0;
 123
 124        return ret;
 125}
 126
 127static int jz_battery_get_property(struct power_supply *psy,
 128        enum power_supply_property psp, union power_supply_propval *val)
 129{
 130        struct jz_battery *jz_battery = psy_to_jz_battery(psy);
 131        struct power_supply_info *info = &jz_battery->pdata->info;
 132        long voltage;
 133
 134        switch (psp) {
 135        case POWER_SUPPLY_PROP_STATUS:
 136                val->intval = jz_battery->status;
 137                break;
 138        case POWER_SUPPLY_PROP_TECHNOLOGY:
 139                val->intval = jz_battery->pdata->info.technology;
 140                break;
 141        case POWER_SUPPLY_PROP_HEALTH:
 142                voltage = jz_battery_read_voltage(jz_battery);
 143                if (voltage < info->voltage_min_design)
 144                        val->intval = POWER_SUPPLY_HEALTH_DEAD;
 145                else
 146                        val->intval = POWER_SUPPLY_HEALTH_GOOD;
 147                break;
 148        case POWER_SUPPLY_PROP_CAPACITY:
 149                val->intval = jz_battery_get_capacity(psy);
 150                break;
 151        case POWER_SUPPLY_PROP_VOLTAGE_NOW:
 152                val->intval = jz_battery_read_voltage(jz_battery);
 153                if (val->intval < 0)
 154                        return val->intval;
 155                break;
 156        case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
 157                val->intval = info->voltage_max_design;
 158                break;
 159        case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
 160                val->intval = info->voltage_min_design;
 161                break;
 162        case POWER_SUPPLY_PROP_PRESENT:
 163                val->intval = 1;
 164                break;
 165        default:
 166                return -EINVAL;
 167        }
 168        return 0;
 169}
 170
 171static void jz_battery_external_power_changed(struct power_supply *psy)
 172{
 173        struct jz_battery *jz_battery = psy_to_jz_battery(psy);
 174
 175        mod_delayed_work(system_wq, &jz_battery->work, 0);
 176}
 177
 178static irqreturn_t jz_battery_charge_irq(int irq, void *data)
 179{
 180        struct jz_battery *jz_battery = data;
 181
 182        mod_delayed_work(system_wq, &jz_battery->work, 0);
 183
 184        return IRQ_HANDLED;
 185}
 186
 187static void jz_battery_update(struct jz_battery *jz_battery)
 188{
 189        int status;
 190        long voltage;
 191        bool has_changed = false;
 192        int is_charging;
 193
 194        if (gpio_is_valid(jz_battery->pdata->gpio_charge)) {
 195                is_charging = gpio_get_value(jz_battery->pdata->gpio_charge);
 196                is_charging ^= jz_battery->pdata->gpio_charge_active_low;
 197                if (is_charging)
 198                        status = POWER_SUPPLY_STATUS_CHARGING;
 199                else
 200                        status = POWER_SUPPLY_STATUS_NOT_CHARGING;
 201
 202                if (status != jz_battery->status) {
 203                        jz_battery->status = status;
 204                        has_changed = true;
 205                }
 206        }
 207
 208        voltage = jz_battery_read_voltage(jz_battery);
 209        if (abs(voltage - jz_battery->voltage) < 50000) {
 210                jz_battery->voltage = voltage;
 211                has_changed = true;
 212        }
 213
 214        if (has_changed)
 215                power_supply_changed(&jz_battery->battery);
 216}
 217
 218static enum power_supply_property jz_battery_properties[] = {
 219        POWER_SUPPLY_PROP_STATUS,
 220        POWER_SUPPLY_PROP_TECHNOLOGY,
 221        POWER_SUPPLY_PROP_HEALTH,
 222        POWER_SUPPLY_PROP_CAPACITY,
 223        POWER_SUPPLY_PROP_VOLTAGE_NOW,
 224        POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
 225        POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
 226        POWER_SUPPLY_PROP_PRESENT,
 227};
 228
 229static void jz_battery_work(struct work_struct *work)
 230{
 231        /* Too small interval will increase system workload */
 232        const int interval = HZ * 30;
 233        struct jz_battery *jz_battery = container_of(work, struct jz_battery,
 234                                            work.work);
 235
 236        jz_battery_update(jz_battery);
 237        schedule_delayed_work(&jz_battery->work, interval);
 238}
 239
 240static int jz_battery_probe(struct platform_device *pdev)
 241{
 242        int ret = 0;
 243        struct jz_battery_platform_data *pdata = pdev->dev.parent->platform_data;
 244        struct jz_battery *jz_battery;
 245        struct power_supply *battery;
 246        struct resource *mem;
 247
 248        if (!pdata) {
 249                dev_err(&pdev->dev, "No platform_data supplied\n");
 250                return -ENXIO;
 251        }
 252
 253        jz_battery = devm_kzalloc(&pdev->dev, sizeof(*jz_battery), GFP_KERNEL);
 254        if (!jz_battery) {
 255                dev_err(&pdev->dev, "Failed to allocate driver structure\n");
 256                return -ENOMEM;
 257        }
 258
 259        jz_battery->cell = mfd_get_cell(pdev);
 260
 261        jz_battery->irq = platform_get_irq(pdev, 0);
 262        if (jz_battery->irq < 0) {
 263                dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret);
 264                return jz_battery->irq;
 265        }
 266
 267        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 268
 269        jz_battery->base = devm_request_and_ioremap(&pdev->dev, mem);
 270        if (!jz_battery->base)
 271                return -EBUSY;
 272
 273        battery = &jz_battery->battery;
 274        battery->name = pdata->info.name;
 275        battery->type = POWER_SUPPLY_TYPE_BATTERY;
 276        battery->properties     = jz_battery_properties;
 277        battery->num_properties = ARRAY_SIZE(jz_battery_properties);
 278        battery->get_property = jz_battery_get_property;
 279        battery->external_power_changed = jz_battery_external_power_changed;
 280        battery->use_for_apm = 1;
 281
 282        jz_battery->pdata = pdata;
 283        jz_battery->pdev = pdev;
 284
 285        init_completion(&jz_battery->read_completion);
 286        mutex_init(&jz_battery->lock);
 287
 288        INIT_DELAYED_WORK(&jz_battery->work, jz_battery_work);
 289
 290        ret = request_irq(jz_battery->irq, jz_battery_irq_handler, 0, pdev->name,
 291                        jz_battery);
 292        if (ret) {
 293                dev_err(&pdev->dev, "Failed to request irq %d\n", ret);
 294                goto err;
 295        }
 296        disable_irq(jz_battery->irq);
 297
 298        if (gpio_is_valid(pdata->gpio_charge)) {
 299                ret = gpio_request(pdata->gpio_charge, dev_name(&pdev->dev));
 300                if (ret) {
 301                        dev_err(&pdev->dev, "charger state gpio request failed.\n");
 302                        goto err_free_irq;
 303                }
 304                ret = gpio_direction_input(pdata->gpio_charge);
 305                if (ret) {
 306                        dev_err(&pdev->dev, "charger state gpio set direction failed.\n");
 307                        goto err_free_gpio;
 308                }
 309
 310                jz_battery->charge_irq = gpio_to_irq(pdata->gpio_charge);
 311
 312                if (jz_battery->charge_irq >= 0) {
 313                        ret = request_irq(jz_battery->charge_irq,
 314                                    jz_battery_charge_irq,
 315                                    IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
 316                                    dev_name(&pdev->dev), jz_battery);
 317                        if (ret) {
 318                                dev_err(&pdev->dev, "Failed to request charge irq: %d\n", ret);
 319                                goto err_free_gpio;
 320                        }
 321                }
 322        } else {
 323                jz_battery->charge_irq = -1;
 324        }
 325
 326        if (jz_battery->pdata->info.voltage_max_design <= 2500000)
 327                jz4740_adc_set_config(pdev->dev.parent, JZ_ADC_CONFIG_BAT_MB,
 328                        JZ_ADC_CONFIG_BAT_MB);
 329        else
 330                jz4740_adc_set_config(pdev->dev.parent, JZ_ADC_CONFIG_BAT_MB, 0);
 331
 332        ret = power_supply_register(&pdev->dev, &jz_battery->battery);
 333        if (ret) {
 334                dev_err(&pdev->dev, "power supply battery register failed.\n");
 335                goto err_free_charge_irq;
 336        }
 337
 338        platform_set_drvdata(pdev, jz_battery);
 339        schedule_delayed_work(&jz_battery->work, 0);
 340
 341        return 0;
 342
 343err_free_charge_irq:
 344        if (jz_battery->charge_irq >= 0)
 345                free_irq(jz_battery->charge_irq, jz_battery);
 346err_free_gpio:
 347        if (gpio_is_valid(pdata->gpio_charge))
 348                gpio_free(jz_battery->pdata->gpio_charge);
 349err_free_irq:
 350        free_irq(jz_battery->irq, jz_battery);
 351err:
 352        platform_set_drvdata(pdev, NULL);
 353        return ret;
 354}
 355
 356static int jz_battery_remove(struct platform_device *pdev)
 357{
 358        struct jz_battery *jz_battery = platform_get_drvdata(pdev);
 359
 360        cancel_delayed_work_sync(&jz_battery->work);
 361
 362        if (gpio_is_valid(jz_battery->pdata->gpio_charge)) {
 363                if (jz_battery->charge_irq >= 0)
 364                        free_irq(jz_battery->charge_irq, jz_battery);
 365                gpio_free(jz_battery->pdata->gpio_charge);
 366        }
 367
 368        power_supply_unregister(&jz_battery->battery);
 369
 370        free_irq(jz_battery->irq, jz_battery);
 371
 372        return 0;
 373}
 374
 375#ifdef CONFIG_PM
 376static int jz_battery_suspend(struct device *dev)
 377{
 378        struct jz_battery *jz_battery = dev_get_drvdata(dev);
 379
 380        cancel_delayed_work_sync(&jz_battery->work);
 381        jz_battery->status = POWER_SUPPLY_STATUS_UNKNOWN;
 382
 383        return 0;
 384}
 385
 386static int jz_battery_resume(struct device *dev)
 387{
 388        struct jz_battery *jz_battery = dev_get_drvdata(dev);
 389
 390        schedule_delayed_work(&jz_battery->work, 0);
 391
 392        return 0;
 393}
 394
 395static const struct dev_pm_ops jz_battery_pm_ops = {
 396        .suspend        = jz_battery_suspend,
 397        .resume         = jz_battery_resume,
 398};
 399
 400#define JZ_BATTERY_PM_OPS (&jz_battery_pm_ops)
 401#else
 402#define JZ_BATTERY_PM_OPS NULL
 403#endif
 404
 405static struct platform_driver jz_battery_driver = {
 406        .probe          = jz_battery_probe,
 407        .remove         = jz_battery_remove,
 408        .driver = {
 409                .name = "jz4740-battery",
 410                .owner = THIS_MODULE,
 411                .pm = JZ_BATTERY_PM_OPS,
 412        },
 413};
 414
 415module_platform_driver(jz_battery_driver);
 416
 417MODULE_ALIAS("platform:jz4740-battery");
 418MODULE_LICENSE("GPL");
 419MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
 420MODULE_DESCRIPTION("JZ4740 SoC battery driver");
 421
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.