From a906f45d148074274ab4f5df4ebc33412bfa9973 Mon Sep 17 00:00:00 2001 From: Prashant Malani Date: Mon, 16 Nov 2020 12:11:44 -0800 Subject: [PATCH 01/10] platform/chrome: cros_ec_typec: Make disc_done flag partner-only Change the disc_done flag, which indicates whether PD discovery is complete, to sop_disc_done instead, since we will process SOP and SOP' discovery data separately. Signed-off-by: Prashant Malani Reviewed-by: Greg Kroah-Hartman Reviewed-by: Heikki Krogerus Signed-off-by: Benson Leung Link: https://lore.kernel.org/r/20201116201150.2919178-5-pmalani@chromium.org --- drivers/platform/chrome/cros_ec_typec.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c index c43868615790..2d8bda491a0c 100644 --- a/drivers/platform/chrome/cros_ec_typec.c +++ b/drivers/platform/chrome/cros_ec_typec.c @@ -57,8 +57,8 @@ struct cros_typec_port { /* Port alt modes. */ struct typec_altmode p_altmode[CROS_EC_ALTMODE_MAX]; - /* Flag indicating that PD discovery data parsing is completed. */ - bool disc_done; + /* Flag indicating that PD partner discovery data parsing is completed. */ + bool sop_disc_done; struct ec_response_typec_discovery *sop_disc; struct list_head partner_mode_list; }; @@ -210,7 +210,7 @@ static void cros_typec_remove_partner(struct cros_typec_data *typec, typec_unregister_partner(port->partner); port->partner = NULL; memset(&port->p_identity, 0, sizeof(port->p_identity)); - port->disc_done = false; + port->sop_disc_done = false; } static void cros_unregister_ports(struct cros_typec_data *typec) @@ -725,18 +725,13 @@ static void cros_typec_handle_status(struct cros_typec_data *typec, int port_num return; } - if (typec->ports[port_num]->disc_done) - return; - /* Handle any events appropriately. */ - if (resp.events & PD_STATUS_EVENT_SOP_DISC_DONE) { + if (resp.events & PD_STATUS_EVENT_SOP_DISC_DONE && !typec->ports[port_num]->sop_disc_done) { ret = cros_typec_handle_sop_disc(typec, port_num); - if (ret < 0) { + if (ret < 0) dev_err(typec->dev, "Couldn't parse SOP Disc data, port: %d\n", port_num); - return; - } - - typec->ports[port_num]->disc_done = true; + else + typec->ports[port_num]->sop_disc_done = true; } } From 8fab2755191f86d4f4195ceb5ade4d4c17052265 Mon Sep 17 00:00:00 2001 From: Prashant Malani Date: Mon, 16 Nov 2020 12:11:46 -0800 Subject: [PATCH 02/10] platform/chrome: cros_ec_typec: Factor out PD identity parsing Factor out the PD identity parsing code into a separate function. This way it can be re-used for Cable PD identity parsing in future patches. No functional changes are introduced by this patch. Signed-off-by: Prashant Malani Reviewed-by: Greg Kroah-Hartman Reviewed-by: Heikki Krogerus Signed-off-by: Benson Leung Link: https://lore.kernel.org/r/20201116201150.2919178-6-pmalani@chromium.org --- drivers/platform/chrome/cros_ec_typec.c | 35 ++++++++++++++++--------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c index 2d8bda491a0c..7a70138e63d1 100644 --- a/drivers/platform/chrome/cros_ec_typec.c +++ b/drivers/platform/chrome/cros_ec_typec.c @@ -655,6 +655,28 @@ err_cleanup: return ret; } +/* + * Parse the PD identity data from the EC PD discovery responses and copy that to the supplied + * PD identity struct. + */ +static void cros_typec_parse_pd_identity(struct usb_pd_identity *id, + struct ec_response_typec_discovery *disc) +{ + int i; + + /* First, update the PD identity VDOs for the partner. */ + if (disc->identity_count > 0) + id->id_header = disc->discovery_vdo[0]; + if (disc->identity_count > 1) + id->cert_stat = disc->discovery_vdo[1]; + if (disc->identity_count > 2) + id->product = disc->discovery_vdo[2]; + + /* Copy the remaining identity VDOs till a maximum of 6. */ + for (i = 3; i < disc->identity_count && i < VDO_MAX_OBJECTS; i++) + id->vdo[i - 3] = disc->discovery_vdo[i]; +} + static int cros_typec_handle_sop_disc(struct cros_typec_data *typec, int port_num) { struct cros_typec_port *port = typec->ports[port_num]; @@ -664,7 +686,6 @@ static int cros_typec_handle_sop_disc(struct cros_typec_data *typec, int port_nu .partner_type = TYPEC_PARTNER_SOP, }; int ret = 0; - int i; if (!port->partner) { dev_err(typec->dev, @@ -682,17 +703,7 @@ static int cros_typec_handle_sop_disc(struct cros_typec_data *typec, int port_nu goto disc_exit; } - /* First, update the PD identity VDOs for the partner. */ - if (sop_disc->identity_count > 0) - port->p_identity.id_header = sop_disc->discovery_vdo[0]; - if (sop_disc->identity_count > 1) - port->p_identity.cert_stat = sop_disc->discovery_vdo[1]; - if (sop_disc->identity_count > 2) - port->p_identity.product = sop_disc->discovery_vdo[2]; - - /* Copy the remaining identity VDOs till a maximum of 6. */ - for (i = 3; i < sop_disc->identity_count && i < VDO_MAX_OBJECTS; i++) - port->p_identity.vdo[i - 3] = sop_disc->discovery_vdo[i]; + cros_typec_parse_pd_identity(&port->p_identity, sop_disc); ret = typec_partner_set_identity(port->partner); if (ret < 0) { From c097f229b71e06e6ef21e4e175b322b3fab06268 Mon Sep 17 00:00:00 2001 From: Prashant Malani Date: Mon, 16 Nov 2020 12:11:48 -0800 Subject: [PATCH 03/10] platform/chrome: cros_ec_typec: Rename discovery struct Rename the sop_disc data struct which is used to store PD discovery data to the more generic name of disc_data. It can then be re-used to store and process cable discovery data. Signed-off-by: Prashant Malani Reviewed-by: Greg Kroah-Hartman Reviewed-by: Heikki Krogerus Signed-off-by: Benson Leung Link: https://lore.kernel.org/r/20201116201150.2919178-7-pmalani@chromium.org --- drivers/platform/chrome/cros_ec_typec.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c index 7a70138e63d1..bf98e2ecda3f 100644 --- a/drivers/platform/chrome/cros_ec_typec.c +++ b/drivers/platform/chrome/cros_ec_typec.c @@ -59,7 +59,7 @@ struct cros_typec_port { /* Flag indicating that PD partner discovery data parsing is completed. */ bool sop_disc_done; - struct ec_response_typec_discovery *sop_disc; + struct ec_response_typec_discovery *disc_data; struct list_head partner_mode_list; }; @@ -323,8 +323,8 @@ static int cros_typec_init_ports(struct cros_typec_data *typec) cros_typec_register_port_altmodes(typec, port_num); - cros_port->sop_disc = devm_kzalloc(dev, EC_PROTO2_MAX_RESPONSE_SIZE, GFP_KERNEL); - if (!cros_port->sop_disc) { + cros_port->disc_data = devm_kzalloc(dev, EC_PROTO2_MAX_RESPONSE_SIZE, GFP_KERNEL); + if (!cros_port->disc_data) { ret = -ENOMEM; goto unregister_ports; } @@ -615,7 +615,7 @@ static int cros_typec_get_mux_info(struct cros_typec_data *typec, int port_num, static int cros_typec_register_altmodes(struct cros_typec_data *typec, int port_num) { struct cros_typec_port *port = typec->ports[port_num]; - struct ec_response_typec_discovery *sop_disc = port->sop_disc; + struct ec_response_typec_discovery *sop_disc = port->disc_data; struct cros_typec_altmode_node *node; struct typec_altmode_desc desc; struct typec_altmode *amode; @@ -680,7 +680,7 @@ static void cros_typec_parse_pd_identity(struct usb_pd_identity *id, static int cros_typec_handle_sop_disc(struct cros_typec_data *typec, int port_num) { struct cros_typec_port *port = typec->ports[port_num]; - struct ec_response_typec_discovery *sop_disc = port->sop_disc; + struct ec_response_typec_discovery *sop_disc = port->disc_data; struct ec_params_typec_discovery req = { .port = port_num, .partner_type = TYPEC_PARTNER_SOP, From 8b46a212ad11f25e5f77e3a2c6df1d0f758583b7 Mon Sep 17 00:00:00 2001 From: Prashant Malani Date: Mon, 16 Nov 2020 12:11:50 -0800 Subject: [PATCH 04/10] platform/chrome: cros_ec_typec: Register cable When the Chrome Embedded Controller notifies the driver that SOP' discovery is complete, retrieve the PD discovery data and register a cable object with the Type C connector class framework. Cc: Heikki Krogerus Signed-off-by: Prashant Malani Reviewed-by: Greg Kroah-Hartman Reviewed-by: Heikki Krogerus Signed-off-by: Benson Leung Link: https://lore.kernel.org/r/20201116201150.2919178-8-pmalani@chromium.org --- drivers/platform/chrome/cros_ec_typec.c | 67 +++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c index bf98e2ecda3f..8ecf2ede9c19 100644 --- a/drivers/platform/chrome/cros_ec_typec.c +++ b/drivers/platform/chrome/cros_ec_typec.c @@ -44,8 +44,11 @@ struct cros_typec_port { /* Initial capabilities for the port. */ struct typec_capability caps; struct typec_partner *partner; + struct typec_cable *cable; /* Port partner PD identity info. */ struct usb_pd_identity p_identity; + /* Port cable PD identity info. */ + struct usb_pd_identity c_identity; struct typec_switch *ori_sw; struct typec_mux *mux; struct usb_role_switch *role_sw; @@ -59,6 +62,7 @@ struct cros_typec_port { /* Flag indicating that PD partner discovery data parsing is completed. */ bool sop_disc_done; + bool sop_prime_disc_done; struct ec_response_typec_discovery *disc_data; struct list_head partner_mode_list; }; @@ -213,6 +217,17 @@ static void cros_typec_remove_partner(struct cros_typec_data *typec, port->sop_disc_done = false; } +static void cros_typec_remove_cable(struct cros_typec_data *typec, + int port_num) +{ + struct cros_typec_port *port = typec->ports[port_num]; + + typec_unregister_cable(port->cable); + port->cable = NULL; + memset(&port->c_identity, 0, sizeof(port->c_identity)); + port->sop_prime_disc_done = false; +} + static void cros_unregister_ports(struct cros_typec_data *typec) { int i; @@ -224,6 +239,9 @@ static void cros_unregister_ports(struct cros_typec_data *typec) if (typec->ports[i]->partner) cros_typec_remove_partner(typec, i); + if (typec->ports[i]->cable) + cros_typec_remove_cable(typec, i); + usb_role_switch_put(typec->ports[i]->role_sw); typec_switch_put(typec->ports[i]->ori_sw); typec_mux_put(typec->ports[i]->mux); @@ -598,6 +616,9 @@ static void cros_typec_set_port_params_v1(struct cros_typec_data *typec, if (!typec->ports[port_num]->partner) return; cros_typec_remove_partner(typec, port_num); + + if (typec->ports[port_num]->cable) + cros_typec_remove_cable(typec, port_num); } } @@ -677,6 +698,43 @@ static void cros_typec_parse_pd_identity(struct usb_pd_identity *id, id->vdo[i - 3] = disc->discovery_vdo[i]; } +static int cros_typec_handle_sop_prime_disc(struct cros_typec_data *typec, int port_num) +{ + struct cros_typec_port *port = typec->ports[port_num]; + struct ec_response_typec_discovery *disc = port->disc_data; + struct typec_cable_desc desc = {}; + struct ec_params_typec_discovery req = { + .port = port_num, + .partner_type = TYPEC_PARTNER_SOP_PRIME, + }; + int ret = 0; + + memset(disc, 0, EC_PROTO2_MAX_RESPONSE_SIZE); + ret = cros_typec_ec_command(typec, 0, EC_CMD_TYPEC_DISCOVERY, &req, sizeof(req), + disc, EC_PROTO2_MAX_RESPONSE_SIZE); + if (ret < 0) { + dev_err(typec->dev, "Failed to get SOP' discovery data for port: %d\n", port_num); + goto sop_prime_disc_exit; + } + + /* Parse the PD identity data, even if only 0s were returned. */ + cros_typec_parse_pd_identity(&port->c_identity, disc); + + if (disc->identity_count != 0) + desc.active = PD_IDH_PTYPE(port->c_identity.id_header) == IDH_PTYPE_ACABLE; + + desc.identity = &port->c_identity; + + port->cable = typec_register_cable(port->port, &desc); + if (IS_ERR(port->cable)) { + ret = PTR_ERR(port->cable); + port->cable = NULL; + } + +sop_prime_disc_exit: + return ret; +} + static int cros_typec_handle_sop_disc(struct cros_typec_data *typec, int port_num) { struct cros_typec_port *port = typec->ports[port_num]; @@ -744,6 +802,15 @@ static void cros_typec_handle_status(struct cros_typec_data *typec, int port_num else typec->ports[port_num]->sop_disc_done = true; } + + if (resp.events & PD_STATUS_EVENT_SOP_PRIME_DISC_DONE && + !typec->ports[port_num]->sop_prime_disc_done) { + ret = cros_typec_handle_sop_prime_disc(typec, port_num); + if (ret < 0) + dev_err(typec->dev, "Couldn't parse SOP' Disc data, port: %d\n", port_num); + else + typec->ports[port_num]->sop_prime_disc_done = true; + } } static int cros_typec_port_update(struct cros_typec_data *typec, int port_num) From 72d6e32bd85bd1e5cb5aa467f4eb5d0a69559953 Mon Sep 17 00:00:00 2001 From: Prashant Malani Date: Mon, 16 Nov 2020 12:11:52 -0800 Subject: [PATCH 05/10] platform/chrome: cros_ec_typec: Store cable plug type Use the PD VDO Type C cable plug type macro to retrieve and store the cable plug type in the cable descriptor. Cc: Heikki Krogerus Cc: Greg Kroah-Hartman Signed-off-by: Prashant Malani Reviewed-by: Greg Kroah-Hartman Reviewed-by: Heikki Krogerus Signed-off-by: Benson Leung Link: https://lore.kernel.org/r/20201116201150.2919178-9-pmalani@chromium.org --- drivers/platform/chrome/cros_ec_typec.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c index 8ecf2ede9c19..c0f34aa9e6dd 100644 --- a/drivers/platform/chrome/cros_ec_typec.c +++ b/drivers/platform/chrome/cros_ec_typec.c @@ -707,6 +707,7 @@ static int cros_typec_handle_sop_prime_disc(struct cros_typec_data *typec, int p .port = port_num, .partner_type = TYPEC_PARTNER_SOP_PRIME, }; + u32 cable_plug_type; int ret = 0; memset(disc, 0, EC_PROTO2_MAX_RESPONSE_SIZE); @@ -720,8 +721,26 @@ static int cros_typec_handle_sop_prime_disc(struct cros_typec_data *typec, int p /* Parse the PD identity data, even if only 0s were returned. */ cros_typec_parse_pd_identity(&port->c_identity, disc); - if (disc->identity_count != 0) + if (disc->identity_count != 0) { + cable_plug_type = VDO_TYPEC_CABLE_TYPE(port->c_identity.vdo[0]); + switch (cable_plug_type) { + case CABLE_ATYPE: + desc.type = USB_PLUG_TYPE_A; + break; + case CABLE_BTYPE: + desc.type = USB_PLUG_TYPE_B; + break; + case CABLE_CTYPE: + desc.type = USB_PLUG_TYPE_C; + break; + case CABLE_CAPTIVE: + desc.type = USB_PLUG_CAPTIVE; + break; + default: + desc.type = USB_PLUG_NONE; + } desc.active = PD_IDH_PTYPE(port->c_identity.id_header) == IDH_PTYPE_ACABLE; + } desc.identity = &port->c_identity; From 5992297639115785e167b9977101287e45106515 Mon Sep 17 00:00:00 2001 From: Prashant Malani Date: Mon, 16 Nov 2020 12:11:54 -0800 Subject: [PATCH 06/10] platform/chrome: cros_ec_typec: Set partner num_altmodes Set the number of altmodes available for a registered partner using the Type C connector class framework routine. Cc: Heikki Krogerus Signed-off-by: Prashant Malani Reviewed-by: Heikki Krogerus Signed-off-by: Benson Leung Link: https://lore.kernel.org/r/20201116201150.2919178-10-pmalani@chromium.org --- drivers/platform/chrome/cros_ec_typec.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c index c0f34aa9e6dd..9466a092904e 100644 --- a/drivers/platform/chrome/cros_ec_typec.c +++ b/drivers/platform/chrome/cros_ec_typec.c @@ -640,6 +640,7 @@ static int cros_typec_register_altmodes(struct cros_typec_data *typec, int port_ struct cros_typec_altmode_node *node; struct typec_altmode_desc desc; struct typec_altmode *amode; + int num_altmodes = 0; int ret = 0; int i, j; @@ -666,9 +667,16 @@ static int cros_typec_register_altmodes(struct cros_typec_data *typec, int port_ node->amode = amode; list_add_tail(&node->list, &port->partner_mode_list); + num_altmodes++; } } + ret = typec_partner_set_num_altmodes(port->partner, num_altmodes); + if (ret < 0) { + dev_err(typec->dev, "Unable to set partner num_altmodes for port: %d\n", port_num); + goto err_cleanup; + } + return 0; err_cleanup: From f4edab68e10119a71a9e62a7fe537685d0f41478 Mon Sep 17 00:00:00 2001 From: Prashant Malani Date: Mon, 16 Nov 2020 12:11:56 -0800 Subject: [PATCH 07/10] platform/chrome: cros_ec_typec: Register SOP' cable plug In order to register cable alternate modes, we need to first register a plug object. Use the Type C connector class framework to register a SOP' plug for this purpose. Since a cable and plug go hand in hand, we can handle the registration and removal together. Signed-off-by: Prashant Malani Reviewed-by: Heikki Krogerus Signed-off-by: Benson Leung Link: https://lore.kernel.org/r/20201116201150.2919178-11-pmalani@chromium.org --- drivers/platform/chrome/cros_ec_typec.c | 35 ++++++++++++++++++------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c index 9466a092904e..afba902b6491 100644 --- a/drivers/platform/chrome/cros_ec_typec.c +++ b/drivers/platform/chrome/cros_ec_typec.c @@ -45,6 +45,8 @@ struct cros_typec_port { struct typec_capability caps; struct typec_partner *partner; struct typec_cable *cable; + /* SOP' plug. */ + struct typec_plug *plug; /* Port partner PD identity info. */ struct usb_pd_identity p_identity; /* Port cable PD identity info. */ @@ -222,6 +224,8 @@ static void cros_typec_remove_cable(struct cros_typec_data *typec, { struct cros_typec_port *port = typec->ports[port_num]; + typec_unregister_plug(port->plug); + port->plug = NULL; typec_unregister_cable(port->cable); port->cable = NULL; memset(&port->c_identity, 0, sizeof(port->c_identity)); @@ -710,7 +714,8 @@ static int cros_typec_handle_sop_prime_disc(struct cros_typec_data *typec, int p { struct cros_typec_port *port = typec->ports[port_num]; struct ec_response_typec_discovery *disc = port->disc_data; - struct typec_cable_desc desc = {}; + struct typec_cable_desc c_desc = {}; + struct typec_plug_desc p_desc; struct ec_params_typec_discovery req = { .port = port_num, .partner_type = TYPEC_PARTNER_SOP_PRIME, @@ -733,32 +738,44 @@ static int cros_typec_handle_sop_prime_disc(struct cros_typec_data *typec, int p cable_plug_type = VDO_TYPEC_CABLE_TYPE(port->c_identity.vdo[0]); switch (cable_plug_type) { case CABLE_ATYPE: - desc.type = USB_PLUG_TYPE_A; + c_desc.type = USB_PLUG_TYPE_A; break; case CABLE_BTYPE: - desc.type = USB_PLUG_TYPE_B; + c_desc.type = USB_PLUG_TYPE_B; break; case CABLE_CTYPE: - desc.type = USB_PLUG_TYPE_C; + c_desc.type = USB_PLUG_TYPE_C; break; case CABLE_CAPTIVE: - desc.type = USB_PLUG_CAPTIVE; + c_desc.type = USB_PLUG_CAPTIVE; break; default: - desc.type = USB_PLUG_NONE; + c_desc.type = USB_PLUG_NONE; } - desc.active = PD_IDH_PTYPE(port->c_identity.id_header) == IDH_PTYPE_ACABLE; + c_desc.active = PD_IDH_PTYPE(port->c_identity.id_header) == IDH_PTYPE_ACABLE; } - desc.identity = &port->c_identity; + c_desc.identity = &port->c_identity; - port->cable = typec_register_cable(port->port, &desc); + port->cable = typec_register_cable(port->port, &c_desc); if (IS_ERR(port->cable)) { ret = PTR_ERR(port->cable); port->cable = NULL; + goto sop_prime_disc_exit; } + p_desc.index = TYPEC_PLUG_SOP_P; + port->plug = typec_register_plug(port->cable, &p_desc); + if (IS_ERR(port->plug)) { + ret = PTR_ERR(port->plug); + port->plug = NULL; + goto sop_prime_disc_exit; + } + + return 0; + sop_prime_disc_exit: + cros_typec_remove_cable(typec, port_num); return ret; } From 1563090965421f7e96cc006c150180f1526a24f7 Mon Sep 17 00:00:00 2001 From: Prashant Malani Date: Mon, 16 Nov 2020 12:11:58 -0800 Subject: [PATCH 08/10] platform/chrome: cros_ec_typec: Register plug altmodes Modify the altmode registration (and unregistration) code so that it can be used by both partners and plugs. Then, add code to register plug altmodes using the newly parameterized function. Also set the number of alternate modes for the plug using the associated Type C connector class function typec_plug_set_num_altmodes(). Signed-off-by: Prashant Malani Reviewed-by: Heikki Krogerus Signed-off-by: Benson Leung Link: https://lore.kernel.org/r/20201116201150.2919178-12-pmalani@chromium.org --- drivers/platform/chrome/cros_ec_typec.c | 50 ++++++++++++++++++++----- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c index afba902b6491..fd446f9029a0 100644 --- a/drivers/platform/chrome/cros_ec_typec.c +++ b/drivers/platform/chrome/cros_ec_typec.c @@ -67,6 +67,7 @@ struct cros_typec_port { bool sop_prime_disc_done; struct ec_response_typec_discovery *disc_data; struct list_head partner_mode_list; + struct list_head plug_mode_list; }; /* Platform-specific data for the Chrome OS EC Type C controller. */ @@ -186,12 +187,15 @@ static int cros_typec_add_partner(struct cros_typec_data *typec, int port_num, return ret; } -static void cros_typec_unregister_altmodes(struct cros_typec_data *typec, int port_num) +static void cros_typec_unregister_altmodes(struct cros_typec_data *typec, int port_num, + bool is_partner) { struct cros_typec_port *port = typec->ports[port_num]; struct cros_typec_altmode_node *node, *tmp; + struct list_head *head; - list_for_each_entry_safe(node, tmp, &port->partner_mode_list, list) { + head = is_partner ? &port->partner_mode_list : &port->plug_mode_list; + list_for_each_entry_safe(node, tmp, head, list) { list_del(&node->list); typec_unregister_altmode(node->amode); devm_kfree(typec->dev, node); @@ -203,7 +207,7 @@ static void cros_typec_remove_partner(struct cros_typec_data *typec, { struct cros_typec_port *port = typec->ports[port_num]; - cros_typec_unregister_altmodes(typec, port_num); + cros_typec_unregister_altmodes(typec, port_num, true); port->state.alt = NULL; port->state.mode = TYPEC_STATE_USB; @@ -224,6 +228,8 @@ static void cros_typec_remove_cable(struct cros_typec_data *typec, { struct cros_typec_port *port = typec->ports[port_num]; + cros_typec_unregister_altmodes(typec, port_num, false); + typec_unregister_plug(port->plug); port->plug = NULL; typec_unregister_cable(port->cable); @@ -352,6 +358,7 @@ static int cros_typec_init_ports(struct cros_typec_data *typec) } INIT_LIST_HEAD(&cros_port->partner_mode_list); + INIT_LIST_HEAD(&cros_port->plug_mode_list); } return 0; @@ -637,7 +644,11 @@ static int cros_typec_get_mux_info(struct cros_typec_data *typec, int port_num, sizeof(req), resp, sizeof(*resp)); } -static int cros_typec_register_altmodes(struct cros_typec_data *typec, int port_num) +/* + * Helper function to register partner/plug altmodes. + */ +static int cros_typec_register_altmodes(struct cros_typec_data *typec, int port_num, + bool is_partner) { struct cros_typec_port *port = typec->ports[port_num]; struct ec_response_typec_discovery *sop_disc = port->disc_data; @@ -655,7 +666,11 @@ static int cros_typec_register_altmodes(struct cros_typec_data *typec, int port_ desc.mode = j; desc.vdo = sop_disc->svids[i].mode_vdo[j]; - amode = typec_partner_register_altmode(port->partner, &desc); + if (is_partner) + amode = typec_partner_register_altmode(port->partner, &desc); + else + amode = typec_plug_register_altmode(port->plug, &desc); + if (IS_ERR(amode)) { ret = PTR_ERR(amode); goto err_cleanup; @@ -670,21 +685,30 @@ static int cros_typec_register_altmodes(struct cros_typec_data *typec, int port_ } node->amode = amode; - list_add_tail(&node->list, &port->partner_mode_list); + + if (is_partner) + list_add_tail(&node->list, &port->partner_mode_list); + else + list_add_tail(&node->list, &port->plug_mode_list); num_altmodes++; } } - ret = typec_partner_set_num_altmodes(port->partner, num_altmodes); + if (is_partner) + ret = typec_partner_set_num_altmodes(port->partner, num_altmodes); + else + ret = typec_plug_set_num_altmodes(port->plug, num_altmodes); + if (ret < 0) { - dev_err(typec->dev, "Unable to set partner num_altmodes for port: %d\n", port_num); + dev_err(typec->dev, "Unable to set %s num_altmodes for port: %d\n", + is_partner ? "partner" : "plug", port_num); goto err_cleanup; } return 0; err_cleanup: - cros_typec_unregister_altmodes(typec, port_num); + cros_typec_unregister_altmodes(typec, port_num, is_partner); return ret; } @@ -772,6 +796,12 @@ static int cros_typec_handle_sop_prime_disc(struct cros_typec_data *typec, int p goto sop_prime_disc_exit; } + ret = cros_typec_register_altmodes(typec, port_num, false); + if (ret < 0) { + dev_err(typec->dev, "Failed to register plug altmodes, port: %d\n", port_num); + goto sop_prime_disc_exit; + } + return 0; sop_prime_disc_exit: @@ -813,7 +843,7 @@ static int cros_typec_handle_sop_disc(struct cros_typec_data *typec, int port_nu goto disc_exit; } - ret = cros_typec_register_altmodes(typec, port_num); + ret = cros_typec_register_altmodes(typec, port_num, true); if (ret < 0) { dev_err(typec->dev, "Failed to register partner altmodes, port: %d\n", port_num); goto disc_exit; From ba8ce515454e1fc5e73ff8989c18c596a3449fef Mon Sep 17 00:00:00 2001 From: Utkarsh Patel Date: Wed, 9 Dec 2020 22:09:02 -0800 Subject: [PATCH 09/10] platform/chrome: cros_ec_typec: Parameterize cros_typec_cmds_supported() cros_typec_cmds_supported() is currently being used to check only one feature flag. Add a new feature parameter to it so that it can be used to check multiple feature flags supported in cros_ec. Rename cros_typec_cmds_supported() to cros_typec_feature_supported(). Signed-off-by: Utkarsh Patel Reviewed-by: Prashant Malani Signed-off-by: Benson Leung Link: https://lore.kernel.org/r/20201210060903.2205-2-utkarsh.h.patel@intel.com --- drivers/platform/chrome/cros_ec_typec.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c index fd446f9029a0..6068433dd2e2 100644 --- a/drivers/platform/chrome/cros_ec_typec.c +++ b/drivers/platform/chrome/cros_ec_typec.c @@ -974,8 +974,8 @@ static int cros_typec_get_cmd_version(struct cros_typec_data *typec) return 0; } -/* Check the EC feature flags to see if TYPEC_* commands are supported. */ -static int cros_typec_cmds_supported(struct cros_typec_data *typec) +/* Check the EC feature flags to see if TYPEC_* features are supported. */ +static int cros_typec_feature_supported(struct cros_typec_data *typec, enum ec_feature_code feature) { struct ec_response_get_features resp = {}; int ret; @@ -984,11 +984,12 @@ static int cros_typec_cmds_supported(struct cros_typec_data *typec) &resp, sizeof(resp)); if (ret < 0) { dev_warn(typec->dev, - "Failed to get features, assuming typec commands unsupported.\n"); + "Failed to get features, assuming typec feature=%d unsupported.\n", + feature); return 0; } - return resp.flags[EC_FEATURE_TYPEC_CMD / 32] & EC_FEATURE_MASK_1(EC_FEATURE_TYPEC_CMD); + return resp.flags[feature / 32] & EC_FEATURE_MASK_1(feature); } static void cros_typec_port_work(struct work_struct *work) @@ -1050,7 +1051,8 @@ static int cros_typec_probe(struct platform_device *pdev) return ret; } - typec->typec_cmd_supported = !!cros_typec_cmds_supported(typec); + typec->typec_cmd_supported = !!cros_typec_feature_supported(typec, + EC_FEATURE_TYPEC_CMD); ret = cros_typec_ec_command(typec, 0, EC_CMD_USB_PD_PORTS, NULL, 0, &resp, sizeof(resp)); From 8553a979fcd03448a4096c7d431b7ee1a52bfca3 Mon Sep 17 00:00:00 2001 From: Utkarsh Patel Date: Wed, 9 Dec 2020 22:09:03 -0800 Subject: [PATCH 10/10] platform/chrome: cros_ec_typec: Send mux configuration acknowledgment to EC In some corner cases downgrade of the superspeed typec device(e.g. Dell typec Dock, apple dongle) was seen because before the SOC mux configuration finishes, EC starts configuring the next mux state. With this change, once the SOC mux is configured, kernel will send an acknowledgment to EC via Host command EC_CMD_USB_PD_MUX_ACK [1]. After sending the host event EC will wait for the acknowledgment from kernel before starting the PD negotiation for the next mux state. This helps to have a framework to build better error handling along with the synchronization of timing sensitive mux states. This change also brings in corresponding EC header updates from the EC code base [1]. [1]: https://chromium.googlesource.com/chromiumos/platform/ec/+/refs/heads/master/include/ec_commands.h Signed-off-by: Utkarsh Patel Reviewed-by: Prashant Malani Signed-off-by: Benson Leung Link: https://lore.kernel.org/r/20201210060903.2205-3-utkarsh.h.patel@intel.com --- drivers/platform/chrome/cros_ec_typec.c | 16 ++++++++++++++++ include/linux/platform_data/cros_ec_commands.h | 17 +++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c index 6068433dd2e2..e724a5eaef1c 100644 --- a/drivers/platform/chrome/cros_ec_typec.c +++ b/drivers/platform/chrome/cros_ec_typec.c @@ -81,6 +81,7 @@ struct cros_typec_data { struct notifier_block nb; struct work_struct port_work; bool typec_cmd_supported; + bool needs_mux_ack; }; static int cros_typec_parse_port_props(struct typec_capability *cap, @@ -531,6 +532,7 @@ static int cros_typec_configure_mux(struct cros_typec_data *typec, int port_num, struct ec_response_usb_pd_control_v2 *pd_ctrl) { struct cros_typec_port *port = typec->ports[port_num]; + struct ec_params_usb_pd_mux_ack mux_ack; enum typec_orientation orientation; int ret; @@ -570,6 +572,18 @@ static int cros_typec_configure_mux(struct cros_typec_data *typec, int port_num, mux_flags); } + if (!typec->needs_mux_ack) + return ret; + + /* Sending Acknowledgment to EC */ + mux_ack.port = port_num; + + if (cros_typec_ec_command(typec, 0, EC_CMD_USB_PD_MUX_ACK, &mux_ack, + sizeof(mux_ack), NULL, 0) < 0) + dev_warn(typec->dev, + "Failed to send Mux ACK to EC for port: %d\n", + port_num); + return ret; } @@ -1053,6 +1067,8 @@ static int cros_typec_probe(struct platform_device *pdev) typec->typec_cmd_supported = !!cros_typec_feature_supported(typec, EC_FEATURE_TYPEC_CMD); + typec->needs_mux_ack = !!cros_typec_feature_supported(typec, + EC_FEATURE_TYPEC_MUX_REQUIRE_AP_ACK); ret = cros_typec_ec_command(typec, 0, EC_CMD_USB_PD_PORTS, NULL, 0, &resp, sizeof(resp)); diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index 86376779ab31..9787715540c7 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -1286,6 +1286,16 @@ enum ec_feature_code { EC_FEATURE_ISH = 40, /* New TCPMv2 TYPEC_ prefaced commands supported */ EC_FEATURE_TYPEC_CMD = 41, + /* + * The EC will wait for direction from the AP to enter Type-C alternate + * modes or USB4. + */ + EC_FEATURE_TYPEC_REQUIRE_AP_MODE_ENTRY = 42, + /* + * The EC will wait for an acknowledge from the AP after setting the + * mux. + */ + EC_FEATURE_TYPEC_MUX_REQUIRE_AP_ACK = 43, }; #define EC_FEATURE_MASK_0(event_code) BIT(event_code % 32) @@ -6054,6 +6064,13 @@ struct ec_params_charger_control { uint8_t allow_charging; } __ec_align_size1; +/* Get ACK from the USB-C SS muxes */ +#define EC_CMD_USB_PD_MUX_ACK 0x0603 + +struct ec_params_usb_pd_mux_ack { + uint8_t port; /* USB-C port number */ +} __ec_align1; + /*****************************************************************************/ /* * Reserve a range of host commands for board-specific, experimental, or