diff options
author | Greg Kroah-Hartman <gregkh@suse.de> | 2010-12-15 12:29:38 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-12-15 12:29:38 -0500 |
commit | 5cdc5bd8b2b1190cb54548c03194b154b4892e2a (patch) | |
tree | 79e9f66825a1155605a30e063ba59297a4ad0618 /drivers/usb/otg | |
parent | 6c34d2888221ca3df81e29f598873b4fb6cf838d (diff) | |
parent | e4a2b3565fc7ac2d70361a36337be57a59d783da (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/Kconfig | 12 | ||||
-rw-r--r-- | drivers/usb/otg/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/otg/twl6030-usb.c | 493 |
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 | ||
62 | config 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 | |||
62 | config NOP_USB_XCEIV | 74 | config 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 | |||
12 | obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o | 12 | obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o |
13 | obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o | 13 | obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o |
14 | obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o | 14 | obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o |
15 | obj-$(CONFIG_TWL6030_USB) += twl6030-usb.o | ||
15 | obj-$(CONFIG_USB_LANGWELL_OTG) += langwell_otg.o | 16 | obj-$(CONFIG_USB_LANGWELL_OTG) += langwell_otg.o |
16 | obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o | 17 | obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o |
17 | obj-$(CONFIG_USB_ULPI) += ulpi.o | 18 | obj-$(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 | |||
88 | struct 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 | |||
108 | static 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 | |||
120 | static 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 | /*-------------------------------------------------------------------------*/ | ||
135 | static 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 | |||
150 | static 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 | |||
173 | static 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 | |||
186 | static 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 | |||
224 | static 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 | } | ||
250 | static DEVICE_ATTR(vbus, 0444, twl6030_usb_vbus_show, NULL); | ||
251 | |||
252 | static 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 | |||
281 | static 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 | |||
311 | static 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 | |||
327 | static 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 | |||
345 | static 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 | |||
362 | static 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 | |||
376 | static 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 | |||
447 | static 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 | |||
469 | static 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 | |||
478 | static int __init twl6030_usb_init(void) | ||
479 | { | ||
480 | return platform_driver_register(&twl6030_usb_driver); | ||
481 | } | ||
482 | subsys_initcall(twl6030_usb_init); | ||
483 | |||
484 | static void __exit twl6030_usb_exit(void) | ||
485 | { | ||
486 | platform_driver_unregister(&twl6030_usb_driver); | ||
487 | } | ||
488 | module_exit(twl6030_usb_exit); | ||
489 | |||
490 | MODULE_ALIAS("platform:twl6030_usb"); | ||
491 | MODULE_AUTHOR("Hema HK <hemahk@ti.com>"); | ||
492 | MODULE_DESCRIPTION("TWL6030 USB transceiver driver"); | ||
493 | MODULE_LICENSE("GPL"); | ||