1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Palmas USB transceiver driver
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>
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>
19 #include <linux/of_platform.h>
20 #include <linux/of_gpio.h>
21 #include <linux/gpio/consumer.h>
22 #include <linux/workqueue.h>
24 #define USB_GPIO_DEBOUNCE_MS 20 /* ms */
26 static const unsigned int palmas_extcon_cable
[] = {
32 static void palmas_usb_wakeup(struct palmas
*palmas
, int enable
)
35 palmas_write(palmas
, PALMAS_USB_OTG_BASE
, PALMAS_USB_WAKEUP
,
36 PALMAS_USB_WAKEUP_ID_WK_UP_COMP
);
38 palmas_write(palmas
, PALMAS_USB_OTG_BASE
, PALMAS_USB_WAKEUP
, 0);
41 static irqreturn_t
palmas_vbus_irq_handler(int irq
, void *_palmas_usb
)
43 struct palmas_usb
*palmas_usb
= _palmas_usb
;
44 struct extcon_dev
*edev
= palmas_usb
->edev
;
45 unsigned int vbus_line_state
;
47 palmas_read(palmas_usb
->palmas
, PALMAS_INTERRUPT_BASE
,
48 PALMAS_INT3_LINE_STATE
, &vbus_line_state
);
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");
56 dev_dbg(palmas_usb
->dev
,
57 "Spurious connect event detected\n");
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");
65 dev_dbg(palmas_usb
->dev
,
66 "Spurious disconnect event detected\n");
73 static irqreturn_t
palmas_id_irq_handler(int irq
, void *_palmas_usb
)
75 unsigned int set
, id_src
;
76 struct palmas_usb
*palmas_usb
= _palmas_usb
;
77 struct extcon_dev
*edev
= palmas_usb
->edev
;
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
);
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");
115 static void palmas_gpio_id_detect(struct work_struct
*work
)
118 struct palmas_usb
*palmas_usb
= container_of(to_delayed_work(work
),
121 struct extcon_dev
*edev
= palmas_usb
->edev
;
123 if (!palmas_usb
->id_gpiod
)
126 id
= gpiod_get_value_cansleep(palmas_usb
->id_gpiod
);
129 extcon_set_state_sync(edev
, EXTCON_USB_HOST
, false);
130 dev_dbg(palmas_usb
->dev
, "USB-HOST cable is detached\n");
132 extcon_set_state_sync(edev
, EXTCON_USB_HOST
, true);
133 dev_dbg(palmas_usb
->dev
, "USB-HOST cable is attached\n");
137 static irqreturn_t
palmas_gpio_id_irq_handler(int irq
, void *_palmas_usb
)
139 struct palmas_usb
*palmas_usb
= _palmas_usb
;
141 queue_delayed_work(system_power_efficient_wq
, &palmas_usb
->wq_detectid
,
142 palmas_usb
->sw_debounce_jiffies
);
147 static void palmas_enable_irq(struct palmas_usb
*palmas_usb
)
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
);
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
);
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
);
164 if (palmas_usb
->enable_vbus_detection
)
165 palmas_vbus_irq_handler(palmas_usb
->vbus_irq
, palmas_usb
);
167 /* cold plug for host mode needs this delay */
168 if (palmas_usb
->enable_id_detection
) {
170 palmas_id_irq_handler(palmas_usb
->id_irq
, palmas_usb
);
174 static int palmas_usb_probe(struct platform_device
*pdev
)
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
;
183 dev_err(&pdev
->dev
, "failed to get valid parent\n");
187 palmas_usb
= devm_kzalloc(&pdev
->dev
, sizeof(*palmas_usb
), GFP_KERNEL
);
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");
198 palmas_usb
->wakeup
= true;
199 palmas_usb
->enable_id_detection
= true;
200 palmas_usb
->enable_vbus_detection
= true;
203 palmas_usb
->wakeup
= pdata
->wakeup
;
206 palmas_usb
->id_gpiod
= devm_gpiod_get_optional(&pdev
->dev
, "id",
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
);
213 palmas_usb
->vbus_gpiod
= devm_gpiod_get_optional(&pdev
->dev
, "vbus",
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
);
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;
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;
230 if (palmas_usb
->enable_gpio_id_detection
) {
233 if (of_property_read_u32(node
, "debounce-delay-ms", &debounce
))
234 debounce
= USB_GPIO_DEBOUNCE_MS
;
236 status
= gpiod_set_debounce(palmas_usb
->id_gpiod
,
239 palmas_usb
->sw_debounce_jiffies
= msecs_to_jiffies(debounce
);
242 INIT_DELAYED_WORK(&palmas_usb
->wq_detectid
, palmas_gpio_id_detect
);
244 palmas
->usb
= palmas_usb
;
245 palmas_usb
->palmas
= palmas
;
247 palmas_usb
->dev
= &pdev
->dev
;
249 palmas_usb_wakeup(palmas
, palmas_usb
->wakeup
);
251 platform_set_drvdata(pdev
, palmas_usb
);
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");
260 status
= devm_extcon_dev_register(&pdev
->dev
, palmas_usb
->edev
);
262 dev_err(&pdev
->dev
, "failed to register extcon device\n");
266 if (palmas_usb
->enable_id_detection
) {
267 palmas_usb
->id_otg_irq
= regmap_irq_get_virq(palmas
->irq_data
,
269 palmas_usb
->id_irq
= regmap_irq_get_virq(palmas
->irq_data
,
271 status
= devm_request_threaded_irq(palmas_usb
->dev
,
273 NULL
, palmas_id_irq_handler
,
274 IRQF_TRIGGER_FALLING
| IRQF_TRIGGER_RISING
|
276 "palmas_usb_id", palmas_usb
);
278 dev_err(&pdev
->dev
, "can't get IRQ %d, err %d\n",
279 palmas_usb
->id_irq
, status
);
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
;
288 status
= devm_request_threaded_irq(&pdev
->dev
,
289 palmas_usb
->gpio_id_irq
,
291 palmas_gpio_id_irq_handler
,
292 IRQF_TRIGGER_RISING
|
293 IRQF_TRIGGER_FALLING
|
299 "failed to request handler for id irq\n");
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
,
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
|
314 "palmas_usb_vbus", palmas_usb
);
316 dev_err(&pdev
->dev
, "can't get IRQ %d, err %d\n",
317 palmas_usb
->vbus_irq
, status
);
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
));
328 dev_err(&pdev
->dev
, "can't remux GPIO1\n");
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
;
339 status
= devm_request_threaded_irq(&pdev
->dev
,
340 palmas_usb
->gpio_vbus_irq
,
342 palmas_vbus_irq_handler
,
343 IRQF_TRIGGER_FALLING
|
344 IRQF_TRIGGER_RISING
|
350 "failed to request handler for vbus irq\n");
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);
364 static int palmas_usb_remove(struct platform_device
*pdev
)
366 struct palmas_usb
*palmas_usb
= platform_get_drvdata(pdev
);
368 cancel_delayed_work_sync(&palmas_usb
->wq_detectid
);
373 #ifdef CONFIG_PM_SLEEP
374 static int palmas_usb_suspend(struct device
*dev
)
376 struct palmas_usb
*palmas_usb
= dev_get_drvdata(dev
);
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
);
391 static int palmas_usb_resume(struct device
*dev
)
393 struct palmas_usb
*palmas_usb
= dev_get_drvdata(dev
);
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
);
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
);
415 static SIMPLE_DEV_PM_OPS(palmas_pm_ops
, palmas_usb_suspend
, palmas_usb_resume
);
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", },
425 static struct platform_driver palmas_usb_driver
= {
426 .probe
= palmas_usb_probe
,
427 .remove
= palmas_usb_remove
,
429 .name
= "palmas-usb",
430 .of_match_table
= of_palmas_match_tbl
,
431 .pm
= &palmas_pm_ops
,
435 module_platform_driver(palmas_usb_driver
);
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
);