brintos

brintos / linux-shallow public Read only

0
0
Text · 5.1 KiB · 0a40f42 Raw
193 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Battery driver for the Ingenic JZ47xx SoCs4 * Copyright (c) 2019 Artur Rojek <contact@artur-rojek.eu>5 *6 * based on drivers/power/supply/jz4740-battery.c7 */8 9#include <linux/iio/consumer.h>10#include <linux/module.h>11#include <linux/of.h>12#include <linux/platform_device.h>13#include <linux/power_supply.h>14#include <linux/property.h>15 16struct ingenic_battery {17	struct device *dev;18	struct iio_channel *channel;19	struct power_supply_desc desc;20	struct power_supply *battery;21	struct power_supply_battery_info *info;22};23 24static int ingenic_battery_get_property(struct power_supply *psy,25					enum power_supply_property psp,26					union power_supply_propval *val)27{28	struct ingenic_battery *bat = power_supply_get_drvdata(psy);29	struct power_supply_battery_info *info = bat->info;30	int ret;31 32	switch (psp) {33	case POWER_SUPPLY_PROP_HEALTH:34		ret = iio_read_channel_processed_scale(bat->channel,35						       &val->intval,36						       1000);37		if (val->intval < info->voltage_min_design_uv)38			val->intval = POWER_SUPPLY_HEALTH_DEAD;39		else if (val->intval > info->voltage_max_design_uv)40			val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;41		else42			val->intval = POWER_SUPPLY_HEALTH_GOOD;43		return ret;44	case POWER_SUPPLY_PROP_VOLTAGE_NOW:45		ret = iio_read_channel_processed_scale(bat->channel,46						       &val->intval,47						       1000);48		return ret;49	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:50		val->intval = info->voltage_min_design_uv;51		return 0;52	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:53		val->intval = info->voltage_max_design_uv;54		return 0;55	default:56		return -EINVAL;57	}58}59 60/* Set the most appropriate IIO channel voltage reference scale61 * based on the battery's max voltage.62 */63static int ingenic_battery_set_scale(struct ingenic_battery *bat)64{65	const int *scale_raw;66	int scale_len, scale_type, best_idx = -1, best_mV, max_raw, i, ret;67	u64 max_mV;68 69	ret = iio_read_max_channel_raw(bat->channel, &max_raw);70	if (ret) {71		dev_err(bat->dev, "Unable to read max raw channel value\n");72		return ret;73	}74 75	ret = iio_read_avail_channel_attribute(bat->channel, &scale_raw,76					       &scale_type, &scale_len,77					       IIO_CHAN_INFO_SCALE);78	if (ret < 0) {79		dev_err(bat->dev, "Unable to read channel avail scale\n");80		return ret;81	}82	if (ret != IIO_AVAIL_LIST || scale_type != IIO_VAL_FRACTIONAL_LOG2)83		return -EINVAL;84 85	max_mV = bat->info->voltage_max_design_uv / 1000;86 87	for (i = 0; i < scale_len; i += 2) {88		u64 scale_mV = (max_raw * scale_raw[i]) >> scale_raw[i + 1];89 90		if (scale_mV < max_mV)91			continue;92 93		if (best_idx >= 0 && scale_mV > best_mV)94			continue;95 96		best_mV = scale_mV;97		best_idx = i;98	}99 100	if (best_idx < 0) {101		dev_err(bat->dev, "Unable to find matching voltage scale\n");102		return -EINVAL;103	}104 105	/* Only set scale if there is more than one (fractional) entry */106	if (scale_len > 2) {107		ret = iio_write_channel_attribute(bat->channel,108						  scale_raw[best_idx],109						  scale_raw[best_idx + 1],110						  IIO_CHAN_INFO_SCALE);111		if (ret)112			return ret;113	}114 115	return 0;116}117 118static enum power_supply_property ingenic_battery_properties[] = {119	POWER_SUPPLY_PROP_HEALTH,120	POWER_SUPPLY_PROP_VOLTAGE_NOW,121	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,122	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,123};124 125static int ingenic_battery_probe(struct platform_device *pdev)126{127	struct device *dev = &pdev->dev;128	struct ingenic_battery *bat;129	struct power_supply_config psy_cfg = {};130	struct power_supply_desc *desc;131	int ret;132 133	bat = devm_kzalloc(dev, sizeof(*bat), GFP_KERNEL);134	if (!bat)135		return -ENOMEM;136 137	bat->dev = dev;138	bat->channel = devm_iio_channel_get(dev, "battery");139	if (IS_ERR(bat->channel))140		return PTR_ERR(bat->channel);141 142	desc = &bat->desc;143	desc->name = "jz-battery";144	desc->type = POWER_SUPPLY_TYPE_BATTERY;145	desc->properties = ingenic_battery_properties;146	desc->num_properties = ARRAY_SIZE(ingenic_battery_properties);147	desc->get_property = ingenic_battery_get_property;148	psy_cfg.drv_data = bat;149	psy_cfg.of_node = dev->of_node;150 151	bat->battery = devm_power_supply_register(dev, desc, &psy_cfg);152	if (IS_ERR(bat->battery))153		return dev_err_probe(dev, PTR_ERR(bat->battery),154				     "Unable to register battery\n");155 156	ret = power_supply_get_battery_info(bat->battery, &bat->info);157	if (ret) {158		dev_err(dev, "Unable to get battery info: %d\n", ret);159		return ret;160	}161	if (bat->info->voltage_min_design_uv < 0) {162		dev_err(dev, "Unable to get voltage min design\n");163		return bat->info->voltage_min_design_uv;164	}165	if (bat->info->voltage_max_design_uv < 0) {166		dev_err(dev, "Unable to get voltage max design\n");167		return bat->info->voltage_max_design_uv;168	}169 170	return ingenic_battery_set_scale(bat);171}172 173#ifdef CONFIG_OF174static const struct of_device_id ingenic_battery_of_match[] = {175	{ .compatible = "ingenic,jz4740-battery", },176	{ },177};178MODULE_DEVICE_TABLE(of, ingenic_battery_of_match);179#endif180 181static struct platform_driver ingenic_battery_driver = {182	.driver = {183		.name = "ingenic-battery",184		.of_match_table = of_match_ptr(ingenic_battery_of_match),185	},186	.probe = ingenic_battery_probe,187};188module_platform_driver(ingenic_battery_driver);189 190MODULE_DESCRIPTION("Battery driver for Ingenic JZ47xx SoCs");191MODULE_AUTHOR("Artur Rojek <contact@artur-rojek.eu>");192MODULE_LICENSE("GPL");193