[PATCH v2 09/11] power: supply: max17042: initial support for Maxim MAX77759

From: André Draszik

Date: Fri Feb 27 2026 - 02:19:20 EST


The Maxim MAX77759 is a companion PMIC intended for use in mobile
phones and tablets. It is used on Google Pixel 6 and 6 Pro (oriole and
raven). Amongst others, it contains a fuel gauge that is similar to the
ones supported by this driver.

The fuel gauge can measure battery charge and discharge current,
battery voltage, battery temperature, and the Type C connector's
temperature.

The MAX77759 incorporates the Maxim ModelGauge m5 algorithm. It, as
well as previous generations like m3 on max17047/max17050, requires
the host to save/restore some register values across power cycles to
maintain full accuracy. Extending the driver for such support is out of
scope in this initial commit.

Signed-off-by: André Draszik <andre.draszik@xxxxxxxxxx>

---
v2: fix typo MAX77759_T_convert -> MAX77759_Tconvert
---
drivers/power/supply/max17042_battery.c | 59 ++++++++++++++++++++++++++++++---
include/linux/power/max17042_battery.h | 24 ++++++++++++--
2 files changed, 77 insertions(+), 6 deletions(-)

diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c
index 823533ea5a17..44626abdab34 100644
--- a/drivers/power/supply/max17042_battery.c
+++ b/drivers/power/supply/max17042_battery.c
@@ -654,7 +654,8 @@ static void max17042_write_config_regs(struct max17042_chip *chip)
regmap_write(map, MAX17042_RelaxCFG, config->relax_cfg);
if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047 ||
chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050 ||
- chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055)
+ chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055 ||
+ chip->chip_type == MAXIM_DEVICE_TYPE_MAX77759)
regmap_write(map, MAX17047_FullSOCThr,
config->full_soc_thresh);
}
@@ -791,7 +792,8 @@ static inline void max17042_override_por_values(struct max17042_chip *chip)

if ((chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) ||
(chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047) ||
- (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050)) {
+ (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050) ||
+ (chip->chip_type == MAXIM_DEVICE_TYPE_MAX77759)) {
max17042_override_por(map, MAX17042_IAvg_empty, config->iavg_empty);
max17042_override_por(map, MAX17042_TempNom, config->temp_nom);
max17042_override_por(map, MAX17042_TempLim, config->temp_lim);
@@ -800,7 +802,8 @@ static inline void max17042_override_por_values(struct max17042_chip *chip)

if ((chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047) ||
(chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050) ||
- (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055)) {
+ (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055) ||
+ (chip->chip_type == MAXIM_DEVICE_TYPE_MAX77759)) {
max17042_override_por(map, MAX17047_V_empty, config->vempty);
}
}
@@ -1023,6 +1026,45 @@ static const struct regmap_config max17042_regmap_config = {
.val_format_endian = REGMAP_ENDIAN_NATIVE,
};

+static const struct regmap_range max77759_fg_registers[] = {
+ regmap_reg_range(MAX17042_STATUS, MAX77759_MixAtFull),
+ regmap_reg_range(MAX17042_VFSOC0Enable, MAX17042_VFSOC0Enable),
+ regmap_reg_range(MAX17042_MLOCKReg1, MAX17042_MLOCKReg2),
+ regmap_reg_range(MAX17042_MODELChrTbl, MAX17055_TimerH),
+ regmap_reg_range(MAX77759_IIn, MAX77759_IIn),
+ regmap_reg_range(MAX17055_AtQResidual, MAX17055_AtAvCap),
+ regmap_reg_range(MAX17042_OCVInternal, MAX17042_OCVInternal),
+ regmap_reg_range(MAX17042_VFSOC, MAX17042_VFSOC),
+};
+
+static const struct regmap_range max77759_fg_ro_registers[] = {
+ regmap_reg_range(MAX17042_FSTAT, MAX17042_FSTAT),
+ regmap_reg_range(MAX17042_OCVInternal, MAX17042_OCVInternal),
+ regmap_reg_range(MAX17042_VFSOC, MAX17042_VFSOC),
+};
+
+static const struct regmap_access_table max77759_fg_write_table = {
+ .yes_ranges = max77759_fg_registers,
+ .n_yes_ranges = ARRAY_SIZE(max77759_fg_registers),
+ .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_NATIVE,
+ .cache_type = REGCACHE_NONE,
+};
+
static const struct power_supply_desc max17042_psy_desc = {
.name = "max170xx_battery",
.type = POWER_SUPPLY_TYPE_BATTERY,
@@ -1049,6 +1091,7 @@ static int max17042_probe(struct i2c_client *client, struct device *dev, int irq
{
struct i2c_adapter *adapter = client->adapter;
const struct power_supply_desc *max17042_desc = &max17042_psy_desc;
+ const struct regmap_config *regmap_config;
struct power_supply_config psy_cfg = {};
struct max17042_chip *chip;
int ret;
@@ -1064,7 +1107,12 @@ static int max17042_probe(struct i2c_client *client, struct device *dev, int irq

chip->dev = dev;
chip->chip_type = chip_type;
- chip->regmap = devm_regmap_init_i2c(client, &max17042_regmap_config);
+
+ if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX77759)
+ regmap_config = &max77759_fg_regmap_cfg;
+ else
+ regmap_config = &max17042_regmap_config;
+ chip->regmap = devm_regmap_init_i2c(client, regmap_config);
if (IS_ERR(chip->regmap))
return dev_err_probe(dev, PTR_ERR(chip->regmap),
"Failed to initialize regmap\n");
@@ -1245,6 +1293,8 @@ static const struct of_device_id max17042_dt_match[] __used = {
.data = (void *) MAXIM_DEVICE_TYPE_MAX17055 },
{ .compatible = "maxim,max77705-battery",
.data = (void *) MAXIM_DEVICE_TYPE_MAX17047 },
+ { .compatible = "maxim,max77759-fg",
+ .data = (void *) MAXIM_DEVICE_TYPE_MAX77759 },
{ .compatible = "maxim,max77849-battery",
.data = (void *) MAXIM_DEVICE_TYPE_MAX17047 },
{ },
@@ -1257,6 +1307,7 @@ static const struct i2c_device_id max17042_id[] = {
{ "max17047", MAXIM_DEVICE_TYPE_MAX17047 },
{ "max17050", MAXIM_DEVICE_TYPE_MAX17050 },
{ "max17055", MAXIM_DEVICE_TYPE_MAX17055 },
+ { "max77759-fg", MAXIM_DEVICE_TYPE_MAX77759 },
{ "max77849-battery", MAXIM_DEVICE_TYPE_MAX17047 },
{ }
};
diff --git a/include/linux/power/max17042_battery.h b/include/linux/power/max17042_battery.h
index c417abd2ab70..05097f08ea36 100644
--- a/include/linux/power/max17042_battery.h
+++ b/include/linux/power/max17042_battery.h
@@ -105,7 +105,7 @@ enum max17042_register {

MAX17042_OCV = 0xEE,

- MAX17042_OCVInternal = 0xFB, /* MAX17055 VFOCV */
+ MAX17042_OCVInternal = 0xFB, /* MAX17055/77759 VFOCV */

MAX17042_VFSOC = 0xFF,
};
@@ -156,7 +156,7 @@ enum max17055_register {
MAX17055_AtAvCap = 0xDF,
};

-/* Registers specific to max17047/50/55 */
+/* Registers specific to max17047/50/55/77759 */
enum max17047_register {
MAX17047_QRTbl00 = 0x12,
MAX17047_FullSOCThr = 0x13,
@@ -167,12 +167,32 @@ enum max17047_register {
MAX17047_QRTbl30 = 0x42,
};

+enum max77759_register {
+ MAX77759_AvgTA0 = 0x26,
+ MAX77759_AtTTF = 0x33,
+ MAX77759_Tconvert = 0x34,
+ MAX77759_AvgCurrent0 = 0x3B,
+ MAX77759_THMHOT = 0x40,
+ MAX77759_CTESample = 0x41,
+ MAX77759_ISys = 0x43,
+ MAX77759_AvgVCell0 = 0x44,
+ MAX77759_RlxSOC = 0x47,
+ MAX77759_AvgISys = 0x4B,
+ MAX77759_QH0 = 0x4C,
+ MAX77759_MixAtFull = 0x4F,
+ MAX77759_VSys = 0xB1,
+ MAX77759_TAlrtTh2 = 0xB2,
+ MAX77759_VByp = 0xB3,
+ MAX77759_IIn = 0xD0,
+};
+
enum max170xx_chip_type {
MAXIM_DEVICE_TYPE_UNKNOWN = 0,
MAXIM_DEVICE_TYPE_MAX17042,
MAXIM_DEVICE_TYPE_MAX17047,
MAXIM_DEVICE_TYPE_MAX17050,
MAXIM_DEVICE_TYPE_MAX17055,
+ MAXIM_DEVICE_TYPE_MAX77759,

MAXIM_DEVICE_TYPE_NUM
};

--
2.53.0.473.g4a7958ca14-goog