00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 #ifndef DWC_DEVICE_ONLY
00034
00042 #include "dwc_otg_hcd.h"
00043 #include "dwc_otg_regs.h"
00044
00053 void dwc_otg_hcd_qh_free(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
00054 {
00055 dwc_otg_qtd_t *qtd, *qtd_tmp;
00056 uint64_t flags;
00057
00058
00059 DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
00060 DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &qh->qtd_list, qtd_list_entry) {
00061 DWC_CIRCLEQ_REMOVE(&qh->qtd_list, qtd, qtd_list_entry);
00062 dwc_otg_hcd_qtd_free(qtd);
00063 }
00064 DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
00065
00066 if (hcd->core_if->dma_desc_enable) {
00067 dwc_otg_hcd_qh_free_ddma(hcd, qh);
00068 }
00069 else if (qh->dw_align_buf) {
00070 uint32_t buf_size;
00071 if(qh->ep_type == UE_ISOCHRONOUS) {
00072 buf_size = 4096;
00073 } else {
00074 buf_size = hcd->core_if->core_params->max_transfer_size;
00075 }
00076 dwc_dma_free(buf_size, qh->dw_align_buf, qh->dw_align_buf_dma);
00077 }
00078
00079
00080
00081 dwc_free(qh);
00082 return;
00083 }
00084
00085 #define BitStuffTime(bytecount) ((8 * 7* bytecount) / 6)
00086 #define HS_HOST_DELAY 5
00087 #define FS_LS_HOST_DELAY 1000
00088 #define HUB_LS_SETUP 333
00089 #define NS_TO_US(ns) ((ns + 500) / 1000)
00090
00091
00092 static uint32_t calc_bus_time(int speed, int is_in, int is_isoc,
00093 int bytecount)
00094 {
00095 unsigned long retval;
00096
00097 switch (speed) {
00098 case USB_SPEED_HIGH:
00099 if (is_isoc) {
00100 retval =
00101 ((38 * 8 * 2083) +
00102 (2083 * (3 + BitStuffTime(bytecount)))) / 1000 +
00103 HS_HOST_DELAY;
00104 } else {
00105 retval =
00106 ((55 * 8 * 2083) +
00107 (2083 * (3 + BitStuffTime(bytecount)))) / 1000 +
00108 HS_HOST_DELAY;
00109 }
00110 break;
00111 case USB_SPEED_FULL:
00112 if (is_isoc) {
00113 retval =
00114 (8354 * (31 + 10 * BitStuffTime(bytecount))) / 1000;
00115 if (is_in) {
00116 retval = 7268 + FS_LS_HOST_DELAY + retval;
00117 } else {
00118 retval = 6265 + FS_LS_HOST_DELAY + retval;
00119 }
00120 } else {
00121 retval =
00122 (8354 * (31 + 10 * BitStuffTime(bytecount))) / 1000;
00123 retval = 9107 + FS_LS_HOST_DELAY + retval;
00124 }
00125 break;
00126 case USB_SPEED_LOW:
00127 if (is_in) {
00128 retval =
00129 (67667 * (31 + 10 * BitStuffTime(bytecount))) /
00130 1000;
00131 retval =
00132 64060 + (2 * HUB_LS_SETUP) + FS_LS_HOST_DELAY +
00133 retval;
00134 } else {
00135 retval =
00136 (66700 * (31 + 10 * BitStuffTime(bytecount))) /
00137 1000;
00138 retval =
00139 64107 + (2 * HUB_LS_SETUP) + FS_LS_HOST_DELAY +
00140 retval;
00141 }
00142 break;
00143 default:
00144 DWC_WARN("Unknown device speed\n");
00145 retval = -1;
00146 }
00147
00148 return NS_TO_US(retval);
00149 }
00150
00159 #define SCHEDULE_SLOP 10
00160 void qh_init(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh,
00161 dwc_otg_hcd_urb_t * urb)
00162 {
00163 char *speed, *type;
00164 int dev_speed;
00165 uint32_t hub_addr, hub_port;
00166
00167 dwc_memset(qh, 0, sizeof(dwc_otg_qh_t));
00168
00169
00170 qh->ep_type = dwc_otg_hcd_get_pipe_type(&urb->pipe_info);
00171
00172 qh->ep_is_in = dwc_otg_hcd_is_pipe_in(&urb->pipe_info) ? 1 : 0;
00173
00174 qh->data_toggle = DWC_OTG_HC_PID_DATA0;
00175 qh->maxp = dwc_otg_hcd_get_mps(&urb->pipe_info);
00176 DWC_CIRCLEQ_INIT(&qh->qtd_list);
00177 DWC_LIST_INIT(&qh->qh_list_entry);
00178 qh->channel = NULL;
00179
00180
00181
00182 dev_speed = hcd->fops->speed(hcd, urb->priv);
00183 hcd->fops->hub_info(hcd, urb->priv, &hub_addr, &hub_port);
00184 qh->do_split = 0;
00185 if (((dev_speed == USB_SPEED_LOW) ||
00186 (dev_speed == USB_SPEED_FULL)) &&
00187 (hub_addr != 0 && hub_addr != 1)) {
00188
00189 DWC_DEBUGPL(DBG_HCD,
00190 "QH init: EP %d: TT found at hub addr %d, for port %d\n",
00191 dwc_otg_hcd_get_ep_num(&urb->pipe_info), hub_addr,
00192 hub_port);
00193
00194 qh->do_split = 1;
00195 }
00196
00197 if (qh->ep_type == UE_INTERRUPT || qh->ep_type == UE_ISOCHRONOUS) {
00198
00199 hprt0_data_t hprt;
00200
00202 int bytecount =
00203 dwc_hb_mult(qh->maxp) * dwc_max_packet(qh->maxp);
00204
00205 qh->usecs = calc_bus_time((qh->do_split ? USB_SPEED_HIGH : dev_speed),
00206 qh->ep_is_in,
00207 (qh->ep_type == UE_ISOCHRONOUS),
00208 bytecount);
00209
00210 qh->sched_frame = dwc_frame_num_inc(hcd->frame_number,
00211 SCHEDULE_SLOP);
00212 qh->interval = urb->interval;
00213
00214 #if 0
00215
00216 if (qh->ep_type == UE_INTERRUPT) {
00217 qh->interval = 8;
00218 }
00219 #endif
00220 hprt.d32 = dwc_read_reg32(hcd->core_if->host_if->hprt0);
00221 if ((hprt.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED) &&
00222 ((dev_speed == USB_SPEED_LOW) ||
00223 (dev_speed == USB_SPEED_FULL))) {
00224 qh->interval *= 8;
00225 qh->sched_frame |= 0x7;
00226 qh->start_split_frame = qh->sched_frame;
00227 }
00228
00229 }
00230
00231 DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD QH Initialized\n");
00232 DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - qh = %p\n", qh);
00233 DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Device Address = %d\n",
00234 dwc_otg_hcd_get_dev_addr(&urb->pipe_info));
00235 DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Endpoint %d, %s\n",
00236 dwc_otg_hcd_get_ep_num(&urb->pipe_info),
00237 dwc_otg_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT");
00238 switch (dev_speed) {
00239 case USB_SPEED_LOW:
00240 qh->dev_speed = DWC_OTG_EP_SPEED_LOW;
00241 speed = "low";
00242 break;
00243 case USB_SPEED_FULL:
00244 qh->dev_speed = DWC_OTG_EP_SPEED_FULL;
00245 speed = "full";
00246 break;
00247 case USB_SPEED_HIGH:
00248 qh->dev_speed = DWC_OTG_EP_SPEED_HIGH;
00249 speed = "high";
00250 break;
00251 default:
00252 speed = "?";
00253 break;
00254 }
00255 DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Speed = %s\n", speed);
00256
00257 switch (qh->ep_type) {
00258 case UE_ISOCHRONOUS:
00259 type = "isochronous";
00260 break;
00261 case UE_INTERRUPT:
00262 type = "interrupt";
00263 break;
00264 case UE_CONTROL:
00265 type = "control";
00266 break;
00267 case UE_BULK:
00268 type = "bulk";
00269 break;
00270 default:
00271 type = "?";
00272 break;
00273 }
00274
00275 DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Type = %s\n", type);
00276
00277 #ifdef DEBUG
00278 if (qh->ep_type == UE_INTERRUPT) {
00279 DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - usecs = %d\n",
00280 qh->usecs);
00281 DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - interval = %d\n",
00282 qh->interval);
00283 }
00284 #endif
00285
00286 }
00287
00296 dwc_otg_qh_t *dwc_otg_hcd_qh_create(dwc_otg_hcd_t * hcd,
00297 dwc_otg_hcd_urb_t * urb)
00298 {
00299 dwc_otg_qh_t *qh;
00300
00301
00303 qh = dwc_otg_hcd_qh_alloc();
00304 if (qh == NULL) {
00305 return NULL;
00306 }
00307
00308 qh_init(hcd, qh, urb);
00309
00310 if (hcd->core_if->dma_desc_enable && (dwc_otg_hcd_qh_init_ddma(hcd, qh) < 0)) {
00311 dwc_otg_hcd_qh_free(hcd, qh);
00312 return NULL;
00313 }
00314
00315 return qh;
00316 }
00317
00323 static int periodic_channel_available(dwc_otg_hcd_t * hcd)
00324 {
00325
00326
00327
00328
00329
00330 int status;
00331 int num_channels;
00332
00333 num_channels = hcd->core_if->core_params->host_channels;
00334 if ((hcd->periodic_channels + hcd->non_periodic_channels < num_channels) &&
00335 (hcd->periodic_channels < num_channels - 1)) {
00336 status = 0;
00337 } else {
00338 DWC_INFO("%s: Total channels: %d, Periodic: %d, Non-periodic: %d\n",
00339 __func__, num_channels, hcd->periodic_channels, hcd->non_periodic_channels);
00340 status = -DWC_E_NO_SPACE;
00341 }
00342
00343 return status;
00344 }
00345
00356 static int check_periodic_bandwidth(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
00357 {
00358 int status;
00359 int16_t max_claimed_usecs;
00360
00361 status = 0;
00362
00363 if ((qh->dev_speed == DWC_OTG_EP_SPEED_HIGH) || qh->do_split) {
00364
00365
00366
00367
00368
00369 max_claimed_usecs = 100 - qh->usecs;
00370 } else {
00371
00372
00373
00374
00375 max_claimed_usecs = 900 - qh->usecs;
00376 }
00377
00378 if (hcd->periodic_usecs > max_claimed_usecs) {
00379 DWC_INFO("%s: already claimed usecs %d, required usecs %d\n", __func__, hcd->periodic_usecs, qh->usecs);
00380 status = -DWC_E_NO_SPACE;
00381 }
00382
00383 return status;
00384 }
00385
00396 static int check_max_xfer_size(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
00397 {
00398 int status;
00399 uint32_t max_xfer_size;
00400 uint32_t max_channel_xfer_size;
00401
00402 status = 0;
00403
00404 max_xfer_size = dwc_max_packet(qh->maxp) * dwc_hb_mult(qh->maxp);
00405 max_channel_xfer_size = hcd->core_if->core_params->max_transfer_size;
00406
00407 if (max_xfer_size > max_channel_xfer_size) {
00408 DWC_INFO("%s: Periodic xfer length %d > " "max xfer length for channel %d\n",
00409 __func__, max_xfer_size, max_channel_xfer_size);
00410 status = -DWC_E_NO_SPACE;
00411 }
00412
00413 return status;
00414 }
00415
00425 static int schedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
00426 {
00427 int status = 0;
00428
00429 status = periodic_channel_available(hcd);
00430 if (status) {
00431 DWC_INFO("%s: No host channel available for periodic " "transfer.\n", __func__);
00432 return status;
00433 }
00434
00435 status = check_periodic_bandwidth(hcd, qh);
00436 if (status) {
00437 DWC_INFO("%s: Insufficient periodic bandwidth for " "periodic transfer.\n", __func__);
00438 return status;
00439 }
00440
00441 status = check_max_xfer_size(hcd, qh);
00442 if (status) {
00443 DWC_INFO("%s: Channel max transfer size too small " "for periodic transfer.\n", __func__);
00444 return status;
00445 }
00446
00447 if (hcd->core_if->dma_desc_enable) {
00448
00449 DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_ready, &qh->qh_list_entry);
00450 }
00451 else {
00452
00453 DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_inactive, &qh->qh_list_entry);
00454 }
00455
00456
00457 hcd->periodic_channels++;
00458
00459
00460 hcd->periodic_usecs += qh->usecs;
00461
00462 return status;
00463 }
00464
00472 int dwc_otg_hcd_qh_add(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
00473 {
00474 int status = 0;
00475 uint64_t flags;
00476
00477 DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
00478
00479 if (!DWC_LIST_EMPTY(&qh->qh_list_entry)) {
00480
00481 goto done;
00482 }
00483
00484
00485 if (dwc_qh_is_non_per(qh)) {
00486
00487 DWC_LIST_INSERT_TAIL(&hcd->non_periodic_sched_inactive,
00488 &qh->qh_list_entry);
00489 } else {
00490 status = schedule_periodic(hcd, qh);
00491 }
00492
00493 done:
00494 DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
00495
00496 return status;
00497 }
00498
00505 static void deschedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
00506 {
00507 DWC_LIST_REMOVE_INIT(&qh->qh_list_entry);
00508
00509
00510 hcd->periodic_channels--;
00511
00512
00513 hcd->periodic_usecs -= qh->usecs;
00514 }
00515
00522 void dwc_otg_hcd_qh_remove(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
00523 {
00524 uint64_t flags;
00525 DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
00526
00527 if (DWC_LIST_EMPTY(&qh->qh_list_entry)) {
00528
00529 goto done;
00530 }
00531
00532 if (dwc_qh_is_non_per(qh)) {
00533 if (hcd->non_periodic_qh_ptr == &qh->qh_list_entry) {
00534 hcd->non_periodic_qh_ptr =
00535 hcd->non_periodic_qh_ptr->next;
00536 }
00537 DWC_LIST_REMOVE_INIT(&qh->qh_list_entry);
00538 } else {
00539 deschedule_periodic(hcd, qh);
00540 }
00541
00542 done:
00543 DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
00544 }
00545
00559 void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh,
00560 int sched_next_periodic_split)
00561 {
00562 uint64_t flags;
00563 DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
00564
00565 if (dwc_qh_is_non_per(qh)) {
00566 dwc_otg_hcd_qh_remove(hcd, qh);
00567 if (!DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) {
00568
00569 dwc_otg_hcd_qh_add(hcd, qh);
00570 }
00571 } else {
00572 uint16_t frame_number = dwc_otg_hcd_get_frame_number(hcd);
00573
00574 if (qh->do_split) {
00575
00576 if (sched_next_periodic_split) {
00577
00578 qh->sched_frame = frame_number;
00579 if (dwc_frame_num_le(frame_number,
00580 dwc_frame_num_inc(qh->
00581 start_split_frame,
00582 1))) {
00583
00584
00585
00586
00587
00588
00589
00590 if ((qh->ep_type != UE_ISOCHRONOUS) ||
00591 (qh->ep_is_in != 0)) {
00592 qh->sched_frame =
00593 dwc_frame_num_inc(qh->sched_frame, 1);
00594 }
00595 }
00596 } else {
00597 qh->sched_frame =
00598 dwc_frame_num_inc(qh->start_split_frame,
00599 qh->interval);
00600 if (dwc_frame_num_le
00601 (qh->sched_frame, frame_number)) {
00602 qh->sched_frame = frame_number;
00603 }
00604 qh->sched_frame |= 0x7;
00605 qh->start_split_frame = qh->sched_frame;
00606 }
00607 } else {
00608 qh->sched_frame =
00609 dwc_frame_num_inc(qh->sched_frame, qh->interval);
00610 if (dwc_frame_num_le(qh->sched_frame, frame_number)) {
00611 qh->sched_frame = frame_number;
00612 }
00613 }
00614
00615 if (DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) {
00616 dwc_otg_hcd_qh_remove(hcd, qh);
00617 } else {
00618
00619
00620
00621
00622 if (qh->sched_frame == frame_number) {
00623 DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready,
00624 &qh->qh_list_entry);
00625 } else {
00626 DWC_LIST_MOVE_HEAD(&hcd->
00627 periodic_sched_inactive,
00628 &qh->qh_list_entry);
00629 }
00630 }
00631 }
00632
00633 DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
00634 }
00635
00643 dwc_otg_qtd_t *dwc_otg_hcd_qtd_create(dwc_otg_hcd_urb_t * urb)
00644 {
00645 dwc_otg_qtd_t *qtd;
00646
00647 qtd = dwc_otg_hcd_qtd_alloc();
00648 if (qtd == NULL) {
00649 return NULL;
00650 }
00651
00652 dwc_otg_hcd_qtd_init(qtd, urb);
00653 return qtd;
00654 }
00655
00661 void dwc_otg_hcd_qtd_init(dwc_otg_qtd_t * qtd, dwc_otg_hcd_urb_t * urb)
00662 {
00663 dwc_memset(qtd, 0, sizeof(dwc_otg_qtd_t));
00664 qtd->urb = urb;
00665 if (dwc_otg_hcd_get_pipe_type(&urb->pipe_info) == UE_CONTROL) {
00666
00667
00668
00669
00670
00671 qtd->data_toggle = DWC_OTG_HC_PID_DATA1;
00672 qtd->control_phase = DWC_OTG_CONTROL_SETUP;
00673 }
00674
00675
00676 qtd->complete_split = 0;
00677 qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_ALL;
00678 qtd->isoc_split_offset = 0;
00679 qtd->in_process = 0;
00680
00681
00682 urb->qtd = qtd;
00683 return;
00684 }
00685
00698 int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t * qtd,
00699 dwc_otg_hcd_t * hcd, dwc_otg_qh_t ** qh)
00700 {
00701 int retval = 0;
00702 uint64_t flags;
00703
00704 dwc_otg_hcd_urb_t *urb = qtd->urb;
00705
00706 DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
00707
00708
00709
00710
00711
00712 if (*qh == NULL) {
00713 *qh = dwc_otg_hcd_qh_create(hcd, urb);
00714 if (*qh == NULL) {
00715 retval = -1;
00716 goto done;
00717 }
00718 }
00719
00720 retval = dwc_otg_hcd_qh_add(hcd, *qh);
00721 if (retval == 0) {
00722 DWC_CIRCLEQ_INSERT_TAIL(&((*qh)->qtd_list), qtd,
00723 qtd_list_entry);
00724 }
00725
00726 done:
00727 DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
00728
00729 return retval;
00730 }
00731
00732 #endif