usb: typec: ucsi: Expand SOP/SOP' Discovery
Subsystems (3)
v3 cover usb: typec: ucsi: Expand SOP/SOP' Discovery
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
Signature · 1 line
2.44.0.rc1.240.g4c46232300-goog
PATCH v3 1/4 usb: typec: ucsi: Clean up UCSI_CABLE_PROP macros
Clean up UCSI_CABLE_PROP macros by fixing a bitmask shifting error for plug type and updating the modal support macro for consistent naming.
---
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(-)
drivers/usb/typec/ucsi/ucsi.h +2 -2
@@ -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;
Signature · 1 line
2.44.0.rc1.240.g4c46232300-goog
PATCH v3 2/4 usb: typec: ucsi: Register cables based on GET_CABLE_PROPERTY
In reply to Jameson ThiesRegister 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.
---
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(+)
drivers/usb/typec/ucsi/ucsi.c +73 -0
@@ -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 */
drivers/usb/typec/ucsi/ucsi.h +5 -0
@@ -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;
Signature · 1 line
2.44.0.rc1.240.g4c46232300-goog
PATCH v3 3/4 usb: typec: ucsi: Register SOP/SOP' Discover Identity Responses
In reply to Jameson ThiesRegister 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.
---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(+)
drivers/usb/typec/ucsi/ucsi.c +125 -0
@@ -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); }
drivers/usb/typec/ucsi/ucsi.h +29 -0
@@ -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,
Signature · 1 line
2.44.0.rc1.240.g4c46232300-goog
Re: [PATCH v3 3/4] usb: typec: ucsi: Register SOP/SOP' Discover Identity Responses
Hi Jameson,
Quoted 6 lines · Jameson Thies
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_MESSAGEUCSI command.Signed-off-by Jameson Thies <[email protected]>
Quoted 275 lines
---
GET_PD_MESSAGEresponses from the PPM populate partner and cable identity in sysfs:
nospike-rev4/sys/class/typec# ls port0-partner/identity/cert_statid_headerproductproduct_type_vdo1product_type_vdo2product_type_vdo3
nospike-rev4/sys/class/typec# ls port0-cable/identity/cert_statid_headerproductproduct_type_vdo1product_type_vdo2product_type_vdo3Changes 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(+)drivers/usb/typec/ucsi/ucsi.c +125
-0@@ -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); }drivers/usb/typec/ucsi/ucsi.h +29
-0@@ -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,Signature · 1 line
2.44.0.rc1.240.g4c46232300-goog
Re: [PATCH v3 3/4] usb: typec: ucsi: Register SOP/SOP' Discover Identity Responses
In reply to Jameson ThiesQuoted 76 lines · Jameson Thies
drivers/usb/typec/ucsi/ucsi.c +68
-0@@ -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; + }
drivers/usb/typec/ucsi/ucsi.c static int ucsi_get_src_pdos(struct ucsi_connector *con) · line 726
Quoted 10 lines
drivers/usb/typec/ucsi/ucsi.c +10
-0+ } + + 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
Re: [PATCH v3 3/4] usb: typec: ucsi: Register SOP/SOP' Discover Identity Responses
Quoted 4 lines
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.
PATCH v3 4/4 usb: typec: ucsi: Register SOP' alternate modes with cable plug
In reply to Jameson ThiesRegister 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'.
---
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(+)
drivers/usb/typec/ucsi/ucsi.c +60 -0
@@ -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 ; }
drivers/usb/typec/ucsi/ucsi.h +2 -0
@@ -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;
Signature · 1 line
2.44.0.rc1.240.g4c46232300-goog