2 * Palmas USB transceiver driver
4 * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * Author: Graeme Gregory <gg@slimlogic.co.uk>
11 * Author: Kishon Vijay Abraham I <kishon@ti.com>
13 * Based on twl6030_usb.c
15 * Author: Hema HK <hemahk@ti.com>
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
23 #include <linux/module.h>
24 #include <linux/interrupt.h>
25 #include <linux/platform_device.h>
26 #include <linux/err.h>
27 #include <linux/mfd/palmas.h>
29 #include <linux/of_platform.h>
31 static const char *palmas_extcon_cable
[] = {
37 static const int mutually_exclusive
[] = {0x3, 0x0};
39 static void palmas_usb_wakeup(struct palmas
*palmas
, int enable
)
42 palmas_write(palmas
, PALMAS_USB_OTG_BASE
, PALMAS_USB_WAKEUP
,
43 PALMAS_USB_WAKEUP_ID_WK_UP_COMP
);
45 palmas_write(palmas
, PALMAS_USB_OTG_BASE
, PALMAS_USB_WAKEUP
, 0);
48 static irqreturn_t
palmas_vbus_irq_handler(int irq
, void *_palmas_usb
)
50 struct palmas_usb
*palmas_usb
= _palmas_usb
;
51 unsigned int vbus_line_state
;
53 palmas_read(palmas_usb
->palmas
, PALMAS_INTERRUPT_BASE
,
54 PALMAS_INT3_LINE_STATE
, &vbus_line_state
);
56 if (vbus_line_state
& PALMAS_INT3_LINE_STATE_VBUS
) {
57 if (palmas_usb
->linkstat
!= PALMAS_USB_STATE_VBUS
) {
58 palmas_usb
->linkstat
= PALMAS_USB_STATE_VBUS
;
59 extcon_set_cable_state(&palmas_usb
->edev
, "USB", true);
61 dev_dbg(palmas_usb
->dev
,
62 "Spurious connect event detected\n");
64 } else if (!(vbus_line_state
& PALMAS_INT3_LINE_STATE_VBUS
)) {
65 if (palmas_usb
->linkstat
== PALMAS_USB_STATE_VBUS
) {
66 palmas_usb
->linkstat
= PALMAS_USB_STATE_DISCONNECT
;
67 extcon_set_cable_state(&palmas_usb
->edev
, "USB", false);
69 dev_dbg(palmas_usb
->dev
,
70 "Spurious disconnect event detected\n");
77 static irqreturn_t
palmas_id_irq_handler(int irq
, void *_palmas_usb
)
80 struct palmas_usb
*palmas_usb
= _palmas_usb
;
82 palmas_read(palmas_usb
->palmas
, PALMAS_USB_OTG_BASE
,
83 PALMAS_USB_ID_INT_LATCH_SET
, &set
);
85 if (set
& PALMAS_USB_ID_INT_SRC_ID_GND
) {
86 palmas_write(palmas_usb
->palmas
, PALMAS_USB_OTG_BASE
,
87 PALMAS_USB_ID_INT_EN_HI_SET
,
88 PALMAS_USB_ID_INT_EN_HI_SET_ID_FLOAT
);
89 palmas_write(palmas_usb
->palmas
, PALMAS_USB_OTG_BASE
,
90 PALMAS_USB_ID_INT_EN_HI_CLR
,
91 PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND
);
92 palmas_write(palmas_usb
->palmas
, PALMAS_USB_OTG_BASE
,
93 PALMAS_USB_ID_INT_LATCH_CLR
,
94 PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND
);
95 palmas_usb
->linkstat
= PALMAS_USB_STATE_ID
;
96 extcon_set_cable_state(&palmas_usb
->edev
, "USB-HOST", true);
97 } else if (set
& PALMAS_USB_ID_INT_SRC_ID_FLOAT
) {
98 palmas_write(palmas_usb
->palmas
, PALMAS_USB_OTG_BASE
,
99 PALMAS_USB_ID_INT_EN_HI_SET
,
100 PALMAS_USB_ID_INT_EN_HI_SET_ID_GND
);
101 palmas_write(palmas_usb
->palmas
, PALMAS_USB_OTG_BASE
,
102 PALMAS_USB_ID_INT_EN_HI_CLR
,
103 PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT
);
104 palmas_write(palmas_usb
->palmas
, PALMAS_USB_OTG_BASE
,
105 PALMAS_USB_ID_INT_LATCH_CLR
,
106 PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT
);
107 palmas_usb
->linkstat
= PALMAS_USB_STATE_DISCONNECT
;
108 extcon_set_cable_state(&palmas_usb
->edev
, "USB-HOST", false);
114 static void palmas_enable_irq(struct palmas_usb
*palmas_usb
)
116 palmas_write(palmas_usb
->palmas
, PALMAS_USB_OTG_BASE
,
117 PALMAS_USB_VBUS_CTRL_SET
,
118 PALMAS_USB_VBUS_CTRL_SET_VBUS_ACT_COMP
);
120 palmas_write(palmas_usb
->palmas
, PALMAS_USB_OTG_BASE
,
121 PALMAS_USB_ID_CTRL_SET
, PALMAS_USB_ID_CTRL_SET_ID_ACT_COMP
);
123 palmas_write(palmas_usb
->palmas
, PALMAS_USB_OTG_BASE
,
124 PALMAS_USB_ID_INT_EN_HI_SET
,
125 PALMAS_USB_ID_INT_EN_HI_SET_ID_GND
);
127 palmas_vbus_irq_handler(palmas_usb
->vbus_irq
, palmas_usb
);
129 /* cold plug for host mode needs this delay */
131 palmas_id_irq_handler(palmas_usb
->id_irq
, palmas_usb
);
134 static int palmas_usb_probe(struct platform_device
*pdev
)
136 struct palmas
*palmas
= dev_get_drvdata(pdev
->dev
.parent
);
137 struct palmas_usb_platform_data
*pdata
= pdev
->dev
.platform_data
;
138 struct device_node
*node
= pdev
->dev
.of_node
;
139 struct palmas_usb
*palmas_usb
;
142 if (node
&& !pdata
) {
143 pdata
= devm_kzalloc(&pdev
->dev
, sizeof(*pdata
), GFP_KERNEL
);
148 pdata
->wakeup
= of_property_read_bool(node
, "ti,wakeup");
153 palmas_usb
= devm_kzalloc(&pdev
->dev
, sizeof(*palmas_usb
), GFP_KERNEL
);
157 palmas
->usb
= palmas_usb
;
158 palmas_usb
->palmas
= palmas
;
160 palmas_usb
->dev
= &pdev
->dev
;
162 palmas_usb
->id_otg_irq
= regmap_irq_get_virq(palmas
->irq_data
,
164 palmas_usb
->id_irq
= regmap_irq_get_virq(palmas
->irq_data
,
166 palmas_usb
->vbus_otg_irq
= regmap_irq_get_virq(palmas
->irq_data
,
167 PALMAS_VBUS_OTG_IRQ
);
168 palmas_usb
->vbus_irq
= regmap_irq_get_virq(palmas
->irq_data
,
171 palmas_usb_wakeup(palmas
, pdata
->wakeup
);
173 platform_set_drvdata(pdev
, palmas_usb
);
175 palmas_usb
->edev
.name
= "palmas-usb";
176 palmas_usb
->edev
.supported_cable
= palmas_extcon_cable
;
177 palmas_usb
->edev
.mutually_exclusive
= mutually_exclusive
;
179 status
= extcon_dev_register(&palmas_usb
->edev
, palmas_usb
->dev
);
181 dev_err(&pdev
->dev
, "failed to register extcon device\n");
185 status
= devm_request_threaded_irq(palmas_usb
->dev
, palmas_usb
->id_irq
,
186 NULL
, palmas_id_irq_handler
,
187 IRQF_TRIGGER_FALLING
| IRQF_TRIGGER_RISING
,
188 "palmas_usb_id", palmas_usb
);
190 dev_err(&pdev
->dev
, "can't get IRQ %d, err %d\n",
191 palmas_usb
->id_irq
, status
);
195 status
= devm_request_threaded_irq(palmas_usb
->dev
,
196 palmas_usb
->vbus_irq
, NULL
, palmas_vbus_irq_handler
,
197 IRQF_TRIGGER_FALLING
| IRQF_TRIGGER_RISING
,
198 "palmas_usb_vbus", palmas_usb
);
200 dev_err(&pdev
->dev
, "can't get IRQ %d, err %d\n",
201 palmas_usb
->vbus_irq
, status
);
205 palmas_enable_irq(palmas_usb
);
210 extcon_dev_unregister(&palmas_usb
->edev
);
215 static int palmas_usb_remove(struct platform_device
*pdev
)
217 struct palmas_usb
*palmas_usb
= platform_get_drvdata(pdev
);
219 extcon_dev_unregister(&palmas_usb
->edev
);
224 static struct of_device_id of_palmas_match_tbl
[] = {
225 { .compatible
= "ti,palmas-usb", },
226 { .compatible
= "ti,twl6035-usb", },
230 static struct platform_driver palmas_usb_driver
= {
231 .probe
= palmas_usb_probe
,
232 .remove
= palmas_usb_remove
,
234 .name
= "palmas-usb",
235 .of_match_table
= of_palmas_match_tbl
,
236 .owner
= THIS_MODULE
,
240 module_platform_driver(palmas_usb_driver
);
242 MODULE_ALIAS("platform:palmas-usb");
243 MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
244 MODULE_DESCRIPTION("Palmas USB transceiver driver");
245 MODULE_LICENSE("GPL");
246 MODULE_DEVICE_TABLE(of
, of_palmas_match_tbl
);