224 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Battery class driver for Apple PMU4 *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 * Power28 *********************************************************************/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 properties62 *********************************************************************/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 else96 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 * Initialisation139 *********************************************************************/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