From nobody Tue Apr 7 03:12:28 2026 Received: from mail-dl1-f73.google.com (mail-dl1-f73.google.com [74.125.82.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 41AD139281B for ; Mon, 16 Mar 2026 15:03:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773673389; cv=none; b=t06uvaJoABns4t/COt1/uUlRN1Mgyl8ji39tgKMHndZxdcnCGpG8gfN5QgJPOEaBbWDX71h2wO3dqZCHq1u2vNN2YGP4rgKsT9C+V3OjmcpNr/5CzKAPQhUUNk+FJqmzHffuRhp9a/hCC79blojzZMYb3rTJ6TESym8xHZKZsnM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773673389; c=relaxed/simple; bh=4emKMQpGnwxg+WazHqPYVUn+OWaTGfYXDzRjRzw8P5w=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=JBmKfesMn/Vy/kD+xA8mlX3NfQw+yIR00fHsQaxQU8DT6RJcSHTsgjsumeZ3KaaQ2z1NAMWaJAujxe2bgEIV2l/9HM6xwXM5fp+ji+E96hd4cdRVt/q7MFu13COoCQwXzCfC1VD82n4Gwn6b0c1/Te4xMa0Z8ivyVtzNt+ItmD8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--badhri.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=pSEAciOq; arc=none smtp.client-ip=74.125.82.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--badhri.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="pSEAciOq" Received: by mail-dl1-f73.google.com with SMTP id a92af1059eb24-128edc72e5bso3747250c88.1 for ; Mon, 16 Mar 2026 08:03:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1773673386; x=1774278186; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=w9RHSlwF1VNunYEv1VCERKRFyFUXQhzCOoRBUszxeFM=; b=pSEAciOqEB1zlJHZ1KvByjGM51kZmmniv2fzN/FhEQhYNmw2RWcGUu6H7R/SPQvG0H Yd7299Ra52hm8hWIHwikjYud55/rm2u0goyW3S3XGPra6dGbb4uFoV+rlw05vTLPHxyW v1X9l5+VDQJRCSWZEwRVTOkNOdJt3YP4YaAMSDSGFHJJ35nD2hnCDioUhblZ1NbqDkeV 4MhyqwczS41gHW9jk2VwkDxTm6fpcSLsP2byC9zArpy1ZU5rd/mtfTnxSwjCTxNBS33J oZH5yr3z4B1FJSs93dHOXMzQxI8SF3QRi0/Sast1keftI9h77nPXmAw4K1yxg1Jk0Hj8 lQBA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773673386; x=1774278186; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=w9RHSlwF1VNunYEv1VCERKRFyFUXQhzCOoRBUszxeFM=; b=ZBI40yVvbvdWZy+ocLaSn2Z3Q1sbQyIyUstKOgPj3nB6s5+xAgEvBitx1bkSqRImMX wI4z8a0a4mlB+BpWPx6qi1bNVEgXMGiQ22zFH2RciG2Mp2cwD8HTZIQPD3cWojVOrRwi PycpmRA/zJ0EgDEnfHc4SrMDyN8Cz7f7vzXrcbEiN/bV1uxhdHhMBK35ESYCeKG4kC+M lCvsX8mqFxLGmnkxOGU8rpOdQCciIJfWJ2g+tRhaiQVB4yH9EQeACLYeUE69tsxfZYA+ yKeyRgqe5p/1IYMDin0ay3+B3L5FAaNNJYMOU9/iaFRvtC9MQeiL7ecE4maDFZjTtXap b2wQ== X-Forwarded-Encrypted: i=1; AJvYcCXhQ6E872ELWt9vcXlNk6x9QH7nMCM9zdXnH5ijfmMMU+7NUx2114VRpoQAEzfoC9W5XLUDhp3pvffPbIE=@vger.kernel.org X-Gm-Message-State: AOJu0Yw8gaQ/sHtkh4CLJHS3l0rQuLMvOsKtR7N33e3rfo5aTxxl+biZ HwbeVbLQrpZ98qN3pP+It9ZpSwCDAq4ToeReFVp/bBh9rTV9wpdI2yrOZaNIK29LO+EXmmoDaPQ tErxWdg== X-Received: from dlbvv17.prod.google.com ([2002:a05:7022:5f11:b0:128:d41f:2c06]) (user=badhri job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:628b:b0:128:cf5c:5357 with SMTP id a92af1059eb24-128f3d0dce2mr6738893c88.2.1773673386080; Mon, 16 Mar 2026 08:03:06 -0700 (PDT) Date: Mon, 16 Mar 2026 15:02:59 +0000 In-Reply-To: <20260316150301.3892223-1-badhri@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260316150301.3892223-1-badhri@google.com> X-Mailer: git-send-email 2.53.0.851.ga537e3e6e9-goog Message-ID: <20260316150301.3892223-2-badhri@google.com> Subject: [PATCH v2 1/3] dt-bindings: connector: Add SPR AVS Sink APDO definitions From: Badhri Jagan Sridharan To: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Sebastian Reichel , Badhri Jagan Sridharan , Heikki Krogerus , Greg Kroah-Hartman , Amit Sunil Dhamne Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, linux-usb@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" USB Power Delivery 3.2 introduces a new power supply type SPR AVS. Add macro definitions for the USB Power Delivery (PD) Standard Power Range (SPR) Adjustable Voltage Supply (AVS) as a Sink Augmented Power Data Object (APDO) in the device tree bindings. Signed-off-by: Badhri Jagan Sridharan Acked-by: Rob Herring (Arm) --- .../bindings/connector/usb-connector.yaml | 5 +++-- include/dt-bindings/usb/pd.h | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/connector/usb-connector.yaml= b/Documentation/devicetree/bindings/connector/usb-connector.yaml index 901986de3e2b..a00b239960a3 100644 --- a/Documentation/devicetree/bindings/connector/usb-connector.yaml +++ b/Documentation/devicetree/bindings/connector/usb-connector.yaml @@ -364,8 +364,9 @@ $defs: "Universal Serial Bus Power Delivery Specification" chapter 6.4.= 1.3 Sink Capabilities Message, the order of each entry(PDO) should f= ollow the PD spec chapter 6.4.1. Required for power sink and power dual ro= le. User - can specify the sink PDO array via PDO_FIXED/BATT/VAR/PPS_APDO()= defined - in dt-bindings/usb/pd.h. + can specify the sink PDO array via + PDO_FIXED/BATT/VAR/PPS_APDO/SPR_AVS_SNK_APDO() defined in + dt-bindings/usb/pd.h. minItems: 1 maxItems: 7 $ref: /schemas/types.yaml#/definitions/uint32-array diff --git a/include/dt-bindings/usb/pd.h b/include/dt-bindings/usb/pd.h index 6cff2339bda3..1e64a1f563f9 100644 --- a/include/dt-bindings/usb/pd.h +++ b/include/dt-bindings/usb/pd.h @@ -60,6 +60,7 @@ PDO_VAR_MAX_VOLT(max_mv) | PDO_VAR_MAX_CURR(max_ma)) =20 #define APDO_TYPE_PPS 0 +#define APDO_TYPE_SPR_AVS 2 =20 #define PDO_APDO_TYPE_SHIFT 28 /* Only valid value currently is 0x0 - PPS = */ #define PDO_APDO_TYPE_MASK 0x3 @@ -85,6 +86,23 @@ PDO_PPS_APDO_MIN_VOLT(min_mv) | PDO_PPS_APDO_MAX_VOLT(max_mv) | \ PDO_PPS_APDO_MAX_CURR(max_ma)) =20 +#define PDO_SPR_AVS_APDO_9V_TO_15V_MAX_CURR_SHIFT 10 /* 10mA units */ +#define PDO_SPR_AVS_APDO_15V_TO_20V_MAX_CURR_SHIFT 0 /* 10mA units */ +#define PDO_SPR_AVS_APDO_MAX_CURR_MASK 0x3ff + +#define PDO_SPR_AVS_APDO_9V_TO_15V_MAX_CURR(max_cur_9v_to_15v_ma) \ + ((((max_cur_9v_to_15v_ma) / 10) & PDO_SPR_AVS_APDO_MAX_CURR_MASK) << \ + PDO_SPR_AVS_APDO_9V_TO_15V_MAX_CURR_SHIFT) + +#define PDO_SPR_AVS_APDO_15V_TO_20V_MAX_CURR(max_cur_15v_to_20v_ma) \ + ((((max_cur_15v_to_20v_ma) / 10) & PDO_SPR_AVS_APDO_MAX_CURR_MASK) << \ + PDO_SPR_AVS_APDO_15V_TO_20V_MAX_CURR_SHIFT) + +#define PDO_SPR_AVS_SNK_APDO(max_cur_9v_to_15v_ma, max_cur_15v_to_20v_ma) \ + (PDO_TYPE(PDO_TYPE_APDO) | PDO_APDO_TYPE(APDO_TYPE_SPR_AVS) | \ + PDO_SPR_AVS_APDO_9V_TO_15V_MAX_CURR(max_cur_9v_to_15v_ma) | \ + PDO_SPR_AVS_APDO_15V_TO_20V_MAX_CURR(max_cur_15v_to_20v_ma)) + /* * Based on "Table 6-14 Fixed Supply PDO - Sink" of "USB Power Delivery S= pecification Revision 3.0, * Version 1.2" --=20 2.53.0.851.ga537e3e6e9-goog From nobody Tue Apr 7 03:12:28 2026 Received: from mail-dl1-f74.google.com (mail-dl1-f74.google.com [74.125.82.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EA710317701 for ; Mon, 16 Mar 2026 15:03:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773673391; cv=none; b=RCNbJN8jlu1K+jv7bgmiBUdcyhXPzAkd7kmgplaa57Z/Vxsk6S019e4VF1iH/QUhn3aChRpVTjmOwYplt90wSvMqlGPfmXj4QTPyAuXbzmXX52+SR4z/M8iTS+ocNs5if7CjgYe5G3/vpMtTa3DccKe7laTGJI9pFdN0W4g6h8g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773673391; c=relaxed/simple; bh=FAgn8PATspMB+pVd+tPE4w1KID3ZX0qCAzJbN6EYHd8=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=pPeZkfvhfotkT2pf6XVscmbgcFDVmlnKRogynx34mTljhYc89fQEAxJPWD0siSI8o8buLO0+aMAIl3XIp//QHKjArCfxZSeZA2+ZIsC6ZiZBsk8rfVAfvrw4CneI1Zg+B1I4jzC42ddLyGUJtBcLWQcbDpuEexh+Z3yRg2074/k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--badhri.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=DWIkJ5bd; arc=none smtp.client-ip=74.125.82.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--badhri.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="DWIkJ5bd" Received: by mail-dl1-f74.google.com with SMTP id a92af1059eb24-128e8ac314eso1110165c88.1 for ; Mon, 16 Mar 2026 08:03:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1773673388; x=1774278188; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=dlmlDuwufDiMYRaMF84SHpudU9RqQKI1UxXXTehJXjg=; b=DWIkJ5bdRc5rhsla3JsCMOhHQUb7puHBUvoEjv0JaP6KvJavtyR0CVpRx5Lc0FMaj5 DZ/Qpxhekm470MJa7v1l00KrHEq+YjL5GE3gKfVfuXo9dXwpSlMEEc4NtpKuMC2ACnmW 222Mz8J8bbPwjxtzDYPjyQ+Ur4mDaP+Kq0FeRHVGhS2LOMhRjuwTnW55sR+cAuMR0Prs 6cEOlf2RLYicKjRtg9tAWep+JjF01Zsc5sxRr8ffFC4dHw9gaFxnI3cst6NtmKXML0sP cH6XR04CXH7owcdWLsyJcHGqz1rvQseA0vdq1wpXvyfUrFo4NAvD2aSB58IaM6FHdVSv rAaQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773673388; x=1774278188; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=dlmlDuwufDiMYRaMF84SHpudU9RqQKI1UxXXTehJXjg=; b=P5WASA4fkQKyb8JN9/rtGHcND9XmvE3bTPx/uEXguwuMqFwZiGtUegxYczoX1m03Lm lJTSuFMVQcuIWfW+eXFQw5/bKg8Q0cO8yTh6+HvrbGOiOGwlf9o7TRTHblKdE712jrhQ AzVtZvD8URrN6MaJU2AVD6A7om1MNKlA2ByclBKSP5/6VjlMtJSrO3CHJEeNigiku6E6 9d9quVW9XXpyQLjiCuug4Z6+gznsZNE7QKMDQQ0pOshfRk8yyNz+Y/AAvo0kBwNcVrSj jAi83XVQXs1Ktjs0oXXE8AkMM1s0HjbOkxYZdzD1VcE4664EX3bO97DXM5e1HA2+LSDE WtgA== X-Forwarded-Encrypted: i=1; AJvYcCXEYkGSVa9ZViALmbk6o1KxVRLxwvXFZQ/WImWubyNn3OGWf4yAr2XoUZYLbubNaGzZu2Mgt1x3DSlUUgs=@vger.kernel.org X-Gm-Message-State: AOJu0YwuI+7qVl1uyMDzv6jc0VD1asiLEXD9Mu3T0otfSMblsI4MsXHi gLZNMR3XBm6ZwePdG07NP4fUPbEMYMsd3VNYrzsnIzl5AI72xy/3iTfZ/JFh0N5LMrdyjQWYY+2 FAgKW8A== X-Received: from dlae14.prod.google.com ([2002:a05:701b:230e:b0:128:d97e:9ee4]) (user=badhri job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:1602:b0:123:2d38:928e with SMTP id a92af1059eb24-128f3df8ea9mr5063477c88.35.1773673387856; Mon, 16 Mar 2026 08:03:07 -0700 (PDT) Date: Mon, 16 Mar 2026 15:03:00 +0000 In-Reply-To: <20260316150301.3892223-1-badhri@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260316150301.3892223-1-badhri@google.com> X-Mailer: git-send-email 2.53.0.851.ga537e3e6e9-goog Message-ID: <20260316150301.3892223-3-badhri@google.com> Subject: [PATCH v2 2/3] power: supply: Add PD SPR AVS support to USB type enum From: Badhri Jagan Sridharan To: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Sebastian Reichel , Badhri Jagan Sridharan , Heikki Krogerus , Greg Kroah-Hartman , Amit Sunil Dhamne Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, linux-usb@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add two new members to the power_supply_usb_type to represent the USB Power Delivery (PD) Standard Power Range (SPR) Adjustable Voltage Supply (AVS) charging types: POWER_SUPPLY_USB_TYPE_PD_SPR_AVS: For devices supporting only the PD SPR AVS type. POWER_SUPPLY_USB_TYPE_PD_PPS_SPR_AVS: For devices that support both PD Programmable Power Supply (PPS) and PD SPR AVS. Signed-off-by: Badhri Jagan Sridharan Acked-by: Sebastian Reichel --- Documentation/ABI/testing/sysfs-class-power | 3 ++- drivers/power/supply/power_supply_sysfs.c | 2 ++ include/linux/power_supply.h | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-class-power b/Documentation/AB= I/testing/sysfs-class-power index 4b21d5d23251..32697b926cc8 100644 --- a/Documentation/ABI/testing/sysfs-class-power +++ b/Documentation/ABI/testing/sysfs-class-power @@ -675,7 +675,8 @@ Description: =20 Valid values: "Unknown", "SDP", "DCP", "CDP", "ACA", "C", "PD", - "PD_DRP", "PD_PPS", "BrickID" + "PD_DRP", "PD_PPS", "BrickID", "PD_SPR_AVS", + "PD_PPS_SPR_AVS" =20 **Device Specific Properties** =20 diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supp= ly/power_supply_sysfs.c index dd3a48d72d2b..f30a7b9ccd5e 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -70,6 +70,8 @@ static const char * const POWER_SUPPLY_USB_TYPE_TEXT[] = =3D { [POWER_SUPPLY_USB_TYPE_PD] =3D "PD", [POWER_SUPPLY_USB_TYPE_PD_DRP] =3D "PD_DRP", [POWER_SUPPLY_USB_TYPE_PD_PPS] =3D "PD_PPS", + [POWER_SUPPLY_USB_TYPE_PD_SPR_AVS] =3D "PD_SPR_AVS", + [POWER_SUPPLY_USB_TYPE_PD_PPS_SPR_AVS] =3D "PD_PPS_SPR_AVS", [POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID] =3D "BrickID", }; =20 diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 360ffdf272da..7a5e4c3242a0 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -210,6 +210,9 @@ enum power_supply_usb_type { POWER_SUPPLY_USB_TYPE_PD, /* Power Delivery Port */ POWER_SUPPLY_USB_TYPE_PD_DRP, /* PD Dual Role Port */ POWER_SUPPLY_USB_TYPE_PD_PPS, /* PD Programmable Power Supply */ + /* PD Standard Power Range Adjustable Voltage Supply */ + POWER_SUPPLY_USB_TYPE_PD_SPR_AVS, + POWER_SUPPLY_USB_TYPE_PD_PPS_SPR_AVS, /* Supports both PD PPS + SPR AVS */ POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID, /* Apple Charging Method */ }; =20 --=20 2.53.0.851.ga537e3e6e9-goog From nobody Tue Apr 7 03:12:28 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BF09B39DBE9 for ; Mon, 16 Mar 2026 15:03:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773673394; cv=none; b=JHUwfDdPiclYlEkXDSuG6BYNMYAzHcD3WPyYvFbIAZsMYH0I4ftxCTFRmQlJ6fyM//itgBDi1f8qHo8HcfdOrHjaOYaXJyLhPyewFUx8BUbXdd0xYFNMrLDfdms3+xfXCKR1XeTBQqe9s4NThhRj/TyhVWjvmOngce7spoLDhZM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773673394; c=relaxed/simple; bh=7DWrQ9WproFUFYDwkNv7KMijJYh1auBTPTw4HDqrjxc=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=AkAsfHx5fjHWEJ/SppLniQRLKKCKm+ZbzFI6OTa9jkJH48COZ7hlAW8Ifuw2bQqm/fHEGOwgdneymWL/DFFjbWk4OPx/CT4cXW5SZW9+MVLgWBAfT2teIu/3l2hRmPlQcgPR63FoZ02YJ/T6NmhJii//940Ik9DxCr2RofyJL74= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--badhri.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=SToZ7bsT; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--badhri.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="SToZ7bsT" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2c0cd2e4aa6so15022277eec.0 for ; Mon, 16 Mar 2026 08:03:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1773673390; x=1774278190; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=70PG56c6aLevnJkQAOJmTZ/3zJM86WKie2/SSke5GJk=; b=SToZ7bsTvA67RSh+CKfdTkNPRX7f8NabG454gz3Ru4Sc173nTVpO+5ejTEp2B2tzJ6 r8ufahLvdce/FntD/CMGgDX73Ai8OpJA1D2wjAm0EnQc+nYypp+m/UBnQEe4cxIniBJ4 GeTDI4Kczp66D2gm6leaSH/eqyCzsFDvF/Cc9B4yvdoTvNmbq+jfQpSlK147P4U78ive z9vMCMtr1+nxjwjzz7pwIJPFAxwdnR3TySqc3Osr07qXCcOBpXGd8tfh85K+Jp1RC6uf bLZksMLn3LHdYcv3DzGV30C10zRrU4p0C+kgMdcExg4IbhVLKT6ZsmFSiw2i4u8HNtu6 xpqw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773673390; x=1774278190; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=70PG56c6aLevnJkQAOJmTZ/3zJM86WKie2/SSke5GJk=; b=pD4zqqVQTuneFRj6d2CkVxJB+UFfjf/nes9PrWkNBqvLJ3JpsUtUe5LzuoKKsKvaAh FEqqBSzsW38tkRuKUZ+g+xVnTJ+cbA30LYeFlxAUhx57J3/S1d8JFMZmeZXJgQunw9BD U+SHblLHNZQMCNDLthOw/I+8t5+oSJ3DtOxaVGyxbiXDmVvFVoI9ONeQ1yVb8hn8O6gk 0x6jopLlLjz+7tJMZeKXHAIvXsUf+i9BD4s4QHXAGz42hS/GkZeMr8dw/vKX4gzvN8ZL aGJd6DIIhwmHgkYMyZgbCvIEPifl8K60+hJxHkDliJurRpPxDwwu/fqKZwYSlNu254Lj CYkg== X-Forwarded-Encrypted: i=1; AJvYcCXFAYoRcfNfL1vJ5RLV3pt2iZRpDKW6cXOJrgEoXaoBMeN/+yiZ0tHq3SAmXUHAvhzp+GzsFgVe89QgO6M=@vger.kernel.org X-Gm-Message-State: AOJu0YzQqqPifT2TiBKSwuKWE4H7A2Ofr0q/B9OqvHHncS98KoMnTHHK CA2PN/1GGUUxsuTv1zITMaURjcS/Ph1esI/gmC6VPyJW0OSM50fp23CFwz6APhkbqxnC3LeMIJD 2zFzXKA== X-Received: from dly14-n2.prod.google.com ([2002:a05:701b:204e:20b0:128:f513:4058]) (user=badhri job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:4587:b0:119:e569:f27f with SMTP id a92af1059eb24-128f3e2a616mr6766517c88.40.1773673389576; Mon, 16 Mar 2026 08:03:09 -0700 (PDT) Date: Mon, 16 Mar 2026 15:03:01 +0000 In-Reply-To: <20260316150301.3892223-1-badhri@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260316150301.3892223-1-badhri@google.com> X-Mailer: git-send-email 2.53.0.851.ga537e3e6e9-goog Message-ID: <20260316150301.3892223-4-badhri@google.com> Subject: [PATCH v2 3/3] tcpm: Implement sink support for PD SPR AVS negotiation From: Badhri Jagan Sridharan To: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Sebastian Reichel , Badhri Jagan Sridharan , Heikki Krogerus , Greg Kroah-Hartman , Amit Sunil Dhamne Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, linux-usb@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add support to enable TCPM to negotiate with USB PD Standard Power Range Adjustable Voltage Supply (SPR AVS) when acting as a power sink. * Added support to the tcpm power supply properties, allowing userspace to enable and control the dynamic limits (voltage and current) specific to the SPR AVS contract. * Implemented tcpm_pd_select_spr_avs_apdo() to select the appropriate APDO and validate the requested voltage/current against both the Source and Sink capabilities. * Implemented tcpm_pd_build_spr_avs_request() to construct the Request Data Object (RDO) for SPR AVS. * Added SNK_NEGOTIATE_SPR_AVS_CAPABILITIES state to the state machine to handle negotiation for SPR AVS. * Updated the SNK_TRANSITION_SINK state to implement the SPR AVS-specific VBUS transition rules, including reducing current draw to PD_I_SNK_STBY_MA for large voltage changes, as required by USB PD spec. Log stub captured when enabling AVS: $ echo 3 > /sys/class/power_supply/tcpm-source-psy-1-0025/online $ cat /d/usb/tcpm-1-0025/log [ 358.895775] request to set AVS online [ 358.895792] AMS POWER_NEGOTIATION start [ 358.895806] state change SNK_READY -> AMS_START [rev3 POWER_NEGOTIATION] [ 358.895850] state change AMS_START -> SNK_NEGOTIATE_SPR_AVS_CAPABILITIES= [rev3 POWER_NEGOTIATION] [ 358.895866] SPR AVS src_pdo_index:4 snk_pdo_index:2 req_op_curr_ma round= up:2200 req_out_volt_mv roundup:9000 [ 358.895880] Requesting APDO SPR AVS 4: 9000 mV, 2200 mA [ 358.896405] set_auto_vbus_discharge_threshold mode:0 pps_active:n vbus:0= pps_apdo_min_volt:0 ret:0 [ 358.896422] PD TX, header: 0x1a82 [ 358.900158] PD TX complete, status: 0 [ 358.900205] pending state change SNK_NEGOTIATE_SPR_AVS_CAPABILITIES -> H= ARD_RESET_SEND @ 60 ms [rev3 POWER_NEGOTIATION] [ 358.904832] PD RX, header: 0x1a3 [1] [ 358.904854] state change SNK_NEGOTIATE_SPR_AVS_CAPABILITIES -> SNK_TRANS= ITION_SINK [rev3 POWER_NEGOTIATION] [ 358.904888] pending state change SNK_TRANSITION_SINK -> HARD_RESET_SEND = @ 700 ms [rev3 POWER_NEGOTIATION] [ 359.021530] PD RX, header: 0x3a6 [1] [ 359.021546] Setting voltage/current limit 9000 mV 2200 mA [ 359.023035] set_auto_vbus_discharge_threshold mode:3 pps_active:n vbus:9= 000 pps_apdo_min_volt:0 ret:0 [ 359.023053] state change SNK_TRANSITION_SINK -> SNK_READY [rev3 POWER_NE= GOTIATION] [ 359.023090] AMS POWER_NEGOTIATION finished $ cat /sys/class/power_supply/tcpm-source-psy-1-0025/online 3 Log stub captured when increasing voltage: $ echo 9100000 > /sys/class/power_supply/tcpm-source-psy-1-0025/voltage_now $ cat /d/usb/tcpm-1-0025/log [ 632.116714] AMS POWER_NEGOTIATION start [ 632.116728] state change SNK_READY -> AMS_START [rev3 POWER_NEGOTIATION] [ 632.116779] state change AMS_START -> SNK_NEGOTIATE_SPR_AVS_CAPABILITIES= [rev3 POWER_NEGOTIATION] [ 632.116798] SPR AVS src_pdo_index:4 snk_pdo_index:2 req_op_curr_ma round= up:2200 req_out_volt_mv roundup:9100 [ 632.116811] Requesting APDO SPR AVS 4: 9100 mV, 2200 mA [ 632.117315] set_auto_vbus_discharge_threshold mode:0 pps_active:n vbus:0= pps_apdo_min_volt:0 ret:0 [ 632.117328] PD TX, header: 0x1c82 [ 632.121007] PD TX complete, status: 0 [ 632.121052] pending state change SNK_NEGOTIATE_SPR_AVS_CAPABILITIES -> H= ARD_RESET_SEND @ 60 ms [rev3 POWER_NEGOTIATION] [ 632.124572] PD RX, header: 0x5a3 [1] [ 632.124594] state change SNK_NEGOTIATE_SPR_AVS_CAPABILITIES -> SNK_TRANS= ITION_SINK [rev3 POWER_NEGOTIATION] [ 632.124623] pending state change SNK_TRANSITION_SINK -> HARD_RESET_SEND = @ 700 ms [rev3 POWER_NEGOTIATION] [ 632.149256] PD RX, header: 0x7a6 [1] [ 632.149271] Setting voltage/current limit 9100 mV 2200 mA [ 632.150770] set_auto_vbus_discharge_threshold mode:3 pps_active:n vbus:9= 100 pps_apdo_min_volt:0 ret:0 [ 632.150787] state change SNK_TRANSITION_SINK -> SNK_READY [rev3 POWER_NE= GOTIATION] [ 632.150823] AMS POWER_NEGOTIATION finished $ cat /sys/class/power_supply/tcpm-source-psy-1-0025/voltage_now 9100000 Signed-off-by: Badhri Jagan Sridharan Reviewed-by: Amit Sunil Dhamne Acked-by: Heikki Krogerus --- drivers/usb/typec/tcpm/tcpm.c | 611 ++++++++++++++++++++++++++++------ include/linux/usb/pd.h | 32 +- include/linux/usb/tcpm.h | 2 +- 3 files changed, 537 insertions(+), 108 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 63a75b94743d..dfbb94ddc98a 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -62,6 +62,7 @@ S(SNK_WAIT_CAPABILITIES_TIMEOUT), \ S(SNK_NEGOTIATE_CAPABILITIES), \ S(SNK_NEGOTIATE_PPS_CAPABILITIES), \ + S(SNK_NEGOTIATE_SPR_AVS_CAPABILITIES), \ S(SNK_TRANSITION_SINK), \ S(SNK_TRANSITION_SINK_VBUS), \ S(SNK_READY), \ @@ -308,6 +309,51 @@ struct pd_pps_data { bool active; }; =20 +enum spr_avs_status { + SPR_AVS_UNKNOWN, + SPR_AVS_NOT_SUPPORTED, + SPR_AVS_SUPPORTED +}; + +static const char * const spr_avs_status_strings[] =3D { + [SPR_AVS_UNKNOWN] =3D "Unknown", + [SPR_AVS_SUPPORTED] =3D "Supported", + [SPR_AVS_NOT_SUPPORTED] =3D "Not Supported", +}; + +/* + * Standard Power Range Adjustable Voltage Supply (SPR - AVS) data + * @max_current_ma_9v_to_15v: Max current for 9V to 15V range derived from + * source cap & sink cap + * @max_current_ma_15v_to_20v: Max current for 15V to 20V range derived fr= om + * source cap & sink cap + * @req_op_curr_ma: Requested operating current to the port partner acting= as source + * @req_out_volt_mv: Requested output voltage to the port partner acting a= s source + * @max_out_volt_mv: Max SPR voltage supported by the port and the port pa= rtner + * @max_current_ma; MAX SPR current supported by the port and the port par= tner + * @port_partner_src_status: SPR AVS status of port partner acting as sour= ce + * @port_partner_src_pdo_index: PDO index of SPR AVS cap of the port partn= er + * acting as source. Valid only when + * port_partner_src_status is SPR_AVS_SUPPORT= ED. + * @port_snk_status: SPR AVS status of the local port acting as sink. + * @port_snk_pdo_index: PDO index of SPR AVS cap of local port acting as s= ink + * @active: True when the local port acting as the sink has negotiated SPR= AVS + * with the partner acting as source. + */ +struct pd_spr_avs_data { + u32 max_current_ma_9v_to_15v; + u32 max_current_ma_15v_to_20v; + u32 req_op_curr_ma; + u32 req_out_volt_mv; + u32 max_out_volt_mv; + u32 max_current_ma; + enum spr_avs_status port_partner_src_status; + unsigned int port_partner_src_pdo_index; + enum spr_avs_status port_snk_status; + unsigned int port_snk_pdo_index; + bool active; +}; + struct pd_data { struct usb_power_delivery *pd; struct usb_power_delivery_capabilities *source_cap; @@ -376,6 +422,11 @@ struct sink_caps_ext_data { u8 spr_max_pdp; }; =20 +enum aug_req_type { + PD_PPS, + PD_SPR_AVS, +}; + struct tcpm_port { struct device *dev; =20 @@ -538,9 +589,14 @@ struct tcpm_port { =20 /* PPS */ struct pd_pps_data pps_data; - struct completion pps_complete; - bool pps_pending; - int pps_status; + + /* SPR AVS */ + struct pd_spr_avs_data spr_avs_data; + + /* Augmented supply request - PPS; SPR_AVS */ + struct completion aug_supply_req_complete; + bool aug_supply_req_pending; + int aug_supply_req_status; =20 /* Alternate mode data */ struct pd_mode_data mode_data; @@ -3285,6 +3341,7 @@ static void tcpm_pd_data_request(struct tcpm_port *po= rt, =20 switch (type) { case PD_DATA_SOURCE_CAP: + port->spr_avs_data.port_partner_src_status =3D SPR_AVS_UNKNOWN; for (i =3D 0; i < cnt; i++) port->source_caps[i] =3D le32_to_cpu(msg->payload[i]); =20 @@ -3456,12 +3513,12 @@ static void tcpm_pd_data_request(struct tcpm_port *= port, } } =20 -static void tcpm_pps_complete(struct tcpm_port *port, int result) +static void tcpm_aug_supply_req_complete(struct tcpm_port *port, int resul= t) { - if (port->pps_pending) { - port->pps_status =3D result; - port->pps_pending =3D false; - complete(&port->pps_complete); + if (port->aug_supply_req_pending) { + port->aug_supply_req_status =3D result; + port->aug_supply_req_pending =3D false; + complete(&port->aug_supply_req_complete); } } =20 @@ -3559,7 +3616,7 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *po= rt, /* Revert data back from any requested PPS updates */ port->pps_data.req_out_volt =3D port->supply_voltage; port->pps_data.req_op_curr =3D port->current_limit; - port->pps_status =3D (type =3D=3D PD_CTRL_WAIT ? + port->aug_supply_req_status =3D (type =3D=3D PD_CTRL_WAIT ? -EAGAIN : -EOPNOTSUPP); =20 /* Threshold was relaxed before sending Request. Restore it back. */ @@ -3567,6 +3624,20 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *p= ort, port->pps_data.active, port->supply_voltage); =20 + tcpm_set_state(port, SNK_READY, 0); + break; + case SNK_NEGOTIATE_SPR_AVS_CAPABILITIES: + /* Revert data back from any requested SPR AVS updates */ + port->spr_avs_data.req_out_volt_mv =3D port->supply_voltage; + port->spr_avs_data.req_op_curr_ma =3D port->current_limit; + port->aug_supply_req_status =3D (type =3D=3D PD_CTRL_WAIT ? + -EAGAIN : -EOPNOTSUPP); + + /* Threshold was relaxed before sending Request. Restore it back. */ + tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_PD, + port->spr_avs_data.active, + port->supply_voltage); + tcpm_set_state(port, SNK_READY, 0); break; case DR_SWAP_SEND: @@ -3621,6 +3692,7 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *po= rt, switch (port->state) { case SNK_NEGOTIATE_CAPABILITIES: port->pps_data.active =3D false; + port->spr_avs_data.active =3D false; tcpm_set_state(port, SNK_TRANSITION_SINK, 0); break; case SNK_NEGOTIATE_PPS_CAPABILITIES: @@ -3633,6 +3705,13 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *p= ort, power_supply_changed(port->psy); tcpm_set_state(port, SNK_TRANSITION_SINK, 0); break; + case SNK_NEGOTIATE_SPR_AVS_CAPABILITIES: + port->spr_avs_data.active =3D true; + port->req_supply_voltage =3D port->spr_avs_data.req_out_volt_mv; + port->req_current_limit =3D port->spr_avs_data.req_op_curr_ma; + power_supply_changed(port->psy); + tcpm_set_state(port, SNK_TRANSITION_SINK, 0); + break; case SOFT_RESET_SEND: if (port->ams =3D=3D SOFT_RESET_AMS) tcpm_ams_finish(port); @@ -4130,9 +4209,9 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port,= int *sink_pdo, case PDO_TYPE_APDO: if (pdo_apdo_type(pdo) =3D=3D APDO_TYPE_PPS) { port->pps_data.supported =3D true; - port->usb_type =3D - POWER_SUPPLY_USB_TYPE_PD_PPS; - power_supply_changed(port->psy); + } else if (pdo_apdo_type(pdo) =3D=3D APDO_TYPE_SPR_AVS) { + port->spr_avs_data.port_partner_src_status =3D SPR_AVS_SUPPORTED; + port->spr_avs_data.port_partner_src_pdo_index =3D i; } continue; default: @@ -4170,6 +4249,10 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port= , int *sink_pdo, min_snk_mv =3D pdo_min_voltage(pdo); break; case PDO_TYPE_APDO: + if (pdo_apdo_type(pdo) =3D=3D APDO_TYPE_SPR_AVS) { + port->spr_avs_data.port_snk_status =3D SPR_AVS_SUPPORTED; + port->spr_avs_data.port_snk_pdo_index =3D j; + } continue; default: tcpm_log(port, "Invalid sink PDO type, ignoring"); @@ -4191,6 +4274,23 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port= , int *sink_pdo, } } =20 + if (port->spr_avs_data.port_snk_status =3D=3D SPR_AVS_UNKNOWN) + port->spr_avs_data.port_snk_status =3D SPR_AVS_NOT_SUPPORTED; + + if (port->spr_avs_data.port_partner_src_status =3D=3D SPR_AVS_UNKNOWN) + port->spr_avs_data.port_partner_src_status =3D SPR_AVS_NOT_SUPPORTED; + + if (port->pps_data.supported && + port->spr_avs_data.port_partner_src_status =3D=3D SPR_AVS_SUPPORTED) + port->usb_type =3D POWER_SUPPLY_USB_TYPE_PD_PPS_SPR_AVS; + else if (port->pps_data.supported) + port->usb_type =3D POWER_SUPPLY_USB_TYPE_PD_PPS; + else if (port->spr_avs_data.port_partner_src_status =3D=3D SPR_AVS_SUPPOR= TED) + port->usb_type =3D POWER_SUPPLY_USB_TYPE_PD_SPR_AVS; + + if (port->usb_type !=3D POWER_SUPPLY_USB_TYPE_PD) + power_supply_changed(port->psy); + return ret; } =20 @@ -4241,6 +4341,88 @@ static unsigned int tcpm_pd_select_pps_apdo(struct t= cpm_port *port) return src_pdo; } =20 +static int tcpm_pd_select_spr_avs_apdo(struct tcpm_port *port) +{ + u32 req_out_volt_mv, req_op_curr_ma, src_max_curr_ma =3D 0, source_cap; + u32 snk_max_curr_ma =3D 0, src_pdo_index, snk_pdo_index, snk_pdo; + + if (port->spr_avs_data.port_snk_status !=3D SPR_AVS_SUPPORTED || + port->spr_avs_data.port_partner_src_status !=3D + SPR_AVS_SUPPORTED) { + tcpm_log(port, "SPR AVS not supported. port:%s partner:%s", + spr_avs_status_strings[port->spr_avs_data.port_snk_status], + spr_avs_status_strings[port->spr_avs_data.port_partner_src_status]); + return -EOPNOTSUPP; + } + + /* Round up to SPR_AVS_VOLT_MV_STEP */ + req_out_volt_mv =3D port->spr_avs_data.req_out_volt_mv; + if (req_out_volt_mv % SPR_AVS_VOLT_MV_STEP) { + req_out_volt_mv +=3D SPR_AVS_VOLT_MV_STEP - + (req_out_volt_mv % SPR_AVS_VOLT_MV_STEP); + port->spr_avs_data.req_out_volt_mv =3D req_out_volt_mv; + } + + /* Round up to RDO_SPR_AVS_CURR_MA_STEP */ + req_op_curr_ma =3D port->spr_avs_data.req_op_curr_ma; + if (req_op_curr_ma % RDO_SPR_AVS_CURR_MA_STEP) { + req_op_curr_ma +=3D RDO_SPR_AVS_CURR_MA_STEP - + (req_op_curr_ma % RDO_SPR_AVS_CURR_MA_STEP); + port->spr_avs_data.req_op_curr_ma =3D req_op_curr_ma; + } + + src_pdo_index =3D port->spr_avs_data.port_partner_src_pdo_index; + snk_pdo_index =3D port->spr_avs_data.port_snk_pdo_index; + source_cap =3D port->source_caps[src_pdo_index]; + snk_pdo =3D port->snk_pdo[snk_pdo_index]; + tcpm_log(port, + "SPR AVS src_pdo_index:%d snk_pdo_index:%d req_op_curr_ma roundup:%u re= q_out_volt_mv roundup:%u", + src_pdo_index, snk_pdo_index, req_op_curr_ma, req_out_volt_mv); + + if (req_out_volt_mv >=3D SPR_AVS_TIER1_MIN_VOLT_MV && + req_out_volt_mv <=3D SPR_AVS_TIER1_MAX_VOLT_MV) { + src_max_curr_ma =3D + pdo_spr_avs_apdo_9v_to_15v_max_current_ma(source_cap); + snk_max_curr_ma =3D + pdo_spr_avs_apdo_9v_to_15v_max_current_ma(snk_pdo); + } else if (req_out_volt_mv > SPR_AVS_TIER1_MAX_VOLT_MV && + req_out_volt_mv <=3D SPR_AVS_TIER2_MAX_VOLT_MV) { + src_max_curr_ma =3D + pdo_spr_avs_apdo_15v_to_20v_max_current_ma(source_cap); + snk_max_curr_ma =3D + pdo_spr_avs_apdo_15v_to_20v_max_current_ma(snk_pdo); + } else { + tcpm_log(port, "Invalid SPR AVS req_volt:%umV", req_out_volt_mv); + return -EINVAL; + } + + if (req_op_curr_ma > src_max_curr_ma || + req_op_curr_ma > snk_max_curr_ma) { + tcpm_log(port, + "Invalid SPR AVS request. req_volt:%umV req_curr:%umA src_max_cur:%umA= snk_max_cur:%umA", + req_out_volt_mv, req_op_curr_ma, src_max_curr_ma, + snk_max_curr_ma); + return -EINVAL; + } + + /* Max SPR voltage based on both the port and the partner caps */ + if (pdo_spr_avs_apdo_15v_to_20v_max_current_ma(snk_pdo) && + pdo_spr_avs_apdo_15v_to_20v_max_current_ma(source_cap)) + port->spr_avs_data.max_out_volt_mv =3D SPR_AVS_TIER2_MAX_VOLT_MV; + else + port->spr_avs_data.max_out_volt_mv =3D SPR_AVS_TIER1_MAX_VOLT_MV; + + /* + * Max SPR AVS curr based on 9V to 15V. This should be higher than or + * equal to 15V to 20V range. + */ + port->spr_avs_data.max_current_ma =3D + min(pdo_spr_avs_apdo_9v_to_15v_max_current_ma(source_cap), + pdo_spr_avs_apdo_9v_to_15v_max_current_ma(snk_pdo)); + + return src_pdo_index; +} + static int tcpm_pd_build_request(struct tcpm_port *port, u32 *rdo) { unsigned int mv, ma, mw, flags; @@ -4408,13 +4590,74 @@ static int tcpm_pd_build_pps_request(struct tcpm_po= rt *port, u32 *rdo) return 0; } =20 -static int tcpm_pd_send_pps_request(struct tcpm_port *port) +static int tcpm_pd_build_spr_avs_request(struct tcpm_port *port, u32 *rdo) +{ + u32 out_mv, op_ma, flags, snk_pdo_index, source_cap; + unsigned int src_power_mw, snk_power_mw; + int src_pdo_index; + u32 snk_pdo; + + src_pdo_index =3D tcpm_pd_select_spr_avs_apdo(port); + if (src_pdo_index < 0) + return src_pdo_index; + snk_pdo_index =3D port->spr_avs_data.port_snk_pdo_index; + source_cap =3D port->source_caps[src_pdo_index]; + snk_pdo =3D port->snk_pdo[snk_pdo_index]; + out_mv =3D port->spr_avs_data.req_out_volt_mv; + op_ma =3D port->spr_avs_data.req_op_curr_ma; + + flags =3D RDO_USB_COMM | RDO_NO_SUSPEND; + + /* + * Set capability mismatch when the maximum power needs in the current + * requested AVS voltage tier range is greater than + * port->operating_snk_mw, however, the maximum power offered by the + * source at the current requested AVS voltage tier is less than + * port->operating_sink_mw. + */ + if (out_mv > SPR_AVS_TIER1_MAX_VOLT_MV) { + src_power_mw =3D + pdo_spr_avs_apdo_15v_to_20v_max_current_ma(source_cap) * + SPR_AVS_TIER2_MAX_VOLT_MV / 1000; + snk_power_mw =3D + pdo_spr_avs_apdo_15v_to_20v_max_current_ma(snk_pdo) * + SPR_AVS_TIER2_MAX_VOLT_MV / 1000; + } else { + src_power_mw =3D + pdo_spr_avs_apdo_9v_to_15v_max_current_ma(source_cap) * + SPR_AVS_TIER1_MAX_VOLT_MV / 1000; + snk_power_mw =3D + pdo_spr_avs_apdo_9v_to_15v_max_current_ma(snk_pdo) * + SPR_AVS_TIER1_MAX_VOLT_MV / 1000; + } + + if (snk_power_mw >=3D port->operating_snk_mw && + src_power_mw < port->operating_snk_mw) + flags |=3D RDO_CAP_MISMATCH; + + *rdo =3D RDO_AVS(src_pdo_index + 1, out_mv, op_ma, flags); + + tcpm_log(port, "Requesting APDO SPR AVS %d: %u mV, %u mA", + src_pdo_index, out_mv, op_ma); + + return 0; +} + +static int tcpm_pd_send_aug_supply_request(struct tcpm_port *port, + enum aug_req_type type) { struct pd_message msg; int ret; u32 rdo; =20 - ret =3D tcpm_pd_build_pps_request(port, &rdo); + if (type =3D=3D PD_PPS) { + ret =3D tcpm_pd_build_pps_request(port, &rdo); + } else if (type =3D=3D PD_SPR_AVS) { + ret =3D tcpm_pd_build_spr_avs_request(port, &rdo); + } else { + tcpm_log(port, "Invalid aug_req_type %d", type); + ret =3D -EOPNOTSUPP; + } if (ret < 0) return ret; =20 @@ -4637,6 +4880,14 @@ static void tcpm_set_partner_usb_comm_capable(struct= tcpm_port *port, bool capab port->tcpc->set_partner_usb_comm_capable(port->tcpc, capable); } =20 +static void tcpm_partner_source_caps_reset(struct tcpm_port *port) +{ + usb_power_delivery_unregister_capabilities(port->partner_source_caps); + port->partner_source_caps =3D NULL; + port->spr_avs_data.port_partner_src_status =3D SPR_AVS_UNKNOWN; + port->spr_avs_data.active =3D false; +} + static void tcpm_reset_port(struct tcpm_port *port) { tcpm_enable_auto_vbus_discharge(port, false); @@ -4676,8 +4927,7 @@ static void tcpm_reset_port(struct tcpm_port *port) =20 usb_power_delivery_unregister_capabilities(port->partner_sink_caps); port->partner_sink_caps =3D NULL; - usb_power_delivery_unregister_capabilities(port->partner_source_caps); - port->partner_source_caps =3D NULL; + tcpm_partner_source_caps_reset(port); usb_power_delivery_unregister(port->partner_pd); port->partner_pd =3D NULL; } @@ -5169,7 +5419,7 @@ static void run_state_machine(struct tcpm_port *port) case SNK_UNATTACHED: if (!port->non_pd_role_swap) tcpm_swap_complete(port, -ENOTCONN); - tcpm_pps_complete(port, -ENOTCONN); + tcpm_aug_supply_req_complete(port, -ENOTCONN); tcpm_snk_detach(port); if (port->potential_contaminant) { tcpm_set_state(port, CHECK_CONTAMINANT, 0); @@ -5400,13 +5650,16 @@ static void run_state_machine(struct tcpm_port *por= t) } break; case SNK_NEGOTIATE_PPS_CAPABILITIES: - ret =3D tcpm_pd_send_pps_request(port); + case SNK_NEGOTIATE_SPR_AVS_CAPABILITIES: + ret =3D tcpm_pd_send_aug_supply_request(port, port->state =3D=3D + SNK_NEGOTIATE_PPS_CAPABILITIES ? + PD_PPS : PD_SPR_AVS); if (ret < 0) { /* Restore back to the original state */ tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_PD, port->pps_data.active, port->supply_voltage); - port->pps_status =3D ret; + port->aug_supply_req_status =3D ret; /* * If this was called due to updates to sink * capabilities, and pps is no longer valid, we should @@ -5422,23 +5675,58 @@ static void run_state_machine(struct tcpm_port *por= t) } break; case SNK_TRANSITION_SINK: - /* From the USB PD spec: - * "The Sink Shall transition to Sink Standby before a positive or - * negative voltage transition of VBUS. During Sink Standby - * the Sink Shall reduce its power draw to pSnkStdby." - * - * This is not applicable to PPS though as the port can continue - * to draw negotiated power without switching to standby. - */ - if (port->supply_voltage !=3D port->req_supply_voltage && !port->pps_dat= a.active && - port->current_limit * port->supply_voltage / 1000 > PD_P_SNK_STDBY_M= W) { - u32 stdby_ma =3D PD_P_SNK_STDBY_MW * 1000 / port->supply_voltage; + if (port->spr_avs_data.active) { + if (abs(port->req_supply_voltage - port->supply_voltage) > + SPR_AVS_AVS_SMALL_STEP_V * 1000) { + /* + * The Sink Shall reduce its current draw to + * iSnkStdby within tSnkStdby. The reduction to + * iSnkStdby is not required if the voltage + * increase is less than or equal to + * vAvsSmallStep. + */ + tcpm_log(port, + "SPR AVS Setting iSnkstandby. Req vol: %u mV Curr vol: %u mV", + port->req_supply_voltage, + port->supply_voltage); + tcpm_set_current_limit(port, PD_I_SNK_STBY_MA, + port->supply_voltage); + } + /* + * Although tAvsSrcTransSmall is expected to be used + * for voltage transistions smaller than 1V, using + * tAvsSrcTransLarge to be resilient against chargers + * which strictly cannot honor tAvsSrcTransSmall to + * improve interoperability. + */ + tcpm_set_state(port, hard_reset_state(port), + PD_T_AVS_SRC_TRANS_LARGE); + /* + * From the USB PD spec: + * "The Sink Shall transition to Sink Standby before a + * positive ornegative voltage transition of VBUS. + * During Sink Standby the Sink Shall reduce its power + * draw to pSnkStdby." + * + * This is not applicable to PPS though as the port can + * continue to draw negotiated power without switching + * to standby. + */ + } else if (port->supply_voltage !=3D port->req_supply_voltage && + !port->pps_data.active && + (port->current_limit * port->supply_voltage / 1000 > + PD_P_SNK_STDBY_MW)) { + u32 stdby_ma =3D PD_P_SNK_STDBY_MW * 1000 / + port->supply_voltage; =20 tcpm_log(port, "Setting standby current %u mV @ %u mA", port->supply_voltage, stdby_ma); - tcpm_set_current_limit(port, stdby_ma, port->supply_voltage); + tcpm_set_current_limit(port, stdby_ma, + port->supply_voltage); + tcpm_set_state(port, hard_reset_state(port), + PD_T_PS_TRANSITION); } - fallthrough; + break; case SNK_TRANSITION_SINK_VBUS: tcpm_set_state(port, hard_reset_state(port), PD_T_PS_TRANSITION); @@ -5458,7 +5746,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_typec_connect(port); if (port->pd_capable && port->source_caps[0] & PDO_FIXED_DUAL_ROLE) mod_enable_frs_delayed_work(port, 0); - tcpm_pps_complete(port, port->pps_status); + tcpm_aug_supply_req_complete(port, port->aug_supply_req_status); =20 if (port->ams !=3D NONE_AMS) tcpm_ams_finish(port); @@ -5645,8 +5933,7 @@ static void run_state_machine(struct tcpm_port *port) port->message_id =3D 0; port->rx_msgid =3D -1; /* remove existing capabilities */ - usb_power_delivery_unregister_capabilities(port->partner_source_caps); - port->partner_source_caps =3D NULL; + tcpm_partner_source_caps_reset(port); tcpm_pd_send_control(port, PD_CTRL_ACCEPT, TCPC_TX_SOP); tcpm_ams_finish(port); if (port->pwr_role =3D=3D TYPEC_SOURCE) { @@ -5679,8 +5966,7 @@ static void run_state_machine(struct tcpm_port *port) port->message_id =3D 0; port->rx_msgid =3D -1; /* remove existing capabilities */ - usb_power_delivery_unregister_capabilities(port->partner_source_caps); - port->partner_source_caps =3D NULL; + tcpm_partner_source_caps_reset(port); if (tcpm_pd_send_control(port, PD_CTRL_SOFT_RESET, TCPC_TX_SOP)) tcpm_set_state_cond(port, hard_reset_state(port), 0); else @@ -5817,8 +6103,7 @@ static void run_state_machine(struct tcpm_port *port) break; case PR_SWAP_SNK_SRC_SINK_OFF: /* will be source, remove existing capabilities */ - usb_power_delivery_unregister_capabilities(port->partner_source_caps); - port->partner_source_caps =3D NULL; + tcpm_partner_source_caps_reset(port); /* * Prevent vbus discharge circuit from turning on during PR_SWAP * as this is not a disconnect. @@ -5966,7 +6251,7 @@ static void run_state_machine(struct tcpm_port *port) break; case ERROR_RECOVERY: tcpm_swap_complete(port, -EPROTO); - tcpm_pps_complete(port, -EPROTO); + tcpm_aug_supply_req_complete(port, -EPROTO); tcpm_set_state(port, PORT_RESET, 0); break; case PORT_RESET: @@ -6940,7 +7225,7 @@ static int tcpm_try_role(struct typec_port *p, int ro= le) return ret; } =20 -static int tcpm_pps_set_op_curr(struct tcpm_port *port, u16 req_op_curr) +static int tcpm_aug_set_op_curr(struct tcpm_port *port, u16 req_op_curr_ma) { unsigned int target_mw; int ret; @@ -6948,7 +7233,19 @@ static int tcpm_pps_set_op_curr(struct tcpm_port *po= rt, u16 req_op_curr) mutex_lock(&port->swap_lock); mutex_lock(&port->lock); =20 - if (!port->pps_data.active) { + if (port->pps_data.active) { + req_op_curr_ma =3D req_op_curr_ma - + (req_op_curr_ma % RDO_PROG_CURR_MA_STEP); + if (req_op_curr_ma > port->pps_data.max_curr) { + ret =3D -EINVAL; + goto port_unlock; + } + target_mw =3D (req_op_curr_ma * port->supply_voltage) / 1000; + if (target_mw < port->operating_snk_mw) { + ret =3D -EINVAL; + goto port_unlock; + } + } else if (!port->spr_avs_data.active) { ret =3D -EOPNOTSUPP; goto port_unlock; } @@ -6958,38 +7255,31 @@ static int tcpm_pps_set_op_curr(struct tcpm_port *p= ort, u16 req_op_curr) goto port_unlock; } =20 - if (req_op_curr > port->pps_data.max_curr) { - ret =3D -EINVAL; - goto port_unlock; - } - - target_mw =3D (req_op_curr * port->supply_voltage) / 1000; - if (target_mw < port->operating_snk_mw) { - ret =3D -EINVAL; - goto port_unlock; - } + if (port->pps_data.active) + port->upcoming_state =3D SNK_NEGOTIATE_PPS_CAPABILITIES; + else + port->upcoming_state =3D SNK_NEGOTIATE_SPR_AVS_CAPABILITIES; =20 - port->upcoming_state =3D SNK_NEGOTIATE_PPS_CAPABILITIES; ret =3D tcpm_ams_start(port, POWER_NEGOTIATION); if (ret =3D=3D -EAGAIN) { port->upcoming_state =3D INVALID_STATE; goto port_unlock; } =20 - /* Round down operating current to align with PPS valid steps */ - req_op_curr =3D req_op_curr - (req_op_curr % RDO_PROG_CURR_MA_STEP); - - reinit_completion(&port->pps_complete); - port->pps_data.req_op_curr =3D req_op_curr; - port->pps_status =3D 0; - port->pps_pending =3D true; + reinit_completion(&port->aug_supply_req_complete); + if (port->pps_data.active) + port->pps_data.req_op_curr =3D req_op_curr_ma; + else + port->spr_avs_data.req_op_curr_ma =3D req_op_curr_ma; + port->aug_supply_req_status =3D 0; + port->aug_supply_req_pending =3D true; mutex_unlock(&port->lock); =20 - if (!wait_for_completion_timeout(&port->pps_complete, - msecs_to_jiffies(PD_PPS_CTRL_TIMEOUT))) + if (!wait_for_completion_timeout(&port->aug_supply_req_complete, + msecs_to_jiffies(PD_AUG_PSY_CTRL_TIMEOUT))) ret =3D -ETIMEDOUT; else - ret =3D port->pps_status; + ret =3D port->aug_supply_req_status; =20 goto swap_unlock; =20 @@ -7001,7 +7291,7 @@ static int tcpm_pps_set_op_curr(struct tcpm_port *por= t, u16 req_op_curr) return ret; } =20 -static int tcpm_pps_set_out_volt(struct tcpm_port *port, u16 req_out_volt) +static int tcpm_aug_set_out_volt(struct tcpm_port *port, u16 req_out_volt_= mv) { unsigned int target_mw; int ret; @@ -7009,7 +7299,16 @@ static int tcpm_pps_set_out_volt(struct tcpm_port *p= ort, u16 req_out_volt) mutex_lock(&port->swap_lock); mutex_lock(&port->lock); =20 - if (!port->pps_data.active) { + if (port->pps_data.active) { + req_out_volt_mv =3D req_out_volt_mv - (req_out_volt_mv % + RDO_PROG_VOLT_MV_STEP); + /* Round down output voltage to align with PPS valid steps */ + target_mw =3D (port->current_limit * req_out_volt_mv) / 1000; + if (target_mw < port->operating_snk_mw) { + ret =3D -EINVAL; + goto port_unlock; + } + } else if (!port->spr_avs_data.active) { ret =3D -EOPNOTSUPP; goto port_unlock; } @@ -7019,33 +7318,31 @@ static int tcpm_pps_set_out_volt(struct tcpm_port *= port, u16 req_out_volt) goto port_unlock; } =20 - target_mw =3D (port->current_limit * req_out_volt) / 1000; - if (target_mw < port->operating_snk_mw) { - ret =3D -EINVAL; - goto port_unlock; - } + if (port->pps_data.active) + port->upcoming_state =3D SNK_NEGOTIATE_PPS_CAPABILITIES; + else + port->upcoming_state =3D SNK_NEGOTIATE_SPR_AVS_CAPABILITIES; =20 - port->upcoming_state =3D SNK_NEGOTIATE_PPS_CAPABILITIES; ret =3D tcpm_ams_start(port, POWER_NEGOTIATION); if (ret =3D=3D -EAGAIN) { port->upcoming_state =3D INVALID_STATE; goto port_unlock; } =20 - /* Round down output voltage to align with PPS valid steps */ - req_out_volt =3D req_out_volt - (req_out_volt % RDO_PROG_VOLT_MV_STEP); - - reinit_completion(&port->pps_complete); - port->pps_data.req_out_volt =3D req_out_volt; - port->pps_status =3D 0; - port->pps_pending =3D true; + reinit_completion(&port->aug_supply_req_complete); + if (port->pps_data.active) + port->pps_data.req_out_volt =3D req_out_volt_mv; + else + port->spr_avs_data.req_out_volt_mv =3D req_out_volt_mv; + port->aug_supply_req_status =3D 0; + port->aug_supply_req_pending =3D true; mutex_unlock(&port->lock); =20 - if (!wait_for_completion_timeout(&port->pps_complete, - msecs_to_jiffies(PD_PPS_CTRL_TIMEOUT))) + if (!wait_for_completion_timeout(&port->aug_supply_req_complete, + msecs_to_jiffies(PD_AUG_PSY_CTRL_TIMEOUT))) ret =3D -ETIMEDOUT; else - ret =3D port->pps_status; + ret =3D port->aug_supply_req_status; =20 goto swap_unlock; =20 @@ -7088,9 +7385,9 @@ static int tcpm_pps_activate(struct tcpm_port *port, = bool activate) goto port_unlock; } =20 - reinit_completion(&port->pps_complete); - port->pps_status =3D 0; - port->pps_pending =3D true; + reinit_completion(&port->aug_supply_req_complete); + port->aug_supply_req_status =3D 0; + port->aug_supply_req_pending =3D true; =20 /* Trigger PPS request or move back to standard PDO contract */ if (activate) { @@ -7099,11 +7396,75 @@ static int tcpm_pps_activate(struct tcpm_port *port= , bool activate) } mutex_unlock(&port->lock); =20 - if (!wait_for_completion_timeout(&port->pps_complete, - msecs_to_jiffies(PD_PPS_CTRL_TIMEOUT))) + if (!wait_for_completion_timeout(&port->aug_supply_req_complete, + msecs_to_jiffies(PD_AUG_PSY_CTRL_TIMEOUT))) + ret =3D -ETIMEDOUT; + else + ret =3D port->aug_supply_req_status; + + goto swap_unlock; + +port_unlock: + mutex_unlock(&port->lock); +swap_unlock: + mutex_unlock(&port->swap_lock); + + return ret; +} + +static int tcpm_spr_avs_activate(struct tcpm_port *port, bool activate) +{ + int ret =3D 0; + + mutex_lock(&port->swap_lock); + mutex_lock(&port->lock); + + if (port->spr_avs_data.port_snk_status =3D=3D SPR_AVS_NOT_SUPPORTED || + port->spr_avs_data.port_partner_src_status =3D=3D SPR_AVS_NOT_SUPPORT= ED) { + tcpm_log(port, "SPR_AVS not supported"); + ret =3D -EOPNOTSUPP; + goto port_unlock; + } + + /* Trying to deactivate SPR AVS when already deactivated so just bail */ + if (!port->spr_avs_data.active && !activate) + goto port_unlock; + + if (port->state !=3D SNK_READY) { + tcpm_log(port, + "SPR_AVS cannot be activated. Port not in SNK_READY"); + ret =3D -EAGAIN; + goto port_unlock; + } + + if (activate) + port->upcoming_state =3D SNK_NEGOTIATE_SPR_AVS_CAPABILITIES; + else + port->upcoming_state =3D SNK_NEGOTIATE_CAPABILITIES; + ret =3D tcpm_ams_start(port, POWER_NEGOTIATION); + if (ret =3D=3D -EAGAIN) { + tcpm_log(port, "SPR_AVS cannot be %s. AMS start failed", + activate ? "activated" : "deactivated"); + port->upcoming_state =3D INVALID_STATE; + goto port_unlock; + } + + reinit_completion(&port->aug_supply_req_complete); + port->aug_supply_req_status =3D 0; + port->aug_supply_req_pending =3D true; + + /* Trigger AVS request or move back to standard PDO contract */ + if (activate) { + port->spr_avs_data.req_out_volt_mv =3D port->supply_voltage; + port->spr_avs_data.req_op_curr_ma =3D port->current_limit; + } + mutex_unlock(&port->lock); + + if (!wait_for_completion_timeout(&port->aug_supply_req_complete, + msecs_to_jiffies(PD_AUG_PSY_CTRL_TIMEOUT))) ret =3D -ETIMEDOUT; else - ret =3D port->pps_status; + ret =3D port->aug_supply_req_status; =20 goto swap_unlock; =20 @@ -7259,16 +7620,26 @@ static int tcpm_pd_set(struct typec_port *p, struct= usb_power_delivery *pd) break; case SNK_NEGOTIATE_CAPABILITIES: case SNK_NEGOTIATE_PPS_CAPABILITIES: + case SNK_NEGOTIATE_SPR_AVS_CAPABILITIES: case SNK_READY: case SNK_TRANSITION_SINK: case SNK_TRANSITION_SINK_VBUS: - if (port->pps_data.active) + if (port->pps_data.active) { port->upcoming_state =3D SNK_NEGOTIATE_PPS_CAPABILITIES; - else if (port->pd_capable) + } else if (port->pd_capable) { port->upcoming_state =3D SNK_NEGOTIATE_CAPABILITIES; - else + if (port->spr_avs_data.active) { + /* + * De-activate AVS and fallback to PD to + * re-evaluate whether AVS is supported in the + * current sink cap set. + */ + port->spr_avs_data.active =3D false; + port->spr_avs_data.port_snk_status =3D SPR_AVS_UNKNOWN; + } + } else { break; - + } port->update_sink_caps =3D true; =20 ret =3D tcpm_ams_start(port, POWER_NEGOTIATION); @@ -7778,7 +8149,8 @@ static void tcpm_fw_get_pd_revision(struct tcpm_port = *port, struct fwnode_handle enum tcpm_psy_online_states { TCPM_PSY_OFFLINE =3D 0, TCPM_PSY_FIXED_ONLINE, - TCPM_PSY_PROG_ONLINE, + TCPM_PSY_PPS_ONLINE, + TCPM_PSY_SPR_AVS_ONLINE, }; =20 static enum power_supply_property tcpm_psy_props[] =3D { @@ -7796,7 +8168,9 @@ static int tcpm_psy_get_online(struct tcpm_port *port, { if (port->vbus_charge) { if (port->pps_data.active) - val->intval =3D TCPM_PSY_PROG_ONLINE; + val->intval =3D TCPM_PSY_PPS_ONLINE; + else if (port->spr_avs_data.active) + val->intval =3D TCPM_PSY_SPR_AVS_ONLINE; else val->intval =3D TCPM_PSY_FIXED_ONLINE; } else { @@ -7811,6 +8185,8 @@ static int tcpm_psy_get_voltage_min(struct tcpm_port = *port, { if (port->pps_data.active) val->intval =3D port->pps_data.min_volt * 1000; + else if (port->spr_avs_data.active) + val->intval =3D SPR_AVS_TIER1_MIN_VOLT_MV * 1000; else val->intval =3D port->supply_voltage * 1000; =20 @@ -7822,6 +8198,8 @@ static int tcpm_psy_get_voltage_max(struct tcpm_port = *port, { if (port->pps_data.active) val->intval =3D port->pps_data.max_volt * 1000; + else if (port->spr_avs_data.active) + val->intval =3D port->spr_avs_data.max_out_volt_mv * 1000; else val->intval =3D port->supply_voltage * 1000; =20 @@ -7841,6 +8219,8 @@ static int tcpm_psy_get_current_max(struct tcpm_port = *port, { if (port->pps_data.active) val->intval =3D port->pps_data.max_curr * 1000; + else if (port->spr_avs_data.active) + val->intval =3D port->spr_avs_data.max_current_ma * 1000; else val->intval =3D port->current_limit * 1000; =20 @@ -7916,17 +8296,41 @@ static int tcpm_psy_get_prop(struct power_supply *p= sy, return ret; } =20 +static int tcpm_disable_pps_avs(struct tcpm_port *port) +{ + int ret =3D 0; + + if (port->pps_data.active) + ret =3D tcpm_pps_activate(port, false); + else if (port->spr_avs_data.active) + ret =3D tcpm_spr_avs_activate(port, false); + + return ret; +} + static int tcpm_psy_set_online(struct tcpm_port *port, const union power_supply_propval *val) { - int ret; + int ret =3D 0; =20 switch (val->intval) { case TCPM_PSY_FIXED_ONLINE: - ret =3D tcpm_pps_activate(port, false); + ret =3D tcpm_disable_pps_avs(port); + break; + case TCPM_PSY_PPS_ONLINE: + if (port->spr_avs_data.active) + ret =3D tcpm_spr_avs_activate(port, false); + if (!ret) + ret =3D tcpm_pps_activate(port, true); break; - case TCPM_PSY_PROG_ONLINE: - ret =3D tcpm_pps_activate(port, true); + case TCPM_PSY_SPR_AVS_ONLINE: + tcpm_log(port, "request to set AVS online"); + if (port->spr_avs_data.active) + return 0; + ret =3D tcpm_disable_pps_avs(port); + if (ret) + break; + ret =3D tcpm_spr_avs_activate(port, true); break; default: ret =3D -EINVAL; @@ -7955,13 +8359,10 @@ static int tcpm_psy_set_prop(struct power_supply *p= sy, ret =3D tcpm_psy_set_online(port, val); break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: - ret =3D tcpm_pps_set_out_volt(port, val->intval / 1000); + ret =3D tcpm_aug_set_out_volt(port, val->intval / 1000); break; case POWER_SUPPLY_PROP_CURRENT_NOW: - if (val->intval > port->pps_data.max_curr * 1000) - ret =3D -EINVAL; - else - ret =3D tcpm_pps_set_op_curr(port, val->intval / 1000); + ret =3D tcpm_aug_set_op_curr(port, val->intval / 1000); break; default: ret =3D -EINVAL; @@ -8006,7 +8407,9 @@ static int devm_tcpm_psy_register(struct tcpm_port *p= ort) port->psy_desc.type =3D POWER_SUPPLY_TYPE_USB; port->psy_desc.usb_types =3D BIT(POWER_SUPPLY_USB_TYPE_C) | BIT(POWER_SUPPLY_USB_TYPE_PD) | - BIT(POWER_SUPPLY_USB_TYPE_PD_PPS); + BIT(POWER_SUPPLY_USB_TYPE_PD_PPS) | + BIT(POWER_SUPPLY_USB_TYPE_PD_PPS_SPR_AVS) | + BIT(POWER_SUPPLY_USB_TYPE_PD_SPR_AVS); port->psy_desc.properties =3D tcpm_psy_props; port->psy_desc.num_properties =3D ARRAY_SIZE(tcpm_psy_props); port->psy_desc.get_property =3D tcpm_psy_get_prop; @@ -8101,7 +8504,7 @@ struct tcpm_port *tcpm_register_port(struct device *d= ev, struct tcpc_dev *tcpc) =20 init_completion(&port->tx_complete); init_completion(&port->swap_complete); - init_completion(&port->pps_complete); + init_completion(&port->aug_supply_req_complete); tcpm_debugfs_init(port); =20 err =3D tcpm_fw_get_caps(port, tcpc->fwnode); diff --git a/include/linux/usb/pd.h b/include/linux/usb/pd.h index 5a98983195cb..337a5485af7c 100644 --- a/include/linux/usb/pd.h +++ b/include/linux/usb/pd.h @@ -398,9 +398,30 @@ enum pd_apdo_type { #define PDO_SPR_AVS_APDO_15V_TO_20V_MAX_CURR GENMASK(9, 0) /* 10mA unit */ =20 /* SPR AVS has two different current ranges 9V - 15V, 15V - 20V */ -#define SPR_AVS_TIER1_MIN_VOLT_MV 9000 -#define SPR_AVS_TIER1_MAX_VOLT_MV 15000 -#define SPR_AVS_TIER2_MAX_VOLT_MV 20000 +#define SPR_AVS_TIER1_MIN_VOLT_MV 9000 +#define SPR_AVS_TIER1_MAX_VOLT_MV 15000 +#define SPR_AVS_TIER2_MAX_VOLT_MV 20000 + +#define SPR_AVS_AVS_SMALL_STEP_V 1 +/* vAvsStep - 100mv */ +#define SPR_AVS_VOLT_MV_STEP 100 +/* SPR AVS RDO Operating Current is in 50mA step */ +#define RDO_SPR_AVS_CURR_MA_STEP 50 +/* SPR AVS RDO Output voltage is in 25mV step */ +#define RDO_SPR_AVS_OUT_VOLT_MV_STEP 25 + +#define RDO_SPR_AVS_VOLT GENMASK(20, 9) +#define RDO_SPR_AVS_CURR GENMASK(6, 0) + +#define RDO_SPR_AVS_OUT_VOLT(mv) \ + FIELD_PREP(RDO_SPR_AVS_VOLT, ((mv) / RDO_SPR_AVS_OUT_VOLT_MV_STEP)) + +#define RDO_SPR_AVS_OP_CURR(ma) \ + FIELD_PREP(RDO_SPR_AVS_CURR, ((ma) / RDO_SPR_AVS_CURR_MA_STEP)) + +#define RDO_AVS(idx, out_mv, op_ma, flags) \ + (RDO_OBJ(idx) | (flags) | \ + RDO_SPR_AVS_OUT_VOLT(out_mv) | RDO_SPR_AVS_OP_CURR(op_ma)) =20 static inline enum pd_pdo_type pdo_type(u32 pdo) { @@ -660,6 +681,11 @@ static inline unsigned int rdo_max_power(u32 rdo) =20 #define PD_P_SNK_STDBY_MW 2500 /* 2500 mW */ =20 +#define PD_I_SNK_STBY_MA 500 /* 500 mA */ + +#define PD_T_AVS_SRC_TRANS_SMALL 50 /* 50 ms */ +#define PD_T_AVS_SRC_TRANS_LARGE 700 /* 700 ms */ + #if IS_ENABLED(CONFIG_TYPEC) =20 struct usb_power_delivery; diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h index b22e659f81ba..93079450bba0 100644 --- a/include/linux/usb/tcpm.h +++ b/include/linux/usb/tcpm.h @@ -31,7 +31,7 @@ enum typec_cc_polarity { /* Time to wait for TCPC to complete transmit */ #define PD_T_TCPC_TX_TIMEOUT 100 /* in ms */ #define PD_ROLE_SWAP_TIMEOUT (MSEC_PER_SEC * 10) -#define PD_PPS_CTRL_TIMEOUT (MSEC_PER_SEC * 10) +#define PD_AUG_PSY_CTRL_TIMEOUT (MSEC_PER_SEC * 10) =20 enum tcpm_transmit_status { TCPC_TX_SUCCESS =3D 0, --=20 2.53.0.851.ga537e3e6e9-goog