platform/chrome: cros_ec_typec: Add role swap ops

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>
This commit is contained in:
Abhishek Pandit-Subedi 2025-07-11 00:35:01 +00:00 committed by Tzung-Bi Shih
parent 5af89b6309
commit ab229c2b72

View 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,