]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
platform/chrome: cros_ec_typec: Add role swap ops
authorAbhishek Pandit-Subedi <abhishekpandit@chromium.org>
Fri, 11 Jul 2025 00:35:01 +0000 (00:35 +0000)
committerTzung-Bi Shih <tzungbi@kernel.org>
Fri, 11 Jul 2025 03:13:08 +0000 (03:13 +0000)
Add the pr_set and dr_set typec_operations to registered typec ports.
This enables sysfs to control power and data role when the port is
capable of doing so.

Signed-off-by: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
Co-developed-by: Radu Vele <raduvele@google.com>
Signed-off-by: Radu Vele <raduvele@google.com>
Link: https://lore.kernel.org/r/20250711003502.857536-1-raduvele@google.com
Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
drivers/platform/chrome/cros_ec_typec.c

index f437b594055cff700da7ced0cc3260a29dda437b..5a141401e4857d771a57cb3c755540a1a5cc69a2 100644 (file)
@@ -58,8 +58,91 @@ static int cros_typec_enter_usb_mode(struct typec_port *tc_port, enum usb_mode m
                          &req, sizeof(req), NULL, 0);
 }
 
+static int cros_typec_perform_role_swap(struct typec_port *tc_port, int target_role, u8 swap_type)
+{
+       struct cros_typec_port *port = typec_get_drvdata(tc_port);
+       struct cros_typec_data *data = port->typec_data;
+       struct ec_response_usb_pd_control_v2 resp;
+       struct ec_params_usb_pd_control req;
+       int role, ret;
+
+       /* Must be at least v1 to support role swap. */
+       if (!data->pd_ctrl_ver)
+               return -EOPNOTSUPP;
+
+       /* First query the state */
+       req.port = port->port_num;
+       req.role = USB_PD_CTRL_ROLE_NO_CHANGE;
+       req.mux = USB_PD_CTRL_MUX_NO_CHANGE;
+       req.swap = USB_PD_CTRL_SWAP_NONE;
+
+       ret = cros_ec_cmd(data->ec, data->pd_ctrl_ver, EC_CMD_USB_PD_CONTROL,
+                               &req, sizeof(req), &resp, sizeof(resp));
+       if (ret < 0)
+               return ret;
+
+       switch (swap_type) {
+       case USB_PD_CTRL_SWAP_DATA:
+               role = (resp.role & PD_CTRL_RESP_ROLE_DATA) ? TYPEC_HOST :
+                                               TYPEC_DEVICE;
+               break;
+       case USB_PD_CTRL_SWAP_POWER:
+               role = (resp.role & PD_CTRL_RESP_ROLE_POWER) ? TYPEC_SOURCE :
+                                               TYPEC_SINK;
+               break;
+       default:
+               dev_warn(data->dev, "Unsupported role swap type %d\n", swap_type);
+               return -EOPNOTSUPP;
+       }
+
+       if (role == target_role)
+               return 0;
+
+       req.swap = swap_type;
+       ret = cros_ec_cmd(data->ec, data->pd_ctrl_ver, EC_CMD_USB_PD_CONTROL,
+                               &req, sizeof(req), &resp, sizeof(resp));
+       if (ret < 0)
+               return ret;
+
+       switch (swap_type) {
+       case USB_PD_CTRL_SWAP_DATA:
+               role = resp.role & PD_CTRL_RESP_ROLE_DATA ? TYPEC_HOST : TYPEC_DEVICE;
+               if (role != target_role) {
+                       dev_err(data->dev, "Data role swap failed despite EC returning success\n");
+                       return -EIO;
+               }
+               typec_set_data_role(tc_port, target_role);
+               break;
+       case USB_PD_CTRL_SWAP_POWER:
+               role = resp.role & PD_CTRL_RESP_ROLE_POWER ? TYPEC_SOURCE : TYPEC_SINK;
+               if (role != target_role) {
+                       dev_err(data->dev, "Power role swap failed despite EC returning success\n");
+                       return -EIO;
+               }
+               typec_set_pwr_role(tc_port, target_role);
+               break;
+       default:
+               /* Should never execute */
+               break;
+       }
+
+       return 0;
+}
+
+static int cros_typec_dr_swap(struct typec_port *port, enum typec_data_role role)
+{
+       return cros_typec_perform_role_swap(port, role, USB_PD_CTRL_SWAP_DATA);
+}
+
+static int cros_typec_pr_swap(struct typec_port *port, enum typec_role role)
+{
+       return cros_typec_perform_role_swap(port, role, USB_PD_CTRL_SWAP_POWER);
+}
+
 static const struct typec_operations cros_typec_usb_mode_ops = {
-       .enter_usb_mode = cros_typec_enter_usb_mode
+       .enter_usb_mode = cros_typec_enter_usb_mode,
+       .dr_set = cros_typec_dr_swap,
+       .pr_set = cros_typec_pr_swap,
 };
 
 static int cros_typec_parse_port_props(struct typec_capability *cap,