Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Anze <aanzdev@gmail.com>
Date: Wed, 3 Jun 2026 19:40:00 +0200
Subject: [PATCH] power: supply: qcom_battmgr: support charge_behaviour
(USB power-path bypass) on sm8550

The battery management firmware on sm8550-class platforms (e.g. the AYN
Odin 3, SM8750, which uses the sm8550 battmgr variant) can keep the system
running from the USB input while leaving the battery untouched - neither
charging nor discharging. The vendor (downstream qti_battery_charger)
exposes this as the OEM USB property "usb_charge_now": writing 0 inhibits
charging from the input (power-path bypass), 1 restores normal charging.

The PMIC GLINK request for this is identical to the one qcom_battmgr
already builds for every other property (owner BATTMGR, REQ_RESP, the
{battery, property, value} body); only the opcode (USB_PROPERTY_SET) and
the property id (14) differ. Expose it through the standard
POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR interface:

auto -> usb_charge_now = 1 (normal charging)
inhibit-charge -> usb_charge_now = 0 (run from the adapter, battery floats)

This adds /sys/class/power_supply/battery/charge_behaviour and is wired
only into the sm8550 battery psy, so other variants are unaffected.

Verified on an AYN Odin 3: with "inhibit-charge" and the charger online,
battery status goes to Discharging / charge_type N/A with battery current
~0, matching the behaviour of the vendor "bypass charging" toggle; "auto"
resumes charging.

Signed-off-by: Anze <aanzdev@gmail.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
--- a/drivers/power/supply/qcom_battmgr.c
+++ b/drivers/power/supply/qcom_battmgr.c
@@ -86,6 +86,13 @@
#define USB_ADAP_TYPE 7
#define USB_MOISTURE_DET_EN 8
#define USB_MOISTURE_DET_STS 9
+/*
+ * OEM USB property exposed by the battery management firmware on sm8550-class
+ * platforms: 1 lets the charger replenish the battery from the USB input,
+ * 0 inhibits battery charging while keeping the system powered from the input
+ * (power-path bypass). Mirrors the vendor "usb_charge_now" control.
+ */
+#define USB_CHARGE_ENABLE 14

#define BATTMGR_WLS_PROPERTY_GET 0x34
#define BATTMGR_WLS_PROPERTY_SET 0x35
@@ -325,6 +332,9 @@

bool service_up;

+ /* cached POWER_SUPPLY_CHARGE_BEHAVIOUR_* value (sm8550 variant) */
+ unsigned int charge_behaviour;
+
struct qcom_battmgr_info info;
struct qcom_battmgr_status status;
struct qcom_battmgr_ac ac;
@@ -633,6 +643,9 @@
case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
val->intval = battmgr->info.charge_ctrl_end;
break;
+ case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
+ val->intval = battmgr->charge_behaviour;
+ break;
case POWER_SUPPLY_PROP_MANUFACTURE_YEAR:
val->intval = battmgr->info.year;
break;
@@ -756,12 +769,46 @@
return 0;
}

+/*
+ * Toggle whether the battery is charged from the USB input. Setting
+ * "inhibit-charge" stops charging while the system keeps running from the
+ * adapter (the battery neither charges nor discharges) - i.e. power-path
+ * bypass; "auto" restores normal charging.
+ */
+static int qcom_battmgr_set_charge_behaviour(struct qcom_battmgr *battmgr,
+ int behaviour)
+{
+ u32 value;
+ int ret;
+
+ switch (behaviour) {
+ case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO:
+ value = 1;
+ break;
+ case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE:
+ value = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mutex_lock(&battmgr->lock);
+ ret = qcom_battmgr_request_property(battmgr, BATTMGR_USB_PROPERTY_SET,
+ USB_CHARGE_ENABLE, value);
+ mutex_unlock(&battmgr->lock);
+ if (!ret)
+ battmgr->charge_behaviour = behaviour;
+
+ return ret;
+}
+
static int qcom_battmgr_bat_is_writeable(struct power_supply *psy,
enum power_supply_property psp)
{
switch (psp) {
case POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD:
case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
+ case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
return 1;
default:
return 0;
@@ -784,6 +831,8 @@
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_BEHAVIOUR:
+ return qcom_battmgr_set_charge_behaviour(battmgr, pval->intval);
default:
return -EINVAL;
}
@@ -918,6 +967,7 @@
POWER_SUPPLY_PROP_POWER_NOW,
POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD,
POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD,
+ POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR,
};

static const struct power_supply_desc sm8550_bat_psy_desc = {
@@ -925,6 +975,8 @@
.type = POWER_SUPPLY_TYPE_BATTERY,
.properties = sm8550_bat_props,
.num_properties = ARRAY_SIZE(sm8550_bat_props),
+ .charge_behaviours = BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) |
+ BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE),
.get_property = qcom_battmgr_bat_get_property,
.set_property = qcom_battmgr_bat_set_property,
.property_is_writeable = qcom_battmgr_bat_is_writeable,
Loading