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