diff options
author | Pavankumar Kondeti <pkondeti@codeaurora.org> | 2010-12-07 07:23:55 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-12-10 17:23:31 -0500 |
commit | e0c201f339fe7fc38d1b0f6f4755ff627686c7e0 (patch) | |
tree | a8245f60d4e0c0e22901a15a655af45eaf17325d /drivers/usb/otg/msm72k_otg.c | |
parent | 05570297ecbe834b1756b522412b68eaffb9ab11 (diff) |
USB: Add MSM OTG Controller driver
This driver implements PHY initialization, clock management, ULPI IO ops
and simple OTG state machine to kick host/peripheral based on Id/VBUS
line status. VBUS/Id lines are tied to a reference voltage on some boards.
Hence provide debugfs interface to select host/peripheral mode.
Signed-off-by: Pavankumar Kondeti <pkondeti@codeaurora.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/otg/msm72k_otg.c')
-rw-r--r-- | drivers/usb/otg/msm72k_otg.c | 850 |
1 files changed, 850 insertions, 0 deletions
diff --git a/drivers/usb/otg/msm72k_otg.c b/drivers/usb/otg/msm72k_otg.c new file mode 100644 index 000000000000..46f468a912f4 --- /dev/null +++ b/drivers/usb/otg/msm72k_otg.c | |||
@@ -0,0 +1,850 @@ | |||
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> | ||
32 | |||
33 | #include <linux/usb.h> | ||
34 | #include <linux/usb/otg.h> | ||
35 | #include <linux/usb/ulpi.h> | ||
36 | #include <linux/usb/gadget.h> | ||
37 | #include <linux/usb/hcd.h> | ||
38 | #include <linux/usb/msm_hsusb.h> | ||
39 | #include <linux/usb/msm_hsusb_hw.h> | ||
40 | |||
41 | #include <mach/clk.h> | ||
42 | |||
43 | #define MSM_USB_BASE (motg->regs) | ||
44 | #define DRIVER_NAME "msm_otg" | ||
45 | |||
46 | #define ULPI_IO_TIMEOUT_USEC (10 * 1000) | ||
47 | static int ulpi_read(struct otg_transceiver *otg, u32 reg) | ||
48 | { | ||
49 | struct msm_otg *motg = container_of(otg, struct msm_otg, otg); | ||
50 | int cnt = 0; | ||
51 | |||
52 | /* initiate read operation */ | ||
53 | writel(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg), | ||
54 | USB_ULPI_VIEWPORT); | ||
55 | |||
56 | /* wait for completion */ | ||
57 | while (cnt < ULPI_IO_TIMEOUT_USEC) { | ||
58 | if (!(readl(USB_ULPI_VIEWPORT) & ULPI_RUN)) | ||
59 | break; | ||
60 | udelay(1); | ||
61 | cnt++; | ||
62 | } | ||
63 | |||
64 | if (cnt >= ULPI_IO_TIMEOUT_USEC) { | ||
65 | dev_err(otg->dev, "ulpi_read: timeout %08x\n", | ||
66 | readl(USB_ULPI_VIEWPORT)); | ||
67 | return -ETIMEDOUT; | ||
68 | } | ||
69 | return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT)); | ||
70 | } | ||
71 | |||
72 | static int ulpi_write(struct otg_transceiver *otg, u32 val, u32 reg) | ||
73 | { | ||
74 | struct msm_otg *motg = container_of(otg, struct msm_otg, otg); | ||
75 | int cnt = 0; | ||
76 | |||
77 | /* initiate write operation */ | ||
78 | writel(ULPI_RUN | ULPI_WRITE | | ||
79 | ULPI_ADDR(reg) | ULPI_DATA(val), | ||
80 | USB_ULPI_VIEWPORT); | ||
81 | |||
82 | /* wait for completion */ | ||
83 | while (cnt < ULPI_IO_TIMEOUT_USEC) { | ||
84 | if (!(readl(USB_ULPI_VIEWPORT) & ULPI_RUN)) | ||
85 | break; | ||
86 | udelay(1); | ||
87 | cnt++; | ||
88 | } | ||
89 | |||
90 | if (cnt >= ULPI_IO_TIMEOUT_USEC) { | ||
91 | dev_err(otg->dev, "ulpi_write: timeout\n"); | ||
92 | return -ETIMEDOUT; | ||
93 | } | ||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | static struct otg_io_access_ops msm_otg_io_ops = { | ||
98 | .read = ulpi_read, | ||
99 | .write = ulpi_write, | ||
100 | }; | ||
101 | |||
102 | static void ulpi_init(struct msm_otg *motg) | ||
103 | { | ||
104 | struct msm_otg_platform_data *pdata = motg->pdata; | ||
105 | int *seq = pdata->phy_init_seq; | ||
106 | |||
107 | if (!seq) | ||
108 | return; | ||
109 | |||
110 | while (seq[0] >= 0) { | ||
111 | dev_vdbg(motg->otg.dev, "ulpi: write 0x%02x to 0x%02x\n", | ||
112 | seq[0], seq[1]); | ||
113 | ulpi_write(&motg->otg, seq[0], seq[1]); | ||
114 | seq += 2; | ||
115 | } | ||
116 | } | ||
117 | |||
118 | static int msm_otg_link_clk_reset(struct msm_otg *motg, bool assert) | ||
119 | { | ||
120 | int ret; | ||
121 | |||
122 | if (assert) { | ||
123 | ret = clk_reset(motg->clk, CLK_RESET_ASSERT); | ||
124 | if (ret) | ||
125 | dev_err(motg->otg.dev, "usb hs_clk assert failed\n"); | ||
126 | } else { | ||
127 | ret = clk_reset(motg->clk, CLK_RESET_DEASSERT); | ||
128 | if (ret) | ||
129 | dev_err(motg->otg.dev, "usb hs_clk deassert failed\n"); | ||
130 | } | ||
131 | return ret; | ||
132 | } | ||
133 | |||
134 | static int msm_otg_phy_clk_reset(struct msm_otg *motg) | ||
135 | { | ||
136 | int ret; | ||
137 | |||
138 | ret = clk_reset(motg->phy_reset_clk, CLK_RESET_ASSERT); | ||
139 | if (ret) { | ||
140 | dev_err(motg->otg.dev, "usb phy clk assert failed\n"); | ||
141 | return ret; | ||
142 | } | ||
143 | usleep_range(10000, 12000); | ||
144 | ret = clk_reset(motg->phy_reset_clk, CLK_RESET_DEASSERT); | ||
145 | if (ret) | ||
146 | dev_err(motg->otg.dev, "usb phy clk deassert failed\n"); | ||
147 | return ret; | ||
148 | } | ||
149 | |||
150 | static int msm_otg_phy_reset(struct msm_otg *motg) | ||
151 | { | ||
152 | u32 val; | ||
153 | int ret; | ||
154 | int retries; | ||
155 | |||
156 | ret = msm_otg_link_clk_reset(motg, 1); | ||
157 | if (ret) | ||
158 | return ret; | ||
159 | ret = msm_otg_phy_clk_reset(motg); | ||
160 | if (ret) | ||
161 | return ret; | ||
162 | ret = msm_otg_link_clk_reset(motg, 0); | ||
163 | if (ret) | ||
164 | return ret; | ||
165 | |||
166 | val = readl(USB_PORTSC) & ~PORTSC_PTS_MASK; | ||
167 | writel(val | PORTSC_PTS_ULPI, USB_PORTSC); | ||
168 | |||
169 | for (retries = 3; retries > 0; retries--) { | ||
170 | ret = ulpi_write(&motg->otg, ULPI_FUNC_CTRL_SUSPENDM, | ||
171 | ULPI_CLR(ULPI_FUNC_CTRL)); | ||
172 | if (!ret) | ||
173 | break; | ||
174 | ret = msm_otg_phy_clk_reset(motg); | ||
175 | if (ret) | ||
176 | return ret; | ||
177 | } | ||
178 | if (!retries) | ||
179 | return -ETIMEDOUT; | ||
180 | |||
181 | /* This reset calibrates the phy, if the above write succeeded */ | ||
182 | ret = msm_otg_phy_clk_reset(motg); | ||
183 | if (ret) | ||
184 | return ret; | ||
185 | |||
186 | for (retries = 3; retries > 0; retries--) { | ||
187 | ret = ulpi_read(&motg->otg, ULPI_DEBUG); | ||
188 | if (ret != -ETIMEDOUT) | ||
189 | break; | ||
190 | ret = msm_otg_phy_clk_reset(motg); | ||
191 | if (ret) | ||
192 | return ret; | ||
193 | } | ||
194 | if (!retries) | ||
195 | return -ETIMEDOUT; | ||
196 | |||
197 | dev_info(motg->otg.dev, "phy_reset: success\n"); | ||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | #define LINK_RESET_TIMEOUT_USEC (250 * 1000) | ||
202 | static int msm_otg_reset(struct otg_transceiver *otg) | ||
203 | { | ||
204 | struct msm_otg *motg = container_of(otg, struct msm_otg, otg); | ||
205 | struct msm_otg_platform_data *pdata = motg->pdata; | ||
206 | int cnt = 0; | ||
207 | int ret; | ||
208 | u32 val = 0; | ||
209 | u32 ulpi_val = 0; | ||
210 | |||
211 | ret = msm_otg_phy_reset(motg); | ||
212 | if (ret) { | ||
213 | dev_err(otg->dev, "phy_reset failed\n"); | ||
214 | return ret; | ||
215 | } | ||
216 | |||
217 | ulpi_init(motg); | ||
218 | |||
219 | writel(USBCMD_RESET, USB_USBCMD); | ||
220 | while (cnt < LINK_RESET_TIMEOUT_USEC) { | ||
221 | if (!(readl(USB_USBCMD) & USBCMD_RESET)) | ||
222 | break; | ||
223 | udelay(1); | ||
224 | cnt++; | ||
225 | } | ||
226 | if (cnt >= LINK_RESET_TIMEOUT_USEC) | ||
227 | return -ETIMEDOUT; | ||
228 | |||
229 | /* select ULPI phy */ | ||
230 | writel(0x80000000, USB_PORTSC); | ||
231 | |||
232 | msleep(100); | ||
233 | |||
234 | writel(0x0, USB_AHBBURST); | ||
235 | writel(0x00, USB_AHBMODE); | ||
236 | |||
237 | if (pdata->otg_control == OTG_PHY_CONTROL) { | ||
238 | val = readl(USB_OTGSC); | ||
239 | if (pdata->mode == USB_OTG) { | ||
240 | ulpi_val = ULPI_INT_IDGRD | ULPI_INT_SESS_VALID; | ||
241 | val |= OTGSC_IDIE | OTGSC_BSVIE; | ||
242 | } else if (pdata->mode == USB_PERIPHERAL) { | ||
243 | ulpi_val = ULPI_INT_SESS_VALID; | ||
244 | val |= OTGSC_BSVIE; | ||
245 | } | ||
246 | writel(val, USB_OTGSC); | ||
247 | ulpi_write(otg, ulpi_val, ULPI_USB_INT_EN_RISE); | ||
248 | ulpi_write(otg, ulpi_val, ULPI_USB_INT_EN_FALL); | ||
249 | } | ||
250 | |||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | static void msm_otg_start_host(struct otg_transceiver *otg, int on) | ||
255 | { | ||
256 | struct msm_otg *motg = container_of(otg, struct msm_otg, otg); | ||
257 | struct msm_otg_platform_data *pdata = motg->pdata; | ||
258 | struct usb_hcd *hcd; | ||
259 | |||
260 | if (!otg->host) | ||
261 | return; | ||
262 | |||
263 | hcd = bus_to_hcd(otg->host); | ||
264 | |||
265 | if (on) { | ||
266 | dev_dbg(otg->dev, "host on\n"); | ||
267 | |||
268 | if (pdata->vbus_power) | ||
269 | pdata->vbus_power(1); | ||
270 | /* | ||
271 | * Some boards have a switch cotrolled by gpio | ||
272 | * to enable/disable internal HUB. Enable internal | ||
273 | * HUB before kicking the host. | ||
274 | */ | ||
275 | if (pdata->setup_gpio) | ||
276 | pdata->setup_gpio(OTG_STATE_A_HOST); | ||
277 | #ifdef CONFIG_USB | ||
278 | usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); | ||
279 | #endif | ||
280 | } else { | ||
281 | dev_dbg(otg->dev, "host off\n"); | ||
282 | |||
283 | #ifdef CONFIG_USB | ||
284 | usb_remove_hcd(hcd); | ||
285 | #endif | ||
286 | if (pdata->setup_gpio) | ||
287 | pdata->setup_gpio(OTG_STATE_UNDEFINED); | ||
288 | if (pdata->vbus_power) | ||
289 | pdata->vbus_power(0); | ||
290 | } | ||
291 | } | ||
292 | |||
293 | static int msm_otg_set_host(struct otg_transceiver *otg, struct usb_bus *host) | ||
294 | { | ||
295 | struct msm_otg *motg = container_of(otg, struct msm_otg, otg); | ||
296 | struct usb_hcd *hcd; | ||
297 | |||
298 | /* | ||
299 | * Fail host registration if this board can support | ||
300 | * only peripheral configuration. | ||
301 | */ | ||
302 | if (motg->pdata->mode == USB_PERIPHERAL) { | ||
303 | dev_info(otg->dev, "Host mode is not supported\n"); | ||
304 | return -ENODEV; | ||
305 | } | ||
306 | |||
307 | if (!host) { | ||
308 | if (otg->state == OTG_STATE_A_HOST) { | ||
309 | msm_otg_start_host(otg, 0); | ||
310 | otg->host = NULL; | ||
311 | otg->state = OTG_STATE_UNDEFINED; | ||
312 | schedule_work(&motg->sm_work); | ||
313 | } else { | ||
314 | otg->host = NULL; | ||
315 | } | ||
316 | |||
317 | return 0; | ||
318 | } | ||
319 | |||
320 | hcd = bus_to_hcd(host); | ||
321 | hcd->power_budget = motg->pdata->power_budget; | ||
322 | |||
323 | otg->host = host; | ||
324 | dev_dbg(otg->dev, "host driver registered w/ tranceiver\n"); | ||
325 | |||
326 | /* | ||
327 | * Kick the state machine work, if peripheral is not supported | ||
328 | * or peripheral is already registered with us. | ||
329 | */ | ||
330 | if (motg->pdata->mode == USB_HOST || otg->gadget) | ||
331 | schedule_work(&motg->sm_work); | ||
332 | |||
333 | return 0; | ||
334 | } | ||
335 | |||
336 | static void msm_otg_start_peripheral(struct otg_transceiver *otg, int on) | ||
337 | { | ||
338 | struct msm_otg *motg = container_of(otg, struct msm_otg, otg); | ||
339 | struct msm_otg_platform_data *pdata = motg->pdata; | ||
340 | |||
341 | if (!otg->gadget) | ||
342 | return; | ||
343 | |||
344 | if (on) { | ||
345 | dev_dbg(otg->dev, "gadget on\n"); | ||
346 | /* | ||
347 | * Some boards have a switch cotrolled by gpio | ||
348 | * to enable/disable internal HUB. Disable internal | ||
349 | * HUB before kicking the gadget. | ||
350 | */ | ||
351 | if (pdata->setup_gpio) | ||
352 | pdata->setup_gpio(OTG_STATE_B_PERIPHERAL); | ||
353 | usb_gadget_vbus_connect(otg->gadget); | ||
354 | } else { | ||
355 | dev_dbg(otg->dev, "gadget off\n"); | ||
356 | usb_gadget_vbus_disconnect(otg->gadget); | ||
357 | if (pdata->setup_gpio) | ||
358 | pdata->setup_gpio(OTG_STATE_UNDEFINED); | ||
359 | } | ||
360 | |||
361 | } | ||
362 | |||
363 | static int msm_otg_set_peripheral(struct otg_transceiver *otg, | ||
364 | struct usb_gadget *gadget) | ||
365 | { | ||
366 | struct msm_otg *motg = container_of(otg, struct msm_otg, otg); | ||
367 | |||
368 | /* | ||
369 | * Fail peripheral registration if this board can support | ||
370 | * only host configuration. | ||
371 | */ | ||
372 | if (motg->pdata->mode == USB_HOST) { | ||
373 | dev_info(otg->dev, "Peripheral mode is not supported\n"); | ||
374 | return -ENODEV; | ||
375 | } | ||
376 | |||
377 | if (!gadget) { | ||
378 | if (otg->state == OTG_STATE_B_PERIPHERAL) { | ||
379 | msm_otg_start_peripheral(otg, 0); | ||
380 | otg->gadget = NULL; | ||
381 | otg->state = OTG_STATE_UNDEFINED; | ||
382 | schedule_work(&motg->sm_work); | ||
383 | } else { | ||
384 | otg->gadget = NULL; | ||
385 | } | ||
386 | |||
387 | return 0; | ||
388 | } | ||
389 | otg->gadget = gadget; | ||
390 | dev_dbg(otg->dev, "peripheral driver registered w/ tranceiver\n"); | ||
391 | |||
392 | /* | ||
393 | * Kick the state machine work, if host is not supported | ||
394 | * or host is already registered with us. | ||
395 | */ | ||
396 | if (motg->pdata->mode == USB_PERIPHERAL || otg->host) | ||
397 | schedule_work(&motg->sm_work); | ||
398 | |||
399 | return 0; | ||
400 | } | ||
401 | |||
402 | /* | ||
403 | * We support OTG, Peripheral only and Host only configurations. In case | ||
404 | * of OTG, mode switch (host-->peripheral/peripheral-->host) can happen | ||
405 | * via Id pin status or user request (debugfs). Id/BSV interrupts are not | ||
406 | * enabled when switch is controlled by user and default mode is supplied | ||
407 | * by board file, which can be changed by userspace later. | ||
408 | */ | ||
409 | static void msm_otg_init_sm(struct msm_otg *motg) | ||
410 | { | ||
411 | struct msm_otg_platform_data *pdata = motg->pdata; | ||
412 | u32 otgsc = readl(USB_OTGSC); | ||
413 | |||
414 | switch (pdata->mode) { | ||
415 | case USB_OTG: | ||
416 | if (pdata->otg_control == OTG_PHY_CONTROL) { | ||
417 | if (otgsc & OTGSC_ID) | ||
418 | set_bit(ID, &motg->inputs); | ||
419 | else | ||
420 | clear_bit(ID, &motg->inputs); | ||
421 | |||
422 | if (otgsc & OTGSC_BSV) | ||
423 | set_bit(B_SESS_VLD, &motg->inputs); | ||
424 | else | ||
425 | clear_bit(B_SESS_VLD, &motg->inputs); | ||
426 | } else if (pdata->otg_control == OTG_USER_CONTROL) { | ||
427 | if (pdata->default_mode == USB_HOST) { | ||
428 | clear_bit(ID, &motg->inputs); | ||
429 | } else if (pdata->default_mode == USB_PERIPHERAL) { | ||
430 | set_bit(ID, &motg->inputs); | ||
431 | set_bit(B_SESS_VLD, &motg->inputs); | ||
432 | } else { | ||
433 | set_bit(ID, &motg->inputs); | ||
434 | clear_bit(B_SESS_VLD, &motg->inputs); | ||
435 | } | ||
436 | } | ||
437 | break; | ||
438 | case USB_HOST: | ||
439 | clear_bit(ID, &motg->inputs); | ||
440 | break; | ||
441 | case USB_PERIPHERAL: | ||
442 | set_bit(ID, &motg->inputs); | ||
443 | if (otgsc & OTGSC_BSV) | ||
444 | set_bit(B_SESS_VLD, &motg->inputs); | ||
445 | else | ||
446 | clear_bit(B_SESS_VLD, &motg->inputs); | ||
447 | break; | ||
448 | default: | ||
449 | break; | ||
450 | } | ||
451 | } | ||
452 | |||
453 | static void msm_otg_sm_work(struct work_struct *w) | ||
454 | { | ||
455 | struct msm_otg *motg = container_of(w, struct msm_otg, sm_work); | ||
456 | struct otg_transceiver *otg = &motg->otg; | ||
457 | |||
458 | switch (otg->state) { | ||
459 | case OTG_STATE_UNDEFINED: | ||
460 | dev_dbg(otg->dev, "OTG_STATE_UNDEFINED state\n"); | ||
461 | msm_otg_reset(otg); | ||
462 | msm_otg_init_sm(motg); | ||
463 | otg->state = OTG_STATE_B_IDLE; | ||
464 | /* FALL THROUGH */ | ||
465 | case OTG_STATE_B_IDLE: | ||
466 | dev_dbg(otg->dev, "OTG_STATE_B_IDLE state\n"); | ||
467 | if (!test_bit(ID, &motg->inputs) && otg->host) { | ||
468 | /* disable BSV bit */ | ||
469 | writel(readl(USB_OTGSC) & ~OTGSC_BSVIE, USB_OTGSC); | ||
470 | msm_otg_start_host(otg, 1); | ||
471 | otg->state = OTG_STATE_A_HOST; | ||
472 | } else if (test_bit(B_SESS_VLD, &motg->inputs) && otg->gadget) { | ||
473 | msm_otg_start_peripheral(otg, 1); | ||
474 | otg->state = OTG_STATE_B_PERIPHERAL; | ||
475 | } | ||
476 | break; | ||
477 | case OTG_STATE_B_PERIPHERAL: | ||
478 | dev_dbg(otg->dev, "OTG_STATE_B_PERIPHERAL state\n"); | ||
479 | if (!test_bit(B_SESS_VLD, &motg->inputs) || | ||
480 | !test_bit(ID, &motg->inputs)) { | ||
481 | msm_otg_start_peripheral(otg, 0); | ||
482 | otg->state = OTG_STATE_B_IDLE; | ||
483 | msm_otg_reset(otg); | ||
484 | schedule_work(w); | ||
485 | } | ||
486 | break; | ||
487 | case OTG_STATE_A_HOST: | ||
488 | dev_dbg(otg->dev, "OTG_STATE_A_HOST state\n"); | ||
489 | if (test_bit(ID, &motg->inputs)) { | ||
490 | msm_otg_start_host(otg, 0); | ||
491 | otg->state = OTG_STATE_B_IDLE; | ||
492 | msm_otg_reset(otg); | ||
493 | schedule_work(w); | ||
494 | } | ||
495 | break; | ||
496 | default: | ||
497 | break; | ||
498 | } | ||
499 | } | ||
500 | |||
501 | static irqreturn_t msm_otg_irq(int irq, void *data) | ||
502 | { | ||
503 | struct msm_otg *motg = data; | ||
504 | struct otg_transceiver *otg = &motg->otg; | ||
505 | u32 otgsc = 0; | ||
506 | |||
507 | otgsc = readl(USB_OTGSC); | ||
508 | if (!(otgsc & (OTGSC_IDIS | OTGSC_BSVIS))) | ||
509 | return IRQ_NONE; | ||
510 | |||
511 | if ((otgsc & OTGSC_IDIS) && (otgsc & OTGSC_IDIE)) { | ||
512 | if (otgsc & OTGSC_ID) | ||
513 | set_bit(ID, &motg->inputs); | ||
514 | else | ||
515 | clear_bit(ID, &motg->inputs); | ||
516 | dev_dbg(otg->dev, "ID set/clear\n"); | ||
517 | } else if ((otgsc & OTGSC_BSVIS) && (otgsc & OTGSC_BSVIE)) { | ||
518 | if (otgsc & OTGSC_BSV) | ||
519 | set_bit(B_SESS_VLD, &motg->inputs); | ||
520 | else | ||
521 | clear_bit(B_SESS_VLD, &motg->inputs); | ||
522 | dev_dbg(otg->dev, "BSV set/clear\n"); | ||
523 | } | ||
524 | |||
525 | writel(otgsc, USB_OTGSC); | ||
526 | schedule_work(&motg->sm_work); | ||
527 | return IRQ_HANDLED; | ||
528 | } | ||
529 | |||
530 | static int msm_otg_mode_show(struct seq_file *s, void *unused) | ||
531 | { | ||
532 | struct msm_otg *motg = s->private; | ||
533 | struct otg_transceiver *otg = &motg->otg; | ||
534 | |||
535 | switch (otg->state) { | ||
536 | case OTG_STATE_A_HOST: | ||
537 | seq_printf(s, "host\n"); | ||
538 | break; | ||
539 | case OTG_STATE_B_PERIPHERAL: | ||
540 | seq_printf(s, "peripheral\n"); | ||
541 | break; | ||
542 | default: | ||
543 | seq_printf(s, "none\n"); | ||
544 | break; | ||
545 | } | ||
546 | |||
547 | return 0; | ||
548 | } | ||
549 | |||
550 | static int msm_otg_mode_open(struct inode *inode, struct file *file) | ||
551 | { | ||
552 | return single_open(file, msm_otg_mode_show, inode->i_private); | ||
553 | } | ||
554 | |||
555 | static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf, | ||
556 | size_t count, loff_t *ppos) | ||
557 | { | ||
558 | struct msm_otg *motg = file->private_data; | ||
559 | char buf[16]; | ||
560 | struct otg_transceiver *otg = &motg->otg; | ||
561 | int status = count; | ||
562 | enum usb_mode_type req_mode; | ||
563 | |||
564 | memset(buf, 0x00, sizeof(buf)); | ||
565 | |||
566 | if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) { | ||
567 | status = -EFAULT; | ||
568 | goto out; | ||
569 | } | ||
570 | |||
571 | if (!strncmp(buf, "host", 4)) { | ||
572 | req_mode = USB_HOST; | ||
573 | } else if (!strncmp(buf, "peripheral", 10)) { | ||
574 | req_mode = USB_PERIPHERAL; | ||
575 | } else if (!strncmp(buf, "none", 4)) { | ||
576 | req_mode = USB_NONE; | ||
577 | } else { | ||
578 | status = -EINVAL; | ||
579 | goto out; | ||
580 | } | ||
581 | |||
582 | switch (req_mode) { | ||
583 | case USB_NONE: | ||
584 | switch (otg->state) { | ||
585 | case OTG_STATE_A_HOST: | ||
586 | case OTG_STATE_B_PERIPHERAL: | ||
587 | set_bit(ID, &motg->inputs); | ||
588 | clear_bit(B_SESS_VLD, &motg->inputs); | ||
589 | break; | ||
590 | default: | ||
591 | goto out; | ||
592 | } | ||
593 | break; | ||
594 | case USB_PERIPHERAL: | ||
595 | switch (otg->state) { | ||
596 | case OTG_STATE_B_IDLE: | ||
597 | case OTG_STATE_A_HOST: | ||
598 | set_bit(ID, &motg->inputs); | ||
599 | set_bit(B_SESS_VLD, &motg->inputs); | ||
600 | break; | ||
601 | default: | ||
602 | goto out; | ||
603 | } | ||
604 | break; | ||
605 | case USB_HOST: | ||
606 | switch (otg->state) { | ||
607 | case OTG_STATE_B_IDLE: | ||
608 | case OTG_STATE_B_PERIPHERAL: | ||
609 | clear_bit(ID, &motg->inputs); | ||
610 | break; | ||
611 | default: | ||
612 | goto out; | ||
613 | } | ||
614 | break; | ||
615 | default: | ||
616 | goto out; | ||
617 | } | ||
618 | |||
619 | schedule_work(&motg->sm_work); | ||
620 | out: | ||
621 | return status; | ||
622 | } | ||
623 | |||
624 | const struct file_operations msm_otg_mode_fops = { | ||
625 | .open = msm_otg_mode_open, | ||
626 | .read = seq_read, | ||
627 | .write = msm_otg_mode_write, | ||
628 | .llseek = seq_lseek, | ||
629 | .release = single_release, | ||
630 | }; | ||
631 | |||
632 | static struct dentry *msm_otg_dbg_root; | ||
633 | static struct dentry *msm_otg_dbg_mode; | ||
634 | |||
635 | static int msm_otg_debugfs_init(struct msm_otg *motg) | ||
636 | { | ||
637 | msm_otg_dbg_root = debugfs_create_dir("msm_otg", NULL); | ||
638 | |||
639 | if (!msm_otg_dbg_root || IS_ERR(msm_otg_dbg_root)) | ||
640 | return -ENODEV; | ||
641 | |||
642 | msm_otg_dbg_mode = debugfs_create_file("mode", S_IRUGO | S_IWUSR, | ||
643 | msm_otg_dbg_root, motg, &msm_otg_mode_fops); | ||
644 | if (!msm_otg_dbg_mode) { | ||
645 | debugfs_remove(msm_otg_dbg_root); | ||
646 | msm_otg_dbg_root = NULL; | ||
647 | return -ENODEV; | ||
648 | } | ||
649 | |||
650 | return 0; | ||
651 | } | ||
652 | |||
653 | static void msm_otg_debugfs_cleanup(void) | ||
654 | { | ||
655 | debugfs_remove(msm_otg_dbg_mode); | ||
656 | debugfs_remove(msm_otg_dbg_root); | ||
657 | } | ||
658 | |||
659 | static int __init msm_otg_probe(struct platform_device *pdev) | ||
660 | { | ||
661 | int ret = 0; | ||
662 | struct resource *res; | ||
663 | struct msm_otg *motg; | ||
664 | struct otg_transceiver *otg; | ||
665 | |||
666 | dev_info(&pdev->dev, "msm_otg probe\n"); | ||
667 | if (!pdev->dev.platform_data) { | ||
668 | dev_err(&pdev->dev, "No platform data given. Bailing out\n"); | ||
669 | return -ENODEV; | ||
670 | } | ||
671 | |||
672 | motg = kzalloc(sizeof(struct msm_otg), GFP_KERNEL); | ||
673 | if (!motg) { | ||
674 | dev_err(&pdev->dev, "unable to allocate msm_otg\n"); | ||
675 | return -ENOMEM; | ||
676 | } | ||
677 | |||
678 | motg->pdata = pdev->dev.platform_data; | ||
679 | otg = &motg->otg; | ||
680 | otg->dev = &pdev->dev; | ||
681 | |||
682 | motg->phy_reset_clk = clk_get(&pdev->dev, "usb_phy_clk"); | ||
683 | if (IS_ERR(motg->phy_reset_clk)) { | ||
684 | dev_err(&pdev->dev, "failed to get usb_phy_clk\n"); | ||
685 | ret = PTR_ERR(motg->phy_reset_clk); | ||
686 | goto free_motg; | ||
687 | } | ||
688 | |||
689 | motg->clk = clk_get(&pdev->dev, "usb_hs_clk"); | ||
690 | if (IS_ERR(motg->clk)) { | ||
691 | dev_err(&pdev->dev, "failed to get usb_hs_clk\n"); | ||
692 | ret = PTR_ERR(motg->clk); | ||
693 | goto put_phy_reset_clk; | ||
694 | } | ||
695 | |||
696 | motg->pclk = clk_get(&pdev->dev, "usb_hs_pclk"); | ||
697 | if (IS_ERR(motg->pclk)) { | ||
698 | dev_err(&pdev->dev, "failed to get usb_hs_pclk\n"); | ||
699 | ret = PTR_ERR(motg->pclk); | ||
700 | goto put_clk; | ||
701 | } | ||
702 | |||
703 | /* | ||
704 | * USB core clock is not present on all MSM chips. This | ||
705 | * clock is introduced to remove the dependency on AXI | ||
706 | * bus frequency. | ||
707 | */ | ||
708 | motg->core_clk = clk_get(&pdev->dev, "usb_hs_core_clk"); | ||
709 | if (IS_ERR(motg->core_clk)) | ||
710 | motg->core_clk = NULL; | ||
711 | |||
712 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
713 | if (!res) { | ||
714 | dev_err(&pdev->dev, "failed to get platform resource mem\n"); | ||
715 | ret = -ENODEV; | ||
716 | goto put_core_clk; | ||
717 | } | ||
718 | |||
719 | motg->regs = ioremap(res->start, resource_size(res)); | ||
720 | if (!motg->regs) { | ||
721 | dev_err(&pdev->dev, "ioremap failed\n"); | ||
722 | ret = -ENOMEM; | ||
723 | goto put_core_clk; | ||
724 | } | ||
725 | dev_info(&pdev->dev, "OTG regs = %p\n", motg->regs); | ||
726 | |||
727 | motg->irq = platform_get_irq(pdev, 0); | ||
728 | if (!motg->irq) { | ||
729 | dev_err(&pdev->dev, "platform_get_irq failed\n"); | ||
730 | ret = -ENODEV; | ||
731 | goto free_regs; | ||
732 | } | ||
733 | |||
734 | clk_enable(motg->clk); | ||
735 | clk_enable(motg->pclk); | ||
736 | if (motg->core_clk) | ||
737 | clk_enable(motg->core_clk); | ||
738 | |||
739 | writel(0, USB_USBINTR); | ||
740 | writel(0, USB_OTGSC); | ||
741 | |||
742 | INIT_WORK(&motg->sm_work, msm_otg_sm_work); | ||
743 | ret = request_irq(motg->irq, msm_otg_irq, IRQF_SHARED, | ||
744 | "msm_otg", motg); | ||
745 | if (ret) { | ||
746 | dev_err(&pdev->dev, "request irq failed\n"); | ||
747 | goto disable_clks; | ||
748 | } | ||
749 | |||
750 | otg->init = msm_otg_reset; | ||
751 | otg->set_host = msm_otg_set_host; | ||
752 | otg->set_peripheral = msm_otg_set_peripheral; | ||
753 | |||
754 | otg->io_ops = &msm_otg_io_ops; | ||
755 | |||
756 | ret = otg_set_transceiver(&motg->otg); | ||
757 | if (ret) { | ||
758 | dev_err(&pdev->dev, "otg_set_transceiver failed\n"); | ||
759 | goto free_irq; | ||
760 | } | ||
761 | |||
762 | platform_set_drvdata(pdev, motg); | ||
763 | device_init_wakeup(&pdev->dev, 1); | ||
764 | |||
765 | if (motg->pdata->mode == USB_OTG && | ||
766 | motg->pdata->otg_control == OTG_USER_CONTROL) { | ||
767 | ret = msm_otg_debugfs_init(motg); | ||
768 | if (ret) | ||
769 | dev_dbg(&pdev->dev, "mode debugfs file is" | ||
770 | "not available\n"); | ||
771 | } | ||
772 | |||
773 | return 0; | ||
774 | |||
775 | free_irq: | ||
776 | free_irq(motg->irq, motg); | ||
777 | disable_clks: | ||
778 | clk_disable(motg->pclk); | ||
779 | clk_disable(motg->clk); | ||
780 | free_regs: | ||
781 | iounmap(motg->regs); | ||
782 | put_core_clk: | ||
783 | if (motg->core_clk) | ||
784 | clk_put(motg->core_clk); | ||
785 | clk_put(motg->pclk); | ||
786 | put_clk: | ||
787 | clk_put(motg->clk); | ||
788 | put_phy_reset_clk: | ||
789 | clk_put(motg->phy_reset_clk); | ||
790 | free_motg: | ||
791 | kfree(motg); | ||
792 | return ret; | ||
793 | } | ||
794 | |||
795 | static int __devexit msm_otg_remove(struct platform_device *pdev) | ||
796 | { | ||
797 | struct msm_otg *motg = platform_get_drvdata(pdev); | ||
798 | struct otg_transceiver *otg = &motg->otg; | ||
799 | |||
800 | if (otg->host || otg->gadget) | ||
801 | return -EBUSY; | ||
802 | |||
803 | msm_otg_debugfs_cleanup(); | ||
804 | cancel_work_sync(&motg->sm_work); | ||
805 | device_init_wakeup(&pdev->dev, 0); | ||
806 | otg_set_transceiver(NULL); | ||
807 | |||
808 | free_irq(motg->irq, motg); | ||
809 | |||
810 | clk_disable(motg->pclk); | ||
811 | clk_disable(motg->clk); | ||
812 | if (motg->core_clk) | ||
813 | clk_disable(motg->core_clk); | ||
814 | |||
815 | iounmap(motg->regs); | ||
816 | |||
817 | clk_put(motg->phy_reset_clk); | ||
818 | clk_put(motg->pclk); | ||
819 | clk_put(motg->clk); | ||
820 | if (motg->core_clk) | ||
821 | clk_put(motg->core_clk); | ||
822 | |||
823 | kfree(motg); | ||
824 | |||
825 | return 0; | ||
826 | } | ||
827 | |||
828 | static struct platform_driver msm_otg_driver = { | ||
829 | .remove = __devexit_p(msm_otg_remove), | ||
830 | .driver = { | ||
831 | .name = DRIVER_NAME, | ||
832 | .owner = THIS_MODULE, | ||
833 | }, | ||
834 | }; | ||
835 | |||
836 | static int __init msm_otg_init(void) | ||
837 | { | ||
838 | return platform_driver_probe(&msm_otg_driver, msm_otg_probe); | ||
839 | } | ||
840 | |||
841 | static void __exit msm_otg_exit(void) | ||
842 | { | ||
843 | platform_driver_unregister(&msm_otg_driver); | ||
844 | } | ||
845 | |||
846 | module_init(msm_otg_init); | ||
847 | module_exit(msm_otg_exit); | ||
848 | |||
849 | MODULE_LICENSE("GPL v2"); | ||
850 | MODULE_DESCRIPTION("MSM USB transceiver driver"); | ||