1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Palmas USB transceiver driver
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>
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>
20 #include <linux/of_platform.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 return dev_err_probe(&pdev
->dev
, PTR_ERR(palmas_usb
->id_gpiod
),
210 "failed to get id gpio\n");
212 palmas_usb
->vbus_gpiod
= devm_gpiod_get_optional(&pdev
->dev
, "vbus",
214 if (IS_ERR(palmas_usb
->vbus_gpiod
))
215 return dev_err_probe(&pdev
->dev
, PTR_ERR(palmas_usb
->vbus_gpiod
),
216 "failed to get id gpio\n");
218 if (palmas_usb
->enable_id_detection
&& palmas_usb
->id_gpiod
) {
219 palmas_usb
->enable_id_detection
= false;
220 palmas_usb
->enable_gpio_id_detection
= true;
223 if (palmas_usb
->enable_vbus_detection
&& palmas_usb
->vbus_gpiod
) {
224 palmas_usb
->enable_vbus_detection
= false;
225 palmas_usb
->enable_gpio_vbus_detection
= true;
228 if (palmas_usb
->enable_gpio_id_detection
) {
231 if (of_property_read_u32(node
, "debounce-delay-ms", &debounce
))
232 debounce
= USB_GPIO_DEBOUNCE_MS
;
234 status
= gpiod_set_debounce(palmas_usb
->id_gpiod
,
237 palmas_usb
->sw_debounce_jiffies
= msecs_to_jiffies(debounce
);
240 status
= devm_delayed_work_autocancel(&pdev
->dev
,
241 &palmas_usb
->wq_detectid
,
242 palmas_gpio_id_detect
);
246 palmas
->usb
= palmas_usb
;
247 palmas_usb
->palmas
= palmas
;
249 palmas_usb
->dev
= &pdev
->dev
;
251 palmas_usb_wakeup(palmas
, palmas_usb
->wakeup
);
253 platform_set_drvdata(pdev
, palmas_usb
);
255 palmas_usb
->edev
= devm_extcon_dev_allocate(&pdev
->dev
,
256 palmas_extcon_cable
);
257 if (IS_ERR(palmas_usb
->edev
)) {
258 dev_err(&pdev
->dev
, "failed to allocate extcon device\n");
262 status
= devm_extcon_dev_register(&pdev
->dev
, palmas_usb
->edev
);
264 dev_err(&pdev
->dev
, "failed to register extcon device\n");
268 if (palmas_usb
->enable_id_detection
) {
269 palmas_usb
->id_otg_irq
= regmap_irq_get_virq(palmas
->irq_data
,
271 palmas_usb
->id_irq
= regmap_irq_get_virq(palmas
->irq_data
,
273 status
= devm_request_threaded_irq(palmas_usb
->dev
,
275 NULL
, palmas_id_irq_handler
,
276 IRQF_TRIGGER_FALLING
| IRQF_TRIGGER_RISING
|
278 "palmas_usb_id", palmas_usb
);
280 dev_err(&pdev
->dev
, "can't get IRQ %d, err %d\n",
281 palmas_usb
->id_irq
, status
);
284 } else if (palmas_usb
->enable_gpio_id_detection
) {
285 palmas_usb
->gpio_id_irq
= gpiod_to_irq(palmas_usb
->id_gpiod
);
286 if (palmas_usb
->gpio_id_irq
< 0) {
287 dev_err(&pdev
->dev
, "failed to get id irq\n");
288 return palmas_usb
->gpio_id_irq
;
290 status
= devm_request_threaded_irq(&pdev
->dev
,
291 palmas_usb
->gpio_id_irq
,
293 palmas_gpio_id_irq_handler
,
294 IRQF_TRIGGER_RISING
|
295 IRQF_TRIGGER_FALLING
|
301 "failed to request handler for id irq\n");
306 if (palmas_usb
->enable_vbus_detection
) {
307 palmas_usb
->vbus_otg_irq
= regmap_irq_get_virq(palmas
->irq_data
,
308 PALMAS_VBUS_OTG_IRQ
);
309 palmas_usb
->vbus_irq
= regmap_irq_get_virq(palmas
->irq_data
,
311 status
= devm_request_threaded_irq(palmas_usb
->dev
,
312 palmas_usb
->vbus_irq
, NULL
,
313 palmas_vbus_irq_handler
,
314 IRQF_TRIGGER_FALLING
| IRQF_TRIGGER_RISING
|
316 "palmas_usb_vbus", palmas_usb
);
318 dev_err(&pdev
->dev
, "can't get IRQ %d, err %d\n",
319 palmas_usb
->vbus_irq
, status
);
322 } else if (palmas_usb
->enable_gpio_vbus_detection
) {
323 /* remux GPIO_1 as VBUSDET */
324 status
= palmas_update_bits(palmas
,
325 PALMAS_PU_PD_OD_BASE
,
326 PALMAS_PRIMARY_SECONDARY_PAD1
,
327 PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_MASK
,
328 (1 << PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_SHIFT
));
330 dev_err(&pdev
->dev
, "can't remux GPIO1\n");
334 palmas_usb
->vbus_otg_irq
= regmap_irq_get_virq(palmas
->irq_data
,
335 PALMAS_VBUS_OTG_IRQ
);
336 palmas_usb
->gpio_vbus_irq
= gpiod_to_irq(palmas_usb
->vbus_gpiod
);
337 if (palmas_usb
->gpio_vbus_irq
< 0) {
338 dev_err(&pdev
->dev
, "failed to get vbus irq\n");
339 return palmas_usb
->gpio_vbus_irq
;
341 status
= devm_request_threaded_irq(&pdev
->dev
,
342 palmas_usb
->gpio_vbus_irq
,
344 palmas_vbus_irq_handler
,
345 IRQF_TRIGGER_FALLING
|
346 IRQF_TRIGGER_RISING
|
352 "failed to request handler for vbus irq\n");
357 palmas_enable_irq(palmas_usb
);
358 /* perform initial detection */
359 if (palmas_usb
->enable_gpio_vbus_detection
)
360 palmas_vbus_irq_handler(palmas_usb
->gpio_vbus_irq
, palmas_usb
);
361 palmas_gpio_id_detect(&palmas_usb
->wq_detectid
.work
);
362 device_set_wakeup_capable(&pdev
->dev
, true);
366 #ifdef CONFIG_PM_SLEEP
367 static int palmas_usb_suspend(struct device
*dev
)
369 struct palmas_usb
*palmas_usb
= dev_get_drvdata(dev
);
371 if (device_may_wakeup(dev
)) {
372 if (palmas_usb
->enable_vbus_detection
)
373 enable_irq_wake(palmas_usb
->vbus_irq
);
374 if (palmas_usb
->enable_gpio_vbus_detection
)
375 enable_irq_wake(palmas_usb
->gpio_vbus_irq
);
376 if (palmas_usb
->enable_id_detection
)
377 enable_irq_wake(palmas_usb
->id_irq
);
378 if (palmas_usb
->enable_gpio_id_detection
)
379 enable_irq_wake(palmas_usb
->gpio_id_irq
);
384 static int palmas_usb_resume(struct device
*dev
)
386 struct palmas_usb
*palmas_usb
= dev_get_drvdata(dev
);
388 if (device_may_wakeup(dev
)) {
389 if (palmas_usb
->enable_vbus_detection
)
390 disable_irq_wake(palmas_usb
->vbus_irq
);
391 if (palmas_usb
->enable_gpio_vbus_detection
)
392 disable_irq_wake(palmas_usb
->gpio_vbus_irq
);
393 if (palmas_usb
->enable_id_detection
)
394 disable_irq_wake(palmas_usb
->id_irq
);
395 if (palmas_usb
->enable_gpio_id_detection
)
396 disable_irq_wake(palmas_usb
->gpio_id_irq
);
399 /* check if GPIO states changed while suspend/resume */
400 if (palmas_usb
->enable_gpio_vbus_detection
)
401 palmas_vbus_irq_handler(palmas_usb
->gpio_vbus_irq
, palmas_usb
);
402 palmas_gpio_id_detect(&palmas_usb
->wq_detectid
.work
);
408 static SIMPLE_DEV_PM_OPS(palmas_pm_ops
, palmas_usb_suspend
, palmas_usb_resume
);
410 static const struct of_device_id of_palmas_match_tbl
[] = {
411 { .compatible
= "ti,palmas-usb", },
412 { .compatible
= "ti,palmas-usb-vid", },
413 { .compatible
= "ti,twl6035-usb", },
414 { .compatible
= "ti,twl6035-usb-vid", },
418 static struct platform_driver palmas_usb_driver
= {
419 .probe
= palmas_usb_probe
,
421 .name
= "palmas-usb",
422 .of_match_table
= of_palmas_match_tbl
,
423 .pm
= &palmas_pm_ops
,
427 module_platform_driver(palmas_usb_driver
);
429 MODULE_ALIAS("platform:palmas-usb");
430 MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
431 MODULE_DESCRIPTION("Palmas USB transceiver driver");
432 MODULE_LICENSE("GPL");
433 MODULE_DEVICE_TABLE(of
, of_palmas_match_tbl
);