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