0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/extcon-provider.h>
0010 #include <linux/gpio/consumer.h>
0011 #include <linux/init.h>
0012 #include <linux/interrupt.h>
0013 #include <linux/irq.h>
0014 #include <linux/kernel.h>
0015 #include <linux/module.h>
0016 #include <linux/platform_device.h>
0017 #include <linux/slab.h>
0018 #include <linux/workqueue.h>
0019 #include <linux/pinctrl/consumer.h>
0020 #include <linux/mod_devicetable.h>
0021
0022 #define USB_GPIO_DEBOUNCE_MS 20
0023
0024 struct usb_extcon_info {
0025 struct device *dev;
0026 struct extcon_dev *edev;
0027
0028 struct gpio_desc *id_gpiod;
0029 struct gpio_desc *vbus_gpiod;
0030 int id_irq;
0031 int vbus_irq;
0032
0033 unsigned long debounce_jiffies;
0034 struct delayed_work wq_detcable;
0035 };
0036
0037 static const unsigned int usb_extcon_cable[] = {
0038 EXTCON_USB,
0039 EXTCON_USB_HOST,
0040 EXTCON_NONE,
0041 };
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060 static void usb_extcon_detect_cable(struct work_struct *work)
0061 {
0062 int id, vbus;
0063 struct usb_extcon_info *info = container_of(to_delayed_work(work),
0064 struct usb_extcon_info,
0065 wq_detcable);
0066
0067
0068 id = info->id_gpiod ?
0069 gpiod_get_value_cansleep(info->id_gpiod) : 1;
0070 vbus = info->vbus_gpiod ?
0071 gpiod_get_value_cansleep(info->vbus_gpiod) : id;
0072
0073
0074 if (id)
0075 extcon_set_state_sync(info->edev, EXTCON_USB_HOST, false);
0076 if (!vbus)
0077 extcon_set_state_sync(info->edev, EXTCON_USB, false);
0078
0079 if (!id) {
0080 extcon_set_state_sync(info->edev, EXTCON_USB_HOST, true);
0081 } else {
0082 if (vbus)
0083 extcon_set_state_sync(info->edev, EXTCON_USB, true);
0084 }
0085 }
0086
0087 static irqreturn_t usb_irq_handler(int irq, void *dev_id)
0088 {
0089 struct usb_extcon_info *info = dev_id;
0090
0091 queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
0092 info->debounce_jiffies);
0093
0094 return IRQ_HANDLED;
0095 }
0096
0097 static int usb_extcon_probe(struct platform_device *pdev)
0098 {
0099 struct device *dev = &pdev->dev;
0100 struct device_node *np = dev->of_node;
0101 struct usb_extcon_info *info;
0102 int ret;
0103
0104 if (!np)
0105 return -EINVAL;
0106
0107 info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
0108 if (!info)
0109 return -ENOMEM;
0110
0111 info->dev = dev;
0112 info->id_gpiod = devm_gpiod_get_optional(&pdev->dev, "id", GPIOD_IN);
0113 info->vbus_gpiod = devm_gpiod_get_optional(&pdev->dev, "vbus",
0114 GPIOD_IN);
0115
0116 if (!info->id_gpiod && !info->vbus_gpiod) {
0117 dev_err(dev, "failed to get gpios\n");
0118 return -ENODEV;
0119 }
0120
0121 if (IS_ERR(info->id_gpiod))
0122 return PTR_ERR(info->id_gpiod);
0123
0124 if (IS_ERR(info->vbus_gpiod))
0125 return PTR_ERR(info->vbus_gpiod);
0126
0127 info->edev = devm_extcon_dev_allocate(dev, usb_extcon_cable);
0128 if (IS_ERR(info->edev)) {
0129 dev_err(dev, "failed to allocate extcon device\n");
0130 return -ENOMEM;
0131 }
0132
0133 ret = devm_extcon_dev_register(dev, info->edev);
0134 if (ret < 0) {
0135 dev_err(dev, "failed to register extcon device\n");
0136 return ret;
0137 }
0138
0139 if (info->id_gpiod)
0140 ret = gpiod_set_debounce(info->id_gpiod,
0141 USB_GPIO_DEBOUNCE_MS * 1000);
0142 if (!ret && info->vbus_gpiod)
0143 ret = gpiod_set_debounce(info->vbus_gpiod,
0144 USB_GPIO_DEBOUNCE_MS * 1000);
0145
0146 if (ret < 0)
0147 info->debounce_jiffies = msecs_to_jiffies(USB_GPIO_DEBOUNCE_MS);
0148
0149 INIT_DELAYED_WORK(&info->wq_detcable, usb_extcon_detect_cable);
0150
0151 if (info->id_gpiod) {
0152 info->id_irq = gpiod_to_irq(info->id_gpiod);
0153 if (info->id_irq < 0) {
0154 dev_err(dev, "failed to get ID IRQ\n");
0155 return info->id_irq;
0156 }
0157
0158 ret = devm_request_threaded_irq(dev, info->id_irq, NULL,
0159 usb_irq_handler,
0160 IRQF_TRIGGER_RISING |
0161 IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
0162 pdev->name, info);
0163 if (ret < 0) {
0164 dev_err(dev, "failed to request handler for ID IRQ\n");
0165 return ret;
0166 }
0167 }
0168
0169 if (info->vbus_gpiod) {
0170 info->vbus_irq = gpiod_to_irq(info->vbus_gpiod);
0171 if (info->vbus_irq < 0) {
0172 dev_err(dev, "failed to get VBUS IRQ\n");
0173 return info->vbus_irq;
0174 }
0175
0176 ret = devm_request_threaded_irq(dev, info->vbus_irq, NULL,
0177 usb_irq_handler,
0178 IRQF_TRIGGER_RISING |
0179 IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
0180 pdev->name, info);
0181 if (ret < 0) {
0182 dev_err(dev, "failed to request handler for VBUS IRQ\n");
0183 return ret;
0184 }
0185 }
0186
0187 platform_set_drvdata(pdev, info);
0188 device_set_wakeup_capable(&pdev->dev, true);
0189
0190
0191 usb_extcon_detect_cable(&info->wq_detcable.work);
0192
0193 return 0;
0194 }
0195
0196 static int usb_extcon_remove(struct platform_device *pdev)
0197 {
0198 struct usb_extcon_info *info = platform_get_drvdata(pdev);
0199
0200 cancel_delayed_work_sync(&info->wq_detcable);
0201 device_init_wakeup(&pdev->dev, false);
0202
0203 return 0;
0204 }
0205
0206 #ifdef CONFIG_PM_SLEEP
0207 static int usb_extcon_suspend(struct device *dev)
0208 {
0209 struct usb_extcon_info *info = dev_get_drvdata(dev);
0210 int ret = 0;
0211
0212 if (device_may_wakeup(dev)) {
0213 if (info->id_gpiod) {
0214 ret = enable_irq_wake(info->id_irq);
0215 if (ret)
0216 return ret;
0217 }
0218 if (info->vbus_gpiod) {
0219 ret = enable_irq_wake(info->vbus_irq);
0220 if (ret) {
0221 if (info->id_gpiod)
0222 disable_irq_wake(info->id_irq);
0223
0224 return ret;
0225 }
0226 }
0227 }
0228
0229 if (!device_may_wakeup(dev))
0230 pinctrl_pm_select_sleep_state(dev);
0231
0232 return ret;
0233 }
0234
0235 static int usb_extcon_resume(struct device *dev)
0236 {
0237 struct usb_extcon_info *info = dev_get_drvdata(dev);
0238 int ret = 0;
0239
0240 if (!device_may_wakeup(dev))
0241 pinctrl_pm_select_default_state(dev);
0242
0243 if (device_may_wakeup(dev)) {
0244 if (info->id_gpiod) {
0245 ret = disable_irq_wake(info->id_irq);
0246 if (ret)
0247 return ret;
0248 }
0249 if (info->vbus_gpiod) {
0250 ret = disable_irq_wake(info->vbus_irq);
0251 if (ret) {
0252 if (info->id_gpiod)
0253 enable_irq_wake(info->id_irq);
0254
0255 return ret;
0256 }
0257 }
0258 }
0259
0260 queue_delayed_work(system_power_efficient_wq,
0261 &info->wq_detcable, 0);
0262
0263 return ret;
0264 }
0265 #endif
0266
0267 static SIMPLE_DEV_PM_OPS(usb_extcon_pm_ops,
0268 usb_extcon_suspend, usb_extcon_resume);
0269
0270 static const struct of_device_id usb_extcon_dt_match[] = {
0271 { .compatible = "linux,extcon-usb-gpio", },
0272 { }
0273 };
0274 MODULE_DEVICE_TABLE(of, usb_extcon_dt_match);
0275
0276 static const struct platform_device_id usb_extcon_platform_ids[] = {
0277 { .name = "extcon-usb-gpio", },
0278 { }
0279 };
0280 MODULE_DEVICE_TABLE(platform, usb_extcon_platform_ids);
0281
0282 static struct platform_driver usb_extcon_driver = {
0283 .probe = usb_extcon_probe,
0284 .remove = usb_extcon_remove,
0285 .driver = {
0286 .name = "extcon-usb-gpio",
0287 .pm = &usb_extcon_pm_ops,
0288 .of_match_table = usb_extcon_dt_match,
0289 },
0290 .id_table = usb_extcon_platform_ids,
0291 };
0292
0293 module_platform_driver(usb_extcon_driver);
0294
0295 MODULE_AUTHOR("Roger Quadros <rogerq@ti.com>");
0296 MODULE_DESCRIPTION("USB GPIO extcon driver");
0297 MODULE_LICENSE("GPL v2");