* [PATCH 0/2] Add LT8491 driver
@ 2025-01-10 8:02 John Erasmus Mari Geronimo
2025-01-10 8:02 ` [PATCH 1/2] dt-bindings: power: supply: add adi,lt8491.yaml John Erasmus Mari Geronimo
` (2 more replies)
0 siblings, 3 replies; 11+ messages in thread
From: John Erasmus Mari Geronimo @ 2025-01-10 8:02 UTC (permalink / raw)
To: linux-pm, devicetree, linux-kernel
Cc: Sebastian Reichel, Rob Herring, Krzysztof Kozlowski, Conor Dooley
This patch series adds a driver and device tree bindings for the LT8491
battery charge controller.
The LT8491 is a buck-boost switching regulator battery charger that
implements a constant-current constant-voltage (CCCV) charging profile
used for most battery types, including sealed lead-acid (SLA), flooded,
gel and lithium-ion.
John Erasmus Mari Geronimo (2):
dt-bindings: power: supply: add adi,lt8491.yaml
power: supply: add LT8491 battery charger driver
.../bindings/power/supply/adi,lt8491.yaml | 89 ++++
MAINTAINERS | 7 +
drivers/power/supply/Kconfig | 9 +
drivers/power/supply/Makefile | 1 +
drivers/power/supply/lt8491_charger.c | 410 ++++++++++++++++++
5 files changed, 516 insertions(+)
create mode 100644 Documentation/devicetree/bindings/power/supply/adi,lt8491.yaml
create mode 100644 drivers/power/supply/lt8491_charger.c
base-commit: a3a8799165ff83bb764fd800c6559c3cba0ddac3
--
2.34.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 1/2] dt-bindings: power: supply: add adi,lt8491.yaml
2025-01-10 8:02 [PATCH 0/2] Add LT8491 driver John Erasmus Mari Geronimo
@ 2025-01-10 8:02 ` John Erasmus Mari Geronimo
2025-01-10 9:26 ` Krzysztof Kozlowski
2025-01-10 8:02 ` [PATCH 2/2] power: supply: add LT8491 battery charger driver John Erasmus Mari Geronimo
2026-09-03 10:46 ` [PATCH v2 0/2] Add LT8491 driver John Erasmus Mari Geronimo
2 siblings, 1 reply; 11+ messages in thread
From: John Erasmus Mari Geronimo @ 2025-01-10 8:02 UTC (permalink / raw)
To: linux-pm, devicetree, linux-kernel
Cc: Sebastian Reichel, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Add documentation for devicetree bindings for LT8491
Signed-off-by: John Erasmus Mari Geronimo <johnerasmusmari.geronimo@analog.com>
---
.../bindings/power/supply/adi,lt8491.yaml | 89 +++++++++++++++++++
MAINTAINERS | 6 ++
2 files changed, 95 insertions(+)
create mode 100644 Documentation/devicetree/bindings/power/supply/adi,lt8491.yaml
diff --git a/Documentation/devicetree/bindings/power/supply/adi,lt8491.yaml b/Documentation/devicetree/bindings/power/supply/adi,lt8491.yaml
new file mode 100644
index 000000000..1451fa1d5
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/adi,lt8491.yaml
@@ -0,0 +1,89 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/power/supply/adi,lt8491.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Linear Technology (Analog Devices) LT8491 Battery Charger
+
+maintainers:
+ - John Erasmus Mari Geronimo <johnerasmusmari.geronimo@analog.com>
+
+description: |
+ The LT8491 is a buck-boost switching regulator battery charger that implements
+ a constant-current constant-voltage (CCCV) charging profile used for most
+ battery types, including sealed lead-acid (SLA), flooded, gel and lithium-ion.
+
+ Specifications about the charger can be found at:
+ https://www.analog.com/en/products/lt8491.html
+
+properties:
+ compatible:
+ enum:
+ - adi,lt8491
+
+ reg:
+ maxItems: 1
+
+ adi,rsense1-micro-ohms:
+ description: Resistance value of RSENSE1 in micro ohms.
+
+ adi,rimon-out-ohms:
+ description: Resistance value of RIMON_OUT in ohms.
+
+ adi,rsense2-micro-ohms:
+ description: Resistance value of RSENSE2 in micro ohms.
+
+ adi,rdaco-ohms:
+ description: Resistance value of RDACO1 + RDACO2 in ohms.
+
+ adi,rfbout1-ohms:
+ description: Resistance value of RFBOUT1 in ohms.
+
+ adi,rfbout2-ohms:
+ description: Resistance value of RFBOUT2 in ohms.
+
+ adi,rdaci-ohms:
+ description: Resistance value of RDACI1 + RDACI2 in ohms.
+
+ adi,rfbin2-ohms:
+ description: Resistance value of RFBIN2 in ohms.
+
+ adi,rfbin1-ohms:
+ description: Resistance value of RFBIN1 in ohms.
+
+required:
+ - compatible
+ - reg
+ - adi,rsense1-micro-ohms
+ - adi,rimon-out-ohms
+ - adi,rsense2-micro-ohms
+ - adi,rdaco-ohms
+ - adi,rfbout1-ohms
+ - adi,rfbout2-ohms
+ - adi,rdaci-ohms
+ - adi,rfbin2-ohms
+ - adi,rfbin1-ohms
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ battery-charger@10 {
+ compatible = "adi,lt8491";
+ reg = <0x10>;
+ adi,rsense1-micro-ohms = <5000>;
+ adi,rimon-out-ohms = <124000>;
+ adi,rsense2-micro-ohms = <3000>;
+ adi,rdaco-ohms = <64900>;
+ adi,rfbout1-ohms = <113000>;
+ adi,rfbout2-ohms = <10000>;
+ adi,rdaci-ohms = <7000>;
+ adi,rfbin2-ohms = <3480>;
+ adi,rfbin1-ohms = <102000>;
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 6e04c83eb..96f510358 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13559,6 +13559,12 @@ L: linux-scsi@vger.kernel.org
S: Maintained
F: drivers/scsi/sym53c8xx_2/
+LT8491 BATTERY CHARGER DRIVER
+M: John Erasmus Mari Geronimo <johnerasmusmari.geronimo@analog.com>
+L: linux-pm@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/power/supply/adi,lt8491.yaml
+
LTC1660 DAC DRIVER
M: Marcus Folkesson <marcus.folkesson@gmail.com>
L: linux-iio@vger.kernel.org
--
2.34.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 2/2] power: supply: add LT8491 battery charger driver
2025-01-10 8:02 [PATCH 0/2] Add LT8491 driver John Erasmus Mari Geronimo
2025-01-10 8:02 ` [PATCH 1/2] dt-bindings: power: supply: add adi,lt8491.yaml John Erasmus Mari Geronimo
@ 2025-01-10 8:02 ` John Erasmus Mari Geronimo
2025-01-14 16:45 ` kernel test robot
2026-09-03 10:46 ` [PATCH v2 0/2] Add LT8491 driver John Erasmus Mari Geronimo
2 siblings, 1 reply; 11+ messages in thread
From: John Erasmus Mari Geronimo @ 2025-01-10 8:02 UTC (permalink / raw)
To: linux-pm, devicetree, linux-kernel
Cc: Sebastian Reichel, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Adds initial support for the LT8491 High Voltage Buck-Boost Battery
Charge Controller with I2C
The driver provides functionality to monitor key parameters including:
Voltage, Current, Power, Temperature, Status
Signed-off-by: John Erasmus Mari Geronimo <johnerasmusmari.geronimo@analog.com>
---
MAINTAINERS | 1 +
drivers/power/supply/Kconfig | 9 +
drivers/power/supply/Makefile | 1 +
drivers/power/supply/lt8491_charger.c | 410 ++++++++++++++++++++++++++
4 files changed, 421 insertions(+)
create mode 100644 drivers/power/supply/lt8491_charger.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 96f510358..0d98734de 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13564,6 +13564,7 @@ M: John Erasmus Mari Geronimo <johnerasmusmari.geronimo@analog.com>
L: linux-pm@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/power/supply/adi,lt8491.yaml
+F: drivers/power/supply/lt8491_charger.c
LTC1660 DAC DRIVER
M: Marcus Folkesson <marcus.folkesson@gmail.com>
diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index 7b18358f1..2a85ae1f7 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -545,6 +545,15 @@ config CHARGER_LT3651
Say Y to include support for the Analog Devices (Linear Technology)
LT3651 battery charger which reports its status via GPIO lines.
+config CHARGER_LT8491
+ tristate "Analog Devices LT8491 charger"
+ depends on I2C
+ help
+ Say Y to include support for the Analog Devices (Linear Technology)
+ LT8491 battery charge controller connected to I2C. The LT8491 is a
+ high voltage buck-boost switching regulator battery charger
+ controller.
+
config CHARGER_LTC4162L
tristate "LTC4162-L charger"
depends on I2C
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
index b55cc48a4..68699430b 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -75,6 +75,7 @@ obj-$(CONFIG_CHARGER_LP8788) += lp8788-charger.o
obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o
obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o
obj-$(CONFIG_CHARGER_LT3651) += lt3651-charger.o
+obj-$(CONFIG_CHARGER_LT8491) += lt8491_charger.o
obj-$(CONFIG_CHARGER_LTC4162L) += ltc4162-l-charger.o
obj-$(CONFIG_CHARGER_MAX14577) += max14577_charger.o
obj-$(CONFIG_CHARGER_DETECTOR_MAX14656) += max14656_charger_detector.o
diff --git a/drivers/power/supply/lt8491_charger.c b/drivers/power/supply/lt8491_charger.c
new file mode 100644
index 000000000..dcef6691d
--- /dev/null
+++ b/drivers/power/supply/lt8491_charger.c
@@ -0,0 +1,410 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Analog Devices LT8491 Battery Charger
+ *
+ * Copyright 2024 Analog Devices Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/power_supply.h>
+
+#define LT8491_TELE_TBAT_REG 0x0
+#define LT8491_TELE_POUT_REG 0x2
+#define LT8491_TELE_PIN_REG 0x4
+#define LT8491_TELE_EFF_REG 0x6
+#define LT8491_TELE_IOUT_REG 0x8
+#define LT8491_TELE_IIN_REG 0xA
+#define LT8491_TELE_VBAT_REG 0xC
+#define LT8491_TELE_VIN_REG 0xE
+#define LT8491_TELE_VINR_REG 0x10
+#define LT8491_STAT_CHARGER_REG 0x12
+#define LT8491_STAT_CHRG_FAULTS_REG 0x19
+#define LT8491_CTRL_UPDATE_TELEM_REG 0x26
+
+#define LT8491_CFG_RSENSE1_REG 0x28
+#define LT8491_CFG_RIMON_OUT_REG 0x2A
+#define LT8491_CFG_RSENSE2_REG 0x2C
+#define LT8491_CFG_RDACO_REG 0x2E
+#define LT8491_CFG_RFBOUT1_REG 0x30
+#define LT8491_CFG_RFBOUT2_REG 0x32
+#define LT8491_CFG_RDACI_REG 0x34
+#define LT8491_CFG_RFBIN2_REG 0x36
+#define LT8491_CFG_RFBIN1_REG 0x38
+#define LT8491_CFG_TBAT_MIN_REG 0x40
+#define LT8491_CFG_TBAT_MAX_REG 0x41
+#define LT8491_MFR_DATA1_LSB_REG 0x5C
+
+#define LT8491_TELEM_ACTIVE_MASK BIT(6)
+#define LT8491_CHARGING_MASK BIT(2)
+#define LT8491_BAT_DISCON_FLT_MASK BIT(3)
+
+#define LT8491_UPDATE_TELEM_CMD 0xAA
+
+#define LT8491_MFR_DATA_LEN 0x3
+
+struct lt8491_info {
+ struct i2c_client *client;
+ struct power_supply *psp;
+ char serial_number[16];
+ /* protect against device accesses */
+ struct mutex lock;
+};
+
+static int lt8491_read_serial_number(struct lt8491_info *info)
+{
+ int i, ret;
+ u32 serial_number[LT8491_MFR_DATA_LEN];
+
+ for (i = 0; i < LT8491_MFR_DATA_LEN; i++) {
+ serial_number[i] = i2c_smbus_read_word_data(info->client,
+ LT8491_MFR_DATA1_LSB_REG + i * 2);
+ if (serial_number[i] < 0)
+ return serial_number[i];
+ }
+
+ ret = sprintf(info->serial_number, "%04x%04x%04x", serial_number[0],
+ serial_number[1], serial_number[2]);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int lt8491_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct lt8491_info *info = power_supply_get_drvdata(psy);
+ s16 ret;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ ret = i2c_smbus_read_byte_data(info->client,
+ LT8491_STAT_CHARGER_REG);
+ if (ret < 0)
+ return ret;
+
+ val->intval = FIELD_GET(LT8491_CHARGING_MASK, ret) ?
+ POWER_SUPPLY_STATUS_CHARGING :
+ POWER_SUPPLY_STATUS_NOT_CHARGING;
+
+ return 0;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ scoped_guard(mutex, &info->lock) {
+ ret = i2c_smbus_read_byte_data(info->client,
+ LT8491_STAT_CHARGER_REG);
+ if (ret < 0)
+ return ret;
+
+ if (!FIELD_GET(LT8491_TELEM_ACTIVE_MASK, ret)) {
+ ret = i2c_smbus_write_byte_data(info->client,
+ LT8491_CTRL_UPDATE_TELEM_REG,
+ LT8491_UPDATE_TELEM_CMD);
+ if (ret)
+ return ret;
+ }
+
+ ret = i2c_smbus_read_word_data(info->client,
+ LT8491_TELE_VBAT_REG);
+ if (ret < 0)
+ return ret;
+ }
+
+ val->intval = ret * 10000;
+
+ return 0;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ scoped_guard(mutex, &info->lock) {
+ ret = i2c_smbus_read_byte_data(info->client,
+ LT8491_STAT_CHARGER_REG);
+ if (ret < 0)
+ return ret;
+
+ if (!FIELD_GET(LT8491_TELEM_ACTIVE_MASK, ret)) {
+ ret = i2c_smbus_write_byte_data(info->client,
+ LT8491_CTRL_UPDATE_TELEM_REG,
+ LT8491_UPDATE_TELEM_CMD);
+ if (ret)
+ return ret;
+ }
+
+ ret = i2c_smbus_read_word_data(info->client,
+ LT8491_TELE_IOUT_REG);
+ if (ret < 0)
+ return ret;
+ }
+
+ val->intval = ret;
+
+ return 0;
+ case POWER_SUPPLY_PROP_POWER_NOW:
+ scoped_guard(mutex, &info->lock) {
+ ret = i2c_smbus_read_byte_data(info->client,
+ LT8491_STAT_CHARGER_REG);
+ if (ret < 0)
+ return ret;
+
+ if (!FIELD_GET(LT8491_TELEM_ACTIVE_MASK, ret)) {
+ ret = i2c_smbus_write_byte_data(info->client,
+ LT8491_CTRL_UPDATE_TELEM_REG,
+ LT8491_UPDATE_TELEM_CMD);
+ if (ret)
+ return ret;
+ }
+
+ ret = i2c_smbus_read_word_data(info->client,
+ LT8491_TELE_POUT_REG);
+ if (ret < 0)
+ return ret;
+ }
+
+ val->intval = ret * 10000;
+
+ return 0;
+ case POWER_SUPPLY_PROP_TEMP:
+ scoped_guard(mutex, &info->lock) {
+ ret = i2c_smbus_read_byte_data(info->client,
+ LT8491_STAT_CHARGER_REG);
+ if (ret < 0)
+ return ret;
+
+ if (!FIELD_GET(LT8491_TELEM_ACTIVE_MASK, ret)) {
+ ret = i2c_smbus_write_byte_data(info->client,
+ LT8491_CTRL_UPDATE_TELEM_REG,
+ LT8491_UPDATE_TELEM_CMD);
+ if (ret)
+ return ret;
+ }
+
+ ret = i2c_smbus_read_word_data(info->client,
+ LT8491_TELE_TBAT_REG);
+ if (ret < 0)
+ return ret;
+ }
+
+ val->intval = sign_extend32(ret, 15);
+
+ return 0;
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
+ ret = i2c_smbus_read_byte_data(info->client,
+ LT8491_CFG_TBAT_MIN_REG);
+ if (ret < 0)
+ return ret;
+
+ val->intval = sign_extend32(ret, 7) * 10;
+
+ return 0;
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
+ ret = i2c_smbus_read_byte_data(info->client,
+ LT8491_CFG_TBAT_MAX_REG);
+ if (ret < 0)
+ return ret;
+
+ val->intval = sign_extend32(ret, 7) * 10;
+
+ return 0;
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = "lt8491";
+
+ return 0;
+ case POWER_SUPPLY_PROP_MANUFACTURER:
+ val->strval = "Analog Devices";
+
+ return 0;
+ case POWER_SUPPLY_PROP_SERIAL_NUMBER:
+ val->strval = info->serial_number;
+
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int lt8491_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct lt8491_info *info = power_supply_get_drvdata(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
+ return i2c_smbus_write_byte_data(info->client,
+ LT8491_CFG_TBAT_MIN_REG,
+ val->intval / 10);
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
+ return i2c_smbus_write_byte_data(info->client,
+ LT8491_CFG_TBAT_MAX_REG,
+ val->intval / 10);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int lt8491_property_is_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static enum power_supply_property lt8491_properties[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_POWER_NOW,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_TEMP_ALERT_MIN,
+ POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+ POWER_SUPPLY_PROP_SERIAL_NUMBER,
+};
+
+static const struct power_supply_desc lt8491_desc = {
+ .name = "lt8491",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .properties = lt8491_properties,
+ .num_properties = ARRAY_SIZE(lt8491_properties),
+ .get_property = lt8491_get_property,
+ .set_property = lt8491_set_property,
+ .property_is_writeable = lt8491_property_is_writeable,
+};
+
+static int lt8491_configure_resistor(struct lt8491_info *info,
+ const char *propname, int divider,
+ unsigned int reg)
+{
+ struct device *dev = &info->client->dev;
+ int ret;
+ u32 val;
+
+ ret = device_property_read_u32(dev, propname, &val);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Missing %s property.\n",
+ propname);
+
+ return i2c_smbus_write_word_data(info->client, reg, val / divider);
+}
+
+static int lt8491_configure_telemetry(struct lt8491_info *info)
+{
+ int ret;
+
+ ret = lt8491_configure_resistor(info, "adi,rsense1-micro-ohms", 10,
+ LT8491_CFG_RSENSE1_REG);
+ if (ret)
+ return ret;
+
+ ret = lt8491_configure_resistor(info, "adi,rimon-out-ohms", 10,
+ LT8491_CFG_RIMON_OUT_REG);
+ if (ret)
+ return ret;
+
+ ret = lt8491_configure_resistor(info, "adi,rsense2-micro-ohms", 10,
+ LT8491_CFG_RSENSE2_REG);
+ if (ret)
+ return ret;
+
+ ret = lt8491_configure_resistor(info, "adi,rdaco-ohms", 10,
+ LT8491_CFG_RDACO_REG);
+ if (ret)
+ return ret;
+
+ ret = lt8491_configure_resistor(info, "adi,rfbout1-ohms", 100,
+ LT8491_CFG_RFBOUT1_REG);
+ if (ret)
+ return ret;
+
+ ret = lt8491_configure_resistor(info, "adi,rfbout2-ohms", 10,
+ LT8491_CFG_RFBOUT2_REG);
+ if (ret)
+ return ret;
+
+ ret = lt8491_configure_resistor(info, "adi,rdaci-ohms", 10,
+ LT8491_CFG_RDACI_REG);
+ if (ret)
+ return ret;
+
+ ret = lt8491_configure_resistor(info, "adi,rfbin2-ohms", 10,
+ LT8491_CFG_RFBIN2_REG);
+ if (ret)
+ return ret;
+
+ return lt8491_configure_resistor(info, "adi,rfbin1-ohms", 100,
+ LT8491_CFG_RFBIN1_REG);
+}
+
+static int lt8491_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct lt8491_info *info;
+ struct power_supply_config psy_cfg = {};
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_READ_WORD_DATA))
+ return -EOPNOTSUPP;
+
+ info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->client = client;
+ psy_cfg.drv_data = info;
+
+ devm_mutex_init(dev, &info->lock);
+
+ ret = lt8491_read_serial_number(info);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Can't read serial. Hardware error.\n");
+
+ ret = lt8491_configure_telemetry(info);
+ if (ret)
+ return ret;
+
+ info->psp = power_supply_register(dev, <8491_desc, &psy_cfg);
+ if (IS_ERR(info->psp))
+ return dev_err_probe(dev, PTR_ERR(info->psp),
+ "Failed to register power supply.\n");
+
+ return 0;
+}
+
+static const struct i2c_device_id lt8491_id[] = {
+ { "lt8491", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lt8491_id);
+
+static const struct of_device_id lt8491_of_match[] = {
+ { .compatible = "adi,lt8491" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, lt8491_of_match);
+
+static struct i2c_driver lt8491_driver = {
+ .driver = {
+ .name = "lt8491",
+ .of_match_table = lt8491_of_match,
+ },
+ .probe = lt8491_probe,
+ .id_table = lt8491_id,
+};
+module_i2c_driver(lt8491_driver);
+
+MODULE_AUTHOR("John Erasmus Mari Geronimo <johnerasmusmari.geronimo@analog.com");
+MODULE_DESCRIPTION("LT8491 battery charger");
+MODULE_LICENSE("GPL");
--
2.34.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 1/2] dt-bindings: power: supply: add adi,lt8491.yaml
2025-01-10 8:02 ` [PATCH 1/2] dt-bindings: power: supply: add adi,lt8491.yaml John Erasmus Mari Geronimo
@ 2025-01-10 9:26 ` Krzysztof Kozlowski
0 siblings, 0 replies; 11+ messages in thread
From: Krzysztof Kozlowski @ 2025-01-10 9:26 UTC (permalink / raw)
To: John Erasmus Mari Geronimo, linux-pm, devicetree, linux-kernel
Cc: Sebastian Reichel, Rob Herring, Krzysztof Kozlowski, Conor Dooley
On 10/01/2025 09:02, John Erasmus Mari Geronimo wrote:
> +$id: http://devicetree.org/schemas/power/supply/adi,lt8491.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Linear Technology (Analog Devices) LT8491 Battery Charger
> +
> +maintainers:
> + - John Erasmus Mari Geronimo <johnerasmusmari.geronimo@analog.com>
> +
> +description: |
> + The LT8491 is a buck-boost switching regulator battery charger that implements
> + a constant-current constant-voltage (CCCV) charging profile used for most
> + battery types, including sealed lead-acid (SLA), flooded, gel and lithium-ion.
> +
> + Specifications about the charger can be found at:
> + https://www.analog.com/en/products/lt8491.html
> +
Mostly looks good, but I don't see $ref to power-supply, which brings
you the battery property.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/2] power: supply: add LT8491 battery charger driver
2025-01-10 8:02 ` [PATCH 2/2] power: supply: add LT8491 battery charger driver John Erasmus Mari Geronimo
@ 2025-01-14 16:45 ` kernel test robot
0 siblings, 0 replies; 11+ messages in thread
From: kernel test robot @ 2025-01-14 16:45 UTC (permalink / raw)
To: John Erasmus Mari Geronimo, linux-pm, devicetree, linux-kernel
Cc: oe-kbuild-all, Sebastian Reichel, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Hi John,
kernel test robot noticed the following build warnings:
[auto build test WARNING on a3a8799165ff83bb764fd800c6559c3cba0ddac3]
url: https://github.com/intel-lab-lkp/linux/commits/John-Erasmus-Mari-Geronimo/dt-bindings-power-supply-add-adi-lt8491-yaml/20250110-160441
base: a3a8799165ff83bb764fd800c6559c3cba0ddac3
patch link: https://lore.kernel.org/r/20250110080235.54808-3-johnerasmusmari.geronimo%40analog.com
patch subject: [PATCH 2/2] power: supply: add LT8491 battery charger driver
config: sparc64-randconfig-r071-20250114 (https://download.01.org/0day-ci/archive/20250115/202501150030.cWwtcIoo-lkp@intel.com/config)
compiler: sparc64-linux-gcc (GCC) 14.2.0
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202501150030.cWwtcIoo-lkp@intel.com/
smatch warnings:
drivers/power/supply/lt8491_charger.c:66 lt8491_read_serial_number() warn: unsigned 'serial_number[i]' is never less than zero.
vim +66 drivers/power/supply/lt8491_charger.c
57
58 static int lt8491_read_serial_number(struct lt8491_info *info)
59 {
60 int i, ret;
61 u32 serial_number[LT8491_MFR_DATA_LEN];
62
63 for (i = 0; i < LT8491_MFR_DATA_LEN; i++) {
64 serial_number[i] = i2c_smbus_read_word_data(info->client,
65 LT8491_MFR_DATA1_LSB_REG + i * 2);
> 66 if (serial_number[i] < 0)
67 return serial_number[i];
68 }
69
70 ret = sprintf(info->serial_number, "%04x%04x%04x", serial_number[0],
71 serial_number[1], serial_number[2]);
72 if (ret < 0)
73 return ret;
74
75 return 0;
76 }
77
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 0/2] Add LT8491 driver
2025-01-10 8:02 [PATCH 0/2] Add LT8491 driver John Erasmus Mari Geronimo
2025-01-10 8:02 ` [PATCH 1/2] dt-bindings: power: supply: add adi,lt8491.yaml John Erasmus Mari Geronimo
2025-01-10 8:02 ` [PATCH 2/2] power: supply: add LT8491 battery charger driver John Erasmus Mari Geronimo
@ 2026-09-03 10:46 ` John Erasmus Mari Geronimo
2026-09-03 10:46 ` [PATCH v2 1/2] dt-bindings: power: supply: add adi,lt8491.yaml John Erasmus Mari Geronimo
2026-09-03 10:46 ` [PATCH v2 2/2] power: supply: add LT8491 battery charger driver John Erasmus Mari Geronimo
2 siblings, 2 replies; 11+ messages in thread
From: John Erasmus Mari Geronimo @ 2026-09-03 10:46 UTC (permalink / raw)
To: linux-pm, devicetree, linux-kernel; +Cc: krzk+dt, conor+dt, robh, sre
This patch series adds a driver and DeviceTree binding for the LT8491
battery charge controller.
The LT8491 is a buck-boost switching regulator battery charger that
implements a constant-current constant-voltage (CCCV) charging profile.
Changes since v1:
- Add a reference to power-supply.yaml so the binding inherits the
standard battery property.
- Fix serial-number error handling by using a signed temporary array.
- Check the return value of devm_mutex_init().
John Erasmus Mari Geronimo (2):
dt-bindings: power: supply: add adi,lt8491.yaml
power: supply: add LT8491 battery charger driver
.../bindings/power/supply/adi,lt8491.yaml | 92 ++++
MAINTAINERS | 7 +
drivers/power/supply/Kconfig | 9 +
drivers/power/supply/Makefile | 1 +
drivers/power/supply/lt8491_charger.c | 412 ++++++++++++++++++
5 files changed, 521 insertions(+)
create mode 100644 Documentation/devicetree/bindings/power/supply/adi,lt8491.yaml
create mode 100644 drivers/power/supply/lt8491_charger.c
base-commit: a888754e51e915731c8974c4d6d62709facb35d3
--
2.34.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 1/2] dt-bindings: power: supply: add adi,lt8491.yaml
2026-09-03 10:46 ` [PATCH v2 0/2] Add LT8491 driver John Erasmus Mari Geronimo
@ 2026-09-03 10:46 ` John Erasmus Mari Geronimo
2026-09-03 10:52 ` sashiko-bot
2026-09-03 17:21 ` Conor Dooley
2026-09-03 10:46 ` [PATCH v2 2/2] power: supply: add LT8491 battery charger driver John Erasmus Mari Geronimo
1 sibling, 2 replies; 11+ messages in thread
From: John Erasmus Mari Geronimo @ 2026-09-03 10:46 UTC (permalink / raw)
To: linux-pm, devicetree, linux-kernel; +Cc: krzk+dt, conor+dt, robh, sre
Add documentation for devicetree bindings for LT8491
Signed-off-by: John Erasmus Mari Geronimo <johnerasmusmari.geronimo@analog.com>
---
.../bindings/power/supply/adi,lt8491.yaml | 92 +++++++++++++++++++
MAINTAINERS | 6 ++
2 files changed, 98 insertions(+)
create mode 100644 Documentation/devicetree/bindings/power/supply/adi,lt8491.yaml
diff --git a/Documentation/devicetree/bindings/power/supply/adi,lt8491.yaml b/Documentation/devicetree/bindings/power/supply/adi,lt8491.yaml
new file mode 100644
index 0000000000000..bb364b48fee58
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/adi,lt8491.yaml
@@ -0,0 +1,92 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/power/supply/adi,lt8491.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Linear Technology (Analog Devices) LT8491 Battery Charger
+
+maintainers:
+ - John Erasmus Mari Geronimo <johnerasmusmari.geronimo@analog.com>
+
+description: |
+ The LT8491 is a buck-boost switching regulator battery charger that implements
+ a constant-current constant-voltage (CCCV) charging profile used for most
+ battery types, including sealed lead-acid (SLA), flooded, gel and lithium-ion.
+
+ Specifications about the charger can be found at:
+ https://www.analog.com/en/products/lt8491.html
+
+allOf:
+ - $ref: power-supply.yaml#
+
+properties:
+ compatible:
+ enum:
+ - adi,lt8491
+
+ reg:
+ maxItems: 1
+
+ adi,rsense1-micro-ohms:
+ description: Resistance value of RSENSE1 in micro ohms.
+
+ adi,rimon-out-ohms:
+ description: Resistance value of RIMON_OUT in ohms.
+
+ adi,rsense2-micro-ohms:
+ description: Resistance value of RSENSE2 in micro ohms.
+
+ adi,rdaco-ohms:
+ description: Resistance value of RDACO1 + RDACO2 in ohms.
+
+ adi,rfbout1-ohms:
+ description: Resistance value of RFBOUT1 in ohms.
+
+ adi,rfbout2-ohms:
+ description: Resistance value of RFBOUT2 in ohms.
+
+ adi,rdaci-ohms:
+ description: Resistance value of RDACI1 + RDACI2 in ohms.
+
+ adi,rfbin2-ohms:
+ description: Resistance value of RFBIN2 in ohms.
+
+ adi,rfbin1-ohms:
+ description: Resistance value of RFBIN1 in ohms.
+
+required:
+ - compatible
+ - reg
+ - adi,rsense1-micro-ohms
+ - adi,rimon-out-ohms
+ - adi,rsense2-micro-ohms
+ - adi,rdaco-ohms
+ - adi,rfbout1-ohms
+ - adi,rfbout2-ohms
+ - adi,rdaci-ohms
+ - adi,rfbin2-ohms
+ - adi,rfbin1-ohms
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ battery-charger@10 {
+ compatible = "adi,lt8491";
+ reg = <0x10>;
+ adi,rsense1-micro-ohms = <5000>;
+ adi,rimon-out-ohms = <124000>;
+ adi,rsense2-micro-ohms = <3000>;
+ adi,rdaco-ohms = <64900>;
+ adi,rfbout1-ohms = <113000>;
+ adi,rfbout2-ohms = <10000>;
+ adi,rdaci-ohms = <7000>;
+ adi,rfbin2-ohms = <3480>;
+ adi,rfbin1-ohms = <102000>;
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 2fb1c75afd163..d79187312f789 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15170,6 +15170,12 @@ F: Documentation/devicetree/bindings/hwmon/pmbus/adi,lt3074.yaml
F: Documentation/hwmon/lt3074.rst
F: drivers/hwmon/pmbus/lt3074.c
+LT8491 BATTERY CHARGER DRIVER
+M: John Erasmus Mari Geronimo <johnerasmusmari.geronimo@analog.com>
+L: linux-pm@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/power/supply/adi,lt8491.yaml
+
LTC1660 DAC DRIVER
M: Marcus Folkesson <marcus.folkesson@gmail.com>
L: linux-iio@vger.kernel.org
--
2.34.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v2 2/2] power: supply: add LT8491 battery charger driver
2026-09-03 10:46 ` [PATCH v2 0/2] Add LT8491 driver John Erasmus Mari Geronimo
2026-09-03 10:46 ` [PATCH v2 1/2] dt-bindings: power: supply: add adi,lt8491.yaml John Erasmus Mari Geronimo
@ 2026-09-03 10:46 ` John Erasmus Mari Geronimo
2026-09-03 10:56 ` sashiko-bot
1 sibling, 1 reply; 11+ messages in thread
From: John Erasmus Mari Geronimo @ 2026-09-03 10:46 UTC (permalink / raw)
To: linux-pm, devicetree, linux-kernel; +Cc: krzk+dt, conor+dt, robh, sre
Adds initial support for the LT8491 High Voltage Buck-Boost Battery
Charge Controller with I2C
The driver provides functionality to monitor key parameters including:
Voltage, Current, Power, Temperature, Status
Signed-off-by: John Erasmus Mari Geronimo <johnerasmusmari.geronimo@analog.com>
---
MAINTAINERS | 1 +
drivers/power/supply/Kconfig | 9 +
drivers/power/supply/Makefile | 1 +
drivers/power/supply/lt8491_charger.c | 412 ++++++++++++++++++++++++++
4 files changed, 423 insertions(+)
create mode 100644 drivers/power/supply/lt8491_charger.c
diff --git a/MAINTAINERS b/MAINTAINERS
index d79187312f789..317ca2e8ca9bd 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15175,6 +15175,7 @@ M: John Erasmus Mari Geronimo <johnerasmusmari.geronimo@analog.com>
L: linux-pm@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/power/supply/adi,lt8491.yaml
+F: drivers/power/supply/lt8491_charger.c
LTC1660 DAC DRIVER
M: Marcus Folkesson <marcus.folkesson@gmail.com>
diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index f0ede1cecb6a8..146ea3743eb2d 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -598,6 +598,15 @@ config CHARGER_LT3651
Say Y to include support for the Analog Devices (Linear Technology)
LT3651 battery charger which reports its status via GPIO lines.
+config CHARGER_LT8491
+ tristate "Analog Devices LT8491 charger"
+ depends on I2C
+ help
+ Say Y to include support for the Analog Devices (Linear Technology)
+ LT8491 battery charge controller connected to I2C. The LT8491 is a
+ high voltage buck-boost switching regulator battery charger
+ controller.
+
config CHARGER_LTC4162L
tristate "LTC4162-L charger"
depends on I2C
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
index 31fe4a145929a..d3d2721333204 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -81,6 +81,7 @@ obj-$(CONFIG_CHARGER_LP8788) += lp8788-charger.o
obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o
obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o
obj-$(CONFIG_CHARGER_LT3651) += lt3651-charger.o
+obj-$(CONFIG_CHARGER_LT8491) += lt8491_charger.o
obj-$(CONFIG_CHARGER_LTC4162L) += ltc4162-l-charger.o
obj-$(CONFIG_CHARGER_MAX14577) += max14577_charger.o
obj-$(CONFIG_CHARGER_DETECTOR_MAX14656) += max14656_charger_detector.o
diff --git a/drivers/power/supply/lt8491_charger.c b/drivers/power/supply/lt8491_charger.c
new file mode 100644
index 0000000000000..225d5dd4dd721
--- /dev/null
+++ b/drivers/power/supply/lt8491_charger.c
@@ -0,0 +1,412 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Analog Devices LT8491 Battery Charger
+ *
+ * Copyright 2024 Analog Devices Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/power_supply.h>
+
+#define LT8491_TELE_TBAT_REG 0x0
+#define LT8491_TELE_POUT_REG 0x2
+#define LT8491_TELE_PIN_REG 0x4
+#define LT8491_TELE_EFF_REG 0x6
+#define LT8491_TELE_IOUT_REG 0x8
+#define LT8491_TELE_IIN_REG 0xA
+#define LT8491_TELE_VBAT_REG 0xC
+#define LT8491_TELE_VIN_REG 0xE
+#define LT8491_TELE_VINR_REG 0x10
+#define LT8491_STAT_CHARGER_REG 0x12
+#define LT8491_STAT_CHRG_FAULTS_REG 0x19
+#define LT8491_CTRL_UPDATE_TELEM_REG 0x26
+
+#define LT8491_CFG_RSENSE1_REG 0x28
+#define LT8491_CFG_RIMON_OUT_REG 0x2A
+#define LT8491_CFG_RSENSE2_REG 0x2C
+#define LT8491_CFG_RDACO_REG 0x2E
+#define LT8491_CFG_RFBOUT1_REG 0x30
+#define LT8491_CFG_RFBOUT2_REG 0x32
+#define LT8491_CFG_RDACI_REG 0x34
+#define LT8491_CFG_RFBIN2_REG 0x36
+#define LT8491_CFG_RFBIN1_REG 0x38
+#define LT8491_CFG_TBAT_MIN_REG 0x40
+#define LT8491_CFG_TBAT_MAX_REG 0x41
+#define LT8491_MFR_DATA1_LSB_REG 0x5C
+
+#define LT8491_TELEM_ACTIVE_MASK BIT(6)
+#define LT8491_CHARGING_MASK BIT(2)
+#define LT8491_BAT_DISCON_FLT_MASK BIT(3)
+
+#define LT8491_UPDATE_TELEM_CMD 0xAA
+
+#define LT8491_MFR_DATA_LEN 0x3
+
+struct lt8491_info {
+ struct i2c_client *client;
+ struct power_supply *psp;
+ char serial_number[16];
+ /* protect against device accesses */
+ struct mutex lock;
+};
+
+static int lt8491_read_serial_number(struct lt8491_info *info)
+{
+ int i, ret;
+ int serial_number[LT8491_MFR_DATA_LEN];
+
+ for (i = 0; i < LT8491_MFR_DATA_LEN; i++) {
+ serial_number[i] = i2c_smbus_read_word_data(info->client,
+ LT8491_MFR_DATA1_LSB_REG + i * 2);
+ if (serial_number[i] < 0)
+ return serial_number[i];
+ }
+
+ ret = sprintf(info->serial_number, "%04x%04x%04x", serial_number[0],
+ serial_number[1], serial_number[2]);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int lt8491_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct lt8491_info *info = power_supply_get_drvdata(psy);
+ s16 ret;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ ret = i2c_smbus_read_byte_data(info->client,
+ LT8491_STAT_CHARGER_REG);
+ if (ret < 0)
+ return ret;
+
+ val->intval = FIELD_GET(LT8491_CHARGING_MASK, ret) ?
+ POWER_SUPPLY_STATUS_CHARGING :
+ POWER_SUPPLY_STATUS_NOT_CHARGING;
+
+ return 0;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ scoped_guard(mutex, &info->lock) {
+ ret = i2c_smbus_read_byte_data(info->client,
+ LT8491_STAT_CHARGER_REG);
+ if (ret < 0)
+ return ret;
+
+ if (!FIELD_GET(LT8491_TELEM_ACTIVE_MASK, ret)) {
+ ret = i2c_smbus_write_byte_data(info->client,
+ LT8491_CTRL_UPDATE_TELEM_REG,
+ LT8491_UPDATE_TELEM_CMD);
+ if (ret)
+ return ret;
+ }
+
+ ret = i2c_smbus_read_word_data(info->client,
+ LT8491_TELE_VBAT_REG);
+ if (ret < 0)
+ return ret;
+ }
+
+ val->intval = ret * 10000;
+
+ return 0;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ scoped_guard(mutex, &info->lock) {
+ ret = i2c_smbus_read_byte_data(info->client,
+ LT8491_STAT_CHARGER_REG);
+ if (ret < 0)
+ return ret;
+
+ if (!FIELD_GET(LT8491_TELEM_ACTIVE_MASK, ret)) {
+ ret = i2c_smbus_write_byte_data(info->client,
+ LT8491_CTRL_UPDATE_TELEM_REG,
+ LT8491_UPDATE_TELEM_CMD);
+ if (ret)
+ return ret;
+ }
+
+ ret = i2c_smbus_read_word_data(info->client,
+ LT8491_TELE_IOUT_REG);
+ if (ret < 0)
+ return ret;
+ }
+
+ val->intval = ret;
+
+ return 0;
+ case POWER_SUPPLY_PROP_POWER_NOW:
+ scoped_guard(mutex, &info->lock) {
+ ret = i2c_smbus_read_byte_data(info->client,
+ LT8491_STAT_CHARGER_REG);
+ if (ret < 0)
+ return ret;
+
+ if (!FIELD_GET(LT8491_TELEM_ACTIVE_MASK, ret)) {
+ ret = i2c_smbus_write_byte_data(info->client,
+ LT8491_CTRL_UPDATE_TELEM_REG,
+ LT8491_UPDATE_TELEM_CMD);
+ if (ret)
+ return ret;
+ }
+
+ ret = i2c_smbus_read_word_data(info->client,
+ LT8491_TELE_POUT_REG);
+ if (ret < 0)
+ return ret;
+ }
+
+ val->intval = ret * 10000;
+
+ return 0;
+ case POWER_SUPPLY_PROP_TEMP:
+ scoped_guard(mutex, &info->lock) {
+ ret = i2c_smbus_read_byte_data(info->client,
+ LT8491_STAT_CHARGER_REG);
+ if (ret < 0)
+ return ret;
+
+ if (!FIELD_GET(LT8491_TELEM_ACTIVE_MASK, ret)) {
+ ret = i2c_smbus_write_byte_data(info->client,
+ LT8491_CTRL_UPDATE_TELEM_REG,
+ LT8491_UPDATE_TELEM_CMD);
+ if (ret)
+ return ret;
+ }
+
+ ret = i2c_smbus_read_word_data(info->client,
+ LT8491_TELE_TBAT_REG);
+ if (ret < 0)
+ return ret;
+ }
+
+ val->intval = sign_extend32(ret, 15);
+
+ return 0;
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
+ ret = i2c_smbus_read_byte_data(info->client,
+ LT8491_CFG_TBAT_MIN_REG);
+ if (ret < 0)
+ return ret;
+
+ val->intval = sign_extend32(ret, 7) * 10;
+
+ return 0;
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
+ ret = i2c_smbus_read_byte_data(info->client,
+ LT8491_CFG_TBAT_MAX_REG);
+ if (ret < 0)
+ return ret;
+
+ val->intval = sign_extend32(ret, 7) * 10;
+
+ return 0;
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = "lt8491";
+
+ return 0;
+ case POWER_SUPPLY_PROP_MANUFACTURER:
+ val->strval = "Analog Devices";
+
+ return 0;
+ case POWER_SUPPLY_PROP_SERIAL_NUMBER:
+ val->strval = info->serial_number;
+
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int lt8491_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct lt8491_info *info = power_supply_get_drvdata(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
+ return i2c_smbus_write_byte_data(info->client,
+ LT8491_CFG_TBAT_MIN_REG,
+ val->intval / 10);
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
+ return i2c_smbus_write_byte_data(info->client,
+ LT8491_CFG_TBAT_MAX_REG,
+ val->intval / 10);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int lt8491_property_is_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static enum power_supply_property lt8491_properties[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_POWER_NOW,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_TEMP_ALERT_MIN,
+ POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+ POWER_SUPPLY_PROP_SERIAL_NUMBER,
+};
+
+static const struct power_supply_desc lt8491_desc = {
+ .name = "lt8491",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .properties = lt8491_properties,
+ .num_properties = ARRAY_SIZE(lt8491_properties),
+ .get_property = lt8491_get_property,
+ .set_property = lt8491_set_property,
+ .property_is_writeable = lt8491_property_is_writeable,
+};
+
+static int lt8491_configure_resistor(struct lt8491_info *info,
+ const char *propname, int divider,
+ unsigned int reg)
+{
+ struct device *dev = &info->client->dev;
+ int ret;
+ u32 val;
+
+ ret = device_property_read_u32(dev, propname, &val);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Missing %s property.\n",
+ propname);
+
+ return i2c_smbus_write_word_data(info->client, reg, val / divider);
+}
+
+static int lt8491_configure_telemetry(struct lt8491_info *info)
+{
+ int ret;
+
+ ret = lt8491_configure_resistor(info, "adi,rsense1-micro-ohms", 10,
+ LT8491_CFG_RSENSE1_REG);
+ if (ret)
+ return ret;
+
+ ret = lt8491_configure_resistor(info, "adi,rimon-out-ohms", 10,
+ LT8491_CFG_RIMON_OUT_REG);
+ if (ret)
+ return ret;
+
+ ret = lt8491_configure_resistor(info, "adi,rsense2-micro-ohms", 10,
+ LT8491_CFG_RSENSE2_REG);
+ if (ret)
+ return ret;
+
+ ret = lt8491_configure_resistor(info, "adi,rdaco-ohms", 10,
+ LT8491_CFG_RDACO_REG);
+ if (ret)
+ return ret;
+
+ ret = lt8491_configure_resistor(info, "adi,rfbout1-ohms", 100,
+ LT8491_CFG_RFBOUT1_REG);
+ if (ret)
+ return ret;
+
+ ret = lt8491_configure_resistor(info, "adi,rfbout2-ohms", 10,
+ LT8491_CFG_RFBOUT2_REG);
+ if (ret)
+ return ret;
+
+ ret = lt8491_configure_resistor(info, "adi,rdaci-ohms", 10,
+ LT8491_CFG_RDACI_REG);
+ if (ret)
+ return ret;
+
+ ret = lt8491_configure_resistor(info, "adi,rfbin2-ohms", 10,
+ LT8491_CFG_RFBIN2_REG);
+ if (ret)
+ return ret;
+
+ return lt8491_configure_resistor(info, "adi,rfbin1-ohms", 100,
+ LT8491_CFG_RFBIN1_REG);
+}
+
+static int lt8491_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct lt8491_info *info;
+ struct power_supply_config psy_cfg = {};
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_READ_WORD_DATA))
+ return -EOPNOTSUPP;
+
+ info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->client = client;
+ psy_cfg.drv_data = info;
+
+ ret = devm_mutex_init(dev, &info->lock);
+ if (ret)
+ return ret;
+
+ ret = lt8491_read_serial_number(info);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Can't read serial. Hardware error.\n");
+
+ ret = lt8491_configure_telemetry(info);
+ if (ret)
+ return ret;
+
+ info->psp = power_supply_register(dev, <8491_desc, &psy_cfg);
+ if (IS_ERR(info->psp))
+ return dev_err_probe(dev, PTR_ERR(info->psp),
+ "Failed to register power supply.\n");
+
+ return 0;
+}
+
+static const struct i2c_device_id lt8491_id[] = {
+ { "lt8491", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lt8491_id);
+
+static const struct of_device_id lt8491_of_match[] = {
+ { .compatible = "adi,lt8491" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, lt8491_of_match);
+
+static struct i2c_driver lt8491_driver = {
+ .driver = {
+ .name = "lt8491",
+ .of_match_table = lt8491_of_match,
+ },
+ .probe = lt8491_probe,
+ .id_table = lt8491_id,
+};
+module_i2c_driver(lt8491_driver);
+
+MODULE_AUTHOR("John Erasmus Mari Geronimo <johnerasmusmari.geronimo@analog.com");
+MODULE_DESCRIPTION("LT8491 battery charger");
+MODULE_LICENSE("GPL");
--
2.34.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v2 1/2] dt-bindings: power: supply: add adi,lt8491.yaml
2026-09-03 10:46 ` [PATCH v2 1/2] dt-bindings: power: supply: add adi,lt8491.yaml John Erasmus Mari Geronimo
@ 2026-09-03 10:52 ` sashiko-bot
2026-09-03 17:21 ` Conor Dooley
1 sibling, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-09-03 10:52 UTC (permalink / raw)
To: John Erasmus Mari Geronimo; +Cc: robh, conor+dt, devicetree
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Incorrect use of `additionalProperties: false` when inheriting from a core schema via `$ref`.
--
commit 7d1541cad5388e9c078d722b3be849bc6995fe46
Author: John Erasmus Mari Geronimo <johnerasmusmari.geronimo@analog.com>
dt-bindings: power: supply: add adi,lt8491.yaml
This commit adds the devicetree binding documentation for the Analog Devices
LT8491 battery charger.
> diff --git a/Documentation/devicetree/bindings/power/supply/adi,lt8491.yaml b/Documentation/devicetree/bindings/power/supply/adi,lt8491.yaml
> new file mode 100644
> index 0000000000000..bb364b48fee58
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/power/supply/adi,lt8491.yaml
> @@ -0,0 +1,92 @@
[ ... ]
> +allOf:
> + - $ref: power-supply.yaml#
[ ... ]
> +required:
> + - compatible
> + - reg
> + - adi,rsense1-micro-ohms
> + - adi,rimon-out-ohms
> + - adi,rsense2-micro-ohms
> + - adi,rdaco-ohms
> + - adi,rfbout1-ohms
> + - adi,rfbout2-ohms
> + - adi,rdaci-ohms
> + - adi,rfbin2-ohms
> + - adi,rfbin1-ohms
> +
> +additionalProperties: false
[Severity: Medium]
Since this schema inherits from power-supply.yaml via the allOf ref, will
setting additionalProperties to false cause the binding to incorrectly reject
valid standard power supply properties like monitored-battery or
power-supplies during validation?
Should this be set to unevaluatedProperties: false instead to allow properties
inherited from the core schema?
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1788432152.git.johnerasmusmari.geronimo@analog.com?part=1
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 2/2] power: supply: add LT8491 battery charger driver
2026-09-03 10:46 ` [PATCH v2 2/2] power: supply: add LT8491 battery charger driver John Erasmus Mari Geronimo
@ 2026-09-03 10:56 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-09-03 10:56 UTC (permalink / raw)
To: John Erasmus Mari Geronimo; +Cc: devicetree, conor+dt, robh
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
- [High] The local variable `ret` in `lt8491_get_property` is declared as `s16`, which will cause valid 16-bit I2C reads >= 32768 to be incorrectly treated as negative error codes.
- [High] The driver registers its power supply without devres and lacks a `.remove` callback, leading to a resource leak and use-after-free on device unbind.
- [Medium] The driver calls `i2c_smbus_write_word_data` but fails to check for `I2C_FUNC_SMBUS_WRITE_WORD_DATA` during probe.
- [Medium] Missing polling or delay after triggering a telemetry update command.
- [Medium] Missing bounds check for temperature limits in `lt8491_set_property()`.
- [Medium] Missing bounds check for resistor configuration values leading to silent truncation.
--
commit 3b2b2398d72c0229f6f2624e687f0338dc39a93b
Author: John Erasmus Mari Geronimo <johnerasmusmari.geronimo@analog.com>
power: supply: add LT8491 battery charger driver
This commit adds initial support for the LT8491 High Voltage Buck-Boost
Battery Charge Controller via I2C. The driver exposes voltage, current,
power, and temperature telemetry through the power supply subsystem.
diff --git a/drivers/power/supply/lt8491_charger.c b/drivers/power/supply/lt8491_charger.c
--- /dev/null
+++ b/drivers/power/supply/lt8491_charger.c
@@ -0,0 +1,412 @@
[ ... ]
> static int lt8491_get_property(struct power_supply *psy,
> enum power_supply_property psp,
> union power_supply_propval *val)
> {
> struct lt8491_info *info = power_supply_get_drvdata(psy);
> s16 ret;
[Severity: High]
Will the ret variable incorrectly wrap to a negative value when reading large
16-bit register values? Since i2c_smbus_read_word_data() returns an s32,
reading a negative temperature (where bit 15 is set) will cause ret to become
negative. Does this prematurely trigger the if (ret < 0) error path and
prevent userspace from reading negative temperatures?
[ ... ]
> case POWER_SUPPLY_PROP_VOLTAGE_NOW:
> scoped_guard(mutex, &info->lock) {
> ret = i2c_smbus_read_byte_data(info->client,
> LT8491_STAT_CHARGER_REG);
> if (ret < 0)
> return ret;
>
> if (!FIELD_GET(LT8491_TELEM_ACTIVE_MASK, ret)) {
> ret = i2c_smbus_write_byte_data(info->client,
> LT8491_CTRL_UPDATE_TELEM_REG,
> LT8491_UPDATE_TELEM_CMD);
> if (ret)
> return ret;
> }
>
> ret = i2c_smbus_read_word_data(info->client,
> LT8491_TELE_VBAT_REG);
[Severity: Medium]
Is a delay or completion check required after triggering the telemetry update?
The code writes the update command to the telemetry register but immediately
reads the voltage data. Could this result in reading uninitialized or stale
data if the hardware ADC requires time to process the conversion?
[ ... ]
> static int lt8491_set_property(struct power_supply *psy,
> enum power_supply_property psp,
> const union power_supply_propval *val)
> {
> struct lt8491_info *info = power_supply_get_drvdata(psy);
>
> switch (psp) {
> case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
> return i2c_smbus_write_byte_data(info->client,
> LT8491_CFG_TBAT_MIN_REG,
> val->intval / 10);
[Severity: Medium]
Should the bounds of val->intval be validated before writing it? If userspace
writes an extremely large or deeply negative value, could it be silently
truncated when cast to the 8-bit parameter of i2c_smbus_write_byte_data(),
configuring incorrect temperature hardware limits?
[ ... ]
> static int lt8491_configure_resistor(struct lt8491_info *info,
> const char *propname, int divider,
> unsigned int reg)
> {
> struct device *dev = &info->client->dev;
> int ret;
> u32 val;
>
> ret = device_property_read_u32(dev, propname, &val);
> if (ret < 0)
> return dev_err_probe(dev, ret, "Missing %s property.\n",
> propname);
>
> return i2c_smbus_write_word_data(info->client, reg, val / divider);
[Severity: Medium]
Does this division risk silent truncation? If the device tree contains
very large resistor values, could the result of val / divider exceed the
maximum 16-bit unsigned integer (65535) and get silently truncated by the
u16 parameter in i2c_smbus_write_word_data()?
[ ... ]
> static int lt8491_probe(struct i2c_client *client)
> {
> struct device *dev = &client->dev;
> struct lt8491_info *info;
> struct power_supply_config psy_cfg = {};
> int ret;
>
> if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
> I2C_FUNC_SMBUS_READ_WORD_DATA))
> return -EOPNOTSUPP;
[Severity: Medium]
Since lt8491_configure_resistor() later calls i2c_smbus_write_word_data(),
should this capability check also require I2C_FUNC_SMBUS_WRITE_WORD_DATA
to avoid unexpected probe failures on adapters that lack word write support?
[ ... ]
> info->psp = power_supply_register(dev, <8491_desc, &psy_cfg);
> if (IS_ERR(info->psp))
> return dev_err_probe(dev, PTR_ERR(info->psp),
> "Failed to register power supply.\n");
>
> return 0;
> }
[ ... ]
> static struct i2c_driver lt8491_driver = {
> .driver = {
> .name = "lt8491",
> .of_match_table = lt8491_of_match,
> },
> .probe = lt8491_probe,
> .id_table = lt8491_id,
> };
[Severity: High]
Will this leave the power supply device registered indefinitely if the driver
is unbound? The probe function uses power_supply_register() instead of the
devres equivalent, and the driver struct entirely omits a remove callback.
Could this lead to a use-after-free when the underlying info struct is
freed via devres but the power supply sysfs nodes remain active?
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1788432152.git.johnerasmusmari.geronimo@analog.com?part=2
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 1/2] dt-bindings: power: supply: add adi,lt8491.yaml
2026-09-03 10:46 ` [PATCH v2 1/2] dt-bindings: power: supply: add adi,lt8491.yaml John Erasmus Mari Geronimo
2026-09-03 10:52 ` sashiko-bot
@ 2026-09-03 17:21 ` Conor Dooley
1 sibling, 0 replies; 11+ messages in thread
From: Conor Dooley @ 2026-09-03 17:21 UTC (permalink / raw)
To: John Erasmus Mari Geronimo
Cc: linux-pm, devicetree, linux-kernel, krzk+dt, conor+dt, robh, sre
[-- Attachment #1: Type: text/plain, Size: 4261 bytes --]
On Thu, Sep 03, 2026 at 06:46:36PM +0800, John Erasmus Mari Geronimo wrote:
> Add documentation for devicetree bindings for LT8491
>
> Signed-off-by: John Erasmus Mari Geronimo <johnerasmusmari.geronimo@analog.com>
> ---
> .../bindings/power/supply/adi,lt8491.yaml | 92 +++++++++++++++++++
> MAINTAINERS | 6 ++
> 2 files changed, 98 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/power/supply/adi,lt8491.yaml
>
> diff --git a/Documentation/devicetree/bindings/power/supply/adi,lt8491.yaml b/Documentation/devicetree/bindings/power/supply/adi,lt8491.yaml
> new file mode 100644
> index 0000000000000..bb364b48fee58
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/power/supply/adi,lt8491.yaml
> @@ -0,0 +1,92 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/power/supply/adi,lt8491.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Linear Technology (Analog Devices) LT8491 Battery Charger
> +
> +maintainers:
> + - John Erasmus Mari Geronimo <johnerasmusmari.geronimo@analog.com>
> +
> +description: |
> + The LT8491 is a buck-boost switching regulator battery charger that implements
> + a constant-current constant-voltage (CCCV) charging profile used for most
> + battery types, including sealed lead-acid (SLA), flooded, gel and lithium-ion.
> +
> + Specifications about the charger can be found at:
> + https://www.analog.com/en/products/lt8491.html
> +
> +allOf:
> + - $ref: power-supply.yaml#
> +
> +properties:
> + compatible:
> + enum:
> + - adi,lt8491
> +
> + reg:
> + maxItems: 1
> +
> + adi,rsense1-micro-ohms:
> + description: Resistance value of RSENSE1 in micro ohms.
> +
> + adi,rimon-out-ohms:
> + description: Resistance value of RIMON_OUT in ohms.
> +
> + adi,rsense2-micro-ohms:
> + description: Resistance value of RSENSE2 in micro ohms.
> +
> + adi,rdaco-ohms:
> + description: Resistance value of RDACO1 + RDACO2 in ohms.
> +
> + adi,rfbout1-ohms:
> + description: Resistance value of RFBOUT1 in ohms.
> +
> + adi,rfbout2-ohms:
> + description: Resistance value of RFBOUT2 in ohms.
> +
> + adi,rdaci-ohms:
> + description: Resistance value of RDACI1 + RDACI2 in ohms.
> +
> + adi,rfbin2-ohms:
> + description: Resistance value of RFBIN2 in ohms.
> +
> + adi,rfbin1-ohms:
> + description: Resistance value of RFBIN1 in ohms.
> +
> +required:
> + - compatible
> + - reg
> + - adi,rsense1-micro-ohms
> + - adi,rimon-out-ohms
> + - adi,rsense2-micro-ohms
> + - adi,rdaco-ohms
> + - adi,rfbout1-ohms
> + - adi,rfbout2-ohms
> + - adi,rdaci-ohms
> + - adi,rfbin2-ohms
> + - adi,rfbin1-ohms
> +
> +additionalProperties: false
Valid point here from Sashiko.
pw-bot: changes-requested
Thanks,
Conor.
> +
> +examples:
> + - |
> + i2c {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + battery-charger@10 {
> + compatible = "adi,lt8491";
> + reg = <0x10>;
> + adi,rsense1-micro-ohms = <5000>;
> + adi,rimon-out-ohms = <124000>;
> + adi,rsense2-micro-ohms = <3000>;
> + adi,rdaco-ohms = <64900>;
> + adi,rfbout1-ohms = <113000>;
> + adi,rfbout2-ohms = <10000>;
> + adi,rdaci-ohms = <7000>;
> + adi,rfbin2-ohms = <3480>;
> + adi,rfbin1-ohms = <102000>;
> + };
> + };
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 2fb1c75afd163..d79187312f789 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -15170,6 +15170,12 @@ F: Documentation/devicetree/bindings/hwmon/pmbus/adi,lt3074.yaml
> F: Documentation/hwmon/lt3074.rst
> F: drivers/hwmon/pmbus/lt3074.c
>
> +LT8491 BATTERY CHARGER DRIVER
> +M: John Erasmus Mari Geronimo <johnerasmusmari.geronimo@analog.com>
> +L: linux-pm@vger.kernel.org
> +S: Maintained
> +F: Documentation/devicetree/bindings/power/supply/adi,lt8491.yaml
> +
> LTC1660 DAC DRIVER
> M: Marcus Folkesson <marcus.folkesson@gmail.com>
> L: linux-iio@vger.kernel.org
> --
> 2.34.1
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-09-03 17:21 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-01-10 8:02 [PATCH 0/2] Add LT8491 driver John Erasmus Mari Geronimo
2025-01-10 8:02 ` [PATCH 1/2] dt-bindings: power: supply: add adi,lt8491.yaml John Erasmus Mari Geronimo
2025-01-10 9:26 ` Krzysztof Kozlowski
2025-01-10 8:02 ` [PATCH 2/2] power: supply: add LT8491 battery charger driver John Erasmus Mari Geronimo
2025-01-14 16:45 ` kernel test robot
2026-09-03 10:46 ` [PATCH v2 0/2] Add LT8491 driver John Erasmus Mari Geronimo
2026-09-03 10:46 ` [PATCH v2 1/2] dt-bindings: power: supply: add adi,lt8491.yaml John Erasmus Mari Geronimo
2026-09-03 10:52 ` sashiko-bot
2026-09-03 17:21 ` Conor Dooley
2026-09-03 10:46 ` [PATCH v2 2/2] power: supply: add LT8491 battery charger driver John Erasmus Mari Geronimo
2026-09-03 10:56 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox