linux/drivers/power/supply/pmu_battery.c
<<
>>
Prefs
   1/*
   2 * Battery class driver for Apple PMU
   3 *
   4 *      Copyright © 2006  David Woodhouse <dwmw2@infradead.org>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 */
  10
  11#include <linux/module.h>
  12#include <linux/platform_device.h>
  13#include <linux/err.h>
  14#include <linux/power_supply.h>
  15#include <linux/adb.h>
  16#include <linux/pmu.h>
  17#include <linux/slab.h>
  18
  19static struct pmu_battery_dev {
  20        struct power_supply *bat;
  21        struct power_supply_desc bat_desc;
  22        struct pmu_battery_info *pbi;
  23        char name[16];
  24        int propval;
  25} *pbats[PMU_MAX_BATTERIES];
  26
  27#define to_pmu_battery_dev(x) power_supply_get_drvdata(x)
  28
  29/*********************************************************************
  30 *              Power
  31 *********************************************************************/
  32
  33static int pmu_get_ac_prop(struct power_supply *psy,
  34                           enum power_supply_property psp,
  35                           union power_supply_propval *val)
  36{
  37        switch (psp) {
  38        case POWER_SUPPLY_PROP_ONLINE:
  39                val->intval = (!!(pmu_power_flags & PMU_PWR_AC_PRESENT)) ||
  40                              (pmu_battery_count == 0);
  41                break;
  42        default:
  43                return -EINVAL;
  44        }
  45
  46        return 0;
  47}
  48
  49static enum power_supply_property pmu_ac_props[] = {
  50        POWER_SUPPLY_PROP_ONLINE,
  51};
  52
  53static const struct power_supply_desc pmu_ac_desc = {
  54        .name = "pmu-ac",
  55        .type = POWER_SUPPLY_TYPE_MAINS,
  56        .properties = pmu_ac_props,
  57        .num_properties = ARRAY_SIZE(pmu_ac_props),
  58        .get_property = pmu_get_ac_prop,
  59};
  60
  61static struct power_supply *pmu_ac;
  62
  63/*********************************************************************
  64 *              Battery properties
  65 *********************************************************************/
  66
  67static char *pmu_batt_types[] = {
  68        "Smart", "Comet", "Hooper", "Unknown"
  69};
  70
  71static char *pmu_bat_get_model_name(struct pmu_battery_info *pbi)
  72{
  73        switch (pbi->flags & PMU_BATT_TYPE_MASK) {
  74        case PMU_BATT_TYPE_SMART:
  75                return pmu_batt_types[0];
  76        case PMU_BATT_TYPE_COMET:
  77                return pmu_batt_types[1];
  78        case PMU_BATT_TYPE_HOOPER:
  79                return pmu_batt_types[2];
  80        default: break;
  81        }
  82        return pmu_batt_types[3];
  83}
  84
  85static int pmu_bat_get_property(struct power_supply *psy,
  86                                enum power_supply_property psp,
  87                                union power_supply_propval *val)
  88{
  89        struct pmu_battery_dev *pbat = to_pmu_battery_dev(psy);
  90        struct pmu_battery_info *pbi = pbat->pbi;
  91
  92        switch (psp) {
  93        case POWER_SUPPLY_PROP_STATUS:
  94                if (pbi->flags & PMU_BATT_CHARGING)
  95                        val->intval = POWER_SUPPLY_STATUS_CHARGING;
  96                else if (pmu_power_flags & PMU_PWR_AC_PRESENT)
  97                        val->intval = POWER_SUPPLY_STATUS_FULL;
  98                else
  99                        val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
 100                break;
 101        case POWER_SUPPLY_PROP_PRESENT:
 102                val->intval = !!(pbi->flags & PMU_BATT_PRESENT);
 103                break;
 104        case POWER_SUPPLY_PROP_MODEL_NAME:
 105                val->strval = pmu_bat_get_model_name(pbi);
 106                break;
 107        case POWER_SUPPLY_PROP_ENERGY_AVG:
 108                val->intval = pbi->charge     * 1000; /* mWh -> µWh */
 109                break;
 110        case POWER_SUPPLY_PROP_ENERGY_FULL:
 111                val->intval = pbi->max_charge * 1000; /* mWh -> µWh */
 112                break;
 113        case POWER_SUPPLY_PROP_CURRENT_AVG:
 114                val->intval = pbi->amperage   * 1000; /* mA -> µA */
 115                break;
 116        case POWER_SUPPLY_PROP_VOLTAGE_AVG:
 117                val->intval = pbi->voltage    * 1000; /* mV -> µV */
 118                break;
 119        case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
 120                val->intval = pbi->time_remaining;
 121                break;
 122        default:
 123                return -EINVAL;
 124        }
 125
 126        return 0;
 127}
 128
 129static enum power_supply_property pmu_bat_props[] = {
 130        POWER_SUPPLY_PROP_STATUS,
 131        POWER_SUPPLY_PROP_PRESENT,
 132        POWER_SUPPLY_PROP_MODEL_NAME,
 133        POWER_SUPPLY_PROP_ENERGY_AVG,
 134        POWER_SUPPLY_PROP_ENERGY_FULL,
 135        POWER_SUPPLY_PROP_CURRENT_AVG,
 136        POWER_SUPPLY_PROP_VOLTAGE_AVG,
 137        POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
 138};
 139
 140/*********************************************************************
 141 *              Initialisation
 142 *********************************************************************/
 143
 144static struct platform_device *bat_pdev;
 145
 146static int __init pmu_bat_init(void)
 147{
 148        int ret = 0;
 149        int i;
 150
 151        bat_pdev = platform_device_register_simple("pmu-battery",
 152                                                   0, NULL, 0);
 153        if (IS_ERR(bat_pdev)) {
 154                ret = PTR_ERR(bat_pdev);
 155                goto pdev_register_failed;
 156        }
 157
 158        pmu_ac = power_supply_register(&bat_pdev->dev, &pmu_ac_desc, NULL);
 159        if (IS_ERR(pmu_ac)) {
 160                ret = PTR_ERR(pmu_ac);
 161                goto ac_register_failed;
 162        }
 163
 164        for (i = 0; i < pmu_battery_count; i++) {
 165                struct power_supply_config psy_cfg = {};
 166                struct pmu_battery_dev *pbat = kzalloc(sizeof(*pbat),
 167                                                       GFP_KERNEL);
 168                if (!pbat)
 169                        break;
 170
 171                sprintf(pbat->name, "PMU_battery_%d", i);
 172                pbat->bat_desc.name = pbat->name;
 173                pbat->bat_desc.properties = pmu_bat_props;
 174                pbat->bat_desc.num_properties = ARRAY_SIZE(pmu_bat_props);
 175                pbat->bat_desc.get_property = pmu_bat_get_property;
 176                pbat->pbi = &pmu_batteries[i];
 177                psy_cfg.drv_data = pbat;
 178
 179                pbat->bat = power_supply_register(&bat_pdev->dev,
 180                                                  &pbat->bat_desc,
 181                                                  &psy_cfg);
 182                if (IS_ERR(pbat->bat)) {
 183                        ret = PTR_ERR(pbat->bat);
 184                        kfree(pbat);
 185                        goto battery_register_failed;
 186                }
 187                pbats[i] = pbat;
 188        }
 189
 190        goto success;
 191
 192battery_register_failed:
 193        while (i--) {
 194                if (!pbats[i])
 195                        continue;
 196                power_supply_unregister(pbats[i]->bat);
 197                kfree(pbats[i]);
 198        }
 199        power_supply_unregister(pmu_ac);
 200ac_register_failed:
 201        platform_device_unregister(bat_pdev);
 202pdev_register_failed:
 203success:
 204        return ret;
 205}
 206
 207static void __exit pmu_bat_exit(void)
 208{
 209        int i;
 210
 211        for (i = 0; i < PMU_MAX_BATTERIES; i++) {
 212                if (!pbats[i])
 213                        continue;
 214                power_supply_unregister(pbats[i]->bat);
 215                kfree(pbats[i]);
 216        }
 217        power_supply_unregister(pmu_ac);
 218        platform_device_unregister(bat_pdev);
 219}
 220
 221module_init(pmu_bat_init);
 222module_exit(pmu_bat_exit);
 223
 224MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
 225MODULE_LICENSE("GPL");
 226MODULE_DESCRIPTION("PMU battery driver");
 227