mirror of
https://github.com/torvalds/linux.git
synced 2026-05-30 10:04:04 +02:00
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:
parent
5af89b6309
commit
ab229c2b72
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user