From nobody Sat Jul 25 18:53:41 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9EA62423E96; Tue, 14 Jul 2026 23:09:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784070558; cv=none; b=rUJLQf3bkoxTsNKbYf+vtxi+WsHITegkiqM/IiuGJUtNgRFYChMbU8KWm6yj6o/NbH++ZWdhHFHInfgj/vYiZ42joAJ6j5IiDF5lGdpzgY428t6zVhy1jswrnPwRILeYAue5vW4kRNs7qOtb8qwA8kK9c8qxjyvaaJ12NsGTdr4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784070558; c=relaxed/simple; bh=d3Zkpg6ooIi1Dl3ywQQRqHKHopgFBoj8kq9jOt0J++A=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:To:Cc; b=KfcxGxTYB6c3pW7QYy52OAy37154xd9mWI7HG1AsuQwDNqauDTZK3D4UF/Cf90y3gjB15iYq9VbC1R4i3pwzjHNhJLcHId3wnGGOHEtzrsZ+g1Bw7Fr32njOeCueYvC8iGt7WpBVU3Tn126BuzW5fd+YpJtSJGZrJJgzrWCDiaY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=qz/rIuKV; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="qz/rIuKV" Received: by smtp.kernel.org (Postfix) with ESMTPS id 32C59C2BCB8; Tue, 14 Jul 2026 23:09:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784070558; bh=d3Zkpg6ooIi1Dl3ywQQRqHKHopgFBoj8kq9jOt0J++A=; h=From:Date:Subject:To:Cc:Reply-To:From; b=qz/rIuKVt9VgUzltP7KqeQzoNKPLeO0pn3KZo0oKMSLmKz8h9m0saAkpph0DuotOY 5fcW1l5zY+4+tCnCmCIvf3qJgogoicpoJxChgCDC2UEQ9U5+P69h6Xh6aV6C1MW/V9 +kSuj+pRvd3Ks3ab8B7Aw8s/rEAjVM2F+4fGCFWL9UvFP7afPD+ZfgekPIYJTKFYfG hje3Us++zeZgM+pXl5EJrfDjklpIf9QLva7TqZ/Fl7xla6FNBfdhC5Ijd80q3E/LGZ CrNjpkcJA5EyirdF0A1pPbSPyJLUxqJo5kZSFKWjVJ/8rIwB+5f9UFQbDESZTsWXJj nVzxbmGSgWecg== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 203EAC44507; Tue, 14 Jul 2026 23:09:18 +0000 (UTC) From: Amit Sunil Dhamne via B4 Relay Date: Tue, 14 Jul 2026 23:06:47 +0000 Subject: [PATCH] usb: typec: tcpm: Add support for Battery Cap response message Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260714-batt-caps-upstream-v1-1-c86f0a7fbbda@google.com> X-B4-Tracking: v=1; b=H4sIAAfBVmoC/0XMSw7CIBSF4a00jCXhIVS7FdMB93JRBn0ItDFpu nexHTj8T3K+jWVKkTLrmo0lWmOO01hDXhqGLzc+iUdfmymhrGjllYMrhaObM1/mXBK5gZs2KAS 6EXnN6nFOFOLnQB/92YneS7XLOf7prjlgI+QJ5+LKkrm0YFsrUQDIbjU/FFwmjtMwxNI1grSBO 1hCEQKiIaURfK17MGiVct5oBUazft+/2wLvs+MAAAA= X-Change-ID: 20260714-batt-caps-upstream-57f2cbe8eed3 To: Badhri Jagan Sridharan , Heikki Krogerus , Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Kyle Tso , RD Babiera , Amit Sunil Dhamne X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1784070557; l=7924; i=amitsd@google.com; s=20241031; h=from:subject:message-id; bh=Pd+vfdxONEvemhiZz0c9A9V4n9jluqOyO39dTwdDP0c=; b=9n+caiMryAdKHXcSCaxDIBfxtzqrPZcMIZ3EDm7Mpi1Nk7eEcXp1BD10k3q8YsyEYWOSCX3wP F2DmJWDgiPcB7leWtkAeKUtcBIw0BO9+yeH0wBUhe/30vY6vVBRPMal X-Developer-Key: i=amitsd@google.com; a=ed25519; pk=wD+XZSST4dmnNZf62/lqJpLm7fiyT8iv462zmQ3H6bI= X-Endpoint-Received: by B4 Relay for amitsd@google.com/20241031 with auth_id=262 X-Original-From: Amit Sunil Dhamne Reply-To: amitsd@google.com From: Amit Sunil Dhamne Add support for responding to Get_Battery_Cap (extended) request with a a Battery_Capabilities (extended) msg. The requester will request Battery Cap for a specific battery using an index in Get_Battery_Cap. In case of failure to identify battery, TCPM shall reply with an appropriate message indicating so. As the Battery Cap Data Block size is 9 Bytes (lesser than MaxExtendedMsgChunkLen of 26B), only a single chunk is required to complete the AMS. Support for Battery_Capabilities message is required for sinks that contain battery as specified in USB PD Rev3.1 v1.8 ("Applicability of Data Messages" section). Signed-off-by: Amit Sunil Dhamne Reviewed-by: Badhri Jagan Sridharan Acked-by: Heikki Krogerus --- This patch depends on the series [1]. [1] https://lore.kernel.org/all/20260714-batt-status-v5-0-9de4aa900b69@goog= le.com/ --- drivers/usb/typec/tcpm/tcpm.c | 108 +++++++++++++++++++++++++++++++++++++-= ---- include/linux/usb/pd.h | 22 +++++++++ 2 files changed, 117 insertions(+), 13 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index cd33ee131ebd..393266dd55f8 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -234,7 +234,8 @@ enum pd_msg_request { PD_MSG_DATA_SOURCE_CAP, PD_MSG_DATA_REV, PD_MSG_EXT_SINK_CAP_EXT, - PD_MSG_DATA_BATT_STATUS + PD_MSG_DATA_BATT_STATUS, + PD_MSG_EXT_BATT_CAP, }; =20 enum adev_actions { @@ -1559,6 +1560,14 @@ static int tcpm_pd_send_sink_cap_ext(struct tcpm_por= t *port) return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); } =20 +static u16 tcpm_charge_to_energy(int charge, int voltage) +{ + u64 energy =3D div_u64((u64)charge * voltage, 1000000); + + /* Battery telemetry is reported in increments of 0.1Wh */ + return (u16)UW_TO_W(energy * 10); +} + static int tcpm_pd_send_batt_status(struct tcpm_port *port) { u16 present_charge =3D BATTERY_PROPERTY_UNKNOWN; @@ -1569,7 +1578,6 @@ static int tcpm_pd_send_batt_status(struct tcpm_port = *port) u8 charging_status =3D 0; struct pd_message msg; int ret, charge_now; - u64 energy_now; u32 bsdo; =20 tcpm_get_fixed_batt(port); @@ -1595,16 +1603,9 @@ static int tcpm_pd_send_batt_status(struct tcpm_port= *port) ret =3D power_supply_get_property(batt, POWER_SUPPLY_PROP_VOLTAGE_AVG, &val); - if (!ret) { - energy_now =3D div_u64((u64)charge_now * val.intval, - 1000000); - - /* - * Battery Present Charge is reported in - * increments of 0.1WH. - */ - present_charge =3D (u16)UW_TO_W(energy_now * 10); - } + if (!ret) + present_charge =3D tcpm_charge_to_energy(charge_now, + val.intval); } =20 ret =3D power_supply_get_property(batt, POWER_SUPPLY_PROP_STATUS, &val); @@ -1640,6 +1641,71 @@ static int tcpm_pd_send_batt_status(struct tcpm_port= *port) return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); } =20 +static int tcpm_pd_send_batt_cap(struct tcpm_port *port) +{ + u16 design_cap =3D BATTERY_PROPERTY_UNKNOWN; + u16 charge_cap =3D BATTERY_PROPERTY_UNKNOWN; + u32 batt_id =3D port->batt_request_id; + union power_supply_propval val; + struct batt_cap_ext_msg bcdb; + struct power_supply *batt; + bool invalid_ref =3D true; + struct pd_message msg; + u8 data_obj_cnt; + int ret, vol; + + tcpm_get_fixed_batt(port); + memset(&msg, 0, sizeof(msg)); + + if (batt_id >=3D port->fixed_batt_cnt || batt_id >=3D MAX_NUM_FIXED_BATT) + goto send_cap; + + invalid_ref =3D false; + batt =3D port->fixed_batt[batt_id]; + ret =3D power_supply_get_property(batt, POWER_SUPPLY_PROP_VOLTAGE_AVG, + &val); + if (!ret) { + vol =3D val.intval; + ret =3D power_supply_get_property(batt, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + &val); + if (!ret) + design_cap =3D tcpm_charge_to_energy(val.intval, vol); + + ret =3D power_supply_get_property(batt, + POWER_SUPPLY_PROP_CHARGE_FULL, + &val); + if (!ret) + charge_cap =3D tcpm_charge_to_energy(val.intval, vol); + } + +send_cap: + + /* + * As per the USB PD Rev3.1 v1.8 spec, if a battery VID (assigned by the + * USB-IF) does not exist or an invalid battery reference is made by the + * requestor, then set the VID field to 0xffff. If the VID field is + * 0xffff, set the PID field to 0. + */ + bcdb.vid =3D BATTERY_PROPERTY_UNKNOWN; + bcdb.pid =3D 0; + bcdb.batt_design_cap =3D cpu_to_le16(design_cap); + bcdb.batt_last_chg_cap =3D cpu_to_le16(charge_cap); + bcdb.batt_type =3D invalid_ref ? BATT_CAP_BATT_TYPE_INVALID_REF : 0; + memcpy(msg.ext_msg.data, &bcdb, sizeof(bcdb)); + msg.ext_msg.header =3D PD_EXT_HDR_LE(sizeof(bcdb), + 0, /* Denotes if request chunk */ + 0, /* Chunk number */ + 1 /* Chunked */); + + data_obj_cnt =3D count_chunked_data_objs(sizeof(bcdb)); + msg.header =3D PD_HEADER_EXT_LE(PD_EXT_BATT_CAP, port->pwr_role, + port->data_role, port->negotiated_rev, + port->message_id, data_obj_cnt); + + return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); +} + static void mod_tcpm_delayed_work(struct tcpm_port *port, unsigned int del= ay_ms) { if (delay_ms) { @@ -4041,8 +4107,16 @@ static void tcpm_pd_ext_msg_request(struct tcpm_port= *port, tcpm_set_state(port, SOFT_RESET_SEND, 0); } break; - case PD_EXT_SOURCE_CAP_EXT: case PD_EXT_GET_BATT_CAP: + if (data_size >=3D 1) { + port->batt_request_id =3D ext_msg->data[0]; + tcpm_pd_handle_msg(port, PD_MSG_EXT_BATT_CAP, + GETTING_BATTERY_CAPABILITIES); + } else { + tcpm_set_state(port, SOFT_RESET_SEND, 0); + } + break; + case PD_EXT_SOURCE_CAP_EXT: case PD_EXT_BATT_CAP: case PD_EXT_GET_MANUFACTURER_INFO: case PD_EXT_MANUFACTURER_INFO: @@ -4261,6 +4335,14 @@ static bool tcpm_send_queued_message(struct tcpm_por= t *port) ret); tcpm_ams_finish(port); break; + case PD_MSG_EXT_BATT_CAP: + ret =3D tcpm_pd_send_batt_cap(port); + if (ret) + tcpm_log(port, + "Failed to send battery cap ret=3D%d", + ret); + tcpm_ams_finish(port); + break; default: break; } diff --git a/include/linux/usb/pd.h b/include/linux/usb/pd.h index afb9c2c65588..10ff4af7dedc 100644 --- a/include/linux/usb/pd.h +++ b/include/linux/usb/pd.h @@ -106,6 +106,9 @@ enum pd_ext_msg_type { #define PD_HEADER_LE(type, pwr, data, rev, id, cnt) \ cpu_to_le16(PD_HEADER((type), (pwr), (data), (rev), (id), (cnt), (0))) =20 +#define PD_HEADER_EXT_LE(type, pwr, data, rev, id, cnt) \ + cpu_to_le16(PD_HEADER((type), (pwr), (data), (rev), (id), (cnt), (1))) + static inline unsigned int pd_header_cnt(u16 header) { return (header >> PD_HEADER_CNT_SHIFT) & PD_HEADER_CNT_MASK; @@ -219,6 +222,25 @@ static inline u8 count_chunked_data_objs(u32 size) return ((size / 4) + (size % 4 ? 1 : 0)); } =20 +/** + * batt_cap_ext_msg - Battery capability extended PD message + * @vid: Battery Vendor ID (assigned by USB-IF) + * @pid: Battery Product ID (assigned by battery or device vendor) + * @batt_design_cap: Battery design capacity in 0.1Wh + * @batt_last_chg_cap: Battery last full charge capacity in 0.1Wh + * @batt_type: Battery Type. bit0 when set indicates invalid battery refer= ence. + * Rest of the bits are reserved. + */ +struct batt_cap_ext_msg { + __le16 vid; + __le16 pid; + __le16 batt_design_cap; + __le16 batt_last_chg_cap; + u8 batt_type; +} __packed; + +#define BATT_CAP_BATT_TYPE_INVALID_REF BIT(0) + /* Sink Caps Extended Data Block Version */ #define SKEDB_VER_1_0 1 =20 --- base-commit: 0e35b9b6ec0ffcc5e23cbdec09f5c622ad532b53 change-id: 20260714-batt-caps-upstream-57f2cbe8eed3 prerequisite-change-id: 20260501-batt-status-16b6761c0bb1:v5 prerequisite-patch-id: 39dd78a0bb18e681fcd8efb01b6879dbeb881a3a prerequisite-patch-id: 621fa71ad856f4b3246ec480ab17f8089b082932 Best regards, --=20 Amit Sunil Dhamne