linux/drivers/usb/common/usb-conn-gpio.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * USB GPIO Based Connection Detection Driver
   4 *
   5 * Copyright (C) 2019 MediaTek Inc.
   6 *
   7 * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
   8 *
   9 * Some code borrowed from drivers/extcon/extcon-usb-gpio.c
  10 */
  11
  12#include <linux/device.h>
  13#include <linux/gpio/consumer.h>
  14#include <linux/interrupt.h>
  15#include <linux/irq.h>
  16#include <linux/module.h>
  17#include <linux/of.h>
  18#include <linux/pinctrl/consumer.h>
  19#include <linux/platform_device.h>
  20#include <linux/regulator/consumer.h>
  21#include <linux/usb/role.h>
  22
  23#define USB_GPIO_DEB_MS         20      /* ms */
  24#define USB_GPIO_DEB_US         ((USB_GPIO_DEB_MS) * 1000)      /* us */
  25
  26#define USB_CONN_IRQF   \
  27        (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT)
  28
  29struct usb_conn_info {
  30        struct device *dev;
  31        struct usb_role_switch *role_sw;
  32        enum usb_role last_role;
  33        struct regulator *vbus;
  34        struct delayed_work dw_det;
  35        unsigned long debounce_jiffies;
  36
  37        struct gpio_desc *id_gpiod;
  38        struct gpio_desc *vbus_gpiod;
  39        int id_irq;
  40        int vbus_irq;
  41};
  42
  43/**
  44 * "DEVICE" = VBUS and "HOST" = !ID, so we have:
  45 * Both "DEVICE" and "HOST" can't be set as active at the same time
  46 * so if "HOST" is active (i.e. ID is 0)  we keep "DEVICE" inactive
  47 * even if VBUS is on.
  48 *
  49 *  Role          |   ID  |  VBUS
  50 * ------------------------------------
  51 *  [1] DEVICE    |   H   |   H
  52 *  [2] NONE      |   H   |   L
  53 *  [3] HOST      |   L   |   H
  54 *  [4] HOST      |   L   |   L
  55 *
  56 * In case we have only one of these signals:
  57 * - VBUS only - we want to distinguish between [1] and [2], so ID is always 1
  58 * - ID only - we want to distinguish between [1] and [4], so VBUS = ID
  59 */
  60static void usb_conn_detect_cable(struct work_struct *work)
  61{
  62        struct usb_conn_info *info;
  63        enum usb_role role;
  64        int id, vbus, ret;
  65
  66        info = container_of(to_delayed_work(work),
  67                            struct usb_conn_info, dw_det);
  68
  69        /* check ID and VBUS */
  70        id = info->id_gpiod ?
  71                gpiod_get_value_cansleep(info->id_gpiod) : 1;
  72        vbus = info->vbus_gpiod ?
  73                gpiod_get_value_cansleep(info->vbus_gpiod) : id;
  74
  75        if (!id)
  76                role = USB_ROLE_HOST;
  77        else if (vbus)
  78                role = USB_ROLE_DEVICE;
  79        else
  80                role = USB_ROLE_NONE;
  81
  82        dev_dbg(info->dev, "role %d/%d, gpios: id %d, vbus %d\n",
  83                info->last_role, role, id, vbus);
  84
  85        if (info->last_role == role) {
  86                dev_warn(info->dev, "repeated role: %d\n", role);
  87                return;
  88        }
  89
  90        if (info->last_role == USB_ROLE_HOST)
  91                regulator_disable(info->vbus);
  92
  93        ret = usb_role_switch_set_role(info->role_sw, role);
  94        if (ret)
  95                dev_err(info->dev, "failed to set role: %d\n", ret);
  96
  97        if (role == USB_ROLE_HOST) {
  98                ret = regulator_enable(info->vbus);
  99                if (ret)
 100                        dev_err(info->dev, "enable vbus regulator failed\n");
 101        }
 102
 103        info->last_role = role;
 104
 105        dev_dbg(info->dev, "vbus regulator is %s\n",
 106                regulator_is_enabled(info->vbus) ? "enabled" : "disabled");
 107}
 108
 109static void usb_conn_queue_dwork(struct usb_conn_info *info,
 110                                 unsigned long delay)
 111{
 112        queue_delayed_work(system_power_efficient_wq, &info->dw_det, delay);
 113}
 114
 115static irqreturn_t usb_conn_isr(int irq, void *dev_id)
 116{
 117        struct usb_conn_info *info = dev_id;
 118
 119        usb_conn_queue_dwork(info, info->debounce_jiffies);
 120
 121        return IRQ_HANDLED;
 122}
 123
 124static int usb_conn_probe(struct platform_device *pdev)
 125{
 126        struct device *dev = &pdev->dev;
 127        struct usb_conn_info *info;
 128        int ret = 0;
 129
 130        info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
 131        if (!info)
 132                return -ENOMEM;
 133
 134        info->dev = dev;
 135        info->id_gpiod = devm_gpiod_get_optional(dev, "id", GPIOD_IN);
 136        if (IS_ERR(info->id_gpiod))
 137                return PTR_ERR(info->id_gpiod);
 138
 139        info->vbus_gpiod = devm_gpiod_get_optional(dev, "vbus", GPIOD_IN);
 140        if (IS_ERR(info->vbus_gpiod))
 141                return PTR_ERR(info->vbus_gpiod);
 142
 143        if (!info->id_gpiod && !info->vbus_gpiod) {
 144                dev_err(dev, "failed to get gpios\n");
 145                return -ENODEV;
 146        }
 147
 148        if (info->id_gpiod)
 149                ret = gpiod_set_debounce(info->id_gpiod, USB_GPIO_DEB_US);
 150        if (!ret && info->vbus_gpiod)
 151                ret = gpiod_set_debounce(info->vbus_gpiod, USB_GPIO_DEB_US);
 152        if (ret < 0)
 153                info->debounce_jiffies = msecs_to_jiffies(USB_GPIO_DEB_MS);
 154
 155        INIT_DELAYED_WORK(&info->dw_det, usb_conn_detect_cable);
 156
 157        info->vbus = devm_regulator_get(dev, "vbus");
 158        if (IS_ERR(info->vbus)) {
 159                if (PTR_ERR(info->vbus) != -EPROBE_DEFER)
 160                        dev_err(dev, "failed to get vbus\n");
 161                return PTR_ERR(info->vbus);
 162        }
 163
 164        info->role_sw = usb_role_switch_get(dev);
 165        if (IS_ERR(info->role_sw)) {
 166                if (PTR_ERR(info->role_sw) != -EPROBE_DEFER)
 167                        dev_err(dev, "failed to get role switch\n");
 168
 169                return PTR_ERR(info->role_sw);
 170        }
 171
 172        if (info->id_gpiod) {
 173                info->id_irq = gpiod_to_irq(info->id_gpiod);
 174                if (info->id_irq < 0) {
 175                        dev_err(dev, "failed to get ID IRQ\n");
 176                        ret = info->id_irq;
 177                        goto put_role_sw;
 178                }
 179
 180                ret = devm_request_threaded_irq(dev, info->id_irq, NULL,
 181                                                usb_conn_isr, USB_CONN_IRQF,
 182                                                pdev->name, info);
 183                if (ret < 0) {
 184                        dev_err(dev, "failed to request ID IRQ\n");
 185                        goto put_role_sw;
 186                }
 187        }
 188
 189        if (info->vbus_gpiod) {
 190                info->vbus_irq = gpiod_to_irq(info->vbus_gpiod);
 191                if (info->vbus_irq < 0) {
 192                        dev_err(dev, "failed to get VBUS IRQ\n");
 193                        ret = info->vbus_irq;
 194                        goto put_role_sw;
 195                }
 196
 197                ret = devm_request_threaded_irq(dev, info->vbus_irq, NULL,
 198                                                usb_conn_isr, USB_CONN_IRQF,
 199                                                pdev->name, info);
 200                if (ret < 0) {
 201                        dev_err(dev, "failed to request VBUS IRQ\n");
 202                        goto put_role_sw;
 203                }
 204        }
 205
 206        platform_set_drvdata(pdev, info);
 207
 208        /* Perform initial detection */
 209        usb_conn_queue_dwork(info, 0);
 210
 211        return 0;
 212
 213put_role_sw:
 214        usb_role_switch_put(info->role_sw);
 215        return ret;
 216}
 217
 218static int usb_conn_remove(struct platform_device *pdev)
 219{
 220        struct usb_conn_info *info = platform_get_drvdata(pdev);
 221
 222        cancel_delayed_work_sync(&info->dw_det);
 223
 224        if (info->last_role == USB_ROLE_HOST)
 225                regulator_disable(info->vbus);
 226
 227        usb_role_switch_put(info->role_sw);
 228
 229        return 0;
 230}
 231
 232static int __maybe_unused usb_conn_suspend(struct device *dev)
 233{
 234        struct usb_conn_info *info = dev_get_drvdata(dev);
 235
 236        if (info->id_gpiod)
 237                disable_irq(info->id_irq);
 238        if (info->vbus_gpiod)
 239                disable_irq(info->vbus_irq);
 240
 241        pinctrl_pm_select_sleep_state(dev);
 242
 243        return 0;
 244}
 245
 246static int __maybe_unused usb_conn_resume(struct device *dev)
 247{
 248        struct usb_conn_info *info = dev_get_drvdata(dev);
 249
 250        pinctrl_pm_select_default_state(dev);
 251
 252        if (info->id_gpiod)
 253                enable_irq(info->id_irq);
 254        if (info->vbus_gpiod)
 255                enable_irq(info->vbus_irq);
 256
 257        usb_conn_queue_dwork(info, 0);
 258
 259        return 0;
 260}
 261
 262static SIMPLE_DEV_PM_OPS(usb_conn_pm_ops,
 263                         usb_conn_suspend, usb_conn_resume);
 264
 265static const struct of_device_id usb_conn_dt_match[] = {
 266        { .compatible = "gpio-usb-b-connector", },
 267        { }
 268};
 269MODULE_DEVICE_TABLE(of, usb_conn_dt_match);
 270
 271static struct platform_driver usb_conn_driver = {
 272        .probe          = usb_conn_probe,
 273        .remove         = usb_conn_remove,
 274        .driver         = {
 275                .name   = "usb-conn-gpio",
 276                .pm     = &usb_conn_pm_ops,
 277                .of_match_table = usb_conn_dt_match,
 278        },
 279};
 280
 281module_platform_driver(usb_conn_driver);
 282
 283MODULE_AUTHOR("Chunfeng Yun <chunfeng.yun@mediatek.com>");
 284MODULE_DESCRIPTION("USB GPIO based connection detection driver");
 285MODULE_LICENSE("GPL v2");
 286