162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Generic battery driver using IIO
462306a36Sopenharmony_ci * Copyright (C) 2012, Anish Kumar <anish198519851985@gmail.com>
562306a36Sopenharmony_ci * Copyright (c) 2023, Sebastian Reichel <sre@kernel.org>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci#include <linux/interrupt.h>
862306a36Sopenharmony_ci#include <linux/platform_device.h>
962306a36Sopenharmony_ci#include <linux/power_supply.h>
1062306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
1162306a36Sopenharmony_ci#include <linux/err.h>
1262306a36Sopenharmony_ci#include <linux/timer.h>
1362306a36Sopenharmony_ci#include <linux/jiffies.h>
1462306a36Sopenharmony_ci#include <linux/errno.h>
1562306a36Sopenharmony_ci#include <linux/init.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci#include <linux/iio/consumer.h>
1962306a36Sopenharmony_ci#include <linux/iio/types.h>
2062306a36Sopenharmony_ci#include <linux/of.h>
2162306a36Sopenharmony_ci#include <linux/devm-helpers.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define JITTER_DEFAULT 10 /* hope 10ms is enough */
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cienum gab_chan_type {
2662306a36Sopenharmony_ci	GAB_VOLTAGE = 0,
2762306a36Sopenharmony_ci	GAB_CURRENT,
2862306a36Sopenharmony_ci	GAB_POWER,
2962306a36Sopenharmony_ci	GAB_TEMP,
3062306a36Sopenharmony_ci	GAB_MAX_CHAN_TYPE
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/*
3462306a36Sopenharmony_ci * gab_chan_name suggests the standard channel names for commonly used
3562306a36Sopenharmony_ci * channel types.
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_cistatic const char *const gab_chan_name[] = {
3862306a36Sopenharmony_ci	[GAB_VOLTAGE]	= "voltage",
3962306a36Sopenharmony_ci	[GAB_CURRENT]	= "current",
4062306a36Sopenharmony_ci	[GAB_POWER]	= "power",
4162306a36Sopenharmony_ci	[GAB_TEMP]	= "temperature",
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistruct gab {
4562306a36Sopenharmony_ci	struct power_supply *psy;
4662306a36Sopenharmony_ci	struct power_supply_desc psy_desc;
4762306a36Sopenharmony_ci	struct iio_channel *channel[GAB_MAX_CHAN_TYPE];
4862306a36Sopenharmony_ci	struct delayed_work bat_work;
4962306a36Sopenharmony_ci	int status;
5062306a36Sopenharmony_ci	struct gpio_desc *charge_finished;
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic struct gab *to_generic_bat(struct power_supply *psy)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	return power_supply_get_drvdata(psy);
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic void gab_ext_power_changed(struct power_supply *psy)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct gab *adc_bat = to_generic_bat(psy);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	schedule_delayed_work(&adc_bat->bat_work, msecs_to_jiffies(0));
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic const enum power_supply_property gab_props[] = {
6662306a36Sopenharmony_ci	POWER_SUPPLY_PROP_STATUS,
6762306a36Sopenharmony_ci};
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/*
7062306a36Sopenharmony_ci * This properties are set based on the received platform data and this
7162306a36Sopenharmony_ci * should correspond one-to-one with enum chan_type.
7262306a36Sopenharmony_ci */
7362306a36Sopenharmony_cistatic const enum power_supply_property gab_dyn_props[] = {
7462306a36Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_NOW,
7562306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CURRENT_NOW,
7662306a36Sopenharmony_ci	POWER_SUPPLY_PROP_POWER_NOW,
7762306a36Sopenharmony_ci	POWER_SUPPLY_PROP_TEMP,
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic bool gab_charge_finished(struct gab *adc_bat)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	if (!adc_bat->charge_finished)
8362306a36Sopenharmony_ci		return false;
8462306a36Sopenharmony_ci	return gpiod_get_value(adc_bat->charge_finished);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic int gab_read_channel(struct gab *adc_bat, enum gab_chan_type channel,
8862306a36Sopenharmony_ci		int *result)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	int ret;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	ret = iio_read_channel_processed(adc_bat->channel[channel], result);
9362306a36Sopenharmony_ci	if (ret < 0)
9462306a36Sopenharmony_ci		dev_err(&adc_bat->psy->dev, "read channel error: %d\n", ret);
9562306a36Sopenharmony_ci	else
9662306a36Sopenharmony_ci		*result *= 1000;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	return ret;
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic int gab_get_property(struct power_supply *psy,
10262306a36Sopenharmony_ci		enum power_supply_property psp, union power_supply_propval *val)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct gab *adc_bat = to_generic_bat(psy);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	switch (psp) {
10762306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_STATUS:
10862306a36Sopenharmony_ci		val->intval = adc_bat->status;
10962306a36Sopenharmony_ci		return 0;
11062306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
11162306a36Sopenharmony_ci		return gab_read_channel(adc_bat, GAB_VOLTAGE, &val->intval);
11262306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CURRENT_NOW:
11362306a36Sopenharmony_ci		return gab_read_channel(adc_bat, GAB_CURRENT, &val->intval);
11462306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_POWER_NOW:
11562306a36Sopenharmony_ci		return gab_read_channel(adc_bat, GAB_POWER, &val->intval);
11662306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_TEMP:
11762306a36Sopenharmony_ci		return gab_read_channel(adc_bat, GAB_TEMP, &val->intval);
11862306a36Sopenharmony_ci	default:
11962306a36Sopenharmony_ci		return -EINVAL;
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic void gab_work(struct work_struct *work)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	struct gab *adc_bat;
12662306a36Sopenharmony_ci	struct delayed_work *delayed_work;
12762306a36Sopenharmony_ci	int status;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	delayed_work = to_delayed_work(work);
13062306a36Sopenharmony_ci	adc_bat = container_of(delayed_work, struct gab, bat_work);
13162306a36Sopenharmony_ci	status = adc_bat->status;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (!power_supply_am_i_supplied(adc_bat->psy))
13462306a36Sopenharmony_ci		adc_bat->status =  POWER_SUPPLY_STATUS_DISCHARGING;
13562306a36Sopenharmony_ci	else if (gab_charge_finished(adc_bat))
13662306a36Sopenharmony_ci		adc_bat->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
13762306a36Sopenharmony_ci	else
13862306a36Sopenharmony_ci		adc_bat->status = POWER_SUPPLY_STATUS_CHARGING;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	if (status != adc_bat->status)
14162306a36Sopenharmony_ci		power_supply_changed(adc_bat->psy);
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic irqreturn_t gab_charged(int irq, void *dev_id)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	struct gab *adc_bat = dev_id;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	schedule_delayed_work(&adc_bat->bat_work,
14962306a36Sopenharmony_ci			      msecs_to_jiffies(JITTER_DEFAULT));
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	return IRQ_HANDLED;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic int gab_probe(struct platform_device *pdev)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	struct gab *adc_bat;
15762306a36Sopenharmony_ci	struct power_supply_desc *psy_desc;
15862306a36Sopenharmony_ci	struct power_supply_config psy_cfg = {};
15962306a36Sopenharmony_ci	enum power_supply_property *properties;
16062306a36Sopenharmony_ci	int ret = 0;
16162306a36Sopenharmony_ci	int chan;
16262306a36Sopenharmony_ci	int index = ARRAY_SIZE(gab_props);
16362306a36Sopenharmony_ci	bool any = false;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	adc_bat = devm_kzalloc(&pdev->dev, sizeof(*adc_bat), GFP_KERNEL);
16662306a36Sopenharmony_ci	if (!adc_bat)
16762306a36Sopenharmony_ci		return -ENOMEM;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	psy_cfg.of_node = pdev->dev.of_node;
17062306a36Sopenharmony_ci	psy_cfg.drv_data = adc_bat;
17162306a36Sopenharmony_ci	psy_desc = &adc_bat->psy_desc;
17262306a36Sopenharmony_ci	psy_desc->name = dev_name(&pdev->dev);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	/* bootup default values for the battery */
17562306a36Sopenharmony_ci	adc_bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
17662306a36Sopenharmony_ci	psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
17762306a36Sopenharmony_ci	psy_desc->get_property = gab_get_property;
17862306a36Sopenharmony_ci	psy_desc->external_power_changed = gab_ext_power_changed;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	/*
18162306a36Sopenharmony_ci	 * copying the static properties and allocating extra memory for holding
18262306a36Sopenharmony_ci	 * the extra configurable properties received from platform data.
18362306a36Sopenharmony_ci	 */
18462306a36Sopenharmony_ci	properties = devm_kcalloc(&pdev->dev,
18562306a36Sopenharmony_ci				  ARRAY_SIZE(gab_props) +
18662306a36Sopenharmony_ci				  ARRAY_SIZE(gab_chan_name),
18762306a36Sopenharmony_ci				  sizeof(*properties),
18862306a36Sopenharmony_ci				  GFP_KERNEL);
18962306a36Sopenharmony_ci	if (!properties)
19062306a36Sopenharmony_ci		return -ENOMEM;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	memcpy(properties, gab_props, sizeof(gab_props));
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	/*
19562306a36Sopenharmony_ci	 * getting channel from iio and copying the battery properties
19662306a36Sopenharmony_ci	 * based on the channel supported by consumer device.
19762306a36Sopenharmony_ci	 */
19862306a36Sopenharmony_ci	for (chan = 0; chan < ARRAY_SIZE(gab_chan_name); chan++) {
19962306a36Sopenharmony_ci		adc_bat->channel[chan] = devm_iio_channel_get(&pdev->dev, gab_chan_name[chan]);
20062306a36Sopenharmony_ci		if (IS_ERR(adc_bat->channel[chan])) {
20162306a36Sopenharmony_ci			ret = PTR_ERR(adc_bat->channel[chan]);
20262306a36Sopenharmony_ci			if (ret != -ENODEV)
20362306a36Sopenharmony_ci				return dev_err_probe(&pdev->dev, ret, "Failed to get ADC channel %s\n", gab_chan_name[chan]);
20462306a36Sopenharmony_ci			adc_bat->channel[chan] = NULL;
20562306a36Sopenharmony_ci		} else if (adc_bat->channel[chan]) {
20662306a36Sopenharmony_ci			/* copying properties for supported channels only */
20762306a36Sopenharmony_ci			int index2;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci			for (index2 = 0; index2 < index; index2++) {
21062306a36Sopenharmony_ci				if (properties[index2] == gab_dyn_props[chan])
21162306a36Sopenharmony_ci					break;	/* already known */
21262306a36Sopenharmony_ci			}
21362306a36Sopenharmony_ci			if (index2 == index)	/* really new */
21462306a36Sopenharmony_ci				properties[index++] = gab_dyn_props[chan];
21562306a36Sopenharmony_ci			any = true;
21662306a36Sopenharmony_ci		}
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	/* none of the channels are supported so let's bail out */
22062306a36Sopenharmony_ci	if (!any)
22162306a36Sopenharmony_ci		return dev_err_probe(&pdev->dev, -ENODEV, "Failed to get any ADC channel\n");
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	/*
22462306a36Sopenharmony_ci	 * Total number of properties is equal to static properties
22562306a36Sopenharmony_ci	 * plus the dynamic properties.Some properties may not be set
22662306a36Sopenharmony_ci	 * as come channels may be not be supported by the device.So
22762306a36Sopenharmony_ci	 * we need to take care of that.
22862306a36Sopenharmony_ci	 */
22962306a36Sopenharmony_ci	psy_desc->properties = properties;
23062306a36Sopenharmony_ci	psy_desc->num_properties = index;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	adc_bat->psy = devm_power_supply_register(&pdev->dev, psy_desc, &psy_cfg);
23362306a36Sopenharmony_ci	if (IS_ERR(adc_bat->psy))
23462306a36Sopenharmony_ci		return dev_err_probe(&pdev->dev, PTR_ERR(adc_bat->psy), "Failed to register power-supply device\n");
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	ret = devm_delayed_work_autocancel(&pdev->dev, &adc_bat->bat_work, gab_work);
23762306a36Sopenharmony_ci	if (ret)
23862306a36Sopenharmony_ci		return dev_err_probe(&pdev->dev, ret, "Failed to register delayed work\n");
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	adc_bat->charge_finished = devm_gpiod_get_optional(&pdev->dev, "charged", GPIOD_IN);
24162306a36Sopenharmony_ci	if (adc_bat->charge_finished) {
24262306a36Sopenharmony_ci		int irq;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci		irq = gpiod_to_irq(adc_bat->charge_finished);
24562306a36Sopenharmony_ci		ret = devm_request_any_context_irq(&pdev->dev, irq, gab_charged,
24662306a36Sopenharmony_ci				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
24762306a36Sopenharmony_ci				"battery charged", adc_bat);
24862306a36Sopenharmony_ci		if (ret < 0)
24962306a36Sopenharmony_ci			return dev_err_probe(&pdev->dev, ret, "Failed to register irq\n");
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	platform_set_drvdata(pdev, adc_bat);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	/* Schedule timer to check current status */
25562306a36Sopenharmony_ci	schedule_delayed_work(&adc_bat->bat_work,
25662306a36Sopenharmony_ci			msecs_to_jiffies(0));
25762306a36Sopenharmony_ci	return 0;
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic int __maybe_unused gab_suspend(struct device *dev)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	struct gab *adc_bat = dev_get_drvdata(dev);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	cancel_delayed_work_sync(&adc_bat->bat_work);
26562306a36Sopenharmony_ci	adc_bat->status = POWER_SUPPLY_STATUS_UNKNOWN;
26662306a36Sopenharmony_ci	return 0;
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic int __maybe_unused gab_resume(struct device *dev)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	struct gab *adc_bat = dev_get_drvdata(dev);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	/* Schedule timer to check current status */
27462306a36Sopenharmony_ci	schedule_delayed_work(&adc_bat->bat_work,
27562306a36Sopenharmony_ci			      msecs_to_jiffies(JITTER_DEFAULT));
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	return 0;
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(gab_pm_ops, gab_suspend, gab_resume);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic const struct of_device_id gab_match[] = {
28362306a36Sopenharmony_ci	{ .compatible = "adc-battery" },
28462306a36Sopenharmony_ci	{ }
28562306a36Sopenharmony_ci};
28662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, gab_match);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic struct platform_driver gab_driver = {
28962306a36Sopenharmony_ci	.driver		= {
29062306a36Sopenharmony_ci		.name	= "generic-adc-battery",
29162306a36Sopenharmony_ci		.pm	= &gab_pm_ops,
29262306a36Sopenharmony_ci		.of_match_table = gab_match,
29362306a36Sopenharmony_ci	},
29462306a36Sopenharmony_ci	.probe		= gab_probe,
29562306a36Sopenharmony_ci};
29662306a36Sopenharmony_cimodule_platform_driver(gab_driver);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ciMODULE_AUTHOR("anish kumar <anish198519851985@gmail.com>");
29962306a36Sopenharmony_ciMODULE_DESCRIPTION("generic battery driver using IIO");
30062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
301