linux/drivers/extcon/extcon-palmas.c
<<
>>
Prefs
   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
  26static const unsigned int palmas_extcon_cable[] = {
  27        EXTCON_USB,
  28        EXTCON_USB_HOST,
  29        EXTCON_NONE,
  30};
  31
  32static 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
  41static 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
  73static 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
 115static 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
 137static 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
 147static 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
 174static 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
 364static 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
 374static 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
 391static 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
 415static SIMPLE_DEV_PM_OPS(palmas_pm_ops, palmas_usb_suspend, palmas_usb_resume);
 416
 417static 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
 425static 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
 435module_platform_driver(palmas_usb_driver);
 436
 437MODULE_ALIAS("platform:palmas-usb");
 438MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
 439MODULE_DESCRIPTION("Palmas USB transceiver driver");
 440MODULE_LICENSE("GPL");
 441MODULE_DEVICE_TABLE(of, of_palmas_match_tbl);
 442