Commit | Line | Data |
---|---|---|
e0c201f3 PK |
1 | /* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. |
2 | * | |
3 | * This program is free software; you can redistribute it and/or modify | |
4 | * it under the terms of the GNU General Public License version 2 and | |
5 | * only version 2 as published by the Free Software Foundation. | |
6 | * | |
7 | * This program is distributed in the hope that it will be useful, | |
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | * GNU General Public License for more details. | |
11 | * | |
12 | * You should have received a copy of the GNU General Public License | |
13 | * along with this program; if not, write to the Free Software | |
14 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
15 | * 02110-1301, USA. | |
16 | * | |
17 | */ | |
18 | ||
19 | #include <linux/module.h> | |
20 | #include <linux/device.h> | |
21 | #include <linux/platform_device.h> | |
22 | #include <linux/clk.h> | |
23 | #include <linux/slab.h> | |
24 | #include <linux/interrupt.h> | |
25 | #include <linux/err.h> | |
26 | #include <linux/delay.h> | |
27 | #include <linux/io.h> | |
28 | #include <linux/ioport.h> | |
29 | #include <linux/uaccess.h> | |
30 | #include <linux/debugfs.h> | |
31 | #include <linux/seq_file.h> | |
87c0104a | 32 | #include <linux/pm_runtime.h> |
e0c201f3 PK |
33 | |
34 | #include <linux/usb.h> | |
35 | #include <linux/usb/otg.h> | |
36 | #include <linux/usb/ulpi.h> | |
37 | #include <linux/usb/gadget.h> | |
38 | #include <linux/usb/hcd.h> | |
39 | #include <linux/usb/msm_hsusb.h> | |
40 | #include <linux/usb/msm_hsusb_hw.h> | |
41 | ||
42 | #include <mach/clk.h> | |
43 | ||
44 | #define MSM_USB_BASE (motg->regs) | |
45 | #define DRIVER_NAME "msm_otg" | |
46 | ||
47 | #define ULPI_IO_TIMEOUT_USEC (10 * 1000) | |
48 | static int ulpi_read(struct otg_transceiver *otg, u32 reg) | |
49 | { | |
50 | struct msm_otg *motg = container_of(otg, struct msm_otg, otg); | |
51 | int cnt = 0; | |
52 | ||
53 | /* initiate read operation */ | |
54 | writel(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg), | |
55 | USB_ULPI_VIEWPORT); | |
56 | ||
57 | /* wait for completion */ | |
58 | while (cnt < ULPI_IO_TIMEOUT_USEC) { | |
59 | if (!(readl(USB_ULPI_VIEWPORT) & ULPI_RUN)) | |
60 | break; | |
61 | udelay(1); | |
62 | cnt++; | |
63 | } | |
64 | ||
65 | if (cnt >= ULPI_IO_TIMEOUT_USEC) { | |
66 | dev_err(otg->dev, "ulpi_read: timeout %08x\n", | |
67 | readl(USB_ULPI_VIEWPORT)); | |
68 | return -ETIMEDOUT; | |
69 | } | |
70 | return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT)); | |
71 | } | |
72 | ||
73 | static int ulpi_write(struct otg_transceiver *otg, u32 val, u32 reg) | |
74 | { | |
75 | struct msm_otg *motg = container_of(otg, struct msm_otg, otg); | |
76 | int cnt = 0; | |
77 | ||
78 | /* initiate write operation */ | |
79 | writel(ULPI_RUN | ULPI_WRITE | | |
80 | ULPI_ADDR(reg) | ULPI_DATA(val), | |
81 | USB_ULPI_VIEWPORT); | |
82 | ||
83 | /* wait for completion */ | |
84 | while (cnt < ULPI_IO_TIMEOUT_USEC) { | |
85 | if (!(readl(USB_ULPI_VIEWPORT) & ULPI_RUN)) | |
86 | break; | |
87 | udelay(1); | |
88 | cnt++; | |
89 | } | |
90 | ||
91 | if (cnt >= ULPI_IO_TIMEOUT_USEC) { | |
92 | dev_err(otg->dev, "ulpi_write: timeout\n"); | |
93 | return -ETIMEDOUT; | |
94 | } | |
95 | return 0; | |
96 | } | |
97 | ||
98 | static struct otg_io_access_ops msm_otg_io_ops = { | |
99 | .read = ulpi_read, | |
100 | .write = ulpi_write, | |
101 | }; | |
102 | ||
103 | static void ulpi_init(struct msm_otg *motg) | |
104 | { | |
105 | struct msm_otg_platform_data *pdata = motg->pdata; | |
106 | int *seq = pdata->phy_init_seq; | |
107 | ||
108 | if (!seq) | |
109 | return; | |
110 | ||
111 | while (seq[0] >= 0) { | |
112 | dev_vdbg(motg->otg.dev, "ulpi: write 0x%02x to 0x%02x\n", | |
113 | seq[0], seq[1]); | |
114 | ulpi_write(&motg->otg, seq[0], seq[1]); | |
115 | seq += 2; | |
116 | } | |
117 | } | |
118 | ||
119 | static int msm_otg_link_clk_reset(struct msm_otg *motg, bool assert) | |
120 | { | |
121 | int ret; | |
122 | ||
123 | if (assert) { | |
124 | ret = clk_reset(motg->clk, CLK_RESET_ASSERT); | |
125 | if (ret) | |
126 | dev_err(motg->otg.dev, "usb hs_clk assert failed\n"); | |
127 | } else { | |
128 | ret = clk_reset(motg->clk, CLK_RESET_DEASSERT); | |
129 | if (ret) | |
130 | dev_err(motg->otg.dev, "usb hs_clk deassert failed\n"); | |
131 | } | |
132 | return ret; | |
133 | } | |
134 | ||
135 | static int msm_otg_phy_clk_reset(struct msm_otg *motg) | |
136 | { | |
137 | int ret; | |
138 | ||
139 | ret = clk_reset(motg->phy_reset_clk, CLK_RESET_ASSERT); | |
140 | if (ret) { | |
141 | dev_err(motg->otg.dev, "usb phy clk assert failed\n"); | |
142 | return ret; | |
143 | } | |
144 | usleep_range(10000, 12000); | |
145 | ret = clk_reset(motg->phy_reset_clk, CLK_RESET_DEASSERT); | |
146 | if (ret) | |
147 | dev_err(motg->otg.dev, "usb phy clk deassert failed\n"); | |
148 | return ret; | |
149 | } | |
150 | ||
151 | static int msm_otg_phy_reset(struct msm_otg *motg) | |
152 | { | |
153 | u32 val; | |
154 | int ret; | |
155 | int retries; | |
156 | ||
157 | ret = msm_otg_link_clk_reset(motg, 1); | |
158 | if (ret) | |
159 | return ret; | |
160 | ret = msm_otg_phy_clk_reset(motg); | |
161 | if (ret) | |
162 | return ret; | |
163 | ret = msm_otg_link_clk_reset(motg, 0); | |
164 | if (ret) | |
165 | return ret; | |
166 | ||
167 | val = readl(USB_PORTSC) & ~PORTSC_PTS_MASK; | |
168 | writel(val | PORTSC_PTS_ULPI, USB_PORTSC); | |
169 | ||
170 | for (retries = 3; retries > 0; retries--) { | |
171 | ret = ulpi_write(&motg->otg, ULPI_FUNC_CTRL_SUSPENDM, | |
172 | ULPI_CLR(ULPI_FUNC_CTRL)); | |
173 | if (!ret) | |
174 | break; | |
175 | ret = msm_otg_phy_clk_reset(motg); | |
176 | if (ret) | |
177 | return ret; | |
178 | } | |
179 | if (!retries) | |
180 | return -ETIMEDOUT; | |
181 | ||
182 | /* This reset calibrates the phy, if the above write succeeded */ | |
183 | ret = msm_otg_phy_clk_reset(motg); | |
184 | if (ret) | |
185 | return ret; | |
186 | ||
187 | for (retries = 3; retries > 0; retries--) { | |
188 | ret = ulpi_read(&motg->otg, ULPI_DEBUG); | |
189 | if (ret != -ETIMEDOUT) | |
190 | break; | |
191 | ret = msm_otg_phy_clk_reset(motg); | |
192 | if (ret) | |
193 | return ret; | |
194 | } | |
195 | if (!retries) | |
196 | return -ETIMEDOUT; | |
197 | ||
198 | dev_info(motg->otg.dev, "phy_reset: success\n"); | |
199 | return 0; | |
200 | } | |
201 | ||
202 | #define LINK_RESET_TIMEOUT_USEC (250 * 1000) | |
203 | static int msm_otg_reset(struct otg_transceiver *otg) | |
204 | { | |
205 | struct msm_otg *motg = container_of(otg, struct msm_otg, otg); | |
206 | struct msm_otg_platform_data *pdata = motg->pdata; | |
207 | int cnt = 0; | |
208 | int ret; | |
209 | u32 val = 0; | |
210 | u32 ulpi_val = 0; | |
211 | ||
212 | ret = msm_otg_phy_reset(motg); | |
213 | if (ret) { | |
214 | dev_err(otg->dev, "phy_reset failed\n"); | |
215 | return ret; | |
216 | } | |
217 | ||
218 | ulpi_init(motg); | |
219 | ||
220 | writel(USBCMD_RESET, USB_USBCMD); | |
221 | while (cnt < LINK_RESET_TIMEOUT_USEC) { | |
222 | if (!(readl(USB_USBCMD) & USBCMD_RESET)) | |
223 | break; | |
224 | udelay(1); | |
225 | cnt++; | |
226 | } | |
227 | if (cnt >= LINK_RESET_TIMEOUT_USEC) | |
228 | return -ETIMEDOUT; | |
229 | ||
230 | /* select ULPI phy */ | |
231 | writel(0x80000000, USB_PORTSC); | |
232 | ||
233 | msleep(100); | |
234 | ||
235 | writel(0x0, USB_AHBBURST); | |
236 | writel(0x00, USB_AHBMODE); | |
237 | ||
238 | if (pdata->otg_control == OTG_PHY_CONTROL) { | |
239 | val = readl(USB_OTGSC); | |
240 | if (pdata->mode == USB_OTG) { | |
241 | ulpi_val = ULPI_INT_IDGRD | ULPI_INT_SESS_VALID; | |
242 | val |= OTGSC_IDIE | OTGSC_BSVIE; | |
243 | } else if (pdata->mode == USB_PERIPHERAL) { | |
244 | ulpi_val = ULPI_INT_SESS_VALID; | |
245 | val |= OTGSC_BSVIE; | |
246 | } | |
247 | writel(val, USB_OTGSC); | |
248 | ulpi_write(otg, ulpi_val, ULPI_USB_INT_EN_RISE); | |
249 | ulpi_write(otg, ulpi_val, ULPI_USB_INT_EN_FALL); | |
250 | } | |
251 | ||
252 | return 0; | |
253 | } | |
254 | ||
87c0104a | 255 | #define PHY_SUSPEND_TIMEOUT_USEC (500 * 1000) |
7018773a PK |
256 | #define PHY_RESUME_TIMEOUT_USEC (100 * 1000) |
257 | ||
258 | #ifdef CONFIG_PM_SLEEP | |
87c0104a PK |
259 | static int msm_otg_suspend(struct msm_otg *motg) |
260 | { | |
261 | struct otg_transceiver *otg = &motg->otg; | |
262 | struct usb_bus *bus = otg->host; | |
263 | struct msm_otg_platform_data *pdata = motg->pdata; | |
264 | int cnt = 0; | |
265 | ||
266 | if (atomic_read(&motg->in_lpm)) | |
267 | return 0; | |
268 | ||
269 | disable_irq(motg->irq); | |
270 | /* | |
271 | * Interrupt Latch Register auto-clear feature is not present | |
272 | * in all PHY versions. Latch register is clear on read type. | |
273 | * Clear latch register to avoid spurious wakeup from | |
274 | * low power mode (LPM). | |
275 | */ | |
276 | ulpi_read(otg, 0x14); | |
277 | ||
278 | /* | |
279 | * PHY comparators are disabled when PHY enters into low power | |
280 | * mode (LPM). Keep PHY comparators ON in LPM only when we expect | |
281 | * VBUS/Id notifications from USB PHY. Otherwise turn off USB | |
282 | * PHY comparators. This save significant amount of power. | |
283 | */ | |
284 | if (pdata->otg_control == OTG_PHY_CONTROL) | |
285 | ulpi_write(otg, 0x01, 0x30); | |
286 | ||
287 | /* | |
288 | * PLL is not turned off when PHY enters into low power mode (LPM). | |
289 | * Disable PLL for maximum power savings. | |
290 | */ | |
291 | ulpi_write(otg, 0x08, 0x09); | |
292 | ||
293 | /* | |
294 | * PHY may take some time or even fail to enter into low power | |
295 | * mode (LPM). Hence poll for 500 msec and reset the PHY and link | |
296 | * in failure case. | |
297 | */ | |
298 | writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC); | |
299 | while (cnt < PHY_SUSPEND_TIMEOUT_USEC) { | |
300 | if (readl(USB_PORTSC) & PORTSC_PHCD) | |
301 | break; | |
302 | udelay(1); | |
303 | cnt++; | |
304 | } | |
305 | ||
306 | if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) { | |
307 | dev_err(otg->dev, "Unable to suspend PHY\n"); | |
308 | msm_otg_reset(otg); | |
309 | enable_irq(motg->irq); | |
310 | return -ETIMEDOUT; | |
311 | } | |
312 | ||
313 | /* | |
314 | * PHY has capability to generate interrupt asynchronously in low | |
315 | * power mode (LPM). This interrupt is level triggered. So USB IRQ | |
316 | * line must be disabled till async interrupt enable bit is cleared | |
317 | * in USBCMD register. Assert STP (ULPI interface STOP signal) to | |
318 | * block data communication from PHY. | |
319 | */ | |
320 | writel(readl(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD); | |
321 | ||
322 | clk_disable(motg->pclk); | |
323 | clk_disable(motg->clk); | |
324 | if (motg->core_clk) | |
325 | clk_disable(motg->core_clk); | |
326 | ||
327 | if (device_may_wakeup(otg->dev)) | |
328 | enable_irq_wake(motg->irq); | |
329 | if (bus) | |
330 | clear_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags); | |
331 | ||
332 | atomic_set(&motg->in_lpm, 1); | |
333 | enable_irq(motg->irq); | |
334 | ||
335 | dev_info(otg->dev, "USB in low power mode\n"); | |
336 | ||
337 | return 0; | |
338 | } | |
339 | ||
87c0104a PK |
340 | static int msm_otg_resume(struct msm_otg *motg) |
341 | { | |
342 | struct otg_transceiver *otg = &motg->otg; | |
343 | struct usb_bus *bus = otg->host; | |
344 | int cnt = 0; | |
345 | unsigned temp; | |
346 | ||
347 | if (!atomic_read(&motg->in_lpm)) | |
348 | return 0; | |
349 | ||
350 | clk_enable(motg->pclk); | |
351 | clk_enable(motg->clk); | |
352 | if (motg->core_clk) | |
353 | clk_enable(motg->core_clk); | |
354 | ||
355 | temp = readl(USB_USBCMD); | |
356 | temp &= ~ASYNC_INTR_CTRL; | |
357 | temp &= ~ULPI_STP_CTRL; | |
358 | writel(temp, USB_USBCMD); | |
359 | ||
360 | /* | |
361 | * PHY comes out of low power mode (LPM) in case of wakeup | |
362 | * from asynchronous interrupt. | |
363 | */ | |
364 | if (!(readl(USB_PORTSC) & PORTSC_PHCD)) | |
365 | goto skip_phy_resume; | |
366 | ||
367 | writel(readl(USB_PORTSC) & ~PORTSC_PHCD, USB_PORTSC); | |
368 | while (cnt < PHY_RESUME_TIMEOUT_USEC) { | |
369 | if (!(readl(USB_PORTSC) & PORTSC_PHCD)) | |
370 | break; | |
371 | udelay(1); | |
372 | cnt++; | |
373 | } | |
374 | ||
375 | if (cnt >= PHY_RESUME_TIMEOUT_USEC) { | |
376 | /* | |
377 | * This is a fatal error. Reset the link and | |
378 | * PHY. USB state can not be restored. Re-insertion | |
379 | * of USB cable is the only way to get USB working. | |
380 | */ | |
381 | dev_err(otg->dev, "Unable to resume USB." | |
382 | "Re-plugin the cable\n"); | |
383 | msm_otg_reset(otg); | |
384 | } | |
385 | ||
386 | skip_phy_resume: | |
387 | if (device_may_wakeup(otg->dev)) | |
388 | disable_irq_wake(motg->irq); | |
389 | if (bus) | |
390 | set_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags); | |
391 | ||
392 | if (motg->async_int) { | |
393 | motg->async_int = 0; | |
394 | pm_runtime_put(otg->dev); | |
395 | enable_irq(motg->irq); | |
396 | } | |
397 | ||
398 | atomic_set(&motg->in_lpm, 0); | |
399 | ||
400 | dev_info(otg->dev, "USB exited from low power mode\n"); | |
401 | ||
402 | return 0; | |
403 | } | |
7018773a | 404 | #endif |
87c0104a | 405 | |
e0c201f3 PK |
406 | static void msm_otg_start_host(struct otg_transceiver *otg, int on) |
407 | { | |
408 | struct msm_otg *motg = container_of(otg, struct msm_otg, otg); | |
409 | struct msm_otg_platform_data *pdata = motg->pdata; | |
410 | struct usb_hcd *hcd; | |
411 | ||
412 | if (!otg->host) | |
413 | return; | |
414 | ||
415 | hcd = bus_to_hcd(otg->host); | |
416 | ||
417 | if (on) { | |
418 | dev_dbg(otg->dev, "host on\n"); | |
419 | ||
420 | if (pdata->vbus_power) | |
421 | pdata->vbus_power(1); | |
422 | /* | |
423 | * Some boards have a switch cotrolled by gpio | |
424 | * to enable/disable internal HUB. Enable internal | |
425 | * HUB before kicking the host. | |
426 | */ | |
427 | if (pdata->setup_gpio) | |
428 | pdata->setup_gpio(OTG_STATE_A_HOST); | |
429 | #ifdef CONFIG_USB | |
430 | usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); | |
431 | #endif | |
432 | } else { | |
433 | dev_dbg(otg->dev, "host off\n"); | |
434 | ||
435 | #ifdef CONFIG_USB | |
436 | usb_remove_hcd(hcd); | |
437 | #endif | |
438 | if (pdata->setup_gpio) | |
439 | pdata->setup_gpio(OTG_STATE_UNDEFINED); | |
440 | if (pdata->vbus_power) | |
441 | pdata->vbus_power(0); | |
442 | } | |
443 | } | |
444 | ||
445 | static int msm_otg_set_host(struct otg_transceiver *otg, struct usb_bus *host) | |
446 | { | |
447 | struct msm_otg *motg = container_of(otg, struct msm_otg, otg); | |
448 | struct usb_hcd *hcd; | |
449 | ||
450 | /* | |
451 | * Fail host registration if this board can support | |
452 | * only peripheral configuration. | |
453 | */ | |
454 | if (motg->pdata->mode == USB_PERIPHERAL) { | |
455 | dev_info(otg->dev, "Host mode is not supported\n"); | |
456 | return -ENODEV; | |
457 | } | |
458 | ||
459 | if (!host) { | |
460 | if (otg->state == OTG_STATE_A_HOST) { | |
87c0104a | 461 | pm_runtime_get_sync(otg->dev); |
e0c201f3 PK |
462 | msm_otg_start_host(otg, 0); |
463 | otg->host = NULL; | |
464 | otg->state = OTG_STATE_UNDEFINED; | |
465 | schedule_work(&motg->sm_work); | |
466 | } else { | |
467 | otg->host = NULL; | |
468 | } | |
469 | ||
470 | return 0; | |
471 | } | |
472 | ||
473 | hcd = bus_to_hcd(host); | |
474 | hcd->power_budget = motg->pdata->power_budget; | |
475 | ||
476 | otg->host = host; | |
477 | dev_dbg(otg->dev, "host driver registered w/ tranceiver\n"); | |
478 | ||
479 | /* | |
480 | * Kick the state machine work, if peripheral is not supported | |
481 | * or peripheral is already registered with us. | |
482 | */ | |
87c0104a PK |
483 | if (motg->pdata->mode == USB_HOST || otg->gadget) { |
484 | pm_runtime_get_sync(otg->dev); | |
e0c201f3 | 485 | schedule_work(&motg->sm_work); |
87c0104a | 486 | } |
e0c201f3 PK |
487 | |
488 | return 0; | |
489 | } | |
490 | ||
491 | static void msm_otg_start_peripheral(struct otg_transceiver *otg, int on) | |
492 | { | |
493 | struct msm_otg *motg = container_of(otg, struct msm_otg, otg); | |
494 | struct msm_otg_platform_data *pdata = motg->pdata; | |
495 | ||
496 | if (!otg->gadget) | |
497 | return; | |
498 | ||
499 | if (on) { | |
500 | dev_dbg(otg->dev, "gadget on\n"); | |
501 | /* | |
502 | * Some boards have a switch cotrolled by gpio | |
503 | * to enable/disable internal HUB. Disable internal | |
504 | * HUB before kicking the gadget. | |
505 | */ | |
506 | if (pdata->setup_gpio) | |
507 | pdata->setup_gpio(OTG_STATE_B_PERIPHERAL); | |
508 | usb_gadget_vbus_connect(otg->gadget); | |
509 | } else { | |
510 | dev_dbg(otg->dev, "gadget off\n"); | |
511 | usb_gadget_vbus_disconnect(otg->gadget); | |
512 | if (pdata->setup_gpio) | |
513 | pdata->setup_gpio(OTG_STATE_UNDEFINED); | |
514 | } | |
515 | ||
516 | } | |
517 | ||
518 | static int msm_otg_set_peripheral(struct otg_transceiver *otg, | |
519 | struct usb_gadget *gadget) | |
520 | { | |
521 | struct msm_otg *motg = container_of(otg, struct msm_otg, otg); | |
522 | ||
523 | /* | |
524 | * Fail peripheral registration if this board can support | |
525 | * only host configuration. | |
526 | */ | |
527 | if (motg->pdata->mode == USB_HOST) { | |
528 | dev_info(otg->dev, "Peripheral mode is not supported\n"); | |
529 | return -ENODEV; | |
530 | } | |
531 | ||
532 | if (!gadget) { | |
533 | if (otg->state == OTG_STATE_B_PERIPHERAL) { | |
87c0104a | 534 | pm_runtime_get_sync(otg->dev); |
e0c201f3 PK |
535 | msm_otg_start_peripheral(otg, 0); |
536 | otg->gadget = NULL; | |
537 | otg->state = OTG_STATE_UNDEFINED; | |
538 | schedule_work(&motg->sm_work); | |
539 | } else { | |
540 | otg->gadget = NULL; | |
541 | } | |
542 | ||
543 | return 0; | |
544 | } | |
545 | otg->gadget = gadget; | |
546 | dev_dbg(otg->dev, "peripheral driver registered w/ tranceiver\n"); | |
547 | ||
548 | /* | |
549 | * Kick the state machine work, if host is not supported | |
550 | * or host is already registered with us. | |
551 | */ | |
87c0104a PK |
552 | if (motg->pdata->mode == USB_PERIPHERAL || otg->host) { |
553 | pm_runtime_get_sync(otg->dev); | |
e0c201f3 | 554 | schedule_work(&motg->sm_work); |
87c0104a | 555 | } |
e0c201f3 PK |
556 | |
557 | return 0; | |
558 | } | |
559 | ||
560 | /* | |
561 | * We support OTG, Peripheral only and Host only configurations. In case | |
562 | * of OTG, mode switch (host-->peripheral/peripheral-->host) can happen | |
563 | * via Id pin status or user request (debugfs). Id/BSV interrupts are not | |
564 | * enabled when switch is controlled by user and default mode is supplied | |
565 | * by board file, which can be changed by userspace later. | |
566 | */ | |
567 | static void msm_otg_init_sm(struct msm_otg *motg) | |
568 | { | |
569 | struct msm_otg_platform_data *pdata = motg->pdata; | |
570 | u32 otgsc = readl(USB_OTGSC); | |
571 | ||
572 | switch (pdata->mode) { | |
573 | case USB_OTG: | |
574 | if (pdata->otg_control == OTG_PHY_CONTROL) { | |
575 | if (otgsc & OTGSC_ID) | |
576 | set_bit(ID, &motg->inputs); | |
577 | else | |
578 | clear_bit(ID, &motg->inputs); | |
579 | ||
580 | if (otgsc & OTGSC_BSV) | |
581 | set_bit(B_SESS_VLD, &motg->inputs); | |
582 | else | |
583 | clear_bit(B_SESS_VLD, &motg->inputs); | |
584 | } else if (pdata->otg_control == OTG_USER_CONTROL) { | |
585 | if (pdata->default_mode == USB_HOST) { | |
586 | clear_bit(ID, &motg->inputs); | |
587 | } else if (pdata->default_mode == USB_PERIPHERAL) { | |
588 | set_bit(ID, &motg->inputs); | |
589 | set_bit(B_SESS_VLD, &motg->inputs); | |
590 | } else { | |
591 | set_bit(ID, &motg->inputs); | |
592 | clear_bit(B_SESS_VLD, &motg->inputs); | |
593 | } | |
594 | } | |
595 | break; | |
596 | case USB_HOST: | |
597 | clear_bit(ID, &motg->inputs); | |
598 | break; | |
599 | case USB_PERIPHERAL: | |
600 | set_bit(ID, &motg->inputs); | |
601 | if (otgsc & OTGSC_BSV) | |
602 | set_bit(B_SESS_VLD, &motg->inputs); | |
603 | else | |
604 | clear_bit(B_SESS_VLD, &motg->inputs); | |
605 | break; | |
606 | default: | |
607 | break; | |
608 | } | |
609 | } | |
610 | ||
611 | static void msm_otg_sm_work(struct work_struct *w) | |
612 | { | |
613 | struct msm_otg *motg = container_of(w, struct msm_otg, sm_work); | |
614 | struct otg_transceiver *otg = &motg->otg; | |
615 | ||
616 | switch (otg->state) { | |
617 | case OTG_STATE_UNDEFINED: | |
618 | dev_dbg(otg->dev, "OTG_STATE_UNDEFINED state\n"); | |
619 | msm_otg_reset(otg); | |
620 | msm_otg_init_sm(motg); | |
621 | otg->state = OTG_STATE_B_IDLE; | |
622 | /* FALL THROUGH */ | |
623 | case OTG_STATE_B_IDLE: | |
624 | dev_dbg(otg->dev, "OTG_STATE_B_IDLE state\n"); | |
625 | if (!test_bit(ID, &motg->inputs) && otg->host) { | |
626 | /* disable BSV bit */ | |
627 | writel(readl(USB_OTGSC) & ~OTGSC_BSVIE, USB_OTGSC); | |
628 | msm_otg_start_host(otg, 1); | |
629 | otg->state = OTG_STATE_A_HOST; | |
630 | } else if (test_bit(B_SESS_VLD, &motg->inputs) && otg->gadget) { | |
631 | msm_otg_start_peripheral(otg, 1); | |
632 | otg->state = OTG_STATE_B_PERIPHERAL; | |
633 | } | |
87c0104a | 634 | pm_runtime_put_sync(otg->dev); |
e0c201f3 PK |
635 | break; |
636 | case OTG_STATE_B_PERIPHERAL: | |
637 | dev_dbg(otg->dev, "OTG_STATE_B_PERIPHERAL state\n"); | |
638 | if (!test_bit(B_SESS_VLD, &motg->inputs) || | |
639 | !test_bit(ID, &motg->inputs)) { | |
640 | msm_otg_start_peripheral(otg, 0); | |
641 | otg->state = OTG_STATE_B_IDLE; | |
642 | msm_otg_reset(otg); | |
643 | schedule_work(w); | |
644 | } | |
645 | break; | |
646 | case OTG_STATE_A_HOST: | |
647 | dev_dbg(otg->dev, "OTG_STATE_A_HOST state\n"); | |
648 | if (test_bit(ID, &motg->inputs)) { | |
649 | msm_otg_start_host(otg, 0); | |
650 | otg->state = OTG_STATE_B_IDLE; | |
651 | msm_otg_reset(otg); | |
652 | schedule_work(w); | |
653 | } | |
654 | break; | |
655 | default: | |
656 | break; | |
657 | } | |
658 | } | |
659 | ||
660 | static irqreturn_t msm_otg_irq(int irq, void *data) | |
661 | { | |
662 | struct msm_otg *motg = data; | |
663 | struct otg_transceiver *otg = &motg->otg; | |
664 | u32 otgsc = 0; | |
665 | ||
87c0104a PK |
666 | if (atomic_read(&motg->in_lpm)) { |
667 | disable_irq_nosync(irq); | |
668 | motg->async_int = 1; | |
669 | pm_runtime_get(otg->dev); | |
670 | return IRQ_HANDLED; | |
671 | } | |
672 | ||
e0c201f3 PK |
673 | otgsc = readl(USB_OTGSC); |
674 | if (!(otgsc & (OTGSC_IDIS | OTGSC_BSVIS))) | |
675 | return IRQ_NONE; | |
676 | ||
677 | if ((otgsc & OTGSC_IDIS) && (otgsc & OTGSC_IDIE)) { | |
678 | if (otgsc & OTGSC_ID) | |
679 | set_bit(ID, &motg->inputs); | |
680 | else | |
681 | clear_bit(ID, &motg->inputs); | |
682 | dev_dbg(otg->dev, "ID set/clear\n"); | |
87c0104a | 683 | pm_runtime_get_noresume(otg->dev); |
e0c201f3 PK |
684 | } else if ((otgsc & OTGSC_BSVIS) && (otgsc & OTGSC_BSVIE)) { |
685 | if (otgsc & OTGSC_BSV) | |
686 | set_bit(B_SESS_VLD, &motg->inputs); | |
687 | else | |
688 | clear_bit(B_SESS_VLD, &motg->inputs); | |
689 | dev_dbg(otg->dev, "BSV set/clear\n"); | |
87c0104a | 690 | pm_runtime_get_noresume(otg->dev); |
e0c201f3 PK |
691 | } |
692 | ||
693 | writel(otgsc, USB_OTGSC); | |
694 | schedule_work(&motg->sm_work); | |
695 | return IRQ_HANDLED; | |
696 | } | |
697 | ||
698 | static int msm_otg_mode_show(struct seq_file *s, void *unused) | |
699 | { | |
700 | struct msm_otg *motg = s->private; | |
701 | struct otg_transceiver *otg = &motg->otg; | |
702 | ||
703 | switch (otg->state) { | |
704 | case OTG_STATE_A_HOST: | |
705 | seq_printf(s, "host\n"); | |
706 | break; | |
707 | case OTG_STATE_B_PERIPHERAL: | |
708 | seq_printf(s, "peripheral\n"); | |
709 | break; | |
710 | default: | |
711 | seq_printf(s, "none\n"); | |
712 | break; | |
713 | } | |
714 | ||
715 | return 0; | |
716 | } | |
717 | ||
718 | static int msm_otg_mode_open(struct inode *inode, struct file *file) | |
719 | { | |
720 | return single_open(file, msm_otg_mode_show, inode->i_private); | |
721 | } | |
722 | ||
723 | static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf, | |
724 | size_t count, loff_t *ppos) | |
725 | { | |
726 | struct msm_otg *motg = file->private_data; | |
727 | char buf[16]; | |
728 | struct otg_transceiver *otg = &motg->otg; | |
729 | int status = count; | |
730 | enum usb_mode_type req_mode; | |
731 | ||
732 | memset(buf, 0x00, sizeof(buf)); | |
733 | ||
734 | if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) { | |
735 | status = -EFAULT; | |
736 | goto out; | |
737 | } | |
738 | ||
739 | if (!strncmp(buf, "host", 4)) { | |
740 | req_mode = USB_HOST; | |
741 | } else if (!strncmp(buf, "peripheral", 10)) { | |
742 | req_mode = USB_PERIPHERAL; | |
743 | } else if (!strncmp(buf, "none", 4)) { | |
744 | req_mode = USB_NONE; | |
745 | } else { | |
746 | status = -EINVAL; | |
747 | goto out; | |
748 | } | |
749 | ||
750 | switch (req_mode) { | |
751 | case USB_NONE: | |
752 | switch (otg->state) { | |
753 | case OTG_STATE_A_HOST: | |
754 | case OTG_STATE_B_PERIPHERAL: | |
755 | set_bit(ID, &motg->inputs); | |
756 | clear_bit(B_SESS_VLD, &motg->inputs); | |
757 | break; | |
758 | default: | |
759 | goto out; | |
760 | } | |
761 | break; | |
762 | case USB_PERIPHERAL: | |
763 | switch (otg->state) { | |
764 | case OTG_STATE_B_IDLE: | |
765 | case OTG_STATE_A_HOST: | |
766 | set_bit(ID, &motg->inputs); | |
767 | set_bit(B_SESS_VLD, &motg->inputs); | |
768 | break; | |
769 | default: | |
770 | goto out; | |
771 | } | |
772 | break; | |
773 | case USB_HOST: | |
774 | switch (otg->state) { | |
775 | case OTG_STATE_B_IDLE: | |
776 | case OTG_STATE_B_PERIPHERAL: | |
777 | clear_bit(ID, &motg->inputs); | |
778 | break; | |
779 | default: | |
780 | goto out; | |
781 | } | |
782 | break; | |
783 | default: | |
784 | goto out; | |
785 | } | |
786 | ||
87c0104a | 787 | pm_runtime_get_sync(otg->dev); |
e0c201f3 PK |
788 | schedule_work(&motg->sm_work); |
789 | out: | |
790 | return status; | |
791 | } | |
792 | ||
793 | const struct file_operations msm_otg_mode_fops = { | |
794 | .open = msm_otg_mode_open, | |
795 | .read = seq_read, | |
796 | .write = msm_otg_mode_write, | |
797 | .llseek = seq_lseek, | |
798 | .release = single_release, | |
799 | }; | |
800 | ||
801 | static struct dentry *msm_otg_dbg_root; | |
802 | static struct dentry *msm_otg_dbg_mode; | |
803 | ||
804 | static int msm_otg_debugfs_init(struct msm_otg *motg) | |
805 | { | |
806 | msm_otg_dbg_root = debugfs_create_dir("msm_otg", NULL); | |
807 | ||
808 | if (!msm_otg_dbg_root || IS_ERR(msm_otg_dbg_root)) | |
809 | return -ENODEV; | |
810 | ||
811 | msm_otg_dbg_mode = debugfs_create_file("mode", S_IRUGO | S_IWUSR, | |
812 | msm_otg_dbg_root, motg, &msm_otg_mode_fops); | |
813 | if (!msm_otg_dbg_mode) { | |
814 | debugfs_remove(msm_otg_dbg_root); | |
815 | msm_otg_dbg_root = NULL; | |
816 | return -ENODEV; | |
817 | } | |
818 | ||
819 | return 0; | |
820 | } | |
821 | ||
822 | static void msm_otg_debugfs_cleanup(void) | |
823 | { | |
824 | debugfs_remove(msm_otg_dbg_mode); | |
825 | debugfs_remove(msm_otg_dbg_root); | |
826 | } | |
827 | ||
828 | static int __init msm_otg_probe(struct platform_device *pdev) | |
829 | { | |
830 | int ret = 0; | |
831 | struct resource *res; | |
832 | struct msm_otg *motg; | |
833 | struct otg_transceiver *otg; | |
834 | ||
835 | dev_info(&pdev->dev, "msm_otg probe\n"); | |
836 | if (!pdev->dev.platform_data) { | |
837 | dev_err(&pdev->dev, "No platform data given. Bailing out\n"); | |
838 | return -ENODEV; | |
839 | } | |
840 | ||
841 | motg = kzalloc(sizeof(struct msm_otg), GFP_KERNEL); | |
842 | if (!motg) { | |
843 | dev_err(&pdev->dev, "unable to allocate msm_otg\n"); | |
844 | return -ENOMEM; | |
845 | } | |
846 | ||
847 | motg->pdata = pdev->dev.platform_data; | |
848 | otg = &motg->otg; | |
849 | otg->dev = &pdev->dev; | |
850 | ||
851 | motg->phy_reset_clk = clk_get(&pdev->dev, "usb_phy_clk"); | |
852 | if (IS_ERR(motg->phy_reset_clk)) { | |
853 | dev_err(&pdev->dev, "failed to get usb_phy_clk\n"); | |
854 | ret = PTR_ERR(motg->phy_reset_clk); | |
855 | goto free_motg; | |
856 | } | |
857 | ||
858 | motg->clk = clk_get(&pdev->dev, "usb_hs_clk"); | |
859 | if (IS_ERR(motg->clk)) { | |
860 | dev_err(&pdev->dev, "failed to get usb_hs_clk\n"); | |
861 | ret = PTR_ERR(motg->clk); | |
862 | goto put_phy_reset_clk; | |
863 | } | |
864 | ||
865 | motg->pclk = clk_get(&pdev->dev, "usb_hs_pclk"); | |
866 | if (IS_ERR(motg->pclk)) { | |
867 | dev_err(&pdev->dev, "failed to get usb_hs_pclk\n"); | |
868 | ret = PTR_ERR(motg->pclk); | |
869 | goto put_clk; | |
870 | } | |
871 | ||
872 | /* | |
873 | * USB core clock is not present on all MSM chips. This | |
874 | * clock is introduced to remove the dependency on AXI | |
875 | * bus frequency. | |
876 | */ | |
877 | motg->core_clk = clk_get(&pdev->dev, "usb_hs_core_clk"); | |
878 | if (IS_ERR(motg->core_clk)) | |
879 | motg->core_clk = NULL; | |
880 | ||
881 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
882 | if (!res) { | |
883 | dev_err(&pdev->dev, "failed to get platform resource mem\n"); | |
884 | ret = -ENODEV; | |
885 | goto put_core_clk; | |
886 | } | |
887 | ||
888 | motg->regs = ioremap(res->start, resource_size(res)); | |
889 | if (!motg->regs) { | |
890 | dev_err(&pdev->dev, "ioremap failed\n"); | |
891 | ret = -ENOMEM; | |
892 | goto put_core_clk; | |
893 | } | |
894 | dev_info(&pdev->dev, "OTG regs = %p\n", motg->regs); | |
895 | ||
896 | motg->irq = platform_get_irq(pdev, 0); | |
897 | if (!motg->irq) { | |
898 | dev_err(&pdev->dev, "platform_get_irq failed\n"); | |
899 | ret = -ENODEV; | |
900 | goto free_regs; | |
901 | } | |
902 | ||
903 | clk_enable(motg->clk); | |
904 | clk_enable(motg->pclk); | |
905 | if (motg->core_clk) | |
906 | clk_enable(motg->core_clk); | |
907 | ||
908 | writel(0, USB_USBINTR); | |
909 | writel(0, USB_OTGSC); | |
910 | ||
911 | INIT_WORK(&motg->sm_work, msm_otg_sm_work); | |
912 | ret = request_irq(motg->irq, msm_otg_irq, IRQF_SHARED, | |
913 | "msm_otg", motg); | |
914 | if (ret) { | |
915 | dev_err(&pdev->dev, "request irq failed\n"); | |
916 | goto disable_clks; | |
917 | } | |
918 | ||
919 | otg->init = msm_otg_reset; | |
920 | otg->set_host = msm_otg_set_host; | |
921 | otg->set_peripheral = msm_otg_set_peripheral; | |
922 | ||
923 | otg->io_ops = &msm_otg_io_ops; | |
924 | ||
925 | ret = otg_set_transceiver(&motg->otg); | |
926 | if (ret) { | |
927 | dev_err(&pdev->dev, "otg_set_transceiver failed\n"); | |
928 | goto free_irq; | |
929 | } | |
930 | ||
931 | platform_set_drvdata(pdev, motg); | |
932 | device_init_wakeup(&pdev->dev, 1); | |
933 | ||
934 | if (motg->pdata->mode == USB_OTG && | |
935 | motg->pdata->otg_control == OTG_USER_CONTROL) { | |
936 | ret = msm_otg_debugfs_init(motg); | |
937 | if (ret) | |
938 | dev_dbg(&pdev->dev, "mode debugfs file is" | |
939 | "not available\n"); | |
940 | } | |
941 | ||
87c0104a PK |
942 | pm_runtime_set_active(&pdev->dev); |
943 | pm_runtime_enable(&pdev->dev); | |
e0c201f3 | 944 | |
87c0104a | 945 | return 0; |
e0c201f3 PK |
946 | free_irq: |
947 | free_irq(motg->irq, motg); | |
948 | disable_clks: | |
949 | clk_disable(motg->pclk); | |
950 | clk_disable(motg->clk); | |
951 | free_regs: | |
952 | iounmap(motg->regs); | |
953 | put_core_clk: | |
954 | if (motg->core_clk) | |
955 | clk_put(motg->core_clk); | |
956 | clk_put(motg->pclk); | |
957 | put_clk: | |
958 | clk_put(motg->clk); | |
959 | put_phy_reset_clk: | |
960 | clk_put(motg->phy_reset_clk); | |
961 | free_motg: | |
962 | kfree(motg); | |
963 | return ret; | |
964 | } | |
965 | ||
966 | static int __devexit msm_otg_remove(struct platform_device *pdev) | |
967 | { | |
968 | struct msm_otg *motg = platform_get_drvdata(pdev); | |
969 | struct otg_transceiver *otg = &motg->otg; | |
87c0104a | 970 | int cnt = 0; |
e0c201f3 PK |
971 | |
972 | if (otg->host || otg->gadget) | |
973 | return -EBUSY; | |
974 | ||
975 | msm_otg_debugfs_cleanup(); | |
976 | cancel_work_sync(&motg->sm_work); | |
87c0104a | 977 | |
7018773a | 978 | pm_runtime_resume(&pdev->dev); |
87c0104a | 979 | |
e0c201f3 | 980 | device_init_wakeup(&pdev->dev, 0); |
87c0104a | 981 | pm_runtime_disable(&pdev->dev); |
e0c201f3 | 982 | |
87c0104a | 983 | otg_set_transceiver(NULL); |
e0c201f3 PK |
984 | free_irq(motg->irq, motg); |
985 | ||
87c0104a PK |
986 | /* |
987 | * Put PHY in low power mode. | |
988 | */ | |
989 | ulpi_read(otg, 0x14); | |
990 | ulpi_write(otg, 0x08, 0x09); | |
991 | ||
992 | writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC); | |
993 | while (cnt < PHY_SUSPEND_TIMEOUT_USEC) { | |
994 | if (readl(USB_PORTSC) & PORTSC_PHCD) | |
995 | break; | |
996 | udelay(1); | |
997 | cnt++; | |
998 | } | |
999 | if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) | |
1000 | dev_err(otg->dev, "Unable to suspend PHY\n"); | |
1001 | ||
e0c201f3 PK |
1002 | clk_disable(motg->pclk); |
1003 | clk_disable(motg->clk); | |
1004 | if (motg->core_clk) | |
1005 | clk_disable(motg->core_clk); | |
1006 | ||
1007 | iounmap(motg->regs); | |
87c0104a | 1008 | pm_runtime_set_suspended(&pdev->dev); |
e0c201f3 PK |
1009 | |
1010 | clk_put(motg->phy_reset_clk); | |
1011 | clk_put(motg->pclk); | |
1012 | clk_put(motg->clk); | |
1013 | if (motg->core_clk) | |
1014 | clk_put(motg->core_clk); | |
1015 | ||
1016 | kfree(motg); | |
1017 | ||
1018 | return 0; | |
1019 | } | |
1020 | ||
87c0104a PK |
1021 | #ifdef CONFIG_PM_RUNTIME |
1022 | static int msm_otg_runtime_idle(struct device *dev) | |
1023 | { | |
1024 | struct msm_otg *motg = dev_get_drvdata(dev); | |
1025 | struct otg_transceiver *otg = &motg->otg; | |
1026 | ||
1027 | dev_dbg(dev, "OTG runtime idle\n"); | |
1028 | ||
1029 | /* | |
1030 | * It is observed some times that a spurious interrupt | |
1031 | * comes when PHY is put into LPM immediately after PHY reset. | |
1032 | * This 1 sec delay also prevents entering into LPM immediately | |
1033 | * after asynchronous interrupt. | |
1034 | */ | |
1035 | if (otg->state != OTG_STATE_UNDEFINED) | |
1036 | pm_schedule_suspend(dev, 1000); | |
1037 | ||
1038 | return -EAGAIN; | |
1039 | } | |
1040 | ||
1041 | static int msm_otg_runtime_suspend(struct device *dev) | |
1042 | { | |
1043 | struct msm_otg *motg = dev_get_drvdata(dev); | |
1044 | ||
1045 | dev_dbg(dev, "OTG runtime suspend\n"); | |
1046 | return msm_otg_suspend(motg); | |
1047 | } | |
1048 | ||
1049 | static int msm_otg_runtime_resume(struct device *dev) | |
1050 | { | |
1051 | struct msm_otg *motg = dev_get_drvdata(dev); | |
1052 | ||
1053 | dev_dbg(dev, "OTG runtime resume\n"); | |
1054 | return msm_otg_resume(motg); | |
1055 | } | |
87c0104a PK |
1056 | #endif |
1057 | ||
7018773a | 1058 | #ifdef CONFIG_PM_SLEEP |
87c0104a PK |
1059 | static int msm_otg_pm_suspend(struct device *dev) |
1060 | { | |
1061 | struct msm_otg *motg = dev_get_drvdata(dev); | |
1062 | ||
1063 | dev_dbg(dev, "OTG PM suspend\n"); | |
1064 | return msm_otg_suspend(motg); | |
1065 | } | |
1066 | ||
1067 | static int msm_otg_pm_resume(struct device *dev) | |
1068 | { | |
1069 | struct msm_otg *motg = dev_get_drvdata(dev); | |
1070 | int ret; | |
1071 | ||
1072 | dev_dbg(dev, "OTG PM resume\n"); | |
1073 | ||
1074 | ret = msm_otg_resume(motg); | |
1075 | if (ret) | |
1076 | return ret; | |
1077 | ||
1078 | /* | |
1079 | * Runtime PM Documentation recommends bringing the | |
1080 | * device to full powered state upon resume. | |
1081 | */ | |
1082 | pm_runtime_disable(dev); | |
1083 | pm_runtime_set_active(dev); | |
1084 | pm_runtime_enable(dev); | |
1085 | ||
1086 | return 0; | |
1087 | } | |
87c0104a PK |
1088 | #endif |
1089 | ||
7018773a | 1090 | #ifdef CONFIG_PM |
87c0104a | 1091 | static const struct dev_pm_ops msm_otg_dev_pm_ops = { |
7018773a PK |
1092 | SET_SYSTEM_SLEEP_PM_OPS(msm_otg_pm_suspend, msm_otg_pm_resume) |
1093 | SET_RUNTIME_PM_OPS(msm_otg_runtime_suspend, msm_otg_runtime_resume, | |
1094 | msm_otg_runtime_idle) | |
87c0104a | 1095 | }; |
7018773a | 1096 | #endif |
87c0104a | 1097 | |
e0c201f3 PK |
1098 | static struct platform_driver msm_otg_driver = { |
1099 | .remove = __devexit_p(msm_otg_remove), | |
1100 | .driver = { | |
1101 | .name = DRIVER_NAME, | |
1102 | .owner = THIS_MODULE, | |
7018773a | 1103 | #ifdef CONFIG_PM |
87c0104a | 1104 | .pm = &msm_otg_dev_pm_ops, |
7018773a | 1105 | #endif |
e0c201f3 PK |
1106 | }, |
1107 | }; | |
1108 | ||
1109 | static int __init msm_otg_init(void) | |
1110 | { | |
1111 | return platform_driver_probe(&msm_otg_driver, msm_otg_probe); | |
1112 | } | |
1113 | ||
1114 | static void __exit msm_otg_exit(void) | |
1115 | { | |
1116 | platform_driver_unregister(&msm_otg_driver); | |
1117 | } | |
1118 | ||
1119 | module_init(msm_otg_init); | |
1120 | module_exit(msm_otg_exit); | |
1121 | ||
1122 | MODULE_LICENSE("GPL v2"); | |
1123 | MODULE_DESCRIPTION("MSM USB transceiver driver"); |