aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/otg
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@suse.de>2010-12-15 12:29:38 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2010-12-15 12:29:38 -0500
commit5cdc5bd8b2b1190cb54548c03194b154b4892e2a (patch)
tree79e9f66825a1155605a30e063ba59297a4ad0618 /drivers/usb/otg
parent6c34d2888221ca3df81e29f598873b4fb6cf838d (diff)
parente4a2b3565fc7ac2d70361a36337be57a59d783da (diff)
Merge branch 'musb-hw' of git://gitorious.org/usb/usb into musb
* 'musb-hw' of git://gitorious.org/usb/usb: (43 commits) usb: musb: core: kill unneeded #include's DA8xx: assign name to MUSB IRQ resource arm: OMAP4430: musb: Configure musb to OTG mode usb: musb: Adding musb support for OMAP4430 usb: otg: TWL6030: Add twl6030_usb file for compilation mfd: TWL6030: OMAP4: Registering the TWL6030-usb device usb: musb: TWL6030: Selecting TWL6030_USB transceiver usb: otg: Kconfig: Add Kconfig option for TWL6030 transceiver. usb: otg: Adding twl6030-usb transceiver driver for OMAP4430 mfd: TWL6030: USBOTG VBUS event generation on usb: musb: add support for ux500 platform musb: am35x: fix compile error due to control apis arm: omap4: enable usb on 4430sdp usb: musb: drop board_set_vbus usb: musb: drop musb_platform_suspend/resume usb: musb: blackfin: usb dev_pm_ops structure usb: musb: am35x: usb dev_pm_ops structure usb: musb: omap2430: use dev_pm_ops structure usb: musb: omap2430: drop the nops usb: musb: mark musb_save/restore_context static ...
Diffstat (limited to 'drivers/usb/otg')
-rw-r--r--drivers/usb/otg/Kconfig12
-rw-r--r--drivers/usb/otg/Makefile1
-rw-r--r--drivers/usb/otg/twl6030-usb.c493
3 files changed, 506 insertions, 0 deletions
diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig
index 6491717a636a..9fb875d5f09c 100644
--- a/drivers/usb/otg/Kconfig
+++ b/drivers/usb/otg/Kconfig
@@ -59,6 +59,18 @@ config TWL4030_USB
59 This transceiver supports high and full speed devices plus, 59 This transceiver supports high and full speed devices plus,
60 in host mode, low speed. 60 in host mode, low speed.
61 61
62config TWL6030_USB
63 tristate "TWL6030 USB Transceiver Driver"
64 depends on TWL4030_CORE
65 select USB_OTG_UTILS
66 help
67 Enable this to support the USB OTG transceiver on TWL6030
68 family chips. This TWL6030 transceiver has the VBUS and ID GND
69 and OTG SRP events capabilities. For all other transceiver functionality
70 UTMI PHY is embedded in OMAP4430. The internal PHY configurations APIs
71 are hooked to this driver through platform_data structure.
72 The definition of internal PHY APIs are in the mach-omap2 layer.
73
62config NOP_USB_XCEIV 74config NOP_USB_XCEIV
63 tristate "NOP USB Transceiver Driver" 75 tristate "NOP USB Transceiver Driver"
64 select USB_OTG_UTILS 76 select USB_OTG_UTILS
diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile
index 30a23f3b7a1f..a520e715cfd6 100644
--- a/drivers/usb/otg/Makefile
+++ b/drivers/usb/otg/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_USB_OTG_UTILS) += otg.o
12obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o 12obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o
13obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o 13obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o
14obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o 14obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o
15obj-$(CONFIG_TWL6030_USB) += twl6030-usb.o
15obj-$(CONFIG_USB_LANGWELL_OTG) += langwell_otg.o 16obj-$(CONFIG_USB_LANGWELL_OTG) += langwell_otg.o
16obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o 17obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o
17obj-$(CONFIG_USB_ULPI) += ulpi.o 18obj-$(CONFIG_USB_ULPI) += ulpi.o
diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c
new file mode 100644
index 000000000000..28f770103640
--- /dev/null
+++ b/drivers/usb/otg/twl6030-usb.c
@@ -0,0 +1,493 @@
1/*
2 * twl6030_usb - TWL6030 USB transceiver, talking to OMAP OTG driver.
3 *
4 * Copyright (C) 2010 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: Hema HK <hemahk@ti.com>
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 *
21 */
22
23#include <linux/module.h>
24#include <linux/init.h>
25#include <linux/interrupt.h>
26#include <linux/platform_device.h>
27#include <linux/io.h>
28#include <linux/usb/otg.h>
29#include <linux/i2c/twl.h>
30#include <linux/regulator/consumer.h>
31#include <linux/err.h>
32#include <linux/notifier.h>
33#include <linux/slab.h>
34
35/* usb register definitions */
36#define USB_VENDOR_ID_LSB 0x00
37#define USB_VENDOR_ID_MSB 0x01
38#define USB_PRODUCT_ID_LSB 0x02
39#define USB_PRODUCT_ID_MSB 0x03
40#define USB_VBUS_CTRL_SET 0x04
41#define USB_VBUS_CTRL_CLR 0x05
42#define USB_ID_CTRL_SET 0x06
43#define USB_ID_CTRL_CLR 0x07
44#define USB_VBUS_INT_SRC 0x08
45#define USB_VBUS_INT_LATCH_SET 0x09
46#define USB_VBUS_INT_LATCH_CLR 0x0A
47#define USB_VBUS_INT_EN_LO_SET 0x0B
48#define USB_VBUS_INT_EN_LO_CLR 0x0C
49#define USB_VBUS_INT_EN_HI_SET 0x0D
50#define USB_VBUS_INT_EN_HI_CLR 0x0E
51#define USB_ID_INT_SRC 0x0F
52#define USB_ID_INT_LATCH_SET 0x10
53#define USB_ID_INT_LATCH_CLR 0x11
54
55#define USB_ID_INT_EN_LO_SET 0x12
56#define USB_ID_INT_EN_LO_CLR 0x13
57#define USB_ID_INT_EN_HI_SET 0x14
58#define USB_ID_INT_EN_HI_CLR 0x15
59#define USB_OTG_ADP_CTRL 0x16
60#define USB_OTG_ADP_HIGH 0x17
61#define USB_OTG_ADP_LOW 0x18
62#define USB_OTG_ADP_RISE 0x19
63#define USB_OTG_REVISION 0x1A
64
65/* to be moved to LDO */
66#define TWL6030_MISC2 0xE5
67#define TWL6030_CFG_LDO_PD2 0xF5
68#define TWL6030_BACKUP_REG 0xFA
69
70#define STS_HW_CONDITIONS 0x21
71
72/* In module TWL6030_MODULE_PM_MASTER */
73#define STS_HW_CONDITIONS 0x21
74#define STS_USB_ID BIT(2)
75
76/* In module TWL6030_MODULE_PM_RECEIVER */
77#define VUSB_CFG_TRANS 0x71
78#define VUSB_CFG_STATE 0x72
79#define VUSB_CFG_VOLTAGE 0x73
80
81/* in module TWL6030_MODULE_MAIN_CHARGE */
82
83#define CHARGERUSB_CTRL1 0x8
84
85#define CONTROLLER_STAT1 0x03
86#define VBUS_DET BIT(2)
87
88struct twl6030_usb {
89 struct otg_transceiver otg;
90 struct device *dev;
91
92 /* for vbus reporting with irqs disabled */
93 spinlock_t lock;
94
95 struct regulator *usb3v3;
96
97 int irq1;
98 int irq2;
99 u8 linkstat;
100 u8 asleep;
101 bool irq_enabled;
102};
103
104#define xceiv_to_twl(x) container_of((x), struct twl6030_usb, otg);
105
106/*-------------------------------------------------------------------------*/
107
108static inline int twl6030_writeb(struct twl6030_usb *twl, u8 module,
109 u8 data, u8 address)
110{
111 int ret = 0;
112
113 ret = twl_i2c_write_u8(module, data, address);
114 if (ret < 0)
115 dev_err(twl->dev,
116 "Write[0x%x] Error %d\n", address, ret);
117 return ret;
118}
119
120static inline u8 twl6030_readb(struct twl6030_usb *twl, u8 module, u8 address)
121{
122 u8 data, ret = 0;
123
124 ret = twl_i2c_read_u8(module, &data, address);
125 if (ret >= 0)
126 ret = data;
127 else
128 dev_err(twl->dev,
129 "readb[0x%x,0x%x] Error %d\n",
130 module, address, ret);
131 return ret;
132}
133
134/*-------------------------------------------------------------------------*/
135static int twl6030_set_phy_clk(struct otg_transceiver *x, int on)
136{
137 struct twl6030_usb *twl;
138 struct device *dev;
139 struct twl4030_usb_data *pdata;
140
141 twl = xceiv_to_twl(x);
142 dev = twl->dev;
143 pdata = dev->platform_data;
144
145 pdata->phy_set_clock(twl->dev, on);
146
147 return 0;
148}
149
150static int twl6030_phy_init(struct otg_transceiver *x)
151{
152 u8 hw_state;
153 struct twl6030_usb *twl;
154 struct device *dev;
155 struct twl4030_usb_data *pdata;
156
157 twl = xceiv_to_twl(x);
158 dev = twl->dev;
159 pdata = dev->platform_data;
160
161 regulator_enable(twl->usb3v3);
162
163 hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS);
164
165 if (hw_state & STS_USB_ID)
166 pdata->phy_power(twl->dev, 1, 1);
167 else
168 pdata->phy_power(twl->dev, 0, 1);
169
170 return 0;
171}
172
173static void twl6030_phy_shutdown(struct otg_transceiver *x)
174{
175 struct twl6030_usb *twl;
176 struct device *dev;
177 struct twl4030_usb_data *pdata;
178
179 twl = xceiv_to_twl(x);
180 dev = twl->dev;
181 pdata = dev->platform_data;
182 pdata->phy_power(twl->dev, 0, 0);
183 regulator_disable(twl->usb3v3);
184}
185
186static int twl6030_usb_ldo_init(struct twl6030_usb *twl)
187{
188
189 /* Set to OTG_REV 1.3 and turn on the ID_WAKEUP_COMP */
190 twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x1, TWL6030_BACKUP_REG);
191
192 /* Program CFG_LDO_PD2 register and set VUSB bit */
193 twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x1, TWL6030_CFG_LDO_PD2);
194
195 /* Program MISC2 register and set bit VUSB_IN_VBAT */
196 twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x10, TWL6030_MISC2);
197
198 twl->usb3v3 = regulator_get(twl->dev, "vusb");
199 if (IS_ERR(twl->usb3v3))
200 return -ENODEV;
201
202 regulator_enable(twl->usb3v3);
203
204 /* Program the VUSB_CFG_TRANS for ACTIVE state. */
205 twl6030_writeb(twl, TWL_MODULE_PM_RECEIVER, 0x3F,
206 VUSB_CFG_TRANS);
207
208 /* Program the VUSB_CFG_STATE register to ON on all groups. */
209 twl6030_writeb(twl, TWL_MODULE_PM_RECEIVER, 0xE1,
210 VUSB_CFG_STATE);
211
212 /* Program the USB_VBUS_CTRL_SET and set VBUS_ACT_COMP bit */
213 twl6030_writeb(twl, TWL_MODULE_USB, 0x4, USB_VBUS_CTRL_SET);
214
215 /*
216 * Program the USB_ID_CTRL_SET register to enable GND drive
217 * and the ID comparators
218 */
219 twl6030_writeb(twl, TWL_MODULE_USB, 0x14, USB_ID_CTRL_SET);
220
221 return 0;
222}
223
224static ssize_t twl6030_usb_vbus_show(struct device *dev,
225 struct device_attribute *attr, char *buf)
226{
227 struct twl6030_usb *twl = dev_get_drvdata(dev);
228 unsigned long flags;
229 int ret = -EINVAL;
230
231 spin_lock_irqsave(&twl->lock, flags);
232
233 switch (twl->linkstat) {
234 case USB_EVENT_VBUS:
235 ret = snprintf(buf, PAGE_SIZE, "vbus\n");
236 break;
237 case USB_EVENT_ID:
238 ret = snprintf(buf, PAGE_SIZE, "id\n");
239 break;
240 case USB_EVENT_NONE:
241 ret = snprintf(buf, PAGE_SIZE, "none\n");
242 break;
243 default:
244 ret = snprintf(buf, PAGE_SIZE, "UNKNOWN\n");
245 }
246 spin_unlock_irqrestore(&twl->lock, flags);
247
248 return ret;
249}
250static DEVICE_ATTR(vbus, 0444, twl6030_usb_vbus_show, NULL);
251
252static irqreturn_t twl6030_usb_irq(int irq, void *_twl)
253{
254 struct twl6030_usb *twl = _twl;
255 int status;
256 u8 vbus_state, hw_state;
257
258 hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS);
259
260 vbus_state = twl6030_readb(twl, TWL_MODULE_MAIN_CHARGE,
261 CONTROLLER_STAT1);
262 if (!(hw_state & STS_USB_ID)) {
263 if (vbus_state & VBUS_DET) {
264 status = USB_EVENT_VBUS;
265 twl->otg.default_a = false;
266 twl->otg.state = OTG_STATE_B_IDLE;
267 } else {
268 status = USB_EVENT_NONE;
269 }
270 if (status >= 0) {
271 twl->linkstat = status;
272 blocking_notifier_call_chain(&twl->otg.notifier,
273 status, twl->otg.gadget);
274 }
275 }
276 sysfs_notify(&twl->dev->kobj, NULL, "vbus");
277
278 return IRQ_HANDLED;
279}
280
281static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl)
282{
283 struct twl6030_usb *twl = _twl;
284 int status = USB_EVENT_NONE;
285 u8 hw_state;
286
287 hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS);
288
289 if (hw_state & STS_USB_ID) {
290
291 twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_CLR, 0x1);
292 twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_SET,
293 0x10);
294 status = USB_EVENT_ID;
295 twl->otg.default_a = true;
296 twl->otg.state = OTG_STATE_A_IDLE;
297 blocking_notifier_call_chain(&twl->otg.notifier, status,
298 twl->otg.gadget);
299 } else {
300 twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_CLR,
301 0x10);
302 twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_SET,
303 0x1);
304 }
305 twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_LATCH_CLR, status);
306 twl->linkstat = status;
307
308 return IRQ_HANDLED;
309}
310
311static int twl6030_set_peripheral(struct otg_transceiver *x,
312 struct usb_gadget *gadget)
313{
314 struct twl6030_usb *twl;
315
316 if (!x)
317 return -ENODEV;
318
319 twl = xceiv_to_twl(x);
320 twl->otg.gadget = gadget;
321 if (!gadget)
322 twl->otg.state = OTG_STATE_UNDEFINED;
323
324 return 0;
325}
326
327static int twl6030_enable_irq(struct otg_transceiver *x)
328{
329 struct twl6030_usb *twl = xceiv_to_twl(x);
330
331 twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_SET, 0x1);
332 twl6030_interrupt_unmask(0x05, REG_INT_MSK_LINE_C);
333 twl6030_interrupt_unmask(0x05, REG_INT_MSK_STS_C);
334
335 twl6030_interrupt_unmask(TWL6030_CHARGER_CTRL_INT_MASK,
336 REG_INT_MSK_LINE_C);
337 twl6030_interrupt_unmask(TWL6030_CHARGER_CTRL_INT_MASK,
338 REG_INT_MSK_STS_C);
339 twl6030_usb_irq(twl->irq2, twl);
340 twl6030_usbotg_irq(twl->irq1, twl);
341
342 return 0;
343}
344
345static int twl6030_set_vbus(struct otg_transceiver *x, bool enabled)
346{
347 struct twl6030_usb *twl = xceiv_to_twl(x);
348
349 /*
350 * Start driving VBUS. Set OPA_MODE bit in CHARGERUSB_CTRL1
351 * register. This enables boost mode.
352 */
353 if (enabled)
354 twl6030_writeb(twl, TWL_MODULE_MAIN_CHARGE , 0x40,
355 CHARGERUSB_CTRL1);
356 else
357 twl6030_writeb(twl, TWL_MODULE_MAIN_CHARGE , 0x00,
358 CHARGERUSB_CTRL1);
359 return 0;
360}
361
362static int twl6030_set_host(struct otg_transceiver *x, struct usb_bus *host)
363{
364 struct twl6030_usb *twl;
365
366 if (!x)
367 return -ENODEV;
368
369 twl = xceiv_to_twl(x);
370 twl->otg.host = host;
371 if (!host)
372 twl->otg.state = OTG_STATE_UNDEFINED;
373 return 0;
374}
375
376static int __devinit twl6030_usb_probe(struct platform_device *pdev)
377{
378 struct twl6030_usb *twl;
379 int status, err;
380 struct twl4030_usb_data *pdata;
381 struct device *dev = &pdev->dev;
382 pdata = dev->platform_data;
383
384 twl = kzalloc(sizeof *twl, GFP_KERNEL);
385 if (!twl)
386 return -ENOMEM;
387
388 twl->dev = &pdev->dev;
389 twl->irq1 = platform_get_irq(pdev, 0);
390 twl->irq2 = platform_get_irq(pdev, 1);
391 twl->otg.dev = twl->dev;
392 twl->otg.label = "twl6030";
393 twl->otg.set_host = twl6030_set_host;
394 twl->otg.set_peripheral = twl6030_set_peripheral;
395 twl->otg.set_vbus = twl6030_set_vbus;
396 twl->otg.init = twl6030_phy_init;
397 twl->otg.shutdown = twl6030_phy_shutdown;
398
399 /* init spinlock for workqueue */
400 spin_lock_init(&twl->lock);
401
402 err = twl6030_usb_ldo_init(twl);
403 if (err) {
404 dev_err(&pdev->dev, "ldo init failed\n");
405 kfree(twl);
406 return err;
407 }
408 otg_set_transceiver(&twl->otg);
409
410 platform_set_drvdata(pdev, twl);
411 if (device_create_file(&pdev->dev, &dev_attr_vbus))
412 dev_warn(&pdev->dev, "could not create sysfs file\n");
413
414 BLOCKING_INIT_NOTIFIER_HEAD(&twl->otg.notifier);
415
416 twl->irq_enabled = true;
417 status = request_threaded_irq(twl->irq1, NULL, twl6030_usbotg_irq,
418 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
419 "twl6030_usb", twl);
420 if (status < 0) {
421 dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
422 twl->irq1, status);
423 device_remove_file(twl->dev, &dev_attr_vbus);
424 kfree(twl);
425 return status;
426 }
427
428 status = request_threaded_irq(twl->irq2, NULL, twl6030_usb_irq,
429 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
430 "twl6030_usb", twl);
431 if (status < 0) {
432 dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
433 twl->irq2, status);
434 free_irq(twl->irq1, twl);
435 device_remove_file(twl->dev, &dev_attr_vbus);
436 kfree(twl);
437 return status;
438 }
439
440 pdata->phy_init(dev);
441 twl6030_enable_irq(&twl->otg);
442 dev_info(&pdev->dev, "Initialized TWL6030 USB module\n");
443
444 return 0;
445}
446
447static int __exit twl6030_usb_remove(struct platform_device *pdev)
448{
449 struct twl6030_usb *twl = platform_get_drvdata(pdev);
450
451 struct twl4030_usb_data *pdata;
452 struct device *dev = &pdev->dev;
453 pdata = dev->platform_data;
454
455 twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK,
456 REG_INT_MSK_LINE_C);
457 twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK,
458 REG_INT_MSK_STS_C);
459 free_irq(twl->irq1, twl);
460 free_irq(twl->irq2, twl);
461 regulator_put(twl->usb3v3);
462 pdata->phy_exit(twl->dev);
463 device_remove_file(twl->dev, &dev_attr_vbus);
464 kfree(twl);
465
466 return 0;
467}
468
469static struct platform_driver twl6030_usb_driver = {
470 .probe = twl6030_usb_probe,
471 .remove = __exit_p(twl6030_usb_remove),
472 .driver = {
473 .name = "twl6030_usb",
474 .owner = THIS_MODULE,
475 },
476};
477
478static int __init twl6030_usb_init(void)
479{
480 return platform_driver_register(&twl6030_usb_driver);
481}
482subsys_initcall(twl6030_usb_init);
483
484static void __exit twl6030_usb_exit(void)
485{
486 platform_driver_unregister(&twl6030_usb_driver);
487}
488module_exit(twl6030_usb_exit);
489
490MODULE_ALIAS("platform:twl6030_usb");
491MODULE_AUTHOR("Hema HK <hemahk@ti.com>");
492MODULE_DESCRIPTION("TWL6030 USB transceiver driver");
493MODULE_LICENSE("GPL");