162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Battery class driver for Apple PMU 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright © 2006 David Woodhouse <dwmw2@infradead.org> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/platform_device.h> 1062306a36Sopenharmony_ci#include <linux/err.h> 1162306a36Sopenharmony_ci#include <linux/power_supply.h> 1262306a36Sopenharmony_ci#include <linux/adb.h> 1362306a36Sopenharmony_ci#include <linux/pmu.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic struct pmu_battery_dev { 1762306a36Sopenharmony_ci struct power_supply *bat; 1862306a36Sopenharmony_ci struct power_supply_desc bat_desc; 1962306a36Sopenharmony_ci struct pmu_battery_info *pbi; 2062306a36Sopenharmony_ci char name[16]; 2162306a36Sopenharmony_ci int propval; 2262306a36Sopenharmony_ci} *pbats[PMU_MAX_BATTERIES]; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define to_pmu_battery_dev(x) power_supply_get_drvdata(x) 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/********************************************************************* 2762306a36Sopenharmony_ci * Power 2862306a36Sopenharmony_ci *********************************************************************/ 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic int pmu_get_ac_prop(struct power_supply *psy, 3162306a36Sopenharmony_ci enum power_supply_property psp, 3262306a36Sopenharmony_ci union power_supply_propval *val) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci switch (psp) { 3562306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 3662306a36Sopenharmony_ci val->intval = (!!(pmu_power_flags & PMU_PWR_AC_PRESENT)) || 3762306a36Sopenharmony_ci (pmu_battery_count == 0); 3862306a36Sopenharmony_ci break; 3962306a36Sopenharmony_ci default: 4062306a36Sopenharmony_ci return -EINVAL; 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci return 0; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic enum power_supply_property pmu_ac_props[] = { 4762306a36Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic const struct power_supply_desc pmu_ac_desc = { 5162306a36Sopenharmony_ci .name = "pmu-ac", 5262306a36Sopenharmony_ci .type = POWER_SUPPLY_TYPE_MAINS, 5362306a36Sopenharmony_ci .properties = pmu_ac_props, 5462306a36Sopenharmony_ci .num_properties = ARRAY_SIZE(pmu_ac_props), 5562306a36Sopenharmony_ci .get_property = pmu_get_ac_prop, 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic struct power_supply *pmu_ac; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/********************************************************************* 6162306a36Sopenharmony_ci * Battery properties 6262306a36Sopenharmony_ci *********************************************************************/ 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic char *pmu_batt_types[] = { 6562306a36Sopenharmony_ci "Smart", "Comet", "Hooper", "Unknown" 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic char *pmu_bat_get_model_name(struct pmu_battery_info *pbi) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci switch (pbi->flags & PMU_BATT_TYPE_MASK) { 7162306a36Sopenharmony_ci case PMU_BATT_TYPE_SMART: 7262306a36Sopenharmony_ci return pmu_batt_types[0]; 7362306a36Sopenharmony_ci case PMU_BATT_TYPE_COMET: 7462306a36Sopenharmony_ci return pmu_batt_types[1]; 7562306a36Sopenharmony_ci case PMU_BATT_TYPE_HOOPER: 7662306a36Sopenharmony_ci return pmu_batt_types[2]; 7762306a36Sopenharmony_ci default: break; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci return pmu_batt_types[3]; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic int pmu_bat_get_property(struct power_supply *psy, 8362306a36Sopenharmony_ci enum power_supply_property psp, 8462306a36Sopenharmony_ci union power_supply_propval *val) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct pmu_battery_dev *pbat = to_pmu_battery_dev(psy); 8762306a36Sopenharmony_ci struct pmu_battery_info *pbi = pbat->pbi; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci switch (psp) { 9062306a36Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 9162306a36Sopenharmony_ci if (pbi->flags & PMU_BATT_CHARGING) 9262306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_CHARGING; 9362306a36Sopenharmony_ci else if (pmu_power_flags & PMU_PWR_AC_PRESENT) 9462306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_FULL; 9562306a36Sopenharmony_ci else 9662306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 9762306a36Sopenharmony_ci break; 9862306a36Sopenharmony_ci case POWER_SUPPLY_PROP_PRESENT: 9962306a36Sopenharmony_ci val->intval = !!(pbi->flags & PMU_BATT_PRESENT); 10062306a36Sopenharmony_ci break; 10162306a36Sopenharmony_ci case POWER_SUPPLY_PROP_MODEL_NAME: 10262306a36Sopenharmony_ci val->strval = pmu_bat_get_model_name(pbi); 10362306a36Sopenharmony_ci break; 10462306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ENERGY_AVG: 10562306a36Sopenharmony_ci val->intval = pbi->charge * 1000; /* mWh -> µWh */ 10662306a36Sopenharmony_ci break; 10762306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ENERGY_FULL: 10862306a36Sopenharmony_ci val->intval = pbi->max_charge * 1000; /* mWh -> µWh */ 10962306a36Sopenharmony_ci break; 11062306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_AVG: 11162306a36Sopenharmony_ci val->intval = pbi->amperage * 1000; /* mA -> µA */ 11262306a36Sopenharmony_ci break; 11362306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_AVG: 11462306a36Sopenharmony_ci val->intval = pbi->voltage * 1000; /* mV -> µV */ 11562306a36Sopenharmony_ci break; 11662306a36Sopenharmony_ci case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: 11762306a36Sopenharmony_ci val->intval = pbi->time_remaining; 11862306a36Sopenharmony_ci break; 11962306a36Sopenharmony_ci default: 12062306a36Sopenharmony_ci return -EINVAL; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci return 0; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic enum power_supply_property pmu_bat_props[] = { 12762306a36Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 12862306a36Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, 12962306a36Sopenharmony_ci POWER_SUPPLY_PROP_MODEL_NAME, 13062306a36Sopenharmony_ci POWER_SUPPLY_PROP_ENERGY_AVG, 13162306a36Sopenharmony_ci POWER_SUPPLY_PROP_ENERGY_FULL, 13262306a36Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_AVG, 13362306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_AVG, 13462306a36Sopenharmony_ci POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 13562306a36Sopenharmony_ci}; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/********************************************************************* 13862306a36Sopenharmony_ci * Initialisation 13962306a36Sopenharmony_ci *********************************************************************/ 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic struct platform_device *bat_pdev; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic int __init pmu_bat_init(void) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci int ret = 0; 14662306a36Sopenharmony_ci int i; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci bat_pdev = platform_device_register_simple("pmu-battery", 14962306a36Sopenharmony_ci 0, NULL, 0); 15062306a36Sopenharmony_ci if (IS_ERR(bat_pdev)) { 15162306a36Sopenharmony_ci ret = PTR_ERR(bat_pdev); 15262306a36Sopenharmony_ci goto pdev_register_failed; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci pmu_ac = power_supply_register(&bat_pdev->dev, &pmu_ac_desc, NULL); 15662306a36Sopenharmony_ci if (IS_ERR(pmu_ac)) { 15762306a36Sopenharmony_ci ret = PTR_ERR(pmu_ac); 15862306a36Sopenharmony_ci goto ac_register_failed; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci for (i = 0; i < pmu_battery_count; i++) { 16262306a36Sopenharmony_ci struct power_supply_config psy_cfg = {}; 16362306a36Sopenharmony_ci struct pmu_battery_dev *pbat = kzalloc(sizeof(*pbat), 16462306a36Sopenharmony_ci GFP_KERNEL); 16562306a36Sopenharmony_ci if (!pbat) 16662306a36Sopenharmony_ci break; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci sprintf(pbat->name, "PMU_battery_%d", i); 16962306a36Sopenharmony_ci pbat->bat_desc.name = pbat->name; 17062306a36Sopenharmony_ci pbat->bat_desc.properties = pmu_bat_props; 17162306a36Sopenharmony_ci pbat->bat_desc.num_properties = ARRAY_SIZE(pmu_bat_props); 17262306a36Sopenharmony_ci pbat->bat_desc.get_property = pmu_bat_get_property; 17362306a36Sopenharmony_ci pbat->pbi = &pmu_batteries[i]; 17462306a36Sopenharmony_ci psy_cfg.drv_data = pbat; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci pbat->bat = power_supply_register(&bat_pdev->dev, 17762306a36Sopenharmony_ci &pbat->bat_desc, 17862306a36Sopenharmony_ci &psy_cfg); 17962306a36Sopenharmony_ci if (IS_ERR(pbat->bat)) { 18062306a36Sopenharmony_ci ret = PTR_ERR(pbat->bat); 18162306a36Sopenharmony_ci kfree(pbat); 18262306a36Sopenharmony_ci goto battery_register_failed; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci pbats[i] = pbat; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci goto success; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cibattery_register_failed: 19062306a36Sopenharmony_ci while (i--) { 19162306a36Sopenharmony_ci if (!pbats[i]) 19262306a36Sopenharmony_ci continue; 19362306a36Sopenharmony_ci power_supply_unregister(pbats[i]->bat); 19462306a36Sopenharmony_ci kfree(pbats[i]); 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci power_supply_unregister(pmu_ac); 19762306a36Sopenharmony_ciac_register_failed: 19862306a36Sopenharmony_ci platform_device_unregister(bat_pdev); 19962306a36Sopenharmony_cipdev_register_failed: 20062306a36Sopenharmony_cisuccess: 20162306a36Sopenharmony_ci return ret; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic void __exit pmu_bat_exit(void) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci int i; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci for (i = 0; i < PMU_MAX_BATTERIES; i++) { 20962306a36Sopenharmony_ci if (!pbats[i]) 21062306a36Sopenharmony_ci continue; 21162306a36Sopenharmony_ci power_supply_unregister(pbats[i]->bat); 21262306a36Sopenharmony_ci kfree(pbats[i]); 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci power_supply_unregister(pmu_ac); 21562306a36Sopenharmony_ci platform_device_unregister(bat_pdev); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cimodule_init(pmu_bat_init); 21962306a36Sopenharmony_cimodule_exit(pmu_bat_exit); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ciMODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); 22262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 22362306a36Sopenharmony_ciMODULE_DESCRIPTION("PMU battery driver"); 224