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