All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 3/4] usb: typec: ucsi: Register SOP/SOP' Discover Identity Responses
@ 2024-02-29  1:53 Jameson Thies
  0 siblings, 0 replies; 9+ messages in thread
From: Jameson Thies @ 2024-02-29  1:53 UTC (permalink / raw)
  To: heikki.krogerus, linux-usb
  Cc: jthies, pmalani, bleung, abhishekpandit, andersson,
	dmitry.baryshkov, fabrice.gasnier, gregkh, hdegoede,
	neil.armstrong, rajaram.regupathy, saranya.gopal, linux-kernel

Register SOP and SOP' Discover Identity responses with the USB Type-C
Connector Class as partner and cable identities, respectively. Discover
Identity responses are requested from the PPM using the GET_PD_MESSAGE
UCSI command.

Signed-off-by: Jameson Thies <jthies@google.com>
---
GET_PD_MESSAGE responses from the PPM populate partner and cable
identity in sysfs:
nospike-rev4 /sys/class/typec # ls port0-partner/identity/
cert_stat  id_header  product  product_type_vdo1  product_type_vdo2
product_type_vdo3
nospike-rev4 /sys/class/typec # ls port0-cable/identity/
cert_stat  id_header  product  product_type_vdo1  product_type_vdo2
product_type_vdo3

Changes in v3:
- None.

Changes in v2:
- Re-ordered memset call and null assignment when unregistering partners
and cables.
- Supports registering partner and cable identity with UCSI versions
before v2.0.
- Shortened lines to within 80 characters with the exception of two
error log lines with three indentations.
- Tested on usb-testing branch merged with chromeOS 6.8-rc2 kernel.

 drivers/usb/typec/ucsi/ucsi.c | 125 ++++++++++++++++++++++++++++++++++
 drivers/usb/typec/ucsi/ucsi.h |  29 ++++++++
 2 files changed, 154 insertions(+)

diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index 7c84687b5d1a3..4088422b33c74 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -646,6 +646,121 @@ static int ucsi_get_src_pdos(struct ucsi_connector *con)
 	return ret;
 }
 
+static int ucsi_read_identity(struct ucsi_connector *con, u8 recipient,
+			      struct usb_pd_identity *id)
+{
+	struct ucsi *ucsi = con->ucsi;
+	struct ucsi_pd_message_disc_id resp = {};
+	u64 command;
+	int ret;
+
+	if (ucsi->version < UCSI_VERSION_2_0) {
+		/*
+		 * Before UCSI v2.0, MESSAGE_IN is 16 bytes which cannot fit
+		 * the 28 byte identity response including the VDM header.
+		 * First request the VDM header, ID Header VDO, Cert Stat VDO
+		 * and Product VDO.
+		 */
+		command = UCSI_COMMAND(UCSI_GET_PD_MESSAGE) |
+		    UCSI_CONNECTOR_NUMBER(con->num);
+		command |= UCSI_GET_PD_MESSAGE_RECIPIENT(recipient);
+		command |= UCSI_GET_PD_MESSAGE_OFFSET(0);
+		command |= UCSI_GET_PD_MESSAGE_BYTES(0x10);
+		command |= UCSI_GET_PD_MESSAGE_TYPE(
+		    UCSI_GET_PD_MESSAGE_TYPE_IDENTITY);
+
+		ret = ucsi_send_command(ucsi, command, &resp, 0x10);
+		if (ret < 0) {
+			dev_err(ucsi->dev,
+				"UCSI_GET_PD_MESSAGE v1.2 failed first request (%d)\n",
+				ret);
+			return ret;
+		}
+
+		/* Then request Product Type VDO1 through Product Type VDO3. */
+		command = UCSI_COMMAND(UCSI_GET_PD_MESSAGE) |
+		    UCSI_CONNECTOR_NUMBER(con->num);
+		command |= UCSI_GET_PD_MESSAGE_RECIPIENT(recipient);
+		command |= UCSI_GET_PD_MESSAGE_OFFSET(0x10);
+		command |= UCSI_GET_PD_MESSAGE_BYTES(0xc);
+		command |= UCSI_GET_PD_MESSAGE_TYPE(
+		    UCSI_GET_PD_MESSAGE_TYPE_IDENTITY);
+
+		ret = ucsi_send_command(ucsi, command, &resp.vdo[0], 0xc);
+		if (ret < 0) {
+			dev_err(ucsi->dev,
+				"UCSI_GET_PD_MESSAGE v1.2 failed second request (%d)\n",
+				ret);
+			return ret;
+		}
+	} else {
+		/*
+		 * In UCSI v2.0 and after, MESSAGE_IN is large enough to request
+		 * the large enough to request the full Discover Identity
+		 * response at once.
+		 */
+		command = UCSI_COMMAND(UCSI_GET_PD_MESSAGE) |
+		    UCSI_CONNECTOR_NUMBER(con->num);
+		command |= UCSI_GET_PD_MESSAGE_RECIPIENT(recipient);
+		/* VDM Header + 6 VDOs (0x1c bytes) without an offset */
+		command |= UCSI_GET_PD_MESSAGE_OFFSET(0);
+		command |= UCSI_GET_PD_MESSAGE_BYTES(0x1c);
+		command |= UCSI_GET_PD_MESSAGE_TYPE(
+		    UCSI_GET_PD_MESSAGE_TYPE_IDENTITY);
+
+		ret = ucsi_send_command(ucsi, command, &resp, sizeof(resp));
+		if (ret < 0) {
+			dev_err(ucsi->dev, "UCSI_GET_PD_MESSAGE failed (%d)\n",
+				ret);
+			return ret;
+		}
+	}
+
+	id->id_header = resp.id_header;
+	id->cert_stat = resp.cert_stat;
+	id->product = resp.product;
+	id->vdo[0] = resp.vdo[0];
+	id->vdo[1] = resp.vdo[1];
+	id->vdo[2] = resp.vdo[2];
+	return 0;
+}
+
+static int ucsi_get_partner_identity(struct ucsi_connector *con)
+{
+	int ret;
+
+	ret = ucsi_read_identity(con, UCSI_RECIPIENT_SOP,
+				 &con->partner_identity);
+	if (ret < 0)
+		return ret;
+
+	ret = typec_partner_set_identity(con->partner);
+	if (ret < 0) {
+		dev_err(con->ucsi->dev, "Failed to set partner identity (%d)\n",
+			ret);
+	}
+
+	return ret;
+}
+
+static int ucsi_get_cable_identity(struct ucsi_connector *con)
+{
+	int ret;
+
+	ret = ucsi_read_identity(con, UCSI_RECIPIENT_SOP_P,
+				 &con->cable_identity);
+	if (ret < 0)
+		return ret;
+
+	ret = typec_cable_set_identity(con->cable);
+	if (ret < 0) {
+		dev_err(con->ucsi->dev, "Failed to set cable identity (%d)\n",
+			ret);
+	}
+
+	return ret;
+}
+
 static int ucsi_check_altmodes(struct ucsi_connector *con)
 {
 	int ret, num_partner_am;
@@ -754,6 +869,7 @@ static int ucsi_register_cable(struct ucsi_connector *con)
 		break;
 	}
 
+	desc.identity = &con->cable_identity;
 	desc.active = !!(UCSI_CABLE_PROP_FLAG_ACTIVE_CABLE &
 			 con->cable_prop.flags);
 	desc.pd_revision = UCSI_CABLE_PROP_FLAG_PD_MAJOR_REV_AS_BCD(
@@ -777,6 +893,7 @@ static void ucsi_unregister_cable(struct ucsi_connector *con)
 		return;
 
 	typec_unregister_cable(con->cable);
+	memset(&con->cable_identity, 0, sizeof(con->cable_identity));
 	con->cable = NULL;
 }
 
@@ -827,6 +944,7 @@ static int ucsi_register_partner(struct ucsi_connector *con)
 		break;
 	}
 
+	desc.identity = &con->partner_identity;
 	desc.usb_pd = pwr_opmode == UCSI_CONSTAT_PWR_OPMODE_PD;
 	desc.pd_revision = UCSI_CONCAP_FLAG_PARTNER_PD_MAJOR_REV_AS_BCD(con->cap.flags);
 
@@ -855,6 +973,7 @@ static void ucsi_unregister_partner(struct ucsi_connector *con)
 	ucsi_unregister_altmodes(con, UCSI_RECIPIENT_SOP);
 	ucsi_unregister_cable(con);
 	typec_unregister_partner(con->partner);
+	memset(&con->partner_identity, 0, sizeof(con->partner_identity));
 	con->partner = NULL;
 }
 
@@ -975,6 +1094,10 @@ static int ucsi_check_cable(struct ucsi_connector *con)
 	if (ret < 0)
 		return ret;
 
+	ret = ucsi_get_cable_identity(con);
+	if (ret < 0)
+		return ret;
+
 	return 0;
 }
 
@@ -1019,6 +1142,7 @@ static void ucsi_handle_connector_change(struct work_struct *work)
 			ucsi_register_partner(con);
 			ucsi_partner_task(con, ucsi_check_connection, 1, HZ);
 			ucsi_partner_task(con, ucsi_check_connector_capability, 1, HZ);
+			ucsi_partner_task(con, ucsi_get_partner_identity, 1, HZ);
 			ucsi_partner_task(con, ucsi_check_cable, 1, HZ);
 
 			if (UCSI_CONSTAT_PWR_OPMODE(con->status.flags) ==
@@ -1418,6 +1542,7 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con)
 		ucsi_register_partner(con);
 		ucsi_pwr_opmode_change(con);
 		ucsi_port_psy_changed(con);
+		ucsi_get_partner_identity(con);
 		ucsi_check_cable(con);
 	}
 
diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
index f0aabef0b7c64..b89fae82e8ce7 100644
--- a/drivers/usb/typec/ucsi/ucsi.h
+++ b/drivers/usb/typec/ucsi/ucsi.h
@@ -106,6 +106,7 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num);
 #define UCSI_GET_CABLE_PROPERTY		0x11
 #define UCSI_GET_CONNECTOR_STATUS	0x12
 #define UCSI_GET_ERROR_STATUS		0x13
+#define UCSI_GET_PD_MESSAGE		0x15
 
 #define UCSI_CONNECTOR_NUMBER(_num_)		((u64)(_num_) << 16)
 #define UCSI_COMMAND(_cmd_)			((_cmd_) & 0xff)
@@ -159,6 +160,18 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num);
 #define UCSI_MAX_PDOS				(4)
 #define UCSI_GET_PDOS_SRC_PDOS			((u64)1 << 34)
 
+/* GET_PD_MESSAGE command bits */
+#define UCSI_GET_PD_MESSAGE_RECIPIENT(_r_)	((u64)(_r_) << 23)
+#define UCSI_GET_PD_MESSAGE_OFFSET(_r_)		((u64)(_r_) << 26)
+#define UCSI_GET_PD_MESSAGE_BYTES(_r_)		((u64)(_r_) << 34)
+#define UCSI_GET_PD_MESSAGE_TYPE(_r_)		((u64)(_r_) << 42)
+#define   UCSI_GET_PD_MESSAGE_TYPE_SNK_CAP_EXT	0
+#define   UCSI_GET_PD_MESSAGE_TYPE_SRC_CAP_EXT	1
+#define   UCSI_GET_PD_MESSAGE_TYPE_BAT_CAP	2
+#define   UCSI_GET_PD_MESSAGE_TYPE_BAT_STAT	3
+#define   UCSI_GET_PD_MESSAGE_TYPE_IDENTITY	4
+#define   UCSI_GET_PD_MESSAGE_TYPE_REVISION	5
+
 /* -------------------------------------------------------------------------- */
 
 /* Error information returned by PPM in response to GET_ERROR_STATUS command. */
@@ -338,6 +351,18 @@ struct ucsi_connector_status {
 	((get_unaligned_le32(&(_p_)[5]) & GENMASK(16, 1)) >> 1)
 } __packed;
 
+/*
+ * Data structure filled by PPM in response to GET_PD_MESSAGE command with the
+ * Response Message Type set to Discover Identity Response.
+ */
+struct ucsi_pd_message_disc_id {
+	u32 vdm_header;
+	u32 id_header;
+	u32 cert_stat;
+	u32 product;
+	u32 vdo[3];
+} __packed;
+
 /* -------------------------------------------------------------------------- */
 
 struct ucsi_debugfs_entry {
@@ -428,6 +453,10 @@ struct ucsi_connector {
 	struct usb_power_delivery_capabilities *partner_sink_caps;
 
 	struct usb_role_switch *usb_role_sw;
+
+	/* USB PD identity */
+	struct usb_pd_identity partner_identity;
+	struct usb_pd_identity cable_identity;
 };
 
 int ucsi_send_command(struct ucsi *ucsi, u64 command,
-- 
2.44.0.rc1.240.g4c46232300-goog


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v3 0/4] usb: typec: ucsi: Expand SOP/SOP' Discovery
@ 2024-02-29 23:26 Jameson Thies
  2024-02-29 23:26 ` [PATCH v3 1/4] usb: typec: ucsi: Clean up UCSI_CABLE_PROP macros Jameson Thies
                   ` (3 more replies)
  0 siblings, 4 replies; 9+ messages in thread
From: Jameson Thies @ 2024-02-29 23:26 UTC (permalink / raw)
  To: heikki.krogerus, linux-usb
  Cc: jthies, pmalani, bleung, abhishekpandit, andersson,
	dmitry.baryshkov, fabrice.gasnier, gregkh, hdegoede,
	neil.armstrong, rajaram.regupathy, saranya.gopal, linux-kernel

Hi Heikki,

This patch series expands support for partner and cable discover in the
UCSI driver. There are a few pieces here.

1. Some cleanup of the GET_CABLE_PROP definitions in ucsi.h.
2. Cable discovery and registration with the USB Type-C connector class.
3. Partner/Cable identity registration with the USB Type-C connector
class.
4. SOP' alternate mode registration with the USB-C connector class using
a cable plug.

These have been tested on a the usb-testing branch merged with a
chromeOS 6.8-rc2 kernel. Let me know if you have any questions.

Thanks,
Jameson

Changes in v3:
- Fixed CC stable and email threading issue.

Changes in v2:
- Re-ordered memset call and null assignment when unregistering partners
and cables.
- Supports registering partner and cable identity with UCSI versions
before v2.0.
- Shortened lines to within 80 characters with the exception of two
error log lines with three indentations.
- Tested on usb-testing branch merged with chromeOS 6.8-rc2 kernel.

Jameson Thies (4):
  usb: typec: ucsi: Clean up UCSI_CABLE_PROP macros
  usb: typec: ucsi: Register cables based on GET_CABLE_PROPERTY
  usb: typec: ucsi: Register SOP/SOP' Discover Identity Responses
  usb: typec: ucsi: Register SOP' alternate modes with cable plug

 drivers/usb/typec/ucsi/ucsi.c | 258 ++++++++++++++++++++++++++++++++++
 drivers/usb/typec/ucsi/ucsi.h |  40 +++++-
 2 files changed, 296 insertions(+), 2 deletions(-)


base-commit: a560a5672826fc1e057068bda93b3d4c98d037a2
-- 
2.44.0.rc1.240.g4c46232300-goog


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v3 1/4] usb: typec: ucsi: Clean up UCSI_CABLE_PROP macros
  2024-02-29 23:26 [PATCH v3 0/4] usb: typec: ucsi: Expand SOP/SOP' Discovery Jameson Thies
@ 2024-02-29 23:26 ` Jameson Thies
  2024-02-29 23:26 ` [PATCH v3 2/4] usb: typec: ucsi: Register cables based on GET_CABLE_PROPERTY Jameson Thies
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 9+ messages in thread
From: Jameson Thies @ 2024-02-29 23:26 UTC (permalink / raw)
  To: heikki.krogerus, linux-usb
  Cc: jthies, pmalani, bleung, abhishekpandit, andersson,
	dmitry.baryshkov, fabrice.gasnier, gregkh, hdegoede,
	neil.armstrong, rajaram.regupathy, saranya.gopal, linux-kernel,
	stable, Benson Leung

Clean up UCSI_CABLE_PROP macros by fixing a bitmask shifting error for
plug type and updating the modal support macro for consistent naming.

Fixes: 3cf657f07918 ("usb: typec: ucsi: Remove all bit-fields")
Cc: stable@vger.kernel.org
Reviewed-by: Benson Leung <bleung@chromium.org>
Reviewed-by: Prashant Malani <pmalani@chromium.org>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Signed-off-by: Jameson Thies <jthies@google.com>
---
Changes in v3:
- Fixed CC stable.

Changes in v2:
- Tested on usb-testing branch merged with chromeOS 6.8-rc2 kernel.

 drivers/usb/typec/ucsi/ucsi.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
index 7e35ffbe0a6f2..469a2baf472e4 100644
--- a/drivers/usb/typec/ucsi/ucsi.h
+++ b/drivers/usb/typec/ucsi/ucsi.h
@@ -259,12 +259,12 @@ struct ucsi_cable_property {
 #define UCSI_CABLE_PROP_FLAG_VBUS_IN_CABLE	BIT(0)
 #define UCSI_CABLE_PROP_FLAG_ACTIVE_CABLE	BIT(1)
 #define UCSI_CABLE_PROP_FLAG_DIRECTIONALITY	BIT(2)
-#define UCSI_CABLE_PROP_FLAG_PLUG_TYPE(_f_)	((_f_) & GENMASK(3, 0))
+#define UCSI_CABLE_PROP_FLAG_PLUG_TYPE(_f_)	(((_f_) & GENMASK(4, 3)) >> 3)
 #define   UCSI_CABLE_PROPERTY_PLUG_TYPE_A	0
 #define   UCSI_CABLE_PROPERTY_PLUG_TYPE_B	1
 #define   UCSI_CABLE_PROPERTY_PLUG_TYPE_C	2
 #define   UCSI_CABLE_PROPERTY_PLUG_OTHER	3
-#define UCSI_CABLE_PROP_MODE_SUPPORT		BIT(5)
+#define UCSI_CABLE_PROP_FLAG_MODE_SUPPORT	BIT(5)
 	u8 latency;
 } __packed;
 
-- 
2.44.0.rc1.240.g4c46232300-goog


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v3 2/4] usb: typec: ucsi: Register cables based on GET_CABLE_PROPERTY
  2024-02-29 23:26 [PATCH v3 0/4] usb: typec: ucsi: Expand SOP/SOP' Discovery Jameson Thies
  2024-02-29 23:26 ` [PATCH v3 1/4] usb: typec: ucsi: Clean up UCSI_CABLE_PROP macros Jameson Thies
@ 2024-02-29 23:26 ` Jameson Thies
  2024-02-29 23:26 ` [PATCH v3 3/4] usb: typec: ucsi: Register SOP/SOP' Discover Identity Responses Jameson Thies
  2024-02-29 23:26 ` [PATCH v3 4/4] usb: typec: ucsi: Register SOP' alternate modes with cable plug Jameson Thies
  3 siblings, 0 replies; 9+ messages in thread
From: Jameson Thies @ 2024-02-29 23:26 UTC (permalink / raw)
  To: heikki.krogerus, linux-usb
  Cc: jthies, pmalani, bleung, abhishekpandit, andersson,
	dmitry.baryshkov, fabrice.gasnier, gregkh, hdegoede,
	neil.armstrong, rajaram.regupathy, saranya.gopal, linux-kernel,
	Benson Leung

Register cables with the Type-C Connector Class in the UCSI driver based
on the PPM response to GET_CABLE_PROPERTY. Registered cable properties
include plug type, cable type and major revision.

Reviewed-by: Benson Leung <bleung@chromium.org>
Reviewed-by: Prashant Malani <pmalani@chromium.org>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: Jameson Thies <jthies@google.com>
---
Expected cable properties populate the USB Type-C connector class sysfs
paths:
nospike-rev4 /sys/class/typec # ls port0-cable
device  identity  plug_type  port0-plug0  power  subsystem  type  uevent
usb_power_delivery_revision

Changes in v3:
- None.

Changes in v2:
- Shortened lines to within 80 characters.
- Tested on usb-testing branch merged with chromeOS 6.8-rc2 kernel.

 drivers/usb/typec/ucsi/ucsi.c | 73 +++++++++++++++++++++++++++++++++++
 drivers/usb/typec/ucsi/ucsi.h |  5 +++
 2 files changed, 78 insertions(+)

diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index ae105383e69e7..7c84687b5d1a3 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -734,6 +734,52 @@ static void ucsi_unregister_partner_pdos(struct ucsi_connector *con)
 	con->partner_pd = NULL;
 }
 
+static int ucsi_register_cable(struct ucsi_connector *con)
+{
+	struct typec_cable *cable;
+	struct typec_cable_desc desc = {};
+
+	switch (UCSI_CABLE_PROP_FLAG_PLUG_TYPE(con->cable_prop.flags)) {
+	case UCSI_CABLE_PROPERTY_PLUG_TYPE_A:
+		desc.type = USB_PLUG_TYPE_A;
+		break;
+	case UCSI_CABLE_PROPERTY_PLUG_TYPE_B:
+		desc.type = USB_PLUG_TYPE_B;
+		break;
+	case UCSI_CABLE_PROPERTY_PLUG_TYPE_C:
+		desc.type = USB_PLUG_TYPE_C;
+		break;
+	default:
+		desc.type = USB_PLUG_NONE;
+		break;
+	}
+
+	desc.active = !!(UCSI_CABLE_PROP_FLAG_ACTIVE_CABLE &
+			 con->cable_prop.flags);
+	desc.pd_revision = UCSI_CABLE_PROP_FLAG_PD_MAJOR_REV_AS_BCD(
+	    con->cable_prop.flags);
+
+	cable = typec_register_cable(con->port, &desc);
+	if (IS_ERR(cable)) {
+		dev_err(con->ucsi->dev,
+			"con%d: failed to register cable (%ld)\n", con->num,
+			PTR_ERR(cable));
+		return PTR_ERR(cable);
+	}
+
+	con->cable = cable;
+	return 0;
+}
+
+static void ucsi_unregister_cable(struct ucsi_connector *con)
+{
+	if (!con->cable)
+		return;
+
+	typec_unregister_cable(con->cable);
+	con->cable = NULL;
+}
+
 static void ucsi_pwr_opmode_change(struct ucsi_connector *con)
 {
 	switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
@@ -807,6 +853,7 @@ static void ucsi_unregister_partner(struct ucsi_connector *con)
 	typec_partner_set_usb_power_delivery(con->partner, NULL);
 	ucsi_unregister_partner_pdos(con);
 	ucsi_unregister_altmodes(con, UCSI_RECIPIENT_SOP);
+	ucsi_unregister_cable(con);
 	typec_unregister_partner(con->partner);
 	con->partner = NULL;
 }
@@ -907,6 +954,30 @@ static int ucsi_check_connection(struct ucsi_connector *con)
 	return 0;
 }
 
+static int ucsi_check_cable(struct ucsi_connector *con)
+{
+	u64 command;
+	int ret;
+
+	if (con->cable)
+		return 0;
+
+	command = UCSI_GET_CABLE_PROPERTY | UCSI_CONNECTOR_NUMBER(con->num);
+	ret = ucsi_send_command(con->ucsi, command, &con->cable_prop,
+				sizeof(con->cable_prop));
+	if (ret < 0) {
+		dev_err(con->ucsi->dev, "GET_CABLE_PROPERTY failed (%d)\n",
+			ret);
+		return ret;
+	}
+
+	ret = ucsi_register_cable(con);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
 static void ucsi_handle_connector_change(struct work_struct *work)
 {
 	struct ucsi_connector *con = container_of(work, struct ucsi_connector,
@@ -948,6 +1019,7 @@ static void ucsi_handle_connector_change(struct work_struct *work)
 			ucsi_register_partner(con);
 			ucsi_partner_task(con, ucsi_check_connection, 1, HZ);
 			ucsi_partner_task(con, ucsi_check_connector_capability, 1, HZ);
+			ucsi_partner_task(con, ucsi_check_cable, 1, HZ);
 
 			if (UCSI_CONSTAT_PWR_OPMODE(con->status.flags) ==
 			    UCSI_CONSTAT_PWR_OPMODE_PD)
@@ -1346,6 +1418,7 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con)
 		ucsi_register_partner(con);
 		ucsi_pwr_opmode_change(con);
 		ucsi_port_psy_changed(con);
+		ucsi_check_cable(con);
 	}
 
 	/* Only notify USB controller if partner supports USB data */
diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
index 469a2baf472e4..f0aabef0b7c64 100644
--- a/drivers/usb/typec/ucsi/ucsi.h
+++ b/drivers/usb/typec/ucsi/ucsi.h
@@ -265,6 +265,9 @@ struct ucsi_cable_property {
 #define   UCSI_CABLE_PROPERTY_PLUG_TYPE_C	2
 #define   UCSI_CABLE_PROPERTY_PLUG_OTHER	3
 #define UCSI_CABLE_PROP_FLAG_MODE_SUPPORT	BIT(5)
+#define UCSI_CABLE_PROP_FLAG_PD_MAJOR_REV(_f_)	(((_f_) & GENMASK(7, 6)) >> 6)
+#define UCSI_CABLE_PROP_FLAG_PD_MAJOR_REV_AS_BCD(_f_) \
+	UCSI_SPEC_REVISION_TO_BCD(UCSI_CABLE_PROP_FLAG_PD_MAJOR_REV(_f_))
 	u8 latency;
 } __packed;
 
@@ -400,6 +403,7 @@ struct ucsi_connector {
 
 	struct typec_port *port;
 	struct typec_partner *partner;
+	struct typec_cable *cable;
 
 	struct typec_altmode *port_altmode[UCSI_MAX_ALTMODES];
 	struct typec_altmode *partner_altmode[UCSI_MAX_ALTMODES];
@@ -408,6 +412,7 @@ struct ucsi_connector {
 
 	struct ucsi_connector_status status;
 	struct ucsi_connector_capability cap;
+	struct ucsi_cable_property cable_prop;
 	struct power_supply *psy;
 	struct power_supply_desc psy_desc;
 	u32 rdo;
-- 
2.44.0.rc1.240.g4c46232300-goog


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v3 3/4] usb: typec: ucsi: Register SOP/SOP' Discover Identity Responses
  2024-02-29 23:26 [PATCH v3 0/4] usb: typec: ucsi: Expand SOP/SOP' Discovery Jameson Thies
  2024-02-29 23:26 ` [PATCH v3 1/4] usb: typec: ucsi: Clean up UCSI_CABLE_PROP macros Jameson Thies
  2024-02-29 23:26 ` [PATCH v3 2/4] usb: typec: ucsi: Register cables based on GET_CABLE_PROPERTY Jameson Thies
@ 2024-02-29 23:26 ` Jameson Thies
  2024-03-01  1:19   ` Benson Leung
  2024-03-01 19:37   ` Prashant Malani
  2024-02-29 23:26 ` [PATCH v3 4/4] usb: typec: ucsi: Register SOP' alternate modes with cable plug Jameson Thies
  3 siblings, 2 replies; 9+ messages in thread
From: Jameson Thies @ 2024-02-29 23:26 UTC (permalink / raw)
  To: heikki.krogerus, linux-usb
  Cc: jthies, pmalani, bleung, abhishekpandit, andersson,
	dmitry.baryshkov, fabrice.gasnier, gregkh, hdegoede,
	neil.armstrong, rajaram.regupathy, saranya.gopal, linux-kernel

Register SOP and SOP' Discover Identity responses with the USB Type-C
Connector Class as partner and cable identities, respectively. Discover
Identity responses are requested from the PPM using the GET_PD_MESSAGE
UCSI command.

Signed-off-by: Jameson Thies <jthies@google.com>
---
GET_PD_MESSAGE responses from the PPM populate partner and cable
identity in sysfs:
nospike-rev4 /sys/class/typec # ls port0-partner/identity/
cert_stat  id_header  product  product_type_vdo1  product_type_vdo2
product_type_vdo3
nospike-rev4 /sys/class/typec # ls port0-cable/identity/
cert_stat  id_header  product  product_type_vdo1  product_type_vdo2
product_type_vdo3

Changes in v3:
- None.

Changes in v2:
- Re-ordered memset call and null assignment when unregistering partners
and cables.
- Supports registering partner and cable identity with UCSI versions
before v2.0.
- Shortened lines to within 80 characters with the exception of two
error log lines with three indentations.
- Tested on usb-testing branch merged with chromeOS 6.8-rc2 kernel.

 drivers/usb/typec/ucsi/ucsi.c | 125 ++++++++++++++++++++++++++++++++++
 drivers/usb/typec/ucsi/ucsi.h |  29 ++++++++
 2 files changed, 154 insertions(+)

diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index 7c84687b5d1a3..4088422b33c74 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -646,6 +646,121 @@ static int ucsi_get_src_pdos(struct ucsi_connector *con)
 	return ret;
 }
 
+static int ucsi_read_identity(struct ucsi_connector *con, u8 recipient,
+			      struct usb_pd_identity *id)
+{
+	struct ucsi *ucsi = con->ucsi;
+	struct ucsi_pd_message_disc_id resp = {};
+	u64 command;
+	int ret;
+
+	if (ucsi->version < UCSI_VERSION_2_0) {
+		/*
+		 * Before UCSI v2.0, MESSAGE_IN is 16 bytes which cannot fit
+		 * the 28 byte identity response including the VDM header.
+		 * First request the VDM header, ID Header VDO, Cert Stat VDO
+		 * and Product VDO.
+		 */
+		command = UCSI_COMMAND(UCSI_GET_PD_MESSAGE) |
+		    UCSI_CONNECTOR_NUMBER(con->num);
+		command |= UCSI_GET_PD_MESSAGE_RECIPIENT(recipient);
+		command |= UCSI_GET_PD_MESSAGE_OFFSET(0);
+		command |= UCSI_GET_PD_MESSAGE_BYTES(0x10);
+		command |= UCSI_GET_PD_MESSAGE_TYPE(
+		    UCSI_GET_PD_MESSAGE_TYPE_IDENTITY);
+
+		ret = ucsi_send_command(ucsi, command, &resp, 0x10);
+		if (ret < 0) {
+			dev_err(ucsi->dev,
+				"UCSI_GET_PD_MESSAGE v1.2 failed first request (%d)\n",
+				ret);
+			return ret;
+		}
+
+		/* Then request Product Type VDO1 through Product Type VDO3. */
+		command = UCSI_COMMAND(UCSI_GET_PD_MESSAGE) |
+		    UCSI_CONNECTOR_NUMBER(con->num);
+		command |= UCSI_GET_PD_MESSAGE_RECIPIENT(recipient);
+		command |= UCSI_GET_PD_MESSAGE_OFFSET(0x10);
+		command |= UCSI_GET_PD_MESSAGE_BYTES(0xc);
+		command |= UCSI_GET_PD_MESSAGE_TYPE(
+		    UCSI_GET_PD_MESSAGE_TYPE_IDENTITY);
+
+		ret = ucsi_send_command(ucsi, command, &resp.vdo[0], 0xc);
+		if (ret < 0) {
+			dev_err(ucsi->dev,
+				"UCSI_GET_PD_MESSAGE v1.2 failed second request (%d)\n",
+				ret);
+			return ret;
+		}
+	} else {
+		/*
+		 * In UCSI v2.0 and after, MESSAGE_IN is large enough to request
+		 * the large enough to request the full Discover Identity
+		 * response at once.
+		 */
+		command = UCSI_COMMAND(UCSI_GET_PD_MESSAGE) |
+		    UCSI_CONNECTOR_NUMBER(con->num);
+		command |= UCSI_GET_PD_MESSAGE_RECIPIENT(recipient);
+		/* VDM Header + 6 VDOs (0x1c bytes) without an offset */
+		command |= UCSI_GET_PD_MESSAGE_OFFSET(0);
+		command |= UCSI_GET_PD_MESSAGE_BYTES(0x1c);
+		command |= UCSI_GET_PD_MESSAGE_TYPE(
+		    UCSI_GET_PD_MESSAGE_TYPE_IDENTITY);
+
+		ret = ucsi_send_command(ucsi, command, &resp, sizeof(resp));
+		if (ret < 0) {
+			dev_err(ucsi->dev, "UCSI_GET_PD_MESSAGE failed (%d)\n",
+				ret);
+			return ret;
+		}
+	}
+
+	id->id_header = resp.id_header;
+	id->cert_stat = resp.cert_stat;
+	id->product = resp.product;
+	id->vdo[0] = resp.vdo[0];
+	id->vdo[1] = resp.vdo[1];
+	id->vdo[2] = resp.vdo[2];
+	return 0;
+}
+
+static int ucsi_get_partner_identity(struct ucsi_connector *con)
+{
+	int ret;
+
+	ret = ucsi_read_identity(con, UCSI_RECIPIENT_SOP,
+				 &con->partner_identity);
+	if (ret < 0)
+		return ret;
+
+	ret = typec_partner_set_identity(con->partner);
+	if (ret < 0) {
+		dev_err(con->ucsi->dev, "Failed to set partner identity (%d)\n",
+			ret);
+	}
+
+	return ret;
+}
+
+static int ucsi_get_cable_identity(struct ucsi_connector *con)
+{
+	int ret;
+
+	ret = ucsi_read_identity(con, UCSI_RECIPIENT_SOP_P,
+				 &con->cable_identity);
+	if (ret < 0)
+		return ret;
+
+	ret = typec_cable_set_identity(con->cable);
+	if (ret < 0) {
+		dev_err(con->ucsi->dev, "Failed to set cable identity (%d)\n",
+			ret);
+	}
+
+	return ret;
+}
+
 static int ucsi_check_altmodes(struct ucsi_connector *con)
 {
 	int ret, num_partner_am;
@@ -754,6 +869,7 @@ static int ucsi_register_cable(struct ucsi_connector *con)
 		break;
 	}
 
+	desc.identity = &con->cable_identity;
 	desc.active = !!(UCSI_CABLE_PROP_FLAG_ACTIVE_CABLE &
 			 con->cable_prop.flags);
 	desc.pd_revision = UCSI_CABLE_PROP_FLAG_PD_MAJOR_REV_AS_BCD(
@@ -777,6 +893,7 @@ static void ucsi_unregister_cable(struct ucsi_connector *con)
 		return;
 
 	typec_unregister_cable(con->cable);
+	memset(&con->cable_identity, 0, sizeof(con->cable_identity));
 	con->cable = NULL;
 }
 
@@ -827,6 +944,7 @@ static int ucsi_register_partner(struct ucsi_connector *con)
 		break;
 	}
 
+	desc.identity = &con->partner_identity;
 	desc.usb_pd = pwr_opmode == UCSI_CONSTAT_PWR_OPMODE_PD;
 	desc.pd_revision = UCSI_CONCAP_FLAG_PARTNER_PD_MAJOR_REV_AS_BCD(con->cap.flags);
 
@@ -855,6 +973,7 @@ static void ucsi_unregister_partner(struct ucsi_connector *con)
 	ucsi_unregister_altmodes(con, UCSI_RECIPIENT_SOP);
 	ucsi_unregister_cable(con);
 	typec_unregister_partner(con->partner);
+	memset(&con->partner_identity, 0, sizeof(con->partner_identity));
 	con->partner = NULL;
 }
 
@@ -975,6 +1094,10 @@ static int ucsi_check_cable(struct ucsi_connector *con)
 	if (ret < 0)
 		return ret;
 
+	ret = ucsi_get_cable_identity(con);
+	if (ret < 0)
+		return ret;
+
 	return 0;
 }
 
@@ -1019,6 +1142,7 @@ static void ucsi_handle_connector_change(struct work_struct *work)
 			ucsi_register_partner(con);
 			ucsi_partner_task(con, ucsi_check_connection, 1, HZ);
 			ucsi_partner_task(con, ucsi_check_connector_capability, 1, HZ);
+			ucsi_partner_task(con, ucsi_get_partner_identity, 1, HZ);
 			ucsi_partner_task(con, ucsi_check_cable, 1, HZ);
 
 			if (UCSI_CONSTAT_PWR_OPMODE(con->status.flags) ==
@@ -1418,6 +1542,7 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con)
 		ucsi_register_partner(con);
 		ucsi_pwr_opmode_change(con);
 		ucsi_port_psy_changed(con);
+		ucsi_get_partner_identity(con);
 		ucsi_check_cable(con);
 	}
 
diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
index f0aabef0b7c64..b89fae82e8ce7 100644
--- a/drivers/usb/typec/ucsi/ucsi.h
+++ b/drivers/usb/typec/ucsi/ucsi.h
@@ -106,6 +106,7 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num);
 #define UCSI_GET_CABLE_PROPERTY		0x11
 #define UCSI_GET_CONNECTOR_STATUS	0x12
 #define UCSI_GET_ERROR_STATUS		0x13
+#define UCSI_GET_PD_MESSAGE		0x15
 
 #define UCSI_CONNECTOR_NUMBER(_num_)		((u64)(_num_) << 16)
 #define UCSI_COMMAND(_cmd_)			((_cmd_) & 0xff)
@@ -159,6 +160,18 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num);
 #define UCSI_MAX_PDOS				(4)
 #define UCSI_GET_PDOS_SRC_PDOS			((u64)1 << 34)
 
+/* GET_PD_MESSAGE command bits */
+#define UCSI_GET_PD_MESSAGE_RECIPIENT(_r_)	((u64)(_r_) << 23)
+#define UCSI_GET_PD_MESSAGE_OFFSET(_r_)		((u64)(_r_) << 26)
+#define UCSI_GET_PD_MESSAGE_BYTES(_r_)		((u64)(_r_) << 34)
+#define UCSI_GET_PD_MESSAGE_TYPE(_r_)		((u64)(_r_) << 42)
+#define   UCSI_GET_PD_MESSAGE_TYPE_SNK_CAP_EXT	0
+#define   UCSI_GET_PD_MESSAGE_TYPE_SRC_CAP_EXT	1
+#define   UCSI_GET_PD_MESSAGE_TYPE_BAT_CAP	2
+#define   UCSI_GET_PD_MESSAGE_TYPE_BAT_STAT	3
+#define   UCSI_GET_PD_MESSAGE_TYPE_IDENTITY	4
+#define   UCSI_GET_PD_MESSAGE_TYPE_REVISION	5
+
 /* -------------------------------------------------------------------------- */
 
 /* Error information returned by PPM in response to GET_ERROR_STATUS command. */
@@ -338,6 +351,18 @@ struct ucsi_connector_status {
 	((get_unaligned_le32(&(_p_)[5]) & GENMASK(16, 1)) >> 1)
 } __packed;
 
+/*
+ * Data structure filled by PPM in response to GET_PD_MESSAGE command with the
+ * Response Message Type set to Discover Identity Response.
+ */
+struct ucsi_pd_message_disc_id {
+	u32 vdm_header;
+	u32 id_header;
+	u32 cert_stat;
+	u32 product;
+	u32 vdo[3];
+} __packed;
+
 /* -------------------------------------------------------------------------- */
 
 struct ucsi_debugfs_entry {
@@ -428,6 +453,10 @@ struct ucsi_connector {
 	struct usb_power_delivery_capabilities *partner_sink_caps;
 
 	struct usb_role_switch *usb_role_sw;
+
+	/* USB PD identity */
+	struct usb_pd_identity partner_identity;
+	struct usb_pd_identity cable_identity;
 };
 
 int ucsi_send_command(struct ucsi *ucsi, u64 command,
-- 
2.44.0.rc1.240.g4c46232300-goog


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v3 4/4] usb: typec: ucsi: Register SOP' alternate modes with cable plug
  2024-02-29 23:26 [PATCH v3 0/4] usb: typec: ucsi: Expand SOP/SOP' Discovery Jameson Thies
                   ` (2 preceding siblings ...)
  2024-02-29 23:26 ` [PATCH v3 3/4] usb: typec: ucsi: Register SOP/SOP' Discover Identity Responses Jameson Thies
@ 2024-02-29 23:26 ` Jameson Thies
  3 siblings, 0 replies; 9+ messages in thread
From: Jameson Thies @ 2024-02-29 23:26 UTC (permalink / raw)
  To: heikki.krogerus, linux-usb
  Cc: jthies, pmalani, bleung, abhishekpandit, andersson,
	dmitry.baryshkov, fabrice.gasnier, gregkh, hdegoede,
	neil.armstrong, rajaram.regupathy, saranya.gopal, linux-kernel,
	Benson Leung

Register SOP' alternate modes with a Type-C Connector Class cable plug.
Alternate modes are queried from the PPM using the GET_ALTERNATE_MODES
command with recipient set to SOP'.

Reviewed-by: Benson Leung <bleung@chromium.org>
Reviewed-by: Prashant Malani <pmalani@chromium.org>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: Jameson Thies <jthies@google.com>
---
SOP' GET_ALTERNATE_MODE responses from the PPM are correctly registered
to the cable plug.
nospike-rev4 /sys/class/typec # ls port0-cable/port0-plug0/
device  port0-plug0.0  port0-plug0.1  power  subsystem  uevent

Changes in v3:
- None.

Changes in v2:
- Shortened lines to within 80 characters.
- Tested on usb-testing branch merged with chromeOS 6.8-rc2 kernel.

 drivers/usb/typec/ucsi/ucsi.c | 60 +++++++++++++++++++++++++++++++++++
 drivers/usb/typec/ucsi/ucsi.h |  2 ++
 2 files changed, 62 insertions(+)

diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index 4088422b33c74..281954fe9d855 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -399,6 +399,27 @@ static int ucsi_register_altmode(struct ucsi_connector *con,
 
 		con->partner_altmode[i] = alt;
 		break;
+	case UCSI_RECIPIENT_SOP_P:
+		i = ucsi_next_altmode(con->plug_altmode);
+		if (i < 0) {
+			ret = i;
+			goto err;
+		}
+
+		ret = ucsi_altmode_next_mode(con->plug_altmode, desc->svid);
+		if (ret < 0)
+			return ret;
+
+		desc->mode = ret;
+
+		alt = typec_plug_register_altmode(con->plug, desc);
+		if (IS_ERR(alt)) {
+			ret = PTR_ERR(alt);
+			goto err;
+		}
+
+		con->plug_altmode[i] = alt;
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -566,6 +587,9 @@ static void ucsi_unregister_altmodes(struct ucsi_connector *con, u8 recipient)
 	case UCSI_RECIPIENT_SOP:
 		adev = con->partner_altmode;
 		break;
+	case UCSI_RECIPIENT_SOP_P:
+		adev = con->plug_altmode;
+		break;
 	default:
 		return;
 	}
@@ -849,6 +873,33 @@ static void ucsi_unregister_partner_pdos(struct ucsi_connector *con)
 	con->partner_pd = NULL;
 }
 
+static int ucsi_register_plug(struct ucsi_connector *con)
+{
+	struct typec_plug *plug;
+	struct typec_plug_desc desc = {.index = TYPEC_PLUG_SOP_P};
+
+	plug = typec_register_plug(con->cable, &desc);
+	if (IS_ERR(plug)) {
+		dev_err(con->ucsi->dev,
+			"con%d: failed to register plug (%ld)\n", con->num,
+			PTR_ERR(plug));
+		return PTR_ERR(plug);
+	}
+
+	con->plug = plug;
+	return 0;
+}
+
+static void ucsi_unregister_plug(struct ucsi_connector *con)
+{
+	if (!con->plug)
+		return;
+
+	ucsi_unregister_altmodes(con, UCSI_RECIPIENT_SOP_P);
+	typec_unregister_plug(con->plug);
+	con->plug = NULL;
+}
+
 static int ucsi_register_cable(struct ucsi_connector *con)
 {
 	struct typec_cable *cable;
@@ -892,6 +943,7 @@ static void ucsi_unregister_cable(struct ucsi_connector *con)
 	if (!con->cable)
 		return;
 
+	ucsi_unregister_plug(con);
 	typec_unregister_cable(con->cable);
 	memset(&con->cable_identity, 0, sizeof(con->cable_identity));
 	con->cable = NULL;
@@ -1098,6 +1150,14 @@ static int ucsi_check_cable(struct ucsi_connector *con)
 	if (ret < 0)
 		return ret;
 
+	ret = ucsi_register_plug(con);
+	if (ret < 0)
+		return ret;
+
+	ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_SOP_P);
+	if (ret < 0)
+		return ret;
+
 	return 0;
 }
 
diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
index b89fae82e8ce7..32daf5f586505 100644
--- a/drivers/usb/typec/ucsi/ucsi.h
+++ b/drivers/usb/typec/ucsi/ucsi.h
@@ -429,9 +429,11 @@ struct ucsi_connector {
 	struct typec_port *port;
 	struct typec_partner *partner;
 	struct typec_cable *cable;
+	struct typec_plug *plug;
 
 	struct typec_altmode *port_altmode[UCSI_MAX_ALTMODES];
 	struct typec_altmode *partner_altmode[UCSI_MAX_ALTMODES];
+	struct typec_altmode *plug_altmode[UCSI_MAX_ALTMODES];
 
 	struct typec_capability typec_cap;
 
-- 
2.44.0.rc1.240.g4c46232300-goog


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH v3 3/4] usb: typec: ucsi: Register SOP/SOP' Discover Identity Responses
  2024-02-29 23:26 ` [PATCH v3 3/4] usb: typec: ucsi: Register SOP/SOP' Discover Identity Responses Jameson Thies
@ 2024-03-01  1:19   ` Benson Leung
  2024-03-01 19:37   ` Prashant Malani
  1 sibling, 0 replies; 9+ messages in thread
From: Benson Leung @ 2024-03-01  1:19 UTC (permalink / raw)
  To: Jameson Thies
  Cc: heikki.krogerus, linux-usb, pmalani, abhishekpandit, andersson,
	dmitry.baryshkov, fabrice.gasnier, gregkh, hdegoede,
	neil.armstrong, rajaram.regupathy, saranya.gopal, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 10054 bytes --]

Hi Jameson,

On Thu, Feb 29, 2024 at 11:26:24PM +0000, Jameson Thies wrote:
> Register SOP and SOP' Discover Identity responses with the USB Type-C
> Connector Class as partner and cable identities, respectively. Discover
> Identity responses are requested from the PPM using the GET_PD_MESSAGE
> UCSI command.
> 
> Signed-off-by: Jameson Thies <jthies@google.com>

Reviewed-by: Benson Leung <bleung@chromium.org>

> ---
> GET_PD_MESSAGE responses from the PPM populate partner and cable
> identity in sysfs:
> nospike-rev4 /sys/class/typec # ls port0-partner/identity/
> cert_stat  id_header  product  product_type_vdo1  product_type_vdo2
> product_type_vdo3
> nospike-rev4 /sys/class/typec # ls port0-cable/identity/
> cert_stat  id_header  product  product_type_vdo1  product_type_vdo2
> product_type_vdo3
> 
> Changes in v3:
> - None.
> 
> Changes in v2:
> - Re-ordered memset call and null assignment when unregistering partners
> and cables.
> - Supports registering partner and cable identity with UCSI versions
> before v2.0.
> - Shortened lines to within 80 characters with the exception of two
> error log lines with three indentations.
> - Tested on usb-testing branch merged with chromeOS 6.8-rc2 kernel.
> 
>  drivers/usb/typec/ucsi/ucsi.c | 125 ++++++++++++++++++++++++++++++++++
>  drivers/usb/typec/ucsi/ucsi.h |  29 ++++++++
>  2 files changed, 154 insertions(+)
> 
> diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
> index 7c84687b5d1a3..4088422b33c74 100644
> --- a/drivers/usb/typec/ucsi/ucsi.c
> +++ b/drivers/usb/typec/ucsi/ucsi.c
> @@ -646,6 +646,121 @@ static int ucsi_get_src_pdos(struct ucsi_connector *con)
>  	return ret;
>  }
>  
> +static int ucsi_read_identity(struct ucsi_connector *con, u8 recipient,
> +			      struct usb_pd_identity *id)
> +{
> +	struct ucsi *ucsi = con->ucsi;
> +	struct ucsi_pd_message_disc_id resp = {};
> +	u64 command;
> +	int ret;
> +
> +	if (ucsi->version < UCSI_VERSION_2_0) {
> +		/*
> +		 * Before UCSI v2.0, MESSAGE_IN is 16 bytes which cannot fit
> +		 * the 28 byte identity response including the VDM header.
> +		 * First request the VDM header, ID Header VDO, Cert Stat VDO
> +		 * and Product VDO.
> +		 */
> +		command = UCSI_COMMAND(UCSI_GET_PD_MESSAGE) |
> +		    UCSI_CONNECTOR_NUMBER(con->num);
> +		command |= UCSI_GET_PD_MESSAGE_RECIPIENT(recipient);
> +		command |= UCSI_GET_PD_MESSAGE_OFFSET(0);
> +		command |= UCSI_GET_PD_MESSAGE_BYTES(0x10);
> +		command |= UCSI_GET_PD_MESSAGE_TYPE(
> +		    UCSI_GET_PD_MESSAGE_TYPE_IDENTITY);
> +
> +		ret = ucsi_send_command(ucsi, command, &resp, 0x10);
> +		if (ret < 0) {
> +			dev_err(ucsi->dev,
> +				"UCSI_GET_PD_MESSAGE v1.2 failed first request (%d)\n",
> +				ret);
> +			return ret;
> +		}
> +
> +		/* Then request Product Type VDO1 through Product Type VDO3. */
> +		command = UCSI_COMMAND(UCSI_GET_PD_MESSAGE) |
> +		    UCSI_CONNECTOR_NUMBER(con->num);
> +		command |= UCSI_GET_PD_MESSAGE_RECIPIENT(recipient);
> +		command |= UCSI_GET_PD_MESSAGE_OFFSET(0x10);
> +		command |= UCSI_GET_PD_MESSAGE_BYTES(0xc);
> +		command |= UCSI_GET_PD_MESSAGE_TYPE(
> +		    UCSI_GET_PD_MESSAGE_TYPE_IDENTITY);
> +
> +		ret = ucsi_send_command(ucsi, command, &resp.vdo[0], 0xc);
> +		if (ret < 0) {
> +			dev_err(ucsi->dev,
> +				"UCSI_GET_PD_MESSAGE v1.2 failed second request (%d)\n",
> +				ret);
> +			return ret;
> +		}
> +	} else {
> +		/*
> +		 * In UCSI v2.0 and after, MESSAGE_IN is large enough to request
> +		 * the large enough to request the full Discover Identity
> +		 * response at once.
> +		 */
> +		command = UCSI_COMMAND(UCSI_GET_PD_MESSAGE) |
> +		    UCSI_CONNECTOR_NUMBER(con->num);
> +		command |= UCSI_GET_PD_MESSAGE_RECIPIENT(recipient);
> +		/* VDM Header + 6 VDOs (0x1c bytes) without an offset */
> +		command |= UCSI_GET_PD_MESSAGE_OFFSET(0);
> +		command |= UCSI_GET_PD_MESSAGE_BYTES(0x1c);
> +		command |= UCSI_GET_PD_MESSAGE_TYPE(
> +		    UCSI_GET_PD_MESSAGE_TYPE_IDENTITY);
> +
> +		ret = ucsi_send_command(ucsi, command, &resp, sizeof(resp));
> +		if (ret < 0) {
> +			dev_err(ucsi->dev, "UCSI_GET_PD_MESSAGE failed (%d)\n",
> +				ret);
> +			return ret;
> +		}
> +	}
> +
> +	id->id_header = resp.id_header;
> +	id->cert_stat = resp.cert_stat;
> +	id->product = resp.product;
> +	id->vdo[0] = resp.vdo[0];
> +	id->vdo[1] = resp.vdo[1];
> +	id->vdo[2] = resp.vdo[2];
> +	return 0;
> +}
> +
> +static int ucsi_get_partner_identity(struct ucsi_connector *con)
> +{
> +	int ret;
> +
> +	ret = ucsi_read_identity(con, UCSI_RECIPIENT_SOP,
> +				 &con->partner_identity);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = typec_partner_set_identity(con->partner);
> +	if (ret < 0) {
> +		dev_err(con->ucsi->dev, "Failed to set partner identity (%d)\n",
> +			ret);
> +	}
> +
> +	return ret;
> +}
> +
> +static int ucsi_get_cable_identity(struct ucsi_connector *con)
> +{
> +	int ret;
> +
> +	ret = ucsi_read_identity(con, UCSI_RECIPIENT_SOP_P,
> +				 &con->cable_identity);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = typec_cable_set_identity(con->cable);
> +	if (ret < 0) {
> +		dev_err(con->ucsi->dev, "Failed to set cable identity (%d)\n",
> +			ret);
> +	}
> +
> +	return ret;
> +}
> +
>  static int ucsi_check_altmodes(struct ucsi_connector *con)
>  {
>  	int ret, num_partner_am;
> @@ -754,6 +869,7 @@ static int ucsi_register_cable(struct ucsi_connector *con)
>  		break;
>  	}
>  
> +	desc.identity = &con->cable_identity;
>  	desc.active = !!(UCSI_CABLE_PROP_FLAG_ACTIVE_CABLE &
>  			 con->cable_prop.flags);
>  	desc.pd_revision = UCSI_CABLE_PROP_FLAG_PD_MAJOR_REV_AS_BCD(
> @@ -777,6 +893,7 @@ static void ucsi_unregister_cable(struct ucsi_connector *con)
>  		return;
>  
>  	typec_unregister_cable(con->cable);
> +	memset(&con->cable_identity, 0, sizeof(con->cable_identity));
>  	con->cable = NULL;
>  }
>  
> @@ -827,6 +944,7 @@ static int ucsi_register_partner(struct ucsi_connector *con)
>  		break;
>  	}
>  
> +	desc.identity = &con->partner_identity;
>  	desc.usb_pd = pwr_opmode == UCSI_CONSTAT_PWR_OPMODE_PD;
>  	desc.pd_revision = UCSI_CONCAP_FLAG_PARTNER_PD_MAJOR_REV_AS_BCD(con->cap.flags);
>  
> @@ -855,6 +973,7 @@ static void ucsi_unregister_partner(struct ucsi_connector *con)
>  	ucsi_unregister_altmodes(con, UCSI_RECIPIENT_SOP);
>  	ucsi_unregister_cable(con);
>  	typec_unregister_partner(con->partner);
> +	memset(&con->partner_identity, 0, sizeof(con->partner_identity));
>  	con->partner = NULL;
>  }
>  
> @@ -975,6 +1094,10 @@ static int ucsi_check_cable(struct ucsi_connector *con)
>  	if (ret < 0)
>  		return ret;
>  
> +	ret = ucsi_get_cable_identity(con);
> +	if (ret < 0)
> +		return ret;
> +
>  	return 0;
>  }
>  
> @@ -1019,6 +1142,7 @@ static void ucsi_handle_connector_change(struct work_struct *work)
>  			ucsi_register_partner(con);
>  			ucsi_partner_task(con, ucsi_check_connection, 1, HZ);
>  			ucsi_partner_task(con, ucsi_check_connector_capability, 1, HZ);
> +			ucsi_partner_task(con, ucsi_get_partner_identity, 1, HZ);
>  			ucsi_partner_task(con, ucsi_check_cable, 1, HZ);
>  
>  			if (UCSI_CONSTAT_PWR_OPMODE(con->status.flags) ==
> @@ -1418,6 +1542,7 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con)
>  		ucsi_register_partner(con);
>  		ucsi_pwr_opmode_change(con);
>  		ucsi_port_psy_changed(con);
> +		ucsi_get_partner_identity(con);
>  		ucsi_check_cable(con);
>  	}
>  
> diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
> index f0aabef0b7c64..b89fae82e8ce7 100644
> --- a/drivers/usb/typec/ucsi/ucsi.h
> +++ b/drivers/usb/typec/ucsi/ucsi.h
> @@ -106,6 +106,7 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num);
>  #define UCSI_GET_CABLE_PROPERTY		0x11
>  #define UCSI_GET_CONNECTOR_STATUS	0x12
>  #define UCSI_GET_ERROR_STATUS		0x13
> +#define UCSI_GET_PD_MESSAGE		0x15
>  
>  #define UCSI_CONNECTOR_NUMBER(_num_)		((u64)(_num_) << 16)
>  #define UCSI_COMMAND(_cmd_)			((_cmd_) & 0xff)
> @@ -159,6 +160,18 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num);
>  #define UCSI_MAX_PDOS				(4)
>  #define UCSI_GET_PDOS_SRC_PDOS			((u64)1 << 34)
>  
> +/* GET_PD_MESSAGE command bits */
> +#define UCSI_GET_PD_MESSAGE_RECIPIENT(_r_)	((u64)(_r_) << 23)
> +#define UCSI_GET_PD_MESSAGE_OFFSET(_r_)		((u64)(_r_) << 26)
> +#define UCSI_GET_PD_MESSAGE_BYTES(_r_)		((u64)(_r_) << 34)
> +#define UCSI_GET_PD_MESSAGE_TYPE(_r_)		((u64)(_r_) << 42)
> +#define   UCSI_GET_PD_MESSAGE_TYPE_SNK_CAP_EXT	0
> +#define   UCSI_GET_PD_MESSAGE_TYPE_SRC_CAP_EXT	1
> +#define   UCSI_GET_PD_MESSAGE_TYPE_BAT_CAP	2
> +#define   UCSI_GET_PD_MESSAGE_TYPE_BAT_STAT	3
> +#define   UCSI_GET_PD_MESSAGE_TYPE_IDENTITY	4
> +#define   UCSI_GET_PD_MESSAGE_TYPE_REVISION	5
> +
>  /* -------------------------------------------------------------------------- */
>  
>  /* Error information returned by PPM in response to GET_ERROR_STATUS command. */
> @@ -338,6 +351,18 @@ struct ucsi_connector_status {
>  	((get_unaligned_le32(&(_p_)[5]) & GENMASK(16, 1)) >> 1)
>  } __packed;
>  
> +/*
> + * Data structure filled by PPM in response to GET_PD_MESSAGE command with the
> + * Response Message Type set to Discover Identity Response.
> + */
> +struct ucsi_pd_message_disc_id {
> +	u32 vdm_header;
> +	u32 id_header;
> +	u32 cert_stat;
> +	u32 product;
> +	u32 vdo[3];
> +} __packed;
> +
>  /* -------------------------------------------------------------------------- */
>  
>  struct ucsi_debugfs_entry {
> @@ -428,6 +453,10 @@ struct ucsi_connector {
>  	struct usb_power_delivery_capabilities *partner_sink_caps;
>  
>  	struct usb_role_switch *usb_role_sw;
> +
> +	/* USB PD identity */
> +	struct usb_pd_identity partner_identity;
> +	struct usb_pd_identity cable_identity;
>  };
>  
>  int ucsi_send_command(struct ucsi *ucsi, u64 command,
> -- 
> 2.44.0.rc1.240.g4c46232300-goog
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v3 3/4] usb: typec: ucsi: Register SOP/SOP' Discover Identity Responses
  2024-02-29 23:26 ` [PATCH v3 3/4] usb: typec: ucsi: Register SOP/SOP' Discover Identity Responses Jameson Thies
  2024-03-01  1:19   ` Benson Leung
@ 2024-03-01 19:37   ` Prashant Malani
  2024-03-05  2:49     ` Jameson Thies
  1 sibling, 1 reply; 9+ messages in thread
From: Prashant Malani @ 2024-03-01 19:37 UTC (permalink / raw)
  To: Jameson Thies
  Cc: heikki.krogerus, linux-usb, bleung, abhishekpandit, andersson,
	dmitry.baryshkov, fabrice.gasnier, gregkh, hdegoede,
	neil.armstrong, rajaram.regupathy, saranya.gopal, linux-kernel

On Thu, Feb 29, 2024 at 3:28 PM Jameson Thies <jthies@google.com> wrote:
> diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
> index 7c84687b5d1a3..4088422b33c74 100644
> --- a/drivers/usb/typec/ucsi/ucsi.c
> +++ b/drivers/usb/typec/ucsi/ucsi.c
> @@ -646,6 +646,121 @@ static int ucsi_get_src_pdos(struct ucsi_connector *con)
>         return ret;
>  }
>
> +static int ucsi_read_identity(struct ucsi_connector *con, u8 recipient,
> +                             struct usb_pd_identity *id)
> +{
> +       struct ucsi *ucsi = con->ucsi;
> +       struct ucsi_pd_message_disc_id resp = {};
> +       u64 command;
> +       int ret;
> +
> +       if (ucsi->version < UCSI_VERSION_2_0) {
> +               /*
> +                * Before UCSI v2.0, MESSAGE_IN is 16 bytes which cannot fit
> +                * the 28 byte identity response including the VDM header.
> +                * First request the VDM header, ID Header VDO, Cert Stat VDO
> +                * and Product VDO.
> +                */
> +               command = UCSI_COMMAND(UCSI_GET_PD_MESSAGE) |
> +                   UCSI_CONNECTOR_NUMBER(con->num);
> +               command |= UCSI_GET_PD_MESSAGE_RECIPIENT(recipient);
> +               command |= UCSI_GET_PD_MESSAGE_OFFSET(0);
> +               command |= UCSI_GET_PD_MESSAGE_BYTES(0x10);
> +               command |= UCSI_GET_PD_MESSAGE_TYPE(
> +                   UCSI_GET_PD_MESSAGE_TYPE_IDENTITY);
> +
> +               ret = ucsi_send_command(ucsi, command, &resp, 0x10);
> +               if (ret < 0) {
> +                       dev_err(ucsi->dev,
> +                               "UCSI_GET_PD_MESSAGE v1.2 failed first request (%d)\n",
> +                               ret);
> +                       return ret;
> +               }
> +
> +               /* Then request Product Type VDO1 through Product Type VDO3. */
> +               command = UCSI_COMMAND(UCSI_GET_PD_MESSAGE) |
> +                   UCSI_CONNECTOR_NUMBER(con->num);
> +               command |= UCSI_GET_PD_MESSAGE_RECIPIENT(recipient);
> +               command |= UCSI_GET_PD_MESSAGE_OFFSET(0x10);
> +               command |= UCSI_GET_PD_MESSAGE_BYTES(0xc);
> +               command |= UCSI_GET_PD_MESSAGE_TYPE(
> +                   UCSI_GET_PD_MESSAGE_TYPE_IDENTITY);
> +
> +               ret = ucsi_send_command(ucsi, command, &resp.vdo[0], 0xc);
> +               if (ret < 0) {
> +                       dev_err(ucsi->dev,
> +                               "UCSI_GET_PD_MESSAGE v1.2 failed second request (%d)\n",
> +                               ret);
> +                       return ret;
> +               }
> +       } else {
> +               /*
> +                * In UCSI v2.0 and after, MESSAGE_IN is large enough to request
> +                * the large enough to request the full Discover Identity
> +                * response at once.
> +                */
> +               command = UCSI_COMMAND(UCSI_GET_PD_MESSAGE) |
> +                   UCSI_CONNECTOR_NUMBER(con->num);
> +               command |= UCSI_GET_PD_MESSAGE_RECIPIENT(recipient);
> +               /* VDM Header + 6 VDOs (0x1c bytes) without an offset */
> +               command |= UCSI_GET_PD_MESSAGE_OFFSET(0);
> +               command |= UCSI_GET_PD_MESSAGE_BYTES(0x1c);
> +               command |= UCSI_GET_PD_MESSAGE_TYPE(
> +                   UCSI_GET_PD_MESSAGE_TYPE_IDENTITY);
> +
> +               ret = ucsi_send_command(ucsi, command, &resp, sizeof(resp));
> +               if (ret < 0) {
> +                       dev_err(ucsi->dev, "UCSI_GET_PD_MESSAGE failed (%d)\n",
> +                               ret);
> +                       return ret;
> +               }
> +       }
> +
> +       id->id_header = resp.id_header;
> +       id->cert_stat = resp.cert_stat;
> +       id->product = resp.product;
> +       id->vdo[0] = resp.vdo[0];
> +       id->vdo[1] = resp.vdo[1];
> +       id->vdo[2] = resp.vdo[2];
> +       return 0;
> +}

There is some repetition here, both with the "if" block and the
UCSI command itself. You can factor out the command setup code into a separate
function (it can take offset and size as arguments). Then, factor out
common parts in the "if" block. For example:

int ucsi_pd_message_get_identity(struct *ucsi, u8 offset, u8 size, u8 recipient,
                                                              void *data) {
        u64 command = UCSI_COMMAND(UCSI_GET_PD_MESSAGE) |
UCSI_CONNECTOR_NUMBER(ucsi->con->num) |
                      UCSI_GET_PD_MESSAGE_RECIPIENT(recipient) |
                      UCSI_GET_PD_MESSAGE_OFFSET(offset) |
UCSI_GET_PD_MESSAGE_BYTES(size) |

UCSI_GET_PD_MESSAGE_TYPE(UCSI_GET_PD_MESSAGE_TYPE_IDENTITY);

        return ucsi_send_command(ucsi, command, data, size);
}

Then in the ucsi_read_identity() :
        u8 offset = 0;
        u8 size;

        /*
         * In UCSI v2.0 and after, MESSAGE_IN is large enough to request
         * the large enough to request the full Discover Identity
         * response at once.
         */
        if (ucsi->version >= UCSI_VERSION_2_0) {
                size = 0x1c;
        else
                size = 0x10;

        ret = ucsi_pd_message_get_identity(ucsi, offset, size, &resp);
        if (ret < 0) {
                /* Handle error */
        }

        /* Request Product Type VDO1 through Product Type VDO3. */
        if (ucsi->version < UCSI_VERSION_2_0) {
                offset = 0x10;
                size = 0xC;
                ret = ucsi_pd_message_get_identity(ucsi, offset, size,
&resp.vdo[0]);
                if (ret < 0) {
                /* Handle error */
                }
        }

BR,

-Prashant

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v3 3/4] usb: typec: ucsi: Register SOP/SOP' Discover Identity Responses
  2024-03-01 19:37   ` Prashant Malani
@ 2024-03-05  2:49     ` Jameson Thies
  0 siblings, 0 replies; 9+ messages in thread
From: Jameson Thies @ 2024-03-05  2:49 UTC (permalink / raw)
  To: Prashant Malani
  Cc: heikki.krogerus, linux-usb, bleung, abhishekpandit, andersson,
	dmitry.baryshkov, fabrice.gasnier, gregkh, hdegoede,
	neil.armstrong, rajaram.regupathy, saranya.gopal, linux-kernel

> There is some repetition here, both with the "if" block and the
> UCSI command itself. You can factor out the command setup code into a separate
> function (it can take offset and size as arguments). Then, factor out
> common parts in the "if" block. For example:

Hi Prashant,
thank you for the feedback. I'll upload a v4 series to resolve this redundancy.

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2024-03-05  2:49 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-02-29 23:26 [PATCH v3 0/4] usb: typec: ucsi: Expand SOP/SOP' Discovery Jameson Thies
2024-02-29 23:26 ` [PATCH v3 1/4] usb: typec: ucsi: Clean up UCSI_CABLE_PROP macros Jameson Thies
2024-02-29 23:26 ` [PATCH v3 2/4] usb: typec: ucsi: Register cables based on GET_CABLE_PROPERTY Jameson Thies
2024-02-29 23:26 ` [PATCH v3 3/4] usb: typec: ucsi: Register SOP/SOP' Discover Identity Responses Jameson Thies
2024-03-01  1:19   ` Benson Leung
2024-03-01 19:37   ` Prashant Malani
2024-03-05  2:49     ` Jameson Thies
2024-02-29 23:26 ` [PATCH v3 4/4] usb: typec: ucsi: Register SOP' alternate modes with cable plug Jameson Thies
  -- strict thread matches above, loose matches on Subject: below --
2024-02-29  1:53 [PATCH v3 3/4] usb: typec: ucsi: Register SOP/SOP' Discover Identity Responses Jameson Thies

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.