* [PATCH 0/2] power: supply: Add Silicon Mitus SM5714 fuel gauge
@ 2026-11-11 15:34 Asdlerto Korry via B4 Relay
2026-11-11 15:34 ` [PATCH 1/2] dt-bindings: " Asdlerto Korry via B4 Relay
2026-11-11 15:35 ` [PATCH 2/2] power: supply: Add Silicon Mitus SM5714 fuel gauge driver Asdlerto Korry via B4 Relay
0 siblings, 2 replies; 5+ messages in thread
From: Asdlerto Korry via B4 Relay @ 2026-11-11 15:34 UTC (permalink / raw)
To: Sebastian Reichel, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-pm, devicetree, linux-kernel, Asdlerto Korry
The Silicon Mitus SM5714 is a power management chip for mobile phones
with a charger, a USB Type-C port controller, a MUIC and a fuel gauge,
each on its own I2C address. It is used in the Samsung Galaxy M52 5G
(SM7325).
This series adds a binding and a read-only driver for the fuel gauge
part. It reports the state of charge, the battery voltage and current
and the battery temperature. The temperature measured by the gauge
itself rises with the charge current, so an optional ADC channel can
point at the battery thermistor.
Tested on a Samsung Galaxy M52 5G with the driver backported to 7.1:
the voltage, open-circuit voltage and current match the out-of-tree
driver used before, and the status follows the cable (discharging when
unplugged, full when plugged in with a full battery).
The charger and Type-C parts of the chip, and the devicetree nodes for
the M52, will come in separate series.
Signed-off-by: Asdlerto Korry <korryasdlerto@gmail.com>
---
Asdlerto Korry (2):
dt-bindings: power: supply: Add Silicon Mitus SM5714 fuel gauge
power: supply: Add Silicon Mitus SM5714 fuel gauge driver
.../supply/siliconmitus,sm5714-fuelgauge.yaml | 53 ++++
MAINTAINERS | 7 +
drivers/power/supply/Kconfig | 13 +
drivers/power/supply/Makefile | 1 +
drivers/power/supply/sm5714_fuel_gauge.c | 315 +++++++++++++++++++++
5 files changed, 389 insertions(+)
---
base-commit: 7649ba73f9e7e1e04d0b46213e39c79ae09dd55c
change-id: 20261111-sm5714-fuel-gauge-c3c617078240
Best regards,
--
Asdlerto Korry <korryasdlerto@gmail.com>
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH 1/2] dt-bindings: power: supply: Add Silicon Mitus SM5714 fuel gauge 2026-11-11 15:34 [PATCH 0/2] power: supply: Add Silicon Mitus SM5714 fuel gauge Asdlerto Korry via B4 Relay @ 2026-11-11 15:34 ` Asdlerto Korry via B4 Relay 2026-11-11 15:35 ` [PATCH 2/2] power: supply: Add Silicon Mitus SM5714 fuel gauge driver Asdlerto Korry via B4 Relay 1 sibling, 0 replies; 5+ messages in thread From: Asdlerto Korry via B4 Relay @ 2026-11-11 15:34 UTC (permalink / raw) To: Sebastian Reichel, Rob Herring, Krzysztof Kozlowski, Conor Dooley Cc: linux-pm, devicetree, linux-kernel, Asdlerto Korry From: Asdlerto Korry <korryasdlerto@gmail.com> The SM5714 is a power management chip for mobile phones that combines a charger, a USB Type-C port controller, a MUIC and a fuel gauge, each on its own I2C address. It is used in the Samsung Galaxy M52 5G. Add a binding for the fuel gauge. The temperature measured by the gauge itself rises with the charge current, so an optional ADC channel can point at the battery thermistor. Signed-off-by: Asdlerto Korry <korryasdlerto@gmail.com> --- .../supply/siliconmitus,sm5714-fuelgauge.yaml | 53 ++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/Documentation/devicetree/bindings/power/supply/siliconmitus,sm5714-fuelgauge.yaml b/Documentation/devicetree/bindings/power/supply/siliconmitus,sm5714-fuelgauge.yaml new file mode 100644 index 000000000000..af51db8cf13d --- /dev/null +++ b/Documentation/devicetree/bindings/power/supply/siliconmitus,sm5714-fuelgauge.yaml @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/power/supply/siliconmitus,sm5714-fuelgauge.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Silicon Mitus SM5714 fuel gauge + +maintainers: + - Asdlerto Korry <korryasdlerto@gmail.com> + +description: + The SM5714 is a power management chip for mobile phones with a charger, + a USB Type-C port controller, a MUIC and a fuel gauge, each on its own + I2C address. This binding describes the fuel gauge, which reports the + battery state of charge, voltage and current. + +allOf: + - $ref: power-supply.yaml# + +properties: + compatible: + const: siliconmitus,sm5714-fuelgauge + + reg: + maxItems: 1 + + io-channels: + maxItems: 1 + description: + ADC channel that reports the temperature of the battery thermistor. + The temperature measured by the fuel gauge itself rises with the + charge current, so the battery temperature is taken from this + channel when it is present. + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + fuel-gauge@71 { + compatible = "siliconmitus,sm5714-fuelgauge"; + reg = <0x71>; + io-channels = <&pmk8350_vadc 0x14b>; + }; + }; -- 2.25.1 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 2/2] power: supply: Add Silicon Mitus SM5714 fuel gauge driver 2026-11-11 15:34 [PATCH 0/2] power: supply: Add Silicon Mitus SM5714 fuel gauge Asdlerto Korry via B4 Relay 2026-11-11 15:34 ` [PATCH 1/2] dt-bindings: " Asdlerto Korry via B4 Relay @ 2026-11-11 15:35 ` Asdlerto Korry via B4 Relay 2026-11-11 16:12 ` sashiko-bot 1 sibling, 1 reply; 5+ messages in thread From: Asdlerto Korry via B4 Relay @ 2026-11-11 15:35 UTC (permalink / raw) To: Sebastian Reichel, Rob Herring, Krzysztof Kozlowski, Conor Dooley Cc: linux-pm, devicetree, linux-kernel, Asdlerto Korry From: Asdlerto Korry <korryasdlerto@gmail.com> Add a driver for the fuel gauge of the Silicon Mitus SM5714. The gauge keeps its results in an internal SRAM that is read through an address and a data register. The driver only reads: it reports the state of charge, the battery voltage (now, average and open-circuit), the current (now and average) and the temperature. The conversions follow the vendor driver. The temperature reported by the gauge rises with the charge current, so the battery temperature is read from the thermistor ADC channel when the devicetree provides one. The gauge interrupt is not used, power_supply_changed() is reported once a minute. Signed-off-by: Asdlerto Korry <korryasdlerto@gmail.com> --- MAINTAINERS | 7 + drivers/power/supply/Kconfig | 13 ++ drivers/power/supply/Makefile | 1 + drivers/power/supply/sm5714_fuel_gauge.c | 315 +++++++++++++++++++++++++++++++ 4 files changed, 336 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index aaf2825ac945..9d08b04c1537 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25750,6 +25750,13 @@ S: Supported F: Documentation/devicetree/bindings/net/wireless/silabs,wfx.yaml F: drivers/net/wireless/silabs/ +SILICON MITUS SM5714 FUEL GAUGE DRIVER +M: Asdlerto Korry <korryasdlerto@gmail.com> +L: linux-pm@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/power/supply/siliconmitus,sm5714-fuelgauge.yaml +F: drivers/power/supply/sm5714_fuel_gauge.c + SILICON MOTION SM712 FRAME BUFFER DRIVER M: Sudip Mukherjee <sudipm.mukherjee@gmail.com> M: Teddy Wang <teddy.wang@siliconmotion.com> diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index ee7dc5f2617a..a84de54229cd 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -1192,6 +1192,19 @@ config FUEL_GAUGE_MM8013 the state of charge, temperature, cycle count, actual and design capacity, etc. +config BATTERY_SM5714 + tristate "Silicon Mitus SM5714 fuel gauge driver" + depends on I2C + depends on IIO + select REGMAP_I2C + help + Say Y here to enable the driver for the fuel gauge of the Silicon + Mitus SM5714 power management chip. It reports the battery state of + charge, voltage, current and temperature. + + This driver can also be built as a module. If so, the module will be + called sm5714_fuel_gauge. + config MACSMC_POWER tristate "Apple SMC Battery and Power Driver" depends on MFD_MACSMC diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index f3c0d1006c1a..00d0f975f5ec 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -133,5 +133,6 @@ obj-$(CONFIG_BATTERY_CHARGER_SURFACE_RT) += surface-rt-ec.o obj-$(CONFIG_BATTERY_UG3105) += ug3105_battery.o obj-$(CONFIG_CHARGER_QCOM_SMB2) += qcom_smbx.o obj-$(CONFIG_FUEL_GAUGE_MM8013) += mm8013.o +obj-$(CONFIG_BATTERY_SM5714) += sm5714_fuel_gauge.o obj-$(CONFIG_MACSMC_POWER) += macsmc-power.o obj-$(CONFIG_CHARGER_MAX77759) += max77759_charger.o diff --git a/drivers/power/supply/sm5714_fuel_gauge.c b/drivers/power/supply/sm5714_fuel_gauge.c new file mode 100644 index 000000000000..531583fb211b --- /dev/null +++ b/drivers/power/supply/sm5714_fuel_gauge.c @@ -0,0 +1,315 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Silicon Mitus SM5714 fuel gauge driver + * + * The gauge keeps its results in an internal SRAM: the SRAM address is + * written to RADDR, then the 16-bit word is read from RDATA. The driver only + * reads; the gauge algorithm and charging stay under hardware control. The + * conversions follow the vendor driver. + * + * The temperature measured by the gauge itself rises with the charge current, + * so the battery temperature is read from the battery thermistor ADC channel + * when the devicetree provides one. + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/devm-helpers.h> +#include <linux/i2c.h> +#include <linux/iio/consumer.h> +#include <linux/minmax.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/mutex.h> +#include <linux/power_supply.h> +#include <linux/regmap.h> +#include <linux/workqueue.h> + +#define SM5714_FG_REG_DEVICE_ID 0x00 +#define SM5714_FG_REG_AUX_STAT 0x22 +#define SM5714_FG_REG_SRAM_RADDR 0x8c +#define SM5714_FG_REG_SRAM_RDATA 0x8d + +#define SM5714_FG_SRAM_SOC 0x00 +#define SM5714_FG_SRAM_OCV 0x01 +#define SM5714_FG_SRAM_VBAT 0x03 +#define SM5714_FG_SRAM_CURRENT 0x05 +#define SM5714_FG_SRAM_TEMPERATURE 0x07 +#define SM5714_FG_SRAM_VBAT_AVG 0x08 +#define SM5714_FG_SRAM_CURRENT_AVG 0x09 + +#define SM5714_FG_AUX_STAT_CHG_ON BIT(5) +#define SM5714_FG_AUX_STAT_CURR_X2_5 BIT(10) + +#define SM5714_FG_SIGN BIT(15) +#define SM5714_FG_MAGNITUDE GENMASK(14, 0) + +/* the gauge interrupt is not used: report changes periodically */ +#define SM5714_FG_POLL_INTERVAL (60 * HZ) + +struct sm5714_fg { + struct regmap *regmap; + struct power_supply *psy; + struct iio_channel *ntc; /* NULL: use the gauge temperature */ + struct mutex lock; /* RADDR/RDATA pair */ + struct delayed_work work; +}; + +static const struct regmap_config sm5714_fg_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .val_format_endian = REGMAP_ENDIAN_LITTLE, + .max_register = SM5714_FG_REG_SRAM_RDATA, +}; + +static int sm5714_fg_read_sram(struct sm5714_fg *fg, u8 addr, unsigned int *val) +{ + int ret; + + mutex_lock(&fg->lock); + ret = regmap_write(fg->regmap, SM5714_FG_REG_SRAM_RADDR, addr); + if (!ret) + ret = regmap_read(fg->regmap, SM5714_FG_REG_SRAM_RDATA, val); + mutex_unlock(&fg->lock); + + return ret; +} + +/* 0.1 % */ +static int sm5714_fg_soc_permille(unsigned int raw) +{ + return (raw * 10) >> 8; +} + +/* mV */ +static int sm5714_fg_raw_to_mv(unsigned int raw) +{ + int mv = (FIELD_GET(SM5714_FG_MAGNITUDE, raw) * 10) / 109; + + return raw & SM5714_FG_SIGN ? 2700 - mv : 2700 + mv; +} + +/* mA, positive while charging */ +static int sm5714_fg_get_current_ma(struct sm5714_fg *fg, u8 sram, int *ma) +{ + unsigned int raw, aux; + int ret; + + ret = sm5714_fg_read_sram(fg, sram, &raw); + if (ret) + return ret; + ret = regmap_read(fg->regmap, SM5714_FG_REG_AUX_STAT, &aux); + if (ret) + return ret; + + *ma = (FIELD_GET(SM5714_FG_MAGNITUDE, raw) * 1000) / 2044; + if (raw & SM5714_FG_SIGN) + *ma = -*ma; + if (aux & SM5714_FG_AUX_STAT_CURR_X2_5) + *ma = *ma * 25 / 10; + + return 0; +} + +/* 0.1 degC */ +static int sm5714_fg_get_temp(struct sm5714_fg *fg, int *temp) +{ + unsigned int raw; + int ret, mdegc; + + if (fg->ntc) { + ret = iio_read_channel_processed(fg->ntc, &mdegc); + if (ret < 0) + return ret; + *temp = mdegc / 100; + return 0; + } + + ret = sm5714_fg_read_sram(fg, SM5714_FG_SRAM_TEMPERATURE, &raw); + if (ret) + return ret; + + *temp = ((FIELD_GET(SM5714_FG_MAGNITUDE, raw) * 10) * 2989) >> 19; + if (raw & SM5714_FG_SIGN) + *temp = -*temp; + + return 0; +} + +static int sm5714_fg_get_status(struct sm5714_fg *fg, int *status) +{ + unsigned int aux, soc; + int ma, ret; + + ret = regmap_read(fg->regmap, SM5714_FG_REG_AUX_STAT, &aux); + if (ret) + return ret; + ret = sm5714_fg_read_sram(fg, SM5714_FG_SRAM_SOC, &soc); + if (ret) + return ret; + ret = sm5714_fg_get_current_ma(fg, SM5714_FG_SRAM_CURRENT_AVG, &ma); + if (ret) + return ret; + + /* the charger stops after top-off while the adapter stays connected */ + if (!(aux & SM5714_FG_AUX_STAT_CHG_ON) && power_supply_am_i_supplied(fg->psy) <= 0) + *status = POWER_SUPPLY_STATUS_DISCHARGING; + else if (sm5714_fg_soc_permille(soc) >= 990) + *status = POWER_SUPPLY_STATUS_FULL; + else if (ma > 0) + *status = POWER_SUPPLY_STATUS_CHARGING; + else + *status = POWER_SUPPLY_STATUS_NOT_CHARGING; + + return 0; +} + +static int sm5714_fg_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct sm5714_fg *fg = power_supply_get_drvdata(psy); + unsigned int raw; + int ret; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + return sm5714_fg_get_status(fg, &val->intval); + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + return 0; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + return 0; + case POWER_SUPPLY_PROP_CAPACITY: + ret = sm5714_fg_read_sram(fg, SM5714_FG_SRAM_SOC, &raw); + if (ret) + return ret; + val->intval = clamp((sm5714_fg_soc_permille(raw) + 5) / 10, 0, 100); + return 0; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + case POWER_SUPPLY_PROP_VOLTAGE_AVG: + ret = sm5714_fg_read_sram(fg, psp == POWER_SUPPLY_PROP_VOLTAGE_NOW ? + SM5714_FG_SRAM_VBAT : SM5714_FG_SRAM_VBAT_AVG, &raw); + if (ret) + return ret; + val->intval = sm5714_fg_raw_to_mv(raw) * 1000; + return 0; + case POWER_SUPPLY_PROP_VOLTAGE_OCV: + ret = sm5714_fg_read_sram(fg, SM5714_FG_SRAM_OCV, &raw); + if (ret) + return ret; + val->intval = ((raw * 1000) >> 11) * 1000; + return 0; + case POWER_SUPPLY_PROP_CURRENT_NOW: + case POWER_SUPPLY_PROP_CURRENT_AVG: + ret = sm5714_fg_get_current_ma(fg, psp == POWER_SUPPLY_PROP_CURRENT_NOW ? + SM5714_FG_SRAM_CURRENT : + SM5714_FG_SRAM_CURRENT_AVG, &val->intval); + if (ret) + return ret; + val->intval *= 1000; + return 0; + case POWER_SUPPLY_PROP_TEMP: + return sm5714_fg_get_temp(fg, &val->intval); + default: + return -EINVAL; + } +} + +static const enum power_supply_property sm5714_fg_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_AVG, + POWER_SUPPLY_PROP_VOLTAGE_OCV, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_TEMP, +}; + +static const struct power_supply_desc sm5714_fg_desc = { + .name = "battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = sm5714_fg_props, + .num_properties = ARRAY_SIZE(sm5714_fg_props), + .get_property = sm5714_fg_get_property, +}; + +static void sm5714_fg_work(struct work_struct *work) +{ + struct sm5714_fg *fg = container_of(work, struct sm5714_fg, work.work); + + power_supply_changed(fg->psy); + schedule_delayed_work(&fg->work, SM5714_FG_POLL_INTERVAL); +} + +static int sm5714_fg_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct power_supply_config cfg = {}; + struct sm5714_fg *fg; + unsigned int id; + int ret; + + fg = devm_kzalloc(dev, sizeof(*fg), GFP_KERNEL); + if (!fg) + return -ENOMEM; + + ret = devm_mutex_init(dev, &fg->lock); + if (ret) + return ret; + + fg->regmap = devm_regmap_init_i2c(client, &sm5714_fg_regmap_config); + if (IS_ERR(fg->regmap)) + return dev_err_probe(dev, PTR_ERR(fg->regmap), "failed to init regmap\n"); + + ret = regmap_read(fg->regmap, SM5714_FG_REG_DEVICE_ID, &id); + if (ret) + return dev_err_probe(dev, ret, "failed to read device id\n"); + + fg->ntc = devm_iio_channel_get(dev, NULL); + if (IS_ERR(fg->ntc)) { + ret = PTR_ERR(fg->ntc); + if (ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to get thermistor channel\n"); + fg->ntc = NULL; + } + + cfg.drv_data = fg; + cfg.fwnode = dev_fwnode(dev); + fg->psy = devm_power_supply_register(dev, &sm5714_fg_desc, &cfg); + if (IS_ERR(fg->psy)) + return dev_err_probe(dev, PTR_ERR(fg->psy), "failed to register power supply\n"); + + ret = devm_delayed_work_autocancel(dev, &fg->work, sm5714_fg_work); + if (ret) + return ret; + schedule_delayed_work(&fg->work, SM5714_FG_POLL_INTERVAL); + + dev_dbg(dev, "device id 0x%04x, temperature from %s\n", id, + fg->ntc ? "battery thermistor" : "fuel gauge"); + + return 0; +} + +static const struct of_device_id sm5714_fg_of_match[] = { + { .compatible = "siliconmitus,sm5714-fuelgauge" }, + { } +}; +MODULE_DEVICE_TABLE(of, sm5714_fg_of_match); + +static struct i2c_driver sm5714_fg_driver = { + .driver = { + .name = "sm5714-fuelgauge", + .of_match_table = sm5714_fg_of_match, + }, + .probe = sm5714_fg_probe, +}; +module_i2c_driver(sm5714_fg_driver); + +MODULE_AUTHOR("Asdlerto Korry <korryasdlerto@gmail.com>"); +MODULE_DESCRIPTION("Silicon Mitus SM5714 fuel gauge driver"); +MODULE_LICENSE("GPL"); -- 2.25.1 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 2/2] power: supply: Add Silicon Mitus SM5714 fuel gauge driver 2026-11-11 15:35 ` [PATCH 2/2] power: supply: Add Silicon Mitus SM5714 fuel gauge driver Asdlerto Korry via B4 Relay @ 2026-11-11 16:12 ` sashiko-bot 0 siblings, 0 replies; 5+ messages in thread From: sashiko-bot @ 2026-11-11 16:12 UTC (permalink / raw) To: Asdlerto Korry; +Cc: devicetree, robh, conor+dt Thank you for your contribution! Sashiko AI review found 2 potential issues to consider: - [High] power: supply: sm5714: missing MODULE_IMPORT_NS() for IIO_CONSUMER - [Medium] power: supply: sm5714: NULL deref on fg->psy in sm5714_fg_get_status() -- commit f1f865d4819006729764f420d2f1ef002104e726 Author: Asdlerto Korry <korryasdlerto@gmail.com> power: supply: Add Silicon Mitus SM5714 fuel gauge driver This commit adds a power supply driver for the Silicon Mitus SM5714 fuel gauge, reading state of charge, voltage, current, and temperature over I2C and an optional thermistor IIO channel. > diff --git a/drivers/power/supply/sm5714_fuel_gauge.c b/drivers/power/supply/sm5714_fuel_gauge.c > new file mode 100644 > index 0000000000000..531583fb211bb > --- /dev/null > +++ b/drivers/power/supply/sm5714_fuel_gauge.c [ ... ] > +static int sm5714_fg_get_status(struct sm5714_fg *fg, int *status) > +{ > + unsigned int aux, soc; > + int ma, ret; > + > + ret = regmap_read(fg->regmap, SM5714_FG_REG_AUX_STAT, &aux); > + if (ret) > + return ret; > + ret = sm5714_fg_read_sram(fg, SM5714_FG_SRAM_SOC, &soc); > + if (ret) > + return ret; > + ret = sm5714_fg_get_current_ma(fg, SM5714_FG_SRAM_CURRENT_AVG, &ma); > + if (ret) > + return ret; > + > + /* the charger stops after top-off while the adapter stays connected */ > + if (!(aux & SM5714_FG_AUX_STAT_CHG_ON) && power_supply_am_i_supplied(fg->psy) <= 0) > + *status = POWER_SUPPLY_STATUS_DISCHARGING; > + else if (sm5714_fg_soc_permille(soc) >= 990) > + *status = POWER_SUPPLY_STATUS_FULL; > + else if (ma > 0) > + *status = POWER_SUPPLY_STATUS_CHARGING; > + else > + *status = POWER_SUPPLY_STATUS_NOT_CHARGING; > + > + return 0; > +} > + > +static int sm5714_fg_get_property(struct power_supply *psy, > + enum power_supply_property psp, > + union power_supply_propval *val) > +{ > + struct sm5714_fg *fg = power_supply_get_drvdata(psy); > + unsigned int raw; > + int ret; > + > + switch (psp) { > + case POWER_SUPPLY_PROP_STATUS: > + return sm5714_fg_get_status(fg, &val->intval); [Severity: Medium] Can sm5714_fg_get_status() pass a NULL fg->psy to power_supply_am_i_supplied()? __power_supply_register() exposes the sysfs attributes and increments psy->use_cnt before devm_power_supply_register() returns and assigns fg->psy in sm5714_fg_probe(): CPU0 CPU1 sm5714_fg_probe() devm_power_supply_register() __power_supply_register() device_add() atomic_inc(&psy->use_cnt) power_supply_show_property() sm5714_fg_get_property(psy, ...) sm5714_fg_get_status(fg, ...) power_supply_am_i_supplied(fg->psy) ... __power_supply_is_supplied_by() supply->supplied_from <-- NULL pointer dereference fg->psy = ... Passing psy from sm5714_fg_get_property() into sm5714_fg_get_status() would avoid reading fg->psy before it is initialized. [ ... ] > +MODULE_AUTHOR("Asdlerto Korry <korryasdlerto@gmail.com>"); > +MODULE_DESCRIPTION("Silicon Mitus SM5714 fuel gauge driver"); > +MODULE_LICENSE("GPL"); [Severity: High] Does building this driver with CONFIG_BATTERY_SM5714=m fail in modpost due to a missing MODULE_IMPORT_NS("IIO_CONSUMER")? devm_iio_channel_get() and iio_read_channel_processed() are exported in the IIO_CONSUMER symbol namespace. -- Sashiko AI review · https://sashiko.dev/#/patchset/20261111-sm5714-fuel-gauge-v1-0-b9d163fff6ff@gmail.com?part=2 ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 0/2] power: supply: Add Silicon Mitus SM5714 fuel gauge
@ 2026-11-08 11:22 Nizat Mikayilli via B4 Relay
2026-11-08 11:22 ` [PATCH 2/2] power: supply: Add Silicon Mitus SM5714 fuel gauge driver Nizat Mikayilli via B4 Relay
0 siblings, 1 reply; 5+ messages in thread
From: Nizat Mikayilli via B4 Relay @ 2026-11-08 11:22 UTC (permalink / raw)
To: Sebastian Reichel, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-pm, devicetree, linux-kernel, Nizat Mikayilli
The Samsung Galaxy Tab S9 Wi-Fi uses the fuel gauge of the Silicon
Mitus SM5714. This series adds a binding and a small driver for it,
which reports the state of charge, voltage, current and status.
The fuel gauge node for the tablet will be sent with its board series:
https://lore.kernel.org/r/20261022-gts9wifi-dts-v4-0-a361428fe59c@dxdmail.cc
I tested it on the tablet, with the USB cable in and out, and the
power-supply selftest passes.
Signed-off-by: Nizat Mikayilli <nacht@dxdmail.cc>
---
Nizat Mikayilli (2):
dt-bindings: power: supply: Add Silicon Mitus SM5714 fuel gauge
power: supply: Add Silicon Mitus SM5714 fuel gauge driver
.../supply/siliconmitus,sm5714-fuel-gauge.yaml | 47 +++++
MAINTAINERS | 7 +
drivers/power/supply/Kconfig | 10 +
drivers/power/supply/Makefile | 1 +
drivers/power/supply/sm5714-battery.c | 221 +++++++++++++++++++++
5 files changed, 286 insertions(+)
---
base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
change-id: 20261108-b4-sm5714-fuel-gauge-ccbe7c191c0c
Best regards,
--
Nizat Mikayilli <nacht@dxdmail.cc>
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH 2/2] power: supply: Add Silicon Mitus SM5714 fuel gauge driver 2026-11-08 11:22 [PATCH 0/2] power: supply: Add Silicon Mitus SM5714 fuel gauge Nizat Mikayilli via B4 Relay @ 2026-11-08 11:22 ` Nizat Mikayilli via B4 Relay 0 siblings, 0 replies; 5+ messages in thread From: Nizat Mikayilli via B4 Relay @ 2026-11-08 11:22 UTC (permalink / raw) To: Sebastian Reichel, Rob Herring, Krzysztof Kozlowski, Conor Dooley Cc: linux-pm, devicetree, linux-kernel, Nizat Mikayilli From: Nizat Mikayilli <nacht@dxdmail.cc> Add a driver for the fuel gauge of the Silicon Mitus SM5714. The state of charge, voltage and current are read through an SRAM window, and the SRAM read address is the only register the driver writes. The status is derived from the battery current, as in s2mu005-battery. The driver does not load a battery model, so capacity is only reported once Samsung's software has initialized the gauge. The register layout and scaling come from Samsung's GPL driver for the Galaxy Tab S9. Signed-off-by: Nizat Mikayilli <nacht@dxdmail.cc> --- MAINTAINERS | 1 + drivers/power/supply/Kconfig | 10 ++ drivers/power/supply/Makefile | 1 + drivers/power/supply/sm5714-battery.c | 221 ++++++++++++++++++++++++++++++++++ 4 files changed, 233 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 2875017d21c2..d9f37c040453 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25718,6 +25718,7 @@ M: Nizat Mikayilli <nacht@dxdmail.cc> L: linux-pm@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/power/supply/siliconmitus,sm5714-fuel-gauge.yaml +F: drivers/power/supply/sm5714-battery.c SILICON MOTION SM712 FRAME BUFFER DRIVER M: Sudip Mukherjee <sudipm.mukherjee@gmail.com> diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index ee7dc5f2617a..c92d09a4f1c9 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -240,6 +240,16 @@ config BATTERY_S2MU005 This driver, if built as a module, will be called s2mu005-fuel-gauge. +config BATTERY_SM5714 + tristate "Silicon Mitus SM5714 fuel gauge driver" + depends on I2C + help + Say Y to enable support for the fuel gauge of the Silicon Mitus + SM5714, which reports the battery state of charge, voltage and + current. + + This driver, if built as a module, will be called sm5714-battery. + config BATTERY_COLLIE tristate "Sharp SL-5500 (collie) battery" depends on SA1100_COLLIE && MCP_UCB1200 diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index f3c0d1006c1a..459f9cbe5f30 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_BATTERY_QCOM_BATTMGR) += qcom_battmgr.o obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o obj-$(CONFIG_BATTERY_SAMSUNG_SDI) += samsung-sdi-battery.o obj-$(CONFIG_BATTERY_S2MU005) += s2mu005-battery.o +obj-$(CONFIG_BATTERY_SM5714) += sm5714-battery.o obj-$(CONFIG_BATTERY_COLLIE) += collie_battery.o obj-$(CONFIG_BATTERY_INGENIC) += ingenic-battery.o obj-$(CONFIG_BATTERY_INTEL_DC_TI) += intel_dc_ti_battery.o diff --git a/drivers/power/supply/sm5714-battery.c b/drivers/power/supply/sm5714-battery.c new file mode 100644 index 000000000000..bd8cb97d81df --- /dev/null +++ b/drivers/power/supply/sm5714-battery.c @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/bits.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/math.h> +#include <linux/minmax.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/power_supply.h> +#include <linux/property.h> +#include <linux/units.h> + +#define SM5714_FG_REG_DEVICE_ID 0x00 +#define SM5714_FG_REG_SYSTEM_STATUS 0x10 +#define SM5714_FG_REG_AUX_STAT 0x22 +#define SM5714_FG_REG_SRAM_RADDR 0x8c +#define SM5714_FG_REG_SRAM_RDATA 0x8d + +#define SM5714_FG_SYSTEM_STATUS_INIT BIT(12) +#define SM5714_FG_AUX_STAT_CURRENT_X2_5 BIT(10) + +#define SM5714_FG_SRAM_SOC 0x00 +#define SM5714_FG_SRAM_VBAT 0x03 +#define SM5714_FG_SRAM_CURRENT 0x05 +#define SM5714_FG_SRAM_VBAT_AVG 0x08 +#define SM5714_FG_SRAM_CURRENT_AVG 0x09 + +struct sm5714_fg { + struct i2c_client *client; + struct mutex lock; /* SRAM read address and data */ +}; + +static int sm5714_fg_read_sram(struct sm5714_fg *fg, u8 addr) +{ + int ret; + + guard(mutex)(&fg->lock); + + ret = i2c_smbus_write_word_data(fg->client, SM5714_FG_REG_SRAM_RADDR, addr); + if (ret) + return ret; + + return i2c_smbus_read_word_data(fg->client, SM5714_FG_REG_SRAM_RDATA); +} + +static int sm5714_fg_sign_mag(int raw, int mul, int div) +{ + int val = mult_frac(raw & 0x7fff, mul, div); + + return raw & BIT(15) ? -val : val; +} + +static int sm5714_fg_get_voltage(struct sm5714_fg *fg, u8 addr, int *val) +{ + int ret; + + ret = sm5714_fg_read_sram(fg, addr); + if (ret < 0) + return ret; + + *val = 2700000 + sm5714_fg_sign_mag(ret, 10000, 109); + + return 0; +} + +static int sm5714_fg_get_current(struct sm5714_fg *fg, u8 addr, int *val) +{ + int aux, ret; + + ret = sm5714_fg_read_sram(fg, addr); + if (ret < 0) + return ret; + + aux = i2c_smbus_read_word_data(fg->client, SM5714_FG_REG_AUX_STAT); + if (aux < 0) + return aux; + + *val = sm5714_fg_sign_mag(ret, MICRO, 2044); + if (aux & SM5714_FG_AUX_STAT_CURRENT_X2_5) + *val = *val * 5 / 2; + + return 0; +} + +static int sm5714_fg_get_capacity(struct sm5714_fg *fg, int *val) +{ + int ret; + + ret = i2c_smbus_read_word_data(fg->client, SM5714_FG_REG_SYSTEM_STATUS); + if (ret < 0) + return ret; + + if (!(ret & SM5714_FG_SYSTEM_STATUS_INIT)) + return -ENODATA; + + ret = sm5714_fg_read_sram(fg, SM5714_FG_SRAM_SOC); + if (ret < 0) + return ret; + + *val = min(ret >> 8, 100); + + return 0; +} + +static int sm5714_fg_get_status(struct sm5714_fg *fg, int *val) +{ + int now, avg, capacity, ret; + + ret = sm5714_fg_get_current(fg, SM5714_FG_SRAM_CURRENT, &now); + if (ret) + return ret; + + ret = sm5714_fg_get_current(fg, SM5714_FG_SRAM_CURRENT_AVG, &avg); + if (ret) + return ret; + + /* Use the average as well, to ride out noise around 0 A */ + if (now < 0 && avg < 0) + *val = POWER_SUPPLY_STATUS_DISCHARGING; + else if (!now) + *val = POWER_SUPPLY_STATUS_NOT_CHARGING; + else if (!sm5714_fg_get_capacity(fg, &capacity) && capacity > 98) + *val = POWER_SUPPLY_STATUS_FULL; + else + *val = POWER_SUPPLY_STATUS_CHARGING; + + return 0; +} + +static const enum power_supply_property sm5714_fg_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_AVG, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_AVG, +}; + +static int sm5714_fg_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct sm5714_fg *fg = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + return sm5714_fg_get_status(fg, &val->intval); + case POWER_SUPPLY_PROP_CAPACITY: + return sm5714_fg_get_capacity(fg, &val->intval); + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + return sm5714_fg_get_voltage(fg, SM5714_FG_SRAM_VBAT, &val->intval); + case POWER_SUPPLY_PROP_VOLTAGE_AVG: + return sm5714_fg_get_voltage(fg, SM5714_FG_SRAM_VBAT_AVG, &val->intval); + case POWER_SUPPLY_PROP_CURRENT_NOW: + return sm5714_fg_get_current(fg, SM5714_FG_SRAM_CURRENT, &val->intval); + case POWER_SUPPLY_PROP_CURRENT_AVG: + return sm5714_fg_get_current(fg, SM5714_FG_SRAM_CURRENT_AVG, &val->intval); + default: + return -EINVAL; + } +} + +static const struct power_supply_desc sm5714_fg_desc = { + .name = "sm5714-fuel-gauge", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = sm5714_fg_props, + .num_properties = ARRAY_SIZE(sm5714_fg_props), + .get_property = sm5714_fg_get_property, +}; + +static int sm5714_fg_probe(struct i2c_client *client) +{ + struct power_supply_config psy_cfg = {}; + struct device *dev = &client->dev; + struct power_supply *psy; + struct sm5714_fg *fg; + int ret; + + fg = devm_kzalloc(dev, sizeof(*fg), GFP_KERNEL); + if (!fg) + return -ENOMEM; + + fg->client = client; + + ret = i2c_smbus_read_word_data(client, SM5714_FG_REG_DEVICE_ID); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to read device ID\n"); + + ret = devm_mutex_init(dev, &fg->lock); + if (ret) + return ret; + + psy_cfg.drv_data = fg; + psy_cfg.fwnode = dev_fwnode(dev); + + psy = devm_power_supply_register(dev, &sm5714_fg_desc, &psy_cfg); + if (IS_ERR(psy)) + return dev_err_probe(dev, PTR_ERR(psy), "failed to register power supply\n"); + + return 0; +} + +static const struct of_device_id sm5714_fg_of_match[] = { + { .compatible = "siliconmitus,sm5714-fuel-gauge" }, + { } +}; +MODULE_DEVICE_TABLE(of, sm5714_fg_of_match); + +static struct i2c_driver sm5714_fg_driver = { + .driver = { + .name = "sm5714-fuel-gauge", + .of_match_table = sm5714_fg_of_match, + }, + .probe = sm5714_fg_probe, +}; +module_i2c_driver(sm5714_fg_driver); + +MODULE_AUTHOR("Nizat Mikayilli <nacht@dxdmail.cc>"); +MODULE_DESCRIPTION("Silicon Mitus SM5714 fuel gauge driver"); +MODULE_LICENSE("GPL"); -- 2.56.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-11-11 16:12 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-11-11 15:34 [PATCH 0/2] power: supply: Add Silicon Mitus SM5714 fuel gauge Asdlerto Korry via B4 Relay 2026-11-11 15:34 ` [PATCH 1/2] dt-bindings: " Asdlerto Korry via B4 Relay 2026-11-11 15:35 ` [PATCH 2/2] power: supply: Add Silicon Mitus SM5714 fuel gauge driver Asdlerto Korry via B4 Relay 2026-11-11 16:12 ` sashiko-bot -- strict thread matches above, loose matches on Subject: below -- 2026-11-08 11:22 [PATCH 0/2] power: supply: Add Silicon Mitus SM5714 fuel gauge Nizat Mikayilli via B4 Relay 2026-11-08 11:22 ` [PATCH 2/2] power: supply: Add Silicon Mitus SM5714 fuel gauge driver Nizat Mikayilli via B4 Relay
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox