Re: [RFC 01/19] extcon: add extcon-odroid-usbotg driver

From: Robert Baldyga
Date: Thu Mar 19 2015 - 08:07:12 EST


Hi George,

On 03/19/2015 09:50 AM, George Cherian wrote:
> Hi Robert,
>
> This looks like a extcon driver based on gpio for USB.
>
> Roger posted a generic one a while back.
> https://lkml.org/lkml/2015/2/2/187
>
> Doesn't this serve the purpose rather than adding this driver?

Roger's driver doesn't support VBUS state detection so it cannot handle
situation when USB cable is unpluged. In addition some of Odroid boards
has only VBUS detection (without ID pin), so this driver cannot handle
them at all.

Best regards,
Robert

>
> On Wed, Mar 18, 2015 at 7:34 PM, Robert Baldyga <r.baldyga@xxxxxxxxxxx> wrote:
>> This patch adds extcon driver for Odroid U3, U3+ and X boards.
>> It recognizes type of USB cable connected to Odroid board basing on
>> two signal lines VBUS_DET and OTG_ID (the second one is present only
>> on Odroid U3+ board).
>>
>> Following table of states presents relationship between this signals
>> and detected cable type:
>>
>> state | VBUS_DET | OTG_ID
>> -------------------------------
>> USB | H | H
>> USB_HOST | H | L
>> disconn. | L | H
>> USB-HOST | L | L
>>
>> This driver is based on extcon-gpio driver.
>>
>> Signed-off-by: Åukasz Stelmach <l.stelmach@xxxxxxxxxxx>
>> Signed-off-by: Robert Baldyga <r.baldyga@xxxxxxxxxxx>
>> ---
>> drivers/extcon/Kconfig | 4 +
>> drivers/extcon/Makefile | 1 +
>> drivers/extcon/extcon-odroid-usbotg.c | 256 ++++++++++++++++++++++++++++++++++
>> 3 files changed, 261 insertions(+)
>> create mode 100644 drivers/extcon/extcon-odroid-usbotg.c
>>
>> diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig
>> index 6a1f7de..891dca3 100644
>> --- a/drivers/extcon/Kconfig
>> +++ b/drivers/extcon/Kconfig
>> @@ -93,4 +93,8 @@ config EXTCON_SM5502
>> Silicon Mitus SM5502. The SM5502 is a USB port accessory
>> detector and switch.
>>
>> +config EXTCON_ODROID_USBOTG
>> + tristate "Extcon Odroid U3, U3+, X and XU USB OTG port"
>> + depends on OF_GPIO
>> +
>> endif # MULTISTATE_SWITCH
>> diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile
>> index 0370b42..514384e 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_ODROID_USBOTG) += extcon-odroid-usbotg.o
>> diff --git a/drivers/extcon/extcon-odroid-usbotg.c b/drivers/extcon/extcon-odroid-usbotg.c
>> new file mode 100644
>> index 0000000..7f71c48
>> --- /dev/null
>> +++ b/drivers/extcon/extcon-odroid-usbotg.c
>> @@ -0,0 +1,256 @@
>> +/*
>> + * drivers/extcon/extcon-odroid-usbotg.c
>> + *
>> + * USB cable extcon driver for Odroid U3, Odroid U3+ and Odroid X
>> + *
>> + * Copyright (C) 2014 Samsung Electronics
>> + * Author: Lukasz Stelmach <l.stelmach@xxxxxxxxxxx>
>> + * Author: Robert Baldyga <r.baldyga@xxxxxxxxxxx>
>> + *
>> + * based on drivers/extcon/extcon-gpio.c
>> + * Copyright (C) 2008 Google, Inc.
>> + * Author: Mike Lockwood <lockwood@xxxxxxxxxxx>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * 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/module.h>
>> +#include <linux/kernel.h>
>> +#include <linux/init.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +#include <linux/gpio.h>
>> +#include <linux/of_gpio.h>
>> +#include <linux/extcon.h>
>> +#include <linux/extcon/extcon-gpio.h>
>> +#include <linux/delay.h>
>> +
>> +enum {
>> + EXTCON_CABLE_USB = 0,
>> + EXTCON_CABLE_USB_HOST,
>> +
>> + _EXTCON_CABLE_NUM,
>> +};
>> +
>> +static const char * const odroid_usbotg_cable[] = {
>> + [EXTCON_CABLE_USB] = "USB",
>> + [EXTCON_CABLE_USB_HOST] = "USB-HOST",
>> +
>> + NULL,
>> +};
>> +
>> +struct odroid_usbotg_data {
>> + struct extcon_dev *edev;
>> + struct gpio_desc *otg_id;
>> + struct gpio_desc *vbus_det;
>> + int otg_id_irq;
>> + int vbus_det_irq;
>> + unsigned long debounce_ms;
>> +};
>> +
>> +/*
>> + * state | VBUS_DET | OTG_ID
>> + * -------------------------------
>> + * USB | H | H
>> + * USB-HOST | H | L
>> + * disconn. | L | H
>> + * USB-HOST | L | L
>> + *
>> + * Only Odroid U3+ has OTG_ID line. U3 and X versions can detect only
>> + * USB slave cable.
>> + */
>> +
>> +static void odroid_usbotg_detect_cable(struct odroid_usbotg_data *extcon_data)
>> +{
>> + int state;
>> +
>> + mdelay(extcon_data->debounce_ms);
>> +
>> + if (extcon_data->otg_id)
>> + state = (gpiod_get_value(extcon_data->vbus_det) << 1) |
>> + gpiod_get_value(extcon_data->otg_id);
>> + else
>> + state = (gpiod_get_value(extcon_data->vbus_det) << 1) | 1;
>> +
>> + dev_dbg(&extcon_data->edev->dev, "cable state changed to %d\n", state);
>> +
>> + if (state & 0x1)
>> + extcon_set_cable_state_(extcon_data->edev,
>> + EXTCON_CABLE_USB_HOST, false);
>> + if (state != 0x3)
>> + extcon_set_cable_state_(extcon_data->edev,
>> + EXTCON_CABLE_USB, false);
>> +
>> + if (!(state & 0x1))
>> + extcon_set_cable_state_(extcon_data->edev,
>> + EXTCON_CABLE_USB_HOST, true);
>> + else if (state == 0x3)
>> + extcon_set_cable_state_(extcon_data->edev,
>> + EXTCON_CABLE_USB, true);
>> +}
>> +
>> +static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
>> +{
>> + struct odroid_usbotg_data *extcon_data = dev_id;
>> +
>> + odroid_usbotg_detect_cable(extcon_data);
>> +
>> + return IRQ_HANDLED;
>> +}
>> +
>> +static int odroid_usbotg_parse_dt(struct platform_device *pdev,
>> + struct odroid_usbotg_data *extcon_data)
>> +{
>> + struct device_node *np = pdev->dev.of_node;
>> + u32 val;
>> +
>> + if (!np)
>> + return -ENODEV;
>> +
>> + extcon_data->edev->name = np->name;
>> +
>> + if (of_property_read_u32(np, "debounce", &val) != 0)
>> + val = 50;
>> + extcon_data->debounce_ms = val;
>> +
>> + return 0;
>> +}
>> +
>> +static int odroid_usbotg_probe(struct platform_device *pdev)
>> +{
>> + struct odroid_usbotg_data *extcon_data;
>> + int ret = 0;
>> +
>> + extcon_data = devm_kzalloc(&pdev->dev,
>> + sizeof(struct odroid_usbotg_data), GFP_KERNEL);
>> + if (!extcon_data)
>> + return -ENOMEM;
>> +
>> + extcon_data->edev = devm_kzalloc(&pdev->dev, sizeof(struct extcon_dev),
>> + GFP_KERNEL);
>> + if (IS_ERR(extcon_data->edev)) {
>> + dev_err(&pdev->dev, "failed to allocate extcon device\n");
>> + return -ENOMEM;
>> + }
>> + extcon_data->edev->supported_cable = odroid_usbotg_cable;
>> +
>> + ret = odroid_usbotg_parse_dt(pdev, extcon_data);
>> + if (IS_ERR_VALUE(ret)) {
>> + dev_err(&pdev->dev, "failed to get data from device tree\n");
>> + return ret;
>> + }
>> +
>> + /* gpios */
>> + extcon_data->vbus_det = devm_gpiod_get(&pdev->dev, "vbus-det");
>> + if (IS_ERR(extcon_data->vbus_det)) {
>> + dev_err(&pdev->dev, "failed to get vbus_det gpio\n");
>> + return PTR_ERR(extcon_data->vbus_det);
>> + }
>> + extcon_data->otg_id = devm_gpiod_get_optional(&pdev->dev, "otg-id");
>> +
>> + extcon_data->edev->dev.parent = &pdev->dev;
>> + ret = extcon_dev_register(extcon_data->edev);
>> + if (ret < 0)
>> + return ret;
>> +
>> + /* irq */
>> + extcon_data->vbus_det_irq = gpiod_to_irq(extcon_data->vbus_det);
>> + if (extcon_data->vbus_det_irq < 0) {
>> + dev_err(&pdev->dev, "failed to get irq from vbus_det\n");
>> + ret = extcon_data->vbus_det_irq;
>> + goto err;
>> + }
>> + ret = request_threaded_irq(extcon_data->vbus_det_irq, NULL,
>> + gpio_irq_handler, IRQF_ONESHOT |
>> + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>> + pdev->name, extcon_data);
>> + if (ret < 0) {
>> + dev_err(&pdev->dev, "failed to request vbus_det irq\n");
>> + goto err;
>> + }
>> +
>> + if (extcon_data->otg_id) {
>> + extcon_data->otg_id_irq = gpiod_to_irq(extcon_data->otg_id);
>> + if (extcon_data->otg_id_irq < 0) {
>> + dev_err(&pdev->dev, "failed to get irq from otg_id\n");
>> + ret = extcon_data->otg_id_irq;
>> + goto err;
>> + }
>> + ret = request_threaded_irq(extcon_data->otg_id_irq, NULL,
>> + gpio_irq_handler, IRQF_ONESHOT |
>> + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>> + pdev->name, extcon_data);
>> + if (ret < 0) {
>> + dev_err(&pdev->dev, "failed to request otg_id irq\n");
>> + goto err;
>> + }
>> + }
>> +
>> + platform_set_drvdata(pdev, extcon_data);
>> + /* Perform initial detection */
>> + odroid_usbotg_detect_cable(extcon_data);
>> +
>> + dev_dbg(&pdev->dev, "probe: success\n");
>> +
>> + return 0;
>> +
>> +err:
>> + extcon_dev_unregister(extcon_data->edev);
>> +
>> + return ret;
>> +}
>> +
>> +static int odroid_usbotg_remove(struct platform_device *pdev)
>> +{
>> + struct odroid_usbotg_data *extcon_data = platform_get_drvdata(pdev);
>> +
>> + free_irq(extcon_data->vbus_det_irq, extcon_data);
>> + if (extcon_data->otg_id)
>> + free_irq(extcon_data->otg_id_irq, extcon_data);
>> + extcon_dev_unregister(extcon_data->edev);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct of_device_id odroid_usbotg_of_match[] = {
>> + { .compatible = "extcon-odroid-usbotg" },
>> + { },
>> +};
>> +MODULE_DEVICE_TABLE(of, odroid_usbotg_of_match);
>> +
>> +static struct platform_driver odroid_usbotg_driver = {
>> + .probe = odroid_usbotg_probe,
>> + .remove = odroid_usbotg_remove,
>> + .driver = {
>> + .name = "odroid-usbotg",
>> + .owner = THIS_MODULE,
>> + .of_match_table = of_match_ptr(odroid_usbotg_of_match)
>> + },
>> +};
>> +
>> +static int __init odroid_usbotg_init(void)
>> +{
>> + return platform_driver_register(&odroid_usbotg_driver);
>> +}
>> +
>> +subsys_initcall(odroid_usbotg_init);
>> +
>> +static void __exit odroid_usbotg_cleanup(void)
>> +{
>> + platform_driver_unregister(&odroid_usbotg_driver);
>> +}
>> +
>> +module_exit(odroid_usbotg_cleanup);
>> +
>> +MODULE_AUTHOR("Lukasz Stelmach <l.stelmach@xxxxxxxxxxx>, Robert Baldyga <r.baldyga@xxxxxxxxxxx>");
>> +MODULE_DESCRIPTION("USB OTG extcon driver for Odroid U3, U3+ and X");
>> +MODULE_LICENSE("GPL");
>> --
>> 1.9.1
>>
>> --
>> 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
> --
> 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
>

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/