From 853c1a789f5fe8e783586a5c2dcc2ad1b57ac20f Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 30 Oct 2020 16:25:23 -0700 Subject: [PATCH 1/9] platform/chrome: Don't treat RTC events as wakeup sources The EC sends an RTC host event when the RTC fires, but we don't need to treat that as a wakeup event here. The RTC class already properly handles activating and deactivating a wakeup source in rtc_update_irq() by calling pm_stay_awake() at the start of processing and pm_relax() once all expired RTC timers have been processed. This reduces one wakeup increment but not much else. I noticed this while debugging RTC wakeups and how they always incremented the wakeup count by two instead of one because this is duplicated. Signed-off-by: Stephen Boyd Signed-off-by: Enric Balletbo i Serra Cc: Guenter Roeck Cc: Alessandro Zummo Cc: Alexandre Belloni Cc: Link: https://lore.kernel.org/r/20201030232523.2654478-1-swboyd@chromium.org --- drivers/platform/chrome/cros_ec_proto.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c index 0ecee8b8773d..7c92a6e22d75 100644 --- a/drivers/platform/chrome/cros_ec_proto.c +++ b/drivers/platform/chrome/cros_ec_proto.c @@ -742,12 +742,16 @@ int cros_ec_get_next_event(struct cros_ec_device *ec_dev, * Sensor events need to be parsed by the sensor sub-device. * Defer them, and don't report the wakeup here. */ - if (event_type == EC_MKBP_EVENT_SENSOR_FIFO) - *wake_event = false; - /* Masked host-events should not count as wake events. */ - else if (host_event && - !(host_event & ec_dev->host_event_wake_mask)) + if (event_type == EC_MKBP_EVENT_SENSOR_FIFO) { *wake_event = false; + } else if (host_event) { + /* rtc_update_irq() already handles wakeup events. */ + if (host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_RTC)) + *wake_event = false; + /* Masked host-events should not count as wake events. */ + if (!(host_event & ec_dev->host_event_wake_mask)) + *wake_event = false; + } } return ret; From 0498710be002b35bcb43895c4133a4c4bbfd837e Mon Sep 17 00:00:00 2001 From: Prashant Malani Date: Thu, 29 Oct 2020 15:27:30 -0700 Subject: [PATCH 2/9] platform/chrome: cros_ec_typec: Relocate set_port_params_v*() functions Move the cros_typec_set_port_params_v0/v1() functions closer to the place where they are called, cros_typec_port_update(). While we are performing the relocation, also move cros_typec_get_mux_info() closer to its call-site. No functional changes are introduced by this commit. Signed-off-by: Prashant Malani Signed-off-by: Enric Balletbo i Serra Link: https://lore.kernel.org/r/20201029222738.482366-2-pmalani@chromium.org --- drivers/platform/chrome/cros_ec_typec.c | 136 ++++++++++++------------ 1 file changed, 68 insertions(+), 68 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c index 31be31161350..49083e21317d 100644 --- a/drivers/platform/chrome/cros_ec_typec.c +++ b/drivers/platform/chrome/cros_ec_typec.c @@ -329,74 +329,6 @@ static int cros_typec_ec_command(struct cros_typec_data *typec, return ret; } -static void cros_typec_set_port_params_v0(struct cros_typec_data *typec, - int port_num, struct ec_response_usb_pd_control *resp) -{ - struct typec_port *port = typec->ports[port_num]->port; - enum typec_orientation polarity; - - if (!resp->enabled) - polarity = TYPEC_ORIENTATION_NONE; - else if (!resp->polarity) - polarity = TYPEC_ORIENTATION_NORMAL; - else - polarity = TYPEC_ORIENTATION_REVERSE; - - typec_set_pwr_role(port, resp->role ? TYPEC_SOURCE : TYPEC_SINK); - typec_set_orientation(port, polarity); -} - -static void cros_typec_set_port_params_v1(struct cros_typec_data *typec, - int port_num, struct ec_response_usb_pd_control_v1 *resp) -{ - struct typec_port *port = typec->ports[port_num]->port; - enum typec_orientation polarity; - bool pd_en; - int ret; - - if (!(resp->enabled & PD_CTRL_RESP_ENABLED_CONNECTED)) - polarity = TYPEC_ORIENTATION_NONE; - else if (!resp->polarity) - polarity = TYPEC_ORIENTATION_NORMAL; - else - polarity = TYPEC_ORIENTATION_REVERSE; - typec_set_orientation(port, polarity); - typec_set_data_role(port, resp->role & PD_CTRL_RESP_ROLE_DATA ? - TYPEC_HOST : TYPEC_DEVICE); - typec_set_pwr_role(port, resp->role & PD_CTRL_RESP_ROLE_POWER ? - TYPEC_SOURCE : TYPEC_SINK); - typec_set_vconn_role(port, resp->role & PD_CTRL_RESP_ROLE_VCONN ? - TYPEC_SOURCE : TYPEC_SINK); - - /* Register/remove partners when a connect/disconnect occurs. */ - if (resp->enabled & PD_CTRL_RESP_ENABLED_CONNECTED) { - if (typec->ports[port_num]->partner) - return; - - pd_en = resp->enabled & PD_CTRL_RESP_ENABLED_PD_CAPABLE; - ret = cros_typec_add_partner(typec, port_num, pd_en); - if (ret) - dev_warn(typec->dev, - "Failed to register partner on port: %d\n", - port_num); - } else { - if (!typec->ports[port_num]->partner) - return; - cros_typec_remove_partner(typec, port_num); - } -} - -static int cros_typec_get_mux_info(struct cros_typec_data *typec, int port_num, - struct ec_response_usb_pd_mux_info *resp) -{ - struct ec_params_usb_pd_mux_info req = { - .port = port_num, - }; - - return cros_typec_ec_command(typec, 0, EC_CMD_USB_PD_MUX_INFO, &req, - sizeof(req), resp, sizeof(*resp)); -} - static int cros_typec_usb_safe_state(struct cros_typec_port *port) { port->state.mode = TYPEC_STATE_SAFE; @@ -573,6 +505,74 @@ static int cros_typec_configure_mux(struct cros_typec_data *typec, int port_num, return ret; } +static void cros_typec_set_port_params_v0(struct cros_typec_data *typec, + int port_num, struct ec_response_usb_pd_control *resp) +{ + struct typec_port *port = typec->ports[port_num]->port; + enum typec_orientation polarity; + + if (!resp->enabled) + polarity = TYPEC_ORIENTATION_NONE; + else if (!resp->polarity) + polarity = TYPEC_ORIENTATION_NORMAL; + else + polarity = TYPEC_ORIENTATION_REVERSE; + + typec_set_pwr_role(port, resp->role ? TYPEC_SOURCE : TYPEC_SINK); + typec_set_orientation(port, polarity); +} + +static void cros_typec_set_port_params_v1(struct cros_typec_data *typec, + int port_num, struct ec_response_usb_pd_control_v1 *resp) +{ + struct typec_port *port = typec->ports[port_num]->port; + enum typec_orientation polarity; + bool pd_en; + int ret; + + if (!(resp->enabled & PD_CTRL_RESP_ENABLED_CONNECTED)) + polarity = TYPEC_ORIENTATION_NONE; + else if (!resp->polarity) + polarity = TYPEC_ORIENTATION_NORMAL; + else + polarity = TYPEC_ORIENTATION_REVERSE; + typec_set_orientation(port, polarity); + typec_set_data_role(port, resp->role & PD_CTRL_RESP_ROLE_DATA ? + TYPEC_HOST : TYPEC_DEVICE); + typec_set_pwr_role(port, resp->role & PD_CTRL_RESP_ROLE_POWER ? + TYPEC_SOURCE : TYPEC_SINK); + typec_set_vconn_role(port, resp->role & PD_CTRL_RESP_ROLE_VCONN ? + TYPEC_SOURCE : TYPEC_SINK); + + /* Register/remove partners when a connect/disconnect occurs. */ + if (resp->enabled & PD_CTRL_RESP_ENABLED_CONNECTED) { + if (typec->ports[port_num]->partner) + return; + + pd_en = resp->enabled & PD_CTRL_RESP_ENABLED_PD_CAPABLE; + ret = cros_typec_add_partner(typec, port_num, pd_en); + if (ret) + dev_warn(typec->dev, + "Failed to register partner on port: %d\n", + port_num); + } else { + if (!typec->ports[port_num]->partner) + return; + cros_typec_remove_partner(typec, port_num); + } +} + +static int cros_typec_get_mux_info(struct cros_typec_data *typec, int port_num, + struct ec_response_usb_pd_mux_info *resp) +{ + struct ec_params_usb_pd_mux_info req = { + .port = port_num, + }; + + return cros_typec_ec_command(typec, 0, EC_CMD_USB_PD_MUX_INFO, &req, + sizeof(req), resp, sizeof(*resp)); +} + static int cros_typec_port_update(struct cros_typec_data *typec, int port_num) { struct ec_params_usb_pd_control req; From 7ab5a673f4ce65875c76e9812d2e6da063b87fb7 Mon Sep 17 00:00:00 2001 From: Prashant Malani Date: Thu, 29 Oct 2020 15:27:32 -0700 Subject: [PATCH 3/9] platform/chrome: cros_ec_typec: Fix remove partner logic The cros_unregister_ports() function can be called in situations where the partner has not been registered yet, and so its related data structures would not have been initialized. Calling cros_typec_remove_partner() in such a situation can lead to null pointer dereferences. So, only call cros_typec_remove_partner() if there is a valid registered partner pointer. Signed-off-by: Prashant Malani Signed-off-by: Enric Balletbo i Serra Link: https://lore.kernel.org/r/20201029222738.482366-3-pmalani@chromium.org --- drivers/platform/chrome/cros_ec_typec.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c index 49083e21317d..2665d8125910 100644 --- a/drivers/platform/chrome/cros_ec_typec.c +++ b/drivers/platform/chrome/cros_ec_typec.c @@ -190,7 +190,10 @@ static void cros_unregister_ports(struct cros_typec_data *typec) for (i = 0; i < typec->num_ports; i++) { if (!typec->ports[i]) continue; - cros_typec_remove_partner(typec, i); + + if (typec->ports[i]->partner) + cros_typec_remove_partner(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); From 514acf1cefd020eb21d7c180050a8d66b723d2d8 Mon Sep 17 00:00:00 2001 From: Prashant Malani Date: Thu, 29 Oct 2020 15:27:34 -0700 Subject: [PATCH 4/9] platform/chrome: cros_ec_typec: Clear partner identity on device removal The partner identity struct isn't reset when a partner is removed, meaning a subsequent partner can inherit an old partner's identity VDOs before discovery is complete. So, clear that struct when a partner removal is detected. Signed-off-by: Prashant Malani Signed-off-by: Enric Balletbo i Serra Link: https://lore.kernel.org/r/20201029222738.482366-4-pmalani@chromium.org --- drivers/platform/chrome/cros_ec_typec.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c index 2665d8125910..faef56bcb9c5 100644 --- a/drivers/platform/chrome/cros_ec_typec.c +++ b/drivers/platform/chrome/cros_ec_typec.c @@ -181,6 +181,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)); } static void cros_unregister_ports(struct cros_typec_data *typec) From cd2c40ff90b0e385c18f881ab5e17f7137864223 Mon Sep 17 00:00:00 2001 From: Prashant Malani Date: Thu, 29 Oct 2020 15:27:36 -0700 Subject: [PATCH 5/9] platform/chrome: cros_ec: Import Type C host commands Import the EC_CMD_TYPEC_STATUS and EC_CMD_TYPEC_DISCOVERY Chrome OS EC host commands from the EC code base [1]. These commands can be used by the application processor to query Power Delivery (PD) discovery information concerning connected Type C peripherals. Also add the EC_FEATURE_TYPEC_CMD feature flag, which is used to determine whether these commands are supported by the EC. [1]: https://chromium.googlesource.com/chromiumos/platform/ec/+/refs/heads/master/include/ec_commands.h Signed-off-by: Prashant Malani Signed-off-by: Enric Balletbo i Serra Link: https://lore.kernel.org/r/20201029222738.482366-5-pmalani@chromium.org --- .../linux/platform_data/cros_ec_commands.h | 155 ++++++++++++++++++ 1 file changed, 155 insertions(+) diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index 1fcfe9e63cb9..7f54fdcdd8cb 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -1284,6 +1284,8 @@ enum ec_feature_code { EC_FEATURE_SCP = 39, /* The MCU is an Integrated Sensor Hub */ EC_FEATURE_ISH = 40, + /* New TCPMv2 TYPEC_ prefaced commands supported */ + EC_FEATURE_TYPEC_CMD = 41, }; #define EC_FEATURE_MASK_0(event_code) BIT(event_code % 32) @@ -5528,6 +5530,159 @@ struct ec_response_regulator_get_voltage { uint32_t voltage_mv; } __ec_align4; +/* + * Gather all discovery information for the given port and partner type. + * + * Note that if discovery has not yet completed, only the currently completed + * responses will be filled in. If the discovery data structures are changed + * in the process of the command running, BUSY will be returned. + * + * VDO field sizes are set to the maximum possible number of VDOs a VDM may + * contain, while the number of SVIDs here is selected to fit within the PROTO2 + * maximum parameter size. + */ +#define EC_CMD_TYPEC_DISCOVERY 0x0131 + +enum typec_partner_type { + TYPEC_PARTNER_SOP = 0, + TYPEC_PARTNER_SOP_PRIME = 1, +}; + +struct ec_params_typec_discovery { + uint8_t port; + uint8_t partner_type; /* enum typec_partner_type */ +} __ec_align1; + +struct svid_mode_info { + uint16_t svid; + uint16_t mode_count; /* Number of modes partner sent */ + uint32_t mode_vdo[6]; /* Max VDOs allowed after VDM header is 6 */ +}; + +struct ec_response_typec_discovery { + uint8_t identity_count; /* Number of identity VDOs partner sent */ + uint8_t svid_count; /* Number of SVIDs partner sent */ + uint16_t reserved; + uint32_t discovery_vdo[6]; /* Max VDOs allowed after VDM header is 6 */ + struct svid_mode_info svids[0]; +} __ec_align1; + +/* + * Gather all status information for a port. + * + * Note: this covers many of the return fields from the deprecated + * EC_CMD_USB_PD_CONTROL command, except those that are redundant with the + * discovery data. The "enum pd_cc_states" is defined with the deprecated + * EC_CMD_USB_PD_CONTROL command. + * + * This also combines in the EC_CMD_USB_PD_MUX_INFO flags. + */ +#define EC_CMD_TYPEC_STATUS 0x0133 + +/* + * Power role. + * + * Note this is also used for PD header creation, and values align to those in + * the Power Delivery Specification Revision 3.0 (See + * 6.2.1.1.4 Port Power Role). + */ +enum pd_power_role { + PD_ROLE_SINK = 0, + PD_ROLE_SOURCE = 1 +}; + +/* + * Data role. + * + * Note this is also used for PD header creation, and the first two values + * align to those in the Power Delivery Specification Revision 3.0 (See + * 6.2.1.1.6 Port Data Role). + */ +enum pd_data_role { + PD_ROLE_UFP = 0, + PD_ROLE_DFP = 1, + PD_ROLE_DISCONNECTED = 2, +}; + +enum pd_vconn_role { + PD_ROLE_VCONN_OFF = 0, + PD_ROLE_VCONN_SRC = 1, +}; + +/* + * Note: BIT(0) may be used to determine whether the polarity is CC1 or CC2, + * regardless of whether a debug accessory is connected. + */ +enum tcpc_cc_polarity { + /* + * _CCx: is used to indicate the polarity while not connected to + * a Debug Accessory. Only one CC line will assert a resistor and + * the other will be open. + */ + POLARITY_CC1 = 0, + POLARITY_CC2 = 1, + + /* + * _CCx_DTS is used to indicate the polarity while connected to a + * SRC Debug Accessory. Assert resistors on both lines. + */ + POLARITY_CC1_DTS = 2, + POLARITY_CC2_DTS = 3, + + /* + * The current TCPC code relies on these specific POLARITY values. + * Adding in a check to verify if the list grows for any reason + * that this will give a hint that other places need to be + * adjusted. + */ + POLARITY_COUNT +}; + +#define PD_STATUS_EVENT_SOP_DISC_DONE BIT(0) +#define PD_STATUS_EVENT_SOP_PRIME_DISC_DONE BIT(1) + +struct ec_params_typec_status { + uint8_t port; +} __ec_align1; + +struct ec_response_typec_status { + uint8_t pd_enabled; /* PD communication enabled - bool */ + uint8_t dev_connected; /* Device connected - bool */ + uint8_t sop_connected; /* Device is SOP PD capable - bool */ + uint8_t source_cap_count; /* Number of Source Cap PDOs */ + + uint8_t power_role; /* enum pd_power_role */ + uint8_t data_role; /* enum pd_data_role */ + uint8_t vconn_role; /* enum pd_vconn_role */ + uint8_t sink_cap_count; /* Number of Sink Cap PDOs */ + + uint8_t polarity; /* enum tcpc_cc_polarity */ + uint8_t cc_state; /* enum pd_cc_states */ + uint8_t dp_pin; /* DP pin mode (MODE_DP_IN_[A-E]) */ + uint8_t mux_state; /* USB_PD_MUX* - encoded mux state */ + + char tc_state[32]; /* TC state name */ + + uint32_t events; /* PD_STATUS_EVENT bitmask */ + + /* + * BCD PD revisions for partners + * + * The format has the PD major reversion in the upper nibble, and PD + * minor version in the next nibble. Following two nibbles are + * currently 0. + * ex. PD 3.2 would map to 0x3200 + * + * PD major/minor will be 0 if no PD device is connected. + */ + uint16_t sop_revision; + uint16_t sop_prime_revision; + + uint32_t source_cap_pdos[7]; /* Max 7 PDOs can be present */ + + uint32_t sink_cap_pdos[7]; /* Max 7 PDOs can be present */ +} __ec_align1; + /*****************************************************************************/ /* The command range 0x200-0x2FF is reserved for Rotor. */ From 80f8cef60d79f23c02e546ba3de2fce84d5e8bdb Mon Sep 17 00:00:00 2001 From: Prashant Malani Date: Thu, 29 Oct 2020 15:27:38 -0700 Subject: [PATCH 6/9] platform/chrome: cros_ec_typec: Introduce TYPEC_STATUS Make a call to the newly introduced EC_CMD_TYPEC_STATUS command. Currently we just check to see if the SOP (port-partner) discovery was done and emit a debug level print for it. Subsequent patches will retrieve and parse the discovery data and fill out the Type C connector class data structures. Also check the EC_FEATURE_TYPEC_CMD feature flag at probe, and only call the new TYPEC_STATUS command if the feature flag is supported. Reported-by: kernel test robot Signed-off-by: Prashant Malani Signed-off-by: Enric Balletbo i Serra Link: https://lore.kernel.org/r/20201029222738.482366-6-pmalani@chromium.org --- drivers/platform/chrome/cros_ec_typec.c | 52 +++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c index faef56bcb9c5..f578d0bfbe5a 100644 --- a/drivers/platform/chrome/cros_ec_typec.c +++ b/drivers/platform/chrome/cros_ec_typec.c @@ -48,6 +48,9 @@ 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; }; /* Platform-specific data for the Chrome OS EC Type C controller. */ @@ -60,6 +63,7 @@ struct cros_typec_data { struct cros_typec_port *ports[EC_USB_PD_MAX_PORTS]; struct notifier_block nb; struct work_struct port_work; + bool typec_cmd_supported; }; static int cros_typec_parse_port_props(struct typec_capability *cap, @@ -182,6 +186,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; } static void cros_unregister_ports(struct cros_typec_data *typec) @@ -577,6 +582,31 @@ static int cros_typec_get_mux_info(struct cros_typec_data *typec, int port_num, sizeof(req), resp, sizeof(*resp)); } +static void cros_typec_handle_status(struct cros_typec_data *typec, int port_num) +{ + struct ec_response_typec_status resp; + struct ec_params_typec_status req = { + .port = port_num, + }; + int ret; + + ret = cros_typec_ec_command(typec, 0, EC_CMD_TYPEC_STATUS, &req, sizeof(req), + &resp, sizeof(resp)); + if (ret < 0) { + dev_warn(typec->dev, "EC_CMD_TYPEC_STATUS failed for port: %d\n", port_num); + return; + } + + if (typec->ports[port_num]->disc_done) + return; + + /* Handle any events appropriately. */ + if (resp.events & PD_STATUS_EVENT_SOP_DISC_DONE) { + dev_dbg(typec->dev, "SOP Discovery done for port: %d\n", port_num); + typec->ports[port_num]->disc_done = true; + } +} + static int cros_typec_port_update(struct cros_typec_data *typec, int port_num) { struct ec_params_usb_pd_control req; @@ -613,6 +643,9 @@ static int cros_typec_port_update(struct cros_typec_data *typec, int port_num) cros_typec_set_port_params_v0(typec, port_num, (struct ec_response_usb_pd_control *) &resp); + if (typec->typec_cmd_supported) + cros_typec_handle_status(typec, port_num); + /* Update the switches if they exist, according to requested state */ ret = cros_typec_get_mux_info(typec, port_num, &mux_resp); if (ret < 0) { @@ -661,6 +694,23 @@ 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) +{ + struct ec_response_get_features resp = {}; + int ret; + + ret = cros_typec_ec_command(typec, 0, EC_CMD_GET_FEATURES, NULL, 0, + &resp, sizeof(resp)); + if (ret < 0) { + dev_warn(typec->dev, + "Failed to get features, assuming typec commands unsupported.\n"); + return 0; + } + + return resp.flags[EC_FEATURE_TYPEC_CMD / 32] & EC_FEATURE_MASK_1(EC_FEATURE_TYPEC_CMD); +} + static void cros_typec_port_work(struct work_struct *work) { struct cros_typec_data *typec = container_of(work, struct cros_typec_data, port_work); @@ -720,6 +770,8 @@ static int cros_typec_probe(struct platform_device *pdev) return ret; } + typec->typec_cmd_supported = !!cros_typec_cmds_supported(typec); + ret = cros_typec_ec_command(typec, 0, EC_CMD_USB_PD_PORTS, NULL, 0, &resp, sizeof(resp)); if (ret < 0) From f6f668118918f533676e51f3214f5a104562b59c Mon Sep 17 00:00:00 2001 From: Prashant Malani Date: Thu, 29 Oct 2020 15:27:40 -0700 Subject: [PATCH 7/9] platform/chrome: cros_ec_typec: Parse partner PD ID VDOs Use EC_CMD_TYPE_DISCOVERY to retrieve and store the discovery data for the port partner. With that data, update the PD Identity VDO values for the partner, which were earlier not initialized. Signed-off-by: Prashant Malani Signed-off-by: Enric Balletbo i Serra Acked-by: Heikki Krogerus Cc: Heikki Krogerus Link: https://lore.kernel.org/r/20201029222738.482366-7-pmalani@chromium.org --- drivers/platform/chrome/cros_ec_typec.c | 60 ++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c index f578d0bfbe5a..f14550dac614 100644 --- a/drivers/platform/chrome/cros_ec_typec.c +++ b/drivers/platform/chrome/cros_ec_typec.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +52,7 @@ struct cros_typec_port { /* Flag indicating that PD discovery data parsing is completed. */ bool disc_done; + struct ec_response_typec_discovery *sop_disc; }; /* Platform-specific data for the Chrome OS EC Type C controller. */ @@ -298,6 +300,12 @@ static int cros_typec_init_ports(struct cros_typec_data *typec) port_num); 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) { + ret = -ENOMEM; + goto unregister_ports; + } } return 0; @@ -582,6 +590,51 @@ static int cros_typec_get_mux_info(struct cros_typec_data *typec, int port_num, sizeof(req), resp, sizeof(*resp)); } +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_params_typec_discovery req = { + .port = port_num, + .partner_type = TYPEC_PARTNER_SOP, + }; + int ret = 0; + int i; + + if (!port->partner) { + dev_err(typec->dev, + "SOP Discovery received without partner registered, port: %d\n", + port_num); + ret = -EINVAL; + goto disc_exit; + } + + memset(sop_disc, 0, EC_PROTO2_MAX_RESPONSE_SIZE); + ret = cros_typec_ec_command(typec, 0, EC_CMD_TYPEC_DISCOVERY, &req, sizeof(req), + sop_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 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]; + + ret = typec_partner_set_identity(port->partner); + +disc_exit: + return ret; +} + static void cros_typec_handle_status(struct cros_typec_data *typec, int port_num) { struct ec_response_typec_status resp; @@ -602,7 +655,12 @@ static void cros_typec_handle_status(struct cros_typec_data *typec, int port_num /* Handle any events appropriately. */ if (resp.events & PD_STATUS_EVENT_SOP_DISC_DONE) { - dev_dbg(typec->dev, "SOP Discovery done for port: %d\n", port_num); + ret = cros_typec_handle_sop_disc(typec, port_num); + 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; } } From de0f49487db3667f5204dcec6d3482c9bd1a0a30 Mon Sep 17 00:00:00 2001 From: Prashant Malani Date: Thu, 29 Oct 2020 15:27:42 -0700 Subject: [PATCH 8/9] platform/chrome: cros_ec_typec: Register partner altmodes Use the discovery data from the Chrome EC to register parter altmodes with the Type C Connector Class framework. Also introduce a node struct to keep track of the list of registered alt modes. Signed-off-by: Prashant Malani Signed-off-by: Enric Balletbo i Serra Acked-by: Heikki Krogerus Cc: Heikki Krogerus Link: https://lore.kernel.org/r/20201029222738.482366-8-pmalani@chromium.org --- drivers/platform/chrome/cros_ec_typec.c | 77 +++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c index f14550dac614..ce031a10eb1b 100644 --- a/drivers/platform/chrome/cros_ec_typec.c +++ b/drivers/platform/chrome/cros_ec_typec.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -31,6 +32,12 @@ enum { CROS_EC_ALTMODE_MAX, }; +/* Container for altmode pointer nodes. */ +struct cros_typec_altmode_node { + struct typec_altmode *amode; + struct list_head list; +}; + /* Per port data. */ struct cros_typec_port { struct typec_port *port; @@ -53,6 +60,7 @@ struct cros_typec_port { /* Flag indicating that PD discovery data parsing is completed. */ bool disc_done; struct ec_response_typec_discovery *sop_disc; + struct list_head partner_mode_list; }; /* Platform-specific data for the Chrome OS EC Type C controller. */ @@ -172,11 +180,25 @@ 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) +{ + struct cros_typec_port *port = typec->ports[port_num]; + struct cros_typec_altmode_node *node, *tmp; + + list_for_each_entry_safe(node, tmp, &port->partner_mode_list, list) { + list_del(&node->list); + typec_unregister_altmode(node->amode); + devm_kfree(typec->dev, node); + } +} + static void cros_typec_remove_partner(struct cros_typec_data *typec, int port_num) { struct cros_typec_port *port = typec->ports[port_num]; + cros_typec_unregister_altmodes(typec, port_num); + port->state.alt = NULL; port->state.mode = TYPEC_STATE_USB; port->state.data = NULL; @@ -306,6 +328,8 @@ static int cros_typec_init_ports(struct cros_typec_data *typec) ret = -ENOMEM; goto unregister_ports; } + + INIT_LIST_HEAD(&cros_port->partner_mode_list); } return 0; @@ -590,6 +614,49 @@ 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) +{ + struct cros_typec_port *port = typec->ports[port_num]; + struct ec_response_typec_discovery *sop_disc = port->sop_disc; + struct cros_typec_altmode_node *node; + struct typec_altmode_desc desc; + struct typec_altmode *amode; + int ret = 0; + int i, j; + + for (i = 0; i < sop_disc->svid_count; i++) { + for (j = 0; j < sop_disc->svids[i].mode_count; j++) { + memset(&desc, 0, sizeof(desc)); + desc.svid = sop_disc->svids[i].svid; + desc.mode = j; + desc.vdo = sop_disc->svids[i].mode_vdo[j]; + + amode = typec_partner_register_altmode(port->partner, &desc); + if (IS_ERR(amode)) { + ret = PTR_ERR(amode); + goto err_cleanup; + } + + /* If no memory is available we should unregister and exit. */ + node = devm_kzalloc(typec->dev, sizeof(*node), GFP_KERNEL); + if (!node) { + ret = -ENOMEM; + typec_unregister_altmode(amode); + goto err_cleanup; + } + + node->amode = amode; + list_add_tail(&node->list, &port->partner_mode_list); + } + } + + return 0; + +err_cleanup: + cros_typec_unregister_altmodes(typec, port_num); + 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]; @@ -630,6 +697,16 @@ static int cros_typec_handle_sop_disc(struct cros_typec_data *typec, int port_nu port->p_identity.vdo[i - 3] = sop_disc->discovery_vdo[i]; ret = typec_partner_set_identity(port->partner); + if (ret < 0) { + dev_err(typec->dev, "Failed to update partner PD identity, port: %d\n", port_num); + goto disc_exit; + } + + ret = cros_typec_register_altmodes(typec, port_num); + if (ret < 0) { + dev_err(typec->dev, "Failed to register partner altmodes, port: %d\n", port_num); + goto disc_exit; + } disc_exit: return ret; From 6ae9b5ffcaeba64c290dfb8bd7b0194b1fdf0c92 Mon Sep 17 00:00:00 2001 From: Prashant Malani Date: Thu, 5 Nov 2020 18:03:05 -0800 Subject: [PATCH 9/9] platform/chrome: cros_ec_typec: Tolerate unrecognized mux flags On occasion, the Chrome Embedded Controller (EC) can send a mux configuration which doesn't map to a particular data mode. For instance, dedicated Type C chargers, when connected, may cause only USB_PD_MUX_POLARITY_INVERTED to be set. This is a valid flag combination and should not lead to a driver abort. Modify the mux configuration handling to not return an error when an unrecognized mux flag combination is encountered. Concordantly, make the ensuing print a debug level print so as to not pollute the kernel logs. Cc: Keith Short Signed-off-by: Prashant Malani Acked-by: Heikki Krogerus Signed-off-by: Benson Leung Link: https://lore.kernel.org/r/20201106020305.767202-1-pmalani@chromium.org --- drivers/platform/chrome/cros_ec_typec.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c index ce031a10eb1b..5b8db02ab84a 100644 --- a/drivers/platform/chrome/cros_ec_typec.c +++ b/drivers/platform/chrome/cros_ec_typec.c @@ -537,10 +537,9 @@ static int cros_typec_configure_mux(struct cros_typec_data *typec, int port_num, port->state.mode = TYPEC_STATE_USB; ret = typec_mux_set(port->mux, &port->state); } else { - dev_info(typec->dev, - "Unsupported mode requested, mux flags: %x\n", - mux_flags); - ret = -ENOTSUPP; + dev_dbg(typec->dev, + "Unrecognized mode requested, mux flags: %x\n", + mux_flags); } return ret;