aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGraeme Gregory <gg@slimlogic.co.uk>2013-05-27 21:50:11 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-06-17 19:08:36 -0400
commitb1f254e35d85535b17af2786d06fe88f15f304f7 (patch)
treef20a4fc680fa2af1793ab11e11c09a6d23acad40
parent9c8a013af49cb82a1a47bede56e8ce5efd93b734 (diff)
extcon: Palmas Extcon Driver
This is the driver for the USB comparator built into the palmas chip. It handles the various USB OTG events that can be generated by cable insertion/removal. Signed-off-by: Graeme Gregory <gg@slimlogic.co.uk> Signed-off-by: Moiz Sonasath <m-sonasath@ti.com> Signed-off-by: Ruchika Kharwar <ruchika@ti.com> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com> Signed-off-by: George Cherian <george.cherian@ti.com> [kishon@ti.com: adapted palmas usb driver to use the extcon framework] Signed-off-by: Sebastien Guiriec <s-guiriec@ti.com> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com> Signed-off-by: Myungjoo Ham <myungjoo.ham@samsung.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--Documentation/devicetree/bindings/extcon/extcon-twl.txt15
-rw-r--r--drivers/extcon/Kconfig7
-rw-r--r--drivers/extcon/Makefile1
-rw-r--r--drivers/extcon/extcon-palmas.c246
-rw-r--r--include/linux/mfd/palmas.h26
5 files changed, 283 insertions, 12 deletions
diff --git a/Documentation/devicetree/bindings/extcon/extcon-twl.txt b/Documentation/devicetree/bindings/extcon/extcon-twl.txt
new file mode 100644
index 000000000000..58f531ab4df3
--- /dev/null
+++ b/Documentation/devicetree/bindings/extcon/extcon-twl.txt
@@ -0,0 +1,15 @@
1EXTCON FOR TWL CHIPS
2
3PALMAS USB COMPARATOR
4Required Properties:
5 - compatible : Should be "ti,palmas-usb" or "ti,twl6035-usb"
6 - vbus-supply : phandle to the regulator device tree node.
7
8Optional Properties:
9 - ti,wakeup : To enable the wakeup comparator in probe
10
11palmas-usb {
12 compatible = "ti,twl6035-usb", "ti,palmas-usb";
13 vbus-supply = <&smps10_reg>;
14 ti,wakeup;
15};
diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig
index 3297301a42d4..63f454e20576 100644
--- a/drivers/extcon/Kconfig
+++ b/drivers/extcon/Kconfig
@@ -53,4 +53,11 @@ config EXTCON_ARIZONA
53 with Wolfson Arizona devices. These are audio CODECs with 53 with Wolfson Arizona devices. These are audio CODECs with
54 advanced audio accessory detection support. 54 advanced audio accessory detection support.
55 55
56config EXTCON_PALMAS
57 tristate "Palmas USB EXTCON support"
58 depends on MFD_PALMAS
59 help
60 Say Y here to enable support for USB peripheral and USB host
61 detection by palmas usb.
62
56endif # MULTISTATE_SWITCH 63endif # MULTISTATE_SWITCH
diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile
index f98a3c4d46e0..540e2c3a4431 100644
--- a/drivers/extcon/Makefile
+++ b/drivers/extcon/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o
8obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o 8obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o
9obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o 9obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o
10obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o 10obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o
11obj-$(CONFIG_EXTCON_PALMAS) += extcon-palmas.o
diff --git a/drivers/extcon/extcon-palmas.c b/drivers/extcon/extcon-palmas.c
new file mode 100644
index 000000000000..b752a0ad7b63
--- /dev/null
+++ b/drivers/extcon/extcon-palmas.c
@@ -0,0 +1,246 @@
1/*
2 * Palmas USB transceiver driver
3 *
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.
9 *
10 * Author: Graeme Gregory <gg@slimlogic.co.uk>
11 * Author: Kishon Vijay Abraham I <kishon@ti.com>
12 *
13 * Based on twl6030_usb.c
14 *
15 * Author: Hema HK <hemahk@ti.com>
16 *
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.
21 */
22
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>
28#include <linux/of.h>
29#include <linux/of_platform.h>
30
31static const char *palmas_extcon_cable[] = {
32 [0] = "USB",
33 [1] = "USB-HOST",
34 NULL,
35};
36
37static const int mutually_exclusive[] = {0x3, 0x0};
38
39static void palmas_usb_wakeup(struct palmas *palmas, int enable)
40{
41 if (enable)
42 palmas_write(palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_WAKEUP,
43 PALMAS_USB_WAKEUP_ID_WK_UP_COMP);
44 else
45 palmas_write(palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_WAKEUP, 0);
46}
47
48static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb)
49{
50 struct palmas_usb *palmas_usb = _palmas_usb;
51 unsigned int vbus_line_state;
52
53 palmas_read(palmas_usb->palmas, PALMAS_INTERRUPT_BASE,
54 PALMAS_INT3_LINE_STATE, &vbus_line_state);
55
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);
60 } else {
61 dev_dbg(palmas_usb->dev,
62 "Spurious connect event detected\n");
63 }
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);
68 } else {
69 dev_dbg(palmas_usb->dev,
70 "Spurious disconnect event detected\n");
71 }
72 }
73
74 return IRQ_HANDLED;
75}
76
77static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb)
78{
79 unsigned int set;
80 struct palmas_usb *palmas_usb = _palmas_usb;
81
82 palmas_read(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
83 PALMAS_USB_ID_INT_LATCH_SET, &set);
84
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);
109 }
110
111 return IRQ_HANDLED;
112}
113
114static void palmas_enable_irq(struct palmas_usb *palmas_usb)
115{
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);
119
120 palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
121 PALMAS_USB_ID_CTRL_SET, PALMAS_USB_ID_CTRL_SET_ID_ACT_COMP);
122
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);
126
127 palmas_vbus_irq_handler(palmas_usb->vbus_irq, palmas_usb);
128
129 /* cold plug for host mode needs this delay */
130 msleep(30);
131 palmas_id_irq_handler(palmas_usb->id_irq, palmas_usb);
132}
133
134static int palmas_usb_probe(struct platform_device *pdev)
135{
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;
140 int status;
141
142 if (node && !pdata) {
143 pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
144
145 if (!pdata)
146 return -ENOMEM;
147
148 pdata->wakeup = of_property_read_bool(node, "ti,wakeup");
149 } else if (!pdata) {
150 return -EINVAL;
151 }
152
153 palmas_usb = devm_kzalloc(&pdev->dev, sizeof(*palmas_usb), GFP_KERNEL);
154 if (!palmas_usb)
155 return -ENOMEM;
156
157 palmas->usb = palmas_usb;
158 palmas_usb->palmas = palmas;
159
160 palmas_usb->dev = &pdev->dev;
161
162 palmas_usb->id_otg_irq = regmap_irq_get_virq(palmas->irq_data,
163 PALMAS_ID_OTG_IRQ);
164 palmas_usb->id_irq = regmap_irq_get_virq(palmas->irq_data,
165 PALMAS_ID_IRQ);
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,
169 PALMAS_VBUS_IRQ);
170
171 palmas_usb_wakeup(palmas, pdata->wakeup);
172
173 platform_set_drvdata(pdev, palmas_usb);
174
175 palmas_usb->edev.name = "palmas-usb";
176 palmas_usb->edev.supported_cable = palmas_extcon_cable;
177 palmas_usb->edev.mutually_exclusive = mutually_exclusive;
178
179 status = extcon_dev_register(&palmas_usb->edev, palmas_usb->dev);
180 if (status) {
181 dev_err(&pdev->dev, "failed to register extcon device\n");
182 return status;
183 }
184
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);
189 if (status < 0) {
190 dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
191 palmas_usb->id_irq, status);
192 goto fail_extcon;
193 }
194
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);
199 if (status < 0) {
200 dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
201 palmas_usb->vbus_irq, status);
202 goto fail_extcon;
203 }
204
205 palmas_enable_irq(palmas_usb);
206
207 return 0;
208
209fail_extcon:
210 extcon_dev_unregister(&palmas_usb->edev);
211
212 return status;
213}
214
215static int palmas_usb_remove(struct platform_device *pdev)
216{
217 struct palmas_usb *palmas_usb = platform_get_drvdata(pdev);
218
219 extcon_dev_unregister(&palmas_usb->edev);
220
221 return 0;
222}
223
224static struct of_device_id of_palmas_match_tbl[] = {
225 { .compatible = "ti,palmas-usb", },
226 { .compatible = "ti,twl6035-usb", },
227 { /* end */ }
228};
229
230static struct platform_driver palmas_usb_driver = {
231 .probe = palmas_usb_probe,
232 .remove = palmas_usb_remove,
233 .driver = {
234 .name = "palmas-usb",
235 .of_match_table = of_palmas_match_tbl,
236 .owner = THIS_MODULE,
237 },
238};
239
240module_platform_driver(palmas_usb_driver);
241
242MODULE_ALIAS("platform:palmas-usb");
243MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
244MODULE_DESCRIPTION("Palmas USB transceiver driver");
245MODULE_LICENSE("GPL");
246MODULE_DEVICE_TABLE(of, of_palmas_match_tbl);
diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h
index 8f21daf62fb5..9b81b2bdc46b 100644
--- a/include/linux/mfd/palmas.h
+++ b/include/linux/mfd/palmas.h
@@ -20,6 +20,8 @@
20#include <linux/leds.h> 20#include <linux/leds.h>
21#include <linux/regmap.h> 21#include <linux/regmap.h>
22#include <linux/regulator/driver.h> 22#include <linux/regulator/driver.h>
23#include <linux/extcon.h>
24#include <linux/usb/phy_companion.h>
23 25
24#define PALMAS_NUM_CLIENTS 3 26#define PALMAS_NUM_CLIENTS 3
25 27
@@ -37,6 +39,12 @@ struct palmas_gpadc;
37struct palmas_resource; 39struct palmas_resource;
38struct palmas_usb; 40struct palmas_usb;
39 41
42enum palmas_usb_state {
43 PALMAS_USB_STATE_DISCONNECT,
44 PALMAS_USB_STATE_VBUS,
45 PALMAS_USB_STATE_ID,
46};
47
40struct palmas { 48struct palmas {
41 struct device *dev; 49 struct device *dev;
42 50
@@ -180,9 +188,6 @@ struct palmas_pmic_platform_data {
180}; 188};
181 189
182struct palmas_usb_platform_data { 190struct palmas_usb_platform_data {
183 /* Set this if platform wishes its own vbus control */
184 int no_control_vbus;
185
186 /* Do we enable the wakeup comparator on probe */ 191 /* Do we enable the wakeup comparator on probe */
187 int wakeup; 192 int wakeup;
188}; 193};
@@ -350,22 +355,19 @@ struct palmas_usb {
350 struct palmas *palmas; 355 struct palmas *palmas;
351 struct device *dev; 356 struct device *dev;
352 357
353 /* for vbus reporting with irqs disabled */ 358 struct extcon_dev edev;
354 spinlock_t lock;
355
356 struct regulator *vbus_reg;
357 359
358 /* used to set vbus, in atomic path */ 360 /* used to set vbus, in atomic path */
359 struct work_struct set_vbus_work; 361 struct work_struct set_vbus_work;
360 362
361 int irq1; 363 int id_otg_irq;
362 int irq2; 364 int id_irq;
363 int irq3; 365 int vbus_otg_irq;
364 int irq4; 366 int vbus_irq;
365 367
366 int vbus_enable; 368 int vbus_enable;
367 369
368 u8 linkstat; 370 enum palmas_usb_state linkstat;
369}; 371};
370 372
371#define comparator_to_palmas(x) container_of((x), struct palmas_usb, comparator) 373#define comparator_to_palmas(x) container_of((x), struct palmas_usb, comparator)