]>
Commit | Line | Data |
---|---|---|
ccd93fb3 | 1 | |
2 | /* ========================================================================== | |
3 | * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_linux.c $ | |
4 | * $Revision: #20 $ | |
5 | * $Date: 2011/10/26 $ | |
6 | * $Change: 1872981 $ | |
7 | * | |
8 | * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, | |
9 | * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless | |
10 | * otherwise expressly agreed to in writing between Synopsys and you. | |
11 | * | |
12 | * The Software IS NOT an item of Licensed Software or Licensed Product under | |
13 | * any End User Software License Agreement or Agreement for Licensed Product | |
14 | * with Synopsys or any supplement thereto. You are permitted to use and | |
15 | * redistribute this Software in source and binary forms, with or without | |
16 | * modification, provided that redistributions of source code must retain this | |
17 | * notice. You may not view, use, disclose, copy or distribute this file or | |
18 | * any information contained herein except pursuant to this license grant from | |
19 | * Synopsys. If you do not agree with this notice, including the disclaimer | |
20 | * below, then you are not authorized to use the Software. | |
21 | * | |
22 | * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS | |
23 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
25 | * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, | |
26 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
27 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
28 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
29 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | |
32 | * DAMAGE. | |
33 | * ========================================================================== */ | |
34 | #ifndef DWC_DEVICE_ONLY | |
35 | ||
36 | /** | |
37 | * @file | |
38 | * | |
39 | * This file contains the implementation of the HCD. In Linux, the HCD | |
40 | * implements the hc_driver API. | |
41 | */ | |
42 | #include <linux/kernel.h> | |
43 | #include <linux/module.h> | |
44 | #include <linux/moduleparam.h> | |
45 | #include <linux/init.h> | |
46 | #include <linux/device.h> | |
47 | #include <linux/errno.h> | |
48 | #include <linux/list.h> | |
49 | #include <linux/interrupt.h> | |
50 | #include <linux/string.h> | |
51 | #include <linux/dma-mapping.h> | |
52 | #include <linux/version.h> | |
53 | #include <asm/io.h> | |
a1465ff0 | 54 | #ifdef CONFIG_ARM |
ccd93fb3 | 55 | #include <asm/fiq.h> |
a1465ff0 | 56 | #endif |
ccd93fb3 | 57 | #include <linux/usb.h> |
58 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) | |
59 | #include <../drivers/usb/core/hcd.h> | |
60 | #else | |
61 | #include <linux/usb/hcd.h> | |
62 | #endif | |
63 | #include <asm/bug.h> | |
64 | ||
65 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)) | |
66 | #define USB_URB_EP_LINKING 1 | |
67 | #else | |
68 | #define USB_URB_EP_LINKING 0 | |
69 | #endif | |
70 | ||
71 | #include "dwc_otg_hcd_if.h" | |
72 | #include "dwc_otg_dbg.h" | |
73 | #include "dwc_otg_driver.h" | |
74 | #include "dwc_otg_hcd.h" | |
75 | ||
a1465ff0 MZ |
76 | #ifndef __virt_to_bus |
77 | #define __virt_to_bus __virt_to_phys | |
78 | #define __bus_to_virt __phys_to_virt | |
79 | #define __pfn_to_bus(x) __pfn_to_phys(x) | |
80 | #define __bus_to_pfn(x) __phys_to_pfn(x) | |
81 | #endif | |
82 | ||
ccd93fb3 | 83 | extern unsigned char _dwc_otg_fiq_stub, _dwc_otg_fiq_stub_end; |
84 | ||
85 | /** | |
86 | * Gets the endpoint number from a _bEndpointAddress argument. The endpoint is | |
87 | * qualified with its direction (possible 32 endpoints per device). | |
88 | */ | |
89 | #define dwc_ep_addr_to_endpoint(_bEndpointAddress_) ((_bEndpointAddress_ & USB_ENDPOINT_NUMBER_MASK) | \ | |
90 | ((_bEndpointAddress_ & USB_DIR_IN) != 0) << 4) | |
91 | ||
92 | static const char dwc_otg_hcd_name[] = "dwc_otg_hcd"; | |
93 | ||
94 | extern bool fiq_enable; | |
95 | ||
96 | /** @name Linux HC Driver API Functions */ | |
97 | /** @{ */ | |
98 | /* manage i/o requests, device state */ | |
99 | static int dwc_otg_urb_enqueue(struct usb_hcd *hcd, | |
100 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) | |
101 | struct usb_host_endpoint *ep, | |
102 | #endif | |
103 | struct urb *urb, gfp_t mem_flags); | |
104 | ||
105 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30) | |
106 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) | |
107 | static int dwc_otg_urb_dequeue(struct usb_hcd *hcd, struct urb *urb); | |
108 | #endif | |
109 | #else /* kernels at or post 2.6.30 */ | |
110 | static int dwc_otg_urb_dequeue(struct usb_hcd *hcd, | |
111 | struct urb *urb, int status); | |
112 | #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30) */ | |
113 | ||
114 | static void endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep); | |
115 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30) | |
116 | static void endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep); | |
117 | #endif | |
118 | static irqreturn_t dwc_otg_hcd_irq(struct usb_hcd *hcd); | |
119 | extern int hcd_start(struct usb_hcd *hcd); | |
120 | extern void hcd_stop(struct usb_hcd *hcd); | |
121 | static int get_frame_number(struct usb_hcd *hcd); | |
122 | extern int hub_status_data(struct usb_hcd *hcd, char *buf); | |
123 | extern int hub_control(struct usb_hcd *hcd, | |
124 | u16 typeReq, | |
125 | u16 wValue, u16 wIndex, char *buf, u16 wLength); | |
126 | ||
127 | struct wrapper_priv_data { | |
128 | dwc_otg_hcd_t *dwc_otg_hcd; | |
129 | }; | |
130 | ||
131 | /** @} */ | |
132 | ||
133 | static struct hc_driver dwc_otg_hc_driver = { | |
134 | ||
135 | .description = dwc_otg_hcd_name, | |
136 | .product_desc = "DWC OTG Controller", | |
137 | .hcd_priv_size = sizeof(struct wrapper_priv_data), | |
138 | ||
139 | .irq = dwc_otg_hcd_irq, | |
140 | ||
141 | .flags = HCD_MEMORY | HCD_USB2, | |
142 | ||
143 | //.reset = | |
144 | .start = hcd_start, | |
145 | //.suspend = | |
146 | //.resume = | |
147 | .stop = hcd_stop, | |
148 | ||
149 | .urb_enqueue = dwc_otg_urb_enqueue, | |
150 | .urb_dequeue = dwc_otg_urb_dequeue, | |
151 | .endpoint_disable = endpoint_disable, | |
152 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30) | |
153 | .endpoint_reset = endpoint_reset, | |
154 | #endif | |
155 | .get_frame_number = get_frame_number, | |
156 | ||
157 | .hub_status_data = hub_status_data, | |
158 | .hub_control = hub_control, | |
159 | //.bus_suspend = | |
160 | //.bus_resume = | |
161 | }; | |
162 | ||
163 | /** Gets the dwc_otg_hcd from a struct usb_hcd */ | |
164 | static inline dwc_otg_hcd_t *hcd_to_dwc_otg_hcd(struct usb_hcd *hcd) | |
165 | { | |
166 | struct wrapper_priv_data *p; | |
167 | p = (struct wrapper_priv_data *)(hcd->hcd_priv); | |
168 | return p->dwc_otg_hcd; | |
169 | } | |
170 | ||
171 | /** Gets the struct usb_hcd that contains a dwc_otg_hcd_t. */ | |
172 | static inline struct usb_hcd *dwc_otg_hcd_to_hcd(dwc_otg_hcd_t * dwc_otg_hcd) | |
173 | { | |
174 | return dwc_otg_hcd_get_priv_data(dwc_otg_hcd); | |
175 | } | |
176 | ||
177 | /** Gets the usb_host_endpoint associated with an URB. */ | |
178 | inline struct usb_host_endpoint *dwc_urb_to_endpoint(struct urb *urb) | |
179 | { | |
180 | struct usb_device *dev = urb->dev; | |
181 | int ep_num = usb_pipeendpoint(urb->pipe); | |
182 | ||
183 | if (usb_pipein(urb->pipe)) | |
184 | return dev->ep_in[ep_num]; | |
185 | else | |
186 | return dev->ep_out[ep_num]; | |
187 | } | |
188 | ||
189 | static int _disconnect(dwc_otg_hcd_t * hcd) | |
190 | { | |
191 | struct usb_hcd *usb_hcd = dwc_otg_hcd_to_hcd(hcd); | |
192 | ||
193 | usb_hcd->self.is_b_host = 0; | |
194 | return 0; | |
195 | } | |
196 | ||
197 | static int _start(dwc_otg_hcd_t * hcd) | |
198 | { | |
199 | struct usb_hcd *usb_hcd = dwc_otg_hcd_to_hcd(hcd); | |
200 | ||
201 | usb_hcd->self.is_b_host = dwc_otg_hcd_is_b_host(hcd); | |
202 | hcd_start(usb_hcd); | |
203 | ||
204 | return 0; | |
205 | } | |
206 | ||
207 | static int _hub_info(dwc_otg_hcd_t * hcd, void *urb_handle, uint32_t * hub_addr, | |
208 | uint32_t * port_addr) | |
209 | { | |
210 | struct urb *urb = (struct urb *)urb_handle; | |
211 | struct usb_bus *bus; | |
212 | #if 1 //GRAYG - temporary | |
213 | if (NULL == urb_handle) | |
214 | DWC_ERROR("**** %s - NULL URB handle\n", __func__);//GRAYG | |
215 | if (NULL == urb->dev) | |
216 | DWC_ERROR("**** %s - URB has no device\n", __func__);//GRAYG | |
217 | if (NULL == port_addr) | |
218 | DWC_ERROR("**** %s - NULL port_address\n", __func__);//GRAYG | |
219 | #endif | |
220 | if (urb->dev->tt) { | |
221 | if (NULL == urb->dev->tt->hub) { | |
222 | DWC_ERROR("**** %s - (URB's transactor has no TT - giving no hub)\n", | |
223 | __func__); //GRAYG | |
224 | //*hub_addr = (u8)usb_pipedevice(urb->pipe); //GRAYG | |
225 | *hub_addr = 0; //GRAYG | |
226 | // we probably shouldn't have a transaction translator if | |
227 | // there's no associated hub? | |
228 | } else { | |
229 | bus = hcd_to_bus(dwc_otg_hcd_to_hcd(hcd)); | |
230 | if (urb->dev->tt->hub == bus->root_hub) | |
231 | *hub_addr = 0; | |
232 | else | |
233 | *hub_addr = urb->dev->tt->hub->devnum; | |
234 | } | |
235 | *port_addr = urb->dev->tt->multi ? urb->dev->ttport : 1; | |
236 | } else { | |
237 | *hub_addr = 0; | |
238 | *port_addr = urb->dev->ttport; | |
239 | } | |
240 | return 0; | |
241 | } | |
242 | ||
243 | static int _speed(dwc_otg_hcd_t * hcd, void *urb_handle) | |
244 | { | |
245 | struct urb *urb = (struct urb *)urb_handle; | |
246 | return urb->dev->speed; | |
247 | } | |
248 | ||
249 | static int _get_b_hnp_enable(dwc_otg_hcd_t * hcd) | |
250 | { | |
251 | struct usb_hcd *usb_hcd = dwc_otg_hcd_to_hcd(hcd); | |
252 | return usb_hcd->self.b_hnp_enable; | |
253 | } | |
254 | ||
255 | static void allocate_bus_bandwidth(struct usb_hcd *hcd, uint32_t bw, | |
256 | struct urb *urb) | |
257 | { | |
258 | hcd_to_bus(hcd)->bandwidth_allocated += bw / urb->interval; | |
259 | if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { | |
260 | hcd_to_bus(hcd)->bandwidth_isoc_reqs++; | |
261 | } else { | |
262 | hcd_to_bus(hcd)->bandwidth_int_reqs++; | |
263 | } | |
264 | } | |
265 | ||
266 | static void free_bus_bandwidth(struct usb_hcd *hcd, uint32_t bw, | |
267 | struct urb *urb) | |
268 | { | |
269 | hcd_to_bus(hcd)->bandwidth_allocated -= bw / urb->interval; | |
270 | if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { | |
271 | hcd_to_bus(hcd)->bandwidth_isoc_reqs--; | |
272 | } else { | |
273 | hcd_to_bus(hcd)->bandwidth_int_reqs--; | |
274 | } | |
275 | } | |
276 | ||
277 | /** | |
278 | * Sets the final status of an URB and returns it to the device driver. Any | |
279 | * required cleanup of the URB is performed. The HCD lock should be held on | |
280 | * entry. | |
281 | */ | |
282 | static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle, | |
283 | dwc_otg_hcd_urb_t * dwc_otg_urb, int32_t status) | |
284 | { | |
285 | struct urb *urb = (struct urb *)urb_handle; | |
286 | urb_tq_entry_t *new_entry; | |
287 | int rc = 0; | |
288 | if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { | |
289 | DWC_PRINTF("%s: urb %p, device %d, ep %d %s, status=%d\n", | |
290 | __func__, urb, usb_pipedevice(urb->pipe), | |
291 | usb_pipeendpoint(urb->pipe), | |
292 | usb_pipein(urb->pipe) ? "IN" : "OUT", status); | |
293 | if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { | |
294 | int i; | |
295 | for (i = 0; i < urb->number_of_packets; i++) { | |
296 | DWC_PRINTF(" ISO Desc %d status: %d\n", | |
297 | i, urb->iso_frame_desc[i].status); | |
298 | } | |
299 | } | |
300 | } | |
301 | new_entry = DWC_ALLOC_ATOMIC(sizeof(urb_tq_entry_t)); | |
302 | urb->actual_length = dwc_otg_hcd_urb_get_actual_length(dwc_otg_urb); | |
303 | /* Convert status value. */ | |
304 | switch (status) { | |
305 | case -DWC_E_PROTOCOL: | |
306 | status = -EPROTO; | |
307 | break; | |
308 | case -DWC_E_IN_PROGRESS: | |
309 | status = -EINPROGRESS; | |
310 | break; | |
311 | case -DWC_E_PIPE: | |
312 | status = -EPIPE; | |
313 | break; | |
314 | case -DWC_E_IO: | |
315 | status = -EIO; | |
316 | break; | |
317 | case -DWC_E_TIMEOUT: | |
318 | status = -ETIMEDOUT; | |
319 | break; | |
320 | case -DWC_E_OVERFLOW: | |
321 | status = -EOVERFLOW; | |
322 | break; | |
323 | case -DWC_E_SHUTDOWN: | |
324 | status = -ESHUTDOWN; | |
325 | break; | |
326 | default: | |
327 | if (status) { | |
328 | DWC_PRINTF("Uknown urb status %d\n", status); | |
329 | ||
330 | } | |
331 | } | |
332 | ||
333 | if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { | |
334 | int i; | |
335 | ||
336 | urb->error_count = dwc_otg_hcd_urb_get_error_count(dwc_otg_urb); | |
a44eea26 | 337 | urb->actual_length = 0; |
ccd93fb3 | 338 | for (i = 0; i < urb->number_of_packets; ++i) { |
339 | urb->iso_frame_desc[i].actual_length = | |
340 | dwc_otg_hcd_urb_get_iso_desc_actual_length | |
341 | (dwc_otg_urb, i); | |
a44eea26 | 342 | urb->actual_length += urb->iso_frame_desc[i].actual_length; |
ccd93fb3 | 343 | urb->iso_frame_desc[i].status = |
344 | dwc_otg_hcd_urb_get_iso_desc_status(dwc_otg_urb, i); | |
345 | } | |
346 | } | |
347 | ||
348 | urb->status = status; | |
349 | urb->hcpriv = NULL; | |
350 | if (!status) { | |
351 | if ((urb->transfer_flags & URB_SHORT_NOT_OK) && | |
352 | (urb->actual_length < urb->transfer_buffer_length)) { | |
353 | urb->status = -EREMOTEIO; | |
354 | } | |
355 | } | |
356 | ||
357 | if ((usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) || | |
358 | (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) { | |
359 | struct usb_host_endpoint *ep = dwc_urb_to_endpoint(urb); | |
360 | if (ep) { | |
361 | free_bus_bandwidth(dwc_otg_hcd_to_hcd(hcd), | |
362 | dwc_otg_hcd_get_ep_bandwidth(hcd, | |
363 | ep->hcpriv), | |
364 | urb); | |
365 | } | |
366 | } | |
367 | DWC_FREE(dwc_otg_urb); | |
368 | if (!new_entry) { | |
369 | DWC_ERROR("dwc_otg_hcd: complete: cannot allocate URB TQ entry\n"); | |
370 | urb->status = -EPROTO; | |
371 | /* don't schedule the tasklet - | |
372 | * directly return the packet here with error. */ | |
373 | #if USB_URB_EP_LINKING | |
374 | usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb); | |
375 | #endif | |
376 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) | |
377 | usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb); | |
378 | #else | |
379 | usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, urb->status); | |
380 | #endif | |
381 | } else { | |
382 | new_entry->urb = urb; | |
383 | #if USB_URB_EP_LINKING | |
384 | rc = usb_hcd_check_unlink_urb(dwc_otg_hcd_to_hcd(hcd), urb, urb->status); | |
385 | if(0 == rc) { | |
386 | usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb); | |
387 | } | |
388 | #endif | |
389 | if(0 == rc) { | |
390 | DWC_TAILQ_INSERT_TAIL(&hcd->completed_urb_list, new_entry, | |
391 | urb_tq_entries); | |
392 | DWC_TASK_HI_SCHEDULE(hcd->completion_tasklet); | |
393 | } | |
394 | } | |
395 | return 0; | |
396 | } | |
397 | ||
398 | static struct dwc_otg_hcd_function_ops hcd_fops = { | |
399 | .start = _start, | |
400 | .disconnect = _disconnect, | |
401 | .hub_info = _hub_info, | |
402 | .speed = _speed, | |
403 | .complete = _complete, | |
404 | .get_b_hnp_enable = _get_b_hnp_enable, | |
405 | }; | |
406 | ||
a1465ff0 MZ |
407 | #ifdef CONFIG_ARM64 |
408 | ||
409 | static int simfiq_irq = -1; | |
410 | ||
411 | void local_fiq_enable(void) | |
412 | { | |
413 | if (simfiq_irq >= 0) | |
414 | enable_irq(simfiq_irq); | |
415 | } | |
416 | ||
417 | void local_fiq_disable(void) | |
418 | { | |
419 | if (simfiq_irq >= 0) | |
420 | disable_irq(simfiq_irq); | |
421 | } | |
422 | ||
423 | irqreturn_t fiq_irq_handler(int irq, void *dev_id) | |
424 | { | |
425 | dwc_otg_hcd_t *dwc_otg_hcd = (dwc_otg_hcd_t *)dev_id; | |
426 | ||
427 | if (fiq_fsm_enable) | |
428 | dwc_otg_fiq_fsm(dwc_otg_hcd->fiq_state, dwc_otg_hcd->core_if->core_params->host_channels); | |
429 | else | |
430 | dwc_otg_fiq_nop(dwc_otg_hcd->fiq_state); | |
431 | ||
432 | return IRQ_HANDLED; | |
433 | } | |
434 | ||
435 | #else | |
ccd93fb3 | 436 | static struct fiq_handler fh = { |
437 | .name = "usb_fiq", | |
438 | }; | |
439 | ||
a1465ff0 MZ |
440 | #endif |
441 | ||
ccd93fb3 | 442 | static void hcd_init_fiq(void *cookie) |
443 | { | |
444 | dwc_otg_device_t *otg_dev = cookie; | |
445 | dwc_otg_hcd_t *dwc_otg_hcd = otg_dev->hcd; | |
a1465ff0 MZ |
446 | #ifdef CONFIG_ARM64 |
447 | int retval = 0; | |
448 | int irq; | |
449 | #else | |
ccd93fb3 | 450 | struct pt_regs regs; |
451 | int irq; | |
452 | ||
453 | if (claim_fiq(&fh)) { | |
454 | DWC_ERROR("Can't claim FIQ"); | |
455 | BUG(); | |
456 | } | |
457 | DWC_WARN("FIQ on core %d at 0x%08x", | |
458 | smp_processor_id(), | |
459 | (fiq_fsm_enable ? (int)&dwc_otg_fiq_fsm : (int)&dwc_otg_fiq_nop)); | |
460 | DWC_WARN("FIQ ASM at 0x%08x length %d", (int)&_dwc_otg_fiq_stub, (int)(&_dwc_otg_fiq_stub_end - &_dwc_otg_fiq_stub)); | |
461 | set_fiq_handler((void *) &_dwc_otg_fiq_stub, &_dwc_otg_fiq_stub_end - &_dwc_otg_fiq_stub); | |
462 | memset(®s,0,sizeof(regs)); | |
463 | ||
464 | regs.ARM_r8 = (long) dwc_otg_hcd->fiq_state; | |
465 | if (fiq_fsm_enable) { | |
466 | regs.ARM_r9 = dwc_otg_hcd->core_if->core_params->host_channels; | |
467 | //regs.ARM_r10 = dwc_otg_hcd->dma; | |
468 | regs.ARM_fp = (long) dwc_otg_fiq_fsm; | |
469 | } else { | |
470 | regs.ARM_fp = (long) dwc_otg_fiq_nop; | |
471 | } | |
472 | ||
473 | regs.ARM_sp = (long) dwc_otg_hcd->fiq_stack + (sizeof(struct fiq_stack) - 4); | |
474 | ||
475 | // __show_regs(®s); | |
476 | set_fiq_regs(®s); | |
a1465ff0 | 477 | #endif |
ccd93fb3 | 478 | |
479 | //Set the mphi periph to the required registers | |
480 | dwc_otg_hcd->fiq_state->mphi_regs.base = otg_dev->os_dep.mphi_base; | |
481 | dwc_otg_hcd->fiq_state->mphi_regs.ctrl = otg_dev->os_dep.mphi_base + 0x4c; | |
482 | dwc_otg_hcd->fiq_state->mphi_regs.outdda = otg_dev->os_dep.mphi_base + 0x28; | |
483 | dwc_otg_hcd->fiq_state->mphi_regs.outddb = otg_dev->os_dep.mphi_base + 0x2c; | |
484 | dwc_otg_hcd->fiq_state->mphi_regs.intstat = otg_dev->os_dep.mphi_base + 0x50; | |
485 | dwc_otg_hcd->fiq_state->dwc_regs_base = otg_dev->os_dep.base; | |
486 | DWC_WARN("MPHI regs_base at 0x%08x", (int)dwc_otg_hcd->fiq_state->mphi_regs.base); | |
487 | //Enable mphi peripheral | |
488 | writel((1<<31),dwc_otg_hcd->fiq_state->mphi_regs.ctrl); | |
489 | #ifdef DEBUG | |
490 | if (readl(dwc_otg_hcd->fiq_state->mphi_regs.ctrl) & 0x80000000) | |
491 | DWC_WARN("MPHI periph has been enabled"); | |
492 | else | |
493 | DWC_WARN("MPHI periph has NOT been enabled"); | |
494 | #endif | |
495 | // Enable FIQ interrupt from USB peripheral | |
a1465ff0 MZ |
496 | #ifdef CONFIG_ARM64 |
497 | irq = platform_get_irq(otg_dev->os_dep.platformdev, 1); | |
498 | ||
499 | if (irq < 0) { | |
500 | DWC_ERROR("Can't get SIM-FIQ irq"); | |
501 | return; | |
502 | } | |
503 | ||
504 | retval = request_irq(irq, fiq_irq_handler, 0, "dwc_otg_sim-fiq", dwc_otg_hcd); | |
505 | ||
506 | if (retval < 0) { | |
507 | DWC_ERROR("Unable to request SIM-FIQ irq\n"); | |
508 | return; | |
509 | } | |
510 | ||
511 | simfiq_irq = irq; | |
512 | #else | |
ccd93fb3 | 513 | #ifdef CONFIG_MULTI_IRQ_HANDLER |
514 | irq = platform_get_irq(otg_dev->os_dep.platformdev, 1); | |
515 | #else | |
516 | irq = INTERRUPT_VC_USB; | |
517 | #endif | |
518 | if (irq < 0) { | |
519 | DWC_ERROR("Can't get FIQ irq"); | |
520 | return; | |
521 | } | |
522 | enable_fiq(irq); | |
523 | local_fiq_enable(); | |
a1465ff0 MZ |
524 | #endif |
525 | ||
ccd93fb3 | 526 | } |
527 | ||
528 | /** | |
529 | * Initializes the HCD. This function allocates memory for and initializes the | |
530 | * static parts of the usb_hcd and dwc_otg_hcd structures. It also registers the | |
531 | * USB bus with the core and calls the hc_driver->start() function. It returns | |
532 | * a negative error on failure. | |
533 | */ | |
534 | int hcd_init(dwc_bus_dev_t *_dev) | |
535 | { | |
536 | struct usb_hcd *hcd = NULL; | |
537 | dwc_otg_hcd_t *dwc_otg_hcd = NULL; | |
538 | dwc_otg_device_t *otg_dev = DWC_OTG_BUSDRVDATA(_dev); | |
539 | int retval = 0; | |
540 | u64 dmamask; | |
541 | ||
542 | DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD INIT otg_dev=%p\n", otg_dev); | |
543 | ||
544 | /* Set device flags indicating whether the HCD supports DMA. */ | |
545 | if (dwc_otg_is_dma_enable(otg_dev->core_if)) | |
546 | dmamask = DMA_BIT_MASK(32); | |
547 | else | |
548 | dmamask = 0; | |
549 | ||
550 | #if defined(LM_INTERFACE) || defined(PLATFORM_INTERFACE) | |
551 | dma_set_mask(&_dev->dev, dmamask); | |
552 | dma_set_coherent_mask(&_dev->dev, dmamask); | |
553 | #elif defined(PCI_INTERFACE) | |
554 | pci_set_dma_mask(_dev, dmamask); | |
555 | pci_set_consistent_dma_mask(_dev, dmamask); | |
556 | #endif | |
557 | ||
558 | /* | |
559 | * Allocate memory for the base HCD plus the DWC OTG HCD. | |
560 | * Initialize the base HCD. | |
561 | */ | |
562 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30) | |
563 | hcd = usb_create_hcd(&dwc_otg_hc_driver, &_dev->dev, _dev->dev.bus_id); | |
564 | #else | |
565 | hcd = usb_create_hcd(&dwc_otg_hc_driver, &_dev->dev, dev_name(&_dev->dev)); | |
566 | hcd->has_tt = 1; | |
567 | // hcd->uses_new_polling = 1; | |
568 | // hcd->poll_rh = 0; | |
569 | #endif | |
570 | if (!hcd) { | |
571 | retval = -ENOMEM; | |
572 | goto error1; | |
573 | } | |
574 | ||
575 | hcd->regs = otg_dev->os_dep.base; | |
576 | ||
577 | ||
578 | /* Initialize the DWC OTG HCD. */ | |
579 | dwc_otg_hcd = dwc_otg_hcd_alloc_hcd(); | |
580 | if (!dwc_otg_hcd) { | |
581 | goto error2; | |
582 | } | |
583 | ((struct wrapper_priv_data *)(hcd->hcd_priv))->dwc_otg_hcd = | |
584 | dwc_otg_hcd; | |
585 | otg_dev->hcd = dwc_otg_hcd; | |
586 | otg_dev->hcd->otg_dev = otg_dev; | |
587 | ||
a1465ff0 MZ |
588 | #ifdef CONFIG_ARM64 |
589 | if (dwc_otg_hcd_init(dwc_otg_hcd, otg_dev->core_if)) | |
590 | goto error2; | |
591 | ||
592 | if (fiq_enable) | |
593 | hcd_init_fiq(otg_dev); | |
594 | #else | |
ccd93fb3 | 595 | if (dwc_otg_hcd_init(dwc_otg_hcd, otg_dev->core_if)) { |
596 | goto error2; | |
597 | } | |
598 | ||
599 | if (fiq_enable) { | |
600 | if (num_online_cpus() > 1) { | |
601 | /* bcm2709: can run the FIQ on a separate core to IRQs */ | |
602 | smp_call_function_single(1, hcd_init_fiq, otg_dev, 1); | |
603 | } else { | |
604 | smp_call_function_single(0, hcd_init_fiq, otg_dev, 1); | |
605 | } | |
606 | } | |
a1465ff0 | 607 | #endif |
ccd93fb3 | 608 | |
609 | hcd->self.otg_port = dwc_otg_hcd_otg_port(dwc_otg_hcd); | |
610 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33) //don't support for LM(with 2.6.20.1 kernel) | |
611 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) //version field absent later | |
612 | hcd->self.otg_version = dwc_otg_get_otg_version(otg_dev->core_if); | |
613 | #endif | |
614 | /* Don't support SG list at this point */ | |
615 | hcd->self.sg_tablesize = 0; | |
616 | #endif | |
617 | /* | |
618 | * Finish generic HCD initialization and start the HCD. This function | |
619 | * allocates the DMA buffer pool, registers the USB bus, requests the | |
620 | * IRQ line, and calls hcd_start method. | |
621 | */ | |
622 | #ifdef PLATFORM_INTERFACE | |
623 | retval = usb_add_hcd(hcd, platform_get_irq(_dev, fiq_enable ? 0 : 1), IRQF_SHARED); | |
624 | #else | |
625 | retval = usb_add_hcd(hcd, _dev->irq, IRQF_SHARED); | |
626 | #endif | |
627 | if (retval < 0) { | |
628 | goto error2; | |
629 | } | |
630 | ||
631 | dwc_otg_hcd_set_priv_data(dwc_otg_hcd, hcd); | |
632 | return 0; | |
633 | ||
634 | error2: | |
635 | usb_put_hcd(hcd); | |
636 | error1: | |
637 | return retval; | |
638 | } | |
639 | ||
640 | /** | |
641 | * Removes the HCD. | |
642 | * Frees memory and resources associated with the HCD and deregisters the bus. | |
643 | */ | |
644 | void hcd_remove(dwc_bus_dev_t *_dev) | |
645 | { | |
646 | dwc_otg_device_t *otg_dev = DWC_OTG_BUSDRVDATA(_dev); | |
647 | dwc_otg_hcd_t *dwc_otg_hcd; | |
648 | struct usb_hcd *hcd; | |
649 | ||
650 | DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD REMOVE otg_dev=%p\n", otg_dev); | |
651 | ||
652 | if (!otg_dev) { | |
653 | DWC_DEBUGPL(DBG_ANY, "%s: otg_dev NULL!\n", __func__); | |
654 | return; | |
655 | } | |
656 | ||
657 | dwc_otg_hcd = otg_dev->hcd; | |
658 | ||
659 | if (!dwc_otg_hcd) { | |
660 | DWC_DEBUGPL(DBG_ANY, "%s: otg_dev->hcd NULL!\n", __func__); | |
661 | return; | |
662 | } | |
663 | ||
664 | hcd = dwc_otg_hcd_to_hcd(dwc_otg_hcd); | |
665 | ||
666 | if (!hcd) { | |
667 | DWC_DEBUGPL(DBG_ANY, | |
668 | "%s: dwc_otg_hcd_to_hcd(dwc_otg_hcd) NULL!\n", | |
669 | __func__); | |
670 | return; | |
671 | } | |
672 | usb_remove_hcd(hcd); | |
673 | dwc_otg_hcd_set_priv_data(dwc_otg_hcd, NULL); | |
674 | dwc_otg_hcd_remove(dwc_otg_hcd); | |
675 | usb_put_hcd(hcd); | |
676 | } | |
677 | ||
678 | /* ========================================================================= | |
679 | * Linux HC Driver Functions | |
680 | * ========================================================================= */ | |
681 | ||
682 | /** Initializes the DWC_otg controller and its root hub and prepares it for host | |
683 | * mode operation. Activates the root port. Returns 0 on success and a negative | |
684 | * error code on failure. */ | |
685 | int hcd_start(struct usb_hcd *hcd) | |
686 | { | |
687 | dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); | |
688 | struct usb_bus *bus; | |
689 | ||
690 | DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD START\n"); | |
691 | bus = hcd_to_bus(hcd); | |
692 | ||
693 | hcd->state = HC_STATE_RUNNING; | |
694 | if (dwc_otg_hcd_start(dwc_otg_hcd, &hcd_fops)) { | |
695 | return 0; | |
696 | } | |
697 | ||
698 | /* Initialize and connect root hub if one is not already attached */ | |
699 | if (bus->root_hub) { | |
700 | DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Has Root Hub\n"); | |
701 | /* Inform the HUB driver to resume. */ | |
702 | usb_hcd_resume_root_hub(hcd); | |
703 | } | |
704 | ||
705 | return 0; | |
706 | } | |
707 | ||
708 | /** | |
709 | * Halts the DWC_otg host mode operations in a clean manner. USB transfers are | |
710 | * stopped. | |
711 | */ | |
712 | void hcd_stop(struct usb_hcd *hcd) | |
713 | { | |
714 | dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); | |
715 | ||
716 | dwc_otg_hcd_stop(dwc_otg_hcd); | |
717 | } | |
718 | ||
719 | /** Returns the current frame number. */ | |
720 | static int get_frame_number(struct usb_hcd *hcd) | |
721 | { | |
722 | hprt0_data_t hprt0; | |
723 | dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); | |
724 | hprt0.d32 = DWC_READ_REG32(dwc_otg_hcd->core_if->host_if->hprt0); | |
725 | if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED) | |
726 | return dwc_otg_hcd_get_frame_number(dwc_otg_hcd) >> 3; | |
727 | else | |
728 | return dwc_otg_hcd_get_frame_number(dwc_otg_hcd); | |
729 | } | |
730 | ||
731 | #ifdef DEBUG | |
732 | static void dump_urb_info(struct urb *urb, char *fn_name) | |
733 | { | |
734 | DWC_PRINTF("%s, urb %p\n", fn_name, urb); | |
735 | DWC_PRINTF(" Device address: %d\n", usb_pipedevice(urb->pipe)); | |
736 | DWC_PRINTF(" Endpoint: %d, %s\n", usb_pipeendpoint(urb->pipe), | |
737 | (usb_pipein(urb->pipe) ? "IN" : "OUT")); | |
738 | DWC_PRINTF(" Endpoint type: %s\n", ( { | |
739 | char *pipetype; | |
740 | switch (usb_pipetype(urb->pipe)) { | |
741 | case PIPE_CONTROL: | |
742 | pipetype = "CONTROL"; break; case PIPE_BULK: | |
743 | pipetype = "BULK"; break; case PIPE_INTERRUPT: | |
744 | pipetype = "INTERRUPT"; break; case PIPE_ISOCHRONOUS: | |
745 | pipetype = "ISOCHRONOUS"; break; default: | |
746 | pipetype = "UNKNOWN"; break;}; | |
747 | pipetype;} | |
748 | )) ; | |
749 | DWC_PRINTF(" Speed: %s\n", ( { | |
750 | char *speed; switch (urb->dev->speed) { | |
751 | case USB_SPEED_HIGH: | |
752 | speed = "HIGH"; break; case USB_SPEED_FULL: | |
753 | speed = "FULL"; break; case USB_SPEED_LOW: | |
754 | speed = "LOW"; break; default: | |
755 | speed = "UNKNOWN"; break;}; | |
756 | speed;} | |
757 | )) ; | |
758 | DWC_PRINTF(" Max packet size: %d\n", | |
759 | usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))); | |
760 | DWC_PRINTF(" Data buffer length: %d\n", urb->transfer_buffer_length); | |
761 | DWC_PRINTF(" Transfer buffer: %p, Transfer DMA: %p\n", | |
762 | urb->transfer_buffer, (void *)urb->transfer_dma); | |
763 | DWC_PRINTF(" Setup buffer: %p, Setup DMA: %p\n", | |
764 | urb->setup_packet, (void *)urb->setup_dma); | |
765 | DWC_PRINTF(" Interval: %d\n", urb->interval); | |
766 | if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { | |
767 | int i; | |
768 | for (i = 0; i < urb->number_of_packets; i++) { | |
769 | DWC_PRINTF(" ISO Desc %d:\n", i); | |
770 | DWC_PRINTF(" offset: %d, length %d\n", | |
771 | urb->iso_frame_desc[i].offset, | |
772 | urb->iso_frame_desc[i].length); | |
773 | } | |
774 | } | |
775 | } | |
776 | #endif | |
777 | ||
778 | /** Starts processing a USB transfer request specified by a USB Request Block | |
779 | * (URB). mem_flags indicates the type of memory allocation to use while | |
780 | * processing this URB. */ | |
781 | static int dwc_otg_urb_enqueue(struct usb_hcd *hcd, | |
782 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) | |
783 | struct usb_host_endpoint *ep, | |
784 | #endif | |
785 | struct urb *urb, gfp_t mem_flags) | |
786 | { | |
787 | int retval = 0; | |
788 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) | |
789 | struct usb_host_endpoint *ep = urb->ep; | |
790 | #endif | |
791 | dwc_irqflags_t irqflags; | |
792 | void **ref_ep_hcpriv = &ep->hcpriv; | |
793 | dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); | |
794 | dwc_otg_hcd_urb_t *dwc_otg_urb; | |
795 | int i; | |
796 | int alloc_bandwidth = 0; | |
797 | uint8_t ep_type = 0; | |
798 | uint32_t flags = 0; | |
799 | void *buf; | |
800 | ||
801 | #ifdef DEBUG | |
802 | if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { | |
803 | dump_urb_info(urb, "dwc_otg_urb_enqueue"); | |
804 | } | |
805 | #endif | |
806 | ||
807 | if (!urb->transfer_buffer && urb->transfer_buffer_length) | |
808 | return -EINVAL; | |
809 | ||
810 | if ((usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) | |
811 | || (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) { | |
812 | if (!dwc_otg_hcd_is_bandwidth_allocated | |
813 | (dwc_otg_hcd, ref_ep_hcpriv)) { | |
814 | alloc_bandwidth = 1; | |
815 | } | |
816 | } | |
817 | ||
818 | switch (usb_pipetype(urb->pipe)) { | |
819 | case PIPE_CONTROL: | |
820 | ep_type = USB_ENDPOINT_XFER_CONTROL; | |
821 | break; | |
822 | case PIPE_ISOCHRONOUS: | |
823 | ep_type = USB_ENDPOINT_XFER_ISOC; | |
824 | break; | |
825 | case PIPE_BULK: | |
826 | ep_type = USB_ENDPOINT_XFER_BULK; | |
827 | break; | |
828 | case PIPE_INTERRUPT: | |
829 | ep_type = USB_ENDPOINT_XFER_INT; | |
830 | break; | |
831 | default: | |
832 | DWC_WARN("Wrong EP type - %d\n", usb_pipetype(urb->pipe)); | |
833 | } | |
834 | ||
835 | /* # of packets is often 0 - do we really need to call this then? */ | |
836 | dwc_otg_urb = dwc_otg_hcd_urb_alloc(dwc_otg_hcd, | |
837 | urb->number_of_packets, | |
838 | mem_flags == GFP_ATOMIC ? 1 : 0); | |
839 | ||
840 | if(dwc_otg_urb == NULL) | |
841 | return -ENOMEM; | |
842 | ||
843 | if (!dwc_otg_urb && urb->number_of_packets) | |
844 | return -ENOMEM; | |
845 | ||
846 | dwc_otg_hcd_urb_set_pipeinfo(dwc_otg_urb, usb_pipedevice(urb->pipe), | |
847 | usb_pipeendpoint(urb->pipe), ep_type, | |
848 | usb_pipein(urb->pipe), | |
849 | usb_maxpacket(urb->dev, urb->pipe, | |
850 | !(usb_pipein(urb->pipe)))); | |
851 | ||
852 | buf = urb->transfer_buffer; | |
853 | if (hcd->self.uses_dma && !buf && urb->transfer_buffer_length) { | |
854 | /* | |
855 | * Calculate virtual address from physical address, | |
856 | * because some class driver may not fill transfer_buffer. | |
857 | * In Buffer DMA mode virual address is used, | |
858 | * when handling non DWORD aligned buffers. | |
859 | */ | |
860 | buf = (void *)__bus_to_virt((unsigned long)urb->transfer_dma); | |
861 | dev_warn_once(&urb->dev->dev, | |
862 | "USB transfer_buffer was NULL, will use __bus_to_virt(%pad)=%p\n", | |
863 | &urb->transfer_dma, buf); | |
864 | } | |
865 | ||
866 | if (!(urb->transfer_flags & URB_NO_INTERRUPT)) | |
867 | flags |= URB_GIVEBACK_ASAP; | |
868 | if (urb->transfer_flags & URB_ZERO_PACKET) | |
869 | flags |= URB_SEND_ZERO_PACKET; | |
870 | ||
871 | dwc_otg_hcd_urb_set_params(dwc_otg_urb, urb, buf, | |
872 | urb->transfer_dma, | |
873 | urb->transfer_buffer_length, | |
874 | urb->setup_packet, | |
875 | urb->setup_dma, flags, urb->interval); | |
876 | ||
877 | for (i = 0; i < urb->number_of_packets; ++i) { | |
878 | dwc_otg_hcd_urb_set_iso_desc_params(dwc_otg_urb, i, | |
879 | urb-> | |
880 | iso_frame_desc[i].offset, | |
881 | urb-> | |
882 | iso_frame_desc[i].length); | |
883 | } | |
884 | ||
885 | DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &irqflags); | |
886 | urb->hcpriv = dwc_otg_urb; | |
887 | #if USB_URB_EP_LINKING | |
888 | retval = usb_hcd_link_urb_to_ep(hcd, urb); | |
889 | if (0 == retval) | |
890 | #endif | |
891 | { | |
892 | retval = dwc_otg_hcd_urb_enqueue(dwc_otg_hcd, dwc_otg_urb, | |
893 | /*(dwc_otg_qh_t **)*/ | |
894 | ref_ep_hcpriv, 1); | |
895 | if (0 == retval) { | |
896 | if (alloc_bandwidth) { | |
897 | allocate_bus_bandwidth(hcd, | |
898 | dwc_otg_hcd_get_ep_bandwidth( | |
899 | dwc_otg_hcd, *ref_ep_hcpriv), | |
900 | urb); | |
901 | } | |
902 | } else { | |
903 | DWC_DEBUGPL(DBG_HCD, "DWC OTG dwc_otg_hcd_urb_enqueue failed rc %d\n", retval); | |
904 | #if USB_URB_EP_LINKING | |
905 | usb_hcd_unlink_urb_from_ep(hcd, urb); | |
906 | #endif | |
907 | DWC_FREE(dwc_otg_urb); | |
908 | urb->hcpriv = NULL; | |
909 | if (retval == -DWC_E_NO_DEVICE) | |
910 | retval = -ENODEV; | |
911 | } | |
912 | } | |
913 | #if USB_URB_EP_LINKING | |
914 | else | |
915 | { | |
916 | DWC_FREE(dwc_otg_urb); | |
917 | urb->hcpriv = NULL; | |
918 | } | |
919 | #endif | |
920 | DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, irqflags); | |
921 | return retval; | |
922 | } | |
923 | ||
924 | /** Aborts/cancels a USB transfer request. Always returns 0 to indicate | |
925 | * success. */ | |
926 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) | |
927 | static int dwc_otg_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) | |
928 | #else | |
929 | static int dwc_otg_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) | |
930 | #endif | |
931 | { | |
932 | dwc_irqflags_t flags; | |
933 | dwc_otg_hcd_t *dwc_otg_hcd; | |
934 | int rc; | |
935 | ||
936 | DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD URB Dequeue\n"); | |
937 | ||
938 | dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); | |
939 | ||
940 | #ifdef DEBUG | |
941 | if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { | |
942 | dump_urb_info(urb, "dwc_otg_urb_dequeue"); | |
943 | } | |
944 | #endif | |
945 | ||
946 | DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &flags); | |
947 | rc = usb_hcd_check_unlink_urb(hcd, urb, status); | |
948 | if (0 == rc) { | |
949 | if(urb->hcpriv != NULL) { | |
950 | dwc_otg_hcd_urb_dequeue(dwc_otg_hcd, | |
951 | (dwc_otg_hcd_urb_t *)urb->hcpriv); | |
952 | ||
953 | DWC_FREE(urb->hcpriv); | |
954 | urb->hcpriv = NULL; | |
955 | } | |
956 | } | |
957 | ||
958 | if (0 == rc) { | |
959 | /* Higher layer software sets URB status. */ | |
960 | #if USB_URB_EP_LINKING | |
961 | usb_hcd_unlink_urb_from_ep(hcd, urb); | |
962 | #endif | |
963 | DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, flags); | |
964 | ||
965 | ||
966 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) | |
967 | usb_hcd_giveback_urb(hcd, urb); | |
968 | #else | |
969 | usb_hcd_giveback_urb(hcd, urb, status); | |
970 | #endif | |
971 | if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { | |
972 | DWC_PRINTF("Called usb_hcd_giveback_urb() \n"); | |
973 | DWC_PRINTF(" 1urb->status = %d\n", urb->status); | |
974 | } | |
975 | DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD URB Dequeue OK\n"); | |
976 | } else { | |
977 | DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, flags); | |
978 | DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD URB Dequeue failed - rc %d\n", | |
979 | rc); | |
980 | } | |
981 | ||
982 | return rc; | |
983 | } | |
984 | ||
985 | /* Frees resources in the DWC_otg controller related to a given endpoint. Also | |
986 | * clears state in the HCD related to the endpoint. Any URBs for the endpoint | |
987 | * must already be dequeued. */ | |
988 | static void endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep) | |
989 | { | |
990 | dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); | |
991 | ||
992 | DWC_DEBUGPL(DBG_HCD, | |
993 | "DWC OTG HCD EP DISABLE: _bEndpointAddress=0x%02x, " | |
994 | "endpoint=%d\n", ep->desc.bEndpointAddress, | |
995 | dwc_ep_addr_to_endpoint(ep->desc.bEndpointAddress)); | |
996 | dwc_otg_hcd_endpoint_disable(dwc_otg_hcd, ep->hcpriv, 250); | |
997 | ep->hcpriv = NULL; | |
998 | } | |
999 | ||
1000 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30) | |
1001 | /* Resets endpoint specific parameter values, in current version used to reset | |
1002 | * the data toggle(as a WA). This function can be called from usb_clear_halt routine */ | |
1003 | static void endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) | |
1004 | { | |
1005 | dwc_irqflags_t flags; | |
1006 | struct usb_device *udev = NULL; | |
1007 | int epnum = usb_endpoint_num(&ep->desc); | |
1008 | int is_out = usb_endpoint_dir_out(&ep->desc); | |
1009 | int is_control = usb_endpoint_xfer_control(&ep->desc); | |
1010 | dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); | |
1011 | struct device *dev = DWC_OTG_OS_GETDEV(dwc_otg_hcd->otg_dev->os_dep); | |
1012 | ||
1013 | if (dev) | |
1014 | udev = to_usb_device(dev); | |
1015 | else | |
1016 | return; | |
1017 | ||
1018 | DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD EP RESET: Endpoint Num=0x%02d\n", epnum); | |
1019 | ||
1020 | DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &flags); | |
1021 | usb_settoggle(udev, epnum, is_out, 0); | |
1022 | if (is_control) | |
1023 | usb_settoggle(udev, epnum, !is_out, 0); | |
1024 | ||
1025 | if (ep->hcpriv) { | |
1026 | dwc_otg_hcd_endpoint_reset(dwc_otg_hcd, ep->hcpriv); | |
1027 | } | |
1028 | DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, flags); | |
1029 | } | |
1030 | #endif | |
1031 | ||
1032 | /** Handles host mode interrupts for the DWC_otg controller. Returns IRQ_NONE if | |
1033 | * there was no interrupt to handle. Returns IRQ_HANDLED if there was a valid | |
1034 | * interrupt. | |
1035 | * | |
1036 | * This function is called by the USB core when an interrupt occurs */ | |
1037 | static irqreturn_t dwc_otg_hcd_irq(struct usb_hcd *hcd) | |
1038 | { | |
1039 | dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); | |
1040 | int32_t retval = dwc_otg_hcd_handle_intr(dwc_otg_hcd); | |
1041 | if (retval != 0) { | |
1042 | S3C2410X_CLEAR_EINTPEND(); | |
1043 | } | |
1044 | return IRQ_RETVAL(retval); | |
1045 | } | |
1046 | ||
1047 | /** Creates Status Change bitmap for the root hub and root port. The bitmap is | |
1048 | * returned in buf. Bit 0 is the status change indicator for the root hub. Bit 1 | |
1049 | * is the status change indicator for the single root port. Returns 1 if either | |
1050 | * change indicator is 1, otherwise returns 0. */ | |
1051 | int hub_status_data(struct usb_hcd *hcd, char *buf) | |
1052 | { | |
1053 | dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); | |
1054 | ||
1055 | buf[0] = 0; | |
1056 | buf[0] |= (dwc_otg_hcd_is_status_changed(dwc_otg_hcd, 1)) << 1; | |
1057 | ||
1058 | return (buf[0] != 0); | |
1059 | } | |
1060 | ||
1061 | /** Handles hub class-specific requests. */ | |
1062 | int hub_control(struct usb_hcd *hcd, | |
1063 | u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) | |
1064 | { | |
1065 | int retval; | |
1066 | ||
1067 | retval = dwc_otg_hcd_hub_control(hcd_to_dwc_otg_hcd(hcd), | |
1068 | typeReq, wValue, wIndex, buf, wLength); | |
1069 | ||
1070 | switch (retval) { | |
1071 | case -DWC_E_INVALID: | |
1072 | retval = -EINVAL; | |
1073 | break; | |
1074 | } | |
1075 | ||
1076 | return retval; | |
1077 | } | |
1078 | ||
1079 | #endif /* DWC_DEVICE_ONLY */ |