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

Commit 074daedc authored by Pratham Pratap's avatar Pratham Pratap
Browse files

usb: pd: Avoid double queue of usbpd_sm work



PD state transitions call kick_sm which can either queue the
workqueue immediately or with some delay. Both phy_msg_received()
and psy_changed() also call this when receiving a message or
Type-C event. But there is a possibility that usbpd_sm is already
executing due to a previous state transition and hence an extra
queue_work() is called from these events. If the next transition
has a deliberate delay such as waiting for SENDER_RESPONSE_TIME,
the delay is effectively eliminated and usbpd_sm gets executed
immediately again which can cause the state machine to think that
timeout occurred and will result in soft or hard reset. Avoid this
by adding a work_busy check for usbpd_sm work so that is is not
queued twice without any delay.

Change-Id: I9d5f478e8e0abe5609c80979b3158db16373dc52
Signed-off-by: default avatarPratham Pratap <prathampratap@codeaurora.org>
parent 5ddc46af
Loading
Loading
Loading
Loading
+11 −3
Original line number Diff line number Diff line
@@ -1165,7 +1165,10 @@ static void phy_msg_received(struct usbpd *pd, enum pd_sop_type sop,
	list_add_tail(&rx_msg->entry, &pd->rx_q);
	spin_unlock_irqrestore(&pd->rx_lock, flags);

	if (!work_busy(&pd->sm_work))
		kick_sm(pd, 0);
	else
		usbpd_dbg(&pd->dev, "usbpd_sm already running\n");
}

static void phy_shutdown(struct usbpd *pd)
@@ -3365,7 +3368,7 @@ static void usbpd_sm(struct work_struct *w)
	spin_unlock_irqrestore(&pd->rx_lock, flags);

	/* requeue if there are any new/pending RX messages */
	if (!ret)
	if (!ret && !pd->sm_queued)
		kick_sm(pd, 0);

	if (!pd->sm_queued)
@@ -3457,7 +3460,12 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr)
		usbpd_dbg(&pd->dev, "hard reset: typec mode:%d present:%d\n",
			typec_mode, pd->vbus_present);
		pd->typec_mode = typec_mode;

		if (!work_busy(&pd->sm_work))
			kick_sm(pd, 0);
		else
			usbpd_dbg(&pd->dev, "usbpd_sm already running\n");

		return 0;
	}