[PATCH 3/7] platform/chrome: cros_ec_typec: Support EC mode entry
From: Stephen Boyd
Date: Tue Apr 15 2025 - 20:03:02 EST
Support ChromeOS EC firmwares that don't support AP mode entry. Check
that the mode has been entered by querying the EC and reject mode entry
attempts if the EC hasn't already entered the mode requested. This
allows us to bind the DP altmode driver on devices that don't support AP
mode entry, i.e. most ChromeOS devices where the EC controls mode entry.
Cc: Benson Leung <bleung@xxxxxxxxxxxx>
Cc: Tzung-Bi Shih <tzungbi@xxxxxxxxxx>
Cc: <chrome-platform@xxxxxxxxxxxxxxx>
Cc: Pin-yen Lin <treapking@xxxxxxxxxxxx>
Cc: Abhishek Pandit-Subedi <abhishekpandit@xxxxxxxxxxxx>
Cc: Łukasz Bartosik <ukaszb@xxxxxxxxxxxx>
Cc: Jameson Thies <jthies@xxxxxxxxxx>
Cc: Andrei Kuchynski <akuchynski@xxxxxxxxxxxx>
Signed-off-by: Stephen Boyd <swboyd@xxxxxxxxxxxx>
---
drivers/platform/chrome/cros_typec_altmode.c | 112 +++++++++++++------
1 file changed, 75 insertions(+), 37 deletions(-)
diff --git a/drivers/platform/chrome/cros_typec_altmode.c b/drivers/platform/chrome/cros_typec_altmode.c
index c2d9c548b5e8..97ca4cfabbc0 100644
--- a/drivers/platform/chrome/cros_typec_altmode.c
+++ b/drivers/platform/chrome/cros_typec_altmode.c
@@ -58,31 +58,50 @@ static void cros_typec_altmode_work(struct work_struct *work)
static int cros_typec_altmode_enter(struct typec_altmode *alt, u32 *vdo)
{
struct cros_typec_altmode_data *adata = typec_altmode_get_drvdata(alt);
- struct ec_params_typec_control req = {
- .port = adata->port->port_num,
- .command = TYPEC_CONTROL_COMMAND_ENTER_MODE,
- };
+ struct cros_ec_device *ec = adata->port->typec_data->ec;
+ unsigned int port = adata->port->port_num;
int svdm_version;
int ret;
if (!adata->ap_mode_entry) {
- dev_warn(&alt->dev,
- "EC does not support AP driven mode entry\n");
- return -EOPNOTSUPP;
+ struct ec_response_usb_pd_mux_info resp;
+ struct ec_params_usb_pd_mux_info req = {
+ .port = port,
+ };
+ uint8_t flags;
+
+ if (adata->sid == USB_TYPEC_DP_SID)
+ flags = USB_PD_MUX_DP_ENABLED;
+ else if (adata->sid == USB_TYPEC_TBT_SID)
+ flags = USB_PD_MUX_TBT_COMPAT_ENABLED;
+ else
+ return -EOPNOTSUPP;
+
+ ret = cros_ec_cmd(ec, 0, EC_CMD_USB_PD_MUX_INFO,
+ &req, sizeof(req), &resp, sizeof(resp));
+ if (ret < 0)
+ return ret;
+
+ if (!(resp.flags & flags))
+ return -EINVAL;
+ } else {
+ struct ec_params_typec_control req = {
+ .port = port,
+ .command = TYPEC_CONTROL_COMMAND_ENTER_MODE,
+ };
+
+ if (adata->sid == USB_TYPEC_DP_SID)
+ req.mode_to_enter = CROS_EC_ALTMODE_DP;
+ else if (adata->sid == USB_TYPEC_TBT_SID)
+ req.mode_to_enter = CROS_EC_ALTMODE_TBT;
+ else
+ return -EOPNOTSUPP;
+
+ ret = cros_ec_cmd(ec, 0, EC_CMD_TYPEC_CONTROL, &req, sizeof(req), NULL, 0);
+ if (ret < 0)
+ return ret;
}
- if (adata->sid == USB_TYPEC_DP_SID)
- req.mode_to_enter = CROS_EC_ALTMODE_DP;
- else if (adata->sid == USB_TYPEC_TBT_SID)
- req.mode_to_enter = CROS_EC_ALTMODE_TBT;
- else
- return -EOPNOTSUPP;
-
- ret = cros_ec_cmd(adata->port->typec_data->ec, 0, EC_CMD_TYPEC_CONTROL,
- &req, sizeof(req), NULL, 0);
- if (ret < 0)
- return ret;
-
svdm_version = typec_altmode_get_svdm_version(alt);
if (svdm_version < 0)
return svdm_version;
@@ -97,31 +116,52 @@ static int cros_typec_altmode_enter(struct typec_altmode *alt, u32 *vdo)
schedule_work(&adata->work);
mutex_unlock(&adata->lock);
- return ret;
+
+ return 0;
}
static int cros_typec_altmode_exit(struct typec_altmode *alt)
{
struct cros_typec_altmode_data *adata = typec_altmode_get_drvdata(alt);
- struct ec_params_typec_control req = {
- .port = adata->port->port_num,
- .command = TYPEC_CONTROL_COMMAND_EXIT_MODES,
- };
+ struct cros_ec_device *ec = adata->port->typec_data->ec;
+ unsigned int port = adata->port->port_num;
int svdm_version;
int ret;
if (!adata->ap_mode_entry) {
- dev_warn(&alt->dev,
- "EC does not support AP driven mode exit\n");
- return -EOPNOTSUPP;
+ struct ec_response_usb_pd_mux_info resp;
+ struct ec_params_usb_pd_mux_info req = {
+ .port = port,
+ };
+ uint8_t flags;
+
+ if (adata->sid == USB_TYPEC_DP_SID)
+ flags = USB_PD_MUX_DP_ENABLED;
+ else if (adata->sid == USB_TYPEC_TBT_SID)
+ flags = USB_PD_MUX_TBT_COMPAT_ENABLED;
+ else
+ return -EOPNOTSUPP;
+
+ ret = cros_ec_cmd(ec, 0, EC_CMD_USB_PD_MUX_INFO,
+ &req, sizeof(req), &resp, sizeof(resp));
+ if (ret < 0)
+ return ret;
+
+ if (resp.flags & flags)
+ return -EINVAL;
+ } else {
+ struct ec_params_typec_control req = {
+ .port = port,
+ .command = TYPEC_CONTROL_COMMAND_EXIT_MODES,
+ };
+
+ ret = cros_ec_cmd(adata->port->typec_data->ec, 0, EC_CMD_TYPEC_CONTROL,
+ &req, sizeof(req), NULL, 0);
+
+ if (ret < 0)
+ return ret;
}
- ret = cros_ec_cmd(adata->port->typec_data->ec, 0, EC_CMD_TYPEC_CONTROL,
- &req, sizeof(req), NULL, 0);
-
- if (ret < 0)
- return ret;
-
svdm_version = typec_altmode_get_svdm_version(alt);
if (svdm_version < 0)
return svdm_version;
@@ -136,7 +176,8 @@ static int cros_typec_altmode_exit(struct typec_altmode *alt)
schedule_work(&adata->work);
mutex_unlock(&adata->lock);
- return ret;
+
+ return 0;
}
static int cros_typec_displayport_vdm(struct typec_altmode *alt, u32 header,
@@ -254,9 +295,6 @@ static int cros_typec_altmode_vdm(struct typec_altmode *alt, u32 header,
{
struct cros_typec_altmode_data *adata = typec_altmode_get_drvdata(alt);
- if (!adata->ap_mode_entry)
- return -EOPNOTSUPP;
-
if (adata->sid == USB_TYPEC_DP_SID)
return cros_typec_displayport_vdm(alt, header, data, count);
--
https://chromeos.dev