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