* [PATCH 1/2] power: supply: qcom_battmgr: fix the battery current sign on Kaanapali
@ 2026-10-16 12:48 Joonhoe Kim
2026-10-16 12:48 ` [PATCH 2/2] power: supply: qcom_battmgr: expose the charge current limit Joonhoe Kim
2026-10-16 13:34 ` [PATCH 1/2] power: supply: qcom_battmgr: fix the battery current sign on Kaanapali Konrad Dybcio
0 siblings, 2 replies; 3+ messages in thread
From: Joonhoe Kim @ 2026-10-16 12:48 UTC (permalink / raw)
To: sre
Cc: konrad.dybcio, kamal.wadhwa, anjelique.melendez, dhruvin.rajpura,
andersson, dmitry.baryshkov, linux-arm-msm, linux-pm,
linux-kernel
The Kaanapali battery firmware reports the battery current with
discharge positive: on a Lenovo Legion Tab Y700 Gen 5 current_now reads
-9.1 A while charging and +0.57 A on battery. The power_supply ABI has
charging positive, and upower reports such a charging battery as
discharging.
Other firmware behind the same callback uses the ABI sign [1], so
negate the value only for qcom,kaanapali-pmic-glink.
Fixes: 16a7c32e586e ("power: supply: qcom_battmgr: Add support for Glymur and Kaanapali")
Link: https://lore.kernel.org/all/20260829054546.86210-1-jan.brummer@tabos.org/ [1]
Signed-off-by: Joonhoe Kim <26rote@gmail.com>
---
I could only check this on one device, not on MTP or QRD firmware.
Anjelique, could you confirm the sign there?
Both patches apply before or after my two battmgr patches still under
review (USB_ADAP_TYPE v2 and the USB input current limit).
drivers/power/supply/qcom_battmgr.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/power/supply/qcom_battmgr.c b/drivers/power/supply/qcom_battmgr.c
index a7237ba09be1..0d55458b2881 100644
--- a/drivers/power/supply/qcom_battmgr.c
+++ b/drivers/power/supply/qcom_battmgr.c
@@ -313,6 +313,7 @@ struct qcom_battmgr {
struct pmic_glink_client *client;
enum qcom_battmgr_variant variant;
+ bool current_inverted;
struct power_supply *ac_psy;
struct power_supply *bat_psy;
@@ -1435,6 +1436,8 @@ static void qcom_battmgr_sm8350_callback(struct qcom_battmgr *battmgr,
break;
case BATT_CURR_NOW:
battmgr->status.current_now = le32_to_cpu(resp->intval.value);
+ if (battmgr->current_inverted)
+ battmgr->status.current_now = -battmgr->status.current_now;
break;
case BATT_TEMP:
val = le32_to_cpu(resp->intval.value);
@@ -1659,6 +1662,9 @@ static int qcom_battmgr_probe(struct auxiliary_device *adev,
else
battmgr->variant = QCOM_BATTMGR_SM8350;
+ battmgr->current_inverted = device_is_compatible(dev->parent,
+ "qcom,kaanapali-pmic-glink");
+
ret = qcom_battmgr_charge_control_thresholds_init(battmgr);
if (ret < 0)
return dev_err_probe(dev, ret,
base-commit: b810335a0699d1810683796b0e3ffe8827ddfe6c
--
2.43.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH 2/2] power: supply: qcom_battmgr: expose the charge current limit
2026-10-16 12:48 [PATCH 1/2] power: supply: qcom_battmgr: fix the battery current sign on Kaanapali Joonhoe Kim
@ 2026-10-16 12:48 ` Joonhoe Kim
2026-10-16 13:34 ` [PATCH 1/2] power: supply: qcom_battmgr: fix the battery current sign on Kaanapali Konrad Dybcio
1 sibling, 0 replies; 3+ messages in thread
From: Joonhoe Kim @ 2026-10-16 12:48 UTC (permalink / raw)
To: sre
Cc: konrad.dybcio, kamal.wadhwa, anjelique.melendez, dhruvin.rajpura,
andersson, dmitry.baryshkov, linux-arm-msm, linux-pm,
linux-kernel
The battery manager firmware takes a charge current limit in uA
(BATT_CHG_CTRL_LIM) and reports the highest value it accepts
(BATT_CHG_CTRL_LIM_MAX). Expose them on SM8550 class batteries as
charge_control_limit and charge_control_limit_max, clamping writes to
the maximum.
Also handle the BATTMGR_BAT_PROPERTY_SET response, which was logged as
an unknown message, and return its result.
On a Lenovo Legion Tab Y700 Gen 5 with a 45 W PD charger, limits of
500 mA and 100 mA give about 480 mA and 90 mA of charge current while
the PD contract stays at 9 V. A limit of 0 stops charging, and this
firmware does not resume when the limit is raised again until the end
threshold is moved below the capacity and back.
Signed-off-by: Joonhoe Kim <26rote@gmail.com>
---
The downstream qti_battery_charger driver also writes this property in
uA. The thermal mitigation series [1] used it as a level index, and
Konrad noted that the ABI defines charge_control_limit in uA; this
follows the ABI. Only tested on Kaanapali.
[1] https://lore.kernel.org/all/20260609-cooling_device_reg-v1-2-e15bddcb0086@qti.qualcomm.com/
drivers/power/supply/qcom_battmgr.c | 52 +++++++++++++++++++++++++++++
1 file changed, 52 insertions(+)
diff --git a/drivers/power/supply/qcom_battmgr.c b/drivers/power/supply/qcom_battmgr.c
index 0d55458b2881..69931827bc08 100644
--- a/drivers/power/supply/qcom_battmgr.c
+++ b/drivers/power/supply/qcom_battmgr.c
@@ -260,6 +260,8 @@ struct qcom_battmgr_info {
unsigned int charge_count;
unsigned int charge_ctrl_start;
unsigned int charge_ctrl_end;
+ unsigned int charge_ctrl_limit;
+ unsigned int charge_ctrl_limit_max;
char model_number[BATTMGR_STRING_LEN];
char serial_number[BATTMGR_STRING_LEN];
char oem_info[BATTMGR_STRING_LEN];
@@ -451,6 +453,8 @@ static const u8 sm8350_bat_prop_map[] = {
[POWER_SUPPLY_PROP_POWER_NOW] = BATT_POWER_NOW,
[POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD] = BATT_CHG_CTRL_START_THR,
[POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD] = BATT_CHG_CTRL_END_THR,
+ [POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT] = BATT_CHG_CTRL_LIM,
+ [POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX] = BATT_CHG_CTRL_LIM_MAX,
};
static int qcom_battmgr_bat_sm8350_update(struct qcom_battmgr *battmgr,
@@ -635,6 +639,12 @@ static int qcom_battmgr_bat_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
val->intval = battmgr->info.charge_ctrl_end;
break;
+ case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
+ val->intval = battmgr->info.charge_ctrl_limit;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
+ val->intval = battmgr->info.charge_ctrl_limit_max;
+ break;
case POWER_SUPPLY_PROP_MANUFACTURE_YEAR:
val->intval = battmgr->info.year;
break;
@@ -729,6 +739,26 @@ static int qcom_battmgr_set_charge_end_threshold(struct qcom_battmgr *battmgr, i
return 0;
}
+static int qcom_battmgr_set_charge_control_limit(struct qcom_battmgr *battmgr, int limit)
+{
+ int ret;
+
+ if (limit < 0)
+ return -EINVAL;
+
+ mutex_lock(&battmgr->lock);
+ ret = qcom_battmgr_request_property(battmgr, BATTMGR_BAT_PROPERTY_GET,
+ BATT_CHG_CTRL_LIM_MAX, 0);
+ if (!ret) {
+ limit = min_t(u32, limit, battmgr->info.charge_ctrl_limit_max);
+ ret = qcom_battmgr_request_property(battmgr, BATTMGR_BAT_PROPERTY_SET,
+ BATT_CHG_CTRL_LIM, limit);
+ }
+ mutex_unlock(&battmgr->lock);
+
+ return ret;
+}
+
static int qcom_battmgr_charge_control_thresholds_init(struct qcom_battmgr *battmgr)
{
int ret;
@@ -764,6 +794,7 @@ static int qcom_battmgr_bat_is_writeable(struct power_supply *psy,
switch (psp) {
case POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD:
case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
+ case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
return 1;
default:
return 0;
@@ -786,6 +817,8 @@ static int qcom_battmgr_bat_set_property(struct power_supply *psy,
return qcom_battmgr_set_charge_start_threshold(battmgr, pval->intval);
case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
return qcom_battmgr_set_charge_end_threshold(battmgr, pval->intval);
+ case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
+ return qcom_battmgr_set_charge_control_limit(battmgr, pval->intval);
default:
return -EINVAL;
}
@@ -921,6 +954,8 @@ static const enum power_supply_property sm8550_bat_props[] = {
POWER_SUPPLY_PROP_POWER_NOW,
POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD,
POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD,
+ POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
+ POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
};
static const struct power_supply_desc sm8550_bat_psy_desc = {
@@ -1479,11 +1514,28 @@ static void qcom_battmgr_sm8350_callback(struct qcom_battmgr *battmgr,
case BATT_CHG_CTRL_END_THR:
battmgr->info.charge_ctrl_end = le32_to_cpu(resp->intval.value);
break;
+ case BATT_CHG_CTRL_LIM:
+ battmgr->info.charge_ctrl_limit = le32_to_cpu(resp->intval.value);
+ break;
+ case BATT_CHG_CTRL_LIM_MAX:
+ battmgr->info.charge_ctrl_limit_max = le32_to_cpu(resp->intval.value);
+ break;
default:
dev_warn(battmgr->dev, "unknown property %#x\n", property);
break;
}
break;
+ case BATTMGR_BAT_PROPERTY_SET:
+ if (payload_len != sizeof(resp->intval)) {
+ dev_warn(battmgr->dev,
+ "invalid payload length for battery property set: %zd\n",
+ payload_len);
+ battmgr->error = -ENODATA;
+ return;
+ }
+
+ battmgr->error = le32_to_cpu(resp->intval.result) ? -EIO : 0;
+ break;
case BATTMGR_USB_PROPERTY_GET:
property = le32_to_cpu(resp->intval.property);
if (payload_len != sizeof(resp->intval)) {
--
2.43.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH 1/2] power: supply: qcom_battmgr: fix the battery current sign on Kaanapali
2026-10-16 12:48 [PATCH 1/2] power: supply: qcom_battmgr: fix the battery current sign on Kaanapali Joonhoe Kim
2026-10-16 12:48 ` [PATCH 2/2] power: supply: qcom_battmgr: expose the charge current limit Joonhoe Kim
@ 2026-10-16 13:34 ` Konrad Dybcio
1 sibling, 0 replies; 3+ messages in thread
From: Konrad Dybcio @ 2026-10-16 13:34 UTC (permalink / raw)
To: Joonhoe Kim, sre, Jingyi Wang
Cc: kamal.wadhwa, anjelique.melendez, dhruvin.rajpura, andersson,
dmitry.baryshkov, linux-arm-msm, linux-pm, linux-kernel
On 10/16/26 2:48 PM, Joonhoe Kim wrote:
> The Kaanapali battery firmware reports the battery current with
> discharge positive: on a Lenovo Legion Tab Y700 Gen 5 current_now reads
> -9.1 A while charging and +0.57 A on battery. The power_supply ABI has
> charging positive, and upower reports such a charging battery as
> discharging.
>
> Other firmware behind the same callback uses the ABI sign [1], so
> negate the value only for qcom,kaanapali-pmic-glink.
>
> Fixes: 16a7c32e586e ("power: supply: qcom_battmgr: Add support for Glymur and Kaanapali")
> Link: https://lore.kernel.org/all/20260829054546.86210-1-jan.brummer@tabos.org/ [1]
> Signed-off-by: Joonhoe Kim <26rote@gmail.com>
> ---
> I could only check this on one device, not on MTP or QRD firmware.
> Anjelique, could you confirm the sign there?
>
> Both patches apply before or after my two battmgr patches still under
> review (USB_ADAP_TYPE v2 and the USB input current limit).
+Jingyi could you please check if this applies to QRD/MTP?
Konrad
>
> drivers/power/supply/qcom_battmgr.c | 6 ++++++
> 1 file changed, 6 insertions(+)
>
> diff --git a/drivers/power/supply/qcom_battmgr.c b/drivers/power/supply/qcom_battmgr.c
> index a7237ba09be1..0d55458b2881 100644
> --- a/drivers/power/supply/qcom_battmgr.c
> +++ b/drivers/power/supply/qcom_battmgr.c
> @@ -313,6 +313,7 @@ struct qcom_battmgr {
> struct pmic_glink_client *client;
>
> enum qcom_battmgr_variant variant;
> + bool current_inverted;
>
> struct power_supply *ac_psy;
> struct power_supply *bat_psy;
> @@ -1435,6 +1436,8 @@ static void qcom_battmgr_sm8350_callback(struct qcom_battmgr *battmgr,
> break;
> case BATT_CURR_NOW:
> battmgr->status.current_now = le32_to_cpu(resp->intval.value);
> + if (battmgr->current_inverted)
> + battmgr->status.current_now = -battmgr->status.current_now;
> break;
> case BATT_TEMP:
> val = le32_to_cpu(resp->intval.value);
> @@ -1659,6 +1662,9 @@ static int qcom_battmgr_probe(struct auxiliary_device *adev,
> else
> battmgr->variant = QCOM_BATTMGR_SM8350;
>
> + battmgr->current_inverted = device_is_compatible(dev->parent,
> + "qcom,kaanapali-pmic-glink");
> +
> ret = qcom_battmgr_charge_control_thresholds_init(battmgr);
> if (ret < 0)
> return dev_err_probe(dev, ret,
>
> base-commit: b810335a0699d1810683796b0e3ffe8827ddfe6c
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-10-16 13:35 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-16 12:48 [PATCH 1/2] power: supply: qcom_battmgr: fix the battery current sign on Kaanapali Joonhoe Kim
2026-10-16 12:48 ` [PATCH 2/2] power: supply: qcom_battmgr: expose the charge current limit Joonhoe Kim
2026-10-16 13:34 ` [PATCH 1/2] power: supply: qcom_battmgr: fix the battery current sign on Kaanapali Konrad Dybcio
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox