devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v6 0/2] MAX77759 Fuel Gauge driver support
@ 2025-09-15 10:14 Thomas Antoine via B4 Relay
  2025-09-15 10:14 ` [PATCH v6 1/2] power: supply: add support for MAX77759 fuel gauge Thomas Antoine via B4 Relay
  2025-09-15 10:14 ` [PATCH v6 2/2] dt-bindings: " Thomas Antoine via B4 Relay
  0 siblings, 2 replies; 10+ messages in thread
From: Thomas Antoine via B4 Relay @ 2025-09-15 10:14 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sebastian Reichel,
	Peter Griffin, André Draszik
  Cc: linux-kernel, linux-pm, devicetree, Thomas Antoine

The gs101-oriole (Google Pixel 6) and gs101-raven (Google Pixel 6 Pro)
have a Maxim MAX77759 which provides a fuel gauge functionality based
on the MAX M5 fuel gauge.

Add a driver for the fuel gauge of the Maxim MAX77759 based on the
one for the Maxim MAX1720x which also uses the MAX M5 fuel gauge.

A future patch will add both gs101-oriole and gs101-raven as clients.

Signed-off-by: Thomas Antoine <t.antoine@uclouvain.be>
---
Changes in v6:
- Remove devicetree and defconfig changes from patch
- Driver: Check return of power_supply_get_battery_info (Peter Griffin)
- Binding: Fix properties order of the example and add power supply ref
(Krzysztof Kozlowski)
- Link to v5: https://lore.kernel.org/all/20250804-b4-gs101_max77759_fg-v5-0-03a40e6c0e3d@uclouvain.be

Changes in v5:
- Separate MAX77759 from MAX1720x for clarity
- Remove voltage reporting
- Add initialization of the chip
- Add device dependent initialization data
- Add access to eeprom for access to non-volatile backup data.
- Link to v4: https://lore.kernel.org/r/20250523-b4-gs101_max77759_fg-v4-0-b49904e35a34@uclouvain.be

Changes in v4:
- Make first patch standalone
- Separate MAX77759 defines from MAX1720x defines (Dimitri Fedrau)
- Inline device name property (Dimitri Fedrau)
- Separate MAX77759 capacity lsb logic from the MAX1720x capacity
  computation (Dimitri Fedrau)
- Use device_property_read_u32 instead of of_property_read_u32
  (Sebastian Reichel)
- Removed leftover debugs
- Move shunt-resistor-micro-ohms to out of allOf:if: (Krzysztof Kozlowski)
- Fix reg-names constraints
- Fix style errors
- Link to v3: https://lore.kernel.org/r/20250421-b4-gs101_max77759_fg-v3-0-50cd8caf9017@uclouvain.be

Changes in v3:
- Update base tree to avoid conflicts
- Fix capacity computation for max1720x
- Add separate properties for the max7759 to disable non-functional ones
- Take TASKPERIOD into account for voltage computation of max77759
- Simplify vcell computation (Dimitri Fedrau)
- Switch has_nvmem to bool and keep it only in chip_data (Dimitri Fedrau)
- Drop the yes_range from the write table (Sebastian Reichel)
- Add test_power_supply_properties.sh to cover letter (Sebastian Reichel)
- Switch back some changes to binding and actually use allOf:if: to
  restrict constraints (Krzysztof Kozlowski)
- Fix style errors
- Link to v2: https://lore.kernel.org/r/20250102-b4-gs101_max77759_fg-v2-0-87959abeb7ff@uclouvain.be

Changes in v2:
- Add fallback for voltage measurement (André Draszik)
- Add regmap for the max77759 (André Draszik)
- Add chip identification for the max77759 (André Draszik, Peter Griffin)
- Move RSense value to a devicetree property shunt-resistor-micro-ohms
  (Dimitri Fedrau, André Draszik)
- Use allOf:if to narrow binding per variant (Krzysztof Kozlowski)
- Remove binding example (Krzysztof Kozlowski)
- Change defconfig order to follow savedefconfig (Krzysztof Kozlowski)
- Fix style errors
- Link to v1: https://lore.kernel.org/r/20241202-b4-gs101_max77759_fg-v1-0-98d2fa7bfe30@uclouvain.be

tools/testing/selftests/power_supply/test_power_supply_properties.sh:
gs101-oriole:
  # Testing device max77759-fg
  ok 1 max77759-fg.exists
  ok 2 max77759-fg.uevent.NAME
  ok 3 max77759-fg.sysfs.type
  ok 4 max77759-fg.uevent.TYPE
  ok 5 max77759-fg.sysfs.usb_type # SKIP
  ok 6 max77759-fg.sysfs.online # SKIP
  # Reported: '1' ()
  ok 7 max77759-fg.sysfs.present
  ok 8 max77759-fg.sysfs.status # SKIP
  # Reported: '99' % ()
  ok 9 max77759-fg.sysfs.capacity
  ok 10 max77759-fg.sysfs.capacity_level # SKIP
  # Reported: 'MAX77759' ()
  ok 11 max77759-fg.sysfs.model_name
  # Reported: 'Maxim Integrated' ()
  ok 12 max77759-fg.sysfs.manufacturer
  ok 13 max77759-fg.sysfs.serial_number # SKIP
  ok 14 max77759-fg.sysfs.technology # SKIP
  ok 15 max77759-fg.sysfs.cycle_count # SKIP
  ok 16 max77759-fg.sysfs.scope # SKIP
  ok 17 max77759-fg.sysfs.input_current_limit # SKIP
  ok 18 max77759-fg.sysfs.input_voltage_limit # SKIP
  ok 19 max77759-fg.sysfs.voltage_now # SKIP
  ok 20 max77759-fg.sysfs.voltage_min # SKIP
  ok 21 max77759-fg.sysfs.voltage_max # SKIP
  ok 22 max77759-fg.sysfs.voltage_min_design # SKIP
  ok 23 max77759-fg.sysfs.voltage_max_design # SKIP
  # Reported: '1562' uA (1.562 mA)
  ok 24 max77759-fg.sysfs.current_now
  ok 25 max77759-fg.sysfs.current_max # SKIP
  ok 26 max77759-fg.sysfs.charge_now # SKIP
  # Reported: '4562000' uAh (4.562 Ah)
  ok 27 max77759-fg.sysfs.charge_full
  # Reported: '4524000' uAh (4.524 Ah)
  ok 28 max77759-fg.sysfs.charge_full_design
  ok 29 max77759-fg.sysfs.power_now # SKIP
  ok 30 max77759-fg.sysfs.energy_now # SKIP
  ok 31 max77759-fg.sysfs.energy_full # SKIP
  ok 32 max77759-fg.sysfs.energy_full_design # SKIP
  ok 33 max77759-fg.sysfs.energy_full_design # SKIP

  gs101-raven:
  # Testing device max77759-fg
  ok 1 max77759-fg.exists
  ok 2 max77759-fg.uevent.NAME
  ok 3 max77759-fg.sysfs.type
  ok 4 max77759-fg.uevent.TYPE
  ok 5 max77759-fg.sysfs.usb_type # SKIP
  ok 6 max77759-fg.sysfs.online # SKIP
  # Reported: '1' ()
  ok 7 max77759-fg.sysfs.present
  ok 8 max77759-fg.sysfs.status # SKIP
  # Reported: '100' % ()
  ok 9 max77759-fg.sysfs.capacity
  ok 10 max77759-fg.sysfs.capacity_level # SKIP
  # Reported: 'MAX77759' ()
  ok 11 max77759-fg.sysfs.model_name
  # Reported: 'Maxim Integrated' ()
  ok 12 max77759-fg.sysfs.manufacturer
  ok 13 max77759-fg.sysfs.serial_number # SKIP
  ok 14 max77759-fg.sysfs.technology # SKIP
  ok 15 max77759-fg.sysfs.cycle_count # SKIP
  ok 16 max77759-fg.sysfs.scope # SKIP
  ok 17 max77759-fg.sysfs.input_current_limit # SKIP
  ok 18 max77759-fg.sysfs.input_voltage_limit # SKIP
  ok 19 max77759-fg.sysfs.voltage_now # SKIP
  ok 20 max77759-fg.sysfs.voltage_min # SKIP
  ok 21 max77759-fg.sysfs.voltage_max # SKIP
  ok 22 max77759-fg.sysfs.voltage_min_design # SKIP
  ok 23 max77759-fg.sysfs.voltage_max_design # SKIP
  # Reported: '4375' uA (4.375 mA)
  ok 24 max77759-fg.sysfs.current_now
  ok 25 max77759-fg.sysfs.current_max # SKIP
  ok 26 max77759-fg.sysfs.charge_now # SKIP
  # Reported: '4676000' uAh (4.676 Ah)
  ok 27 max77759-fg.sysfs.charge_full
  # Reported: '4904000' uAh (4.904 Ah)
  ok 28 max77759-fg.sysfs.charge_full_design
  ok 29 max77759-fg.sysfs.power_now # SKIP
  ok 30 max77759-fg.sysfs.energy_now # SKIP
  ok 31 max77759-fg.sysfs.energy_full # SKIP
  ok 32 max77759-fg.sysfs.energy_full_design # SKIP
  ok 33 max77759-fg.sysfs.energy_full_design # SKIP

---
Thomas Antoine (2):
      power: supply: add support for MAX77759 fuel gauge
      dt-bindings: power: supply: add support for MAX77759 fuel gauge

 .../bindings/power/supply/maxim,max77759.yaml      |  78 +++
 drivers/power/supply/Kconfig                       |  14 +
 drivers/power/supply/Makefile                      |   1 +
 drivers/power/supply/max77759_battery.c            | 652 +++++++++++++++++++++
 4 files changed, 745 insertions(+)
---
base-commit: 590b221ed4256fd6c34d3dea77aa5bd6e741bbc1
change-id: 20241202-b4-gs101_max77759_fg-402e231a4b33

Best regards,
-- 
Thomas Antoine <t.antoine@uclouvain.be>



^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v6 1/2] power: supply: add support for MAX77759 fuel gauge
  2025-09-15 10:14 [PATCH v6 0/2] MAX77759 Fuel Gauge driver support Thomas Antoine via B4 Relay
@ 2025-09-15 10:14 ` Thomas Antoine via B4 Relay
  2025-09-16 16:51   ` Sebastian Reichel
  2025-09-15 10:14 ` [PATCH v6 2/2] dt-bindings: " Thomas Antoine via B4 Relay
  1 sibling, 1 reply; 10+ messages in thread
From: Thomas Antoine via B4 Relay @ 2025-09-15 10:14 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sebastian Reichel,
	Peter Griffin, André Draszik
  Cc: linux-kernel, linux-pm, devicetree, Thomas Antoine

From: Thomas Antoine <t.antoine@uclouvain.be>

The Maxim MAX77759 is a PMIC used in gs101-oriole and gs101-raven
(Google Pixel 6 and 6 Pro). It contains a fuel gauge on a separate
I2C address. Add basic support for this fuel gauge. The driver is
based on the driver for the MAX17201 and MAX17205 which also uses
the MAX M5 fuel gauge. There is a lot in common between the two
devices with some key differences. The main one is the lack of nvmem
in the fuel gauge of the MAX77759.

The initialization of the chip is very basic and mostly hardcoded.
Loading the model of the fuel gauge is not implemented here.
Values are extracted from available devices.

On both gs101-oriole and gs101-raven, the same EEPROM as for the
battery id is used to backup some of the state of the fuel gauge.
Use a standard nvmem binding to access this data. The CRC8 is
computed to allow to go from linux to a stock android without
apparent data corruption. If other devices using the MAX77759 are
found/created, a similar nvmem layout should be made or the driver
should be extended to support those devices.

The current, capacity, temperature and charge have all been tested.
The charge full design and capacity equal the ones seen on android,
the ratio between average charge and average current does predict
pretty accurately the time to empty under a constant workload and
temperature is coherent with the dynamic state of the device.

Health is not enabled as it always reports overheating. The time to
empty is wrong by about a factor 2. The voltage reporting is
correct when using VCELL (which reports the lowest voltage of all
cells) when considering that the device is connected to a single
cell. It could be enabled by either confirming that the device is
connected to a single cell or finding an alternative reporting mean.

Modifications have been made to it since but the regmap was
originally proposed by André Draszik in

Link: https://lore.kernel.org/all/d1bade77b5281c1de6b2ddcb4dbbd033e455a116.camel@linaro.org/

Signed-off-by: Thomas Antoine <t.antoine@uclouvain.be>

Reviewed-by: Peter Griffin <peter.griffin@linaro.org>
---
 drivers/power/supply/Kconfig            |  14 +
 drivers/power/supply/Makefile           |   1 +
 drivers/power/supply/max77759_battery.c | 652 ++++++++++++++++++++++++++++++++
 3 files changed, 667 insertions(+)

diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index 72f3b2b4d346df354475274b1c6b4ed047fdca1d..2d024fd3a45644d9f6d4e7c48ea98f4efe0c443d 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -473,6 +473,20 @@ config BATTERY_MAX1721X
 	  Say Y here to enable support for the MAX17211/MAX17215 standalone
 	  battery gas-gauge.
 
+config BATTERY_MAX77759
+	tristate "Maxim Integrated MAX77759 Fuel Gauge"
+	depends on I2C
+	select REGMAP_I2C
+	help
+	  Say yes to enable support for the Fuel gauge of the Maxim Integrated
+	  MAX77759. It is a companion Power Management IC for USB Type-C
+	  applications with Battery Charger, Fuel Gauge, temperature sensors,
+	  USB Type-C Port Controller (TCPC), NVMEM, and additional GPIO
+	  interfaces.
+
+	  To compile this driver as module, choose M here: the
+	  module will be called max77759_fg.
+
 config BATTERY_TWL4030_MADC
 	tristate "TWL4030 MADC battery driver"
 	depends on TWL4030_MADC
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
index 51e37e8bdeb35a5ee7a74cd86d4def9d900a542d..b96b5ce9c846c57ce71c23ede949d0e89c06a65a 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -59,6 +59,7 @@ obj-$(CONFIG_BATTERY_MAX17040)	+= max17040_battery.o
 obj-$(CONFIG_BATTERY_MAX17042)	+= max17042_battery.o
 obj-$(CONFIG_BATTERY_MAX1720X)	+= max1720x_battery.o
 obj-$(CONFIG_BATTERY_MAX1721X)	+= max1721x_battery.o
+obj-$(CONFIG_BATTERY_MAX77759)	+= max77759_battery.o
 obj-$(CONFIG_BATTERY_RT5033)	+= rt5033_battery.o
 obj-$(CONFIG_CHARGER_RT5033)	+= rt5033_charger.o
 obj-$(CONFIG_CHARGER_RT9455)	+= rt9455_charger.o
diff --git a/drivers/power/supply/max77759_battery.c b/drivers/power/supply/max77759_battery.c
new file mode 100644
index 0000000000000000000000000000000000000000..a49980241f8951705237d1f9c4328d0ff516c344
--- /dev/null
+++ b/drivers/power/supply/max77759_battery.c
@@ -0,0 +1,652 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Fuel gauge driver for Maxim 777759
+ *
+ * based on max1720x_battery.c
+ *
+ * Copyright (C) 2024 Liebherr-Electronics and Drives GmbH
+ */
+
+#include <linux/bitfield.h>
+#include <linux/crc8.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+
+#include <linux/unaligned.h>
+
+#define MAX77759_FG_CRC8_POLYNOMIAL  0x07
+DECLARE_CRC8_TABLE(max77759_fg_crc8_table);
+
+#define MAX77759_FG_STATUS		0x00	/* Status */
+#define MAX77759_FG_STATUS_POR		BIT(1)	/* Power-On Reset */
+#define MAX77759_FG_STATUS_BAT_ABSENT	BIT(3)	/* Battery absent */
+#define MAX77759_FG_REPCAP		0x05	/* Average capacity */
+#define MAX77759_FG_REPSOC		0x06	/* Percentage of charge */
+#define MAX77759_FG_TEMP		0x08	/* Temperature */
+#define MAX77759_FG_CURRENT		0x0A	/* Actual current */
+#define MAX77759_FG_AVG_CURRENT		0x0B	/* Average current */
+#define MAX77759_FG_FULL_CAP		0x10	/* Calculated full capacity */
+#define MAX77759_FG_QR_TABLE00		0x12
+#define MAX77759_FG_FULLSOCTHR		0x13
+#define MAX77759_FG_CYCLES		0x17
+#define MAX77759_FG_DESIGN_CAP		0x18	/* Design capacity */
+#define MAX77759_FG_CONFIG		0x1D
+#define MAX77759_FG_ICHGTERM		0x1E
+#define MAX77759_FG_DEV_NAME		0x21	/* Device name */
+#define MAX77759_FG_DEV_NAME_TYPE_MASK	GENMASK(15, 9)
+#define MAX77759_FG_DEV_NAME_TYPE	0x31
+#define MAX77759_FG_QR_TABLE10		0x22
+#define MAX77759_FG_FULLCAPNOM		0x23	/* Nominal full capacity */
+#define MAX77759_FG_LEARNCFG		0x28
+#define MAX77759_FG_FILTERCFG		0x29
+#define MAX77759_FG_RELAXCFG		0x2A
+#define MAX77759_FG_MISCCFG		0x2B
+#define MAX77759_FG_TGAIN		0x2C
+#define MAX77759_FG_TOFF		0x2D
+#define MAX77759_FG_CGAIN		0x2E
+#define MAX77759_FG_QR_TABLE20		0x32
+#define MAX77759_FG_FULLCAPREP		0x35	/* Reported full capacity */
+#define MAX77759_FG_RCOMP0		0x38
+#define MAX77759_FG_TEMPCO		0x39	/* Temperature Compensation*/
+#define MAX77759_FG_TASKPERIOD		0x3C
+#define MAX77759_FG_TASKPERIOD_175MS	0x1680
+#define MAX77759_FG_TASKPERIOD_351MS	0x2D00
+#define MAX77759_FG_QR_TABLE30		0x42
+#define MAX77759_FG_DQACC		0x45
+#define MAX77759_FG_DPACC		0x46
+#define MAX77759_FG_VFSOC0		0x48
+#define MAX77759_FG_CONVGCFG		0x49
+#define MAX77759_FG_COMMAND		0x60
+#define MAX77759_FG_COMMAND_LOCK_CONF	0x0000	/* Lock extra config */
+#define MAX77759_FG_COMMAND_UNLOCK_CONF	0x0080	/* Unlock extra config */
+#define MAX77759_FG_CV_MIXCAP		0xB6
+#define MAX77759_FG_CV_HALFTIME		0xB7
+#define MAX77759_FG_CURVE		0xB9
+#define MAX77759_FG_CONFIG2		0xBB
+#define MAX77759_FG_CONFIG2_OCVQEN	BIT(4)
+#define MAX77759_FG_CONFIG2_LDMDL	BIT(5)	/* Load model */
+#define MAX77759_FG_CONFIG2_DSOCEN	BIT(7)
+#define MAX77759_FG_VFSOC		0xFF
+
+static const char *const max77759_fg_manufacturer = "Maxim Integrated";
+static const char *const max77759_fg_model = "MAX77759";
+
+struct max77759_fg_device_info {
+	struct regmap *regmap;
+	int rsense;
+};
+
+/*
+ * Registers 0x80 up to 0xaf which contain the model for the fuel gauge
+ * algorithm are locked. They can be unlocked by writing 0x59 to 0x62
+ * and 0xc4 to 0x63. They should be enabled in the regmap if the driver
+ * is extended to manage the model.
+ */
+static const struct regmap_range max77759_fg_registers[] = {
+	regmap_reg_range(0x00, 0x4f),
+	regmap_reg_range(0x60, 0x60),
+	regmap_reg_range(0xb0, 0xbf),
+	regmap_reg_range(0xd0, 0xd0),
+	regmap_reg_range(0xdc, 0xdf),
+	regmap_reg_range(0xfb, 0xfb),
+	regmap_reg_range(0xff, 0xff),
+};
+
+static const struct regmap_range max77759_fg_ro_registers[] = {
+	regmap_reg_range(0x3d, 0x3d),
+	regmap_reg_range(0xfb, 0xfb),
+	regmap_reg_range(0xff, 0xff),
+};
+
+static const struct regmap_access_table max77759_fg_write_table = {
+	.no_ranges = max77759_fg_ro_registers,
+	.n_no_ranges = ARRAY_SIZE(max77759_fg_ro_registers),
+};
+
+static const struct regmap_access_table max77759_fg_rd_table = {
+	.yes_ranges = max77759_fg_registers,
+	.n_yes_ranges = ARRAY_SIZE(max77759_fg_registers),
+};
+
+static const struct regmap_config max77759_fg_regmap_cfg = {
+	.reg_bits = 8,
+	.val_bits = 16,
+	.max_register = 0xff,
+	.wr_table = &max77759_fg_write_table,
+	.rd_table = &max77759_fg_rd_table,
+	.val_format_endian = REGMAP_ENDIAN_LITTLE,
+	.cache_type = REGCACHE_NONE,
+};
+
+static const enum power_supply_property max77759_fg_battery_props[] = {
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_CHARGE_FULL,
+	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+	POWER_SUPPLY_PROP_CHARGE_AVG,
+	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+	POWER_SUPPLY_PROP_CURRENT_AVG,
+	POWER_SUPPLY_PROP_MODEL_NAME,
+	POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+struct max77759_fg_state_save {
+	u16 rcomp0;
+	u16 tempco;
+	u16 fullcaprep;
+	u16 cycles;
+	u16 fullcapnom;
+	u16 qrtable00;
+	u16 qrtable10;
+	u16 qrtable20;
+	u16 qrtable30;
+	u16 mixcap;
+	u16 halftime;
+	u8 crc;
+} __packed;
+
+/* Convert regs value to power_supply units */
+
+static int max77759_fg_percent_to_ps(unsigned int reg)
+{
+	return reg / 256;	/* in percent from 0 to 100 */
+}
+
+static int max77759_fg_capacity_to_ps(unsigned int reg,
+				      struct max77759_fg_device_info *info)
+{
+	return reg * (500000 / info->rsense);	/* in uAh */
+}
+
+static int max77759_fg_capacity_lsb(struct max77759_fg_device_info *info,
+				    unsigned int *lsb)
+{
+	unsigned int reg_task_period;
+	int ret;
+
+	ret = regmap_read(info->regmap, MAX77759_FG_TASKPERIOD,
+			  &reg_task_period);
+	if (ret < 0)
+		return ret;
+
+	switch (reg_task_period) {
+	case MAX77759_FG_TASKPERIOD_175MS:
+		*lsb = 1;
+		break;
+	case MAX77759_FG_TASKPERIOD_351MS:
+		*lsb = 2;
+		break;
+	default:
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+/*
+ * Current and temperature is signed values, so unsigned regs
+ * value must be converted to signed type
+ */
+
+static int max77759_fg_temperature_to_ps(unsigned int reg)
+{
+	int val = (int16_t)reg;
+
+	return val * 10 / 256; /* in tenths of deg. C */
+}
+
+/*
+ * Calculating current registers resolution:
+ *
+ * RSense stored in 10^-5 Ohm, so measurement voltage must be
+ * in 10^-11 Volts for get current in uA.
+ * 16 bit current reg fullscale +/-51.2mV is 102400 uV.
+ * So: 102400 / 65535 * 10^5 = 156252
+ */
+static int max77759_fg_current_to_voltage(unsigned int reg)
+{
+	int val = (int16_t)reg;
+
+	return val * 156252;
+}
+
+static int max77759_fg_battery_get_property(struct power_supply *psy,
+					    enum power_supply_property psp,
+					    union power_supply_propval *val)
+{
+	struct max77759_fg_device_info *info = power_supply_get_drvdata(psy);
+	unsigned int reg_val;
+	int ret = 0;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_PRESENT:
+		/*
+		 * POWER_SUPPLY_PROP_PRESENT will always readable via
+		 * sysfs interface. Value return 0 if battery not
+		 * present or inaccesable via I2C.
+		 */
+		ret = regmap_read(info->regmap, MAX77759_FG_STATUS, &reg_val);
+		if (ret < 0) {
+			val->intval = 0;
+			return 0;
+		}
+
+		val->intval = !FIELD_GET(MAX77759_FG_STATUS_BAT_ABSENT, reg_val);
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		ret = regmap_read(info->regmap, MAX77759_FG_REPSOC, &reg_val);
+		val->intval = max77759_fg_percent_to_ps(reg_val);
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+		ret = regmap_read(info->regmap, MAX77759_FG_DESIGN_CAP, &reg_val);
+		if (ret < 0)
+			return ret;
+
+		val->intval = max77759_fg_capacity_to_ps(reg_val, info);
+		ret = max77759_fg_capacity_lsb(info, &reg_val);
+		val->intval *= reg_val;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_AVG:
+		ret = regmap_read(info->regmap, MAX77759_FG_REPCAP, &reg_val);
+		if (ret < 0)
+			return ret;
+
+		val->intval = max77759_fg_capacity_to_ps(reg_val, info);
+		ret = max77759_fg_capacity_lsb(info, &reg_val);
+		val->intval *= reg_val;
+		break;
+	case POWER_SUPPLY_PROP_TEMP:
+		ret = regmap_read(info->regmap, MAX77759_FG_TEMP, &reg_val);
+		val->intval = max77759_fg_temperature_to_ps(reg_val);
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_NOW:
+		ret = regmap_read(info->regmap, MAX77759_FG_CURRENT, &reg_val);
+		val->intval = max77759_fg_current_to_voltage(reg_val) / info->rsense;
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_AVG:
+		ret = regmap_read(info->regmap, MAX77759_FG_AVG_CURRENT, &reg_val);
+		val->intval = max77759_fg_current_to_voltage(reg_val) / info->rsense;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_FULL:
+		ret = regmap_read(info->regmap, MAX77759_FG_FULL_CAP, &reg_val);
+		if (ret < 0)
+			return ret;
+
+		val->intval = max77759_fg_capacity_to_ps(reg_val, info);
+		ret = max77759_fg_capacity_lsb(info, &reg_val);
+		val->intval *= reg_val;
+		break;
+	case POWER_SUPPLY_PROP_MODEL_NAME:
+		ret = regmap_read(info->regmap, MAX77759_FG_DEV_NAME, &reg_val);
+		if (ret < 0)
+			return ret;
+
+		reg_val = FIELD_GET(MAX77759_FG_DEV_NAME_TYPE_MASK, reg_val);
+		if (reg_val == MAX77759_FG_DEV_NAME_TYPE)
+			val->strval = max77759_fg_model;
+		else
+			return -ENODEV;
+		break;
+	case POWER_SUPPLY_PROP_MANUFACTURER:
+		val->strval = max77759_fg_manufacturer;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+static int max77759_fg_init(struct device *dev,
+			    struct max77759_fg_device_info *info,
+			    struct power_supply *bat_psy)
+{
+	struct max77759_fg_state_save *state;
+	struct power_supply_battery_info *bat_info;
+	struct nvmem_cell *cell;
+	unsigned int val;
+	int ret;
+	size_t len;
+
+	ret = power_supply_get_battery_info(bat_psy, &bat_info);
+	if (ret)
+		return ret;
+
+	cell = devm_nvmem_cell_get(dev, "fg_state");
+	if (IS_ERR(cell))
+		return PTR_ERR(cell);
+
+	state = (struct max77759_fg_state_save *)nvmem_cell_read(cell, &len);
+	if (IS_ERR(state))
+		return PTR_ERR(state);
+	if (len != sizeof(struct max77759_fg_state_save)) {
+		ret = -EINVAL;
+		goto err_init;
+	}
+
+	ret = regmap_write(info->regmap, MAX77759_FG_REPCAP, 0x0000);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_RELAXCFG, 0x0839);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_COMMAND,
+			   MAX77759_FG_COMMAND_UNLOCK_CONF);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_read(info->regmap, MAX77759_FG_VFSOC, &val);
+	if (ret < 0)
+		goto err_init;
+	ret = regmap_write(info->regmap, MAX77759_FG_VFSOC0, val);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_LEARNCFG, 0x260E);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_CONFIG, 0x4217);
+	if (ret < 0)
+		goto err_init;
+
+	val = MAX77759_FG_CONFIG2_DSOCEN | MAX77759_FG_CONFIG2_OCVQEN;
+	ret = regmap_write(info->regmap, MAX77759_FG_CONFIG2, val);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_FULLSOCTHR, 0x5F00);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_FULLCAPREP,
+			   state->fullcaprep);
+	if (ret < 0)
+		goto err_init;
+
+	//Use an LSB of 2 because TASKPERIOD will be set to 351MS
+	val = bat_info->charge_full_design_uah * (info->rsense / 100) / 10000;
+	ret = regmap_write(info->regmap, MAX77759_FG_DESIGN_CAP, val);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_DPACC, 0x0C80);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_DQACC,
+			   state->fullcapnom >> 4);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_STATUS,
+			   MAX77759_FG_STATUS_POR);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_FULLCAPNOM,
+			   state->fullcapnom);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_QR_TABLE00,
+			   state->qrtable00);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_QR_TABLE10,
+			   state->qrtable10);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_QR_TABLE20,
+			   state->qrtable20);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_QR_TABLE30,
+			   state->qrtable30);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_RCOMP0, state->rcomp0);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_TEMPCO, state->tempco);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_TASKPERIOD,
+			   MAX77759_FG_TASKPERIOD_351MS);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_ICHGTERM,
+			   bat_info->charge_term_current_ua *
+			   info->rsense / 15625);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_TGAIN, 0xED51);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_TOFF, 0x1EBA);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_MISCCFG, 0x3870);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_CV_MIXCAP, state->mixcap);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_CV_HALFTIME,
+			   state->halftime);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_CONVGCFG, 0x2241);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_COMMAND,
+			   MAX77759_FG_COMMAND_LOCK_CONF);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_CURVE, 0x0014);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_FILTERCFG, 0xc623);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_CGAIN, 0x0400);
+	if (ret < 0)
+		goto err_init;
+
+	val = MAX77759_FG_CONFIG2_DSOCEN | MAX77759_FG_CONFIG2_OCVQEN;
+	val |= MAX77759_FG_CONFIG2_LDMDL;
+	ret = regmap_write(info->regmap, MAX77759_FG_CONFIG2, val);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_STATUS, 0x0000);
+	if (ret < 0)
+		goto err_init;
+
+	ret = regmap_write(info->regmap, MAX77759_FG_CYCLES, state->cycles);
+	if (ret < 0)
+		goto err_init;
+
+	kfree(state);
+	return 0;
+
+err_init:
+	kfree(state);
+	return ret;
+}
+
+static const struct power_supply_desc max77759_fg_bat_desc = {
+	.name = "max77759-fg",
+	.type = POWER_SUPPLY_TYPE_BATTERY,
+	.properties = max77759_fg_battery_props,
+	.num_properties = ARRAY_SIZE(max77759_fg_battery_props),
+	.get_property = max77759_fg_battery_get_property,
+};
+
+static int max77759_fg_backup_fg_state(struct device *dev,
+				       struct regmap *regmap)
+{
+	struct max77759_fg_state_save state;
+	struct nvmem_cell *cell;
+	int val;
+	int ret;
+
+	ret = regmap_read(regmap, MAX77759_FG_RCOMP0, &val);
+	if (ret < 0)
+		return ret;
+	state.rcomp0 = (u16)val;
+
+	ret = regmap_read(regmap, MAX77759_FG_TEMPCO, &val);
+	if (ret < 0)
+		return ret;
+	state.tempco = (u16)val;
+
+	ret = regmap_read(regmap, MAX77759_FG_FULLCAPREP, &val);
+	if (ret < 0)
+		return ret;
+	state.fullcaprep = (u16)val;
+
+	ret = regmap_read(regmap, MAX77759_FG_CYCLES, &val);
+	if (ret < 0)
+		return ret;
+	state.cycles = (u16)val;
+
+	ret = regmap_read(regmap, MAX77759_FG_FULLCAPNOM, &val);
+	if (ret < 0)
+		return ret;
+	state.fullcapnom = (u16)val;
+
+	ret = regmap_read(regmap, MAX77759_FG_QR_TABLE00, &val);
+	if (ret < 0)
+		return ret;
+	state.qrtable00 = (u16)val;
+
+	ret = regmap_read(regmap, MAX77759_FG_QR_TABLE10, &val);
+	if (ret < 0)
+		return ret;
+	state.qrtable10 = (u16)val;
+
+	ret = regmap_read(regmap, MAX77759_FG_QR_TABLE20, &val);
+	if (ret < 0)
+		return ret;
+	state.qrtable20 = (u16)val;
+
+	ret = regmap_read(regmap, MAX77759_FG_QR_TABLE30, &val);
+	if (ret < 0)
+		return ret;
+	state.qrtable30 = (u16)val;
+
+	ret = regmap_read(regmap, MAX77759_FG_CV_MIXCAP, &val);
+	if (ret < 0)
+		return ret;
+	state.mixcap = (u16)val;
+
+	ret = regmap_read(regmap, MAX77759_FG_CV_HALFTIME, &val);
+	if (ret < 0)
+		return ret;
+	state.halftime = (u16)val;
+
+	state.crc = crc8(max77759_fg_crc8_table, (u8 *)&state,
+			 sizeof(state) - sizeof(state.crc), CRC8_INIT_VALUE);
+
+	cell = devm_nvmem_cell_get(dev, "fg_state");
+	if (IS_ERR(cell))
+		return PTR_ERR(cell);
+	ret = nvmem_cell_write(cell, &state, sizeof(state));
+	if (ret < 0)
+		dev_err(dev, "Failed to write fg_state to NVMEM: %d\n", ret);
+
+	return ret;
+}
+
+static void max77759_fg_remove(struct i2c_client *client)
+{
+	struct max77759_fg_device_info *info = i2c_get_clientdata(client);
+
+	max77759_fg_backup_fg_state(&client->dev, info->regmap);
+}
+
+static int max77759_fg_probe(struct i2c_client *client)
+{
+	struct power_supply_config psy_cfg = {};
+	struct device *dev = &client->dev;
+	struct max77759_fg_device_info *info;
+	struct power_supply *bat;
+	int ret, val;
+
+	info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	psy_cfg.drv_data = info;
+	psy_cfg.fwnode = dev_fwnode(dev);
+
+	crc8_populate_msb(max77759_fg_crc8_table, MAX77759_FG_CRC8_POLYNOMIAL);
+
+	i2c_set_clientdata(client, info);
+
+	info->regmap = devm_regmap_init_i2c(client, &max77759_fg_regmap_cfg);
+	if (IS_ERR(info->regmap))
+		return dev_err_probe(dev, PTR_ERR(info->regmap),
+				     "regmap initialization failed\n");
+
+	ret = device_property_read_u32(dev, "shunt-resistor-micro-ohms", &val);
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "Failed to read RSense from devicetree\n");
+	info->rsense = val / 10;
+
+	bat = devm_power_supply_register(dev, &max77759_fg_bat_desc, &psy_cfg);
+	if (IS_ERR(bat))
+		return dev_err_probe(dev, PTR_ERR(bat),
+				     "Failed to register power supply\n");
+
+	ret = max77759_fg_init(dev, info, bat);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to initialize chip\n");
+
+	return 0;
+}
+
+static const struct of_device_id max77759_fg_of_match[] = {
+	{ .compatible = "maxim,max77759-fg" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, max77759_fg_of_match);
+
+static struct i2c_driver max77759_fg_i2c_driver = {
+	.driver = {
+		.name = "max77759_fg",
+		.of_match_table = max77759_fg_of_match,
+	},
+	.probe = max77759_fg_probe,
+	.remove = max77759_fg_remove,
+};
+module_i2c_driver(max77759_fg_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Thomas Antoine <t.antoine@uclouvain.be>");
+MODULE_DESCRIPTION("Maxim MAX77759 Fuel Gauge IC driver");

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v6 2/2] dt-bindings: power: supply: add support for MAX77759 fuel gauge
  2025-09-15 10:14 [PATCH v6 0/2] MAX77759 Fuel Gauge driver support Thomas Antoine via B4 Relay
  2025-09-15 10:14 ` [PATCH v6 1/2] power: supply: add support for MAX77759 fuel gauge Thomas Antoine via B4 Relay
@ 2025-09-15 10:14 ` Thomas Antoine via B4 Relay
  2025-09-15 17:31   ` Conor Dooley
  1 sibling, 1 reply; 10+ messages in thread
From: Thomas Antoine via B4 Relay @ 2025-09-15 10:14 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sebastian Reichel,
	Peter Griffin, André Draszik
  Cc: linux-kernel, linux-pm, devicetree, Thomas Antoine

From: Thomas Antoine <t.antoine@uclouvain.be>

The Maxim MAX77759 is a companion PMIC for USB Type-C. It contains
Battery Charger, Fuel Gauge, temperature sensors, USB Type-C Port
Controller (TCPC), NVMEM, and additional GPIO interfaces

Use max77759-fg compatible to avoid conflict with drivers for other
functions.

The battery node is used to pass the REPCAP and ICHGTERM values
needed for the initialization of the fuel gauge.

The nvmem cells are used to get initialization values and to backup
the learning and the number of cycles. It should work out of the box
with gs101-oriole and gs101-raven which were previously running
Android.

Signed-off-by: Thomas Antoine <t.antoine@uclouvain.be>
---
 .../bindings/power/supply/maxim,max77759.yaml      | 78 ++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/Documentation/devicetree/bindings/power/supply/maxim,max77759.yaml b/Documentation/devicetree/bindings/power/supply/maxim,max77759.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..4d45739fcaf26273ec57b60049d6d0421df38efb
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/maxim,max77759.yaml
@@ -0,0 +1,78 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/power/supply/maxim,max77759.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Maxim Integrated MAX77759 fuel gauge
+
+maintainers:
+  - Thomas Antoine <t.antoine@uclouvain.be>
+
+allOf:
+  - $ref: power-supply.yaml#
+
+properties:
+  compatible:
+    const: maxim,max77759-fg
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  shunt-resistor-micro-ohms:
+    description: The value of the current sense resistor in microohms.
+
+  monitored-battery:
+    description: |
+      The fuel gauge needs the following battery properties:
+      - charge-full-design-microamp-hours
+      - charge-term-current-microamp
+
+  nvmem-cells:
+    maxItems: 1
+    description: |
+      Saved fuel gauge state. This state will be used during the initialization
+      and saved on exit. It must be initialized beforehand.
+      Its layout must be composed of
+        - RCOMP0 (characterization of the open-circuit voltage)
+        - TCOMPO (temperature compensation information)
+        - FULLCAPREP (reported full capacity)
+        - QRTABLE00, QRTABLE10, QRTABLE20, QRTABLE30 (cell capacity information)
+        - cv_mixcap (remaining capacity of the cell without empty compensation)
+        - cv_halftime (time-to-full characterization time constant)
+      They must all be aligned on 2 bytes. A valid CRC8 checksum must
+      also be found at the end (polynomial x^8 + x^2 + x + 1).
+
+  nvmem-cell-names:
+    const: fg_state
+
+required:
+  - compatible
+  - reg
+  - shunt-resistor-micro-ohms
+  - monitored-battery
+  - nvmem-cells
+  - nvmem-cell-names
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    i2c {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      fuel-gauge@36 {
+        compatible = "maxim,max77759-fg";
+        reg = <0x36>;
+        interrupts-extended = <&gpa9 3 IRQ_TYPE_LEVEL_LOW>;
+        shunt-resistor-micro-ohms = <5000>;
+        monitored-battery = <&battery>;
+        nvmem-cells = <&fg_state>;
+        nvmem-cell-names = "fg_state";
+      };
+    };

-- 
2.51.0



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v6 2/2] dt-bindings: power: supply: add support for MAX77759 fuel gauge
  2025-09-15 10:14 ` [PATCH v6 2/2] dt-bindings: " Thomas Antoine via B4 Relay
@ 2025-09-15 17:31   ` Conor Dooley
  2025-09-18 12:36     ` Thomas Antoine
  0 siblings, 1 reply; 10+ messages in thread
From: Conor Dooley @ 2025-09-15 17:31 UTC (permalink / raw)
  To: t.antoine
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sebastian Reichel,
	Peter Griffin, André Draszik, linux-kernel, linux-pm,
	devicetree

[-- Attachment #1: Type: text/plain, Size: 3936 bytes --]

On Mon, Sep 15, 2025 at 12:14:11PM +0200, Thomas Antoine via B4 Relay wrote:
> From: Thomas Antoine <t.antoine@uclouvain.be>
> 
> The Maxim MAX77759 is a companion PMIC for USB Type-C. It contains
> Battery Charger, Fuel Gauge, temperature sensors, USB Type-C Port
> Controller (TCPC), NVMEM, and additional GPIO interfaces
> 
> Use max77759-fg compatible to avoid conflict with drivers for other
> functions.
> 
> The battery node is used to pass the REPCAP and ICHGTERM values
> needed for the initialization of the fuel gauge.
> 
> The nvmem cells are used to get initialization values and to backup
> the learning and the number of cycles. It should work out of the box
> with gs101-oriole and gs101-raven which were previously running
> Android.
> 
> Signed-off-by: Thomas Antoine <t.antoine@uclouvain.be>
> ---
>  .../bindings/power/supply/maxim,max77759.yaml      | 78 ++++++++++++++++++++++
>  1 file changed, 78 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/power/supply/maxim,max77759.yaml b/Documentation/devicetree/bindings/power/supply/maxim,max77759.yaml
> new file mode 100644
> index 0000000000000000000000000000000000000000..4d45739fcaf26273ec57b60049d6d0421df38efb
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/power/supply/maxim,max77759.yaml
> @@ -0,0 +1,78 @@
> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/power/supply/maxim,max77759.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Maxim Integrated MAX77759 fuel gauge
> +
> +maintainers:
> +  - Thomas Antoine <t.antoine@uclouvain.be>
> +
> +allOf:
> +  - $ref: power-supply.yaml#
> +
> +properties:
> +  compatible:
> +    const: maxim,max77759-fg

Compatible doesn't match the filename, why?
I assume the "fg" is fuel-gauge, but can this device be anything else?

> +
> +  reg:
> +    maxItems: 1
> +
> +  interrupts:
> +    maxItems: 1
> +
> +  shunt-resistor-micro-ohms:
> +    description: The value of the current sense resistor in microohms.
> +
> +  monitored-battery:
> +    description: |
> +      The fuel gauge needs the following battery properties:
> +      - charge-full-design-microamp-hours
> +      - charge-term-current-microamp
> +
> +  nvmem-cells:
> +    maxItems: 1
> +    description: |
> +      Saved fuel gauge state. This state will be used during the initialization
> +      and saved on exit. It must be initialized beforehand.
> +      Its layout must be composed of
> +        - RCOMP0 (characterization of the open-circuit voltage)
> +        - TCOMPO (temperature compensation information)
> +        - FULLCAPREP (reported full capacity)
> +        - QRTABLE00, QRTABLE10, QRTABLE20, QRTABLE30 (cell capacity information)
> +        - cv_mixcap (remaining capacity of the cell without empty compensation)
> +        - cv_halftime (time-to-full characterization time constant)
> +      They must all be aligned on 2 bytes. A valid CRC8 checksum must
> +      also be found at the end (polynomial x^8 + x^2 + x + 1).
> +
> +  nvmem-cell-names:
> +    const: fg_state
> +
> +required:
> +  - compatible
> +  - reg
> +  - shunt-resistor-micro-ohms
> +  - monitored-battery
> +  - nvmem-cells
> +  - nvmem-cell-names
> +
> +unevaluatedProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/interrupt-controller/irq.h>
> +    i2c {
> +      #address-cells = <1>;
> +      #size-cells = <0>;
> +
> +      fuel-gauge@36 {
> +        compatible = "maxim,max77759-fg";
> +        reg = <0x36>;
> +        interrupts-extended = <&gpa9 3 IRQ_TYPE_LEVEL_LOW>;
> +        shunt-resistor-micro-ohms = <5000>;
> +        monitored-battery = <&battery>;
> +        nvmem-cells = <&fg_state>;
> +        nvmem-cell-names = "fg_state";
> +      };
> +    };
> 
> -- 
> 2.51.0
> 
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v6 1/2] power: supply: add support for MAX77759 fuel gauge
  2025-09-15 10:14 ` [PATCH v6 1/2] power: supply: add support for MAX77759 fuel gauge Thomas Antoine via B4 Relay
@ 2025-09-16 16:51   ` Sebastian Reichel
  0 siblings, 0 replies; 10+ messages in thread
From: Sebastian Reichel @ 2025-09-16 16:51 UTC (permalink / raw)
  To: t.antoine
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Peter Griffin,
	André Draszik, linux-kernel, linux-pm, devicetree

[-- Attachment #1: Type: text/plain, Size: 25010 bytes --]

Hi,

On Mon, Sep 15, 2025 at 12:14:10PM +0200, Thomas Antoine via B4 Relay wrote:
> From: Thomas Antoine <t.antoine@uclouvain.be>
> 
> The Maxim MAX77759 is a PMIC used in gs101-oriole and gs101-raven
> (Google Pixel 6 and 6 Pro). It contains a fuel gauge on a separate
> I2C address. Add basic support for this fuel gauge. The driver is
> based on the driver for the MAX17201 and MAX17205 which also uses
> the MAX M5 fuel gauge. There is a lot in common between the two
> devices with some key differences. The main one is the lack of nvmem
> in the fuel gauge of the MAX77759.
> 
> The initialization of the chip is very basic and mostly hardcoded.
> Loading the model of the fuel gauge is not implemented here.
> Values are extracted from available devices.
> 
> On both gs101-oriole and gs101-raven, the same EEPROM as for the
> battery id is used to backup some of the state of the fuel gauge.
> Use a standard nvmem binding to access this data. The CRC8 is
> computed to allow to go from linux to a stock android without
> apparent data corruption. If other devices using the MAX77759 are
> found/created, a similar nvmem layout should be made or the driver
> should be extended to support those devices.
> 
> The current, capacity, temperature and charge have all been tested.
> The charge full design and capacity equal the ones seen on android,
> the ratio between average charge and average current does predict
> pretty accurately the time to empty under a constant workload and
> temperature is coherent with the dynamic state of the device.
> 
> Health is not enabled as it always reports overheating. The time to
> empty is wrong by about a factor 2. The voltage reporting is
> correct when using VCELL (which reports the lowest voltage of all
> cells) when considering that the device is connected to a single
> cell. It could be enabled by either confirming that the device is
> connected to a single cell or finding an alternative reporting mean.
> 
> Modifications have been made to it since but the regmap was
> originally proposed by André Draszik in
> 
> Link: https://lore.kernel.org/all/d1bade77b5281c1de6b2ddcb4dbbd033e455a116.camel@linaro.org/
> Signed-off-by: Thomas Antoine <t.antoine@uclouvain.be>
> Reviewed-by: Peter Griffin <peter.griffin@linaro.org>
> ---

Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>

Greetings,

-- Sebastian

>  drivers/power/supply/Kconfig            |  14 +
>  drivers/power/supply/Makefile           |   1 +
>  drivers/power/supply/max77759_battery.c | 652 ++++++++++++++++++++++++++++++++
>  3 files changed, 667 insertions(+)
> 
> diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
> index 72f3b2b4d346df354475274b1c6b4ed047fdca1d..2d024fd3a45644d9f6d4e7c48ea98f4efe0c443d 100644
> --- a/drivers/power/supply/Kconfig
> +++ b/drivers/power/supply/Kconfig
> @@ -473,6 +473,20 @@ config BATTERY_MAX1721X
>  	  Say Y here to enable support for the MAX17211/MAX17215 standalone
>  	  battery gas-gauge.
>  
> +config BATTERY_MAX77759
> +	tristate "Maxim Integrated MAX77759 Fuel Gauge"
> +	depends on I2C
> +	select REGMAP_I2C
> +	help
> +	  Say yes to enable support for the Fuel gauge of the Maxim Integrated
> +	  MAX77759. It is a companion Power Management IC for USB Type-C
> +	  applications with Battery Charger, Fuel Gauge, temperature sensors,
> +	  USB Type-C Port Controller (TCPC), NVMEM, and additional GPIO
> +	  interfaces.
> +
> +	  To compile this driver as module, choose M here: the
> +	  module will be called max77759_fg.
> +
>  config BATTERY_TWL4030_MADC
>  	tristate "TWL4030 MADC battery driver"
>  	depends on TWL4030_MADC
> diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
> index 51e37e8bdeb35a5ee7a74cd86d4def9d900a542d..b96b5ce9c846c57ce71c23ede949d0e89c06a65a 100644
> --- a/drivers/power/supply/Makefile
> +++ b/drivers/power/supply/Makefile
> @@ -59,6 +59,7 @@ obj-$(CONFIG_BATTERY_MAX17040)	+= max17040_battery.o
>  obj-$(CONFIG_BATTERY_MAX17042)	+= max17042_battery.o
>  obj-$(CONFIG_BATTERY_MAX1720X)	+= max1720x_battery.o
>  obj-$(CONFIG_BATTERY_MAX1721X)	+= max1721x_battery.o
> +obj-$(CONFIG_BATTERY_MAX77759)	+= max77759_battery.o
>  obj-$(CONFIG_BATTERY_RT5033)	+= rt5033_battery.o
>  obj-$(CONFIG_CHARGER_RT5033)	+= rt5033_charger.o
>  obj-$(CONFIG_CHARGER_RT9455)	+= rt9455_charger.o
> diff --git a/drivers/power/supply/max77759_battery.c b/drivers/power/supply/max77759_battery.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..a49980241f8951705237d1f9c4328d0ff516c344
> --- /dev/null
> +++ b/drivers/power/supply/max77759_battery.c
> @@ -0,0 +1,652 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Fuel gauge driver for Maxim 777759
> + *
> + * based on max1720x_battery.c
> + *
> + * Copyright (C) 2024 Liebherr-Electronics and Drives GmbH
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/crc8.h>
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/nvmem-consumer.h>
> +#include <linux/power_supply.h>
> +#include <linux/regmap.h>
> +
> +#include <linux/unaligned.h>
> +
> +#define MAX77759_FG_CRC8_POLYNOMIAL  0x07
> +DECLARE_CRC8_TABLE(max77759_fg_crc8_table);
> +
> +#define MAX77759_FG_STATUS		0x00	/* Status */
> +#define MAX77759_FG_STATUS_POR		BIT(1)	/* Power-On Reset */
> +#define MAX77759_FG_STATUS_BAT_ABSENT	BIT(3)	/* Battery absent */
> +#define MAX77759_FG_REPCAP		0x05	/* Average capacity */
> +#define MAX77759_FG_REPSOC		0x06	/* Percentage of charge */
> +#define MAX77759_FG_TEMP		0x08	/* Temperature */
> +#define MAX77759_FG_CURRENT		0x0A	/* Actual current */
> +#define MAX77759_FG_AVG_CURRENT		0x0B	/* Average current */
> +#define MAX77759_FG_FULL_CAP		0x10	/* Calculated full capacity */
> +#define MAX77759_FG_QR_TABLE00		0x12
> +#define MAX77759_FG_FULLSOCTHR		0x13
> +#define MAX77759_FG_CYCLES		0x17
> +#define MAX77759_FG_DESIGN_CAP		0x18	/* Design capacity */
> +#define MAX77759_FG_CONFIG		0x1D
> +#define MAX77759_FG_ICHGTERM		0x1E
> +#define MAX77759_FG_DEV_NAME		0x21	/* Device name */
> +#define MAX77759_FG_DEV_NAME_TYPE_MASK	GENMASK(15, 9)
> +#define MAX77759_FG_DEV_NAME_TYPE	0x31
> +#define MAX77759_FG_QR_TABLE10		0x22
> +#define MAX77759_FG_FULLCAPNOM		0x23	/* Nominal full capacity */
> +#define MAX77759_FG_LEARNCFG		0x28
> +#define MAX77759_FG_FILTERCFG		0x29
> +#define MAX77759_FG_RELAXCFG		0x2A
> +#define MAX77759_FG_MISCCFG		0x2B
> +#define MAX77759_FG_TGAIN		0x2C
> +#define MAX77759_FG_TOFF		0x2D
> +#define MAX77759_FG_CGAIN		0x2E
> +#define MAX77759_FG_QR_TABLE20		0x32
> +#define MAX77759_FG_FULLCAPREP		0x35	/* Reported full capacity */
> +#define MAX77759_FG_RCOMP0		0x38
> +#define MAX77759_FG_TEMPCO		0x39	/* Temperature Compensation*/
> +#define MAX77759_FG_TASKPERIOD		0x3C
> +#define MAX77759_FG_TASKPERIOD_175MS	0x1680
> +#define MAX77759_FG_TASKPERIOD_351MS	0x2D00
> +#define MAX77759_FG_QR_TABLE30		0x42
> +#define MAX77759_FG_DQACC		0x45
> +#define MAX77759_FG_DPACC		0x46
> +#define MAX77759_FG_VFSOC0		0x48
> +#define MAX77759_FG_CONVGCFG		0x49
> +#define MAX77759_FG_COMMAND		0x60
> +#define MAX77759_FG_COMMAND_LOCK_CONF	0x0000	/* Lock extra config */
> +#define MAX77759_FG_COMMAND_UNLOCK_CONF	0x0080	/* Unlock extra config */
> +#define MAX77759_FG_CV_MIXCAP		0xB6
> +#define MAX77759_FG_CV_HALFTIME		0xB7
> +#define MAX77759_FG_CURVE		0xB9
> +#define MAX77759_FG_CONFIG2		0xBB
> +#define MAX77759_FG_CONFIG2_OCVQEN	BIT(4)
> +#define MAX77759_FG_CONFIG2_LDMDL	BIT(5)	/* Load model */
> +#define MAX77759_FG_CONFIG2_DSOCEN	BIT(7)
> +#define MAX77759_FG_VFSOC		0xFF
> +
> +static const char *const max77759_fg_manufacturer = "Maxim Integrated";
> +static const char *const max77759_fg_model = "MAX77759";
> +
> +struct max77759_fg_device_info {
> +	struct regmap *regmap;
> +	int rsense;
> +};
> +
> +/*
> + * Registers 0x80 up to 0xaf which contain the model for the fuel gauge
> + * algorithm are locked. They can be unlocked by writing 0x59 to 0x62
> + * and 0xc4 to 0x63. They should be enabled in the regmap if the driver
> + * is extended to manage the model.
> + */
> +static const struct regmap_range max77759_fg_registers[] = {
> +	regmap_reg_range(0x00, 0x4f),
> +	regmap_reg_range(0x60, 0x60),
> +	regmap_reg_range(0xb0, 0xbf),
> +	regmap_reg_range(0xd0, 0xd0),
> +	regmap_reg_range(0xdc, 0xdf),
> +	regmap_reg_range(0xfb, 0xfb),
> +	regmap_reg_range(0xff, 0xff),
> +};
> +
> +static const struct regmap_range max77759_fg_ro_registers[] = {
> +	regmap_reg_range(0x3d, 0x3d),
> +	regmap_reg_range(0xfb, 0xfb),
> +	regmap_reg_range(0xff, 0xff),
> +};
> +
> +static const struct regmap_access_table max77759_fg_write_table = {
> +	.no_ranges = max77759_fg_ro_registers,
> +	.n_no_ranges = ARRAY_SIZE(max77759_fg_ro_registers),
> +};
> +
> +static const struct regmap_access_table max77759_fg_rd_table = {
> +	.yes_ranges = max77759_fg_registers,
> +	.n_yes_ranges = ARRAY_SIZE(max77759_fg_registers),
> +};
> +
> +static const struct regmap_config max77759_fg_regmap_cfg = {
> +	.reg_bits = 8,
> +	.val_bits = 16,
> +	.max_register = 0xff,
> +	.wr_table = &max77759_fg_write_table,
> +	.rd_table = &max77759_fg_rd_table,
> +	.val_format_endian = REGMAP_ENDIAN_LITTLE,
> +	.cache_type = REGCACHE_NONE,
> +};
> +
> +static const enum power_supply_property max77759_fg_battery_props[] = {
> +	POWER_SUPPLY_PROP_PRESENT,
> +	POWER_SUPPLY_PROP_CAPACITY,
> +	POWER_SUPPLY_PROP_CHARGE_FULL,
> +	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
> +	POWER_SUPPLY_PROP_CHARGE_AVG,
> +	POWER_SUPPLY_PROP_TEMP,
> +	POWER_SUPPLY_PROP_CURRENT_NOW,
> +	POWER_SUPPLY_PROP_CURRENT_AVG,
> +	POWER_SUPPLY_PROP_MODEL_NAME,
> +	POWER_SUPPLY_PROP_MANUFACTURER,
> +};
> +
> +struct max77759_fg_state_save {
> +	u16 rcomp0;
> +	u16 tempco;
> +	u16 fullcaprep;
> +	u16 cycles;
> +	u16 fullcapnom;
> +	u16 qrtable00;
> +	u16 qrtable10;
> +	u16 qrtable20;
> +	u16 qrtable30;
> +	u16 mixcap;
> +	u16 halftime;
> +	u8 crc;
> +} __packed;
> +
> +/* Convert regs value to power_supply units */
> +
> +static int max77759_fg_percent_to_ps(unsigned int reg)
> +{
> +	return reg / 256;	/* in percent from 0 to 100 */
> +}
> +
> +static int max77759_fg_capacity_to_ps(unsigned int reg,
> +				      struct max77759_fg_device_info *info)
> +{
> +	return reg * (500000 / info->rsense);	/* in uAh */
> +}
> +
> +static int max77759_fg_capacity_lsb(struct max77759_fg_device_info *info,
> +				    unsigned int *lsb)
> +{
> +	unsigned int reg_task_period;
> +	int ret;
> +
> +	ret = regmap_read(info->regmap, MAX77759_FG_TASKPERIOD,
> +			  &reg_task_period);
> +	if (ret < 0)
> +		return ret;
> +
> +	switch (reg_task_period) {
> +	case MAX77759_FG_TASKPERIOD_175MS:
> +		*lsb = 1;
> +		break;
> +	case MAX77759_FG_TASKPERIOD_351MS:
> +		*lsb = 2;
> +		break;
> +	default:
> +		return -ENODEV;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * Current and temperature is signed values, so unsigned regs
> + * value must be converted to signed type
> + */
> +
> +static int max77759_fg_temperature_to_ps(unsigned int reg)
> +{
> +	int val = (int16_t)reg;
> +
> +	return val * 10 / 256; /* in tenths of deg. C */
> +}
> +
> +/*
> + * Calculating current registers resolution:
> + *
> + * RSense stored in 10^-5 Ohm, so measurement voltage must be
> + * in 10^-11 Volts for get current in uA.
> + * 16 bit current reg fullscale +/-51.2mV is 102400 uV.
> + * So: 102400 / 65535 * 10^5 = 156252
> + */
> +static int max77759_fg_current_to_voltage(unsigned int reg)
> +{
> +	int val = (int16_t)reg;
> +
> +	return val * 156252;
> +}
> +
> +static int max77759_fg_battery_get_property(struct power_supply *psy,
> +					    enum power_supply_property psp,
> +					    union power_supply_propval *val)
> +{
> +	struct max77759_fg_device_info *info = power_supply_get_drvdata(psy);
> +	unsigned int reg_val;
> +	int ret = 0;
> +
> +	switch (psp) {
> +	case POWER_SUPPLY_PROP_PRESENT:
> +		/*
> +		 * POWER_SUPPLY_PROP_PRESENT will always readable via
> +		 * sysfs interface. Value return 0 if battery not
> +		 * present or inaccesable via I2C.
> +		 */
> +		ret = regmap_read(info->regmap, MAX77759_FG_STATUS, &reg_val);
> +		if (ret < 0) {
> +			val->intval = 0;
> +			return 0;
> +		}
> +
> +		val->intval = !FIELD_GET(MAX77759_FG_STATUS_BAT_ABSENT, reg_val);
> +		break;
> +	case POWER_SUPPLY_PROP_CAPACITY:
> +		ret = regmap_read(info->regmap, MAX77759_FG_REPSOC, &reg_val);
> +		val->intval = max77759_fg_percent_to_ps(reg_val);
> +		break;
> +	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
> +		ret = regmap_read(info->regmap, MAX77759_FG_DESIGN_CAP, &reg_val);
> +		if (ret < 0)
> +			return ret;
> +
> +		val->intval = max77759_fg_capacity_to_ps(reg_val, info);
> +		ret = max77759_fg_capacity_lsb(info, &reg_val);
> +		val->intval *= reg_val;
> +		break;
> +	case POWER_SUPPLY_PROP_CHARGE_AVG:
> +		ret = regmap_read(info->regmap, MAX77759_FG_REPCAP, &reg_val);
> +		if (ret < 0)
> +			return ret;
> +
> +		val->intval = max77759_fg_capacity_to_ps(reg_val, info);
> +		ret = max77759_fg_capacity_lsb(info, &reg_val);
> +		val->intval *= reg_val;
> +		break;
> +	case POWER_SUPPLY_PROP_TEMP:
> +		ret = regmap_read(info->regmap, MAX77759_FG_TEMP, &reg_val);
> +		val->intval = max77759_fg_temperature_to_ps(reg_val);
> +		break;
> +	case POWER_SUPPLY_PROP_CURRENT_NOW:
> +		ret = regmap_read(info->regmap, MAX77759_FG_CURRENT, &reg_val);
> +		val->intval = max77759_fg_current_to_voltage(reg_val) / info->rsense;
> +		break;
> +	case POWER_SUPPLY_PROP_CURRENT_AVG:
> +		ret = regmap_read(info->regmap, MAX77759_FG_AVG_CURRENT, &reg_val);
> +		val->intval = max77759_fg_current_to_voltage(reg_val) / info->rsense;
> +		break;
> +	case POWER_SUPPLY_PROP_CHARGE_FULL:
> +		ret = regmap_read(info->regmap, MAX77759_FG_FULL_CAP, &reg_val);
> +		if (ret < 0)
> +			return ret;
> +
> +		val->intval = max77759_fg_capacity_to_ps(reg_val, info);
> +		ret = max77759_fg_capacity_lsb(info, &reg_val);
> +		val->intval *= reg_val;
> +		break;
> +	case POWER_SUPPLY_PROP_MODEL_NAME:
> +		ret = regmap_read(info->regmap, MAX77759_FG_DEV_NAME, &reg_val);
> +		if (ret < 0)
> +			return ret;
> +
> +		reg_val = FIELD_GET(MAX77759_FG_DEV_NAME_TYPE_MASK, reg_val);
> +		if (reg_val == MAX77759_FG_DEV_NAME_TYPE)
> +			val->strval = max77759_fg_model;
> +		else
> +			return -ENODEV;
> +		break;
> +	case POWER_SUPPLY_PROP_MANUFACTURER:
> +		val->strval = max77759_fg_manufacturer;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return ret;
> +}
> +
> +static int max77759_fg_init(struct device *dev,
> +			    struct max77759_fg_device_info *info,
> +			    struct power_supply *bat_psy)
> +{
> +	struct max77759_fg_state_save *state;
> +	struct power_supply_battery_info *bat_info;
> +	struct nvmem_cell *cell;
> +	unsigned int val;
> +	int ret;
> +	size_t len;
> +
> +	ret = power_supply_get_battery_info(bat_psy, &bat_info);
> +	if (ret)
> +		return ret;
> +
> +	cell = devm_nvmem_cell_get(dev, "fg_state");
> +	if (IS_ERR(cell))
> +		return PTR_ERR(cell);
> +
> +	state = (struct max77759_fg_state_save *)nvmem_cell_read(cell, &len);
> +	if (IS_ERR(state))
> +		return PTR_ERR(state);
> +	if (len != sizeof(struct max77759_fg_state_save)) {
> +		ret = -EINVAL;
> +		goto err_init;
> +	}
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_REPCAP, 0x0000);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_RELAXCFG, 0x0839);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_COMMAND,
> +			   MAX77759_FG_COMMAND_UNLOCK_CONF);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_read(info->regmap, MAX77759_FG_VFSOC, &val);
> +	if (ret < 0)
> +		goto err_init;
> +	ret = regmap_write(info->regmap, MAX77759_FG_VFSOC0, val);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_LEARNCFG, 0x260E);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_CONFIG, 0x4217);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	val = MAX77759_FG_CONFIG2_DSOCEN | MAX77759_FG_CONFIG2_OCVQEN;
> +	ret = regmap_write(info->regmap, MAX77759_FG_CONFIG2, val);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_FULLSOCTHR, 0x5F00);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_FULLCAPREP,
> +			   state->fullcaprep);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	//Use an LSB of 2 because TASKPERIOD will be set to 351MS
> +	val = bat_info->charge_full_design_uah * (info->rsense / 100) / 10000;
> +	ret = regmap_write(info->regmap, MAX77759_FG_DESIGN_CAP, val);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_DPACC, 0x0C80);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_DQACC,
> +			   state->fullcapnom >> 4);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_STATUS,
> +			   MAX77759_FG_STATUS_POR);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_FULLCAPNOM,
> +			   state->fullcapnom);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_QR_TABLE00,
> +			   state->qrtable00);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_QR_TABLE10,
> +			   state->qrtable10);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_QR_TABLE20,
> +			   state->qrtable20);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_QR_TABLE30,
> +			   state->qrtable30);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_RCOMP0, state->rcomp0);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_TEMPCO, state->tempco);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_TASKPERIOD,
> +			   MAX77759_FG_TASKPERIOD_351MS);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_ICHGTERM,
> +			   bat_info->charge_term_current_ua *
> +			   info->rsense / 15625);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_TGAIN, 0xED51);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_TOFF, 0x1EBA);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_MISCCFG, 0x3870);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_CV_MIXCAP, state->mixcap);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_CV_HALFTIME,
> +			   state->halftime);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_CONVGCFG, 0x2241);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_COMMAND,
> +			   MAX77759_FG_COMMAND_LOCK_CONF);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_CURVE, 0x0014);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_FILTERCFG, 0xc623);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_CGAIN, 0x0400);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	val = MAX77759_FG_CONFIG2_DSOCEN | MAX77759_FG_CONFIG2_OCVQEN;
> +	val |= MAX77759_FG_CONFIG2_LDMDL;
> +	ret = regmap_write(info->regmap, MAX77759_FG_CONFIG2, val);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_STATUS, 0x0000);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	ret = regmap_write(info->regmap, MAX77759_FG_CYCLES, state->cycles);
> +	if (ret < 0)
> +		goto err_init;
> +
> +	kfree(state);
> +	return 0;
> +
> +err_init:
> +	kfree(state);
> +	return ret;
> +}
> +
> +static const struct power_supply_desc max77759_fg_bat_desc = {
> +	.name = "max77759-fg",
> +	.type = POWER_SUPPLY_TYPE_BATTERY,
> +	.properties = max77759_fg_battery_props,
> +	.num_properties = ARRAY_SIZE(max77759_fg_battery_props),
> +	.get_property = max77759_fg_battery_get_property,
> +};
> +
> +static int max77759_fg_backup_fg_state(struct device *dev,
> +				       struct regmap *regmap)
> +{
> +	struct max77759_fg_state_save state;
> +	struct nvmem_cell *cell;
> +	int val;
> +	int ret;
> +
> +	ret = regmap_read(regmap, MAX77759_FG_RCOMP0, &val);
> +	if (ret < 0)
> +		return ret;
> +	state.rcomp0 = (u16)val;
> +
> +	ret = regmap_read(regmap, MAX77759_FG_TEMPCO, &val);
> +	if (ret < 0)
> +		return ret;
> +	state.tempco = (u16)val;
> +
> +	ret = regmap_read(regmap, MAX77759_FG_FULLCAPREP, &val);
> +	if (ret < 0)
> +		return ret;
> +	state.fullcaprep = (u16)val;
> +
> +	ret = regmap_read(regmap, MAX77759_FG_CYCLES, &val);
> +	if (ret < 0)
> +		return ret;
> +	state.cycles = (u16)val;
> +
> +	ret = regmap_read(regmap, MAX77759_FG_FULLCAPNOM, &val);
> +	if (ret < 0)
> +		return ret;
> +	state.fullcapnom = (u16)val;
> +
> +	ret = regmap_read(regmap, MAX77759_FG_QR_TABLE00, &val);
> +	if (ret < 0)
> +		return ret;
> +	state.qrtable00 = (u16)val;
> +
> +	ret = regmap_read(regmap, MAX77759_FG_QR_TABLE10, &val);
> +	if (ret < 0)
> +		return ret;
> +	state.qrtable10 = (u16)val;
> +
> +	ret = regmap_read(regmap, MAX77759_FG_QR_TABLE20, &val);
> +	if (ret < 0)
> +		return ret;
> +	state.qrtable20 = (u16)val;
> +
> +	ret = regmap_read(regmap, MAX77759_FG_QR_TABLE30, &val);
> +	if (ret < 0)
> +		return ret;
> +	state.qrtable30 = (u16)val;
> +
> +	ret = regmap_read(regmap, MAX77759_FG_CV_MIXCAP, &val);
> +	if (ret < 0)
> +		return ret;
> +	state.mixcap = (u16)val;
> +
> +	ret = regmap_read(regmap, MAX77759_FG_CV_HALFTIME, &val);
> +	if (ret < 0)
> +		return ret;
> +	state.halftime = (u16)val;
> +
> +	state.crc = crc8(max77759_fg_crc8_table, (u8 *)&state,
> +			 sizeof(state) - sizeof(state.crc), CRC8_INIT_VALUE);
> +
> +	cell = devm_nvmem_cell_get(dev, "fg_state");
> +	if (IS_ERR(cell))
> +		return PTR_ERR(cell);
> +	ret = nvmem_cell_write(cell, &state, sizeof(state));
> +	if (ret < 0)
> +		dev_err(dev, "Failed to write fg_state to NVMEM: %d\n", ret);
> +
> +	return ret;
> +}
> +
> +static void max77759_fg_remove(struct i2c_client *client)
> +{
> +	struct max77759_fg_device_info *info = i2c_get_clientdata(client);
> +
> +	max77759_fg_backup_fg_state(&client->dev, info->regmap);
> +}
> +
> +static int max77759_fg_probe(struct i2c_client *client)
> +{
> +	struct power_supply_config psy_cfg = {};
> +	struct device *dev = &client->dev;
> +	struct max77759_fg_device_info *info;
> +	struct power_supply *bat;
> +	int ret, val;
> +
> +	info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
> +	if (!info)
> +		return -ENOMEM;
> +
> +	psy_cfg.drv_data = info;
> +	psy_cfg.fwnode = dev_fwnode(dev);
> +
> +	crc8_populate_msb(max77759_fg_crc8_table, MAX77759_FG_CRC8_POLYNOMIAL);
> +
> +	i2c_set_clientdata(client, info);
> +
> +	info->regmap = devm_regmap_init_i2c(client, &max77759_fg_regmap_cfg);
> +	if (IS_ERR(info->regmap))
> +		return dev_err_probe(dev, PTR_ERR(info->regmap),
> +				     "regmap initialization failed\n");
> +
> +	ret = device_property_read_u32(dev, "shunt-resistor-micro-ohms", &val);
> +	if (ret)
> +		return dev_err_probe(dev, ret,
> +				     "Failed to read RSense from devicetree\n");
> +	info->rsense = val / 10;
> +
> +	bat = devm_power_supply_register(dev, &max77759_fg_bat_desc, &psy_cfg);
> +	if (IS_ERR(bat))
> +		return dev_err_probe(dev, PTR_ERR(bat),
> +				     "Failed to register power supply\n");
> +
> +	ret = max77759_fg_init(dev, info, bat);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "Failed to initialize chip\n");
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id max77759_fg_of_match[] = {
> +	{ .compatible = "maxim,max77759-fg" },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, max77759_fg_of_match);
> +
> +static struct i2c_driver max77759_fg_i2c_driver = {
> +	.driver = {
> +		.name = "max77759_fg",
> +		.of_match_table = max77759_fg_of_match,
> +	},
> +	.probe = max77759_fg_probe,
> +	.remove = max77759_fg_remove,
> +};
> +module_i2c_driver(max77759_fg_i2c_driver);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Thomas Antoine <t.antoine@uclouvain.be>");
> +MODULE_DESCRIPTION("Maxim MAX77759 Fuel Gauge IC driver");
> 
> -- 
> 2.51.0
> 
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v6 2/2] dt-bindings: power: supply: add support for MAX77759 fuel gauge
  2025-09-15 17:31   ` Conor Dooley
@ 2025-09-18 12:36     ` Thomas Antoine
  2025-09-18 13:02       ` André Draszik
  0 siblings, 1 reply; 10+ messages in thread
From: Thomas Antoine @ 2025-09-18 12:36 UTC (permalink / raw)
  To: Conor Dooley
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sebastian Reichel,
	Peter Griffin, André Draszik, linux-kernel, linux-pm,
	devicetree

Hello,


On 9/15/25 7:31 PM, Conor Dooley wrote:
> On Mon, Sep 15, 2025 at 12:14:11PM +0200, Thomas Antoine via B4 Relay wrote:
>> From: Thomas Antoine <t.antoine@uclouvain.be>
>>
>> The Maxim MAX77759 is a companion PMIC for USB Type-C. It contains
>> Battery Charger, Fuel Gauge, temperature sensors, USB Type-C Port
>> Controller (TCPC), NVMEM, and additional GPIO interfaces
>>
>> Use max77759-fg compatible to avoid conflict with drivers for other
>> functions.
>>
>> The battery node is used to pass the REPCAP and ICHGTERM values
>> needed for the initialization of the fuel gauge.
>>
>> The nvmem cells are used to get initialization values and to backup
>> the learning and the number of cycles. It should work out of the box
>> with gs101-oriole and gs101-raven which were previously running
>> Android.
>>
>> Signed-off-by: Thomas Antoine <t.antoine@uclouvain.be>
>> ---
>>  .../bindings/power/supply/maxim,max77759.yaml      | 78 ++++++++++++++++++++++
>>  1 file changed, 78 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/power/supply/maxim,max77759.yaml b/Documentation/devicetree/bindings/power/supply/maxim,max77759.yaml
>> new file mode 100644
>> index 0000000000000000000000000000000000000000..4d45739fcaf26273ec57b60049d6d0421df38efb
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/power/supply/maxim,max77759.yaml
>> @@ -0,0 +1,78 @@
>> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/power/supply/maxim,max77759.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Maxim Integrated MAX77759 fuel gauge
>> +
>> +maintainers:
>> +  - Thomas Antoine <t.antoine@uclouvain.be>
>> +
>> +allOf:
>> +  - $ref: power-supply.yaml#
>> +
>> +properties:
>> +  compatible:
>> +    const: maxim,max77759-fg
> 
> Compatible doesn't match the filename, why?
> I assume the "fg" is fuel-gauge, but can this device be anything else?

The max77759 is a multifunction chip.
The following compatibles are already used for some of those functions:
- maxim,max77759 (for the pmic)
- maxim,max77759-gpio
- maxim,max77759-nvmem
- maxim,max77759-tcpci

The fuel gauge functionality that is added with this patch is very similar
to the functionality of the max1720x which is why the filename was chosen
to fit other maxim fuel gauge chips pattern.

Maybe it would be better to use the maxim,max77759-battery compatible to
match the filename? It would also fit with the already existing
maxim,max77705-battery and maxim,max77849-battery compatibles.

>> +
>> +  reg:
>> +    maxItems: 1
>> +
>> +  interrupts:
>> +    maxItems: 1
>> +
>> +  shunt-resistor-micro-ohms:
>> +    description: The value of the current sense resistor in microohms.
>> +
>> +  monitored-battery:
>> +    description: |
>> +      The fuel gauge needs the following battery properties:
>> +      - charge-full-design-microamp-hours
>> +      - charge-term-current-microamp
>> +
>> +  nvmem-cells:
>> +    maxItems: 1
>> +    description: |
>> +      Saved fuel gauge state. This state will be used during the initialization
>> +      and saved on exit. It must be initialized beforehand.
>> +      Its layout must be composed of
>> +        - RCOMP0 (characterization of the open-circuit voltage)
>> +        - TCOMPO (temperature compensation information)
>> +        - FULLCAPREP (reported full capacity)
>> +        - QRTABLE00, QRTABLE10, QRTABLE20, QRTABLE30 (cell capacity information)
>> +        - cv_mixcap (remaining capacity of the cell without empty compensation)
>> +        - cv_halftime (time-to-full characterization time constant)
>> +      They must all be aligned on 2 bytes. A valid CRC8 checksum must
>> +      also be found at the end (polynomial x^8 + x^2 + x + 1).
>> +
>> +  nvmem-cell-names:
>> +    const: fg_state
>> +
>> +required:
>> +  - compatible
>> +  - reg
>> +  - shunt-resistor-micro-ohms
>> +  - monitored-battery
>> +  - nvmem-cells
>> +  - nvmem-cell-names
>> +
>> +unevaluatedProperties: false
>> +
>> +examples:
>> +  - |
>> +    #include <dt-bindings/interrupt-controller/irq.h>
>> +    i2c {
>> +      #address-cells = <1>;
>> +      #size-cells = <0>;
>> +
>> +      fuel-gauge@36 {
>> +        compatible = "maxim,max77759-fg";
>> +        reg = <0x36>;
>> +        interrupts-extended = <&gpa9 3 IRQ_TYPE_LEVEL_LOW>;
>> +        shunt-resistor-micro-ohms = <5000>;
>> +        monitored-battery = <&battery>;
>> +        nvmem-cells = <&fg_state>;
>> +        nvmem-cell-names = "fg_state";
>> +      };
>> +    };
>>
>> -- 
>> 2.51.0
>>
>>

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v6 2/2] dt-bindings: power: supply: add support for MAX77759 fuel gauge
  2025-09-18 12:36     ` Thomas Antoine
@ 2025-09-18 13:02       ` André Draszik
  2025-09-18 14:51         ` Conor Dooley
  2025-09-18 22:32         ` Sebastian Reichel
  0 siblings, 2 replies; 10+ messages in thread
From: André Draszik @ 2025-09-18 13:02 UTC (permalink / raw)
  To: Thomas Antoine, Conor Dooley
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sebastian Reichel,
	Peter Griffin, linux-kernel, linux-pm, devicetree

Hi,

On Thu, 2025-09-18 at 14:36 +0200, Thomas Antoine wrote:
> Hello,
> 
> 
> On 9/15/25 7:31 PM, Conor Dooley wrote:
> > On Mon, Sep 15, 2025 at 12:14:11PM +0200, Thomas Antoine via B4 Relay wrote:
> > > From: Thomas Antoine <t.antoine@uclouvain.be>
> > > 
> > > The Maxim MAX77759 is a companion PMIC for USB Type-C. It contains
> > > Battery Charger, Fuel Gauge, temperature sensors, USB Type-C Port
> > > Controller (TCPC), NVMEM, and additional GPIO interfaces
> > > 
> > > Use max77759-fg compatible to avoid conflict with drivers for other
> > > functions.
> > > 
> > > The battery node is used to pass the REPCAP and ICHGTERM values
> > > needed for the initialization of the fuel gauge.
> > > 
> > > The nvmem cells are used to get initialization values and to backup
> > > the learning and the number of cycles. It should work out of the box
> > > with gs101-oriole and gs101-raven which were previously running
> > > Android.
> > > 
> > > Signed-off-by: Thomas Antoine <t.antoine@uclouvain.be>
> > > ---
> > >  .../bindings/power/supply/maxim,max77759.yaml      | 78 ++++++++++++++++++++++
> > >  1 file changed, 78 insertions(+)
> > > 
> > > diff --git a/Documentation/devicetree/bindings/power/supply/maxim,max77759.yaml
> > > b/Documentation/devicetree/bindings/power/supply/maxim,max77759.yaml
> > > new file mode 100644
> > > index 0000000000000000000000000000000000000000..4d45739fcaf26273ec57b60049d6d0421df38efb
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/power/supply/maxim,max77759.yaml
> > > @@ -0,0 +1,78 @@
> > > +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> > > +%YAML 1.2
> > > +---
> > > +$id: http://devicetree.org/schemas/power/supply/maxim,max77759.yaml#
> > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > +
> > > +title: Maxim Integrated MAX77759 fuel gauge
> > > +
> > > +maintainers:
> > > +  - Thomas Antoine <t.antoine@uclouvain.be>
> > > +
> > > +allOf:
> > > +  - $ref: power-supply.yaml#
> > > +
> > > +properties:
> > > +  compatible:
> > > +    const: maxim,max77759-fg
> > 
> > Compatible doesn't match the filename, why?
> > I assume the "fg" is fuel-gauge, but can this device be anything else?
> 
> The max77759 is a multifunction chip.
> The following compatibles are already used for some of those functions:
> - maxim,max77759 (for the pmic)
> - maxim,max77759-gpio
> - maxim,max77759-nvmem
> - maxim,max77759-tcpci
> 
> The fuel gauge functionality that is added with this patch is very similar
> to the functionality of the max1720x which is why the filename was chosen
> to fit other maxim fuel gauge chips pattern.
> 
> Maybe it would be better to use the maxim,max77759-battery compatible to
> match the filename? It would also fit with the already existing
> maxim,max77705-battery and maxim,max77849-battery compatibles.

It also has a (battery) charger, a -battery compatible could be misleading.
The datasheet refers to these subblocks as FG (for fuelgauge) and CHARGER.
I'd suggest keeping those terms.

Additionally, the FG block can also measure temperature and battery ID. For
those, a combination of (top-level) PMIC and FG registers are needed
unfortunately. Which means that the FG should probably be an MFD child
device, even though the FG itself doesn't depend on the top-level. Otherwise
it'd be hard to access the top-level PMIC register.


Cheers,
Andre'


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v6 2/2] dt-bindings: power: supply: add support for MAX77759 fuel gauge
  2025-09-18 13:02       ` André Draszik
@ 2025-09-18 14:51         ` Conor Dooley
  2025-09-18 22:32         ` Sebastian Reichel
  1 sibling, 0 replies; 10+ messages in thread
From: Conor Dooley @ 2025-09-18 14:51 UTC (permalink / raw)
  To: André Draszik
  Cc: Thomas Antoine, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Sebastian Reichel, Peter Griffin, linux-kernel, linux-pm,
	devicetree

[-- Attachment #1: Type: text/plain, Size: 3783 bytes --]

On Thu, Sep 18, 2025 at 02:02:55PM +0100, André Draszik wrote:
> On Thu, 2025-09-18 at 14:36 +0200, Thomas Antoine wrote:
> > On 9/15/25 7:31 PM, Conor Dooley wrote:
> > > On Mon, Sep 15, 2025 at 12:14:11PM +0200, Thomas Antoine via B4 Relay wrote:
> > > > From: Thomas Antoine <t.antoine@uclouvain.be>
> > > > 
> > > > The Maxim MAX77759 is a companion PMIC for USB Type-C. It contains
> > > > Battery Charger, Fuel Gauge, temperature sensors, USB Type-C Port
> > > > Controller (TCPC), NVMEM, and additional GPIO interfaces
> > > > 
> > > > Use max77759-fg compatible to avoid conflict with drivers for other
> > > > functions.
> > > > 
> > > > The battery node is used to pass the REPCAP and ICHGTERM values
> > > > needed for the initialization of the fuel gauge.
> > > > 
> > > > The nvmem cells are used to get initialization values and to backup
> > > > the learning and the number of cycles. It should work out of the box
> > > > with gs101-oriole and gs101-raven which were previously running
> > > > Android.
> > > > 
> > > > Signed-off-by: Thomas Antoine <t.antoine@uclouvain.be>
> > > > ---
> > > >  .../bindings/power/supply/maxim,max77759.yaml      | 78 ++++++++++++++++++++++
> > > >  1 file changed, 78 insertions(+)
> > > > 
> > > > diff --git a/Documentation/devicetree/bindings/power/supply/maxim,max77759.yaml
> > > > b/Documentation/devicetree/bindings/power/supply/maxim,max77759.yaml
> > > > new file mode 100644
> > > > index 0000000000000000000000000000000000000000..4d45739fcaf26273ec57b60049d6d0421df38efb
> > > > --- /dev/null
> > > > +++ b/Documentation/devicetree/bindings/power/supply/maxim,max77759.yaml
> > > > @@ -0,0 +1,78 @@
> > > > +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> > > > +%YAML 1.2
> > > > +---
> > > > +$id: http://devicetree.org/schemas/power/supply/maxim,max77759.yaml#
> > > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > > +
> > > > +title: Maxim Integrated MAX77759 fuel gauge
> > > > +
> > > > +maintainers:
> > > > +  - Thomas Antoine <t.antoine@uclouvain.be>
> > > > +
> > > > +allOf:
> > > > +  - $ref: power-supply.yaml#
> > > > +
> > > > +properties:
> > > > +  compatible:
> > > > +    const: maxim,max77759-fg
> > > 
> > > Compatible doesn't match the filename, why?
> > > I assume the "fg" is fuel-gauge, but can this device be anything else?
> > 
> > The max77759 is a multifunction chip.
> > The following compatibles are already used for some of those functions:
> > - maxim,max77759 (for the pmic)
> > - maxim,max77759-gpio
> > - maxim,max77759-nvmem
> > - maxim,max77759-tcpci
> > 
> > The fuel gauge functionality that is added with this patch is very similar
> > to the functionality of the max1720x which is why the filename was chosen
> > to fit other maxim fuel gauge chips pattern.
> > 
> > Maybe it would be better to use the maxim,max77759-battery compatible to
> > match the filename? It would also fit with the already existing
> > maxim,max77705-battery and maxim,max77849-battery compatibles.
> 
> It also has a (battery) charger, a -battery compatible could be misleading.
> The datasheet refers to these subblocks as FG (for fuelgauge) and CHARGER.
> I'd suggest keeping those terms.
> 
> Additionally, the FG block can also measure temperature and battery ID. For
> those, a combination of (top-level) PMIC and FG registers are needed
> unfortunately. Which means that the FG should probably be an MFD child
> device, even though the FG itself doesn't depend on the top-level. Otherwise
> it'd be hard to access the top-level PMIC register.

Keeping the -fg is fine, just propagate it to the filename. I dunno
where the -battery came from, since that doesn't match the filename
either.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v6 2/2] dt-bindings: power: supply: add support for MAX77759 fuel gauge
  2025-09-18 13:02       ` André Draszik
  2025-09-18 14:51         ` Conor Dooley
@ 2025-09-18 22:32         ` Sebastian Reichel
  2025-09-19 13:17           ` André Draszik
  1 sibling, 1 reply; 10+ messages in thread
From: Sebastian Reichel @ 2025-09-18 22:32 UTC (permalink / raw)
  To: André Draszik
  Cc: Thomas Antoine, Conor Dooley, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Peter Griffin, linux-kernel, linux-pm, devicetree

[-- Attachment #1: Type: text/plain, Size: 4312 bytes --]

Hi,

On Thu, Sep 18, 2025 at 02:02:55PM +0100, André Draszik wrote:
> > On 9/15/25 7:31 PM, Conor Dooley wrote:
> > > On Mon, Sep 15, 2025 at 12:14:11PM +0200, Thomas Antoine via B4 Relay wrote:
> > > > From: Thomas Antoine <t.antoine@uclouvain.be>
> > > > 
> > > > The Maxim MAX77759 is a companion PMIC for USB Type-C. It contains
> > > > Battery Charger, Fuel Gauge, temperature sensors, USB Type-C Port
> > > > Controller (TCPC), NVMEM, and additional GPIO interfaces
> > > > 
> > > > Use max77759-fg compatible to avoid conflict with drivers for other
> > > > functions.
> > > > 
> > > > The battery node is used to pass the REPCAP and ICHGTERM values
> > > > needed for the initialization of the fuel gauge.
> > > > 
> > > > The nvmem cells are used to get initialization values and to backup
> > > > the learning and the number of cycles. It should work out of the box
> > > > with gs101-oriole and gs101-raven which were previously running
> > > > Android.
> > > > 
> > > > Signed-off-by: Thomas Antoine <t.antoine@uclouvain.be>
> > > > ---
> > > >  .../bindings/power/supply/maxim,max77759.yaml      | 78 ++++++++++++++++++++++
> > > >  1 file changed, 78 insertions(+)
> > > > 
> > > > diff --git a/Documentation/devicetree/bindings/power/supply/maxim,max77759.yaml
> > > > b/Documentation/devicetree/bindings/power/supply/maxim,max77759.yaml
> > > > new file mode 100644
> > > > index 0000000000000000000000000000000000000000..4d45739fcaf26273ec57b60049d6d0421df38efb
> > > > --- /dev/null
> > > > +++ b/Documentation/devicetree/bindings/power/supply/maxim,max77759.yaml
> > > > @@ -0,0 +1,78 @@
> > > > +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> > > > +%YAML 1.2
> > > > +---
> > > > +$id: http://devicetree.org/schemas/power/supply/maxim,max77759.yaml#
> > > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > > +
> > > > +title: Maxim Integrated MAX77759 fuel gauge
> > > > +
> > > > +maintainers:
> > > > +  - Thomas Antoine <t.antoine@uclouvain.be>
> > > > +
> > > > +allOf:
> > > > +  - $ref: power-supply.yaml#
> > > > +
> > > > +properties:
> > > > +  compatible:
> > > > +    const: maxim,max77759-fg
> > > 
> > > Compatible doesn't match the filename, why?
> > > I assume the "fg" is fuel-gauge, but can this device be anything else?
> > 
> > The max77759 is a multifunction chip.
> > The following compatibles are already used for some of those functions:
> > - maxim,max77759 (for the pmic)
> > - maxim,max77759-gpio
> > - maxim,max77759-nvmem
> > - maxim,max77759-tcpci
> > 
> > The fuel gauge functionality that is added with this patch is very similar
> > to the functionality of the max1720x which is why the filename was chosen
> > to fit other maxim fuel gauge chips pattern.
> > 
> > Maybe it would be better to use the maxim,max77759-battery compatible to
> > match the filename? It would also fit with the already existing
> > maxim,max77705-battery and maxim,max77849-battery compatibles.
> 
> It also has a (battery) charger, a -battery compatible could be misleading.
> The datasheet refers to these subblocks as FG (for fuelgauge) and CHARGER.
> I'd suggest keeping those terms.
> 
> Additionally, the FG block can also measure temperature and battery ID. For
> those, a combination of (top-level) PMIC and FG registers are needed
> unfortunately. Which means that the FG should probably be an MFD child
> device, even though the FG itself doesn't depend on the top-level. Otherwise
> it'd be hard to access the top-level PMIC register.

My understanding is, that the FG has a dedicated I2C device address
and thus cannot be a simple MFD child of the PMIC. Did I misunderstood
that part?

Assuming I understood things correctly, I think I suggest to model
things like this for the battery temperature/ID:

i2c {
    pmic: pmic@42 {
        compatible = "maxim,max77759";
        ...

        pmic_adc: adc {
            compatible = "maxim,max77759-adc";
            ...
        };
    };

    fuel-gauge@43 {
        compatible = "maxim,max77759-fg";
        ...
        io-channels = <&pmic_adc MAX77759_ADC_BAT_TEMP>, <&pmic_adc MAX77759_ADC_BAT_ID>;
        io-channel-names = "temperature", "ID";
    };
};

Greetings,

-- Sebastian

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v6 2/2] dt-bindings: power: supply: add support for MAX77759 fuel gauge
  2025-09-18 22:32         ` Sebastian Reichel
@ 2025-09-19 13:17           ` André Draszik
  0 siblings, 0 replies; 10+ messages in thread
From: André Draszik @ 2025-09-19 13:17 UTC (permalink / raw)
  To: Sebastian Reichel
  Cc: Thomas Antoine, Conor Dooley, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Peter Griffin, linux-kernel, linux-pm, devicetree

Hi,

On Fri, 2025-09-19 at 00:32 +0200, Sebastian Reichel wrote:
> Hi,
> 
> On Thu, Sep 18, 2025 at 02:02:55PM +0100, André Draszik wrote:
> > 
> > Additionally, the FG block can also measure temperature and battery ID. For
> > those, a combination of (top-level) PMIC and FG registers are needed
> > unfortunately. Which means that the FG should probably be an MFD child
> > device, even though the FG itself doesn't depend on the top-level. Otherwise
> > it'd be hard to access the top-level PMIC register.
> 
> My understanding is, that the FG has a dedicated I2C device address

Yes, that is correct. It also has its own dedicated interrupt output.

> and thus cannot be a simple MFD child of the PMIC.

The core can still create child devices if a child uses a different
i2c address, as already done by the max77759 core driver for the charger
(which e.g. doesn't have its own interrupt). Some MFD other core drivers
also use such an approach.


[...]

> Assuming I understood things correctly, I think I suggest to model
> things like this for the battery temperature/ID:

Nice, yes, that should work - didn't think of that...

Cheers,
Andre'

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2025-09-19 13:17 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-15 10:14 [PATCH v6 0/2] MAX77759 Fuel Gauge driver support Thomas Antoine via B4 Relay
2025-09-15 10:14 ` [PATCH v6 1/2] power: supply: add support for MAX77759 fuel gauge Thomas Antoine via B4 Relay
2025-09-16 16:51   ` Sebastian Reichel
2025-09-15 10:14 ` [PATCH v6 2/2] dt-bindings: " Thomas Antoine via B4 Relay
2025-09-15 17:31   ` Conor Dooley
2025-09-18 12:36     ` Thomas Antoine
2025-09-18 13:02       ` André Draszik
2025-09-18 14:51         ` Conor Dooley
2025-09-18 22:32         ` Sebastian Reichel
2025-09-19 13:17           ` André Draszik

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).