diff mbox series

[v2] usb: typec: fusb302: Fix masking of comparator and bc_lvl interrupts

Message ID 20211108102833.2793803-1-megous@megous.com (mailing list archive)
State Accepted
Commit 362468830dd5bea8bf6ad5203b2ea61f8a4e8288
Headers show
Series [v2] usb: typec: fusb302: Fix masking of comparator and bc_lvl interrupts | expand

Commit Message

Ondřej Jirman Nov. 8, 2021, 10:28 a.m. UTC
The code that enables either BC_LVL or COMP_CHNG interrupt in tcpm_set_cc
wrongly assumes that the interrupt is unmasked by writing 1 to the apropriate
bit in the mask register. In fact, interrupts are enabled when the mask
is 0, so the tcpm_set_cc enables interrupt for COMP_CHNG when it expects
BC_LVL interrupt to be enabled.

This causes inability of the driver to recognize cable unplug events
in host mode (unplug is recognized only via a COMP_CHNG interrupt).

In device mode this bug was masked by simultaneous triggering of the VBUS
change interrupt, because of loss of VBUS when the port peer is providing
power.

Fixes: 48242e30532b ("usb: typec: fusb302: Revert "Resolve fixed power role contract setup"")
Signed-off-by: Ondrej Jirman <megous@megous.com>
Cc: Hans de Goede <hdegoede@redhat.com>
---
 drivers/usb/typec/tcpm/fusb302.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

Comments

Hans de Goede Nov. 8, 2021, 11:24 a.m. UTC | #1
Hi,

On 11/8/21 11:28, Ondrej Jirman wrote:
> The code that enables either BC_LVL or COMP_CHNG interrupt in tcpm_set_cc
> wrongly assumes that the interrupt is unmasked by writing 1 to the apropriate
> bit in the mask register. In fact, interrupts are enabled when the mask
> is 0, so the tcpm_set_cc enables interrupt for COMP_CHNG when it expects
> BC_LVL interrupt to be enabled.
> 
> This causes inability of the driver to recognize cable unplug events
> in host mode (unplug is recognized only via a COMP_CHNG interrupt).
> 
> In device mode this bug was masked by simultaneous triggering of the VBUS
> change interrupt, because of loss of VBUS when the port peer is providing
> power.
> 
> Fixes: 48242e30532b ("usb: typec: fusb302: Revert "Resolve fixed power role contract setup"")
> Signed-off-by: Ondrej Jirman <megous@megous.com>
> Cc: Hans de Goede <hdegoede@redhat.com>

Thanks, patch looks good to me:

Reviewed-by: Hans de Goede <hdegoede@redhat.com>

Regards,

Hans

> ---
>  drivers/usb/typec/tcpm/fusb302.c | 6 ++++--
>  1 file changed, 4 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c
> index 7a2a17866a823..72f9001b07921 100644
> --- a/drivers/usb/typec/tcpm/fusb302.c
> +++ b/drivers/usb/typec/tcpm/fusb302.c
> @@ -669,25 +669,27 @@ static int tcpm_set_cc(struct tcpc_dev *dev, enum typec_cc_status cc)
>  		ret = fusb302_i2c_mask_write(chip, FUSB_REG_MASK,
>  					     FUSB_REG_MASK_BC_LVL |
>  					     FUSB_REG_MASK_COMP_CHNG,
> -					     FUSB_REG_MASK_COMP_CHNG);
> +					     FUSB_REG_MASK_BC_LVL);
>  		if (ret < 0) {
>  			fusb302_log(chip, "cannot set SRC interrupt, ret=%d",
>  				    ret);
>  			goto done;
>  		}
>  		chip->intr_comp_chng = true;


> +		chip->intr_bc_lvl = false;

FWIW this is not necessary because the fusb302_set_toggling(chip, TOGGLING_MODE_OFF)
already does this, but it makes the code more clear, so lets keep it.

>  		break;
>  	case TYPEC_CC_RD:
>  		ret = fusb302_i2c_mask_write(chip, FUSB_REG_MASK,
>  					     FUSB_REG_MASK_BC_LVL |
>  					     FUSB_REG_MASK_COMP_CHNG,
> -					     FUSB_REG_MASK_BC_LVL);
> +					     FUSB_REG_MASK_COMP_CHNG);
>  		if (ret < 0) {
>  			fusb302_log(chip, "cannot set SRC interrupt, ret=%d",
>  				    ret);
>  			goto done;
>  		}
>  		chip->intr_bc_lvl = true;


> +		chip->intr_comp_chng = false;

Idem.

>  		break;
>  	default:
>  		break;
> 

Regards,

Hans
Heikki Krogerus Nov. 15, 2021, 7:11 a.m. UTC | #2
On Mon, Nov 08, 2021 at 11:28:32AM +0100, Ondrej Jirman wrote:
> The code that enables either BC_LVL or COMP_CHNG interrupt in tcpm_set_cc
> wrongly assumes that the interrupt is unmasked by writing 1 to the apropriate
> bit in the mask register. In fact, interrupts are enabled when the mask
> is 0, so the tcpm_set_cc enables interrupt for COMP_CHNG when it expects
> BC_LVL interrupt to be enabled.
> 
> This causes inability of the driver to recognize cable unplug events
> in host mode (unplug is recognized only via a COMP_CHNG interrupt).
> 
> In device mode this bug was masked by simultaneous triggering of the VBUS
> change interrupt, because of loss of VBUS when the port peer is providing
> power.
> 
> Fixes: 48242e30532b ("usb: typec: fusb302: Revert "Resolve fixed power role contract setup"")
> Signed-off-by: Ondrej Jirman <megous@megous.com>
> Cc: Hans de Goede <hdegoede@redhat.com>

Should this go to stable?

Acked-by: Heikki Krogerus@linux.intel.com

> ---
>  drivers/usb/typec/tcpm/fusb302.c | 6 ++++--
>  1 file changed, 4 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c
> index 7a2a17866a823..72f9001b07921 100644
> --- a/drivers/usb/typec/tcpm/fusb302.c
> +++ b/drivers/usb/typec/tcpm/fusb302.c
> @@ -669,25 +669,27 @@ static int tcpm_set_cc(struct tcpc_dev *dev, enum typec_cc_status cc)
>  		ret = fusb302_i2c_mask_write(chip, FUSB_REG_MASK,
>  					     FUSB_REG_MASK_BC_LVL |
>  					     FUSB_REG_MASK_COMP_CHNG,
> -					     FUSB_REG_MASK_COMP_CHNG);
> +					     FUSB_REG_MASK_BC_LVL);
>  		if (ret < 0) {
>  			fusb302_log(chip, "cannot set SRC interrupt, ret=%d",
>  				    ret);
>  			goto done;
>  		}
>  		chip->intr_comp_chng = true;
> +		chip->intr_bc_lvl = false;
>  		break;
>  	case TYPEC_CC_RD:
>  		ret = fusb302_i2c_mask_write(chip, FUSB_REG_MASK,
>  					     FUSB_REG_MASK_BC_LVL |
>  					     FUSB_REG_MASK_COMP_CHNG,
> -					     FUSB_REG_MASK_BC_LVL);
> +					     FUSB_REG_MASK_COMP_CHNG);
>  		if (ret < 0) {
>  			fusb302_log(chip, "cannot set SRC interrupt, ret=%d",
>  				    ret);
>  			goto done;
>  		}
>  		chip->intr_bc_lvl = true;
> +		chip->intr_comp_chng = false;
>  		break;
>  	default:
>  		break;

thanks,
Heikki Krogerus Nov. 15, 2021, 7:15 a.m. UTC | #3
On Mon, Nov 15, 2021 at 09:11:48AM +0200, Heikki Krogerus wrote:
> On Mon, Nov 08, 2021 at 11:28:32AM +0100, Ondrej Jirman wrote:
> > The code that enables either BC_LVL or COMP_CHNG interrupt in tcpm_set_cc
> > wrongly assumes that the interrupt is unmasked by writing 1 to the apropriate
> > bit in the mask register. In fact, interrupts are enabled when the mask
> > is 0, so the tcpm_set_cc enables interrupt for COMP_CHNG when it expects
> > BC_LVL interrupt to be enabled.
> > 
> > This causes inability of the driver to recognize cable unplug events
> > in host mode (unplug is recognized only via a COMP_CHNG interrupt).
> > 
> > In device mode this bug was masked by simultaneous triggering of the VBUS
> > change interrupt, because of loss of VBUS when the port peer is providing
> > power.
> > 
> > Fixes: 48242e30532b ("usb: typec: fusb302: Revert "Resolve fixed power role contract setup"")
> > Signed-off-by: Ondrej Jirman <megous@megous.com>
> > Cc: Hans de Goede <hdegoede@redhat.com>
> 
> Should this go to stable?
> 
> Acked-by: Heikki Krogerus@linux.intel.com

Sorry, that's wrong...

Acked-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>


> > ---
> >  drivers/usb/typec/tcpm/fusb302.c | 6 ++++--
> >  1 file changed, 4 insertions(+), 2 deletions(-)
> > 
> > diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c
> > index 7a2a17866a823..72f9001b07921 100644
> > --- a/drivers/usb/typec/tcpm/fusb302.c
> > +++ b/drivers/usb/typec/tcpm/fusb302.c
> > @@ -669,25 +669,27 @@ static int tcpm_set_cc(struct tcpc_dev *dev, enum typec_cc_status cc)
> >  		ret = fusb302_i2c_mask_write(chip, FUSB_REG_MASK,
> >  					     FUSB_REG_MASK_BC_LVL |
> >  					     FUSB_REG_MASK_COMP_CHNG,
> > -					     FUSB_REG_MASK_COMP_CHNG);
> > +					     FUSB_REG_MASK_BC_LVL);
> >  		if (ret < 0) {
> >  			fusb302_log(chip, "cannot set SRC interrupt, ret=%d",
> >  				    ret);
> >  			goto done;
> >  		}
> >  		chip->intr_comp_chng = true;
> > +		chip->intr_bc_lvl = false;
> >  		break;
> >  	case TYPEC_CC_RD:
> >  		ret = fusb302_i2c_mask_write(chip, FUSB_REG_MASK,
> >  					     FUSB_REG_MASK_BC_LVL |
> >  					     FUSB_REG_MASK_COMP_CHNG,
> > -					     FUSB_REG_MASK_BC_LVL);
> > +					     FUSB_REG_MASK_COMP_CHNG);
> >  		if (ret < 0) {
> >  			fusb302_log(chip, "cannot set SRC interrupt, ret=%d",
> >  				    ret);
> >  			goto done;
> >  		}
> >  		chip->intr_bc_lvl = true;
> > +		chip->intr_comp_chng = false;
> >  		break;
> >  	default:
> >  		break;
> 
> thanks,
Ondřej Jirman Nov. 15, 2021, 8:55 a.m. UTC | #4
On Mon, Nov 15, 2021 at 09:11:42AM +0200, Heikki Krogerus wrote:
> On Mon, Nov 08, 2021 at 11:28:32AM +0100, Ondrej Jirman wrote:
> > The code that enables either BC_LVL or COMP_CHNG interrupt in tcpm_set_cc
> > wrongly assumes that the interrupt is unmasked by writing 1 to the apropriate
> > bit in the mask register. In fact, interrupts are enabled when the mask
> > is 0, so the tcpm_set_cc enables interrupt for COMP_CHNG when it expects
> > BC_LVL interrupt to be enabled.
> > 
> > This causes inability of the driver to recognize cable unplug events
> > in host mode (unplug is recognized only via a COMP_CHNG interrupt).
> > 
> > In device mode this bug was masked by simultaneous triggering of the VBUS
> > change interrupt, because of loss of VBUS when the port peer is providing
> > power.
> > 
> > Fixes: 48242e30532b ("usb: typec: fusb302: Revert "Resolve fixed power role contract setup"")
> > Signed-off-by: Ondrej Jirman <megous@megous.com>
> > Cc: Hans de Goede <hdegoede@redhat.com>
> 
> Should this go to stable?

Without this patch, VBUS is not turned off when I disconnect a hub from the
Type-C port (because fusb302 will not notice the disconnect), and it stays on
until next plugin of some device, say a normal non PD charger.

So I guess for a brief period you can have both sides provide VBUS (until
fusb302/tcpm processes the next plugin). It may be a problem if VBUS was more
than 5V (not very likely for devices running this driver, I guess).

regards,
	o.

> Acked-by: Heikki Krogerus@linux.intel.com
> 
> > ---
> >  drivers/usb/typec/tcpm/fusb302.c | 6 ++++--
> >  1 file changed, 4 insertions(+), 2 deletions(-)
> > 
> > diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c
> > index 7a2a17866a823..72f9001b07921 100644
> > --- a/drivers/usb/typec/tcpm/fusb302.c
> > +++ b/drivers/usb/typec/tcpm/fusb302.c
> > @@ -669,25 +669,27 @@ static int tcpm_set_cc(struct tcpc_dev *dev, enum typec_cc_status cc)
> >  		ret = fusb302_i2c_mask_write(chip, FUSB_REG_MASK,
> >  					     FUSB_REG_MASK_BC_LVL |
> >  					     FUSB_REG_MASK_COMP_CHNG,
> > -					     FUSB_REG_MASK_COMP_CHNG);
> > +					     FUSB_REG_MASK_BC_LVL);
> >  		if (ret < 0) {
> >  			fusb302_log(chip, "cannot set SRC interrupt, ret=%d",
> >  				    ret);
> >  			goto done;
> >  		}
> >  		chip->intr_comp_chng = true;
> > +		chip->intr_bc_lvl = false;
> >  		break;
> >  	case TYPEC_CC_RD:
> >  		ret = fusb302_i2c_mask_write(chip, FUSB_REG_MASK,
> >  					     FUSB_REG_MASK_BC_LVL |
> >  					     FUSB_REG_MASK_COMP_CHNG,
> > -					     FUSB_REG_MASK_BC_LVL);
> > +					     FUSB_REG_MASK_COMP_CHNG);
> >  		if (ret < 0) {
> >  			fusb302_log(chip, "cannot set SRC interrupt, ret=%d",
> >  				    ret);
> >  			goto done;
> >  		}
> >  		chip->intr_bc_lvl = true;
> > +		chip->intr_comp_chng = false;
> >  		break;
> >  	default:
> >  		break;
> 
> thanks,
> 
> -- 
> heikki
Heikki Krogerus Nov. 15, 2021, 9:21 a.m. UTC | #5
On Mon, Nov 15, 2021 at 09:55:32AM +0100, Ondřej Jirman wrote:
> On Mon, Nov 15, 2021 at 09:11:42AM +0200, Heikki Krogerus wrote:
> > On Mon, Nov 08, 2021 at 11:28:32AM +0100, Ondrej Jirman wrote:
> > > The code that enables either BC_LVL or COMP_CHNG interrupt in tcpm_set_cc
> > > wrongly assumes that the interrupt is unmasked by writing 1 to the apropriate
> > > bit in the mask register. In fact, interrupts are enabled when the mask
> > > is 0, so the tcpm_set_cc enables interrupt for COMP_CHNG when it expects
> > > BC_LVL interrupt to be enabled.
> > > 
> > > This causes inability of the driver to recognize cable unplug events
> > > in host mode (unplug is recognized only via a COMP_CHNG interrupt).
> > > 
> > > In device mode this bug was masked by simultaneous triggering of the VBUS
> > > change interrupt, because of loss of VBUS when the port peer is providing
> > > power.
> > > 
> > > Fixes: 48242e30532b ("usb: typec: fusb302: Revert "Resolve fixed power role contract setup"")
> > > Signed-off-by: Ondrej Jirman <megous@megous.com>
> > > Cc: Hans de Goede <hdegoede@redhat.com>
> > 
> > Should this go to stable?
> 
> Without this patch, VBUS is not turned off when I disconnect a hub from the
> Type-C port (because fusb302 will not notice the disconnect), and it stays on
> until next plugin of some device, say a normal non PD charger.
> 
> So I guess for a brief period you can have both sides provide VBUS (until
> fusb302/tcpm processes the next plugin). It may be a problem if VBUS was more
> than 5V (not very likely for devices running this driver, I guess).

OK. So this should find it's way to the stable kernel releases as
well, and it should have the appropriate "Cc: stable@vger.kernel.org"
tag:

https://www.kernel.org/doc/html/latest/process/submitting-patches.html#select-the-recipients-for-your-patch

thanks,
diff mbox series

Patch

diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c
index 7a2a17866a823..72f9001b07921 100644
--- a/drivers/usb/typec/tcpm/fusb302.c
+++ b/drivers/usb/typec/tcpm/fusb302.c
@@ -669,25 +669,27 @@  static int tcpm_set_cc(struct tcpc_dev *dev, enum typec_cc_status cc)
 		ret = fusb302_i2c_mask_write(chip, FUSB_REG_MASK,
 					     FUSB_REG_MASK_BC_LVL |
 					     FUSB_REG_MASK_COMP_CHNG,
-					     FUSB_REG_MASK_COMP_CHNG);
+					     FUSB_REG_MASK_BC_LVL);
 		if (ret < 0) {
 			fusb302_log(chip, "cannot set SRC interrupt, ret=%d",
 				    ret);
 			goto done;
 		}
 		chip->intr_comp_chng = true;
+		chip->intr_bc_lvl = false;
 		break;
 	case TYPEC_CC_RD:
 		ret = fusb302_i2c_mask_write(chip, FUSB_REG_MASK,
 					     FUSB_REG_MASK_BC_LVL |
 					     FUSB_REG_MASK_COMP_CHNG,
-					     FUSB_REG_MASK_BC_LVL);
+					     FUSB_REG_MASK_COMP_CHNG);
 		if (ret < 0) {
 			fusb302_log(chip, "cannot set SRC interrupt, ret=%d",
 				    ret);
 			goto done;
 		}
 		chip->intr_bc_lvl = true;
+		chip->intr_comp_chng = false;
 		break;
 	default:
 		break;