linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: David Cohen <david.a.cohen@linux.intel.com>
To: myungjoo.ham@samsung.com, cw00.choi@samsung.com
Cc: linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org,
	baolu.lu@linux.intel.com,
	David Cohen <david.a.cohen@linux.intel.com>
Subject: [RFC/PATCH] extcon: otg_gpio: add driver for USB OTG port controlled by GPIO(s)
Date: Mon, 22 Dec 2014 14:43:37 -0800	[thread overview]
Message-ID: <1419288217-19262-1-git-send-email-david.a.cohen@linux.intel.com> (raw)

Some platforms have an USB OTG port fully (or partially) controlled by
GPIOs:

(1) USB ID is connected directly to GPIO

Optionally:
(2) VBUS is enabled by a GPIO (when ID is grounded)
(3) Platform has 2 USB controllers connected to same port: one for
    device and one for host role. D+/- are switched between phys
    by GPIO.

As per initial version, this driver has the duty to control whether
USB-Host cable is plugged in or not:
 - If yes, OTG port is configured for host role
 - If no, by standard, the OTG port is configured for device role

Signed-off-by: David Cohen <david.a.cohen@linux.intel.com>
---

Hi,

Some Intel Bay Trail boards have an unusual way to handle the USB OTG port:
 - The USB ID pin is connected directly to GPIO on SoC
 - When in host role, VBUS is provided by enabling a GPIO
 - Device and host roles are supported by 2 independent controllers with D+/-
   pins from port switched between different phys according a GPIO level.

The ACPI table describes this USB port as a (virtual) device with all the
necessary GPIOs. This driver implements support to this virtual device as an
extcon class driver. All drivers that depend on the USB OTG port state (USB phy,
PMIC, charge detection) will listen to extcon events.

Comments are welcome.

Br, David
---

 drivers/extcon/Kconfig           |   8 ++
 drivers/extcon/Makefile          |   1 +
 drivers/extcon/extcon-otg_gpio.c | 200 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 209 insertions(+)
 create mode 100644 drivers/extcon/extcon-otg_gpio.c

diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig
index 6a1f7de6fa54..e8010cda4642 100644
--- a/drivers/extcon/Kconfig
+++ b/drivers/extcon/Kconfig
@@ -93,4 +93,12 @@ config EXTCON_SM5502
 	  Silicon Mitus SM5502. The SM5502 is a USB port accessory
 	  detector and switch.
 
+config EXTCON_OTG_GPIO
+	tristate "VIRTUAL USB OTG PORT support"
+	depends on GPIOLIB
+	help
+	  Say Y here to enable support for virtual USB OTG port device
+	  controlled by GPIOs. This driver can be used when at least USB ID pin
+	  is connected directly to GPIO.
+
 endif # MULTISTATE_SWITCH
diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile
index 0370b42e5a27..9e81088c6584 100644
--- a/drivers/extcon/Makefile
+++ b/drivers/extcon/Makefile
@@ -12,3 +12,4 @@ obj-$(CONFIG_EXTCON_MAX8997)	+= extcon-max8997.o
 obj-$(CONFIG_EXTCON_PALMAS)	+= extcon-palmas.o
 obj-$(CONFIG_EXTCON_RT8973A)	+= extcon-rt8973a.o
 obj-$(CONFIG_EXTCON_SM5502)	+= extcon-sm5502.o
+obj-$(CONFIG_EXTCON_OTG_GPIO) += extcon-otg_gpio.o
diff --git a/drivers/extcon/extcon-otg_gpio.c b/drivers/extcon/extcon-otg_gpio.c
new file mode 100644
index 000000000000..c5ee765a5f4f
--- /dev/null
+++ b/drivers/extcon/extcon-otg_gpio.c
@@ -0,0 +1,200 @@
+/*
+ * Virtual USB OTG Port driver controlled by gpios
+ *
+ * Copyright (c) 2014, Intel Corporation.
+ * Author: David Cohen <david.a.cohen@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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/acpi.h>
+#include <linux/extcon.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#define DRV_NAME	"usb_otg_port"
+
+struct vuport {
+	struct device *dev;
+	struct gpio_desc *gpio_vbus_en;
+	struct gpio_desc *gpio_usb_id;
+	struct gpio_desc *gpio_usb_mux;
+
+	struct extcon_dev edev;
+};
+
+static const char *const vuport_extcon_cable[] = {
+	[0] = "USB-Host",
+	NULL,
+};
+
+/*
+ * If id == 1, USB port should be set to peripheral
+ * if id == 0, USB port should be set to host
+ *
+ * Peripheral: set USB mux to peripheral and disable VBUS
+ * Host: set USB mux to host and enable VBUS
+ */
+static void vuport_set_port(struct vuport *vup, int id)
+{
+	int mux_val = id;
+	int vbus_val = !id;
+
+	if (!IS_ERR(vup->gpio_usb_mux))
+		gpiod_direction_output(vup->gpio_usb_mux, mux_val);
+
+	if (!IS_ERR(vup->gpio_vbus_en))
+		gpiod_direction_output(vup->gpio_vbus_en, vbus_val);
+}
+
+static void vuport_do_usb_id(struct vuport *vup)
+{
+	int id = gpiod_get_value(vup->gpio_usb_id);
+
+	dev_info(vup->dev, "USB PORT ID: %s\n", id ? "PERIPHERAL" : "HOST");
+
+	/*
+	 * id == 1: PERIPHERAL
+	 * id == 0: HOST
+	 */
+	vuport_set_port(vup, id);
+
+	/*
+	 * id == 0: HOST connected
+	 * id == 1: Host disconnected
+	 */
+	extcon_set_cable_state(&vup->edev, "USB-Host", !id);
+}
+
+static irqreturn_t vuport_thread_isr(int irq, void *priv)
+{
+	struct vuport *vup = priv;
+
+	vuport_do_usb_id(vup);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t vuport_isr(int irq, void *priv)
+{
+	return IRQ_WAKE_THREAD;
+}
+
+#define VUPORT_GPIO_USB_ID	0
+#define VUPORT_GPIO_VBUS_EN	1
+#define VUPORT_GPIO_USB_MUX	2
+static int vuport_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct vuport *vup;
+	int ret;
+
+	vup = devm_kzalloc(dev, sizeof(*vup), GFP_KERNEL);
+	if (!vup) {
+		dev_err(dev, "cannot allocate private data\n");
+		return -ENOMEM;
+	}
+	vup->dev = dev;
+
+	vup->gpio_usb_id = devm_gpiod_get_index(dev, "id", VUPORT_GPIO_USB_ID);
+	if (IS_ERR(vup->gpio_usb_id)) {
+		dev_err(dev, "cannot request USB ID GPIO: ret = %ld\n",
+			PTR_ERR(vup->gpio_usb_id));
+		return PTR_ERR(vup->gpio_usb_id);
+	}
+
+	ret = gpiod_direction_input(vup->gpio_usb_id);
+	if (ret < 0) {
+		dev_err(dev, "cannot set input direction to USB ID GPIO: ret = %d\n",
+			ret);
+		return ret;
+	}
+
+	vup->gpio_vbus_en = devm_gpiod_get_index(dev, "vbus en",
+						 VUPORT_GPIO_VBUS_EN);
+	if (IS_ERR(vup->gpio_vbus_en))
+		dev_info(dev, "cannot request VBUS EN GPIO, skipping it.\n");
+
+	vup->gpio_usb_mux = devm_gpiod_get_index(dev, "usb mux",
+						 VUPORT_GPIO_USB_MUX);
+	if (IS_ERR(vup->gpio_usb_mux))
+		dev_info(dev, "cannot request USB USB MUX, skipping it.\n");
+
+	/* register extcon device */
+	vup->edev.dev.parent = dev;
+	vup->edev.supported_cable = vuport_extcon_cable;
+	ret = extcon_dev_register(&vup->edev);
+	if (ret < 0) {
+		dev_err(dev, "failed to register extcon device: ret = %d\n",
+			ret);
+		return ret;
+	}
+
+	ret = devm_request_threaded_irq(dev, gpiod_to_irq(vup->gpio_usb_id),
+					vuport_isr, vuport_thread_isr,
+					IRQF_SHARED | IRQF_TRIGGER_RISING |
+					IRQF_TRIGGER_FALLING,
+					dev_name(dev), vup);
+	if (ret < 0) {
+		dev_err(dev, "cannot request IRQ for USB ID GPIO: ret = %d\n",
+			ret);
+		goto irq_err;
+	}
+	vuport_do_usb_id(vup);
+
+	platform_set_drvdata(pdev, vup);
+
+	dev_info(dev, "driver successfully probed\n");
+
+	return 0;
+
+irq_err:
+	extcon_dev_unregister(&vup->edev);
+
+	return ret;
+}
+
+static int vuport_remove(struct platform_device *pdev)
+{
+	struct vuport *vup = platform_get_drvdata(pdev);
+
+	extcon_dev_unregister(&vup->edev);
+	return 0;
+}
+
+static struct acpi_device_id vuport_acpi_match[] = {
+	{ "INT3496" },
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, vuport_acpi_match);
+
+static struct platform_driver vuport_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.acpi_match_table = ACPI_PTR(vuport_acpi_match),
+	},
+	.probe = vuport_probe,
+	.remove = vuport_remove,
+};
+
+static int __init vuport_init(void)
+{
+	return platform_driver_register(&vuport_driver);
+}
+subsys_initcall(vuport_init);
+
+static void __exit vuport_exit(void)
+{
+	platform_driver_unregister(&vuport_driver);
+}
+module_exit(vuport_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Cohen <david.a.cohen@linux.intel.com>");
-- 
2.1.1


             reply	other threads:[~2014-12-22 22:42 UTC|newest]

Thread overview: 41+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-12-22 22:43 David Cohen [this message]
2014-12-23  1:25 ` [RFC/PATCH] extcon: otg_gpio: add driver for USB OTG port controlled by GPIO(s) Peter Chen
2014-12-23 19:40   ` David Cohen
2014-12-24  3:08     ` Peter Chen
2014-12-24 22:46       ` David Cohen
2014-12-23 13:10 ` Sergei Shtylyov
2014-12-23 19:57   ` David Cohen
2014-12-23 20:09     ` Sergei Shtylyov
2014-12-23 20:43       ` David Cohen
2014-12-24  0:29 ` Felipe Balbi
2014-12-24 22:43   ` David Cohen
2014-12-26  4:49     ` Felipe Balbi
2015-02-17 19:18       ` David Cohen
2015-02-17 19:25         ` Felipe Balbi
2015-02-17 19:35           ` David Cohen
2015-02-18 10:17             ` Mika Westerberg
2015-02-18 17:53               ` David Cohen
2015-01-08 19:23 ` Linus Walleij
2015-02-17 19:20   ` David Cohen
2015-02-19 19:59 ` [PATCH v2] " David Cohen
2015-02-19 22:39   ` Paul Bolle
2015-02-20 19:02     ` David Cohen
2015-02-20 19:09       ` Felipe Balbi
2015-02-20 19:18         ` David Cohen
2015-02-20 19:10       ` Paul Bolle
2015-02-20 19:18         ` David Cohen
2015-02-20  6:41   ` Robert Baldyga
2015-02-20  9:53     ` Linus Walleij
2015-02-20 19:17       ` David Cohen
2015-02-20 19:36         ` Felipe Balbi
2015-02-20 19:59           ` David Cohen
2015-02-20 20:00             ` Felipe Balbi
2015-02-20 20:40               ` David Cohen
2015-03-07 20:03                 ` Linus Walleij
2015-02-24 19:18         ` David Cohen
2015-03-07 20:06         ` Linus Walleij
2015-03-09 16:16           ` Felipe Balbi
2015-03-09 19:10             ` David Cohen
2015-03-16 16:46               ` David Cohen
2015-03-16 16:46                 ` David Cohen
2015-03-19  8:18               ` Linus Walleij

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1419288217-19262-1-git-send-email-david.a.cohen@linux.intel.com \
    --to=david.a.cohen@linux.intel.com \
    --cc=baolu.lu@linux.intel.com \
    --cc=cw00.choi@samsung.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=myungjoo.ham@samsung.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).