root/drivers/usb/common/usb-conn-gpio.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. usb_conn_detect_cable
  2. usb_conn_queue_dwork
  3. usb_conn_isr
  4. usb_conn_probe
  5. usb_conn_remove
  6. usb_conn_suspend
  7. usb_conn_resume

   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 
  29 struct 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  */
  60 static 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 
 109 static 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 
 115 static 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 
 124 static 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 
 213 put_role_sw:
 214         usb_role_switch_put(info->role_sw);
 215         return ret;
 216 }
 217 
 218 static 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 
 232 static 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 
 246 static 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 
 262 static SIMPLE_DEV_PM_OPS(usb_conn_pm_ops,
 263                          usb_conn_suspend, usb_conn_resume);
 264 
 265 static const struct of_device_id usb_conn_dt_match[] = {
 266         { .compatible = "gpio-usb-b-connector", },
 267         { }
 268 };
 269 MODULE_DEVICE_TABLE(of, usb_conn_dt_match);
 270 
 271 static 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 
 281 module_platform_driver(usb_conn_driver);
 282 
 283 MODULE_AUTHOR("Chunfeng Yun <chunfeng.yun@mediatek.com>");
 284 MODULE_DESCRIPTION("USB GPIO based connection detection driver");
 285 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */