When two tcpm_set_state are successively executed in non-worker context, such as tcpm_init, an abnormal sequence may be exist which causes two continuous same state to be handled. tcpm_init: [ 1.686293] CC1: 0 -> 0, CC2: 0 -> 0 [state SNK_UNATTACHED, polarity 0, disconnected] [ 1.686300] state change SNK_UNATTACHED -> PORT_RESET [rev1 NONE_AMS] [ 1.686309] 1-0050: registered [ 1.686315] Setting usb_comm capable false [ 1.687417] Setting voltage/current limit 0 mV 0 mA [ 1.687425] polarity 0 [ 1.690709] Requesting mux state 0, usb-role 0, orientation 0 [ 1.691599] cc:=0 [ 1.691871] pending state change PORT_RESET -> PORT_RESET_WAIT_OFF @ 100 ms [rev1 NONE_AMS] [ 1.691880] Setting usb_comm capable false [ 1.692973] Setting voltage/current limit 0 mV 0 mA [ 1.692980] polarity 0 [ 1.696843] Requesting mux state 0, usb-role 0, orientation 0 [ 1.697729] cc:=0 [ 1.697994] pending state change PORT_RESET -> PORT_RESET_WAIT_OFF @ 100 ms [rev1 NONE_AMS] abnormal sequence: [tcpm_init] [tcpm_state_machine_work] 1 tcpm_set_state(A) 2 port->state = A 3 kthread_queue_work 4 queue work to worker_list 5 dequeue work from worker_list 6 tcpm_set_state(B) 7 port->state = B 8 kthread_queue_work 9 queue work to worker_list 10 tcpm_state_machine_work(B) 11 port->state not change 12 dequeue work from worker_list 13 tcpm_state_machine_work(B) state B is handled twice. Fixes: 4b4e02c83167 ("typec: tcpm: Move out of staging") cc: <stable@xxxxxxxxxxxxxxx> Signed-off-by: Xu Yang <xu.yang_2@xxxxxxx> diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 4bdf119b1306..e0a319e00b12 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -4825,6 +4825,10 @@ static void tcpm_state_machine_work(struct kthread_work *work) port->delayed_state = INVALID_STATE; } + /* If target state is the same as the last entered state, skip it */ + if (port->enter_state == port->state) + goto done; + /* * Continue running as long as we have (non-delayed) state changes * to make. -- 2.25.1