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 pmu_battery_info *pbi; 22 char name[16]; 23 int propval; 24} *pbats[PMU_MAX_BATTERIES]; 25 26#define to_pmu_battery_dev(x) container_of(x, struct pmu_battery_dev, bat) 27 28/********************************************************************* 29 * Power 30 *********************************************************************/ 31 32static int pmu_get_ac_prop(struct power_supply *psy, 33 enum power_supply_property psp, 34 union power_supply_propval *val) 35{ 36 switch (psp) { 37 case POWER_SUPPLY_PROP_ONLINE: 38 val->intval = (!!(pmu_power_flags & PMU_PWR_AC_PRESENT)) || 39 (pmu_battery_count == 0); 40 break; 41 default: 42 return -EINVAL; 43 } 44 45 return 0; 46} 47 48static enum power_supply_property pmu_ac_props[] = { 49 POWER_SUPPLY_PROP_ONLINE, 50}; 51 52static struct power_supply pmu_ac = { 53 .name = "pmu-ac", 54 .type = POWER_SUPPLY_TYPE_MAINS, 55 .properties = pmu_ac_props, 56 .num_properties = ARRAY_SIZE(pmu_ac_props), 57 .get_property = pmu_get_ac_prop, 58}; 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; 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 ret = power_supply_register(&bat_pdev->dev, &pmu_ac); 156 if (ret) 157 goto ac_register_failed; 158 159 for (i = 0; i < pmu_battery_count; i++) { 160 struct pmu_battery_dev *pbat = kzalloc(sizeof(*pbat), 161 GFP_KERNEL); 162 if (!pbat) 163 break; 164 165 sprintf(pbat->name, "PMU_battery_%d", i); 166 pbat->bat.name = pbat->name; 167 pbat->bat.properties = pmu_bat_props; 168 pbat->bat.num_properties = ARRAY_SIZE(pmu_bat_props); 169 pbat->bat.get_property = pmu_bat_get_property; 170 pbat->pbi = &pmu_batteries[i]; 171 172 ret = power_supply_register(&bat_pdev->dev, &pbat->bat); 173 if (ret) { 174 kfree(pbat); 175 goto battery_register_failed; 176 } 177 pbats[i] = pbat; 178 } 179 180 goto success; 181 182battery_register_failed: 183 while (i--) { 184 if (!pbats[i]) 185 continue; 186 power_supply_unregister(&pbats[i]->bat); 187 kfree(pbats[i]); 188 } 189 power_supply_unregister(&pmu_ac); 190ac_register_failed: 191 platform_device_unregister(bat_pdev); 192pdev_register_failed: 193success: 194 return ret; 195} 196 197static void __exit pmu_bat_exit(void) 198{ 199 int i; 200 201 for (i = 0; i < PMU_MAX_BATTERIES; i++) { 202 if (!pbats[i]) 203 continue; 204 power_supply_unregister(&pbats[i]->bat); 205 kfree(pbats[i]); 206 } 207 power_supply_unregister(&pmu_ac); 208 platform_device_unregister(bat_pdev); 209} 210 211module_init(pmu_bat_init); 212module_exit(pmu_bat_exit); 213 214MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); 215MODULE_LICENSE("GPL"); 216MODULE_DESCRIPTION("PMU battery driver"); 217