root/drivers/extcon/extcon-palmas.c

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

DEFINITIONS

This source file includes following definitions.
  1. palmas_usb_wakeup
  2. palmas_vbus_irq_handler
  3. palmas_id_irq_handler
  4. palmas_gpio_id_detect
  5. palmas_gpio_id_irq_handler
  6. palmas_enable_irq
  7. palmas_usb_probe
  8. palmas_usb_remove
  9. palmas_usb_suspend
  10. palmas_usb_resume

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Palmas USB transceiver driver
   4  *
   5  * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
   6  * Author: Graeme Gregory <gg@slimlogic.co.uk>
   7  * Author: Kishon Vijay Abraham I <kishon@ti.com>
   8  * Based on twl6030_usb.c
   9  * Author: Hema HK <hemahk@ti.com>
  10  */
  11 
  12 #include <linux/module.h>
  13 #include <linux/interrupt.h>
  14 #include <linux/platform_device.h>
  15 #include <linux/slab.h>
  16 #include <linux/err.h>
  17 #include <linux/mfd/palmas.h>
  18 #include <linux/of.h>
  19 #include <linux/of_platform.h>
  20 #include <linux/of_gpio.h>
  21 #include <linux/gpio/consumer.h>
  22 #include <linux/workqueue.h>
  23 
  24 #define USB_GPIO_DEBOUNCE_MS    20      /* ms */
  25 
  26 static const unsigned int palmas_extcon_cable[] = {
  27         EXTCON_USB,
  28         EXTCON_USB_HOST,
  29         EXTCON_NONE,
  30 };
  31 
  32 static void palmas_usb_wakeup(struct palmas *palmas, int enable)
  33 {
  34         if (enable)
  35                 palmas_write(palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_WAKEUP,
  36                         PALMAS_USB_WAKEUP_ID_WK_UP_COMP);
  37         else
  38                 palmas_write(palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_WAKEUP, 0);
  39 }
  40 
  41 static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb)
  42 {
  43         struct palmas_usb *palmas_usb = _palmas_usb;
  44         struct extcon_dev *edev = palmas_usb->edev;
  45         unsigned int vbus_line_state;
  46 
  47         palmas_read(palmas_usb->palmas, PALMAS_INTERRUPT_BASE,
  48                 PALMAS_INT3_LINE_STATE, &vbus_line_state);
  49 
  50         if (vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS) {
  51                 if (palmas_usb->linkstat != PALMAS_USB_STATE_VBUS) {
  52                         palmas_usb->linkstat = PALMAS_USB_STATE_VBUS;
  53                         extcon_set_state_sync(edev, EXTCON_USB, true);
  54                         dev_dbg(palmas_usb->dev, "USB cable is attached\n");
  55                 } else {
  56                         dev_dbg(palmas_usb->dev,
  57                                 "Spurious connect event detected\n");
  58                 }
  59         } else if (!(vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS)) {
  60                 if (palmas_usb->linkstat == PALMAS_USB_STATE_VBUS) {
  61                         palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
  62                         extcon_set_state_sync(edev, EXTCON_USB, false);
  63                         dev_dbg(palmas_usb->dev, "USB cable is detached\n");
  64                 } else {
  65                         dev_dbg(palmas_usb->dev,
  66                                 "Spurious disconnect event detected\n");
  67                 }
  68         }
  69 
  70         return IRQ_HANDLED;
  71 }
  72 
  73 static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb)
  74 {
  75         unsigned int set, id_src;
  76         struct palmas_usb *palmas_usb = _palmas_usb;
  77         struct extcon_dev *edev = palmas_usb->edev;
  78 
  79         palmas_read(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
  80                 PALMAS_USB_ID_INT_LATCH_SET, &set);
  81         palmas_read(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
  82                 PALMAS_USB_ID_INT_SRC, &id_src);
  83 
  84         if ((set & PALMAS_USB_ID_INT_SRC_ID_GND) &&
  85                                 (id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) {
  86                 palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
  87                         PALMAS_USB_ID_INT_LATCH_CLR,
  88                         PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND);
  89                 palmas_usb->linkstat = PALMAS_USB_STATE_ID;
  90                 extcon_set_state_sync(edev, EXTCON_USB_HOST, true);
  91                 dev_dbg(palmas_usb->dev, "USB-HOST cable is attached\n");
  92         } else if ((set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) &&
  93                                 (id_src & PALMAS_USB_ID_INT_SRC_ID_FLOAT)) {
  94                 palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
  95                         PALMAS_USB_ID_INT_LATCH_CLR,
  96                         PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT);
  97                 palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
  98                 extcon_set_state_sync(edev, EXTCON_USB_HOST, false);
  99                 dev_dbg(palmas_usb->dev, "USB-HOST cable is detached\n");
 100         } else if ((palmas_usb->linkstat == PALMAS_USB_STATE_ID) &&
 101                                 (!(set & PALMAS_USB_ID_INT_SRC_ID_GND))) {
 102                 palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
 103                 extcon_set_state_sync(edev, EXTCON_USB_HOST, false);
 104                 dev_dbg(palmas_usb->dev, "USB-HOST cable is detached\n");
 105         } else if ((palmas_usb->linkstat == PALMAS_USB_STATE_DISCONNECT) &&
 106                                 (id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) {
 107                 palmas_usb->linkstat = PALMAS_USB_STATE_ID;
 108                 extcon_set_state_sync(edev, EXTCON_USB_HOST, true);
 109                 dev_dbg(palmas_usb->dev, " USB-HOST cable is attached\n");
 110         }
 111 
 112         return IRQ_HANDLED;
 113 }
 114 
 115 static void palmas_gpio_id_detect(struct work_struct *work)
 116 {
 117         int id;
 118         struct palmas_usb *palmas_usb = container_of(to_delayed_work(work),
 119                                                      struct palmas_usb,
 120                                                      wq_detectid);
 121         struct extcon_dev *edev = palmas_usb->edev;
 122 
 123         if (!palmas_usb->id_gpiod)
 124                 return;
 125 
 126         id = gpiod_get_value_cansleep(palmas_usb->id_gpiod);
 127 
 128         if (id) {
 129                 extcon_set_state_sync(edev, EXTCON_USB_HOST, false);
 130                 dev_dbg(palmas_usb->dev, "USB-HOST cable is detached\n");
 131         } else {
 132                 extcon_set_state_sync(edev, EXTCON_USB_HOST, true);
 133                 dev_dbg(palmas_usb->dev, "USB-HOST cable is attached\n");
 134         }
 135 }
 136 
 137 static irqreturn_t palmas_gpio_id_irq_handler(int irq, void *_palmas_usb)
 138 {
 139         struct palmas_usb *palmas_usb = _palmas_usb;
 140 
 141         queue_delayed_work(system_power_efficient_wq, &palmas_usb->wq_detectid,
 142                            palmas_usb->sw_debounce_jiffies);
 143 
 144         return IRQ_HANDLED;
 145 }
 146 
 147 static void palmas_enable_irq(struct palmas_usb *palmas_usb)
 148 {
 149         palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
 150                 PALMAS_USB_VBUS_CTRL_SET,
 151                 PALMAS_USB_VBUS_CTRL_SET_VBUS_ACT_COMP);
 152 
 153         if (palmas_usb->enable_id_detection) {
 154                 palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
 155                              PALMAS_USB_ID_CTRL_SET,
 156                              PALMAS_USB_ID_CTRL_SET_ID_ACT_COMP);
 157 
 158                 palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
 159                              PALMAS_USB_ID_INT_EN_HI_SET,
 160                              PALMAS_USB_ID_INT_EN_HI_SET_ID_GND |
 161                              PALMAS_USB_ID_INT_EN_HI_SET_ID_FLOAT);
 162         }
 163 
 164         if (palmas_usb->enable_vbus_detection)
 165                 palmas_vbus_irq_handler(palmas_usb->vbus_irq, palmas_usb);
 166 
 167         /* cold plug for host mode needs this delay */
 168         if (palmas_usb->enable_id_detection) {
 169                 msleep(30);
 170                 palmas_id_irq_handler(palmas_usb->id_irq, palmas_usb);
 171         }
 172 }
 173 
 174 static int palmas_usb_probe(struct platform_device *pdev)
 175 {
 176         struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
 177         struct palmas_usb_platform_data *pdata = dev_get_platdata(&pdev->dev);
 178         struct device_node *node = pdev->dev.of_node;
 179         struct palmas_usb *palmas_usb;
 180         int status;
 181 
 182         if (!palmas) {
 183                 dev_err(&pdev->dev, "failed to get valid parent\n");
 184                 return -EINVAL;
 185         }
 186 
 187         palmas_usb = devm_kzalloc(&pdev->dev, sizeof(*palmas_usb), GFP_KERNEL);
 188         if (!palmas_usb)
 189                 return -ENOMEM;
 190 
 191         if (node && !pdata) {
 192                 palmas_usb->wakeup = of_property_read_bool(node, "ti,wakeup");
 193                 palmas_usb->enable_id_detection = of_property_read_bool(node,
 194                                                 "ti,enable-id-detection");
 195                 palmas_usb->enable_vbus_detection = of_property_read_bool(node,
 196                                                 "ti,enable-vbus-detection");
 197         } else {
 198                 palmas_usb->wakeup = true;
 199                 palmas_usb->enable_id_detection = true;
 200                 palmas_usb->enable_vbus_detection = true;
 201 
 202                 if (pdata)
 203                         palmas_usb->wakeup = pdata->wakeup;
 204         }
 205 
 206         palmas_usb->id_gpiod = devm_gpiod_get_optional(&pdev->dev, "id",
 207                                                         GPIOD_IN);
 208         if (IS_ERR(palmas_usb->id_gpiod)) {
 209                 dev_err(&pdev->dev, "failed to get id gpio\n");
 210                 return PTR_ERR(palmas_usb->id_gpiod);
 211         }
 212 
 213         palmas_usb->vbus_gpiod = devm_gpiod_get_optional(&pdev->dev, "vbus",
 214                                                         GPIOD_IN);
 215         if (IS_ERR(palmas_usb->vbus_gpiod)) {
 216                 dev_err(&pdev->dev, "failed to get vbus gpio\n");
 217                 return PTR_ERR(palmas_usb->vbus_gpiod);
 218         }
 219 
 220         if (palmas_usb->enable_id_detection && palmas_usb->id_gpiod) {
 221                 palmas_usb->enable_id_detection = false;
 222                 palmas_usb->enable_gpio_id_detection = true;
 223         }
 224 
 225         if (palmas_usb->enable_vbus_detection && palmas_usb->vbus_gpiod) {
 226                 palmas_usb->enable_vbus_detection = false;
 227                 palmas_usb->enable_gpio_vbus_detection = true;
 228         }
 229 
 230         if (palmas_usb->enable_gpio_id_detection) {
 231                 u32 debounce;
 232 
 233                 if (of_property_read_u32(node, "debounce-delay-ms", &debounce))
 234                         debounce = USB_GPIO_DEBOUNCE_MS;
 235 
 236                 status = gpiod_set_debounce(palmas_usb->id_gpiod,
 237                                             debounce * 1000);
 238                 if (status < 0)
 239                         palmas_usb->sw_debounce_jiffies = msecs_to_jiffies(debounce);
 240         }
 241 
 242         INIT_DELAYED_WORK(&palmas_usb->wq_detectid, palmas_gpio_id_detect);
 243 
 244         palmas->usb = palmas_usb;
 245         palmas_usb->palmas = palmas;
 246 
 247         palmas_usb->dev  = &pdev->dev;
 248 
 249         palmas_usb_wakeup(palmas, palmas_usb->wakeup);
 250 
 251         platform_set_drvdata(pdev, palmas_usb);
 252 
 253         palmas_usb->edev = devm_extcon_dev_allocate(&pdev->dev,
 254                                                     palmas_extcon_cable);
 255         if (IS_ERR(palmas_usb->edev)) {
 256                 dev_err(&pdev->dev, "failed to allocate extcon device\n");
 257                 return -ENOMEM;
 258         }
 259 
 260         status = devm_extcon_dev_register(&pdev->dev, palmas_usb->edev);
 261         if (status) {
 262                 dev_err(&pdev->dev, "failed to register extcon device\n");
 263                 return status;
 264         }
 265 
 266         if (palmas_usb->enable_id_detection) {
 267                 palmas_usb->id_otg_irq = regmap_irq_get_virq(palmas->irq_data,
 268                                                              PALMAS_ID_OTG_IRQ);
 269                 palmas_usb->id_irq = regmap_irq_get_virq(palmas->irq_data,
 270                                                          PALMAS_ID_IRQ);
 271                 status = devm_request_threaded_irq(palmas_usb->dev,
 272                                 palmas_usb->id_irq,
 273                                 NULL, palmas_id_irq_handler,
 274                                 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
 275                                 IRQF_ONESHOT,
 276                                 "palmas_usb_id", palmas_usb);
 277                 if (status < 0) {
 278                         dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
 279                                         palmas_usb->id_irq, status);
 280                         return status;
 281                 }
 282         } else if (palmas_usb->enable_gpio_id_detection) {
 283                 palmas_usb->gpio_id_irq = gpiod_to_irq(palmas_usb->id_gpiod);
 284                 if (palmas_usb->gpio_id_irq < 0) {
 285                         dev_err(&pdev->dev, "failed to get id irq\n");
 286                         return palmas_usb->gpio_id_irq;
 287                 }
 288                 status = devm_request_threaded_irq(&pdev->dev,
 289                                                    palmas_usb->gpio_id_irq,
 290                                                    NULL,
 291                                                    palmas_gpio_id_irq_handler,
 292                                                    IRQF_TRIGGER_RISING |
 293                                                    IRQF_TRIGGER_FALLING |
 294                                                    IRQF_ONESHOT,
 295                                                    "palmas_usb_id",
 296                                                    palmas_usb);
 297                 if (status < 0) {
 298                         dev_err(&pdev->dev,
 299                                 "failed to request handler for id irq\n");
 300                         return status;
 301                 }
 302         }
 303 
 304         if (palmas_usb->enable_vbus_detection) {
 305                 palmas_usb->vbus_otg_irq = regmap_irq_get_virq(palmas->irq_data,
 306                                                        PALMAS_VBUS_OTG_IRQ);
 307                 palmas_usb->vbus_irq = regmap_irq_get_virq(palmas->irq_data,
 308                                                            PALMAS_VBUS_IRQ);
 309                 status = devm_request_threaded_irq(palmas_usb->dev,
 310                                 palmas_usb->vbus_irq, NULL,
 311                                 palmas_vbus_irq_handler,
 312                                 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
 313                                 IRQF_ONESHOT,
 314                                 "palmas_usb_vbus", palmas_usb);
 315                 if (status < 0) {
 316                         dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
 317                                         palmas_usb->vbus_irq, status);
 318                         return status;
 319                 }
 320         } else if (palmas_usb->enable_gpio_vbus_detection) {
 321                 /* remux GPIO_1 as VBUSDET */
 322                 status = palmas_update_bits(palmas,
 323                         PALMAS_PU_PD_OD_BASE,
 324                         PALMAS_PRIMARY_SECONDARY_PAD1,
 325                         PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_MASK,
 326                         (1 << PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_SHIFT));
 327                 if (status < 0) {
 328                         dev_err(&pdev->dev, "can't remux GPIO1\n");
 329                         return status;
 330                 }
 331 
 332                 palmas_usb->vbus_otg_irq = regmap_irq_get_virq(palmas->irq_data,
 333                                                        PALMAS_VBUS_OTG_IRQ);
 334                 palmas_usb->gpio_vbus_irq = gpiod_to_irq(palmas_usb->vbus_gpiod);
 335                 if (palmas_usb->gpio_vbus_irq < 0) {
 336                         dev_err(&pdev->dev, "failed to get vbus irq\n");
 337                         return palmas_usb->gpio_vbus_irq;
 338                 }
 339                 status = devm_request_threaded_irq(&pdev->dev,
 340                                                 palmas_usb->gpio_vbus_irq,
 341                                                 NULL,
 342                                                 palmas_vbus_irq_handler,
 343                                                 IRQF_TRIGGER_FALLING |
 344                                                 IRQF_TRIGGER_RISING |
 345                                                 IRQF_ONESHOT,
 346                                                 "palmas_usb_vbus",
 347                                                 palmas_usb);
 348                 if (status < 0) {
 349                         dev_err(&pdev->dev,
 350                                 "failed to request handler for vbus irq\n");
 351                         return status;
 352                 }
 353         }
 354 
 355         palmas_enable_irq(palmas_usb);
 356         /* perform initial detection */
 357         if (palmas_usb->enable_gpio_vbus_detection)
 358                 palmas_vbus_irq_handler(palmas_usb->gpio_vbus_irq, palmas_usb);
 359         palmas_gpio_id_detect(&palmas_usb->wq_detectid.work);
 360         device_set_wakeup_capable(&pdev->dev, true);
 361         return 0;
 362 }
 363 
 364 static int palmas_usb_remove(struct platform_device *pdev)
 365 {
 366         struct palmas_usb *palmas_usb = platform_get_drvdata(pdev);
 367 
 368         cancel_delayed_work_sync(&palmas_usb->wq_detectid);
 369 
 370         return 0;
 371 }
 372 
 373 #ifdef CONFIG_PM_SLEEP
 374 static int palmas_usb_suspend(struct device *dev)
 375 {
 376         struct palmas_usb *palmas_usb = dev_get_drvdata(dev);
 377 
 378         if (device_may_wakeup(dev)) {
 379                 if (palmas_usb->enable_vbus_detection)
 380                         enable_irq_wake(palmas_usb->vbus_irq);
 381                 if (palmas_usb->enable_gpio_vbus_detection)
 382                         enable_irq_wake(palmas_usb->gpio_vbus_irq);
 383                 if (palmas_usb->enable_id_detection)
 384                         enable_irq_wake(palmas_usb->id_irq);
 385                 if (palmas_usb->enable_gpio_id_detection)
 386                         enable_irq_wake(palmas_usb->gpio_id_irq);
 387         }
 388         return 0;
 389 }
 390 
 391 static int palmas_usb_resume(struct device *dev)
 392 {
 393         struct palmas_usb *palmas_usb = dev_get_drvdata(dev);
 394 
 395         if (device_may_wakeup(dev)) {
 396                 if (palmas_usb->enable_vbus_detection)
 397                         disable_irq_wake(palmas_usb->vbus_irq);
 398                 if (palmas_usb->enable_gpio_vbus_detection)
 399                         disable_irq_wake(palmas_usb->gpio_vbus_irq);
 400                 if (palmas_usb->enable_id_detection)
 401                         disable_irq_wake(palmas_usb->id_irq);
 402                 if (palmas_usb->enable_gpio_id_detection)
 403                         disable_irq_wake(palmas_usb->gpio_id_irq);
 404         }
 405 
 406         /* check if GPIO states changed while suspend/resume */
 407         if (palmas_usb->enable_gpio_vbus_detection)
 408                 palmas_vbus_irq_handler(palmas_usb->gpio_vbus_irq, palmas_usb);
 409         palmas_gpio_id_detect(&palmas_usb->wq_detectid.work);
 410 
 411         return 0;
 412 };
 413 #endif
 414 
 415 static SIMPLE_DEV_PM_OPS(palmas_pm_ops, palmas_usb_suspend, palmas_usb_resume);
 416 
 417 static const struct of_device_id of_palmas_match_tbl[] = {
 418         { .compatible = "ti,palmas-usb", },
 419         { .compatible = "ti,palmas-usb-vid", },
 420         { .compatible = "ti,twl6035-usb", },
 421         { .compatible = "ti,twl6035-usb-vid", },
 422         { /* end */ }
 423 };
 424 
 425 static struct platform_driver palmas_usb_driver = {
 426         .probe = palmas_usb_probe,
 427         .remove = palmas_usb_remove,
 428         .driver = {
 429                 .name = "palmas-usb",
 430                 .of_match_table = of_palmas_match_tbl,
 431                 .pm = &palmas_pm_ops,
 432         },
 433 };
 434 
 435 module_platform_driver(palmas_usb_driver);
 436 
 437 MODULE_ALIAS("platform:palmas-usb");
 438 MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
 439 MODULE_DESCRIPTION("Palmas USB transceiver driver");
 440 MODULE_LICENSE("GPL");
 441 MODULE_DEVICE_TABLE(of, of_palmas_match_tbl);

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