From nobody Tue Apr 7 14:04:16 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 E2AEA38A731 for ; Thu, 26 Feb 2026 05:53:18 +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=1772085200; cv=none; b=uW4b2m8q5MrzMNcAw1Jl2AmOlse5ze7kqccFhQGpkIZmrfhAewrPXaoQ0MFSBR095TkyJVQqEkOzL4KTMA+EibDhPQ3yC5TZ0oyP3ubAblBs318sg+M52736ShUs/knbvho3vAn/E+A9B3JX+Sm4+0jUZiJDaur0TKMIDJbVsI4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772085200; c=relaxed/simple; bh=LfhQDPGS3jOh7xhVxS4sehprRV4qazzcCsUCb66y5Hw=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=gOVMGzMlB7PtNdH9Ch9E+54dVo2ceMapepJkJXTJfTJkbc+/RzFHOVMXmQFyveyInT0eiX86Crztt/bC00byfWNtpkYfw1N9ls4pbO0L49f/pyZaL9SAlKv9ctKb4LdP5AJiQQxwOWAxqQspADJZsz0dMAvcs6x0toWHLUUinFU= 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=T/ZXRGEQ; 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="T/ZXRGEQ" Received: by mail-dl1-f73.google.com with SMTP id a92af1059eb24-1276e71652fso837453c88.0 for ; Wed, 25 Feb 2026 21:53:18 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1772085198; x=1772689998; 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=cFC9qEvebwSLDfjL8YA0PaUI3cLmvAY/JQ63j4juSKA=; b=T/ZXRGEQCXFViHtJ/BlxD3SE5ElWDg44jPLsIVzvsmgzj0S6LG5JwFT6R3wjQhfmGd 9gL8cvfKXakRiQCoc1KE7h0/0ooP/nfGAeSgFJGnWVwseBjSlAFCK7e80wmga8/ouTEy ziacAg7vSWFWOOFWOg9+itkYXzD8k9Tzw7AnIL14HoRdLVQFlS81tCaz0F0a98J0O/vz xfxf5N20a8zcgldplHKM0PYgV80BOKF/TG4dFnNUxi2NQOGXQlzvffmmHXVQW9bMFiVc +AVmKwHPyEtHuHFgogSjBjAL21U/h4dFgPf0IMJyfifPD25V8N6zqG+E/qNwZRArmFWW xZfw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772085198; x=1772689998; 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=cFC9qEvebwSLDfjL8YA0PaUI3cLmvAY/JQ63j4juSKA=; b=kr2DUzPNcZpT1Xewv/KSIvwmniqCB+TVulFnMnoLcClTnchHyIdmGCLhEWe9SLOpP/ aia8obAb7Ma2umS5kxGacGKWUsVBH5D+nxZ/RMas1I+oGSP54YCzFGfnIBPO9TxYfSzK Mflx+SMD674iiIt566uPtG3lHMf+iEkLJG9mA+9qnfMgVkJv1E6ZRS2dhnOd+x66h+6/ UWkiu+QmK37+cDYyivhC23B9SfM9iSSiaV+/P3rYenvSVwJfzcL/HHJZzTiPaMEeIXSk 6bfeSMHHHwTHn8G+nJVP/fNBcC8jLksU5rczdWkVaYuOYhBnDZ76BOTT8WhRO/dd1suB jq/w== X-Forwarded-Encrypted: i=1; AJvYcCUWMH/6nrocYLHOCDN3oJsVzlsa4HB0XLd0C+ChgOcfBj4z+audlbGuTAYBB3vdIKwDGP6MJbvs38GU8vM=@vger.kernel.org X-Gm-Message-State: AOJu0YxhYDrFPohPURqCL8h+pfeAnlI3Kty3bp3EHOdKa0Foy+I9bBge LXRsrjyGsuwYCpzQUHnrTUHX47Jy+25H2DSSQUCPxEfoV5/w43HD7O9HJFnJexTU3LqBw7GdW2z /QBudGg== X-Received: from dlbcm15.prod.google.com ([2002:a05:7022:688f:b0:127:335b:9077]) (user=badhri job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:2216:b0:124:b11f:67ec with SMTP id a92af1059eb24-127869c256cmr1193427c88.39.1772085198007; Wed, 25 Feb 2026 21:53:18 -0800 (PST) Date: Thu, 26 Feb 2026 05:53:09 +0000 In-Reply-To: <20260226055311.2591357-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: <20260226055311.2591357-1-badhri@google.com> X-Mailer: git-send-email 2.53.0.414.gf7e9f6c205-goog Message-ID: <20260226055311.2591357-2-badhri@google.com> Subject: [PATCH v1 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.414.gf7e9f6c205-goog From nobody Tue Apr 7 14:04:16 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 EA0A638B7B9 for ; Thu, 26 Feb 2026 05:53:20 +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=1772085202; cv=none; b=KMWc+21xw/LIVZARlOZygrNt8ns1C/adhnmFXjQwCRO/PDiyGaETNhUmgXf5rEUuNhFnw4av041ziAIeK+fH0kYjjTIVK6RLSNI4/42/vuDmI2nz1ajHy6B12mifeQZRxFssujvSfrQoiXb3ltKe6gCCgDVDn/pPOTwYVQXwxFc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772085202; c=relaxed/simple; bh=5PZ8CggFuOHWg9Rc+M/mGjcAtyt2FOwDfgn4r8JYe38=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=a5tyZKLmtOGLrnLF5l/LjlYzvq1dJ1FkVt98VL45cMOS4MKlxDZQQFy05zKZz0uUYCfv8dJbPqYSAxw59G5zVdagZmXUMFD5CEU22FYiJeb4t/SZTygtRjmee5/2i6S8ApQCtRsupvpb2mib8jSe0eaIj3IFxWgthlskz43MMqM= 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=nJ5EzEGh; 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="nJ5EzEGh" Received: by mail-dl1-f73.google.com with SMTP id a92af1059eb24-1273665df8fso697789c88.1 for ; Wed, 25 Feb 2026 21:53:20 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1772085200; x=1772690000; 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=LkHfgITQ7X2diSbprSgbvJHo2GTsX0M8Rs/FPJ/8x+8=; b=nJ5EzEGhJv6NmwB4nf2mwyPuJnrPiy9O22MdUKN8QI4muUNlT996WHs8KJMUlxtE8N cFiKEUk7NBspLKBA68rdwRDIo274+R4Y35st47VUzHp/w/g74+2eKCSVg9EilehRf+Gv DBwNtd2hPuFFC73XhB1iIcNzEDj/lETGUdsz76shNWHU6JNoniZ0kbx0yvChQfG+xQZ5 +Qw5oYDr3m5neNpLtGKbVG/VECbaXPWAcR5YUdoZ353SoIV4NNnLntRvFi57UoDNxoZ1 vhVzXlUenosXwYTLrM57wCNZY9sJrKDbsSVHKqeXR0lM5mzrivLBhO4JYYb1sKYpPP9j /78A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772085200; x=1772690000; 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=LkHfgITQ7X2diSbprSgbvJHo2GTsX0M8Rs/FPJ/8x+8=; b=Z38xdOLS753P+bEbq9brJUrljDBaZ032oBRfrF4+LE4a/3gxUzMulv/7dKK521lXZy xGxsrc6igYU0K/KTzV17kO3Kgg65SzJoLReo0WHKd3fHvmOhtCKjnoQnq8qsqF7wVEnv cKQQXNIbSd1BYJTEvVgrItDE1p5liePRx9lNQ+qsPmdntmIfIGwO5RxB3mhFZlfldRcB e0NoslqYLeYooq+nmRYsbTXC5wrqaIgNTqIy8rZodIKt2mvquSYnOhH4k+G0Hm2jF8yb b2lhe0DVfsNgGK/TZA8LJGqoq2L/9UyRWNOeMlcDzYB5DvN5SdmmuHNC3Qbq6uWZ/k9J X/dQ== X-Forwarded-Encrypted: i=1; AJvYcCVT30AiJ7OGlmCdGpYjsBwbF742zHttZIGMVRC1FAukkTUptA4ceNaTNqMQMnYTEyGUIeEN7Lwt+Bq5Vqo=@vger.kernel.org X-Gm-Message-State: AOJu0YxO7DS13uMkPUkc4/mb6b7edz7Qc6ok4r8/fWfRQJx+a0k3Jaxc bOHyfYPBPzfh//qt0z6IAwTQI+/p3jP6hQE884aGivH/vRc1EkUxXoF7RAoQ0LBhGvCHU1lHeTe z+7S/aQ== X-Received: from dlbuy16.prod.google.com ([2002:a05:7022:1e10:b0:127:32c4:b97b]) (user=badhri job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:4384:b0:11a:fec5:d005 with SMTP id a92af1059eb24-12789c76961mr613663c88.10.1772085199939; Wed, 25 Feb 2026 21:53:19 -0800 (PST) Date: Thu, 26 Feb 2026 05:53:10 +0000 In-Reply-To: <20260226055311.2591357-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: <20260226055311.2591357-1-badhri@google.com> X-Mailer: git-send-email 2.53.0.414.gf7e9f6c205-goog Message-ID: <20260226055311.2591357-3-badhri@google.com> Subject: [PATCH v1 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 --- drivers/power/supply/power_supply_sysfs.c | 2 ++ include/linux/power_supply.h | 3 +++ 2 files changed, 5 insertions(+) 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.414.gf7e9f6c205-goog From nobody Tue Apr 7 14:04:16 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 C578938B7DB for ; Thu, 26 Feb 2026 05:53:22 +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=1772085206; cv=none; b=J3/4IPZZ2cVu6HpZzjWlKuFy/yZ4MjMekMYr38vOnWxiYiOFC9MD+rXkPFWOh/dBUJ/ksw8jApEOfToJ27DBs7Xn1K4nhJznfQPd9xQmC8OdgX774dwsyB4tQpE/OJa6HrCKGmHaDT6qaRKNceBOTOivri4W66HnkUjytWsuADE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772085206; c=relaxed/simple; bh=uDzzx8A1bZhURcD1aP7xq6SqvZ+yCSSjiJrjw5yZNRM=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=CqJ7DCT67mxw1Dt/pTITQkdbHEOwGzHIBgrjomBgPEAOyQ6MFMEuox3JQ+PAt8BaSMvER7ovkeKoOCABmX15C/zhoDJnciJZboagN1mHU9kptbikq/eNALaSqyGOAHnW18SosgdUldgCxw2awL21h1vFQ+QTz+BjlyXraidcZJY= 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=XXXTNQol; 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="XXXTNQol" Received: by mail-dl1-f74.google.com with SMTP id a92af1059eb24-1275c6fc58aso1332472c88.0 for ; Wed, 25 Feb 2026 21:53:22 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1772085202; x=1772690002; 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=kpoZpWA6974yYHQqBpTWRhGNhZD4JQOsH5lJPstk/YI=; b=XXXTNQolkzqRQ+OfZLhkpcE/adH47LPLPuwOH/9sJLmWuZVdQP4L1G5teWPwj5wdYy IMgVXni4+rt5ogd09j8HgCOCWhtAb8+63KTIAg5VjFvI3Ou4ghz4KdAk3ovPw8cDPsOA 2EL8aUOwEXWNeVUDoUbQ+Js0oqBSjYpZ6JYNPqpr0vnbgyeSn4vkJIzaRzAM9Pqc0eKB pIhGmlaMf+cy0zx+NynjH8aTFEglftCXExfACh7gsF2eNmROY2NWWBKMB2yyHMSX/g1T 7fm8l/WLzn14heTI4/+cQlt1D5IhVyDUgEZIQlpwZqmFIxN3Eo62GFf4jhV8cL0SmajF NKxg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772085202; x=1772690002; 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=kpoZpWA6974yYHQqBpTWRhGNhZD4JQOsH5lJPstk/YI=; b=eBNjbSVAKZeQERPp59Wrl40otA4vwhRuvZMW50PKl4eCo4jrVEFGllFAa+OSWAxdAX lbXmsrPwn6YtcJqtNeZFbFbTtoZ/A0//dzqhG8f3DUQkQAJJKraMDP1q6JH0Bo0En8xr b/L1lh0xjn//cvCZ6x1yFVVI9Vmm6UT6ntznfKcKtT7PTV2WBfWV9+GsXL/scrLzFM8p bl5nNQoE70PRzlBrpU7FVygybzA05k9LYHOyhWozCJfAx1havNzu+ItdCaVfwBVFjyUh vhEbiqsyGidcIh64Ue8MQTFB9ZZxr5Q6siCe2CE0OVNCCAnykJiLS/98zfseok5KQ1WZ SVRQ== X-Forwarded-Encrypted: i=1; AJvYcCXSyn6DGOPV2WzMsh5HV+6PF8bt+502cZQ8uX5D+dQfo6mDr24lsAadQXd4P82juNNnaaIgSzTK3BMAZbE=@vger.kernel.org X-Gm-Message-State: AOJu0YzJ6uikgdZsklFHs6cZsnvC1d5BEoK/OFJk1KFPslL7U/3pHhmL WvqjG9MmvHKtE9EadnOhdLGRV8osyaVL3GbIXN8A9ZYzS3+LIKJqyNtXqeLXxfX8EPA0Mt+/S8a fe1okgw== X-Received: from dlbcy10.prod.google.com ([2002:a05:7022:b8a:b0:127:f7c:81f9]) (user=badhri job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:408:b0:11b:9b9f:4283 with SMTP id a92af1059eb24-127869a584cmr1086612c88.24.1772085201809; Wed, 25 Feb 2026 21:53:21 -0800 (PST) Date: Thu, 26 Feb 2026 05:53:11 +0000 In-Reply-To: <20260226055311.2591357-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: <20260226055311.2591357-1-badhri@google.com> X-Mailer: git-send-email 2.53.0.414.gf7e9f6c205-goog Message-ID: <20260226055311.2591357-4-badhri@google.com> Subject: [PATCH v1 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 89c7757388ec..43471748f625 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; @@ -3294,6 +3350,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 @@ -3465,12 +3522,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 @@ -3568,7 +3625,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. */ @@ -3576,6 +3633,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: @@ -3630,6 +3701,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: @@ -3642,6 +3714,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); @@ -4139,9 +4218,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: @@ -4179,6 +4258,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"); @@ -4200,6 +4283,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 @@ -4250,6 +4350,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; @@ -4417,13 +4599,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 @@ -4646,6 +4889,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); @@ -4685,8 +4936,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; } @@ -5178,7 +5428,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); @@ -5409,13 +5659,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 @@ -5431,23 +5684,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); @@ -5467,7 +5755,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); @@ -5654,8 +5942,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) { @@ -5688,8 +5975,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 @@ -5826,8 +6112,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. @@ -5975,7 +6260,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: @@ -6949,7 +7234,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; @@ -6957,7 +7242,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; } @@ -6967,38 +7264,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 @@ -7010,7 +7300,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; @@ -7018,7 +7308,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; } @@ -7028,33 +7327,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 @@ -7097,9 +7394,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) { @@ -7108,11 +7405,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 @@ -7268,16 +7629,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); @@ -7787,7 +8158,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 { @@ -7805,7 +8177,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 { @@ -7820,6 +8194,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 @@ -7831,6 +8207,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 @@ -7850,6 +8228,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 @@ -7925,17 +8305,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; @@ -7964,13 +8368,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; @@ -8015,7 +8416,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; @@ -8110,7 +8513,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.414.gf7e9f6c205-goog