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

André Draszik posted 11 patches 1 month, 1 week ago
There is a newer version of this series
[PATCH v2 09/11] power: supply: max17042: initial support for Maxim MAX77759
Posted by André Draszik 1 month, 1 week ago
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@linaro.org>

---
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

Re: [PATCH v2 09/11] power: supply: max17042: initial support for Maxim MAX77759
Posted by Peter Griffin 1 month, 1 week ago
On Fri, 27 Feb 2026 at 07:15, André Draszik <andre.draszik@linaro.org> wrote:
>
> 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@linaro.org>
>
> ---

Reviewed-by: Peter Griffin <peter.griffin@linaro.org>

> 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
>