Hi, On Mon, Aug 04, 2025 at 04:26:38PM +0200, Thomas Antoine via B4 Relay wrote: > From: Thomas Antoine <t.antoine@xxxxxxxxxxxx> > > 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 use > the MAX M5 fuel gauge. There is a lot of 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. > > 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@xxxxxxxxxx/ > > Signed-off-by: Thomas Antoine <t.antoine@xxxxxxxxxxxx> > --- Reviewed-by: Sebastian Reichel <sebastian.reichel@xxxxxxxxxxxxx> I would have picked it, but the DT binding has some review feedback from Krzysztof and needs to be taken together with this driver :) Greetings, -- Sebastian > drivers/power/supply/Kconfig | 14 + > drivers/power/supply/Makefile | 1 + > drivers/power/supply/max77759_battery.c | 649 ++++++++++++++++++++++++++++++++ > 3 files changed, 664 insertions(+) > > diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig > index 79ddb006e2dad6bf96b71ed570a37c006b5f9433..147d049b836c3fbb24b762dbaf31eebb8ba041f7 100644 > --- a/drivers/power/supply/Kconfig > +++ b/drivers/power/supply/Kconfig > @@ -458,6 +458,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 4f5f8e3507f80da02812f0d08c2d81ddff0a272f..114578fa4fd08356822f13ce1fbad29923defad8 100644 > --- a/drivers/power/supply/Makefile > +++ b/drivers/power/supply/Makefile > @@ -57,6 +57,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..d8d702af607211e391733cd14323698b54be734c > --- /dev/null > +++ b/drivers/power/supply/max77759_battery.c > @@ -0,0 +1,649 @@ > +// 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, > + ®_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 unaccesable via I2c. > + */ > + ret = regmap_read(info->regmap, MAX77759_FG_STATUS, ®_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, ®_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, ®_val); > + if (ret < 0) > + return ret; > + > + val->intval = max77759_fg_capacity_to_ps(reg_val, info); > + ret = max77759_fg_capacity_lsb(info, ®_val); > + val->intval *= reg_val; > + break; > + case POWER_SUPPLY_PROP_CHARGE_AVG: > + ret = regmap_read(info->regmap, MAX77759_FG_REPCAP, ®_val); > + if (ret < 0) > + return ret; > + > + val->intval = max77759_fg_capacity_to_ps(reg_val, info); > + ret = max77759_fg_capacity_lsb(info, ®_val); > + val->intval *= reg_val; > + break; > + case POWER_SUPPLY_PROP_TEMP: > + ret = regmap_read(info->regmap, MAX77759_FG_TEMP, ®_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, ®_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, ®_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, ®_val); > + if (ret < 0) > + return ret; > + > + val->intval = max77759_fg_capacity_to_ps(reg_val, info); > + ret = max77759_fg_capacity_lsb(info, ®_val); > + val->intval *= reg_val; > + break; > + case POWER_SUPPLY_PROP_MODEL_NAME: > + ret = regmap_read(info->regmap, MAX77759_FG_DEV_NAME, ®_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; > + > + power_supply_get_battery_info(bat_psy, &bat_info); > + > + 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@xxxxxxxxxxxx>"); > +MODULE_DESCRIPTION("Maxim MAX77759 Fuel Gauge IC driver"); > > -- > 2.50.1 > >
Attachment:
signature.asc
Description: PGP signature