Hi David, On 02/19/2015 08:59 PM, David Cohen wrote: > Some Intel platforms have an USB OTG port fully (or partially) > controlled by GPIOs: > > (1) USB ID is connected directly to a pulled up GPIO. > > Optionally: > (2) VBUS is enabled/disabled by a GPIO > (3) Platform has 2 USB controllers connected to same port: one for > device and one for host role. D+/- are switched between phys. > according to this GPIO level. > > This driver configures USB OTG port for device or host role according to > USB ID value. > - If USB ID's GPIO level is low, OTG port is configured for host role > by sourcing VBUS and switching D+/- to host phy. > - If USB ID's GPIO level is high, by standard, the OTG port is > configured for device role by not sourcing VBUS and switching D+/- to > device controller. IMO it's not very elegant to handle VBUS power on/off in extcon driver. Creating fixed regulator would allow to make VBUS handling more generic. Then extcon client could handle VBUS regulator. Also D+/- routing would be separated but I don't see better place for it. Odroid platforms also uses GPIO for USB cable detection so your driver would be useful in that case. I created some simple driver for it but I see I could use your driver after adding VBUS detection. BTW don't you have VBUS detection on your board? It would allow to distinguish between USB, USB-Host and disconnected states. -- Robert > > Signed-off-by: David Cohen <david.a.cohen@xxxxxxxxxxxxxxx> > --- > > Hi, > > Since splitting this driver into smaller pieces would result in ugly fixes WRT > ACPI, I'm resending the same approach. > This time I addressed all comments I got from RFC version. > > As always, comments are welcome. > > Br, David > --- > > drivers/extcon/Kconfig | 8 ++ > drivers/extcon/Makefile | 1 + > drivers/extcon/extcon-otg_gpio.c | 186 +++++++++++++++++++++++++++++++++++++++ > 3 files changed, 195 insertions(+) > create mode 100644 drivers/extcon/extcon-otg_gpio.c > > diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig > index 6a1f7de6fa54..986ca7da9c1b 100644 > --- a/drivers/extcon/Kconfig > +++ b/drivers/extcon/Kconfig > @@ -63,6 +63,14 @@ config EXTCON_MAX8997 > Maxim MAX8997 PMIC. The MAX8997 MUIC is a USB port accessory > detector and switch. > > +config EXTCON_OTG_GPIO > + tristate "VIRTUAL USB OTG PORT support" > + depends on GPIOLIB && ACPI > + 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. > + > config EXTCON_PALMAS > tristate "Palmas USB EXTCON support" > depends on MFD_PALMAS > diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile > index 0370b42e5a27..0d5b152b51ea 100644 > --- a/drivers/extcon/Makefile > +++ b/drivers/extcon/Makefile > @@ -9,6 +9,7 @@ obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o > obj-$(CONFIG_EXTCON_MAX14577) += extcon-max14577.o > obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o > obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o > +obj-$(CONFIG_EXTCON_OTG_GPIO) += extcon-otg_gpio.o > obj-$(CONFIG_EXTCON_PALMAS) += extcon-palmas.o > obj-$(CONFIG_EXTCON_RT8973A) += extcon-rt8973a.o > obj-$(CONFIG_EXTCON_SM5502) += extcon-sm5502.o > diff --git a/drivers/extcon/extcon-otg_gpio.c b/drivers/extcon/extcon-otg_gpio.c > new file mode 100644 > index 000000000000..7dfa2c15b562 > --- /dev/null > +++ b/drivers/extcon/extcon-otg_gpio.c > @@ -0,0 +1,186 @@ > +/* > + * Virtual USB OTG Port driver controlled by gpios > + * > + * Copyright (c) 2014, Intel Corporation. > + * Author: David Cohen <david.a.cohen@xxxxxxxxxxxxxxx> > + * > + * 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/consumer.h> > +#include <linux/interrupt.h> > +#include <linux/module.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 *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; > +} > + > +#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_dbg(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_dbg(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), > + NULL, vuport_thread_isr, > + IRQF_SHARED | IRQF_TRIGGER_RISING | > + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, > + 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_dbg(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, > +}; > + > +module_platform_driver(vuport_driver); > + > +MODULE_LICENSE("GPLv2"); > +MODULE_AUTHOR("David Cohen <david.a.cohen@xxxxxxxxxxxxxxx>"); > -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html