File Index
Files and directories
Sign in
FILE

lt3651-charger.c

File preview with syntax highlighting.

linux-6.19.14/drivers/power/supply/lt3651-charger.c C
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 *  Driver for Analog Devices (Linear Technology) LT3651 charger IC.
  4 *  Copyright (C) 2017, Topic Embedded Products
  5 */
  6
  7#include <linux/device.h>
  8#include <linux/gpio/consumer.h>
  9#include <linux/init.h>
 10#include <linux/interrupt.h>
 11#include <linux/kernel.h>
 12#include <linux/module.h>
 13#include <linux/platform_device.h>
 14#include <linux/power_supply.h>
 15#include <linux/slab.h>
 16#include <linux/of.h>
 17
 18struct lt3651_charger {
 19	struct power_supply *charger;
 20	struct power_supply_desc charger_desc;
 21	struct gpio_desc *acpr_gpio;
 22	struct gpio_desc *fault_gpio;
 23	struct gpio_desc *chrg_gpio;
 24};
 25
 26static irqreturn_t lt3651_charger_irq(int irq, void *devid)
 27{
 28	struct power_supply *charger = devid;
 29
 30	power_supply_changed(charger);
 31
 32	return IRQ_HANDLED;
 33}
 34
 35static inline struct lt3651_charger *psy_to_lt3651_charger(
 36	struct power_supply *psy)
 37{
 38	return power_supply_get_drvdata(psy);
 39}
 40
 41static int lt3651_charger_get_property(struct power_supply *psy,
 42		enum power_supply_property psp, union power_supply_propval *val)
 43{
 44	struct lt3651_charger *lt3651_charger = psy_to_lt3651_charger(psy);
 45
 46	switch (psp) {
 47	case POWER_SUPPLY_PROP_STATUS:
 48		if (!lt3651_charger->chrg_gpio) {
 49			val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
 50			break;
 51		}
 52		if (gpiod_get_value(lt3651_charger->chrg_gpio))
 53			val->intval = POWER_SUPPLY_STATUS_CHARGING;
 54		else
 55			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
 56		break;
 57	case POWER_SUPPLY_PROP_ONLINE:
 58		val->intval = gpiod_get_value(lt3651_charger->acpr_gpio);
 59		break;
 60	case POWER_SUPPLY_PROP_HEALTH:
 61		if (!lt3651_charger->fault_gpio) {
 62			val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
 63			break;
 64		}
 65		if (!gpiod_get_value(lt3651_charger->fault_gpio)) {
 66			val->intval = POWER_SUPPLY_HEALTH_GOOD;
 67			break;
 68		}
 69		/*
 70		 * If the fault pin is active, the chrg pin explains the type
 71		 * of failure.
 72		 */
 73		if (!lt3651_charger->chrg_gpio) {
 74			val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
 75			break;
 76		}
 77		val->intval = gpiod_get_value(lt3651_charger->chrg_gpio) ?
 78				POWER_SUPPLY_HEALTH_OVERHEAT :
 79				POWER_SUPPLY_HEALTH_DEAD;
 80		break;
 81	default:
 82		return -EINVAL;
 83	}
 84
 85	return 0;
 86}
 87
 88static enum power_supply_property lt3651_charger_properties[] = {
 89	POWER_SUPPLY_PROP_STATUS,
 90	POWER_SUPPLY_PROP_ONLINE,
 91	POWER_SUPPLY_PROP_HEALTH,
 92};
 93
 94static int lt3651_charger_probe(struct platform_device *pdev)
 95{
 96	struct power_supply_config psy_cfg = {};
 97	struct lt3651_charger *lt3651_charger;
 98	struct power_supply_desc *charger_desc;
 99	int ret;
100
101	lt3651_charger = devm_kzalloc(&pdev->dev, sizeof(*lt3651_charger),
102					GFP_KERNEL);
103	if (!lt3651_charger)
104		return -ENOMEM;
105
106	lt3651_charger->acpr_gpio = devm_gpiod_get(&pdev->dev,
107					"lltc,acpr", GPIOD_IN);
108	if (IS_ERR(lt3651_charger->acpr_gpio)) {
109		ret = PTR_ERR(lt3651_charger->acpr_gpio);
110		dev_err(&pdev->dev, "Failed to acquire acpr GPIO: %d\n", ret);
111		return ret;
112	}
113	lt3651_charger->fault_gpio = devm_gpiod_get_optional(&pdev->dev,
114					"lltc,fault", GPIOD_IN);
115	if (IS_ERR(lt3651_charger->fault_gpio)) {
116		ret = PTR_ERR(lt3651_charger->fault_gpio);
117		dev_err(&pdev->dev, "Failed to acquire fault GPIO: %d\n", ret);
118		return ret;
119	}
120	lt3651_charger->chrg_gpio = devm_gpiod_get_optional(&pdev->dev,
121					"lltc,chrg", GPIOD_IN);
122	if (IS_ERR(lt3651_charger->chrg_gpio)) {
123		ret = PTR_ERR(lt3651_charger->chrg_gpio);
124		dev_err(&pdev->dev, "Failed to acquire chrg GPIO: %d\n", ret);
125		return ret;
126	}
127
128	charger_desc = &lt3651_charger->charger_desc;
129	charger_desc->name = pdev->dev.of_node->name;
130	charger_desc->type = POWER_SUPPLY_TYPE_MAINS;
131	charger_desc->properties = lt3651_charger_properties;
132	charger_desc->num_properties = ARRAY_SIZE(lt3651_charger_properties);
133	charger_desc->get_property = lt3651_charger_get_property;
134	psy_cfg.fwnode = dev_fwnode(&pdev->dev);
135	psy_cfg.drv_data = lt3651_charger;
136
137	lt3651_charger->charger = devm_power_supply_register(&pdev->dev,
138						      charger_desc, &psy_cfg);
139	if (IS_ERR(lt3651_charger->charger)) {
140		ret = PTR_ERR(lt3651_charger->charger);
141		dev_err(&pdev->dev, "Failed to register power supply: %d\n",
142			ret);
143		return ret;
144	}
145
146	/*
147	 * Acquire IRQs for the GPIO pins if possible. If the system does not
148	 * support IRQs on these pins, userspace will have to poll the sysfs
149	 * files manually.
150	 */
151	if (lt3651_charger->acpr_gpio) {
152		ret = gpiod_to_irq(lt3651_charger->acpr_gpio);
153		if (ret >= 0)
154			ret = devm_request_any_context_irq(&pdev->dev, ret,
155				lt3651_charger_irq,
156				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
157				dev_name(&pdev->dev), lt3651_charger->charger);
158		if (ret < 0)
159			dev_warn(&pdev->dev, "Failed to request acpr irq\n");
160	}
161	if (lt3651_charger->fault_gpio) {
162		ret = gpiod_to_irq(lt3651_charger->fault_gpio);
163		if (ret >= 0)
164			ret = devm_request_any_context_irq(&pdev->dev, ret,
165				lt3651_charger_irq,
166				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
167				dev_name(&pdev->dev), lt3651_charger->charger);
168		if (ret < 0)
169			dev_warn(&pdev->dev, "Failed to request fault irq\n");
170	}
171	if (lt3651_charger->chrg_gpio) {
172		ret = gpiod_to_irq(lt3651_charger->chrg_gpio);
173		if (ret >= 0)
174			ret = devm_request_any_context_irq(&pdev->dev, ret,
175				lt3651_charger_irq,
176				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
177				dev_name(&pdev->dev), lt3651_charger->charger);
178		if (ret < 0)
179			dev_warn(&pdev->dev, "Failed to request chrg irq\n");
180	}
181
182	platform_set_drvdata(pdev, lt3651_charger);
183
184	return 0;
185}
186
187static const struct of_device_id lt3651_charger_match[] = {
188	{ .compatible = "lltc,ltc3651-charger" }, /* DEPRECATED */
189	{ .compatible = "lltc,lt3651-charger" },
190	{ }
191};
192MODULE_DEVICE_TABLE(of, lt3651_charger_match);
193
194static struct platform_driver lt3651_charger_driver = {
195	.probe = lt3651_charger_probe,
196	.driver = {
197		.name = "lt3651-charger",
198		.of_match_table = lt3651_charger_match,
199	},
200};
201
202module_platform_driver(lt3651_charger_driver);
203
204MODULE_AUTHOR("Mike Looijmans <mike.looijmans@topic.nl>");
205MODULE_DESCRIPTION("Driver for LT3651 charger");
206MODULE_LICENSE("GPL");
207MODULE_ALIAS("platform:lt3651-charger");
~/ Read-only file preview.