RE: [PATCH] usb: typec: tcpm: Fix if vbus before cc, hard_reset_count not reset issue

From: Jun Li
Date: Mon Oct 12 2020 - 05:25:18 EST




> -----Original Message-----
> From: ChiYuan Huang <u0084500@xxxxxxxxx>
> Sent: Monday, October 12, 2020 2:23 PM
> To: Guenter Roeck <linux@xxxxxxxxxxxx>
> Cc: Jun Li <jun.li@xxxxxxx>; Jun Li <lijun.kernel@xxxxxxxxx>; Greg KH
> <gregkh@xxxxxxxxxxxxxxxxxxx>; Heikki Krogerus
> <heikki.krogerus@xxxxxxxxxxxxxxx>; Linux USB List
> <linux-usb@xxxxxxxxxxxxxxx>; lkml <linux-kernel@xxxxxxxxxxxxxxx>;
> cy_huang <cy_huang@xxxxxxxxxxx>
> Subject: Re: [PATCH] usb: typec: tcpm: Fix if vbus before cc, hard_reset_count
> not reset issue
>
> Guenter Roeck <linux@xxxxxxxxxxxx> 於 2020年10月11日 週日 上午3:31寫道:
> >
> > On 10/10/20 4:21 AM, Jun Li wrote:
> > >
> > >
> > >> -----Original Message-----
> > >> From: ChiYuan Huang <u0084500@xxxxxxxxx>
> > >> Sent: Saturday, October 10, 2020 12:06 AM
> > >> To: Jun Li <jun.li@xxxxxxx>
> > >> Cc: Jun Li <lijun.kernel@xxxxxxxxx>; Guenter Roeck
> > >> <linux@xxxxxxxxxxxx>; Greg KH <gregkh@xxxxxxxxxxxxxxxxxxx>; Heikki
> > >> Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx>; Linux USB List
> > >> <linux-usb@xxxxxxxxxxxxxxx>; lkml <linux-kernel@xxxxxxxxxxxxxxx>;
> > >> cy_huang <cy_huang@xxxxxxxxxxx>
> > >> Subject: Re: [PATCH] usb: typec: tcpm: Fix if vbus before cc,
> > >> hard_reset_count not reset issue
> > >>
> > >> Jun Li <jun.li@xxxxxxx> 於 2020年10月9日 週五 下午2:12寫道:
> > >>>
> > >>>
> > >>>
> > >>>> -----Original Message-----
> > >>>> From: ChiYuan Huang <u0084500@xxxxxxxxx>
> > >>>> Sent: Wednesday, October 7, 2020 6:13 PM
> > >>>> To: Jun Li <lijun.kernel@xxxxxxxxx>
> > >>>> Cc: Guenter Roeck <linux@xxxxxxxxxxxx>; Greg KH
> > >>>> <gregkh@xxxxxxxxxxxxxxxxxxx>; Heikki Krogerus
> > >>>> <heikki.krogerus@xxxxxxxxxxxxxxx>; Linux USB List
> > >>>> <linux-usb@xxxxxxxxxxxxxxx>; lkml <linux-kernel@xxxxxxxxxxxxxxx>;
> > >>>> cy_huang <cy_huang@xxxxxxxxxxx>; Jun Li <jun.li@xxxxxxx>
> > >>>> Subject: Re: [PATCH] usb: typec: tcpm: Fix if vbus before cc,
> > >>>> hard_reset_count not reset issue
> > >>>>
> > >>>> ChiYuan Huang <u0084500@xxxxxxxxx> 於 2020年10月7日 週三 上午1:39
>
> > >> 道:
> > >>>>>
> > >>>>> Jun Li <lijun.kernel@xxxxxxxxx> 於 2020年10月7日 週三 上午12:52
>
> > >> 道:
> > >>>>>>
> > >>>>>> ChiYuan Huang <u0084500@xxxxxxxxx> 于2020年10月6日周二 下午12:38
> > >> 写
> > >>>> 道:
> > >>>>>>>
> > >>>>>>> Guenter Roeck <linux@xxxxxxxxxxxx> 於 2020年10月5日 週一 下午
> > >> 11:30
> > >>>> 寫道:
> > >>>>>>>>
> > >>>>>>>> On 10/5/20 4:08 AM, Greg KH wrote:
> > >>>>>>>> [ ... ]
> > >>>>>>>>>>> What ever happened with this patch, is there still disagreement?
> > >>>>>>>>>>>
> > >>>>>>>>>>
> > >>>>>>>>>> Yes, there is. I wouldn't have added the conditional
> > >>>>>>>>>> without reason, and I am concerned that removing it
> > >>>>>>>>>> entirely will open
> > >>>> another problem.
> > >>>>>>>>>> Feel free to apply, though - I can't prove that my concern
> > >>>>>>>>>> is valid, and after all we'll get reports from the field
> > >>>>>>>>>> later if
> > >>>> it is.
> > >>>>>>>>>
> > >>>>>>>>> Ok, can I get an ack so I know who to come back to in the
> > >>>>>>>>> future if there are issues? :)
> > >>>>>>>>>
> > >>>>>>>>
> > >>>>>>>> Not from me, for the reasons I stated. I would be ok with
> > >>>>>>>> something
> > >>>> like:
> > >>>>>>>>
> > >>>>>>>> - if (tcpm_port_is_disconnected(port))
> > >>>>>>>> + if (tcpm_port_is_disconnected(port) ||
> > >>>>>>>> + (tcpm_cc_is_open(port->cc1) &&
> > >>>>>>>> + tcpm_cc_is_open(port->cc2)))
> > >>>>>>>>
> > >>>>>>>> to narrow down the condition.
> > >>>>>>>
> > >>>>>>> I have tried the above comment and It doesn't work.
> > >>>>>>> How about to change the judgement like as below
> > >>>>>>>
> > >>>>>>> - if (tcpm_port_is_disconnected(port))
> > >>>>>>> + if (tcpm_port_is_disconnected(port) ||
> > >>>>>>> + !port->vbus_present)
> > >>>>>>>
> > >>>>>>> The hard_reset_count not reset issue is following by the below
> > >>>>>>> order 1. VBUS off ( at the same time, cc is still detected as
> > >>>>>>> attached)
> > >>>>>>> port->attached become false and cc is not open
> > >>>>>>> 2. After that, cc detached.
> > >>>>>>> due to port->attached is false, tcpm_detach() directly return.
> > >>>>>>
> > >>>>>> If tcpm_detach() return directly, then how your patch can reset
> > >>>>>> hard_reset_count?
> > >>>>>>
> > >>>>> Yes, it can. We know vbus_present change from true to false and
> > >>>>> cc detach both trigger tcpm_detach.
> > >>>>> My change is whenever tcpm_detach to be called, hard_reset_count
> > >>>>> will be reset to zero.
> > >>>>>
> > >>>>>> I am seeing the same issue on my platform, the proposed change:
> > >>>>>> - if (tcpm_port_is_disconnected(port))
> > >>>>>> - port->hard_reset_count = 0;
> > >>>>>> + port->hard_reset_count = 0;
> > >>>>>> can't resolve it on my platform.
> > >>>>>>
> > >>>>> I'm not sure what's your condition. Could you directly paste the
> > >>>>> tcpm log for the check?
> > >>>>>> How about reset hard_reset_count in SNK_READY?
> > >>>>>> @@ -3325,6 +3329,7 @@ static void run_state_machine(struct
> > >>>>>> tcpm_port
> > >>>> *port)
> > >>>>>> case SNK_READY:
> > >>>>>> port->try_snk_count = 0;
> > >>>>>> port->update_sink_caps = false;
> > >>>>>> + port->hard_reset_count = 0;
> > >>>>>> if (port->explicit_contract) {
> > >>>>>> typec_set_pwr_opmode(port->typec_port,
> > >>>>>>
> > >>>>>> TYPEC_PWR_MODE_PD);
> > >>>>>>
> > >>>>>> can this resolve your problem?
> > >>>>> I'm not sure. It need to have a try, then I can answer you.
> > >>>>> But from USBPD spec, the hard_reset_count need to reset zero
> > >>>>> only when 1. At src state, pe_src_send_cap and receive GoodCRC
> > >>>>> 2. At snk state, pe_snk_evaluate_cap need to reset
> > >>>>> hard_reset_count
> > >>>
> > >>> 3.
> > >>> 8.3.3.3.8 PE_SNK_Hard_Reset state
> > >>> "Note: The HardResetCounter is reset on a power cycle or Detach."
> > >>>
> > >>>>>>
> > >>>>>> Li Jun
> > >>>>>>>
> > >>>>>>> And that's why hard_reset_count is not reset to 0.
> > >>>>
> > >>>> I tried in snk_ready to reset hard_reset_count.
> > >>>> At normal case, it works.
> > >>>> But it seems still the possible fail case like as below.
> > >>>> 200ms -> cc debounce max time
> > >>>> 240ms -> snk_waitcap max time
> > >>>> If the plugin/out period is between (200+240) and (200+ 2* 240)ms
> > >>>> , and the src side plug out like as the described scenario.
> > >>>> From this case, hard_reset_count may still 1.
> > >>>> And we expect the next plugin hard_reset_count is 0. But not,
> > >>>> actually it never reset.
> > >>>> So at next plugin, only one hard_reset will be sent and reach
> > >>>> hard_reset_count max (2).
> > >>>>
> > >>>> This case is hard to reproduce. But actually it's possible.
> > >>>
> > >>> Make sense.
> > >>>
> > >>> Then I propose doing this at SNK_UNATTACHED @@ -3156,6 +3156,7 @@
> > >>> static void run_state_machine(struct tcpm_port *port)
> > >>> if (!port->non_pd_role_swap)
> > >>> tcpm_swap_complete(port, -ENOTCONN);
> > >>> tcpm_pps_complete(port, -ENOTCONN);
> > >>> + port->hard_reset_count = 0;
> > >>> tcpm_snk_detach(port);
> > >>> if (tcpm_start_toggling(port, TYPEC_CC_RD)) {
> > >>> tcpm_set_state(port, TOGGLING, 0); Li Jun
> > >>
> > >> For the current power role is snk, I think it may work.
> > >> How about the src role? src role only reset the hard_reset_count in
> > >> tcpm_detach and src_ready state?
> > >
> > > Sorry, after gave more check on PD sped, this isn't a right solution.
> > > See below
> > >
> > >>
> > >> I check the flow that you mentioned in the previous mail. It's
> > >> really a special case from any state to port_reset.
> > >> If the case is considered, how about to reset the hard_reset_count
> > >> and don't care the port is attached or not in tcpm_detach function like
> as below.
> > >>
> > >> @@ -2789,6 +2789,8 @@ static void tcpm_reset_port(struct tcpm_port
> > >> *port)
> > >>
> > >> static void tcpm_detach(struct tcpm_port *port) {
> > >> + port->hard_reset_count = 0;
> > >> +
> > >> if (!port->attached)
> > >> return;
> > >>
> > >> @@ -2797,9 +2799,6 @@ static void tcpm_detach(struct tcpm_port *port)
> > >> port->tcpc->set_bist_data(port->tcpc, false);
> > >> }
> > >>
> > >> - if (tcpm_port_is_disconnected(port))
> > >> - port->hard_reset_count = 0;
> > >> -
> > >> tcpm_reset_port(port);
> > >> }
> > >>
> > >> Like I mentioned before, whatever the condition is,
> > >> hard_reset_count must be reset to zero during tcpm_detach.
> > >
> > > This may not be so correct as you said, I think Guenter's concern is
> valid.
> > >
> > > tcpm_detach() is not *only* be called in cases of *physical* detach
> > > like the function name suggests.
> > >
> > > The current proposals may *wrongly* reset this counter.
> > >
> > > Let me give an example:
> > >
> > > HARD_RESET_SEND -> HARD_RESET_START -> SNK_HARD_RESET_SINK_OFF ->
> > > SNK_HARD_RESET_WAIT_VBUS -> SNK_UNATTACHED(in case of VBUS doesn't
> > > come back in expected duration)
> > > -> call to tcpm_detach()
> > >
> > > In this sequence, does the sink need keep the counter? From the PD
> > > spec, I think the answer is yes, sink need this counter to judge if
> > > need do hard reset again or error recovery on later try, see:
> > >
> > > Figure 8-67 Sink Port State Diagram
> > >
> > > The difference between your case and my example, is your case never
> > > turn off vbus, so will not go to SNK_UNATTACHED, so will not call to
> > > tcpm_detach() after first hard reset.
> > >
> > > So
> > > if (tcpm_port_is_disconnected(port))
> > > port->hard_reset_count = 0;
> > >
> > > should keep as it is, the counter can only be cleared if there is
> > > really physical disconnect by *partner*.
> > >
> > > But current tcpm code may have some problem on keeping local CC
> > > state if there is hard reset on-going(port->hard_reset_count > 0),
> > > because the current handling of SNK_UNATTACHED may cause
> > > disconnection generated by *local*(partner actually keeps its CC),
> > > e.g. reset polarity and do drp_toggling, this should be fixed, but
> > > this is another patch, I can try to do this later.
> > >
> > > Back to your problem, I think the issue is, at the first
> > > tcpm_detach() the cc disconnect event hasn't happen, so the reset
> > > counter is left there but the port->attached is cleared, then the
> > > following(real disconnect)
> > > tcpm_detach() will just return directly due to port->attached is false.
> > >
> > > So I guess this will resolve your problem:
> > >
> > > @@ -2885,6 +2885,9 @@ static void tcpm_reset_port(struct tcpm_port
> > > *port)
> > >
> > > static void tcpm_detach(struct tcpm_port *port) {
> > > + if (tcpm_port_is_disconnected(port))
> > > + port->hard_reset_count = 0;
> > > +
> > > if (!port->attached)
> > > return;
> > >
> > > @@ -2893,9 +2896,6 @@ static void tcpm_detach(struct tcpm_port *port)
> > > port->tcpc->set_bist_data(port->tcpc, false);
> > > }
> > >
> > > - if (tcpm_port_is_disconnected(port))
> > > - port->hard_reset_count = 0;
> > > -
> > > tcpm_reset_port(port);
> > > }
> > >
> > >
> I have checked this patch. It can solve the problem.

Good, so this can resolve both your and my problems.

>
> > > Compared with current code's condition:
> > > 3 static bool tcpm_port_is_disconnected(struct tcpm_port *port)
> > > 4 {
> > > 5 return (!port->attached && port->cc1 == TYPEC_CC_OPEN &&
> > > 6 port->cc2 == TYPEC_CC_OPEN) ||
> > > 7 (port->attached && ((port->polarity ==
> TYPEC_POLARITY_CC1 &&
> > > 8 port->cc1 == TYPEC_CC_OPEN) ||
> > > 9 (port->polarity ==
> TYPEC_POLARITY_CC2 &&
> > > 10 port->cc2 == TYPEC_CC_OPEN)));
> > > 11 }
> > >
> > > My above change is only adding another condition to clear the reset counter:
> > > (!port->attached && port->cc1 == TYPEC_CC_OPEN && port->cc2 ==
> > > TYPEC_CC_OPEN)
> > >
> > > This condition is close to Guenter's suggestion in this thread:
> > >
> > > - if (tcpm_port_is_disconnected(port))
> > > + if (tcpm_port_is_disconnected(port) ||
> > > + (tcpm_cc_is_open(port->cc1) &&
> > > + tcpm_cc_is_open(port->cc2)))
> > >
> > > Hi Guenter, any comments on this?
> > >
> >
> > That makes sense, and I would agree to the change you suggest above.
> Jun:
> will you send the patch following the final discussion? Or I also can
> help.
> Thx.

Ok, I will send a formal patch for this.

thanks
Li Jun

> >
> > Guenter
> >
> > > Thanks
> > > Li Jun
> > >
> > >>
> > >> But refer to Guenter's mail, he prefer to narrow down the
> > >> condition to reset this counter.
> > >>
> > >> I think the original thought is important why to put this line there.
> > >>
> > >> Hi, Guenter:
> > >> From the discussion, we really need to know why you put the
> > >> reset line below port attached is false and also make some judgement.
> > >> I think there may be ome condition that we don't considered.
> > >
> > > This condition was added at first place, I think my above
> > >>
> > >>>
> > >>>>
> > >>>>>>>>
> > >>>>>>>> Guenter
> >