Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1359590 > unrolled thread

[PATCH v4 3/7] usb: mux: add common code for Intel dual role port mux

Started byLu Baolu <baolu.lu@linux.intel.com>
First post2016-03-17 06:50 +0100
Last post2016-03-17 08:50 +0100
Articles 5 — 2 participants

Back to article view | Back to linux.kernel

This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by below is the oldest one visible, not the original post.


Contents

  [PATCH v4 3/7] usb: mux: add common code for Intel dual role port mux Lu Baolu <baolu.lu@linux.intel.com> - 2016-03-17 06:50 +0100
    Re: [PATCH v4 3/7] usb: mux: add common code for Intel dual role port  mux Chanwoo Choi <cw00.choi@samsung.com> - 2016-03-17 07:10 +0100
      Re: [PATCH v4 3/7] usb: mux: add common code for Intel dual role port  mux Lu Baolu <baolu.lu@linux.intel.com> - 2016-03-17 08:20 +0100
        Re: [PATCH v4 3/7] usb: mux: add common code for Intel dual role port  mux Chanwoo Choi <cw00.choi@samsung.com> - 2016-03-17 08:30 +0100
          Re: [PATCH v4 3/7] usb: mux: add common code for Intel dual role port  mux Lu Baolu <baolu.lu@linux.intel.com> - 2016-03-17 08:50 +0100

#1359590 — [PATCH v4 3/7] usb: mux: add common code for Intel dual role port mux

FromLu Baolu <baolu.lu@linux.intel.com>
Date2016-03-17 06:50 +0100
Subject[PATCH v4 3/7] usb: mux: add common code for Intel dual role port mux
Message-ID<rdyYO-349-17@gated-at.bofh.it>
Several Intel PCHs and SOCs have an internal mux that is used to
share one USB port between device controller and host controller.

A usb port mux could be abstracted as the following elements:
1) mux state: HOST or PERIPHERAL;
2) an extcon cable which triggers the change of mux state between
   HOST and PERIPHERAL;
3) The required action to do the real port switch.

This patch adds the common code to handle usb port mux. With this
common code, the individual mux driver, which always is platform
dependent, could focus on the real operation of mux switch.

Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Reviewed-by: Felipe Balbi <balbi@kernel.org>
---
 Documentation/ABI/testing/sysfs-bus-platform |  15 +++
 MAINTAINERS                                  |   7 ++
 drivers/usb/Kconfig                          |   2 +
 drivers/usb/Makefile                         |   1 +
 drivers/usb/mux/Kconfig                      |  12 ++
 drivers/usb/mux/Makefile                     |   4 +
 drivers/usb/mux/intel-mux.c                  | 180 +++++++++++++++++++++++++++
 include/linux/usb/intel-mux.h                |  43 +++++++
 8 files changed, 264 insertions(+)
 create mode 100644 drivers/usb/mux/Kconfig
 create mode 100644 drivers/usb/mux/Makefile
 create mode 100644 drivers/usb/mux/intel-mux.c
 create mode 100644 include/linux/usb/intel-mux.h

diff --git a/Documentation/ABI/testing/sysfs-bus-platform b/Documentation/ABI/testing/sysfs-bus-platform
index 5172a61..23bf76e 100644
--- a/Documentation/ABI/testing/sysfs-bus-platform
+++ b/Documentation/ABI/testing/sysfs-bus-platform
@@ -18,3 +18,18 @@ Description:
 		devices to opt-out of driver binding using a driver_override
 		name such as "none".  Only a single driver may be specified in
 		the override, there is no support for parsing delimiters.
+
+What:		/sys/bus/platform/devices/.../port_mux
+Date:		Febuary 2016
+Contact:	Lu Baolu <baolu.lu@linux.intel.com>
+Description:
+		In some platforms, a single USB port is shared between a USB host
+		controller and a device controller. A USB mux driver is needed to
+		handle the port mux. port_mux attribute shows and stores the mux
+		state.
+		For read:
+		'peripheral' - mux switched to PERIPHERAL controller;
+		'host'       - mux switched to HOST controller.
+		For write:
+		'peripheral' - mux will be switched to PERIPHERAL controller;
+		'host'       - mux will be switched to HOST controller.
diff --git a/MAINTAINERS b/MAINTAINERS
index da3e4d8..0dbee11 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11399,6 +11399,13 @@ T:	git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git
 S:	Maintained
 F:	drivers/usb/phy/
 
+USB PORT MUX DRIVER
+M:	Lu Baolu <baolu.lu@linux.intel.com>
+L:	linux-usb@vger.kernel.org
+S:	Supported
+F:	include/linux/usb/intel-mux.h
+F:	drivers/usb/mux/intel-mux.c
+
 USB PRINTER DRIVER (usblp)
 M:	Pete Zaitcev <zaitcev@redhat.com>
 L:	linux-usb@vger.kernel.org
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 8ed451d..dbd6620 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -149,6 +149,8 @@ endif # USB
 
 source "drivers/usb/phy/Kconfig"
 
+source "drivers/usb/mux/Kconfig"
+
 source "drivers/usb/gadget/Kconfig"
 
 config USB_LED_TRIG
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index d5c57f1..6433f0c 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -6,6 +6,7 @@
 
 obj-$(CONFIG_USB)		+= core/
 obj-$(CONFIG_USB_SUPPORT)	+= phy/
+obj-$(CONFIG_USB_SUPPORT)	+= mux/
 
 obj-$(CONFIG_USB_DWC3)		+= dwc3/
 obj-$(CONFIG_USB_DWC2)		+= dwc2/
diff --git a/drivers/usb/mux/Kconfig b/drivers/usb/mux/Kconfig
new file mode 100644
index 0000000..62e2cc3
--- /dev/null
+++ b/drivers/usb/mux/Kconfig
@@ -0,0 +1,12 @@
+#
+# USB port mux driver configuration
+#
+menu "USB Port MUX drivers"
+config INTEL_USB_MUX
+	select EXTCON
+	def_bool n
+	help
+	  Common code for all Intel dual role port mux drivers. All Intel
+	  usb port mux drivers should select it.
+
+endmenu
diff --git a/drivers/usb/mux/Makefile b/drivers/usb/mux/Makefile
new file mode 100644
index 0000000..84f0ae8
--- /dev/null
+++ b/drivers/usb/mux/Makefile
@@ -0,0 +1,4 @@
+#
+# Makefile for USB port mux drivers
+#
+obj-$(CONFIG_INTEL_USB_MUX)		+= intel-mux.o
diff --git a/drivers/usb/mux/intel-mux.c b/drivers/usb/mux/intel-mux.c
new file mode 100644
index 0000000..bb7b192
--- /dev/null
+++ b/drivers/usb/mux/intel-mux.c
@@ -0,0 +1,180 @@
+/**
+ * intel_mux.c - USB Port Mux support
+ *
+ * Copyright (C) 2016 Intel Corporation
+ *
+ * Author: Lu Baolu <baolu.lu@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/slab.h>
+#include <linux/notifier.h>
+#include <linux/extcon.h>
+#include <linux/err.h>
+
+struct intel_usb_mux {
+	struct device	*dev;
+	char		*cable_name;
+	int		(*cable_set_cb)(struct device *dev);
+	int		(*cable_unset_cb)(struct device *dev);
+
+	struct notifier_block		nb;
+	struct extcon_specific_cable_nb	obj;
+
+	/*
+	 * The state of the mux.
+	 * 0, 1 - mux switch state
+	 * -1   - uninitialized state
+	 */
+	int				mux_state;
+
+	 /* lock for mux_state */
+	struct mutex			mux_mutex;
+};
+
+static int usb_mux_change_state(struct intel_usb_mux *mux, int state)
+{
+	int ret;
+	struct device *dev = mux->dev;
+
+	dev_WARN_ONCE(dev,
+		      !mutex_is_locked(&mux->mux_mutex),
+		      "mutex is unlocked\n");
+
+	mux->mux_state = state;
+
+	if (mux->mux_state)
+		ret = mux->cable_set_cb(dev);
+	else
+		ret = mux->cable_unset_cb(dev);
+
+	return ret;
+}
+
+static int usb_mux_notifier(struct notifier_block *nb,
+			    unsigned long event, void *ptr)
+{
+	struct intel_usb_mux *mux;
+	int state;
+	int ret = NOTIFY_DONE;
+
+	mux = container_of(nb, struct intel_usb_mux, nb);
+
+	state = extcon_get_cable_state(mux->obj.edev,
+				       mux->cable_name);
+
+	if (mux->mux_state == -1 || mux->mux_state != state) {
+		mutex_lock(&mux->mux_mutex);
+		ret = usb_mux_change_state(mux, state);
+		mutex_unlock(&mux->mux_mutex);
+	}
+
+	return ret;
+}
+
+static ssize_t port_mux_show(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	struct intel_usb_mux *mux = dev_get_drvdata(dev);
+
+	if (dev_WARN_ONCE(dev, !mux, "mux without data structure\n"))
+		return 0;
+
+	return sprintf(buf, "%s\n", mux->mux_state ? "host" : "peripheral");
+}
+
+static ssize_t port_mux_store(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+	struct intel_usb_mux *mux = dev_get_drvdata(dev);
+	int state;
+
+	if (dev_WARN_ONCE(dev, !mux, "mux without data structure\n"))
+		return -EINVAL;
+
+	if (sysfs_streq(buf, "peripheral"))
+		state = 0;
+	else if (sysfs_streq(buf, "host"))
+		state = 1;
+	else
+		return -EINVAL;
+
+	mutex_lock(&mux->mux_mutex);
+	usb_mux_change_state(mux, state);
+	mutex_unlock(&mux->mux_mutex);
+
+	return count;
+}
+static DEVICE_ATTR_RW(port_mux);
+
+int intel_usb_mux_bind_cable(struct device *dev,
+			     char *extcon_name,
+			     char *cable_name,
+			     int (*cable_set_cb)(struct device *dev),
+			     int (*cable_unset_cb)(struct device *dev))
+{
+	int ret;
+	struct intel_usb_mux *mux;
+
+	if (!cable_name)
+		return -ENODEV;
+
+	mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
+	if (!mux)
+		return -ENOMEM;
+
+	mux->dev = dev;
+	mux->cable_name = kstrdup(cable_name, GFP_KERNEL);
+	mux->cable_set_cb = cable_set_cb;
+	mux->cable_unset_cb = cable_unset_cb;
+	mux->nb.notifier_call = usb_mux_notifier;
+	mutex_init(&mux->mux_mutex);
+	mux->mux_state = -1;
+	dev_set_drvdata(dev, mux);
+	ret = extcon_register_interest(&mux->obj, extcon_name,
+				       cable_name, &mux->nb);
+	if (ret) {
+		kfree(mux->cable_name);
+		dev_err(dev, "failed to register extcon notifier\n");
+		return -ENODEV;
+	}
+
+	usb_mux_notifier(&mux->nb, 0, NULL);
+
+	/* register the sysfs interface */
+	ret = device_create_file(dev, &dev_attr_port_mux);
+	if (ret) {
+		extcon_unregister_interest(&mux->obj);
+		kfree(mux->cable_name);
+		dev_err(dev, "failed to create sysfs attribute\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(intel_usb_mux_bind_cable);
+
+int intel_usb_mux_unbind_cable(struct device *dev)
+{
+	struct intel_usb_mux *mux = dev_get_drvdata(dev);
+
+	device_remove_file(dev, &dev_attr_port_mux);
+	extcon_unregister_interest(&mux->obj);
+	kfree(mux->cable_name);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(intel_usb_mux_unbind_cable);
+
+#ifdef CONFIG_PM_SLEEP
+void intel_usb_mux_complete(struct device *dev)
+{
+	struct intel_usb_mux *mux = dev_get_drvdata(dev);
+
+	usb_mux_notifier(&mux->nb, 0, NULL);
+}
+EXPORT_SYMBOL_GPL(intel_usb_mux_complete);
+#endif
diff --git a/include/linux/usb/intel-mux.h b/include/linux/usb/intel-mux.h
new file mode 100644
index 0000000..fd5612d
--- /dev/null
+++ b/include/linux/usb/intel-mux.h
@@ -0,0 +1,43 @@
+/**
+ * intel_mux.h - USB Port Mux definitions
+ *
+ * Copyright (C) 2016 Intel Corporation
+ *
+ * Author: Lu Baolu <baolu.lu@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __LINUX_USB_INTEL_MUX_H
+#define __LINUX_USB_INTEL_MUX_H
+
+#if IS_ENABLED(CONFIG_INTEL_USB_MUX)
+int intel_usb_mux_bind_cable(struct device *dev, char *extcon_name,
+			     char *cable_name,
+			     int (*cable_set_cb)(struct device *dev),
+			     int (*cable_unset_cb)(struct device *dev));
+int intel_usb_mux_unbind_cable(struct device *dev);
+#ifdef CONFIG_PM_SLEEP
+void intel_usb_mux_complete(struct device *dev);
+#endif
+
+#else
+static inline int
+intel_usb_mux_bind_cable(struct device *dev,
+			 char *extcon_name,
+			 char *cable_name,
+			 int (*cable_set_cb)(struct device *dev),
+			 int (*cable_unset_cb)(struct device *dev))
+{
+	return -ENODEV;
+}
+
+static inline int intel_usb_mux_unbind_cable(struct device *dev)
+{
+	return 0;
+}
+#endif /* CONFIG_INTEL_USB_MUX */
+
+#endif /* __LINUX_USB_INTEL_MUX_H */
-- 
2.1.4

[toc] | [next] | [standalone]


#1359594 — Re: [PATCH v4 3/7] usb: mux: add common code for Intel dual role port mux

FromChanwoo Choi <cw00.choi@samsung.com>
Date2016-03-17 07:10 +0100
SubjectRe: [PATCH v4 3/7] usb: mux: add common code for Intel dual role port mux
Message-ID<rdzia-3r0-1@gated-at.bofh.it>
In reply to#1359590
Hi Lu,

To handle extcon (external connector), I implemented the unique id
for each external connector on patch[1] instead of using the ambiguous string type. 
[1] 2a9de9c0f08d6 (extcon: Use the unique id for external connector instead of string)

So I recommend that you should use the unique id (ex. EXTCON_USB, EXTCON_USB_HOST)
with extcon_register_notifier(), extcon_get_cable_state_() and extcon_set_cable_state_().

extcon_register_interest() is deprecated-> extcon_register_notifier()
extcon_get_cable_state() is deprecated -> extcon_get_cable_state_()
extcon_set_cable_state() is deprecated -> extcon_set_cable_state_()


You can refer to usage for new function with unique id on patch[2]
[2] 5960387a2fb83 (usb: dwc3: omap: Replace deprecated API of extcon)

I'm sorry for late reply. I add the some comment on below.


On 2016년 03월 17일 14:46, Lu Baolu wrote:
> Several Intel PCHs and SOCs have an internal mux that is used to
> share one USB port between device controller and host controller.
> 
> A usb port mux could be abstracted as the following elements:
> 1) mux state: HOST or PERIPHERAL;
> 2) an extcon cable which triggers the change of mux state between
>    HOST and PERIPHERAL;
> 3) The required action to do the real port switch.
> 
> This patch adds the common code to handle usb port mux. With this
> common code, the individual mux driver, which always is platform
> dependent, could focus on the real operation of mux switch.
> 
> Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
> Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
> Reviewed-by: Felipe Balbi <balbi@kernel.org>
> ---
>  Documentation/ABI/testing/sysfs-bus-platform |  15 +++
>  MAINTAINERS                                  |   7 ++
>  drivers/usb/Kconfig                          |   2 +
>  drivers/usb/Makefile                         |   1 +
>  drivers/usb/mux/Kconfig                      |  12 ++
>  drivers/usb/mux/Makefile                     |   4 +
>  drivers/usb/mux/intel-mux.c                  | 180 +++++++++++++++++++++++++++
>  include/linux/usb/intel-mux.h                |  43 +++++++
>  8 files changed, 264 insertions(+)
>  create mode 100644 drivers/usb/mux/Kconfig
>  create mode 100644 drivers/usb/mux/Makefile
>  create mode 100644 drivers/usb/mux/intel-mux.c
>  create mode 100644 include/linux/usb/intel-mux.h
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-platform b/Documentation/ABI/testing/sysfs-bus-platform
> index 5172a61..23bf76e 100644
> --- a/Documentation/ABI/testing/sysfs-bus-platform
> +++ b/Documentation/ABI/testing/sysfs-bus-platform
> @@ -18,3 +18,18 @@ Description:
>  		devices to opt-out of driver binding using a driver_override
>  		name such as "none".  Only a single driver may be specified in
>  		the override, there is no support for parsing delimiters.
> +
> +What:		/sys/bus/platform/devices/.../port_mux
> +Date:		Febuary 2016
> +Contact:	Lu Baolu <baolu.lu@linux.intel.com>
> +Description:
> +		In some platforms, a single USB port is shared between a USB host
> +		controller and a device controller. A USB mux driver is needed to
> +		handle the port mux. port_mux attribute shows and stores the mux
> +		state.
> +		For read:
> +		'peripheral' - mux switched to PERIPHERAL controller;
> +		'host'       - mux switched to HOST controller.
> +		For write:
> +		'peripheral' - mux will be switched to PERIPHERAL controller;
> +		'host'       - mux will be switched to HOST controller.
> diff --git a/MAINTAINERS b/MAINTAINERS
> index da3e4d8..0dbee11 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11399,6 +11399,13 @@ T:	git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git
>  S:	Maintained
>  F:	drivers/usb/phy/
>  
> +USB PORT MUX DRIVER
> +M:	Lu Baolu <baolu.lu@linux.intel.com>
> +L:	linux-usb@vger.kernel.org
> +S:	Supported
> +F:	include/linux/usb/intel-mux.h
> +F:	drivers/usb/mux/intel-mux.c
> +
>  USB PRINTER DRIVER (usblp)
>  M:	Pete Zaitcev <zaitcev@redhat.com>
>  L:	linux-usb@vger.kernel.org
> diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
> index 8ed451d..dbd6620 100644
> --- a/drivers/usb/Kconfig
> +++ b/drivers/usb/Kconfig
> @@ -149,6 +149,8 @@ endif # USB
>  
>  source "drivers/usb/phy/Kconfig"
>  
> +source "drivers/usb/mux/Kconfig"
> +
>  source "drivers/usb/gadget/Kconfig"
>  
>  config USB_LED_TRIG
> diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
> index d5c57f1..6433f0c 100644
> --- a/drivers/usb/Makefile
> +++ b/drivers/usb/Makefile
> @@ -6,6 +6,7 @@
>  
>  obj-$(CONFIG_USB)		+= core/
>  obj-$(CONFIG_USB_SUPPORT)	+= phy/
> +obj-$(CONFIG_USB_SUPPORT)	+= mux/
>  
>  obj-$(CONFIG_USB_DWC3)		+= dwc3/
>  obj-$(CONFIG_USB_DWC2)		+= dwc2/
> diff --git a/drivers/usb/mux/Kconfig b/drivers/usb/mux/Kconfig
> new file mode 100644
> index 0000000..62e2cc3
> --- /dev/null
> +++ b/drivers/usb/mux/Kconfig
> @@ -0,0 +1,12 @@
> +#
> +# USB port mux driver configuration
> +#
> +menu "USB Port MUX drivers"
> +config INTEL_USB_MUX
> +	select EXTCON
> +	def_bool n
> +	help
> +	  Common code for all Intel dual role port mux drivers. All Intel
> +	  usb port mux drivers should select it.
> +
> +endmenu
> diff --git a/drivers/usb/mux/Makefile b/drivers/usb/mux/Makefile
> new file mode 100644
> index 0000000..84f0ae8
> --- /dev/null
> +++ b/drivers/usb/mux/Makefile
> @@ -0,0 +1,4 @@
> +#
> +# Makefile for USB port mux drivers
> +#
> +obj-$(CONFIG_INTEL_USB_MUX)		+= intel-mux.o
> diff --git a/drivers/usb/mux/intel-mux.c b/drivers/usb/mux/intel-mux.c
> new file mode 100644
> index 0000000..bb7b192
> --- /dev/null
> +++ b/drivers/usb/mux/intel-mux.c
> @@ -0,0 +1,180 @@
> +/**
> + * intel_mux.c - USB Port Mux support
> + *
> + * Copyright (C) 2016 Intel Corporation
> + *
> + * Author: Lu Baolu <baolu.lu@linux.intel.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +#include <linux/slab.h>
> +#include <linux/notifier.h>
> +#include <linux/extcon.h>
> +#include <linux/err.h>
> +
> +struct intel_usb_mux {
> +	struct device	*dev;
> +	char		*cable_name;
> +	int		(*cable_set_cb)(struct device *dev);
> +	int		(*cable_unset_cb)(struct device *dev);
> +
> +	struct notifier_block		nb;
> +	struct extcon_specific_cable_nb	obj;
> +
> +	/*
> +	 * The state of the mux.
> +	 * 0, 1 - mux switch state
> +	 * -1   - uninitialized state
> +	 */
> +	int				mux_state;
> +
> +	 /* lock for mux_state */
> +	struct mutex			mux_mutex;
> +};
> +
> +static int usb_mux_change_state(struct intel_usb_mux *mux, int state)
> +{
> +	int ret;
> +	struct device *dev = mux->dev;
> +
> +	dev_WARN_ONCE(dev,
> +		      !mutex_is_locked(&mux->mux_mutex),
> +		      "mutex is unlocked\n");
> +
> +	mux->mux_state = state;
> +
> +	if (mux->mux_state)
> +		ret = mux->cable_set_cb(dev);
> +	else
> +		ret = mux->cable_unset_cb(dev);
> +
> +	return ret;
> +}
> +
> +static int usb_mux_notifier(struct notifier_block *nb,
> +			    unsigned long event, void *ptr)
> +{
> +	struct intel_usb_mux *mux;
> +	int state;
> +	int ret = NOTIFY_DONE;
> +
> +	mux = container_of(nb, struct intel_usb_mux, nb);
> +
> +	state = extcon_get_cable_state(mux->obj.edev,
> +				       mux->cable_name);

Use the extcon_get_cable_stet_().

> +
> +	if (mux->mux_state == -1 || mux->mux_state != state) {
> +		mutex_lock(&mux->mux_mutex);
> +		ret = usb_mux_change_state(mux, state);
> +		mutex_unlock(&mux->mux_mutex);
> +	}
> +
> +	return ret;
> +}
> +
> +static ssize_t port_mux_show(struct device *dev,
> +			     struct device_attribute *attr, char *buf)
> +{
> +	struct intel_usb_mux *mux = dev_get_drvdata(dev);
> +
> +	if (dev_WARN_ONCE(dev, !mux, "mux without data structure\n"))
> +		return 0;
> +
> +	return sprintf(buf, "%s\n", mux->mux_state ? "host" : "peripheral");
> +}
> +
> +static ssize_t port_mux_store(struct device *dev,
> +			      struct device_attribute *attr,
> +			      const char *buf, size_t count)
> +{
> +	struct intel_usb_mux *mux = dev_get_drvdata(dev);
> +	int state;
> +
> +	if (dev_WARN_ONCE(dev, !mux, "mux without data structure\n"))
> +		return -EINVAL;
> +
> +	if (sysfs_streq(buf, "peripheral"))
> +		state = 0;
> +	else if (sysfs_streq(buf, "host"))
> +		state = 1;
> +	else
> +		return -EINVAL;
> +
> +	mutex_lock(&mux->mux_mutex);
> +	usb_mux_change_state(mux, state);
> +	mutex_unlock(&mux->mux_mutex);
> +
> +	return count;
> +}
> +static DEVICE_ATTR_RW(port_mux);
> +
> +int intel_usb_mux_bind_cable(struct device *dev,
> +			     char *extcon_name,
> +			     char *cable_name,
> +			     int (*cable_set_cb)(struct device *dev),
> +			     int (*cable_unset_cb)(struct device *dev))
> +{
> +	int ret;
> +	struct intel_usb_mux *mux;
> +
> +	if (!cable_name)
> +		return -ENODEV;
> +
> +	mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
> +	if (!mux)
> +		return -ENOMEM;
> +
> +	mux->dev = dev;
> +	mux->cable_name = kstrdup(cable_name, GFP_KERNEL);
> +	mux->cable_set_cb = cable_set_cb;
> +	mux->cable_unset_cb = cable_unset_cb;
> +	mux->nb.notifier_call = usb_mux_notifier;
> +	mutex_init(&mux->mux_mutex);
> +	mux->mux_state = -1;
> +	dev_set_drvdata(dev, mux);
> +	ret = extcon_register_interest(&mux->obj, extcon_name,
> +				       cable_name, &mux->nb);

Use the extcon_register_notifier()

> +	if (ret) {
> +		kfree(mux->cable_name);
> +		dev_err(dev, "failed to register extcon notifier\n");
> +		return -ENODEV;
> +	}
> +
> +	usb_mux_notifier(&mux->nb, 0, NULL);
> +
> +	/* register the sysfs interface */
> +	ret = device_create_file(dev, &dev_attr_port_mux);
> +	if (ret) {
> +		extcon_unregister_interest(&mux->obj);

Use the extcon_unregister_notifier()

> +		kfree(mux->cable_name);
> +		dev_err(dev, "failed to create sysfs attribute\n");
> +		return -ENODEV;
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(intel_usb_mux_bind_cable);
> +
> +int intel_usb_mux_unbind_cable(struct device *dev)
> +{
> +	struct intel_usb_mux *mux = dev_get_drvdata(dev);
> +
> +	device_remove_file(dev, &dev_attr_port_mux);
> +	extcon_unregister_interest(&mux->obj);

Use the extcon_unregister_notifier()

> +	kfree(mux->cable_name);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(intel_usb_mux_unbind_cable);
> +
> +#ifdef CONFIG_PM_SLEEP
> +void intel_usb_mux_complete(struct device *dev)
> +{
> +	struct intel_usb_mux *mux = dev_get_drvdata(dev);
> +
> +	usb_mux_notifier(&mux->nb, 0, NULL);
> +}
> +EXPORT_SYMBOL_GPL(intel_usb_mux_complete);
> +#endif
> diff --git a/include/linux/usb/intel-mux.h b/include/linux/usb/intel-mux.h
> new file mode 100644
> index 0000000..fd5612d
> --- /dev/null
> +++ b/include/linux/usb/intel-mux.h
> @@ -0,0 +1,43 @@
> +/**
> + * intel_mux.h - USB Port Mux definitions
> + *
> + * Copyright (C) 2016 Intel Corporation
> + *
> + * Author: Lu Baolu <baolu.lu@linux.intel.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#ifndef __LINUX_USB_INTEL_MUX_H
> +#define __LINUX_USB_INTEL_MUX_H
> +
> +#if IS_ENABLED(CONFIG_INTEL_USB_MUX)
> +int intel_usb_mux_bind_cable(struct device *dev, char *extcon_name,
> +			     char *cable_name,
> +			     int (*cable_set_cb)(struct device *dev),
> +			     int (*cable_unset_cb)(struct device *dev));
> +int intel_usb_mux_unbind_cable(struct device *dev);
> +#ifdef CONFIG_PM_SLEEP
> +void intel_usb_mux_complete(struct device *dev);
> +#endif
> +
> +#else
> +static inline int
> +intel_usb_mux_bind_cable(struct device *dev,
> +			 char *extcon_name,
> +			 char *cable_name,
> +			 int (*cable_set_cb)(struct device *dev),
> +			 int (*cable_unset_cb)(struct device *dev))
> +{
> +	return -ENODEV;
> +}
> +
> +static inline int intel_usb_mux_unbind_cable(struct device *dev)
> +{
> +	return 0;
> +}
> +#endif /* CONFIG_INTEL_USB_MUX */
> +
> +#endif /* __LINUX_USB_INTEL_MUX_H */
> 

Best Regards,
Chanwoo Choi

[toc] | [prev] | [next] | [standalone]


#1359602 — Re: [PATCH v4 3/7] usb: mux: add common code for Intel dual role port mux

FromLu Baolu <baolu.lu@linux.intel.com>
Date2016-03-17 08:20 +0100
SubjectRe: [PATCH v4 3/7] usb: mux: add common code for Intel dual role port mux
Message-ID<rdAnT-4aY-1@gated-at.bofh.it>
In reply to#1359594
Hi Chanwoo,

On 03/17/2016 02:07 PM, Chanwoo Choi wrote:
> Hi Lu,
>
> To handle extcon (external connector), I implemented the unique id
> for each external connector on patch[1] instead of using the ambiguous string type. 
> [1] 2a9de9c0f08d6 (extcon: Use the unique id for external connector instead of string)
>
> So I recommend that you should use the unique id (ex. EXTCON_USB, EXTCON_USB_HOST)
> with extcon_register_notifier(), extcon_get_cable_state_() and extcon_set_cable_state_().
>
> extcon_register_interest() is deprecated-> extcon_register_notifier()
> extcon_get_cable_state() is deprecated -> extcon_get_cable_state_()
> extcon_set_cable_state() is deprecated -> extcon_set_cable_state_()

Sure. I will use the new interfaces with a refreshed patch serial later.

>
>
> You can refer to usage for new function with unique id on patch[2]
> [2] 5960387a2fb83 (usb: dwc3: omap: Replace deprecated API of extcon)

Thanks. That's helpful.

By the way, is extcon_get_extcon_dev() still available?

>
> I'm sorry for late reply. I add the some comment on below.

Never mind. Thank you for reminding me.

Best regards,
Baolu

>
>
> On 2016년 03월 17일 14:46, Lu Baolu wrote:
>> Several Intel PCHs and SOCs have an internal mux that is used to
>> share one USB port between device controller and host controller.
>>
>> A usb port mux could be abstracted as the following elements:
>> 1) mux state: HOST or PERIPHERAL;
>> 2) an extcon cable which triggers the change of mux state between
>>    HOST and PERIPHERAL;
>> 3) The required action to do the real port switch.
>>
>> This patch adds the common code to handle usb port mux. With this
>> common code, the individual mux driver, which always is platform
>> dependent, could focus on the real operation of mux switch.
>>
>> Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
>> Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
>> Reviewed-by: Felipe Balbi <balbi@kernel.org>
>> ---
>>  Documentation/ABI/testing/sysfs-bus-platform |  15 +++
>>  MAINTAINERS                                  |   7 ++
>>  drivers/usb/Kconfig                          |   2 +
>>  drivers/usb/Makefile                         |   1 +
>>  drivers/usb/mux/Kconfig                      |  12 ++
>>  drivers/usb/mux/Makefile                     |   4 +
>>  drivers/usb/mux/intel-mux.c                  | 180 +++++++++++++++++++++++++++
>>  include/linux/usb/intel-mux.h                |  43 +++++++
>>  8 files changed, 264 insertions(+)
>>  create mode 100644 drivers/usb/mux/Kconfig
>>  create mode 100644 drivers/usb/mux/Makefile
>>  create mode 100644 drivers/usb/mux/intel-mux.c
>>  create mode 100644 include/linux/usb/intel-mux.h
>>
>> diff --git a/Documentation/ABI/testing/sysfs-bus-platform b/Documentation/ABI/testing/sysfs-bus-platform
>> index 5172a61..23bf76e 100644
>> --- a/Documentation/ABI/testing/sysfs-bus-platform
>> +++ b/Documentation/ABI/testing/sysfs-bus-platform
>> @@ -18,3 +18,18 @@ Description:
>>  		devices to opt-out of driver binding using a driver_override
>>  		name such as "none".  Only a single driver may be specified in
>>  		the override, there is no support for parsing delimiters.
>> +
>> +What:		/sys/bus/platform/devices/.../port_mux
>> +Date:		Febuary 2016
>> +Contact:	Lu Baolu <baolu.lu@linux.intel.com>
>> +Description:
>> +		In some platforms, a single USB port is shared between a USB host
>> +		controller and a device controller. A USB mux driver is needed to
>> +		handle the port mux. port_mux attribute shows and stores the mux
>> +		state.
>> +		For read:
>> +		'peripheral' - mux switched to PERIPHERAL controller;
>> +		'host'       - mux switched to HOST controller.
>> +		For write:
>> +		'peripheral' - mux will be switched to PERIPHERAL controller;
>> +		'host'       - mux will be switched to HOST controller.
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index da3e4d8..0dbee11 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -11399,6 +11399,13 @@ T:	git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git
>>  S:	Maintained
>>  F:	drivers/usb/phy/
>>  
>> +USB PORT MUX DRIVER
>> +M:	Lu Baolu <baolu.lu@linux.intel.com>
>> +L:	linux-usb@vger.kernel.org
>> +S:	Supported
>> +F:	include/linux/usb/intel-mux.h
>> +F:	drivers/usb/mux/intel-mux.c
>> +
>>  USB PRINTER DRIVER (usblp)
>>  M:	Pete Zaitcev <zaitcev@redhat.com>
>>  L:	linux-usb@vger.kernel.org
>> diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
>> index 8ed451d..dbd6620 100644
>> --- a/drivers/usb/Kconfig
>> +++ b/drivers/usb/Kconfig
>> @@ -149,6 +149,8 @@ endif # USB
>>  
>>  source "drivers/usb/phy/Kconfig"
>>  
>> +source "drivers/usb/mux/Kconfig"
>> +
>>  source "drivers/usb/gadget/Kconfig"
>>  
>>  config USB_LED_TRIG
>> diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
>> index d5c57f1..6433f0c 100644
>> --- a/drivers/usb/Makefile
>> +++ b/drivers/usb/Makefile
>> @@ -6,6 +6,7 @@
>>  
>>  obj-$(CONFIG_USB)		+= core/
>>  obj-$(CONFIG_USB_SUPPORT)	+= phy/
>> +obj-$(CONFIG_USB_SUPPORT)	+= mux/
>>  
>>  obj-$(CONFIG_USB_DWC3)		+= dwc3/
>>  obj-$(CONFIG_USB_DWC2)		+= dwc2/
>> diff --git a/drivers/usb/mux/Kconfig b/drivers/usb/mux/Kconfig
>> new file mode 100644
>> index 0000000..62e2cc3
>> --- /dev/null
>> +++ b/drivers/usb/mux/Kconfig
>> @@ -0,0 +1,12 @@
>> +#
>> +# USB port mux driver configuration
>> +#
>> +menu "USB Port MUX drivers"
>> +config INTEL_USB_MUX
>> +	select EXTCON
>> +	def_bool n
>> +	help
>> +	  Common code for all Intel dual role port mux drivers. All Intel
>> +	  usb port mux drivers should select it.
>> +
>> +endmenu
>> diff --git a/drivers/usb/mux/Makefile b/drivers/usb/mux/Makefile
>> new file mode 100644
>> index 0000000..84f0ae8
>> --- /dev/null
>> +++ b/drivers/usb/mux/Makefile
>> @@ -0,0 +1,4 @@
>> +#
>> +# Makefile for USB port mux drivers
>> +#
>> +obj-$(CONFIG_INTEL_USB_MUX)		+= intel-mux.o
>> diff --git a/drivers/usb/mux/intel-mux.c b/drivers/usb/mux/intel-mux.c
>> new file mode 100644
>> index 0000000..bb7b192
>> --- /dev/null
>> +++ b/drivers/usb/mux/intel-mux.c
>> @@ -0,0 +1,180 @@
>> +/**
>> + * intel_mux.c - USB Port Mux support
>> + *
>> + * Copyright (C) 2016 Intel Corporation
>> + *
>> + * Author: Lu Baolu <baolu.lu@linux.intel.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +#include <linux/slab.h>
>> +#include <linux/notifier.h>
>> +#include <linux/extcon.h>
>> +#include <linux/err.h>
>> +
>> +struct intel_usb_mux {
>> +	struct device	*dev;
>> +	char		*cable_name;
>> +	int		(*cable_set_cb)(struct device *dev);
>> +	int		(*cable_unset_cb)(struct device *dev);
>> +
>> +	struct notifier_block		nb;
>> +	struct extcon_specific_cable_nb	obj;
>> +
>> +	/*
>> +	 * The state of the mux.
>> +	 * 0, 1 - mux switch state
>> +	 * -1   - uninitialized state
>> +	 */
>> +	int				mux_state;
>> +
>> +	 /* lock for mux_state */
>> +	struct mutex			mux_mutex;
>> +};
>> +
>> +static int usb_mux_change_state(struct intel_usb_mux *mux, int state)
>> +{
>> +	int ret;
>> +	struct device *dev = mux->dev;
>> +
>> +	dev_WARN_ONCE(dev,
>> +		      !mutex_is_locked(&mux->mux_mutex),
>> +		      "mutex is unlocked\n");
>> +
>> +	mux->mux_state = state;
>> +
>> +	if (mux->mux_state)
>> +		ret = mux->cable_set_cb(dev);
>> +	else
>> +		ret = mux->cable_unset_cb(dev);
>> +
>> +	return ret;
>> +}
>> +
>> +static int usb_mux_notifier(struct notifier_block *nb,
>> +			    unsigned long event, void *ptr)
>> +{
>> +	struct intel_usb_mux *mux;
>> +	int state;
>> +	int ret = NOTIFY_DONE;
>> +
>> +	mux = container_of(nb, struct intel_usb_mux, nb);
>> +
>> +	state = extcon_get_cable_state(mux->obj.edev,
>> +				       mux->cable_name);
> Use the extcon_get_cable_stet_().
>
>> +
>> +	if (mux->mux_state == -1 || mux->mux_state != state) {
>> +		mutex_lock(&mux->mux_mutex);
>> +		ret = usb_mux_change_state(mux, state);
>> +		mutex_unlock(&mux->mux_mutex);
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static ssize_t port_mux_show(struct device *dev,
>> +			     struct device_attribute *attr, char *buf)
>> +{
>> +	struct intel_usb_mux *mux = dev_get_drvdata(dev);
>> +
>> +	if (dev_WARN_ONCE(dev, !mux, "mux without data structure\n"))
>> +		return 0;
>> +
>> +	return sprintf(buf, "%s\n", mux->mux_state ? "host" : "peripheral");
>> +}
>> +
>> +static ssize_t port_mux_store(struct device *dev,
>> +			      struct device_attribute *attr,
>> +			      const char *buf, size_t count)
>> +{
>> +	struct intel_usb_mux *mux = dev_get_drvdata(dev);
>> +	int state;
>> +
>> +	if (dev_WARN_ONCE(dev, !mux, "mux without data structure\n"))
>> +		return -EINVAL;
>> +
>> +	if (sysfs_streq(buf, "peripheral"))
>> +		state = 0;
>> +	else if (sysfs_streq(buf, "host"))
>> +		state = 1;
>> +	else
>> +		return -EINVAL;
>> +
>> +	mutex_lock(&mux->mux_mutex);
>> +	usb_mux_change_state(mux, state);
>> +	mutex_unlock(&mux->mux_mutex);
>> +
>> +	return count;
>> +}
>> +static DEVICE_ATTR_RW(port_mux);
>> +
>> +int intel_usb_mux_bind_cable(struct device *dev,
>> +			     char *extcon_name,
>> +			     char *cable_name,
>> +			     int (*cable_set_cb)(struct device *dev),
>> +			     int (*cable_unset_cb)(struct device *dev))
>> +{
>> +	int ret;
>> +	struct intel_usb_mux *mux;
>> +
>> +	if (!cable_name)
>> +		return -ENODEV;
>> +
>> +	mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
>> +	if (!mux)
>> +		return -ENOMEM;
>> +
>> +	mux->dev = dev;
>> +	mux->cable_name = kstrdup(cable_name, GFP_KERNEL);
>> +	mux->cable_set_cb = cable_set_cb;
>> +	mux->cable_unset_cb = cable_unset_cb;
>> +	mux->nb.notifier_call = usb_mux_notifier;
>> +	mutex_init(&mux->mux_mutex);
>> +	mux->mux_state = -1;
>> +	dev_set_drvdata(dev, mux);
>> +	ret = extcon_register_interest(&mux->obj, extcon_name,
>> +				       cable_name, &mux->nb);
> Use the extcon_register_notifier()
>
>> +	if (ret) {
>> +		kfree(mux->cable_name);
>> +		dev_err(dev, "failed to register extcon notifier\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	usb_mux_notifier(&mux->nb, 0, NULL);
>> +
>> +	/* register the sysfs interface */
>> +	ret = device_create_file(dev, &dev_attr_port_mux);
>> +	if (ret) {
>> +		extcon_unregister_interest(&mux->obj);
> Use the extcon_unregister_notifier()
>
>> +		kfree(mux->cable_name);
>> +		dev_err(dev, "failed to create sysfs attribute\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(intel_usb_mux_bind_cable);
>> +
>> +int intel_usb_mux_unbind_cable(struct device *dev)
>> +{
>> +	struct intel_usb_mux *mux = dev_get_drvdata(dev);
>> +
>> +	device_remove_file(dev, &dev_attr_port_mux);
>> +	extcon_unregister_interest(&mux->obj);
> Use the extcon_unregister_notifier()
>
>> +	kfree(mux->cable_name);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(intel_usb_mux_unbind_cable);
>> +
>> +#ifdef CONFIG_PM_SLEEP
>> +void intel_usb_mux_complete(struct device *dev)
>> +{
>> +	struct intel_usb_mux *mux = dev_get_drvdata(dev);
>> +
>> +	usb_mux_notifier(&mux->nb, 0, NULL);
>> +}
>> +EXPORT_SYMBOL_GPL(intel_usb_mux_complete);
>> +#endif
>> diff --git a/include/linux/usb/intel-mux.h b/include/linux/usb/intel-mux.h
>> new file mode 100644
>> index 0000000..fd5612d
>> --- /dev/null
>> +++ b/include/linux/usb/intel-mux.h
>> @@ -0,0 +1,43 @@
>> +/**
>> + * intel_mux.h - USB Port Mux definitions
>> + *
>> + * Copyright (C) 2016 Intel Corporation
>> + *
>> + * Author: Lu Baolu <baolu.lu@linux.intel.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#ifndef __LINUX_USB_INTEL_MUX_H
>> +#define __LINUX_USB_INTEL_MUX_H
>> +
>> +#if IS_ENABLED(CONFIG_INTEL_USB_MUX)
>> +int intel_usb_mux_bind_cable(struct device *dev, char *extcon_name,
>> +			     char *cable_name,
>> +			     int (*cable_set_cb)(struct device *dev),
>> +			     int (*cable_unset_cb)(struct device *dev));
>> +int intel_usb_mux_unbind_cable(struct device *dev);
>> +#ifdef CONFIG_PM_SLEEP
>> +void intel_usb_mux_complete(struct device *dev);
>> +#endif
>> +
>> +#else
>> +static inline int
>> +intel_usb_mux_bind_cable(struct device *dev,
>> +			 char *extcon_name,
>> +			 char *cable_name,
>> +			 int (*cable_set_cb)(struct device *dev),
>> +			 int (*cable_unset_cb)(struct device *dev))
>> +{
>> +	return -ENODEV;
>> +}
>> +
>> +static inline int intel_usb_mux_unbind_cable(struct device *dev)
>> +{
>> +	return 0;
>> +}
>> +#endif /* CONFIG_INTEL_USB_MUX */
>> +
>> +#endif /* __LINUX_USB_INTEL_MUX_H */
>>
> Best Regards,
> Chanwoo Choi
>

[toc] | [prev] | [next] | [standalone]


#1359608 — Re: [PATCH v4 3/7] usb: mux: add common code for Intel dual role port mux

FromChanwoo Choi <cw00.choi@samsung.com>
Date2016-03-17 08:30 +0100
SubjectRe: [PATCH v4 3/7] usb: mux: add common code for Intel dual role port mux
Message-ID<rdAxA-4fU-11@gated-at.bofh.it>
In reply to#1359602
Hi Lu,

On 2016년 03월 17일 16:16, Lu Baolu wrote:
> Hi Chanwoo,
> 
> On 03/17/2016 02:07 PM, Chanwoo Choi wrote:
>> Hi Lu,
>>
>> To handle extcon (external connector), I implemented the unique id
>> for each external connector on patch[1] instead of using the ambiguous string type. 
>> [1] 2a9de9c0f08d6 (extcon: Use the unique id for external connector instead of string)
>>
>> So I recommend that you should use the unique id (ex. EXTCON_USB, EXTCON_USB_HOST)
>> with extcon_register_notifier(), extcon_get_cable_state_() and extcon_set_cable_state_().
>>
>> extcon_register_interest() is deprecated-> extcon_register_notifier()
>> extcon_get_cable_state() is deprecated -> extcon_get_cable_state_()
>> extcon_set_cable_state() is deprecated -> extcon_set_cable_state_()
> 
> Sure. I will use the new interfaces with a refreshed patch serial later.
> 
>>
>>
>> You can refer to usage for new function with unique id on patch[2]
>> [2] 5960387a2fb83 (usb: dwc3: omap: Replace deprecated API of extcon)
> 
> Thanks. That's helpful.
> 
> By the way, is extcon_get_extcon_dev() still available?

Yes.
This function find the extcon device with string name.

If extcon client driver use the platform_data to get the name of extcon device,
the extcon client driver should use the extcon_get_extcon_dev() funtcion.
Because we can not pass the point(instance) of extcon device through platform_data.

But, if extcon client driver support the devicetree,
we better to use the extcon_get_edev_by_phandle().

Best Regards,
Chanwoo Choi

> 
>>
>> I'm sorry for late reply. I add the some comment on below.
> 
> Never mind. Thank you for reminding me.
> 
> Best regards,
> Baolu
> 
>>
>>
>> On 2016년 03월 17일 14:46, Lu Baolu wrote:
>>> Several Intel PCHs and SOCs have an internal mux that is used to
>>> share one USB port between device controller and host controller.
>>>
>>> A usb port mux could be abstracted as the following elements:
>>> 1) mux state: HOST or PERIPHERAL;
>>> 2) an extcon cable which triggers the change of mux state between
>>>    HOST and PERIPHERAL;
>>> 3) The required action to do the real port switch.
>>>
>>> This patch adds the common code to handle usb port mux. With this
>>> common code, the individual mux driver, which always is platform
>>> dependent, could focus on the real operation of mux switch.
>>>
>>> Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
>>> Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
>>> Reviewed-by: Felipe Balbi <balbi@kernel.org>
>>> ---
>>>  Documentation/ABI/testing/sysfs-bus-platform |  15 +++
>>>  MAINTAINERS                                  |   7 ++
>>>  drivers/usb/Kconfig                          |   2 +
>>>  drivers/usb/Makefile                         |   1 +
>>>  drivers/usb/mux/Kconfig                      |  12 ++
>>>  drivers/usb/mux/Makefile                     |   4 +
>>>  drivers/usb/mux/intel-mux.c                  | 180 +++++++++++++++++++++++++++
>>>  include/linux/usb/intel-mux.h                |  43 +++++++
>>>  8 files changed, 264 insertions(+)
>>>  create mode 100644 drivers/usb/mux/Kconfig
>>>  create mode 100644 drivers/usb/mux/Makefile
>>>  create mode 100644 drivers/usb/mux/intel-mux.c
>>>  create mode 100644 include/linux/usb/intel-mux.h
>>>
>>> diff --git a/Documentation/ABI/testing/sysfs-bus-platform b/Documentation/ABI/testing/sysfs-bus-platform
>>> index 5172a61..23bf76e 100644
>>> --- a/Documentation/ABI/testing/sysfs-bus-platform
>>> +++ b/Documentation/ABI/testing/sysfs-bus-platform
>>> @@ -18,3 +18,18 @@ Description:
>>>  		devices to opt-out of driver binding using a driver_override
>>>  		name such as "none".  Only a single driver may be specified in
>>>  		the override, there is no support for parsing delimiters.
>>> +
>>> +What:		/sys/bus/platform/devices/.../port_mux
>>> +Date:		Febuary 2016
>>> +Contact:	Lu Baolu <baolu.lu@linux.intel.com>
>>> +Description:
>>> +		In some platforms, a single USB port is shared between a USB host
>>> +		controller and a device controller. A USB mux driver is needed to
>>> +		handle the port mux. port_mux attribute shows and stores the mux
>>> +		state.
>>> +		For read:
>>> +		'peripheral' - mux switched to PERIPHERAL controller;
>>> +		'host'       - mux switched to HOST controller.
>>> +		For write:
>>> +		'peripheral' - mux will be switched to PERIPHERAL controller;
>>> +		'host'       - mux will be switched to HOST controller.
>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>> index da3e4d8..0dbee11 100644
>>> --- a/MAINTAINERS
>>> +++ b/MAINTAINERS
>>> @@ -11399,6 +11399,13 @@ T:	git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git
>>>  S:	Maintained
>>>  F:	drivers/usb/phy/
>>>  
>>> +USB PORT MUX DRIVER
>>> +M:	Lu Baolu <baolu.lu@linux.intel.com>
>>> +L:	linux-usb@vger.kernel.org
>>> +S:	Supported
>>> +F:	include/linux/usb/intel-mux.h
>>> +F:	drivers/usb/mux/intel-mux.c
>>> +
>>>  USB PRINTER DRIVER (usblp)
>>>  M:	Pete Zaitcev <zaitcev@redhat.com>
>>>  L:	linux-usb@vger.kernel.org
>>> diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
>>> index 8ed451d..dbd6620 100644
>>> --- a/drivers/usb/Kconfig
>>> +++ b/drivers/usb/Kconfig
>>> @@ -149,6 +149,8 @@ endif # USB
>>>  
>>>  source "drivers/usb/phy/Kconfig"
>>>  
>>> +source "drivers/usb/mux/Kconfig"
>>> +
>>>  source "drivers/usb/gadget/Kconfig"
>>>  
>>>  config USB_LED_TRIG
>>> diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
>>> index d5c57f1..6433f0c 100644
>>> --- a/drivers/usb/Makefile
>>> +++ b/drivers/usb/Makefile
>>> @@ -6,6 +6,7 @@
>>>  
>>>  obj-$(CONFIG_USB)		+= core/
>>>  obj-$(CONFIG_USB_SUPPORT)	+= phy/
>>> +obj-$(CONFIG_USB_SUPPORT)	+= mux/
>>>  
>>>  obj-$(CONFIG_USB_DWC3)		+= dwc3/
>>>  obj-$(CONFIG_USB_DWC2)		+= dwc2/
>>> diff --git a/drivers/usb/mux/Kconfig b/drivers/usb/mux/Kconfig
>>> new file mode 100644
>>> index 0000000..62e2cc3
>>> --- /dev/null
>>> +++ b/drivers/usb/mux/Kconfig
>>> @@ -0,0 +1,12 @@
>>> +#
>>> +# USB port mux driver configuration
>>> +#
>>> +menu "USB Port MUX drivers"
>>> +config INTEL_USB_MUX
>>> +	select EXTCON
>>> +	def_bool n
>>> +	help
>>> +	  Common code for all Intel dual role port mux drivers. All Intel
>>> +	  usb port mux drivers should select it.
>>> +
>>> +endmenu
>>> diff --git a/drivers/usb/mux/Makefile b/drivers/usb/mux/Makefile
>>> new file mode 100644
>>> index 0000000..84f0ae8
>>> --- /dev/null
>>> +++ b/drivers/usb/mux/Makefile
>>> @@ -0,0 +1,4 @@
>>> +#
>>> +# Makefile for USB port mux drivers
>>> +#
>>> +obj-$(CONFIG_INTEL_USB_MUX)		+= intel-mux.o
>>> diff --git a/drivers/usb/mux/intel-mux.c b/drivers/usb/mux/intel-mux.c
>>> new file mode 100644
>>> index 0000000..bb7b192
>>> --- /dev/null
>>> +++ b/drivers/usb/mux/intel-mux.c
>>> @@ -0,0 +1,180 @@
>>> +/**
>>> + * intel_mux.c - USB Port Mux support
>>> + *
>>> + * Copyright (C) 2016 Intel Corporation
>>> + *
>>> + * Author: Lu Baolu <baolu.lu@linux.intel.com>
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify
>>> + * it under the terms of the GNU General Public License version 2 as
>>> + * published by the Free Software Foundation.
>>> + */
>>> +#include <linux/slab.h>
>>> +#include <linux/notifier.h>
>>> +#include <linux/extcon.h>
>>> +#include <linux/err.h>
>>> +
>>> +struct intel_usb_mux {
>>> +	struct device	*dev;
>>> +	char		*cable_name;
>>> +	int		(*cable_set_cb)(struct device *dev);
>>> +	int		(*cable_unset_cb)(struct device *dev);
>>> +
>>> +	struct notifier_block		nb;
>>> +	struct extcon_specific_cable_nb	obj;
>>> +
>>> +	/*
>>> +	 * The state of the mux.
>>> +	 * 0, 1 - mux switch state
>>> +	 * -1   - uninitialized state
>>> +	 */
>>> +	int				mux_state;
>>> +
>>> +	 /* lock for mux_state */
>>> +	struct mutex			mux_mutex;
>>> +};
>>> +
>>> +static int usb_mux_change_state(struct intel_usb_mux *mux, int state)
>>> +{
>>> +	int ret;
>>> +	struct device *dev = mux->dev;
>>> +
>>> +	dev_WARN_ONCE(dev,
>>> +		      !mutex_is_locked(&mux->mux_mutex),
>>> +		      "mutex is unlocked\n");
>>> +
>>> +	mux->mux_state = state;
>>> +
>>> +	if (mux->mux_state)
>>> +		ret = mux->cable_set_cb(dev);
>>> +	else
>>> +		ret = mux->cable_unset_cb(dev);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static int usb_mux_notifier(struct notifier_block *nb,
>>> +			    unsigned long event, void *ptr)
>>> +{
>>> +	struct intel_usb_mux *mux;
>>> +	int state;
>>> +	int ret = NOTIFY_DONE;
>>> +
>>> +	mux = container_of(nb, struct intel_usb_mux, nb);
>>> +
>>> +	state = extcon_get_cable_state(mux->obj.edev,
>>> +				       mux->cable_name);
>> Use the extcon_get_cable_stet_().
>>
>>> +
>>> +	if (mux->mux_state == -1 || mux->mux_state != state) {
>>> +		mutex_lock(&mux->mux_mutex);
>>> +		ret = usb_mux_change_state(mux, state);
>>> +		mutex_unlock(&mux->mux_mutex);
>>> +	}
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static ssize_t port_mux_show(struct device *dev,
>>> +			     struct device_attribute *attr, char *buf)
>>> +{
>>> +	struct intel_usb_mux *mux = dev_get_drvdata(dev);
>>> +
>>> +	if (dev_WARN_ONCE(dev, !mux, "mux without data structure\n"))
>>> +		return 0;
>>> +
>>> +	return sprintf(buf, "%s\n", mux->mux_state ? "host" : "peripheral");
>>> +}
>>> +
>>> +static ssize_t port_mux_store(struct device *dev,
>>> +			      struct device_attribute *attr,
>>> +			      const char *buf, size_t count)
>>> +{
>>> +	struct intel_usb_mux *mux = dev_get_drvdata(dev);
>>> +	int state;
>>> +
>>> +	if (dev_WARN_ONCE(dev, !mux, "mux without data structure\n"))
>>> +		return -EINVAL;
>>> +
>>> +	if (sysfs_streq(buf, "peripheral"))
>>> +		state = 0;
>>> +	else if (sysfs_streq(buf, "host"))
>>> +		state = 1;
>>> +	else
>>> +		return -EINVAL;
>>> +
>>> +	mutex_lock(&mux->mux_mutex);
>>> +	usb_mux_change_state(mux, state);
>>> +	mutex_unlock(&mux->mux_mutex);
>>> +
>>> +	return count;
>>> +}
>>> +static DEVICE_ATTR_RW(port_mux);
>>> +
>>> +int intel_usb_mux_bind_cable(struct device *dev,
>>> +			     char *extcon_name,
>>> +			     char *cable_name,
>>> +			     int (*cable_set_cb)(struct device *dev),
>>> +			     int (*cable_unset_cb)(struct device *dev))
>>> +{
>>> +	int ret;
>>> +	struct intel_usb_mux *mux;
>>> +
>>> +	if (!cable_name)
>>> +		return -ENODEV;
>>> +
>>> +	mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
>>> +	if (!mux)
>>> +		return -ENOMEM;
>>> +
>>> +	mux->dev = dev;
>>> +	mux->cable_name = kstrdup(cable_name, GFP_KERNEL);
>>> +	mux->cable_set_cb = cable_set_cb;
>>> +	mux->cable_unset_cb = cable_unset_cb;
>>> +	mux->nb.notifier_call = usb_mux_notifier;
>>> +	mutex_init(&mux->mux_mutex);
>>> +	mux->mux_state = -1;
>>> +	dev_set_drvdata(dev, mux);
>>> +	ret = extcon_register_interest(&mux->obj, extcon_name,
>>> +				       cable_name, &mux->nb);
>> Use the extcon_register_notifier()
>>
>>> +	if (ret) {
>>> +		kfree(mux->cable_name);
>>> +		dev_err(dev, "failed to register extcon notifier\n");
>>> +		return -ENODEV;
>>> +	}
>>> +
>>> +	usb_mux_notifier(&mux->nb, 0, NULL);
>>> +
>>> +	/* register the sysfs interface */
>>> +	ret = device_create_file(dev, &dev_attr_port_mux);
>>> +	if (ret) {
>>> +		extcon_unregister_interest(&mux->obj);
>> Use the extcon_unregister_notifier()
>>
>>> +		kfree(mux->cable_name);
>>> +		dev_err(dev, "failed to create sysfs attribute\n");
>>> +		return -ENODEV;
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(intel_usb_mux_bind_cable);
>>> +
>>> +int intel_usb_mux_unbind_cable(struct device *dev)
>>> +{
>>> +	struct intel_usb_mux *mux = dev_get_drvdata(dev);
>>> +
>>> +	device_remove_file(dev, &dev_attr_port_mux);
>>> +	extcon_unregister_interest(&mux->obj);
>> Use the extcon_unregister_notifier()
>>
>>> +	kfree(mux->cable_name);
>>> +
>>> +	return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(intel_usb_mux_unbind_cable);
>>> +
>>> +#ifdef CONFIG_PM_SLEEP
>>> +void intel_usb_mux_complete(struct device *dev)
>>> +{
>>> +	struct intel_usb_mux *mux = dev_get_drvdata(dev);
>>> +
>>> +	usb_mux_notifier(&mux->nb, 0, NULL);
>>> +}
>>> +EXPORT_SYMBOL_GPL(intel_usb_mux_complete);
>>> +#endif
>>> diff --git a/include/linux/usb/intel-mux.h b/include/linux/usb/intel-mux.h
>>> new file mode 100644
>>> index 0000000..fd5612d
>>> --- /dev/null
>>> +++ b/include/linux/usb/intel-mux.h
>>> @@ -0,0 +1,43 @@
>>> +/**
>>> + * intel_mux.h - USB Port Mux definitions
>>> + *
>>> + * Copyright (C) 2016 Intel Corporation
>>> + *
>>> + * Author: Lu Baolu <baolu.lu@linux.intel.com>
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify
>>> + * it under the terms of the GNU General Public License version 2 as
>>> + * published by the Free Software Foundation.
>>> + */
>>> +
>>> +#ifndef __LINUX_USB_INTEL_MUX_H
>>> +#define __LINUX_USB_INTEL_MUX_H
>>> +
>>> +#if IS_ENABLED(CONFIG_INTEL_USB_MUX)
>>> +int intel_usb_mux_bind_cable(struct device *dev, char *extcon_name,
>>> +			     char *cable_name,
>>> +			     int (*cable_set_cb)(struct device *dev),
>>> +			     int (*cable_unset_cb)(struct device *dev));
>>> +int intel_usb_mux_unbind_cable(struct device *dev);
>>> +#ifdef CONFIG_PM_SLEEP
>>> +void intel_usb_mux_complete(struct device *dev);
>>> +#endif
>>> +
>>> +#else
>>> +static inline int
>>> +intel_usb_mux_bind_cable(struct device *dev,
>>> +			 char *extcon_name,
>>> +			 char *cable_name,
>>> +			 int (*cable_set_cb)(struct device *dev),
>>> +			 int (*cable_unset_cb)(struct device *dev))
>>> +{
>>> +	return -ENODEV;
>>> +}
>>> +
>>> +static inline int intel_usb_mux_unbind_cable(struct device *dev)
>>> +{
>>> +	return 0;
>>> +}
>>> +#endif /* CONFIG_INTEL_USB_MUX */
>>> +
>>> +#endif /* __LINUX_USB_INTEL_MUX_H */
>>>
>> Best Regards,
>> Chanwoo Choi
>>
> 
> 
> 

[toc] | [prev] | [next] | [standalone]


#1359612 — Re: [PATCH v4 3/7] usb: mux: add common code for Intel dual role port mux

FromLu Baolu <baolu.lu@linux.intel.com>
Date2016-03-17 08:50 +0100
SubjectRe: [PATCH v4 3/7] usb: mux: add common code for Intel dual role port mux
Message-ID<rdAQV-4m7-1@gated-at.bofh.it>
In reply to#1359608

On 03/17/2016 03:27 PM, Chanwoo Choi wrote:
> Hi Lu,
>
> On 2016년 03월 17일 16:16, Lu Baolu wrote:
>> Hi Chanwoo,
>>
>> On 03/17/2016 02:07 PM, Chanwoo Choi wrote:
>>> Hi Lu,
>>>
>>> To handle extcon (external connector), I implemented the unique id
>>> for each external connector on patch[1] instead of using the ambiguous string type. 
>>> [1] 2a9de9c0f08d6 (extcon: Use the unique id for external connector instead of string)
>>>
>>> So I recommend that you should use the unique id (ex. EXTCON_USB, EXTCON_USB_HOST)
>>> with extcon_register_notifier(), extcon_get_cable_state_() and extcon_set_cable_state_().
>>>
>>> extcon_register_interest() is deprecated-> extcon_register_notifier()
>>> extcon_get_cable_state() is deprecated -> extcon_get_cable_state_()
>>> extcon_set_cable_state() is deprecated -> extcon_set_cable_state_()
>> Sure. I will use the new interfaces with a refreshed patch serial later.
>>
>>>
>>> You can refer to usage for new function with unique id on patch[2]
>>> [2] 5960387a2fb83 (usb: dwc3: omap: Replace deprecated API of extcon)
>> Thanks. That's helpful.
>>
>> By the way, is extcon_get_extcon_dev() still available?
> Yes.
> This function find the extcon device with string name.
>
> If extcon client driver use the platform_data to get the name of extcon device,
> the extcon client driver should use the extcon_get_extcon_dev() funtcion.
> Because we can not pass the point(instance) of extcon device through platform_data.
>
> But, if extcon client driver support the devicetree,
> we better to use the extcon_get_edev_by_phandle().

That's clear. Thank you!

Best regards,
Baolu

>
> Best Regards,
> Chanwoo Choi
>
>>> I'm sorry for late reply. I add the some comment on below.
>> Never mind. Thank you for reminding me.
>>
>> Best regards,
>> Baolu
>>
>>>
>>> On 2016년 03월 17일 14:46, Lu Baolu wrote:
>>>> Several Intel PCHs and SOCs have an internal mux that is used to
>>>> share one USB port between device controller and host controller.
>>>>
>>>> A usb port mux could be abstracted as the following elements:
>>>> 1) mux state: HOST or PERIPHERAL;
>>>> 2) an extcon cable which triggers the change of mux state between
>>>>    HOST and PERIPHERAL;
>>>> 3) The required action to do the real port switch.
>>>>
>>>> This patch adds the common code to handle usb port mux. With this
>>>> common code, the individual mux driver, which always is platform
>>>> dependent, could focus on the real operation of mux switch.
>>>>
>>>> Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
>>>> Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
>>>> Reviewed-by: Felipe Balbi <balbi@kernel.org>
>>>> ---
>>>>  Documentation/ABI/testing/sysfs-bus-platform |  15 +++
>>>>  MAINTAINERS                                  |   7 ++
>>>>  drivers/usb/Kconfig                          |   2 +
>>>>  drivers/usb/Makefile                         |   1 +
>>>>  drivers/usb/mux/Kconfig                      |  12 ++
>>>>  drivers/usb/mux/Makefile                     |   4 +
>>>>  drivers/usb/mux/intel-mux.c                  | 180 +++++++++++++++++++++++++++
>>>>  include/linux/usb/intel-mux.h                |  43 +++++++
>>>>  8 files changed, 264 insertions(+)
>>>>  create mode 100644 drivers/usb/mux/Kconfig
>>>>  create mode 100644 drivers/usb/mux/Makefile
>>>>  create mode 100644 drivers/usb/mux/intel-mux.c
>>>>  create mode 100644 include/linux/usb/intel-mux.h
>>>>
>>>> diff --git a/Documentation/ABI/testing/sysfs-bus-platform b/Documentation/ABI/testing/sysfs-bus-platform
>>>> index 5172a61..23bf76e 100644
>>>> --- a/Documentation/ABI/testing/sysfs-bus-platform
>>>> +++ b/Documentation/ABI/testing/sysfs-bus-platform
>>>> @@ -18,3 +18,18 @@ Description:
>>>>  		devices to opt-out of driver binding using a driver_override
>>>>  		name such as "none".  Only a single driver may be specified in
>>>>  		the override, there is no support for parsing delimiters.
>>>> +
>>>> +What:		/sys/bus/platform/devices/.../port_mux
>>>> +Date:		Febuary 2016
>>>> +Contact:	Lu Baolu <baolu.lu@linux.intel.com>
>>>> +Description:
>>>> +		In some platforms, a single USB port is shared between a USB host
>>>> +		controller and a device controller. A USB mux driver is needed to
>>>> +		handle the port mux. port_mux attribute shows and stores the mux
>>>> +		state.
>>>> +		For read:
>>>> +		'peripheral' - mux switched to PERIPHERAL controller;
>>>> +		'host'       - mux switched to HOST controller.
>>>> +		For write:
>>>> +		'peripheral' - mux will be switched to PERIPHERAL controller;
>>>> +		'host'       - mux will be switched to HOST controller.
>>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>>> index da3e4d8..0dbee11 100644
>>>> --- a/MAINTAINERS
>>>> +++ b/MAINTAINERS
>>>> @@ -11399,6 +11399,13 @@ T:	git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git
>>>>  S:	Maintained
>>>>  F:	drivers/usb/phy/
>>>>  
>>>> +USB PORT MUX DRIVER
>>>> +M:	Lu Baolu <baolu.lu@linux.intel.com>
>>>> +L:	linux-usb@vger.kernel.org
>>>> +S:	Supported
>>>> +F:	include/linux/usb/intel-mux.h
>>>> +F:	drivers/usb/mux/intel-mux.c
>>>> +
>>>>  USB PRINTER DRIVER (usblp)
>>>>  M:	Pete Zaitcev <zaitcev@redhat.com>
>>>>  L:	linux-usb@vger.kernel.org
>>>> diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
>>>> index 8ed451d..dbd6620 100644
>>>> --- a/drivers/usb/Kconfig
>>>> +++ b/drivers/usb/Kconfig
>>>> @@ -149,6 +149,8 @@ endif # USB
>>>>  
>>>>  source "drivers/usb/phy/Kconfig"
>>>>  
>>>> +source "drivers/usb/mux/Kconfig"
>>>> +
>>>>  source "drivers/usb/gadget/Kconfig"
>>>>  
>>>>  config USB_LED_TRIG
>>>> diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
>>>> index d5c57f1..6433f0c 100644
>>>> --- a/drivers/usb/Makefile
>>>> +++ b/drivers/usb/Makefile
>>>> @@ -6,6 +6,7 @@
>>>>  
>>>>  obj-$(CONFIG_USB)		+= core/
>>>>  obj-$(CONFIG_USB_SUPPORT)	+= phy/
>>>> +obj-$(CONFIG_USB_SUPPORT)	+= mux/
>>>>  
>>>>  obj-$(CONFIG_USB_DWC3)		+= dwc3/
>>>>  obj-$(CONFIG_USB_DWC2)		+= dwc2/
>>>> diff --git a/drivers/usb/mux/Kconfig b/drivers/usb/mux/Kconfig
>>>> new file mode 100644
>>>> index 0000000..62e2cc3
>>>> --- /dev/null
>>>> +++ b/drivers/usb/mux/Kconfig
>>>> @@ -0,0 +1,12 @@
>>>> +#
>>>> +# USB port mux driver configuration
>>>> +#
>>>> +menu "USB Port MUX drivers"
>>>> +config INTEL_USB_MUX
>>>> +	select EXTCON
>>>> +	def_bool n
>>>> +	help
>>>> +	  Common code for all Intel dual role port mux drivers. All Intel
>>>> +	  usb port mux drivers should select it.
>>>> +
>>>> +endmenu
>>>> diff --git a/drivers/usb/mux/Makefile b/drivers/usb/mux/Makefile
>>>> new file mode 100644
>>>> index 0000000..84f0ae8
>>>> --- /dev/null
>>>> +++ b/drivers/usb/mux/Makefile
>>>> @@ -0,0 +1,4 @@
>>>> +#
>>>> +# Makefile for USB port mux drivers
>>>> +#
>>>> +obj-$(CONFIG_INTEL_USB_MUX)		+= intel-mux.o
>>>> diff --git a/drivers/usb/mux/intel-mux.c b/drivers/usb/mux/intel-mux.c
>>>> new file mode 100644
>>>> index 0000000..bb7b192
>>>> --- /dev/null
>>>> +++ b/drivers/usb/mux/intel-mux.c
>>>> @@ -0,0 +1,180 @@
>>>> +/**
>>>> + * intel_mux.c - USB Port Mux support
>>>> + *
>>>> + * Copyright (C) 2016 Intel Corporation
>>>> + *
>>>> + * Author: Lu Baolu <baolu.lu@linux.intel.com>
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or modify
>>>> + * it under the terms of the GNU General Public License version 2 as
>>>> + * published by the Free Software Foundation.
>>>> + */
>>>> +#include <linux/slab.h>
>>>> +#include <linux/notifier.h>
>>>> +#include <linux/extcon.h>
>>>> +#include <linux/err.h>
>>>> +
>>>> +struct intel_usb_mux {
>>>> +	struct device	*dev;
>>>> +	char		*cable_name;
>>>> +	int		(*cable_set_cb)(struct device *dev);
>>>> +	int		(*cable_unset_cb)(struct device *dev);
>>>> +
>>>> +	struct notifier_block		nb;
>>>> +	struct extcon_specific_cable_nb	obj;
>>>> +
>>>> +	/*
>>>> +	 * The state of the mux.
>>>> +	 * 0, 1 - mux switch state
>>>> +	 * -1   - uninitialized state
>>>> +	 */
>>>> +	int				mux_state;
>>>> +
>>>> +	 /* lock for mux_state */
>>>> +	struct mutex			mux_mutex;
>>>> +};
>>>> +
>>>> +static int usb_mux_change_state(struct intel_usb_mux *mux, int state)
>>>> +{
>>>> +	int ret;
>>>> +	struct device *dev = mux->dev;
>>>> +
>>>> +	dev_WARN_ONCE(dev,
>>>> +		      !mutex_is_locked(&mux->mux_mutex),
>>>> +		      "mutex is unlocked\n");
>>>> +
>>>> +	mux->mux_state = state;
>>>> +
>>>> +	if (mux->mux_state)
>>>> +		ret = mux->cable_set_cb(dev);
>>>> +	else
>>>> +		ret = mux->cable_unset_cb(dev);
>>>> +
>>>> +	return ret;
>>>> +}
>>>> +
>>>> +static int usb_mux_notifier(struct notifier_block *nb,
>>>> +			    unsigned long event, void *ptr)
>>>> +{
>>>> +	struct intel_usb_mux *mux;
>>>> +	int state;
>>>> +	int ret = NOTIFY_DONE;
>>>> +
>>>> +	mux = container_of(nb, struct intel_usb_mux, nb);
>>>> +
>>>> +	state = extcon_get_cable_state(mux->obj.edev,
>>>> +				       mux->cable_name);
>>> Use the extcon_get_cable_stet_().
>>>
>>>> +
>>>> +	if (mux->mux_state == -1 || mux->mux_state != state) {
>>>> +		mutex_lock(&mux->mux_mutex);
>>>> +		ret = usb_mux_change_state(mux, state);
>>>> +		mutex_unlock(&mux->mux_mutex);
>>>> +	}
>>>> +
>>>> +	return ret;
>>>> +}
>>>> +
>>>> +static ssize_t port_mux_show(struct device *dev,
>>>> +			     struct device_attribute *attr, char *buf)
>>>> +{
>>>> +	struct intel_usb_mux *mux = dev_get_drvdata(dev);
>>>> +
>>>> +	if (dev_WARN_ONCE(dev, !mux, "mux without data structure\n"))
>>>> +		return 0;
>>>> +
>>>> +	return sprintf(buf, "%s\n", mux->mux_state ? "host" : "peripheral");
>>>> +}
>>>> +
>>>> +static ssize_t port_mux_store(struct device *dev,
>>>> +			      struct device_attribute *attr,
>>>> +			      const char *buf, size_t count)
>>>> +{
>>>> +	struct intel_usb_mux *mux = dev_get_drvdata(dev);
>>>> +	int state;
>>>> +
>>>> +	if (dev_WARN_ONCE(dev, !mux, "mux without data structure\n"))
>>>> +		return -EINVAL;
>>>> +
>>>> +	if (sysfs_streq(buf, "peripheral"))
>>>> +		state = 0;
>>>> +	else if (sysfs_streq(buf, "host"))
>>>> +		state = 1;
>>>> +	else
>>>> +		return -EINVAL;
>>>> +
>>>> +	mutex_lock(&mux->mux_mutex);
>>>> +	usb_mux_change_state(mux, state);
>>>> +	mutex_unlock(&mux->mux_mutex);
>>>> +
>>>> +	return count;
>>>> +}
>>>> +static DEVICE_ATTR_RW(port_mux);
>>>> +
>>>> +int intel_usb_mux_bind_cable(struct device *dev,
>>>> +			     char *extcon_name,
>>>> +			     char *cable_name,
>>>> +			     int (*cable_set_cb)(struct device *dev),
>>>> +			     int (*cable_unset_cb)(struct device *dev))
>>>> +{
>>>> +	int ret;
>>>> +	struct intel_usb_mux *mux;
>>>> +
>>>> +	if (!cable_name)
>>>> +		return -ENODEV;
>>>> +
>>>> +	mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
>>>> +	if (!mux)
>>>> +		return -ENOMEM;
>>>> +
>>>> +	mux->dev = dev;
>>>> +	mux->cable_name = kstrdup(cable_name, GFP_KERNEL);
>>>> +	mux->cable_set_cb = cable_set_cb;
>>>> +	mux->cable_unset_cb = cable_unset_cb;
>>>> +	mux->nb.notifier_call = usb_mux_notifier;
>>>> +	mutex_init(&mux->mux_mutex);
>>>> +	mux->mux_state = -1;
>>>> +	dev_set_drvdata(dev, mux);
>>>> +	ret = extcon_register_interest(&mux->obj, extcon_name,
>>>> +				       cable_name, &mux->nb);
>>> Use the extcon_register_notifier()
>>>
>>>> +	if (ret) {
>>>> +		kfree(mux->cable_name);
>>>> +		dev_err(dev, "failed to register extcon notifier\n");
>>>> +		return -ENODEV;
>>>> +	}
>>>> +
>>>> +	usb_mux_notifier(&mux->nb, 0, NULL);
>>>> +
>>>> +	/* register the sysfs interface */
>>>> +	ret = device_create_file(dev, &dev_attr_port_mux);
>>>> +	if (ret) {
>>>> +		extcon_unregister_interest(&mux->obj);
>>> Use the extcon_unregister_notifier()
>>>
>>>> +		kfree(mux->cable_name);
>>>> +		dev_err(dev, "failed to create sysfs attribute\n");
>>>> +		return -ENODEV;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(intel_usb_mux_bind_cable);
>>>> +
>>>> +int intel_usb_mux_unbind_cable(struct device *dev)
>>>> +{
>>>> +	struct intel_usb_mux *mux = dev_get_drvdata(dev);
>>>> +
>>>> +	device_remove_file(dev, &dev_attr_port_mux);
>>>> +	extcon_unregister_interest(&mux->obj);
>>> Use the extcon_unregister_notifier()
>>>
>>>> +	kfree(mux->cable_name);
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(intel_usb_mux_unbind_cable);
>>>> +
>>>> +#ifdef CONFIG_PM_SLEEP
>>>> +void intel_usb_mux_complete(struct device *dev)
>>>> +{
>>>> +	struct intel_usb_mux *mux = dev_get_drvdata(dev);
>>>> +
>>>> +	usb_mux_notifier(&mux->nb, 0, NULL);
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(intel_usb_mux_complete);
>>>> +#endif
>>>> diff --git a/include/linux/usb/intel-mux.h b/include/linux/usb/intel-mux.h
>>>> new file mode 100644
>>>> index 0000000..fd5612d
>>>> --- /dev/null
>>>> +++ b/include/linux/usb/intel-mux.h
>>>> @@ -0,0 +1,43 @@
>>>> +/**
>>>> + * intel_mux.h - USB Port Mux definitions
>>>> + *
>>>> + * Copyright (C) 2016 Intel Corporation
>>>> + *
>>>> + * Author: Lu Baolu <baolu.lu@linux.intel.com>
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or modify
>>>> + * it under the terms of the GNU General Public License version 2 as
>>>> + * published by the Free Software Foundation.
>>>> + */
>>>> +
>>>> +#ifndef __LINUX_USB_INTEL_MUX_H
>>>> +#define __LINUX_USB_INTEL_MUX_H
>>>> +
>>>> +#if IS_ENABLED(CONFIG_INTEL_USB_MUX)
>>>> +int intel_usb_mux_bind_cable(struct device *dev, char *extcon_name,
>>>> +			     char *cable_name,
>>>> +			     int (*cable_set_cb)(struct device *dev),
>>>> +			     int (*cable_unset_cb)(struct device *dev));
>>>> +int intel_usb_mux_unbind_cable(struct device *dev);
>>>> +#ifdef CONFIG_PM_SLEEP
>>>> +void intel_usb_mux_complete(struct device *dev);
>>>> +#endif
>>>> +
>>>> +#else
>>>> +static inline int
>>>> +intel_usb_mux_bind_cable(struct device *dev,
>>>> +			 char *extcon_name,
>>>> +			 char *cable_name,
>>>> +			 int (*cable_set_cb)(struct device *dev),
>>>> +			 int (*cable_unset_cb)(struct device *dev))
>>>> +{
>>>> +	return -ENODEV;
>>>> +}
>>>> +
>>>> +static inline int intel_usb_mux_unbind_cable(struct device *dev)
>>>> +{
>>>> +	return 0;
>>>> +}
>>>> +#endif /* CONFIG_INTEL_USB_MUX */
>>>> +
>>>> +#endif /* __LINUX_USB_INTEL_MUX_H */
>>>>
>>> Best Regards,
>>> Chanwoo Choi
>>>
>>
>>
>

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web