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

Jun Li jun.li at nxp.com
Tue May 25 04:46:18 PDT 2021


Hi Heikki,

> -----Original Message-----
> From: Heikki Krogerus <heikki.krogerus at linux.intel.com>
> Sent: Friday, May 21, 2021 4:38 PM
> To: Jun Li <jun.li at nxp.com>
> Cc: robh+dt at kernel.org; shawnguo at kernel.org; gregkh at linuxfoundation.org;
> linux at roeck-us.net; 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: 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



More information about the linux-arm-kernel mailing list