Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 14fe0f86 authored by Hans de Goede's avatar Hans de Goede Committed by Greg Kroah-Hartman
Browse files

usb: typec: tcpm: Refactor tcpm_handle_vdm_request



[ Upstream commit 95b4d51c96a87cd760c2a4f27fb28a59a27b6368 ]

Refactor tcpm_handle_vdm_request and its tcpm_pd_svdm helper function so
that reporting the results of the vdm to the altmode-driver is separated
out into a clear separate step inside tcpm_handle_vdm_request, instead
of being scattered over various places inside the tcpm_pd_svdm helper.

This is a preparation patch for fixing an AB BA lock inversion between the
tcpm code and some altmode drivers.

Reviewed-by: default avatarHeikki Krogerus <heikki.krogerus@linux.intel.com>
Reviewed-by: default avatarGuenter Roeck <linux@roeck-us.net>
Signed-off-by: default avatarHans de Goede <hdegoede@redhat.com>
Link: https://lore.kernel.org/r/20200724174702.61754-4-hdegoede@redhat.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Stable-dep-of: f23643306430 ("usb: typec: bus: verify partner exists in typec_altmode_attention")
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent 979f8743
Loading
Loading
Loading
Loading
+48 −28
Original line number Diff line number Diff line
@@ -159,6 +159,14 @@ enum pd_msg_request {
	PD_MSG_DATA_SOURCE_CAP,
};

enum adev_actions {
	ADEV_NONE = 0,
	ADEV_NOTIFY_USB_AND_QUEUE_VDM,
	ADEV_QUEUE_VDM,
	ADEV_QUEUE_VDM_SEND_EXIT_MODE_ON_FAIL,
	ADEV_ATTENTION,
};

/* Events from low level driver */

#define TCPM_CC_EVENT		BIT(0)
@@ -1080,10 +1088,10 @@ static void tcpm_register_partner_altmodes(struct tcpm_port *port)

#define supports_modal(port)	PD_IDH_MODAL_SUPP((port)->partner_ident.id_header)

static int tcpm_pd_svdm(struct tcpm_port *port, const u32 *p, int cnt,
			u32 *response)
static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev,
			const u32 *p, int cnt, u32 *response,
			enum adev_actions *adev_action)
{
	struct typec_altmode *adev;
	struct typec_altmode *pdev;
	struct pd_mode_data *modep;
	int rlen = 0;
@@ -1099,9 +1107,6 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const u32 *p, int cnt,

	modep = &port->mode_data;

	adev = typec_match_altmode(port->port_altmode, ALTMODE_DISCOVERY_MAX,
				   PD_VDO_VID(p[0]), PD_VDO_OPOS(p[0]));

	pdev = typec_match_altmode(port->partner_altmode, ALTMODE_DISCOVERY_MAX,
				   PD_VDO_VID(p[0]), PD_VDO_OPOS(p[0]));

@@ -1127,8 +1132,7 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const u32 *p, int cnt,
			break;
		case CMD_ATTENTION:
			/* Attention command does not have response */
			if (adev)
				typec_altmode_attention(adev, p[1]);
			*adev_action = ADEV_ATTENTION;
			return 0;
		default:
			break;
@@ -1182,23 +1186,15 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const u32 *p, int cnt,
		case CMD_ENTER_MODE:
			if (adev && pdev) {
				typec_altmode_update_active(pdev, true);

				if (typec_altmode_vdm(adev, p[0], &p[1], cnt)) {
					response[0] = VDO(adev->svid, 1,
							  CMD_EXIT_MODE);
					response[0] |= VDO_OPOS(adev->mode);
					return 1;
				}
				*adev_action = ADEV_QUEUE_VDM_SEND_EXIT_MODE_ON_FAIL;
			}
			return 0;
		case CMD_EXIT_MODE:
			if (adev && pdev) {
				typec_altmode_update_active(pdev, false);

				/* Back to USB Operation */
				WARN_ON(typec_altmode_notify(adev,
							     TYPEC_STATE_USB,
							     NULL));
				*adev_action = ADEV_NOTIFY_USB_AND_QUEUE_VDM;
				return 0;
			}
			break;
		default:
@@ -1209,11 +1205,8 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const u32 *p, int cnt,
		switch (cmd) {
		case CMD_ENTER_MODE:
			/* Back to USB Operation */
			if (adev)
				WARN_ON(typec_altmode_notify(adev,
							     TYPEC_STATE_USB,
							     NULL));
			break;
			*adev_action = ADEV_NOTIFY_USB_AND_QUEUE_VDM;
			return 0;
		default:
			break;
		}
@@ -1223,15 +1216,15 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const u32 *p, int cnt,
	}

	/* Informing the alternate mode drivers about everything */
	if (adev)
		typec_altmode_vdm(adev, p[0], &p[1], cnt);

	*adev_action = ADEV_QUEUE_VDM;
	return rlen;
}

static void tcpm_handle_vdm_request(struct tcpm_port *port,
				    const __le32 *payload, int cnt)
{
	enum adev_actions adev_action = ADEV_NONE;
	struct typec_altmode *adev;
	u32 p[PD_MAX_PAYLOAD];
	u32 response[8] = { };
	int i, rlen = 0;
@@ -1239,6 +1232,9 @@ static void tcpm_handle_vdm_request(struct tcpm_port *port,
	for (i = 0; i < cnt; i++)
		p[i] = le32_to_cpu(payload[i]);

	adev = typec_match_altmode(port->port_altmode, ALTMODE_DISCOVERY_MAX,
				   PD_VDO_VID(p[0]), PD_VDO_OPOS(p[0]));

	if (port->vdm_state == VDM_STATE_BUSY) {
		/* If UFP responded busy retry after timeout */
		if (PD_VDO_CMDT(p[0]) == CMDT_RSP_BUSY) {
@@ -1253,7 +1249,31 @@ static void tcpm_handle_vdm_request(struct tcpm_port *port,
	}

	if (PD_VDO_SVDM(p[0]))
		rlen = tcpm_pd_svdm(port, p, cnt, response);
		rlen = tcpm_pd_svdm(port, adev, p, cnt, response, &adev_action);

	if (adev) {
		switch (adev_action) {
		case ADEV_NONE:
			break;
		case ADEV_NOTIFY_USB_AND_QUEUE_VDM:
			WARN_ON(typec_altmode_notify(adev, TYPEC_STATE_USB, NULL));
			typec_altmode_vdm(adev, p[0], &p[1], cnt);
			break;
		case ADEV_QUEUE_VDM:
			typec_altmode_vdm(adev, p[0], &p[1], cnt);
			break;
		case ADEV_QUEUE_VDM_SEND_EXIT_MODE_ON_FAIL:
			if (typec_altmode_vdm(adev, p[0], &p[1], cnt)) {
				response[0] = VDO(adev->svid, 1, CMD_EXIT_MODE);
				response[0] |= VDO_OPOS(adev->mode);
				rlen = 1;
			}
			break;
		case ADEV_ATTENTION:
			typec_altmode_attention(adev, p[1]);
			break;
		}
	}

	if (rlen > 0) {
		tcpm_queue_vdm(port, response[0], &response[1], rlen - 1);