pmu_battery.c (5573B)
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");