brintos

brintos / linux-shallow public Read only

0
0
Text · 6.7 KiB · 3935162 Raw
240 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Dumb driver for LiIon batteries using TWL4030 madc.4 *5 * Copyright 2013 Golden Delicious Computers6 * Lukas Märdian <lukas@goldelico.com>7 *8 * Based on dumb driver for gta01 battery9 * Copyright 2009 Openmoko, Inc10 * Balaji Rao <balajirrao@openmoko.org>11 */12 13#include <linux/module.h>14#include <linux/param.h>15#include <linux/delay.h>16#include <linux/workqueue.h>17#include <linux/platform_device.h>18#include <linux/power_supply.h>19#include <linux/slab.h>20#include <linux/sort.h>21#include <linux/power/twl4030_madc_battery.h>22#include <linux/iio/consumer.h>23 24struct twl4030_madc_battery {25	struct power_supply *psy;26	struct twl4030_madc_bat_platform_data *pdata;27	struct iio_channel *channel_temp;28	struct iio_channel *channel_ichg;29	struct iio_channel *channel_vbat;30};31 32static enum power_supply_property twl4030_madc_bat_props[] = {33	POWER_SUPPLY_PROP_PRESENT,34	POWER_SUPPLY_PROP_STATUS,35	POWER_SUPPLY_PROP_TECHNOLOGY,36	POWER_SUPPLY_PROP_VOLTAGE_NOW,37	POWER_SUPPLY_PROP_CURRENT_NOW,38	POWER_SUPPLY_PROP_CAPACITY,39	POWER_SUPPLY_PROP_CHARGE_FULL,40	POWER_SUPPLY_PROP_CHARGE_NOW,41	POWER_SUPPLY_PROP_TEMP,42	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,43};44 45static int madc_read(struct iio_channel *channel)46{47	int val, err;48	err = iio_read_channel_processed(channel, &val);49	if (err < 0)50		return err;51 52	return val;53}54 55static int twl4030_madc_bat_get_charging_status(struct twl4030_madc_battery *bt)56{57	return (madc_read(bt->channel_ichg) > 0) ? 1 : 0;58}59 60static int twl4030_madc_bat_get_voltage(struct twl4030_madc_battery *bt)61{62	return madc_read(bt->channel_vbat);63}64 65static int twl4030_madc_bat_get_current(struct twl4030_madc_battery *bt)66{67	return madc_read(bt->channel_ichg) * 1000;68}69 70static int twl4030_madc_bat_get_temp(struct twl4030_madc_battery *bt)71{72	return madc_read(bt->channel_temp) * 10;73}74 75static int twl4030_madc_bat_voltscale(struct twl4030_madc_battery *bat,76					int volt)77{78	struct twl4030_madc_bat_calibration *calibration;79	int i, res = 0;80 81	/* choose charging curve */82	if (twl4030_madc_bat_get_charging_status(bat))83		calibration = bat->pdata->charging;84	else85		calibration = bat->pdata->discharging;86 87	if (volt > calibration[0].voltage) {88		res = calibration[0].level;89	} else {90		for (i = 0; calibration[i+1].voltage >= 0; i++) {91			if (volt <= calibration[i].voltage &&92					volt >= calibration[i+1].voltage) {93				/* interval found - interpolate within range */94				res = calibration[i].level -95					((calibration[i].voltage - volt) *96					(calibration[i].level -97					calibration[i+1].level)) /98					(calibration[i].voltage -99					calibration[i+1].voltage);100				break;101			}102		}103	}104	return res;105}106 107static int twl4030_madc_bat_get_property(struct power_supply *psy,108					enum power_supply_property psp,109					union power_supply_propval *val)110{111	struct twl4030_madc_battery *bat = power_supply_get_drvdata(psy);112 113	switch (psp) {114	case POWER_SUPPLY_PROP_STATUS:115		if (twl4030_madc_bat_voltscale(bat,116				twl4030_madc_bat_get_voltage(bat)) > 95)117			val->intval = POWER_SUPPLY_STATUS_FULL;118		else {119			if (twl4030_madc_bat_get_charging_status(bat))120				val->intval = POWER_SUPPLY_STATUS_CHARGING;121			else122				val->intval = POWER_SUPPLY_STATUS_DISCHARGING;123		}124		break;125	case POWER_SUPPLY_PROP_VOLTAGE_NOW:126		val->intval = twl4030_madc_bat_get_voltage(bat) * 1000;127		break;128	case POWER_SUPPLY_PROP_TECHNOLOGY:129		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;130		break;131	case POWER_SUPPLY_PROP_CURRENT_NOW:132		val->intval = twl4030_madc_bat_get_current(bat);133		break;134	case POWER_SUPPLY_PROP_PRESENT:135		/* assume battery is always present */136		val->intval = 1;137		break;138	case POWER_SUPPLY_PROP_CHARGE_NOW: {139			int percent = twl4030_madc_bat_voltscale(bat,140					twl4030_madc_bat_get_voltage(bat));141			val->intval = (percent * bat->pdata->capacity) / 100;142			break;143		}144	case POWER_SUPPLY_PROP_CAPACITY:145		val->intval = twl4030_madc_bat_voltscale(bat,146					twl4030_madc_bat_get_voltage(bat));147		break;148	case POWER_SUPPLY_PROP_CHARGE_FULL:149		val->intval = bat->pdata->capacity;150		break;151	case POWER_SUPPLY_PROP_TEMP:152		val->intval = twl4030_madc_bat_get_temp(bat);153		break;154	case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: {155			int percent = twl4030_madc_bat_voltscale(bat,156					twl4030_madc_bat_get_voltage(bat));157			/* in mAh */158			int chg = (percent * (bat->pdata->capacity/1000))/100;159 160			/* assume discharge with 400 mA (ca. 1.5W) */161			val->intval = (3600l * chg) / 400;162			break;163		}164	default:165		return -EINVAL;166	}167 168	return 0;169}170 171static const struct power_supply_desc twl4030_madc_bat_desc = {172	.name			= "twl4030_battery",173	.type			= POWER_SUPPLY_TYPE_BATTERY,174	.properties		= twl4030_madc_bat_props,175	.num_properties		= ARRAY_SIZE(twl4030_madc_bat_props),176	.get_property		= twl4030_madc_bat_get_property,177	.external_power_changed	= power_supply_changed,178};179 180static int twl4030_cmp(const void *a, const void *b)181{182	return ((struct twl4030_madc_bat_calibration *)b)->voltage -183		((struct twl4030_madc_bat_calibration *)a)->voltage;184}185 186static int twl4030_madc_battery_probe(struct platform_device *pdev)187{188	struct twl4030_madc_battery *twl4030_madc_bat;189	struct twl4030_madc_bat_platform_data *pdata = pdev->dev.platform_data;190	struct power_supply_config psy_cfg = {};191 192	twl4030_madc_bat = devm_kzalloc(&pdev->dev, sizeof(*twl4030_madc_bat),193				GFP_KERNEL);194	if (!twl4030_madc_bat)195		return -ENOMEM;196 197	twl4030_madc_bat->channel_temp = devm_iio_channel_get(&pdev->dev, "temp");198	if (IS_ERR(twl4030_madc_bat->channel_temp))199		return PTR_ERR(twl4030_madc_bat->channel_temp);200 201	twl4030_madc_bat->channel_ichg = devm_iio_channel_get(&pdev->dev, "ichg");202	if (IS_ERR(twl4030_madc_bat->channel_ichg))203		return PTR_ERR(twl4030_madc_bat->channel_ichg);204 205	twl4030_madc_bat->channel_vbat = devm_iio_channel_get(&pdev->dev, "vbat");206	if (IS_ERR(twl4030_madc_bat->channel_vbat))207		return PTR_ERR(twl4030_madc_bat->channel_vbat);208 209	/* sort charging and discharging calibration data */210	sort(pdata->charging, pdata->charging_size,211		sizeof(struct twl4030_madc_bat_calibration),212		twl4030_cmp, NULL);213	sort(pdata->discharging, pdata->discharging_size,214		sizeof(struct twl4030_madc_bat_calibration),215		twl4030_cmp, NULL);216 217	twl4030_madc_bat->pdata = pdata;218	psy_cfg.drv_data = twl4030_madc_bat;219	twl4030_madc_bat->psy = devm_power_supply_register(&pdev->dev,220							   &twl4030_madc_bat_desc,221							   &psy_cfg);222	if (IS_ERR(twl4030_madc_bat->psy))223		return PTR_ERR(twl4030_madc_bat->psy);224 225	return 0;226}227 228static struct platform_driver twl4030_madc_battery_driver = {229	.driver = {230		.name = "twl4030_madc_battery",231	},232	.probe  = twl4030_madc_battery_probe,233};234module_platform_driver(twl4030_madc_battery_driver);235 236MODULE_LICENSE("GPL");237MODULE_AUTHOR("Lukas Märdian <lukas@goldelico.com>");238MODULE_DESCRIPTION("twl4030_madc battery driver");239MODULE_ALIAS("platform:twl4030_madc_battery");240