mbox series

[0/4] typec switch via mux controller

Message ID 1621408490-23811-1-git-send-email-jun.li@nxp.com
Headers show
Series typec switch via mux controller | expand

Message

Jun Li May 19, 2021, 7:14 a.m. UTC
This is a follow up patch set to enable typec orientation swtich
via a simple HW block(e.g. controlled by GPIOs), the implementation
is based on Rob's commment to use existing mux controller bindings,
typec port directly use mux controller consumer API, typec_switch
struct is not used.

Last discussion is here:
https://www.spinics.net/lists/linux-usb/msg205492.html

Li Jun (4):
  dt-bindings: connector: Add typec orientation switch properties
  usb: typec: use typec cap fwnode's of_node for typec port
  usb: typec: add typec orientation switch support via mux controller
  arm64: dts: imx8mp-evk: enable usb0 with typec connector

 .../bindings/connector/usb-connector.yaml     |  21 ++++
 arch/arm64/boot/dts/freescale/imx8mp-evk.dts  | 110 ++++++++++++++++++
 drivers/usb/typec/class.c                     |  28 ++++-
 drivers/usb/typec/class.h                     |   2 +
 drivers/usb/typec/mux.c                       |  34 ++++++
 include/linux/usb/typec_mux.h                 |   4 +
 6 files changed, 198 insertions(+), 1 deletion(-)

Comments

Heikki Krogerus May 20, 2021, 12:33 p.m. UTC | #1
On Wed, May 19, 2021 at 03:14:49PM +0800, Li Jun 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 current

> typec_switch interface.

> 

> Signed-off-by: Li Jun <jun.li@nxp.com>

> ---

>  drivers/usb/typec/class.c     | 26 +++++++++++++++++++++++++-

>  drivers/usb/typec/class.h     |  2 ++

>  drivers/usb/typec/mux.c       | 34 ++++++++++++++++++++++++++++++++++

>  include/linux/usb/typec_mux.h |  4 ++++

>  4 files changed, 65 insertions(+), 1 deletion(-)

> 

> diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c

> index a29bf2c32233..1bb0275e6204 100644

> --- a/drivers/usb/typec/class.c

> +++ b/drivers/usb/typec/class.c

> @@ -1601,6 +1601,7 @@ static void typec_release(struct device *dev)

>  	ida_simple_remove(&typec_index_ida, port->id);

>  	ida_destroy(&port->mode_ids);

>  	typec_switch_put(port->sw);

> +	typec_mux_control_switch_put(port->mux_control_switch);

>  	typec_mux_put(port->mux);

>  	kfree(port->cap);

>  	kfree(port);

> @@ -1816,6 +1817,13 @@ int typec_set_orientation(struct typec_port *port,

>  	if (ret)

>  		return ret;

>  

> +	if (!port->sw) {

> +		ret = typec_mux_control_switch_set(port->mux_control_switch,

> +				port->mux_control_switch_states[orientation]);

> +		if (ret)

> +			return ret;

> +	}

> +

>  	port->orientation = orientation;

>  	sysfs_notify(&port->dev.kobj, NULL, "orientation");

>  	kobject_uevent(&port->dev.kobj, KOBJ_CHANGE);

> @@ -1991,7 +1999,7 @@ struct typec_port *typec_register_port(struct device *parent,

>  				       const struct typec_capability *cap)

>  {

>  	struct typec_port *port;

> -	int ret;

> +	int ret = 0;

>  	int id;

>  

>  	port = kzalloc(sizeof(*port), GFP_KERNEL);

> @@ -2068,6 +2076,22 @@ struct typec_port *typec_register_port(struct device *parent,

>  		return ERR_PTR(ret);

>  	}

>  

> +	if (!port->sw) {

> +		/* Try to get typec switch via general mux controller */

> +		port->mux_control_switch = typec_mux_control_switch_get(&port->dev);

> +		if (IS_ERR(port->mux_control_switch))

> +			ret = PTR_ERR(port->mux_control_switch);

> +		else if (port->mux_control_switch)

> +			ret = device_property_read_u32_array(&port->dev,

> +					"mux-control-switch-states",

> +					port->mux_control_switch_states,

> +					3);

> +		if (ret) {

> +			put_device(&port->dev);

> +			return ERR_PTR(ret);

> +		}

> +	}


Why not just do that inside fwnode_typec_switch_get() and handle the
whole thing in drivers/usb/typec/mux.c (or in its own file if you
prefer)?

You'll just need to register a "wrapper" Type-C switch object for the
OF mux controller, but that should not be a problem. That way you
don't need to export any new functions, touch this file or anything
else.


thanks,

-- 
heikki
Rob Herring (Arm) May 21, 2021, 1:30 a.m. UTC | #2
On Wed, May 19, 2021 at 03:14:47PM +0800, Li Jun wrote:
> Typec orientation switch can be implementaed as a consumer of mux

> controller, with this way, mux-control-name must be provided with

> name "typec-orientation-switch", along with its 3 states value array

> for none(high impedance), cc1, cc2.

> 

> Signed-off-by: Li Jun <jun.li@nxp.com>

> ---

>  .../bindings/connector/usb-connector.yaml     | 21 +++++++++++++++++++

>  1 file changed, 21 insertions(+)

> 

> diff --git a/Documentation/devicetree/bindings/connector/usb-connector.yaml b/Documentation/devicetree/bindings/connector/usb-connector.yaml

> index 32509b98142e..567183e199a3 100644

> --- a/Documentation/devicetree/bindings/connector/usb-connector.yaml

> +++ b/Documentation/devicetree/bindings/connector/usb-connector.yaml

> @@ -111,6 +111,24 @@ properties:

>        - 1.5A

>        - 3.0A

>  

> +  mux-controls:

> +    description:

> +      mux controller node to use for orientation switch selection.

> +    maxItems: 1

> +

> +  mux-control-name:

> +    items:

> +      - const: typec-orientation-switch


Don't really need a name with only 1 entry.

> +

> +  mux-control-switch-states:


Not really part of the 'mux-control' binding, but part of the connector. 
So 'typec-orientation-switch-states' or something.

> +    description: |

> +      An ordered u32 array describing the mux state value for each typec

> +      orientations: NONE(high impedance), CC1, CC2, if there is no HW mux

> +      state for NONE, use value of CC1 or CC2 for it,

> +    minItems: 3

> +    maxItems: 3

> +    $ref: /schemas/types.yaml#/definitions/uint32-array

> +

>    # The following are optional properties for "usb-c-connector" with power

>    # delivery support.

>    source-pdos:

> @@ -301,6 +319,9 @@ examples:

>          sink-pdos = <PDO_FIXED(5000, 2000, PDO_FIXED_USB_COMM)

>                       PDO_VAR(5000, 12000, 2000)>;

>          op-sink-microwatt = <10000000>;

> +        mux-controls = <&mux>;

> +        mux-control-names = "typec-orientation-switch";

> +        mux-control-switch-states = <2>, <0>, <1>;

>        };

>      };

>  

> -- 

> 2.25.1

>
Heikki Krogerus May 21, 2021, 8:37 a.m. UTC | #3
Hi,

On Thu, May 20, 2021 at 03:33:36PM +0300, Heikki Krogerus wrote:
> Why not just do that inside fwnode_typec_switch_get() and handle the

> whole thing in drivers/usb/typec/mux.c (or in its own file if you

> prefer)?

> 

> You'll just need to register a "wrapper" Type-C switch object for the

> OF mux controller, but that should not be a problem. That way you

> don't need to export any new functions, touch this file or anything

> else.


I wrote a bit of code just to see how that would look. I'm attaching
you the hack I made. I guess something like that would not be too bad.
A wrapper is probable always a bit clumsy, but I'm not sure that in
this case it's a huge problem. Of course if there are any better
ideas, let's here them :-)


thanks,

-- 
heikki
From bdd63f82788fe95e056ed85ece939e41cfb862ad Mon Sep 17 00:00:00 2001
From: Heikki Krogerus <heikki.krogerus@linux.intel.com>

Date: Fri, 21 May 2021 10:42:23 +0300
Subject: [PATCH] usb: typec: mux: Add wrapper for OF mux controllers that
 handle orientation

Interim. Experiment only.

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

---
 drivers/usb/typec/Makefile |  1 +
 drivers/usb/typec/mux.c    | 13 +++--
 drivers/usb/typec/mux.h    | 15 ++++++
 drivers/usb/typec/of_mux.c | 97 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 122 insertions(+), 4 deletions(-)
 create mode 100644 drivers/usb/typec/of_mux.c

diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
index a0adb8947a301..d85231b2fe10b 100644
--- a/drivers/usb/typec/Makefile
+++ b/drivers/usb/typec/Makefile
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 obj-$(CONFIG_TYPEC)		+= typec.o
 typec-y				:= class.o mux.o bus.o port-mapper.o
+typec-$(MULTIPLEXER)		+= of_mux.o
 obj-$(CONFIG_TYPEC)		+= altmodes/
 obj-$(CONFIG_TYPEC_TCPM)	+= tcpm/
 obj-$(CONFIG_TYPEC_UCSI)	+= ucsi/
diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c
index 9da22ae3006c9..282622276d97b 100644
--- a/drivers/usb/typec/mux.c
+++ b/drivers/usb/typec/mux.c
@@ -63,6 +63,9 @@ struct typec_switch *fwnode_typec_switch_get(struct fwnode_handle *fwnode)
 
 	sw = fwnode_connection_find_match(fwnode, "orientation-switch", NULL,
 					  typec_switch_match);
+	if (!sw)
+		sw = of_switch_register(fwnode);
+
 	if (!IS_ERR_OR_NULL(sw))
 		WARN_ON(!try_module_get(sw->dev.parent->driver->owner));
 
@@ -78,10 +81,12 @@ EXPORT_SYMBOL_GPL(fwnode_typec_switch_get);
  */
 void typec_switch_put(struct typec_switch *sw)
 {
-	if (!IS_ERR_OR_NULL(sw)) {
-		module_put(sw->dev.parent->driver->owner);
-		put_device(&sw->dev);
-	}
+	if (IS_ERR_OR_NULL(sw))
+		return;
+
+	module_put(sw->dev.parent->driver->owner);
+	of_switch_unregister(sw);
+	put_device(&sw->dev);
 }
 EXPORT_SYMBOL_GPL(typec_switch_put);
 
diff --git a/drivers/usb/typec/mux.h b/drivers/usb/typec/mux.h
index 4fd9426ee44f6..c99caab766313 100644
--- a/drivers/usb/typec/mux.h
+++ b/drivers/usb/typec/mux.h
@@ -5,8 +5,11 @@
 
 #include <linux/usb/typec_mux.h>
 
+struct of_switch;
+
 struct typec_switch {
 	struct device dev;
+	struct of_switch *osw;
 	typec_switch_set_fn_t set;
 };
 
@@ -18,4 +21,16 @@ struct typec_mux {
 #define to_typec_switch(_dev_) container_of(_dev_, struct typec_switch, dev)
 #define to_typec_mux(_dev_) container_of(_dev_, struct typec_mux, dev)
 
+#ifdef CONFIG_MULTIPLEXER
+struct typec_switch *of_switch_register(struct fwnode_handle *fwnode);
+void of_switch_unregister(struct typec_switch *sw);
+#else
+static inline struct typec_switch *of_switch_register(struct fwnode_handle *fwnode)
+{
+	return NULL;
+}
+
+static inline void of_switch_unregister(struct typec_switch *sw) { }
+#endif /* MULTIPLEXER */
+
 #endif /* __USB_TYPEC_MUX__ */
diff --git a/drivers/usb/typec/of_mux.c b/drivers/usb/typec/of_mux.c
new file mode 100644
index 0000000000000..48686a92331d7
--- /dev/null
+++ b/drivers/usb/typec/of_mux.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Wrapper for mux controllers handling orientation
+ *
+ * Copyright (C) 2021 Intel Corporation
+ */
+
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/mux/consumer.h>
+#include <linux/usb/typec_mux.h>
+
+#include "mux.h"
+
+struct of_switch {
+	struct mux_control *mc;
+	unsigned int state[3];
+};
+
+static int of_switch_set(struct typec_switch *sw, enum typec_orientation orientation)
+{
+	int ret;
+
+	/* Checking has the switch been unregistered - just not released yet */
+	if (!sw->osw)
+		return -ENODEV;
+
+	ret = mux_control_deselect(sw->osw->mc);
+	if (ret)
+		return ret;
+
+	return mux_control_select(sw->osw->mc, sw->osw->state[orientation]);
+}
+
+struct typec_switch *of_switch_register(struct fwnode_handle *fwnode)
+{
+	struct typec_switch_desc desc;
+	struct typec_switch *sw;
+	struct mux_control *mc;
+	unsigned int state[3];
+	struct of_switch *osw;
+	int ret;
+
+	if (!fwnode_property_present(fwnode, "mux-control-names"))
+		return NULL;
+
+	ret = fwnode_property_read_u32_array(fwnode, "mux-control-switch-states",
+					     state, 3);
+	if (ret)
+		return ERR_PTR(ret);
+
+	desc.fwnode = fwnode;
+	desc.set = of_switch_set;
+	desc.name = fwnode_get_name(fwnode);
+	desc.drvdata = NULL;
+
+	sw = typec_switch_register(NULL, &desc);
+	if (IS_ERR(sw))
+		return sw;
+
+	sw->dev.of_node = to_of_node(fwnode);
+
+	mc = mux_control_get(&sw->dev, "typec-orientation-switch");
+	if (IS_ERR_OR_NULL(mc)) {
+		typec_switch_unregister(sw);
+		if (IS_ERR(mc))
+			return ERR_CAST(mc);
+		return ERR_PTR(-ENODEV);
+	}
+
+	osw = kzalloc(sizeof(osw), GFP_KERNEL);
+	if (!osw) {
+		typec_switch_unregister(sw);
+		mux_control_put(mc);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	memcpy(osw->state, state, sizeof(unsigned int) * 3);
+	osw->mc = mc;
+	sw->osw = osw;
+
+	return sw;
+}
+
+void of_switch_unregister(struct typec_switch *sw)
+{
+	struct of_switch *osw = sw->osw;
+
+	if (!osw)
+		return;
+
+	sw->osw = NULL;
+	typec_switch_unregister(sw);
+	mux_control_put(osw->mc);
+	kfree(osw);
+}
-- 
2.30.2
Jun Li May 21, 2021, 1:02 p.m. UTC | #4
> -----Original Message-----
> From: Heikki Krogerus <heikki.krogerus@linux.intel.com>
> Sent: Thursday, May 20, 2021 8:34 PM
> To: Jun Li <jun.li@nxp.com>
> Cc: robh+dt@kernel.org; shawnguo@kernel.org; gregkh@linuxfoundation.org;
> linux@roeck-us.net; linux-usb@vger.kernel.org; dl-linux-imx
> <linux-imx@nxp.com>; devicetree@vger.kernel.org;
> linux-arm-kernel@lists.infradead.org
> Subject: Re: [PATCH 3/4] usb: typec: add typec orientation switch support
> via mux controller
> 
> On Wed, May 19, 2021 at 03:14:49PM +0800, Li Jun 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 current typec_switch
> > interface.
> >
> > Signed-off-by: Li Jun <jun.li@nxp.com>
> > ---
> >  drivers/usb/typec/class.c     | 26 +++++++++++++++++++++++++-
> >  drivers/usb/typec/class.h     |  2 ++
> >  drivers/usb/typec/mux.c       | 34 ++++++++++++++++++++++++++++++++++
> >  include/linux/usb/typec_mux.h |  4 ++++
> >  4 files changed, 65 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
> > index a29bf2c32233..1bb0275e6204 100644
> > --- a/drivers/usb/typec/class.c
> > +++ b/drivers/usb/typec/class.c
> > @@ -1601,6 +1601,7 @@ static void typec_release(struct device *dev)
> >  	ida_simple_remove(&typec_index_ida, port->id);
> >  	ida_destroy(&port->mode_ids);
> >  	typec_switch_put(port->sw);
> > +	typec_mux_control_switch_put(port->mux_control_switch);
> >  	typec_mux_put(port->mux);
> >  	kfree(port->cap);
> >  	kfree(port);
> > @@ -1816,6 +1817,13 @@ int typec_set_orientation(struct typec_port *port,
> >  	if (ret)
> >  		return ret;
> >
> > +	if (!port->sw) {
> > +		ret = typec_mux_control_switch_set(port->mux_control_switch,
> > +				port->mux_control_switch_states[orientation]);
> > +		if (ret)
> > +			return ret;
> > +	}
> > +
> >  	port->orientation = orientation;
> >  	sysfs_notify(&port->dev.kobj, NULL, "orientation");
> >  	kobject_uevent(&port->dev.kobj, KOBJ_CHANGE); @@ -1991,7 +1999,7 @@
> > struct typec_port *typec_register_port(struct device *parent,
> >  				       const struct typec_capability *cap)  {
> >  	struct typec_port *port;
> > -	int ret;
> > +	int ret = 0;
> >  	int id;
> >
> >  	port = kzalloc(sizeof(*port), GFP_KERNEL); @@ -2068,6 +2076,22 @@
> > struct typec_port *typec_register_port(struct device *parent,
> >  		return ERR_PTR(ret);
> >  	}
> >
> > +	if (!port->sw) {
> > +		/* Try to get typec switch via general mux controller */
> > +		port->mux_control_switch =
> typec_mux_control_switch_get(&port->dev);
> > +		if (IS_ERR(port->mux_control_switch))
> > +			ret = PTR_ERR(port->mux_control_switch);
> > +		else if (port->mux_control_switch)
> > +			ret = device_property_read_u32_array(&port->dev,
> > +					"mux-control-switch-states",
> > +					port->mux_control_switch_states,
> > +					3);
> > +		if (ret) {
> > +			put_device(&port->dev);
> > +			return ERR_PTR(ret);
> > +		}
> > +	}
> 
> Why not just do that inside fwnode_typec_switch_get() and handle the whole
> thing in drivers/usb/typec/mux.c (or in its own file if you prefer)?
> 
> You'll just need to register a "wrapper" Type-C switch object for the OF
> mux controller, but that should not be a problem. That way you don't need
> to export any new functions, touch this file or anything else.
> 

Okay, so stick to current typec_switch is preferred, actually I hesitated
on this, I know that approach will have a unified interface, but with
the cost of creating it only for wrap.

My v2 will go with the direction you suggested.

Thanks
Li Jun

> 
> thanks,
> 
> --
> heikki
Jun Li May 25, 2021, 11:46 a.m. UTC | #5
Hi Heikki,

> -----Original Message-----

> From: Heikki Krogerus <heikki.krogerus@linux.intel.com>

> Sent: Friday, May 21, 2021 4:38 PM

> To: Jun Li <jun.li@nxp.com>

> Cc: robh+dt@kernel.org; shawnguo@kernel.org; gregkh@linuxfoundation.org;

> linux@roeck-us.net; linux-usb@vger.kernel.org; dl-linux-imx

> <linux-imx@nxp.com>; devicetree@vger.kernel.org;

> linux-arm-kernel@lists.infradead.org

> Subject: Re: [PATCH 3/4] usb: typec: add typec orientation switch support

> via mux controller

> 

> Hi,

> 

> On Thu, May 20, 2021 at 03:33:36PM +0300, Heikki Krogerus wrote:

> > Why not just do that inside fwnode_typec_switch_get() and handle the

> > whole thing in drivers/usb/typec/mux.c (or in its own file if you

> > prefer)?

> >

> > You'll just need to register a "wrapper" Type-C switch object for the

> > OF mux controller, but that should not be a problem. That way you

> > don't need to export any new functions, touch this file or anything

> > else.

> 

> I wrote a bit of code just to see how that would look. I'm attaching you

> the hack I made. I guess something like that would not be too bad.

> A wrapper is probable always a bit clumsy, but I'm not sure that in this

> case it's a huge problem. Of course if there are any better ideas, let's

> here them :-)


Thanks for your patch, I am pasting the patch as below.

seems we need consider more than that.

diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
index a0adb8947a301..d85231b2fe10b 100644
--- a/drivers/usb/typec/Makefile
+++ b/drivers/usb/typec/Makefile
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 obj-$(CONFIG_TYPEC)		+= typec.o
 typec-y				:= class.o mux.o bus.o port-mapper.o
+typec-$(MULTIPLEXER)		+= of_mux.o
 obj-$(CONFIG_TYPEC)		+= altmodes/
 obj-$(CONFIG_TYPEC_TCPM)	+= tcpm/
 obj-$(CONFIG_TYPEC_UCSI)	+= ucsi/
diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c
index 9da22ae3006c9..282622276d97b 100644
--- a/drivers/usb/typec/mux.c
+++ b/drivers/usb/typec/mux.c
@@ -63,6 +63,9 @@ struct typec_switch *fwnode_typec_switch_get(struct fwnode_handle *fwnode)
 
 	sw = fwnode_connection_find_match(fwnode, "orientation-switch", NULL,
 					  typec_switch_match);
+	if (!sw)
+		sw = of_switch_register(fwnode);
+

How to support multiple typec_switch_get() for of_mux case?
the second call to fwnode_typec_switch_get() will get the switch
via fwnode_connection_find_match()? This means we still need
a property "orientation-switch" for mux controller node, this
seems not the expected way for a mux consumer, even with this
property, we will get a EPROBE_DEFER for the first call.

If we use mux_control_get() for multiple caller/consumer, then
we need some other device as input.
  
typec_switch object looks to me is a provider, if we create
and maintain it in consumer side, I have not come out a better
way:-).

Thanks
Li Jun


 	if (!IS_ERR_OR_NULL(sw))
 		WARN_ON(!try_module_get(sw->dev.parent->driver->owner));
 
@@ -78,10 +81,12 @@ EXPORT_SYMBOL_GPL(fwnode_typec_switch_get);
  */
 void typec_switch_put(struct typec_switch *sw)
 {
-	if (!IS_ERR_OR_NULL(sw)) {
-		module_put(sw->dev.parent->driver->owner);
-		put_device(&sw->dev);
-	}
+	if (IS_ERR_OR_NULL(sw))
+		return;
+
+	module_put(sw->dev.parent->driver->owner);
+	of_switch_unregister(sw);
+	put_device(&sw->dev);
 }
 EXPORT_SYMBOL_GPL(typec_switch_put);
 
diff --git a/drivers/usb/typec/mux.h b/drivers/usb/typec/mux.h
index 4fd9426ee44f6..c99caab766313 100644
--- a/drivers/usb/typec/mux.h
+++ b/drivers/usb/typec/mux.h
@@ -5,8 +5,11 @@
 
 #include <linux/usb/typec_mux.h>
 
+struct of_switch;
+
 struct typec_switch {
 	struct device dev;
+	struct of_switch *osw;
 	typec_switch_set_fn_t set;
 };
 
@@ -18,4 +21,16 @@ struct typec_mux {
 #define to_typec_switch(_dev_) container_of(_dev_, struct typec_switch, dev)
 #define to_typec_mux(_dev_) container_of(_dev_, struct typec_mux, dev)
 
+#ifdef CONFIG_MULTIPLEXER
+struct typec_switch *of_switch_register(struct fwnode_handle *fwnode);
+void of_switch_unregister(struct typec_switch *sw);
+#else
+static inline struct typec_switch *of_switch_register(struct fwnode_handle *fwnode)
+{
+	return NULL;
+}
+
+static inline void of_switch_unregister(struct typec_switch *sw) { }
+#endif /* MULTIPLEXER */
+
 #endif /* __USB_TYPEC_MUX__ */
diff --git a/drivers/usb/typec/of_mux.c b/drivers/usb/typec/of_mux.c
new file mode 100644
index 0000000000000..48686a92331d7
--- /dev/null
+++ b/drivers/usb/typec/of_mux.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Wrapper for mux controllers handling orientation
+ *
+ * Copyright (C) 2021 Intel Corporation
+ */
+
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/mux/consumer.h>
+#include <linux/usb/typec_mux.h>
+
+#include "mux.h"
+
+struct of_switch {
+	struct mux_control *mc;
+	unsigned int state[3];
+};
+
+static int of_switch_set(struct typec_switch *sw, enum typec_orientation orientation)
+{
+	int ret;
+
+	/* Checking has the switch been unregistered - just not released yet */
+	if (!sw->osw)
+		return -ENODEV;
+
+	ret = mux_control_deselect(sw->osw->mc);
+	if (ret)
+		return ret;
+
+	return mux_control_select(sw->osw->mc, sw->osw->state[orientation]);
+}
+
+struct typec_switch *of_switch_register(struct fwnode_handle *fwnode)
+{
+	struct typec_switch_desc desc;
+	struct typec_switch *sw;
+	struct mux_control *mc;
+	unsigned int state[3];
+	struct of_switch *osw;
+	int ret;
+
+	if (!fwnode_property_present(fwnode, "mux-control-names"))
+		return NULL;
+
+	ret = fwnode_property_read_u32_array(fwnode, "mux-control-switch-states",
+					     state, 3);
+	if (ret)
+		return ERR_PTR(ret);
+
+	desc.fwnode = fwnode;
+	desc.set = of_switch_set;
+	desc.name = fwnode_get_name(fwnode);
+	desc.drvdata = NULL;
+
+	sw = typec_switch_register(NULL, &desc);
+	if (IS_ERR(sw))
+		return sw;
+
+	sw->dev.of_node = to_of_node(fwnode);
+
+	mc = mux_control_get(&sw->dev, "typec-orientation-switch");
+	if (IS_ERR_OR_NULL(mc)) {
+		typec_switch_unregister(sw);
+		if (IS_ERR(mc))
+			return ERR_CAST(mc);
+		return ERR_PTR(-ENODEV);
+	}
+
+	osw = kzalloc(sizeof(osw), GFP_KERNEL);
+	if (!osw) {
+		typec_switch_unregister(sw);
+		mux_control_put(mc);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	memcpy(osw->state, state, sizeof(unsigned int) * 3);
+	osw->mc = mc;
+	sw->osw = osw;
+
+	return sw;
+}
+
+void of_switch_unregister(struct typec_switch *sw)
+{
+	struct of_switch *osw = sw->osw;
+
+	if (!osw)
+		return;
+
+	sw->osw = NULL;
+	typec_switch_unregister(sw);
+	mux_control_put(osw->mc);
+	kfree(osw);
+}
-- 
2.30.2
> 

> 

> thanks,

> 

> --

> heikki
Jun Li May 25, 2021, 11:48 a.m. UTC | #6
Hi

> -----Original Message-----

> From: Rob Herring <robh@kernel.org>

> Sent: Friday, May 21, 2021 9:31 AM

> To: Jun Li <jun.li@nxp.com>

> Cc: heikki.krogerus@linux.intel.com; shawnguo@kernel.org;

> gregkh@linuxfoundation.org; linux@roeck-us.net;

> linux-usb@vger.kernel.org; dl-linux-imx <linux-imx@nxp.com>;

> devicetree@vger.kernel.org; linux-arm-kernel@lists.infradead.org

> Subject: Re: [PATCH 1/4] dt-bindings: connector: Add typec orientation

> switch properties

> 

> On Wed, May 19, 2021 at 03:14:47PM +0800, Li Jun wrote:

> > Typec orientation switch can be implementaed as a consumer of mux

> > controller, with this way, mux-control-name must be provided with name

> > "typec-orientation-switch", along with its 3 states value array for

> > none(high impedance), cc1, cc2.

> >

> > Signed-off-by: Li Jun <jun.li@nxp.com>

> > ---

> >  .../bindings/connector/usb-connector.yaml     | 21 +++++++++++++++++++

> >  1 file changed, 21 insertions(+)

> >

> > diff --git

> > a/Documentation/devicetree/bindings/connector/usb-connector.yaml

> > b/Documentation/devicetree/bindings/connector/usb-connector.yaml

> > index 32509b98142e..567183e199a3 100644

> > --- a/Documentation/devicetree/bindings/connector/usb-connector.yaml

> > +++ b/Documentation/devicetree/bindings/connector/usb-connector.yaml

> > @@ -111,6 +111,24 @@ properties:

> >        - 1.5A

> >        - 3.0A

> >

> > +  mux-controls:

> > +    description:

> > +      mux controller node to use for orientation switch selection.

> > +    maxItems: 1

> > +

> > +  mux-control-name:

> > +    items:

> > +      - const: typec-orientation-switch

> 

> Don't really need a name with only 1 entry.


Okay, will remove it.

> 

> > +

> > +  mux-control-switch-states:

> 

> Not really part of the 'mux-control' binding, but part of the connector.


Yes, agree.

> So 'typec-orientation-switch-states' or something.


will use typec-orientation-switch-states.

Thanks
Li Jun
> 

> > +    description: |

> > +      An ordered u32 array describing the mux state value for each typec

> > +      orientations: NONE(high impedance), CC1, CC2, if there is no HW mux

> > +      state for NONE, use value of CC1 or CC2 for it,

> > +    minItems: 3

> > +    maxItems: 3

> > +    $ref: /schemas/types.yaml#/definitions/uint32-array

> > +

> >    # The following are optional properties for "usb-c-connector" with power

> >    # delivery support.

> >    source-pdos:

> > @@ -301,6 +319,9 @@ examples:

> >          sink-pdos = <PDO_FIXED(5000, 2000, PDO_FIXED_USB_COMM)

> >                       PDO_VAR(5000, 12000, 2000)>;

> >          op-sink-microwatt = <10000000>;

> > +        mux-controls = <&mux>;

> > +        mux-control-names = "typec-orientation-switch";

> > +        mux-control-switch-states = <2>, <0>, <1>;

> >        };

> >      };

> >

> > --

> > 2.25.1

> >
Heikki Krogerus May 26, 2021, 9:16 a.m. UTC | #7
On Tue, May 25, 2021 at 11:46:18AM +0000, Jun Li wrote:
> Hi Heikki,

> 

> > -----Original Message-----

> > From: Heikki Krogerus <heikki.krogerus@linux.intel.com>

> > Sent: Friday, May 21, 2021 4:38 PM

> > To: Jun Li <jun.li@nxp.com>

> > Cc: robh+dt@kernel.org; shawnguo@kernel.org; gregkh@linuxfoundation.org;

> > linux@roeck-us.net; linux-usb@vger.kernel.org; dl-linux-imx

> > <linux-imx@nxp.com>; devicetree@vger.kernel.org;

> > linux-arm-kernel@lists.infradead.org

> > Subject: Re: [PATCH 3/4] usb: typec: add typec orientation switch support

> > via mux controller

> > 

> > Hi,

> > 

> > On Thu, May 20, 2021 at 03:33:36PM +0300, Heikki Krogerus wrote:

> > > Why not just do that inside fwnode_typec_switch_get() and handle the

> > > whole thing in drivers/usb/typec/mux.c (or in its own file if you

> > > prefer)?

> > >

> > > You'll just need to register a "wrapper" Type-C switch object for the

> > > OF mux controller, but that should not be a problem. That way you

> > > don't need to export any new functions, touch this file or anything

> > > else.

> > 

> > I wrote a bit of code just to see how that would look. I'm attaching you

> > the hack I made. I guess something like that would not be too bad.

> > A wrapper is probable always a bit clumsy, but I'm not sure that in this

> > case it's a huge problem. Of course if there are any better ideas, let's

> > here them :-)

> 

> Thanks for your patch, I am pasting the patch as below.

> 

> seems we need consider more than that.

> 

> diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile

> index a0adb8947a301..d85231b2fe10b 100644

> --- a/drivers/usb/typec/Makefile

> +++ b/drivers/usb/typec/Makefile

> @@ -1,6 +1,7 @@

>  # SPDX-License-Identifier: GPL-2.0

>  obj-$(CONFIG_TYPEC)		+= typec.o

>  typec-y				:= class.o mux.o bus.o port-mapper.o

> +typec-$(MULTIPLEXER)		+= of_mux.o

>  obj-$(CONFIG_TYPEC)		+= altmodes/

>  obj-$(CONFIG_TYPEC_TCPM)	+= tcpm/

>  obj-$(CONFIG_TYPEC_UCSI)	+= ucsi/

> diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c

> index 9da22ae3006c9..282622276d97b 100644

> --- a/drivers/usb/typec/mux.c

> +++ b/drivers/usb/typec/mux.c

> @@ -63,6 +63,9 @@ struct typec_switch *fwnode_typec_switch_get(struct fwnode_handle *fwnode)

>  

>  	sw = fwnode_connection_find_match(fwnode, "orientation-switch", NULL,

>  					  typec_switch_match);

> +	if (!sw)

> +		sw = of_switch_register(fwnode);

> +

> 

> How to support multiple typec_switch_get() for of_mux case?

> the second call to fwnode_typec_switch_get() will get the switch

> via fwnode_connection_find_match()? This means we still need

> a property "orientation-switch" for mux controller node, this

> seems not the expected way for a mux consumer, even with this

> property, we will get a EPROBE_DEFER for the first call.

> 

> If we use mux_control_get() for multiple caller/consumer, then

> we need some other device as input.

>   

> typec_switch object looks to me is a provider, if we create

> and maintain it in consumer side, I have not come out a better

> way:-).


Sorry, but can we rewind a bit: Why can't you just register the
orientation switch from your mux driver and be done with it? You
should then be able to use OF graph, and no special bindings should
be needed, no?

If you want to reuse a mux-controller driver, then you do need to
modify it (but only a little), and what ever mux-controller specific
bindings there are, you will not use those when the mux supplies the
orientation switching function, instead you'll use the OF graph for
that. But surely that is not a problem?

The mux-controller framework expects the "consumers" of the muxes to
understand the final function that the mux is used for. The Type-C
"mux" framework (I should not even talk about muxes with those) works
the other way around. The driver for the component that supplies the
orientation switch function must understand that it is handling that
function, and there is a good reason for doing it that way with the
USB Type-C switches. The orientation switch for example quite simply
is _not_ always a mux. In fact, it's seems to be rarely a mux these
days. With USB4 for example the orientation is handled almost always
by the first on-board retimer.

There are actually also some technical reasons why Hans failed to get
the mux-controller thing to work, which is the original reason why we
introduced the dedicated framework for the Type-C "muxes" (I really
should stop talking about muxes), but I don't remember what was the
reason.

In any case, to summarise: the orientation switch is a function. A mux
is a device that can supply that function, and if it does, then the
driver for it really needs to register the dedicated orientation
switch.

thanks,

-- 
heikki
Jun Li May 31, 2021, 11:58 a.m. UTC | #8
> -----Original Message-----

> From: Heikki Krogerus <heikki.krogerus@linux.intel.com>

> Sent: Wednesday, May 26, 2021 5:16 PM

> To: Jun Li <jun.li@nxp.com>

> Cc: robh+dt@kernel.org; shawnguo@kernel.org; gregkh@linuxfoundation.org;

> linux@roeck-us.net; linux-usb@vger.kernel.org; dl-linux-imx

> <linux-imx@nxp.com>; devicetree@vger.kernel.org;

> linux-arm-kernel@lists.infradead.org

> Subject: Re: [PATCH 3/4] usb: typec: add typec orientation switch support

> via mux controller

> 

> On Tue, May 25, 2021 at 11:46:18AM +0000, Jun Li wrote:

> > Hi Heikki,

> >

> > > -----Original Message-----

> > > From: Heikki Krogerus <heikki.krogerus@linux.intel.com>

> > > Sent: Friday, May 21, 2021 4:38 PM

> > > To: Jun Li <jun.li@nxp.com>

> > > Cc: robh+dt@kernel.org; shawnguo@kernel.org;

> > > gregkh@linuxfoundation.org; linux@roeck-us.net;

> > > linux-usb@vger.kernel.org; dl-linux-imx <linux-imx@nxp.com>;

> > > devicetree@vger.kernel.org; linux-arm-kernel@lists.infradead.org

> > > Subject: Re: [PATCH 3/4] usb: typec: add typec orientation switch

> > > support via mux controller

> > >

> > > Hi,

> > >

> > > On Thu, May 20, 2021 at 03:33:36PM +0300, Heikki Krogerus wrote:

> > > > Why not just do that inside fwnode_typec_switch_get() and handle

> > > > the whole thing in drivers/usb/typec/mux.c (or in its own file if

> > > > you prefer)?

> > > >

> > > > You'll just need to register a "wrapper" Type-C switch object for

> > > > the OF mux controller, but that should not be a problem. That way

> > > > you don't need to export any new functions, touch this file or

> > > > anything else.

> > >

> > > I wrote a bit of code just to see how that would look. I'm attaching

> > > you the hack I made. I guess something like that would not be too bad.

> > > A wrapper is probable always a bit clumsy, but I'm not sure that in

> > > this case it's a huge problem. Of course if there are any better

> > > ideas, let's here them :-)

> >

> > Thanks for your patch, I am pasting the patch as below.

> >

> > seems we need consider more than that.

> >

> > diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile

> > index a0adb8947a301..d85231b2fe10b 100644

> > --- a/drivers/usb/typec/Makefile

> > +++ b/drivers/usb/typec/Makefile

> > @@ -1,6 +1,7 @@

> >  # SPDX-License-Identifier: GPL-2.0

> >  obj-$(CONFIG_TYPEC)		+= typec.o

> >  typec-y				:= class.o mux.o bus.o port-mapper.o

> > +typec-$(MULTIPLEXER)		+= of_mux.o

> >  obj-$(CONFIG_TYPEC)		+= altmodes/

> >  obj-$(CONFIG_TYPEC_TCPM)	+= tcpm/

> >  obj-$(CONFIG_TYPEC_UCSI)	+= ucsi/

> > diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c index

> > 9da22ae3006c9..282622276d97b 100644

> > --- a/drivers/usb/typec/mux.c

> > +++ b/drivers/usb/typec/mux.c

> > @@ -63,6 +63,9 @@ struct typec_switch *fwnode_typec_switch_get(struct

> > fwnode_handle *fwnode)

> >

> >  	sw = fwnode_connection_find_match(fwnode, "orientation-switch", NULL,

> >  					  typec_switch_match);

> > +	if (!sw)

> > +		sw = of_switch_register(fwnode);

> > +

> >

> > How to support multiple typec_switch_get() for of_mux case?

> > the second call to fwnode_typec_switch_get() will get the switch via

> > fwnode_connection_find_match()? This means we still need a property

> > "orientation-switch" for mux controller node, this seems not the

> > expected way for a mux consumer, even with this property, we will get

> > a EPROBE_DEFER for the first call.

> >

> > If we use mux_control_get() for multiple caller/consumer, then we need

> > some other device as input.

> >

> > typec_switch object looks to me is a provider, if we create and

> > maintain it in consumer side, I have not come out a better way:-).

> 

> Sorry, but can we rewind a bit: Why can't you just register the orientation

> switch from your mux driver and be done with it? You should then be able

> to use OF graph, and no special bindings should be needed, no?


So we still need a special property for OF graph per discussion on another
thread(use device type other than device name for match), and this has
to be a mux controller core binding for possible different mux chips
(GPIO/MMIO...), register a typec switch if this property exist, but this
is the user specific thing from mux controller point view, I feed this
is again against DT binding's expectation.

> 

> If you want to reuse a mux-controller driver, then you do need to modify

> it (but only a little), and what ever mux-controller specific bindings there

> are, you will not use those when the mux supplies the orientation switching

> function, instead you'll use the OF graph for that. But surely that is not

> a problem?

> 

> The mux-controller framework expects the "consumers" of the muxes to

> understand the final function that the mux is used for. The Type-C "mux"

> framework (I should not even talk about muxes with those) works the other

> way around. 


Fully agree.

> The driver for the component that supplies the orientation switch

> function must understand that it is handling that function, and there is

> a good reason for doing it that way with the USB Type-C switches. 


I understand yes if the switch is only part function of the driver.
  
> The

> orientation switch for example quite simply is _not_ always a mux. In fact,

> it's seems to be rarely a mux these days. With USB4 for example the orientation

> is handled almost always by the first on-board retimer.


If the mux is only part function of a new driver, use the tyepc
"mux" framework and create new binding for the new driver is fine.

But if the typec switch control need a dedicated driver to handle,
on DT platforms, now mux-controller is the only proposed way to go
from binding point view. I am not sure if my case is a normal HW
design, but I guess I should not the only user of this kind of
situation.

> 

> There are actually also some technical reasons why Hans failed to get the

> mux-controller thing to work, which is the original reason why we introduced

> the dedicated framework for the Type-C "muxes" (I really should stop talking

> about muxes), but I don't remember what was the reason.


I checked the patches Hans did, that was mainly to address non-DT
platform, I don't see a clear reason why it can't fit DT platform,
maybe I missed something.

+Hans, It would be great if you can comment on this, thanks.

> 

> In any case, to summarise: the orientation switch is a function. A mux is

> a device that can supply that function, and if it does, then the driver for

> it really needs to register the dedicated orientation switch.


Understand your point, if register the dedicated orientation switch is a must,
I feel using general mux control can't make much sense.

Thanks
Li Jun 
> 

> thanks,

> 

> --

> heikki