[EXT] Re: [PATCH v2 3/4] usb: typec: mux: add typec orientation switch support via mux controller

Xu Yang xu.yang_2 at nxp.com
Thu Aug 25 02:37:28 PDT 2022


Hi Peter,

> -----Original Message-----
> From: Peter Rosin <peda at axentia.se>
> Sent: Wednesday, August 24, 2022 7:51 PM
> To: Xu Yang <xu.yang_2 at nxp.com>; heikki.krogerus at linux.intel.com; robh+dt at kernel.org; shawnguo at kernel.org
> Cc: gregkh at linuxfoundation.org; linux at roeck-us.net; Jun Li <jun.li at nxp.com>; linux-usb at vger.kernel.org; dl-linux-imx
> <linux-imx at nxp.com>; devicetree at vger.kernel.org; linux-arm-kernel at lists.infradead.org
> Subject: [EXT] Re: [PATCH v2 3/4] usb: typec: mux: add typec orientation switch support via mux controller
> 
> Caution: EXT Email
> 
> Hi!
> 
> 2022-08-23 at 21:54, Xu Yang wrote:
> > Some dedicated mux block can use existing mux controller as a mux
> > provider, typec port as a consumer to select channel for orientation
> > switch, this can be an alternate way to control typec orientation switch.
> > Also, one mux controller could cover highspeed, superspeed and sideband
> > use case one time in this implementation.
> >
> > Reported-by: kernel test robot <lkp at intel.com>
> > Signed-off-by: Xu Yang <xu.yang_2 at nxp.com>
> >
> > ---
> > Changes since v1:
> > - add build dependence (select MULTIPLEXER)
> > - improve Multiplexer control logic
> >
> >  drivers/usb/typec/Kconfig     |  1 +
> >  drivers/usb/typec/mux.c       | 76 ++++++++++++++++++++++++++++++++++-
> >  include/linux/usb/typec_mux.h |  7 +---
> >  3 files changed, 78 insertions(+), 6 deletions(-)
> >
> > diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
> > index 5defdfead653..73d4976d8148 100644
> > --- a/drivers/usb/typec/Kconfig
> > +++ b/drivers/usb/typec/Kconfig
> > @@ -2,6 +2,7 @@
> >
> >  menuconfig TYPEC
> >       tristate "USB Type-C Support"
> > +     select MULTIPLEXER
> >       help
> >         USB Type-C Specification defines a cable and connector for USB where
> >         only one type of plug is supported on both ends, i.e. there will not
> > diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c
> > index 464330776cd6..05e6ed217620 100644
> > --- a/drivers/usb/typec/mux.c
> > +++ b/drivers/usb/typec/mux.c
> > @@ -13,6 +13,7 @@
> >  #include <linux/mutex.h>
> >  #include <linux/property.h>
> >  #include <linux/slab.h>
> > +#include <linux/mux/consumer.h>
> >
> >  #include "class.h"
> >  #include "mux.h"
> > @@ -22,6 +23,11 @@
> >  struct typec_switch {
> >       struct typec_switch_dev *sw_devs[TYPEC_MUX_MAX_DEVS];
> >       unsigned int num_sw_devs;
> > +
> > +     /* Could handle HighSpeed, SuperSpeed, Sideband switch one time */
> > +     struct mux_control *mux_switch;
> > +     /* 3 state correspond to NONE, NORMAL, REVERSE for all switches */
> > +     int mux_states[3];
> >  };
> >
> >  static int switch_fwnode_match(struct device *dev, const void *fwnode)
> > @@ -117,6 +123,58 @@ struct typec_switch *fwnode_typec_switch_get(struct fwnode_handle *fwnode)
> >  }
> >  EXPORT_SYMBOL_GPL(fwnode_typec_switch_get);
> >
> > +static struct typec_switch *mux_control_typec_switch_get(struct device *dev)
> > +{
> > +     struct typec_switch *sw;
> > +     struct mux_control *mux;
> > +     int ret;
> > +
> > +     if (!device_property_present(dev, "mux-controls"))
> > +             return NULL;
> > +
> > +     sw = kzalloc(sizeof(*sw), GFP_KERNEL);
> > +     if (!sw)
> > +             return ERR_PTR(-ENOMEM);
> > +
> > +     mux = mux_control_get(dev, NULL);
> > +     if (!IS_ERR(mux)) {
> > +             sw->mux_switch = mux;
> > +             ret = device_property_read_u32_array(dev,
> > +                     "typec-switch-states", sw->mux_states, 3);
> > +             if (ret) {
> > +                     kfree(sw);
> > +                     return ERR_PTR(ret);
> > +             }
> > +     } else {
> > +             kfree(sw);
> > +             return ERR_CAST(mux);
> > +     }
> > +
> > +     return sw;
> > +}
> > +
> > +/**
> > + * typec_switch_get - Find USB Type-C orientation switch
> > + * @dev: The device using switch
> > + *
> > + * Finds a switch used by @dev. Returns a reference to the switch on
> > + * success, NULL if no matching connection was found, or
> > + * ERR_PTR(-EPROBE_DEFER) when a connection was found but the switch
> > + * has not been enumerated yet, or ERR_PTR with a negative errno.
> > + */
> > +struct typec_switch *typec_switch_get(struct device *dev)
> > +{
> > +     struct typec_switch *sw;
> > +
> > +     sw = fwnode_typec_switch_get(dev_fwnode(dev));
> > +     if (!sw)
> > +             /* Try get switch based on mux control */
> > +             sw = mux_control_typec_switch_get(dev);
> > +
> > +     return sw;
> > +}
> > +EXPORT_SYMBOL_GPL(typec_switch_get);
> > +
> >  /**
> >   * typec_switch_put - Release USB Type-C orientation switch
> >   * @sw: USB Type-C orientation switch
> > @@ -137,6 +195,10 @@ void typec_switch_put(struct typec_switch *sw)
> >               module_put(sw_dev->dev.parent->driver->owner);
> >               put_device(&sw_dev->dev);
> >       }
> > +
> > +     if (sw->mux_switch)
> > +             mux_control_put(sw->mux_switch);
> > +
> >       kfree(sw);
> >  }
> >  EXPORT_SYMBOL_GPL(typec_switch_put);
> > @@ -204,6 +266,7 @@ int typec_switch_set(struct typec_switch *sw,
> >                    enum typec_orientation orientation)
> >  {
> >       struct typec_switch_dev *sw_dev;
> > +     struct mux_control *mux;
> >       unsigned int i;
> >       int ret;
> >
> > @@ -218,7 +281,18 @@ int typec_switch_set(struct typec_switch *sw,
> >                       return ret;
> >       }
> >
> > -     return 0;
> > +     mux = sw->mux_switch;
> > +     if (!mux)
> > +             return 0;
> > +
> > +     ret = mux_control_select(mux, sw->mux_states[orientation]);
> > +     if (ret)
> > +             return ret;
> > +
> > +     /* Keep it as it is since idle_state is MUX_IDLE_AS_IS */
> > +     ret = mux_control_deselect(mux);
> 
> No, this is also broken. You cannot, in any client driver, rely on a
> mux keeping its state unless you keep it selected. As soon as you
> deselect it, it might be selected by some other driver. Sure, you
> might know that there are no other users of the mux in question, and
> you might also know that the idles state is "as-is". But the driver
> does not see the bigger picture and has no way of knowing that. So,
> it needs to keep the mux selected.
> 

Understood.  May be a flag is needed like mdio-mux-multiplexer does.
I will improve it in next version.

Thanks,
Xu Yang

> Cheers,
> Peter
> 
> > +
> > +     return ret;
> >  }
> >  EXPORT_SYMBOL_GPL(typec_switch_set);
> >
> > diff --git a/include/linux/usb/typec_mux.h b/include/linux/usb/typec_mux.h
> > index 9292f0e07846..2287e5a5f591 100644
> > --- a/include/linux/usb/typec_mux.h
> > +++ b/include/linux/usb/typec_mux.h
> > @@ -24,16 +24,13 @@ struct typec_switch_desc {
> >       void *drvdata;
> >  };
> >
> > +
> > +struct typec_switch *typec_switch_get(struct device *dev);
> >  struct typec_switch *fwnode_typec_switch_get(struct fwnode_handle *fwnode);
> >  void typec_switch_put(struct typec_switch *sw);
> >  int typec_switch_set(struct typec_switch *sw,
> >                    enum typec_orientation orientation);
> >
> > -static inline struct typec_switch *typec_switch_get(struct device *dev)
> > -{
> > -     return fwnode_typec_switch_get(dev_fwnode(dev));
> > -}
> > -
> >  struct typec_switch_dev *
> >  typec_switch_register(struct device *parent,
> >                     const struct typec_switch_desc *desc);


More information about the linux-arm-kernel mailing list