All of lore.kernel.org
 help / color / mirror / Atom feed
From: Mike Looijmans <mike.looijmans@topic.nl>
To: linux-pm@vger.kernel.org
Cc: Mike Looijmans <mike.looijmans@topic.nl>,
	Sebastian Reichel <sre@kernel.org>,
	linux-kernel@vger.kernel.org
Subject: [PATCH v4 5/5] power: supply: ltc3350-charger: Add driver
Date: Mon, 15 Apr 2024 10:13:05 +0200	[thread overview]
Message-ID: <20240415081305.316107-5-mike.looijmans@topic.nl> (raw)
In-Reply-To: <20240415081305.316107-1-mike.looijmans@topic.nl>

The LTC3350 is a backup power controller that can charge and monitor
a series stack of one to four supercapacitors.

Signed-off-by: Mike Looijmans <mike.looijmans@topic.nl>

---

Changes in v4:
Split into "charger" and "capacitor" parts
Use (new) standard properties
Header include fixups
Explain local "scale" units
Drop i2c_check_functionality
Use dev_err_probe
Use dev_fwnode
Drop of_match_ptr

Changes in v2:
Duplicate "vin_ov" and "vin_uv" attributes

# bash /tmp/tools/testing/selftests/power_supply/test_power_supply_properties.sh
TAP version 13
1..66
# Testing device ltc3350
ok 1 ltc3350.exists
ok 2 ltc3350.uevent.NAME
ok 3 ltc3350.sysfs.type
ok 4 ltc3350.uevent.TYPE
ok 5 ltc3350.sysfs.usb_type # SKIP
# Reported: '1' ()
ok 6 ltc3350.sysfs.online
ok 7 ltc3350.sysfs.present # SKIP
# Reported: 'Full'
ok 8 ltc3350.sysfs.status
ok 9 ltc3350.sysfs.capacity # SKIP
ok 10 ltc3350.sysfs.capacity_level # SKIP
ok 11 ltc3350.sysfs.model_name # SKIP
ok 12 ltc3350.sysfs.manufacturer # SKIP
ok 13 ltc3350.sysfs.serial_number # SKIP
ok 14 ltc3350.sysfs.technology # SKIP
ok 15 ltc3350.sysfs.cycle_count # SKIP
ok 16 ltc3350.sysfs.scope # SKIP
# Reported: '3200000' uA (3200 mA)
ok 17 ltc3350.sysfs.input_current_limit
ok 18 ltc3350.sysfs.input_voltage_limit # SKIP
# Reported: '23742030' uV (23.742 V)
ok 19 ltc3350.sysfs.voltage_now
ok 20 ltc3350.sysfs.voltage_min # SKIP
ok 21 ltc3350.sysfs.voltage_max # SKIP
ok 22 ltc3350.sysfs.voltage_min_design # SKIP
ok 23 ltc3350.sysfs.voltage_max_design # SKIP
# Reported: '467900' uA (467.9 mA)
ok 24 ltc3350.sysfs.current_now
ok 25 ltc3350.sysfs.current_max # SKIP
ok 26 ltc3350.sysfs.charge_now # SKIP
ok 27 ltc3350.sysfs.charge_full # SKIP
ok 28 ltc3350.sysfs.charge_full_design # SKIP
ok 29 ltc3350.sysfs.power_now # SKIP
ok 30 ltc3350.sysfs.energy_now # SKIP
ok 31 ltc3350.sysfs.energy_full # SKIP
ok 32 ltc3350.sysfs.energy_full_design # SKIP
ok 33 ltc3350.sysfs.energy_full_design # SKIP
# Testing device ltc3350_capacitor
ok 34 ltc3350_capacitor.exists
ok 35 ltc3350_capacitor.uevent.NAME
ok 36 ltc3350_capacitor.sysfs.type
ok 37 ltc3350_capacitor.uevent.TYPE
ok 38 ltc3350_capacitor.sysfs.usb_type # SKIP
ok 39 ltc3350_capacitor.sysfs.online # SKIP
ok 40 ltc3350_capacitor.sysfs.present # SKIP
# Reported: 'Full'
ok 41 ltc3350_capacitor.sysfs.status
ok 42 ltc3350_capacitor.sysfs.capacity # SKIP
ok 43 ltc3350_capacitor.sysfs.capacity_level # SKIP
ok 44 ltc3350_capacitor.sysfs.model_name # SKIP
ok 45 ltc3350_capacitor.sysfs.manufacturer # SKIP
ok 46 ltc3350_capacitor.sysfs.serial_number # SKIP
ok 47 ltc3350_capacitor.sysfs.technology # SKIP
ok 48 ltc3350_capacitor.sysfs.cycle_count # SKIP
ok 49 ltc3350_capacitor.sysfs.scope # SKIP
ok 50 ltc3350_capacitor.sysfs.input_current_limit # SKIP
ok 51 ltc3350_capacitor.sysfs.input_voltage_limit # SKIP
# Reported: '5053824' uV (5.05382 V)
ok 52 ltc3350_capacitor.sysfs.voltage_now
# Reported: '4299588' uV (4.29959 V)
ok 53 ltc3350_capacitor.sysfs.voltage_min
# Reported: '4680396' uV (4.6804 V)
ok 54 ltc3350_capacitor.sysfs.voltage_max
ok 55 ltc3350_capacitor.sysfs.voltage_min_design # SKIP
ok 56 ltc3350_capacitor.sysfs.voltage_max_design # SKIP
ok 57 ltc3350_capacitor.sysfs.current_now # SKIP
ok 58 ltc3350_capacitor.sysfs.current_max # SKIP
ok 59 ltc3350_capacitor.sysfs.charge_now # SKIP
ok 60 ltc3350_capacitor.sysfs.charge_full # SKIP
ok 61 ltc3350_capacitor.sysfs.charge_full_design # SKIP
ok 62 ltc3350_capacitor.sysfs.power_now # SKIP
ok 63 ltc3350_capacitor.sysfs.energy_now # SKIP
ok 64 ltc3350_capacitor.sysfs.energy_full # SKIP
ok 65 ltc3350_capacitor.sysfs.energy_full_design # SKIP
ok 66 ltc3350_capacitor.sysfs.energy_full_design # SKIP
# Totals: pass:17 fail:0 xfail:0 xpass:0 skip:49 error:0


 .../ABI/testing/sysfs-class-power-ltc3350     |  88 ++
 drivers/power/supply/Kconfig                  |  10 +
 drivers/power/supply/Makefile                 |   1 +
 drivers/power/supply/ltc3350-charger.c        | 786 ++++++++++++++++++
 4 files changed, 885 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-class-power-ltc3350
 create mode 100644 drivers/power/supply/ltc3350-charger.c

diff --git a/Documentation/ABI/testing/sysfs-class-power-ltc3350 b/Documentation/ABI/testing/sysfs-class-power-ltc3350
new file mode 100644
index 000000000000..d4a2bb0fb62b
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-power-ltc3350
@@ -0,0 +1,88 @@
+What:		/sys/class/power_supply/ltc3350/charge_status
+Date:		April 2024
+KernelVersion:	6.9
+Description:
+		Detailed charge status information as reported by the chip. This
+		returns the raw register value in hex.
+
+		Access: Read
+
+What:		/sys/class/power_supply/ltc3350/vshunt
+Date:		April 2024
+KernelVersion:	6.9
+Description:
+		Voltage for "shunting" the capacitors in the stack. When the
+		capacitor voltage is above this value, the chip will discharge
+		the excess voltage using a shunt resistor.
+		This is typically used to limit the voltage on a single cell,
+		to compensate for imbalance and prevent damaging the capacitor
+		while charging. It can also be used to forcibly discharge the
+		capacitors.
+
+		Access: Read, Write
+
+		Valid values: In microvolts, defaults to 2.7V
+
+What:		/sys/class/power_supply/ltc3350/gpi
+Date:		April 2024
+KernelVersion:	6.9
+Description:
+		General purpose input voltage. Returns a single measurement.
+
+		Access: Read
+
+		Valid values: In microvolts
+
+What:		/sys/class/power_supply/ltc3350/gpi_ov
+Date:		April 2024
+KernelVersion:	6.9
+Description:
+		General purpose input voltage overvoltage level. Triggers an
+		alert for userspace when the voltage goes above this value.
+
+		Access: Read, Write
+
+		Valid values: In microvolts
+
+What:		/sys/class/power_supply/ltc3350/gpi_uv
+Date:		April 2024
+KernelVersion:	6.9
+Description:
+		General purpose input voltage undervoltage level. Triggers an
+		alert for userspace when the voltage drops below this value.
+
+		Access: Read, Write
+
+		Valid values: In microvolts
+
+What:		/sys/class/power_supply/ltc3350/vin
+Date:		April 2024
+KernelVersion:	6.9
+Description:
+		Charger input voltage. Returns a single measurement.
+
+		Access: Read
+
+		Valid values: In microvolts
+
+What:		/sys/class/power_supply/ltc3350/vin_ov
+Date:		April 2024
+KernelVersion:	6.9
+Description:
+		Input voltage overvoltage level. Triggers an alert for userspace
+		when the voltage goes above this value.
+
+		Access: Read, Write
+
+		Valid values: In microvolts
+
+What:		/sys/class/power_supply/ltc3350/vin_uv
+Date:		April 2024
+KernelVersion:	6.9
+Description:
+		Input voltage undervoltage level. Triggers an alert for
+		userspace when the voltage drops below this value.
+
+		Access: Read, Write
+
+		Valid values: In microvolts
diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index 3e31375491d5..7cb1a66e522d 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -514,6 +514,16 @@ 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_LTC3350
+	tristate "LTC3350 Supercapacitor Backup Controller and System Monitor"
+	depends on I2C
+	select REGMAP_I2C
+	select I2C_SMBUS
+	help
+	  Say Y to include support for the Analog Devices (Linear Technology)
+	  LTC3350 Supercapacitor Backup Controller and System Monitor connected
+	  to I2C.
+
 config CHARGER_LTC4162L
 	tristate "LTC4162-L charger"
 	depends on I2C
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
index 58b567278034..a8d618e4ac91 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -72,6 +72,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_LTC3350)	+= ltc3350-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/ltc3350-charger.c b/drivers/power/supply/ltc3350-charger.c
new file mode 100644
index 000000000000..6e0e238766ca
--- /dev/null
+++ b/drivers/power/supply/ltc3350-charger.c
@@ -0,0 +1,786 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Analog Devices (Linear Technology) LTC3350
+ * High Current Supercapacitor Backup Controller and System Monitor
+ * Copyright (C) 2024, Topic Embedded Products
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mod_devicetable.h>
+#include <linux/pm_runtime.h>
+#include <linux/power_supply.h>
+#include <linux/property.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+/* Registers (names based on what datasheet uses) */
+#define LTC3350_REG_CLR_ALARMS		0x00
+#define LTC3350_REG_MSK_ALARMS		0x01
+#define LTC3350_REG_MSK_MON_STATUS	0x02
+#define LTC3350_REG_CAP_ESR_PER		0x04
+#define LTC3350_REG_VCAPFB_DAC		0x05
+#define LTC3350_REG_VSHUNT		0x06
+#define LTC3350_REG_CAP_UV_LVL		0x07
+#define LTC3350_REG_CAP_OV_LVL		0x08
+#define LTC3350_REG_GPI_UV_LVL		0x09
+#define LTC3350_REG_GPI_OV_LVL		0x0A
+#define LTC3350_REG_VIN_UV_LVL		0x0B
+#define LTC3350_REG_VIN_OV_LVL		0x0C
+#define LTC3350_REG_VCAP_UV_LVL		0x0D
+#define LTC3350_REG_VCAP_OV_LVL		0x0E
+#define LTC3350_REG_VOUT_UV_LVL		0x0F
+#define LTC3350_REG_VOUT_OV_LVL		0x10
+#define LTC3350_REG_IIN_OC_LVL		0x11
+#define LTC3350_REG_ICHG_UC_LVL		0x12
+#define LTC3350_REG_DTEMP_COLD_LVL	0x13
+#define LTC3350_REG_DTEMP_HOT_LVL	0x14
+#define LTC3350_REG_ESR_HI_LVL		0x15
+#define LTC3350_REG_CAP_LO_LVL		0x16
+#define LTC3350_REG_CTL_REG		0x17
+#define LTC3350_REG_NUM_CAPS		0x1A
+#define LTC3350_REG_CHRG_STATUS		0x1B
+#define LTC3350_REG_MON_STATUS		0x1C
+#define LTC3350_REG_ALARM_REG		0x1D
+#define LTC3350_REG_MEAS_CAP		0x1E
+#define LTC3350_REG_MEAS_ESR		0x1F
+#define LTC3350_REG_MEAS_VCAP1		0x20
+#define LTC3350_REG_MEAS_VCAP2		0x21
+#define LTC3350_REG_MEAS_VCAP3		0x22
+#define LTC3350_REG_MEAS_VCAP4		0x23
+#define LTC3350_REG_MEAS_GPI		0x24
+#define LTC3350_REG_MEAS_VIN		0x25
+#define LTC3350_REG_MEAS_VCAP		0x26
+#define LTC3350_REG_MEAS_VOUT		0x27
+#define LTC3350_REG_MEAS_IIN		0x28
+#define LTC3350_REG_MEAS_ICHG		0x29
+#define LTC3350_REG_MEAS_DTEMP		0x2A
+
+/* LTC3350_REG_CLR_ALARMS, LTC3350_REG_MASK_ALARMS, LTC3350_REG_ALARM_REG */
+#define LTC3350_MSK_CAP_UV	BIT(0)	/* capacitor undervoltage alarm */
+#define LTC3350_MSK_CAP_OV	BIT(1)	/* capacitor overvoltage alarm */
+#define LTC3350_MSK_GPI_UV	BIT(2)	/* GPI undervoltage alarm */
+#define LTC3350_MSK_GPI_OV	BIT(3)	/* GPI overvoltage alarm */
+#define LTC3350_MSK_VIN_UV	BIT(4)	/* VIN undervoltage alarm */
+#define LTC3350_MSK_VIN_OV	BIT(5)	/* VIN overvoltage alarm */
+#define LTC3350_MSK_VCAP_UV	BIT(6)	/* VCAP undervoltage alarm */
+#define LTC3350_MSK_VCAP_OV	BIT(7)	/* VCAP overvoltage alarm */
+#define LTC3350_MSK_VOUT_UV	BIT(8)	/* VOUT undervoltage alarm */
+#define LTC3350_MSK_VOUT_OV	BIT(9)	/* VOUT overvoltage alarm */
+#define LTC3350_MSK_IIN_OC	BIT(10)	/* input overcurrent alarm */
+#define LTC3350_MSK_ICHG_UC	BIT(11)	/* charge undercurrent alarm */
+#define LTC3350_MSK_DTEMP_COLD	BIT(12)	/* die temperature cold alarm */
+#define LTC3350_MSK_DTEMP_HOT	BIT(13)	/* die temperature hot alarm */
+#define LTC3350_MSK_ESR_HI	BIT(14)	/* ESR high alarm */
+#define LTC3350_MSK_CAP_LO	BIT(15)	/* capacitance low alarm */
+
+/* LTC3350_REG_MSK_MON_STATUS masks */
+#define LTC3350_MSK_MON_CAPESR_ACTIVE		BIT(0)
+#define LTC3350_MSK_MON_CAPESR_SCHEDULED	BIT(1)
+#define LTC3350_MSK_MON_CAPESR_PENDING		BIT(2)
+#define LTC3350_MSK_MON_CAP_DONE		BIT(3)
+#define LTC3350_MSK_MON_ESR_DONE		BIT(4)
+#define LTC3350_MSK_MON_CAP_FAILED		BIT(5)
+#define LTC3350_MSK_MON_ESR_FAILED		BIT(6)
+#define LTC3350_MSK_MON_POWER_FAILED		BIT(8)
+#define LTC3350_MSK_MON_POWER_RETURNED		BIT(9)
+
+/* LTC3350_REG_CTL_REG */
+/* Begin a capacitance and ESR measurement when possible */
+#define LTC3350_CTL_STRT_CAPESR		BIT(0)
+/* A one in this bit location enables the input buffer on the GPI pin */
+#define LTC3350_CTL_GPI_BUFFER_EN	BIT(1)
+/* Stops an active capacitance/ESR measurement */
+#define LTC3350_CTL_STOP_CAPESR		BIT(2)
+/* Increases capacitor measurement resolution by 100x for smaller capacitors */
+#define LTC3350_CTL_CAP_SCALE		BIT(3)
+
+/* LTC3350_REG_CHRG_STATUS */
+#define LTC3350_CHRG_STEPDOWN	BIT(0)	/* Synchronous controller in step-down mode (charging) */
+#define LTC3350_CHRG_STEPUP	BIT(1)	/* Synchronous controller in step-up mode (backup) */
+#define LTC3350_CHRG_CV		BIT(2)	/* The charger is in constant voltage mode */
+#define LTC3350_CHRG_UVLO	BIT(3)	/* The charger is in undervoltage lockout */
+#define LTC3350_CHRG_INPUT_ILIM	BIT(4)	/* The charger is in input current limit */
+#define LTC3350_CHRG_CAPPG	BIT(5)	/* The capacitor voltage is above power good threshold */
+#define LTC3350_CHRG_SHNT	BIT(6)	/* The capacitor manager is shunting */
+#define LTC3350_CHRG_BAL	BIT(7)	/* The capacitor manager is balancing */
+#define LTC3350_CHRG_DIS	BIT(8)	/* Charger disabled for capacitance measurement */
+#define LTC3350_CHRG_CI		BIT(9)	/* The charger is in constant current mode */
+#define LTC3350_CHRG_PFO	BIT(11)	/* Input voltage is below PFI threshold */
+
+/* LTC3350_REG_MON_STATUS */
+#define LTC3350_MON_CAPESR_ACTIVE	BIT(0)	/* Capacitance/ESR measurement in progress */
+#define LTC3350_MON_CAPESR_SCHEDULED	BIT(1)	/* Waiting programmed time */
+#define LTC3350_MON_CAPESR_PENDING	BIT(2)	/* Waiting for satisfactory conditions */
+#define LTC3350_MON_CAP_DONE		BIT(3)	/* Capacitance measurement has completed */
+#define LTC3350_MON_ESR_DONE		BIT(4)	/* ESR Measurement has completed */
+#define LTC3350_MON_CAP_FAILED		BIT(5)	/* Last capacitance measurement failed */
+#define LTC3350_MON_ESR_FAILED		BIT(6)	/* Last ESR measurement failed */
+#define LTC3350_MON_POWER_FAILED	BIT(8)	/* Unable to charge */
+#define LTC3350_MON_POWER_RETURNED	BIT(9)	/* Able to charge */
+
+
+struct ltc3350_info {
+	struct i2c_client	*client;
+	struct regmap		*regmap;
+	struct power_supply	*charger;
+	struct power_supply	*capacitor;
+	u32 rsnsc;	/* Series resistor that sets charge current, microOhm */
+	u32 rsnsi;	/* Series resistor to measure input current, microOhm */
+};
+
+/*
+ * About LTC3350 "alarm" functions: Setting a bit in the LTC3350_REG_MSK_ALARMS
+ * register enables the alarm. The alarm will trigger an SMBALERT only once.
+ * To reset the alarm, write a "1" bit to LTC3350_REG_CLR_ALARMS. Then the alarm
+ * will trigger another SMBALERT when conditions are met (may be immediately).
+ * After writing to one of the corresponding level registers, enable the alarm,
+ * so that a UEVENT triggers when the alarm goes off.
+ */
+static void ltc3350_enable_alarm(struct ltc3350_info *info, unsigned int reg)
+{
+	unsigned int mask;
+
+	/* Register locations correspond to alarm mask bits */
+	mask = BIT(reg - LTC3350_REG_CAP_UV_LVL);
+	/* Clear the alarm bit so it may trigger again */
+	regmap_write(info->regmap, LTC3350_REG_CLR_ALARMS, mask);
+	/* Enable the alarm */
+	regmap_update_bits(info->regmap, LTC3350_REG_MSK_ALARMS, mask, mask);
+}
+
+/* Convert enum value to POWER_SUPPLY_STATUS value */
+static int ltc3350_state_decode(unsigned int value)
+{
+	if (value & LTC3350_CHRG_STEPUP)
+		return POWER_SUPPLY_STATUS_DISCHARGING; /* running on backup */
+
+	if (value & LTC3350_CHRG_PFO)
+		return POWER_SUPPLY_STATUS_NOT_CHARGING;
+
+	if (value & LTC3350_CHRG_STEPDOWN) {
+		/* The chip remains in CV mode indefinitely, hence "full" */
+		if (value & LTC3350_CHRG_CV)
+			return POWER_SUPPLY_STATUS_FULL;
+
+		return POWER_SUPPLY_STATUS_CHARGING;
+	}
+
+	/* Not in step down? Must be full then (never seen this) */
+	return POWER_SUPPLY_STATUS_FULL;
+};
+
+static int ltc3350_get_status(struct ltc3350_info *info, union power_supply_propval *val)
+{
+	unsigned int regval;
+	int ret;
+
+	ret = regmap_read(info->regmap, LTC3350_REG_CHRG_STATUS, &regval);
+	if (ret)
+		return ret;
+
+	val->intval = ltc3350_state_decode(regval);
+
+	return 0;
+}
+
+static int ltc3350_charge_status_decode(unsigned int value)
+{
+	if (!(value & LTC3350_CHRG_STEPDOWN))
+		return POWER_SUPPLY_CHARGE_TYPE_NONE;
+
+	/* constant voltage is "topping off" */
+	if (value & LTC3350_CHRG_CV)
+		return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+
+	/* input limiter */
+	if (value & LTC3350_CHRG_INPUT_ILIM)
+		return POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE;
+
+	/* constant current is "fast" */
+	if (value & LTC3350_CHRG_CI)
+		return POWER_SUPPLY_CHARGE_TYPE_FAST;
+
+	return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+}
+
+static int ltc3350_get_charge_type(struct ltc3350_info *info, union power_supply_propval *val)
+{
+	unsigned int regval;
+	int ret;
+
+	ret = regmap_read(info->regmap, LTC3350_REG_CHRG_STATUS, &regval);
+	if (ret)
+		return ret;
+
+	val->intval = ltc3350_charge_status_decode(regval);
+
+	return 0;
+}
+
+static int ltc3350_get_online(struct ltc3350_info *info, union power_supply_propval *val)
+{
+	unsigned int regval;
+	int ret;
+
+	ret = regmap_read(info->regmap, LTC3350_REG_MON_STATUS, &regval);
+	if (ret)
+		return ret;
+
+	/* indicates if input voltage is sufficient to charge */
+	val->intval = !!(regval & LTC3350_MON_POWER_RETURNED);
+
+	return 0;
+}
+
+static int ltc3350_get_scaled(struct ltc3350_info *info, unsigned int reg,
+			      int scale, int *value)
+{
+	unsigned int regval;
+	int ret;
+
+	ret = regmap_read(info->regmap, reg, &regval);
+	if (ret)
+		return ret;
+
+	/*
+	 * The "scale" here is 10 μV per LSB, this allows all calculations to
+	 * be done in 32-bit integer without overflow. This converts the
+	 * register value to μV.
+	 */
+	*value = regval * scale / 10;
+
+	return 0;
+}
+
+static int ltc3350_set_scaled(struct ltc3350_info *info, unsigned int reg, int scale, int value)
+{
+	int ret;
+
+	value *= 10;
+	value = DIV_ROUND_CLOSEST(value, scale);
+
+	ret = regmap_write(info->regmap, reg, value);
+	if (ret)
+		return ret;
+
+	/* When writing to one of the LVL registers, update the alarm mask */
+	if (reg >= LTC3350_REG_CAP_UV_LVL && reg <= LTC3350_REG_CAP_LO_LVL)
+		ltc3350_enable_alarm(info, reg);
+
+	return 0;
+}
+
+static int ltc3350_get_input_current(struct ltc3350_info *info, union power_supply_propval *val)
+{
+	unsigned int regval;
+	int ret;
+
+	ret = regmap_read(info->regmap, LTC3350_REG_MEAS_IIN, &regval);
+	if (ret)
+		return ret;
+
+	/* 1.983µV/RSNSI amperes per LSB */
+	ret = regval * 19830;
+	ret /= info->rsnsi;
+	ret *= 100;
+
+	val->intval = ret;
+
+	return 0;
+}
+
+static int ltc3350_get_icharge(struct ltc3350_info *info, union power_supply_propval *val)
+{
+	unsigned int regval;
+	int ret;
+
+	ret = regmap_read(info->regmap, LTC3350_REG_MEAS_ICHG, &regval);
+	if (ret)
+		return ret;
+
+	/* 1.983µV/RSNSC amperes per LSB */
+	ret = regval * 19830;
+	ret /= info->rsnsc;
+	ret *= 100;
+
+	val->intval = ret;
+
+	return 0;
+}
+
+static int ltc3350_get_icharge_max(struct ltc3350_info *info, union power_supply_propval *val)
+{
+	/* I_CHG(MAX) = 32mV / RSNSC (Ampere) */
+	val->intval = 3200000000U / (info->rsnsc / 10);
+
+	return 0;
+}
+
+static int ltc3350_get_iin_max(struct ltc3350_info *info, union power_supply_propval *val)
+{
+	/* I_IN(MAX) = 32mV / RSNSI (Ampere) */
+	val->intval = 3200000000U / (info->rsnsi / 10);
+
+	return 0;
+}
+
+
+static int ltc3350_get_die_temp(struct ltc3350_info *info, unsigned int reg,
+				union power_supply_propval *val)
+{
+	unsigned int regval;
+	int ret;
+
+	ret = regmap_read(info->regmap, reg, &regval);
+	if (ret)
+		return ret;
+
+	/* 0.028°C per LSB – 251.4°C */
+	ret = 280 * regval;
+	ret /= 100; /* Centidegrees scale */
+	ret -= 25140;
+	val->intval = ret;
+
+	return 0;
+}
+
+static int ltc3350_set_die_temp(struct ltc3350_info *info, unsigned int reg, int val)
+{
+	unsigned int regval;
+	int ret;
+
+	/* 0.028°C per LSB – 251.4°C */
+	regval = val + 25140;
+	regval *= 100;
+	regval /= 280;
+
+	ret = regmap_write(info->regmap, reg, regval);
+	if (ret)
+		return ret;
+
+	ltc3350_enable_alarm(info, reg);
+	return 0;
+}
+
+static int ltc3350_get_num_caps(struct ltc3350_info *info, union power_supply_propval *val)
+{
+	unsigned int regval;
+	int ret;
+
+	ret = regmap_read(info->regmap, LTC3350_REG_NUM_CAPS, &regval);
+	if (ret)
+		return ret;
+
+	val->intval = regval + 1;
+
+	return 0;
+}
+
+
+/* Custom properties */
+static ssize_t ltc3350_attr_show(struct device *dev,
+				 struct device_attribute *attr, char *buf,
+				 unsigned int reg, int scale)
+{
+	struct power_supply *psy = to_power_supply(dev);
+	struct ltc3350_info *info = power_supply_get_drvdata(psy);
+	int value;
+	int ret;
+
+	ret = ltc3350_get_scaled(info, reg, scale, &value);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "%d\n", value);
+}
+
+static ssize_t ltc3350_attr_store(struct device *dev, struct device_attribute *attr,
+				  const char *buf, size_t count,
+				  unsigned int reg, unsigned int scale)
+{
+	struct power_supply *psy = to_power_supply(dev);
+	struct ltc3350_info *info = power_supply_get_drvdata(psy);
+	unsigned long val;
+	int ret;
+
+	ret = kstrtoul(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	ret = ltc3350_set_scaled(info, reg, val, scale);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+#define LTC3350_DEVICE_ATTR_RO(_name, _reg, _scale)				\
+static ssize_t _name##_show(struct device *dev, struct device_attribute *attr,	\
+			    char *buf)						\
+{										\
+	return ltc3350_attr_show(dev, attr, buf, _reg, _scale);			\
+}										\
+static DEVICE_ATTR_RO(_name)
+
+#define LTC3350_DEVICE_ATTR_RW(_name, _reg, _scale)				\
+static ssize_t _name##_show(struct device *dev, struct device_attribute *attr,	\
+			    char *buf)						\
+{										\
+	return ltc3350_attr_show(dev, attr, buf, _reg, _scale);			\
+}										\
+static ssize_t _name##_store(struct device *dev, struct device_attribute *attr, \
+			     const char *buf, size_t count)			\
+{										\
+	return ltc3350_attr_store(dev, attr, buf, count, _reg, _scale);		\
+}										\
+static DEVICE_ATTR_RW(_name)
+
+/* Shunt voltage, 183.5μV per LSB */
+LTC3350_DEVICE_ATTR_RW(vshunt, LTC3350_REG_VSHUNT, 1835);
+
+/* General purpose input, 183.5μV per LSB */
+LTC3350_DEVICE_ATTR_RO(gpi, LTC3350_REG_MEAS_GPI, 1835);
+LTC3350_DEVICE_ATTR_RW(gpi_uv, LTC3350_REG_GPI_UV_LVL, 1835);
+LTC3350_DEVICE_ATTR_RW(gpi_ov, LTC3350_REG_GPI_OV_LVL, 1835);
+
+/* Input voltage, 2.21mV per LSB */
+LTC3350_DEVICE_ATTR_RO(vin, LTC3350_REG_MEAS_VIN, 22100);
+LTC3350_DEVICE_ATTR_RW(vin_uv, LTC3350_REG_VIN_UV_LVL, 22100);
+LTC3350_DEVICE_ATTR_RW(vin_ov, LTC3350_REG_VIN_OV_LVL, 22100);
+
+static ssize_t charge_status_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct power_supply *psy = to_power_supply(dev);
+	struct ltc3350_info *info = power_supply_get_drvdata(psy);
+	unsigned int regval;
+	int ret;
+
+	ret = regmap_read(info->regmap, LTC3350_REG_CHRG_STATUS, &regval);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "0x%x\n", regval);
+}
+static DEVICE_ATTR_RO(charge_status);
+
+static struct attribute *ltc3350_sysfs_entries[] = {
+	&dev_attr_vshunt.attr,
+	&dev_attr_gpi.attr,
+	&dev_attr_gpi_uv.attr,
+	&dev_attr_gpi_ov.attr,
+	&dev_attr_vin.attr,
+	&dev_attr_vin_uv.attr,
+	&dev_attr_vin_ov.attr,
+	&dev_attr_charge_status.attr,
+	NULL,
+};
+
+static const struct attribute_group ltc3350_attr_group = {
+	.name	= NULL,	/* put in device directory */
+	.attrs	= ltc3350_sysfs_entries,
+};
+
+static const struct attribute_group *ltc3350_attr_groups[] = {
+	&ltc3350_attr_group,
+	NULL,
+};
+
+static int ltc3350_get_property(struct power_supply *psy, enum power_supply_property psp,
+				union power_supply_propval *val)
+{
+	struct ltc3350_info *info = power_supply_get_drvdata(psy);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		return ltc3350_get_status(info, val);
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+		return ltc3350_get_charge_type(info, val);
+	case POWER_SUPPLY_PROP_ONLINE:
+		return ltc3350_get_online(info, val);
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		return ltc3350_get_scaled(info, LTC3350_REG_MEAS_VOUT, 22100, &val->intval);
+	case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+		return ltc3350_get_scaled(info, LTC3350_REG_VOUT_UV_LVL, 22100, &val->intval);
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+		return ltc3350_get_scaled(info, LTC3350_REG_VOUT_OV_LVL, 22100, &val->intval);
+	case POWER_SUPPLY_PROP_CURRENT_NOW:
+		return ltc3350_get_input_current(info, val);
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+		return ltc3350_get_icharge(info, val);
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+		return ltc3350_get_icharge_max(info, val);
+	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+		return ltc3350_get_iin_max(info, val);
+	case POWER_SUPPLY_PROP_TEMP:
+		return ltc3350_get_die_temp(info, LTC3350_REG_MEAS_DTEMP, val);
+	case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
+		return ltc3350_get_die_temp(info, LTC3350_REG_DTEMP_COLD_LVL, val);
+	case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
+		return ltc3350_get_die_temp(info, LTC3350_REG_DTEMP_HOT_LVL, val);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ltc3350_set_property(struct power_supply *psy, enum power_supply_property psp,
+				const union power_supply_propval *val)
+{
+	struct ltc3350_info *info = power_supply_get_drvdata(psy);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
+		return ltc3350_set_die_temp(info, LTC3350_REG_DTEMP_COLD_LVL, val->intval);
+	case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
+		return ltc3350_set_die_temp(info, LTC3350_REG_DTEMP_HOT_LVL, val->intval);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ltc3350_property_is_writeable(struct power_supply *psy, enum power_supply_property psp)
+{
+	switch (psp) {
+	case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+	case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
+	case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
+	case POWER_SUPPLY_PROP_CELL_VOLTAGE_MIN:
+	case POWER_SUPPLY_PROP_CELL_VOLTAGE_MAX:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+/* Charger power supply property routines */
+static enum power_supply_property ltc3350_properties[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_CHARGE_TYPE,
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
+	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_TEMP_ALERT_MIN,
+	POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
+};
+
+static const struct power_supply_desc ltc3350_desc = {
+	.name		= "ltc3350",
+	.type		= POWER_SUPPLY_TYPE_MAINS,
+	.properties	= ltc3350_properties,
+	.num_properties	= ARRAY_SIZE(ltc3350_properties),
+	.get_property	= ltc3350_get_property,
+	.set_property	= ltc3350_set_property,
+	.property_is_writeable = ltc3350_property_is_writeable,
+};
+
+static int ltc3350_capacitor_get_property(struct power_supply *psy, enum power_supply_property psp,
+					  union power_supply_propval *val)
+{
+	struct ltc3350_info *info = power_supply_get_drvdata(psy);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		return ltc3350_get_status(info, val);
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		/* Capacitor stack voltage, 1.476 mV per LSB */
+		return ltc3350_get_scaled(info, LTC3350_REG_MEAS_VCAP, 14760, &val->intval);
+	case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+		return ltc3350_get_scaled(info, LTC3350_REG_VCAP_UV_LVL, 14760, &val->intval);
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+		return ltc3350_get_scaled(info, LTC3350_REG_VCAP_OV_LVL, 14760, &val->intval);
+	case POWER_SUPPLY_PROP_NUMBER_OF_SERIAL_CELLS:
+		return ltc3350_get_num_caps(info, val);
+	case POWER_SUPPLY_PROP_CELL1_VOLTAGE_NOW:
+		/* Single capacitor voltages, 183.5μV per LSB */
+		return ltc3350_get_scaled(info, LTC3350_REG_MEAS_VCAP1, 1835, &val->intval);
+	case POWER_SUPPLY_PROP_CELL2_VOLTAGE_NOW:
+		return ltc3350_get_scaled(info, LTC3350_REG_MEAS_VCAP2, 1835, &val->intval);
+	case POWER_SUPPLY_PROP_CELL3_VOLTAGE_NOW:
+		return ltc3350_get_scaled(info, LTC3350_REG_MEAS_VCAP3, 1835, &val->intval);
+	case POWER_SUPPLY_PROP_CELL4_VOLTAGE_NOW:
+		return ltc3350_get_scaled(info, LTC3350_REG_MEAS_VCAP4, 1835, &val->intval);
+	case POWER_SUPPLY_PROP_CELL_VOLTAGE_MIN:
+		return ltc3350_get_scaled(info, LTC3350_REG_CAP_UV_LVL, 1835, &val->intval);
+	case POWER_SUPPLY_PROP_CELL_VOLTAGE_MAX:
+		return ltc3350_get_scaled(info, LTC3350_REG_CAP_OV_LVL, 1835, &val->intval);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ltc3350_capacitor_set_property(struct power_supply *psy, enum power_supply_property psp,
+					  const union power_supply_propval *val)
+{
+	struct ltc3350_info *info = power_supply_get_drvdata(psy);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+		return ltc3350_set_scaled(info, LTC3350_REG_VCAP_UV_LVL, 14760, val->intval);
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+		return ltc3350_set_scaled(info, LTC3350_REG_VCAP_OV_LVL, 14760, val->intval);
+	case POWER_SUPPLY_PROP_CELL_VOLTAGE_MIN:
+		return ltc3350_set_scaled(info, LTC3350_REG_CAP_UV_LVL, 1835, val->intval);
+	case POWER_SUPPLY_PROP_CELL_VOLTAGE_MAX:
+		return ltc3350_set_scaled(info, LTC3350_REG_CAP_OV_LVL, 1835, val->intval);
+	default:
+		return -EINVAL;
+	}
+}
+
+static enum power_supply_property ltc3350_capacitor_properties[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_VOLTAGE_MIN,
+	POWER_SUPPLY_PROP_VOLTAGE_MAX,
+	POWER_SUPPLY_PROP_NUMBER_OF_SERIAL_CELLS,
+	POWER_SUPPLY_PROP_CELL1_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CELL2_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CELL3_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CELL4_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CELL_VOLTAGE_MIN,
+	POWER_SUPPLY_PROP_CELL_VOLTAGE_MAX,
+};
+
+static const struct power_supply_desc ltc3350_capacitor_desc = {
+	.name		= "ltc3350_capacitor",
+	.type		= POWER_SUPPLY_TYPE_BATTERY,
+	.properties	= ltc3350_capacitor_properties,
+	.num_properties	= ARRAY_SIZE(ltc3350_capacitor_properties),
+	.get_property	= ltc3350_capacitor_get_property,
+	.set_property	= ltc3350_capacitor_set_property,
+	.property_is_writeable = ltc3350_property_is_writeable,
+};
+
+static char *ltc3350_supply_interface_capacitor[] = { "ltc3350_capacitor" };
+
+static bool ltc3350_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+	/* all registers up to this one are writeable */
+	return reg < LTC3350_REG_NUM_CAPS;
+}
+
+static bool ltc3350_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+	/* read-only status registers and self-clearing register */
+	return reg > LTC3350_REG_NUM_CAPS || reg == LTC3350_REG_CLR_ALARMS;
+}
+
+static const struct regmap_config ltc3350_regmap_config = {
+	.reg_bits	= 8,
+	.val_bits	= 16,
+	.val_format_endian = REGMAP_ENDIAN_LITTLE,
+	.writeable_reg	= ltc3350_is_writeable_reg,
+	.volatile_reg	= ltc3350_is_volatile_reg,
+	.max_register	= LTC3350_REG_MEAS_DTEMP,
+	.cache_type	= REGCACHE_MAPLE,
+};
+
+static int ltc3350_probe(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	struct ltc3350_info *info;
+	struct power_supply_config ltc3350_config = {};
+	int ret;
+
+	info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	info->client = client;
+	i2c_set_clientdata(client, info);
+
+	info->regmap = devm_regmap_init_i2c(client, &ltc3350_regmap_config);
+	if (IS_ERR(info->regmap))
+		return dev_err_probe(dev, PTR_ERR(info->regmap),
+				     "Failed to initialize register map\n");
+
+	ret = device_property_read_u32(dev, "lltc,rsnsc-micro-ohms",
+				       &info->rsnsc);
+	if (ret)
+		return dev_err_probe(dev, ret, "Missing lltc,rsnsc-micro-ohms property\n");
+
+	if (!info->rsnsc)
+		return -EINVAL;
+
+	ret = device_property_read_u32(dev, "lltc,rsnsi-micro-ohms",
+				       &info->rsnsi);
+	if (ret)
+		return dev_err_probe(dev, ret, "Missing lltc,rsnsi-micro-ohms property\n");
+
+	if (!info->rsnsi)
+		return -EINVAL;
+
+	/* Clear and disable all interrupt sources*/
+	ret = regmap_write(info->regmap, LTC3350_REG_CLR_ALARMS, 0xFFFF);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to write configuration\n");
+
+	regmap_write(info->regmap, LTC3350_REG_MSK_ALARMS, 0);
+	regmap_write(info->regmap, LTC3350_REG_MSK_MON_STATUS, 0);
+
+	ltc3350_config.fwnode = dev_fwnode(dev);
+	ltc3350_config.drv_data = info;
+	info->capacitor = devm_power_supply_register(dev, &ltc3350_capacitor_desc, &ltc3350_config);
+	if (IS_ERR(info->capacitor)) {
+		dev_err(dev, "Failed to register capacitor\n");
+		return PTR_ERR(info->capacitor);
+	}
+
+	ltc3350_config.supplied_to = ltc3350_supply_interface_capacitor;
+	ltc3350_config.num_supplicants = ARRAY_SIZE(ltc3350_supply_interface_capacitor);
+	ltc3350_config.attr_grp = ltc3350_attr_groups;
+	info->charger = devm_power_supply_register(dev, &ltc3350_desc, &ltc3350_config);
+	if (IS_ERR(info->charger)) {
+		dev_err(dev, "Failed to register charger\n");
+		return PTR_ERR(info->charger);
+	}
+
+	/* Enable interrupts on status changes */
+	regmap_write(info->regmap, LTC3350_REG_MSK_MON_STATUS,
+		     LTC3350_MON_POWER_FAILED | LTC3350_MON_POWER_RETURNED);
+
+	return 0;
+}
+
+static void ltc3350_alert(struct i2c_client *client, enum i2c_alert_protocol type,
+			  unsigned int flag)
+{
+	struct ltc3350_info *info = i2c_get_clientdata(client);
+
+	if (type != I2C_PROTOCOL_SMBUS_ALERT)
+		return;
+
+	power_supply_changed(info->charger);
+}
+
+static const struct i2c_device_id ltc3350_i2c_id_table[] = {
+	{ "ltc3350", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, ltc3350_i2c_id_table);
+
+static const struct of_device_id ltc3350_of_match[] = {
+	{ .compatible = "lltc,ltc3350", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, ltc3350_of_match);
+
+static struct i2c_driver ltc3350_driver = {
+	.probe		= ltc3350_probe,
+	.alert		= ltc3350_alert,
+	.id_table	= ltc3350_i2c_id_table,
+	.driver = {
+		.name		= "ltc3350-charger",
+		.of_match_table	= ltc3350_of_match,
+	},
+};
+module_i2c_driver(ltc3350_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mike Looijmans <mike.looijmans@topic.nl>");
+MODULE_DESCRIPTION("LTC3350 charger driver");
-- 
2.34.1


Met vriendelijke groet / kind regards,

Mike Looijmans
System Expert


TOPIC Embedded Products B.V.
Materiaalweg 4, 5681 RJ Best
The Netherlands

T: +31 (0) 499 33 69 69
E: mike.looijmans@topic.nl
W: www.topic.nl

Please consider the environment before printing this e-mail

  parent reply	other threads:[~2024-04-15  8:13 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <1b153bce-a66a-45ee-a5c6-963ea6fb1c82.949ef384-8293-46b8-903f-40a477c056ae.b2a893bc-f00b-47cf-ae07-b37ec1bace22@emailsignatures365.codetwo.com>
2024-04-15  8:13 ` [PATCH v4 1/5] dt-bindings: power: Extend battery chemistry with capacitor Mike Looijmans
     [not found]   ` <1b153bce-a66a-45ee-a5c6-963ea6fb1c82.949ef384-8293-46b8-903f-40a477c056ae.0aecd676-868f-4018-bb2f-56c7fe7c18ff@emailsignatures365.codetwo.com>
2024-04-15  8:13     ` [PATCH v4 2/5] power: supply: core: Add POWER_SUPPLY_TECHNOLOGY_CAPACITOR Mike Looijmans
     [not found]   ` <1b153bce-a66a-45ee-a5c6-963ea6fb1c82.949ef384-8293-46b8-903f-40a477c056ae.9dd4ffd7-8f51-463b-bf4f-d1ff27030b84@emailsignatures365.codetwo.com>
2024-04-15  8:13     ` [PATCH v4 3/5] power: supply: core: Add POWER_SUPPLY_PROP_*CELL* entries Mike Looijmans
     [not found]   ` <1b153bce-a66a-45ee-a5c6-963ea6fb1c82.949ef384-8293-46b8-903f-40a477c056ae.9959538d-6257-47d8-9ef7-78f72d89b9a7@emailsignatures365.codetwo.com>
2024-04-15  8:13     ` [PATCH v4 4/5] dt-bindings: power: supply: ltc3350-charger: Add bindings Mike Looijmans
     [not found]   ` <1b153bce-a66a-45ee-a5c6-963ea6fb1c82.949ef384-8293-46b8-903f-40a477c056ae.f3ad88bc-748d-4d1b-abcf-bcf420d05ddf@emailsignatures365.codetwo.com>
2024-04-15  8:13     ` Mike Looijmans [this message]
2024-04-15 15:24   ` [PATCH v4 1/5] dt-bindings: power: Extend battery chemistry with capacitor Krzysztof Kozlowski
2024-04-22  7:50     ` Mike Looijmans
2024-04-22 11:56       ` Krzysztof Kozlowski
2024-04-22 12:12         ` Mike Looijmans

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240415081305.316107-5-mike.looijmans@topic.nl \
    --to=mike.looijmans@topic.nl \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=sre@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.