1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
28c0984e5SSebastian Reichel /*
38c0984e5SSebastian Reichel  * Battery class driver for Apple PMU
48c0984e5SSebastian Reichel  *
58c0984e5SSebastian Reichel  *	Copyright © 2006  David Woodhouse <dwmw2@infradead.org>
68c0984e5SSebastian Reichel  */
78c0984e5SSebastian Reichel 
88c0984e5SSebastian Reichel #include <linux/module.h>
98c0984e5SSebastian Reichel #include <linux/platform_device.h>
108c0984e5SSebastian Reichel #include <linux/err.h>
118c0984e5SSebastian Reichel #include <linux/power_supply.h>
128c0984e5SSebastian Reichel #include <linux/adb.h>
138c0984e5SSebastian Reichel #include <linux/pmu.h>
148c0984e5SSebastian Reichel #include <linux/slab.h>
158c0984e5SSebastian Reichel 
168c0984e5SSebastian Reichel static struct pmu_battery_dev {
178c0984e5SSebastian Reichel 	struct power_supply *bat;
188c0984e5SSebastian Reichel 	struct power_supply_desc bat_desc;
198c0984e5SSebastian Reichel 	struct pmu_battery_info *pbi;
208c0984e5SSebastian Reichel 	char name[16];
218c0984e5SSebastian Reichel 	int propval;
228c0984e5SSebastian Reichel } *pbats[PMU_MAX_BATTERIES];
238c0984e5SSebastian Reichel 
248c0984e5SSebastian Reichel #define to_pmu_battery_dev(x) power_supply_get_drvdata(x)
258c0984e5SSebastian Reichel 
268c0984e5SSebastian Reichel /*********************************************************************
278c0984e5SSebastian Reichel  *		Power
288c0984e5SSebastian Reichel  *********************************************************************/
298c0984e5SSebastian Reichel 
pmu_get_ac_prop(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)308c0984e5SSebastian Reichel static int pmu_get_ac_prop(struct power_supply *psy,
318c0984e5SSebastian Reichel 			   enum power_supply_property psp,
328c0984e5SSebastian Reichel 			   union power_supply_propval *val)
338c0984e5SSebastian Reichel {
348c0984e5SSebastian Reichel 	switch (psp) {
358c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_ONLINE:
368c0984e5SSebastian Reichel 		val->intval = (!!(pmu_power_flags & PMU_PWR_AC_PRESENT)) ||
378c0984e5SSebastian Reichel 			      (pmu_battery_count == 0);
388c0984e5SSebastian Reichel 		break;
398c0984e5SSebastian Reichel 	default:
408c0984e5SSebastian Reichel 		return -EINVAL;
418c0984e5SSebastian Reichel 	}
428c0984e5SSebastian Reichel 
438c0984e5SSebastian Reichel 	return 0;
448c0984e5SSebastian Reichel }
458c0984e5SSebastian Reichel 
468c0984e5SSebastian Reichel static enum power_supply_property pmu_ac_props[] = {
478c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_ONLINE,
488c0984e5SSebastian Reichel };
498c0984e5SSebastian Reichel 
508c0984e5SSebastian Reichel static const struct power_supply_desc pmu_ac_desc = {
518c0984e5SSebastian Reichel 	.name = "pmu-ac",
528c0984e5SSebastian Reichel 	.type = POWER_SUPPLY_TYPE_MAINS,
538c0984e5SSebastian Reichel 	.properties = pmu_ac_props,
548c0984e5SSebastian Reichel 	.num_properties = ARRAY_SIZE(pmu_ac_props),
558c0984e5SSebastian Reichel 	.get_property = pmu_get_ac_prop,
568c0984e5SSebastian Reichel };
578c0984e5SSebastian Reichel 
588c0984e5SSebastian Reichel static struct power_supply *pmu_ac;
598c0984e5SSebastian Reichel 
608c0984e5SSebastian Reichel /*********************************************************************
618c0984e5SSebastian Reichel  *		Battery properties
628c0984e5SSebastian Reichel  *********************************************************************/
638c0984e5SSebastian Reichel 
648c0984e5SSebastian Reichel static char *pmu_batt_types[] = {
658c0984e5SSebastian Reichel 	"Smart", "Comet", "Hooper", "Unknown"
668c0984e5SSebastian Reichel };
678c0984e5SSebastian Reichel 
pmu_bat_get_model_name(struct pmu_battery_info * pbi)688c0984e5SSebastian Reichel static char *pmu_bat_get_model_name(struct pmu_battery_info *pbi)
698c0984e5SSebastian Reichel {
708c0984e5SSebastian Reichel 	switch (pbi->flags & PMU_BATT_TYPE_MASK) {
718c0984e5SSebastian Reichel 	case PMU_BATT_TYPE_SMART:
728c0984e5SSebastian Reichel 		return pmu_batt_types[0];
738c0984e5SSebastian Reichel 	case PMU_BATT_TYPE_COMET:
748c0984e5SSebastian Reichel 		return pmu_batt_types[1];
758c0984e5SSebastian Reichel 	case PMU_BATT_TYPE_HOOPER:
768c0984e5SSebastian Reichel 		return pmu_batt_types[2];
778c0984e5SSebastian Reichel 	default: break;
788c0984e5SSebastian Reichel 	}
798c0984e5SSebastian Reichel 	return pmu_batt_types[3];
808c0984e5SSebastian Reichel }
818c0984e5SSebastian Reichel 
pmu_bat_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)828c0984e5SSebastian Reichel static int pmu_bat_get_property(struct power_supply *psy,
838c0984e5SSebastian Reichel 				enum power_supply_property psp,
848c0984e5SSebastian Reichel 				union power_supply_propval *val)
858c0984e5SSebastian Reichel {
868c0984e5SSebastian Reichel 	struct pmu_battery_dev *pbat = to_pmu_battery_dev(psy);
878c0984e5SSebastian Reichel 	struct pmu_battery_info *pbi = pbat->pbi;
888c0984e5SSebastian Reichel 
898c0984e5SSebastian Reichel 	switch (psp) {
908c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_STATUS:
918c0984e5SSebastian Reichel 		if (pbi->flags & PMU_BATT_CHARGING)
928c0984e5SSebastian Reichel 			val->intval = POWER_SUPPLY_STATUS_CHARGING;
938c0984e5SSebastian Reichel 		else if (pmu_power_flags & PMU_PWR_AC_PRESENT)
948c0984e5SSebastian Reichel 			val->intval = POWER_SUPPLY_STATUS_FULL;
958c0984e5SSebastian Reichel 		else
968c0984e5SSebastian Reichel 			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
978c0984e5SSebastian Reichel 		break;
988c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_PRESENT:
998c0984e5SSebastian Reichel 		val->intval = !!(pbi->flags & PMU_BATT_PRESENT);
1008c0984e5SSebastian Reichel 		break;
1018c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_MODEL_NAME:
1028c0984e5SSebastian Reichel 		val->strval = pmu_bat_get_model_name(pbi);
1038c0984e5SSebastian Reichel 		break;
1048c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_ENERGY_AVG:
1058c0984e5SSebastian Reichel 		val->intval = pbi->charge     * 1000; /* mWh -> µWh */
1068c0984e5SSebastian Reichel 		break;
1078c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_ENERGY_FULL:
1088c0984e5SSebastian Reichel 		val->intval = pbi->max_charge * 1000; /* mWh -> µWh */
1098c0984e5SSebastian Reichel 		break;
1108c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_CURRENT_AVG:
1118c0984e5SSebastian Reichel 		val->intval = pbi->amperage   * 1000; /* mA -> µA */
1128c0984e5SSebastian Reichel 		break;
1138c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_VOLTAGE_AVG:
1148c0984e5SSebastian Reichel 		val->intval = pbi->voltage    * 1000; /* mV -> µV */
1158c0984e5SSebastian Reichel 		break;
1168c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
1178c0984e5SSebastian Reichel 		val->intval = pbi->time_remaining;
1188c0984e5SSebastian Reichel 		break;
1198c0984e5SSebastian Reichel 	default:
1208c0984e5SSebastian Reichel 		return -EINVAL;
1218c0984e5SSebastian Reichel 	}
1228c0984e5SSebastian Reichel 
1238c0984e5SSebastian Reichel 	return 0;
1248c0984e5SSebastian Reichel }
1258c0984e5SSebastian Reichel 
1268c0984e5SSebastian Reichel static enum power_supply_property pmu_bat_props[] = {
1278c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_STATUS,
1288c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_PRESENT,
1298c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_MODEL_NAME,
1308c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_ENERGY_AVG,
1318c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_ENERGY_FULL,
1328c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_CURRENT_AVG,
1338c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_AVG,
1348c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
1358c0984e5SSebastian Reichel };
1368c0984e5SSebastian Reichel 
1378c0984e5SSebastian Reichel /*********************************************************************
1388c0984e5SSebastian Reichel  *		Initialisation
1398c0984e5SSebastian Reichel  *********************************************************************/
1408c0984e5SSebastian Reichel 
1418c0984e5SSebastian Reichel static struct platform_device *bat_pdev;
1428c0984e5SSebastian Reichel 
pmu_bat_init(void)1438c0984e5SSebastian Reichel static int __init pmu_bat_init(void)
1448c0984e5SSebastian Reichel {
1458c0984e5SSebastian Reichel 	int ret = 0;
1468c0984e5SSebastian Reichel 	int i;
1478c0984e5SSebastian Reichel 
1488c0984e5SSebastian Reichel 	bat_pdev = platform_device_register_simple("pmu-battery",
1498c0984e5SSebastian Reichel 						   0, NULL, 0);
1508c0984e5SSebastian Reichel 	if (IS_ERR(bat_pdev)) {
1518c0984e5SSebastian Reichel 		ret = PTR_ERR(bat_pdev);
1528c0984e5SSebastian Reichel 		goto pdev_register_failed;
1538c0984e5SSebastian Reichel 	}
1548c0984e5SSebastian Reichel 
1558c0984e5SSebastian Reichel 	pmu_ac = power_supply_register(&bat_pdev->dev, &pmu_ac_desc, NULL);
1568c0984e5SSebastian Reichel 	if (IS_ERR(pmu_ac)) {
1578c0984e5SSebastian Reichel 		ret = PTR_ERR(pmu_ac);
1588c0984e5SSebastian Reichel 		goto ac_register_failed;
1598c0984e5SSebastian Reichel 	}
1608c0984e5SSebastian Reichel 
1618c0984e5SSebastian Reichel 	for (i = 0; i < pmu_battery_count; i++) {
1628c0984e5SSebastian Reichel 		struct power_supply_config psy_cfg = {};
1638c0984e5SSebastian Reichel 		struct pmu_battery_dev *pbat = kzalloc(sizeof(*pbat),
1648c0984e5SSebastian Reichel 						       GFP_KERNEL);
1658c0984e5SSebastian Reichel 		if (!pbat)
1668c0984e5SSebastian Reichel 			break;
1678c0984e5SSebastian Reichel 
1688c0984e5SSebastian Reichel 		sprintf(pbat->name, "PMU_battery_%d", i);
1698c0984e5SSebastian Reichel 		pbat->bat_desc.name = pbat->name;
1708c0984e5SSebastian Reichel 		pbat->bat_desc.properties = pmu_bat_props;
1718c0984e5SSebastian Reichel 		pbat->bat_desc.num_properties = ARRAY_SIZE(pmu_bat_props);
1728c0984e5SSebastian Reichel 		pbat->bat_desc.get_property = pmu_bat_get_property;
1738c0984e5SSebastian Reichel 		pbat->pbi = &pmu_batteries[i];
1748c0984e5SSebastian Reichel 		psy_cfg.drv_data = pbat;
1758c0984e5SSebastian Reichel 
1768c0984e5SSebastian Reichel 		pbat->bat = power_supply_register(&bat_pdev->dev,
1778c0984e5SSebastian Reichel 						  &pbat->bat_desc,
1788c0984e5SSebastian Reichel 						  &psy_cfg);
1798c0984e5SSebastian Reichel 		if (IS_ERR(pbat->bat)) {
1808c0984e5SSebastian Reichel 			ret = PTR_ERR(pbat->bat);
1818c0984e5SSebastian Reichel 			kfree(pbat);
1828c0984e5SSebastian Reichel 			goto battery_register_failed;
1838c0984e5SSebastian Reichel 		}
1848c0984e5SSebastian Reichel 		pbats[i] = pbat;
1858c0984e5SSebastian Reichel 	}
1868c0984e5SSebastian Reichel 
1878c0984e5SSebastian Reichel 	goto success;
1888c0984e5SSebastian Reichel 
1898c0984e5SSebastian Reichel battery_register_failed:
1908c0984e5SSebastian Reichel 	while (i--) {
1918c0984e5SSebastian Reichel 		if (!pbats[i])
1928c0984e5SSebastian Reichel 			continue;
1938c0984e5SSebastian Reichel 		power_supply_unregister(pbats[i]->bat);
1948c0984e5SSebastian Reichel 		kfree(pbats[i]);
1958c0984e5SSebastian Reichel 	}
1968c0984e5SSebastian Reichel 	power_supply_unregister(pmu_ac);
1978c0984e5SSebastian Reichel ac_register_failed:
1988c0984e5SSebastian Reichel 	platform_device_unregister(bat_pdev);
1998c0984e5SSebastian Reichel pdev_register_failed:
2008c0984e5SSebastian Reichel success:
2018c0984e5SSebastian Reichel 	return ret;
2028c0984e5SSebastian Reichel }
2038c0984e5SSebastian Reichel 
pmu_bat_exit(void)2048c0984e5SSebastian Reichel static void __exit pmu_bat_exit(void)
2058c0984e5SSebastian Reichel {
2068c0984e5SSebastian Reichel 	int i;
2078c0984e5SSebastian Reichel 
2088c0984e5SSebastian Reichel 	for (i = 0; i < PMU_MAX_BATTERIES; i++) {
2098c0984e5SSebastian Reichel 		if (!pbats[i])
2108c0984e5SSebastian Reichel 			continue;
2118c0984e5SSebastian Reichel 		power_supply_unregister(pbats[i]->bat);
2128c0984e5SSebastian Reichel 		kfree(pbats[i]);
2138c0984e5SSebastian Reichel 	}
2148c0984e5SSebastian Reichel 	power_supply_unregister(pmu_ac);
2158c0984e5SSebastian Reichel 	platform_device_unregister(bat_pdev);
2168c0984e5SSebastian Reichel }
2178c0984e5SSebastian Reichel 
2188c0984e5SSebastian Reichel module_init(pmu_bat_init);
2198c0984e5SSebastian Reichel module_exit(pmu_bat_exit);
2208c0984e5SSebastian Reichel 
2218c0984e5SSebastian Reichel MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
2228c0984e5SSebastian Reichel MODULE_LICENSE("GPL");
2238c0984e5SSebastian Reichel MODULE_DESCRIPTION("PMU battery driver");
224