root/drivers/extcon/extcon-qcom-spmi-misc.c

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

DEFINITIONS

This source file includes following definitions.
  1. qcom_usb_extcon_detect_cable
  2. qcom_usb_irq_handler
  3. qcom_usb_extcon_probe
  4. qcom_usb_extcon_remove
  5. qcom_usb_extcon_suspend
  6. qcom_usb_extcon_resume

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /**
   3  * extcon-qcom-spmi-misc.c - Qualcomm USB extcon driver to support USB ID
   4  *                              detection based on extcon-usb-gpio.c.
   5  *
   6  * Copyright (C) 2016 Linaro, Ltd.
   7  * Stephen Boyd <stephen.boyd@linaro.org>
   8  */
   9 
  10 #include <linux/extcon-provider.h>
  11 #include <linux/init.h>
  12 #include <linux/interrupt.h>
  13 #include <linux/kernel.h>
  14 #include <linux/module.h>
  15 #include <linux/mod_devicetable.h>
  16 #include <linux/platform_device.h>
  17 #include <linux/slab.h>
  18 #include <linux/workqueue.h>
  19 
  20 #define USB_ID_DEBOUNCE_MS      5       /* ms */
  21 
  22 struct qcom_usb_extcon_info {
  23         struct extcon_dev *edev;
  24         int irq;
  25         struct delayed_work wq_detcable;
  26         unsigned long debounce_jiffies;
  27 };
  28 
  29 static const unsigned int qcom_usb_extcon_cable[] = {
  30         EXTCON_USB_HOST,
  31         EXTCON_NONE,
  32 };
  33 
  34 static void qcom_usb_extcon_detect_cable(struct work_struct *work)
  35 {
  36         bool id;
  37         int ret;
  38         struct qcom_usb_extcon_info *info = container_of(to_delayed_work(work),
  39                                                     struct qcom_usb_extcon_info,
  40                                                     wq_detcable);
  41 
  42         /* check ID and update cable state */
  43         ret = irq_get_irqchip_state(info->irq, IRQCHIP_STATE_LINE_LEVEL, &id);
  44         if (ret)
  45                 return;
  46 
  47         extcon_set_state_sync(info->edev, EXTCON_USB_HOST, !id);
  48 }
  49 
  50 static irqreturn_t qcom_usb_irq_handler(int irq, void *dev_id)
  51 {
  52         struct qcom_usb_extcon_info *info = dev_id;
  53 
  54         queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
  55                            info->debounce_jiffies);
  56 
  57         return IRQ_HANDLED;
  58 }
  59 
  60 static int qcom_usb_extcon_probe(struct platform_device *pdev)
  61 {
  62         struct device *dev = &pdev->dev;
  63         struct qcom_usb_extcon_info *info;
  64         int ret;
  65 
  66         info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
  67         if (!info)
  68                 return -ENOMEM;
  69 
  70         info->edev = devm_extcon_dev_allocate(dev, qcom_usb_extcon_cable);
  71         if (IS_ERR(info->edev)) {
  72                 dev_err(dev, "failed to allocate extcon device\n");
  73                 return -ENOMEM;
  74         }
  75 
  76         ret = devm_extcon_dev_register(dev, info->edev);
  77         if (ret < 0) {
  78                 dev_err(dev, "failed to register extcon device\n");
  79                 return ret;
  80         }
  81 
  82         info->debounce_jiffies = msecs_to_jiffies(USB_ID_DEBOUNCE_MS);
  83         INIT_DELAYED_WORK(&info->wq_detcable, qcom_usb_extcon_detect_cable);
  84 
  85         info->irq = platform_get_irq_byname(pdev, "usb_id");
  86         if (info->irq < 0)
  87                 return info->irq;
  88 
  89         ret = devm_request_threaded_irq(dev, info->irq, NULL,
  90                                         qcom_usb_irq_handler,
  91                                         IRQF_TRIGGER_RISING |
  92                                         IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
  93                                         pdev->name, info);
  94         if (ret < 0) {
  95                 dev_err(dev, "failed to request handler for ID IRQ\n");
  96                 return ret;
  97         }
  98 
  99         platform_set_drvdata(pdev, info);
 100         device_init_wakeup(dev, 1);
 101 
 102         /* Perform initial detection */
 103         qcom_usb_extcon_detect_cable(&info->wq_detcable.work);
 104 
 105         return 0;
 106 }
 107 
 108 static int qcom_usb_extcon_remove(struct platform_device *pdev)
 109 {
 110         struct qcom_usb_extcon_info *info = platform_get_drvdata(pdev);
 111 
 112         cancel_delayed_work_sync(&info->wq_detcable);
 113 
 114         return 0;
 115 }
 116 
 117 #ifdef CONFIG_PM_SLEEP
 118 static int qcom_usb_extcon_suspend(struct device *dev)
 119 {
 120         struct qcom_usb_extcon_info *info = dev_get_drvdata(dev);
 121         int ret = 0;
 122 
 123         if (device_may_wakeup(dev))
 124                 ret = enable_irq_wake(info->irq);
 125 
 126         return ret;
 127 }
 128 
 129 static int qcom_usb_extcon_resume(struct device *dev)
 130 {
 131         struct qcom_usb_extcon_info *info = dev_get_drvdata(dev);
 132         int ret = 0;
 133 
 134         if (device_may_wakeup(dev))
 135                 ret = disable_irq_wake(info->irq);
 136 
 137         return ret;
 138 }
 139 #endif
 140 
 141 static SIMPLE_DEV_PM_OPS(qcom_usb_extcon_pm_ops,
 142                          qcom_usb_extcon_suspend, qcom_usb_extcon_resume);
 143 
 144 static const struct of_device_id qcom_usb_extcon_dt_match[] = {
 145         { .compatible = "qcom,pm8941-misc", },
 146         { }
 147 };
 148 MODULE_DEVICE_TABLE(of, qcom_usb_extcon_dt_match);
 149 
 150 static struct platform_driver qcom_usb_extcon_driver = {
 151         .probe          = qcom_usb_extcon_probe,
 152         .remove         = qcom_usb_extcon_remove,
 153         .driver         = {
 154                 .name   = "extcon-pm8941-misc",
 155                 .pm     = &qcom_usb_extcon_pm_ops,
 156                 .of_match_table = qcom_usb_extcon_dt_match,
 157         },
 158 };
 159 module_platform_driver(qcom_usb_extcon_driver);
 160 
 161 MODULE_DESCRIPTION("QCOM USB ID extcon driver");
 162 MODULE_AUTHOR("Stephen Boyd <stephen.boyd@linaro.org>");
 163 MODULE_LICENSE("GPL v2");

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