Commit ab229c2b authored by Abhishek Pandit-Subedi's avatar Abhishek Pandit-Subedi Committed by Tzung-Bi Shih
Browse files

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: default avatarAbhishek Pandit-Subedi <abhishekpandit@chromium.org>
Co-developed-by: default avatarRadu Vele <raduvele@google.com>
Signed-off-by: default avatarRadu Vele <raduvele@google.com>
Link: https://lore.kernel.org/r/20250711003502.857536-1-raduvele@google.com


Signed-off-by: default avatarTzung-Bi Shih <tzungbi@kernel.org>
parent 5af89b63
Loading
Loading
Loading
Loading
+84 −1
Original line number Diff line number Diff line
@@ -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,