📄 dwc_otg_hcd.c
字号:
if (hprt0.b.prtovrcurract) port_status |= (1 << USB_PORT_FEAT_OVER_CURRENT); if (hprt0.b.prtrst) port_status |= (1 << USB_PORT_FEAT_RESET); if (hprt0.b.prtpwr) port_status |= (1 << USB_PORT_FEAT_POWER); if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED) port_status |= (1 << USB_PORT_FEAT_HIGHSPEED); else if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_LOW_SPEED) port_status |= (1 << USB_PORT_FEAT_LOWSPEED); if (hprt0.b.prttstctl) port_status |= (1 << USB_PORT_FEAT_TEST); /* USB_PORT_FEAT_INDICATOR unsupported always 0 */ *((__le32 *) _buf) = cpu_to_le32(port_status); break; case SetHubFeature: DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " "SetHubFeature\n"); /* No HUB features supported */ break; case SetPortFeature: if (_wValue != USB_PORT_FEAT_TEST && (!_wIndex || _wIndex > 1)) goto error; if (!dwc_otg_hcd->flags.b.port_connect_status) { /* * The port is disconnected, which means the core is * either in device mode or it soon will be. Just * return without doing anything since the port * register can't be written if the core is in device * mode. */ break; } switch (_wValue) { case USB_PORT_FEAT_SUSPEND: printk("DWC OTG HCD HUB CONTROL - " "SetPortFeature - USB_PORT_FEAT_SUSPEND\n"); if (_hcd->self.otg_port == _wIndex && _hcd->self.b_hnp_enable) { gotgctl_data_t gotgctl = {.d32 = 0 }; gotgctl.b.hstsethnpen = 1; dwc_modify_reg32(&core_if->core_global_regs->gotgctl, 0, gotgctl.d32); core_if->op_state = A_SUSPEND; } hprt0.d32 = dwc_otg_read_hprt0(core_if); hprt0.b.prtsusp = 1; dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); //DWC_PRINT( "SUSPEND: HPRT0=%0x\n", hprt0.d32); /* Suspend the Phy Clock */ { pcgcctl_data_t pcgcctl = {.d32 = 0 }; pcgcctl.b.stoppclk = 1; dwc_write_reg32(core_if->pcgcctl, pcgcctl.d32); } /* For HNP the bus must be suspended for at least 200ms. */ if (_hcd->self.b_hnp_enable) { mdelay(200); //DWC_PRINT( "SUSPEND: wait complete! (%d)\n", _hcd->state); } break; case USB_PORT_FEAT_POWER: DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " "SetPortFeature - USB_PORT_FEAT_POWER\n"); hprt0.d32 = dwc_otg_read_hprt0(core_if); hprt0.b.prtpwr = 1; dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); break; case USB_PORT_FEAT_RESET: printk("DWC OTG HCD HUB CONTROL - " "SetPortFeature - USB_PORT_FEAT_RESET\n"); hprt0.d32 = dwc_otg_read_hprt0(core_if); /* When B-Host the Port reset bit is set in * the Start HCD Callback function, so that * the reset is started within 1ms of the HNP * success interrupt. */ if (!_hcd->self.is_b_host) { hprt0.b.prtrst = 1; dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); } /* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */ mdelay(60); hprt0.b.prtrst = 0; dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); break;#ifdef DWC_HS_ELECT_TST case USB_PORT_FEAT_TEST: { uint32_t t; gintmsk_data_t gintmsk; t = (_wIndex >> 8); /* MSB wIndex USB */ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " "SetPortFeature - USB_PORT_FEAT_TEST %d\n", t); warn("USB_PORT_FEAT_TEST %d\n", t); if (t < 6) { hprt0.d32 = dwc_otg_read_hprt0(core_if); hprt0.b.prttstctl = t; dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); } else { /* Setup global vars with reg addresses (quick and * dirty hack, should be cleaned up) */ global_regs = core_if->core_global_regs; hc_global_regs = core_if->host_if->host_global_regs; hc_regs = (dwc_otg_hc_regs_t *) ((char *) global_regs + 0x500); data_fifo = (uint32_t *) ((char *) global_regs + 0x1000); if (t == 6) { /* HS_HOST_PORT_SUSPEND_RESUME */ /* Save current interrupt mask */ gintmsk.d32 = dwc_read_reg32(&global_regs->gintmsk); /* Disable all interrupts while we muck with * the hardware directly */ dwc_write_reg32(&global_regs->gintmsk, 0); /* 15 second delay per the test spec */ mdelay(15000); /* Drive suspend on the root port */ hprt0.d32 = dwc_otg_read_hprt0(core_if); hprt0.b.prtsusp = 1; hprt0.b.prtres = 0; dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); /* 15 second delay per the test spec */ mdelay(15000); /* Drive resume on the root port */ hprt0.d32 = dwc_otg_read_hprt0(core_if); hprt0.b.prtsusp = 0; hprt0.b.prtres = 1; dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); mdelay(100); /* Clear the resume bit */ hprt0.b.prtres = 0; dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); /* Restore interrupts */ dwc_write_reg32(&global_regs->gintmsk, gintmsk.d32); } else if (t == 7) { /* SINGLE_STEP_GET_DEVICE_DESCRIPTOR setup */ /* Save current interrupt mask */ gintmsk.d32 = dwc_read_reg32(&global_regs->gintmsk); /* Disable all interrupts while we muck with * the hardware directly */ dwc_write_reg32(&global_regs->gintmsk, 0); /* 15 second delay per the test spec */ mdelay(15000); /* Send the Setup packet */ do_setup(); /* 15 second delay so nothing else happens for awhile */ mdelay(15000); /* Restore interrupts */ dwc_write_reg32(&global_regs->gintmsk, gintmsk.d32); } else if (t == 8) { /* SINGLE_STEP_GET_DEVICE_DESCRIPTOR execute */ /* Save current interrupt mask */ gintmsk.d32 = dwc_read_reg32(&global_regs->gintmsk); /* Disable all interrupts while we muck with * the hardware directly */ dwc_write_reg32(&global_regs->gintmsk, 0); /* Send the Setup packet */ do_setup(); /* 15 second delay so nothing else happens for awhile */ mdelay(15000); /* Send the In and Ack packets */ do_in_ack(); /* 15 second delay so nothing else happens for awhile */ mdelay(15000); /* Restore interrupts */ dwc_write_reg32(&global_regs->gintmsk, gintmsk.d32); } } break; }#endif /* DWC_HS_ELECT_TST */ case USB_PORT_FEAT_INDICATOR: DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " "SetPortFeature - USB_PORT_FEAT_INDICATOR\n"); /* Not supported */ break; default: retval = -EINVAL; DWC_ERROR("DWC OTG HCD - " "SetPortFeature request %xh " "unknown or unsupported\n", _wValue); break; } break; default: error: retval = -EINVAL; DWC_WARN("DWC OTG HCD - " "Unknown hub control request type or invalid typeReq: %xh wIndex: %xh wValue: %xh\n", _typeReq, _wIndex, _wValue); break; } return retval;}/** * Assigns transactions from a QTD to a free host channel and initializes the * host channel to perform the transactions. The host channel is removed from * the free list. * * @param _hcd The HCD state structure. * @param _qh Transactions from the first QTD for this QH are selected and * assigned to a free host channel. */static void assign_and_init_hc(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh){ dwc_hc_t *hc; dwc_otg_qtd_t *qtd; struct urb *urb; dbg_otg("%s(%p,%p)\n", __FUNCTION__, _hcd, _qh); hc = list_entry(_hcd->free_hc_list.next, dwc_hc_t, hc_list_entry); /* Remove the host channel from the free list. */ list_del_init(&hc->hc_list_entry); qtd = list_entry(_qh->qtd_list.next, dwc_otg_qtd_t, qtd_list_entry); urb = qtd->urb; _qh->channel = hc; _qh->qtd_in_process = qtd; /* * Use usb_pipedevice to determine device address. This address is * 0 before the SET_ADDRESS command and the correct address afterward. */ hc->dev_addr = usb_pipedevice(urb->pipe); hc->ep_num = usb_pipeendpoint(urb->pipe); if (urb->dev->speed == USB_SPEED_LOW) { hc->speed = DWC_OTG_EP_SPEED_LOW; } else if (urb->dev->speed == USB_SPEED_FULL) { hc->speed = DWC_OTG_EP_SPEED_FULL; } else { hc->speed = DWC_OTG_EP_SPEED_HIGH; } hc->max_packet = dwc_max_packet(_qh->maxp); hc->xfer_started = 0; hc->halt_status = DWC_OTG_HC_XFER_NO_HALT_STATUS; hc->error_state = (qtd->error_count > 0); hc->halt_on_queue = 0; hc->halt_pending = 0; hc->requests = 0; /* * The following values may be modified in the transfer type section * below. The xfer_len value may be reduced when the transfer is * started to accommodate the max widths of the XferSize and PktCnt * fields in the HCTSIZn register. */ hc->do_ping = _qh->ping_state; hc->ep_is_in = (usb_pipein(urb->pipe) != 0); hc->data_pid_start = _qh->data_toggle; hc->multi_count = 1; if (_hcd->core_if->dma_enable) { hc->xfer_buff = (uint8_t *) urb->transfer_dma + urb->actual_length; } else { hc->xfer_buff = (uint8_t *) urb->transfer_buffer + urb->actual_length; } hc->xfer_len = urb->transfer_buffer_length - urb->actual_length; hc->xfer_count = 0; /* * Set the split attributes */ hc->do_split = 0; if (_qh->do_split) { hc->do_split = 1; hc->xact_pos = qtd->isoc_split_pos; hc->complete_split = qtd->complete_split; hc->hub_addr = urb->dev->tt->hub->devnum; hc->port_addr = urb->dev->ttport; } switch (usb_pipetype(urb->pipe)) { case PIPE_CONTROL: hc->ep_type = DWC_OTG_EP_TYPE_CONTROL; switch (qtd->control_phase) { case DWC_OTG_CONTROL_SETUP: DWC_DEBUGPL(DBG_HCDV, " Control setup transaction\n"); hc->do_ping = 0; hc->ep_is_in = 0; hc->data_pid_start = DWC_OTG_HC_PID_SETUP; if (_hcd->core_if->dma_enable) { hc->xfer_buff = (uint8_t *) urb->setup_dma; } else { hc->xfer_buff = (uint8_t *) urb->setup_packet; } hc->xfer_len = 8; break; case DWC_OTG_CONTROL_DATA: DWC_DEBUGPL(DBG_HCDV, " Control data transaction\n"); hc->data_pid_start = qtd->data_toggle; break; case DWC_OTG_CONTROL_STATUS: /* * Direction is opposite of data direction or IN if no * data. */ DWC_DEBUGPL(DBG_HCDV, " Control status transaction\n"); if (urb->transfer_buffer_length == 0) { hc->ep_is_in = 1; } else { hc->ep_is_in = (usb_pipein(urb->pipe) != USB_DIR_IN); } if (hc->ep_is_in) { hc->do_ping = 0; } hc->data_pid_start = DWC_OTG_HC_PID_DATA1; hc->xfer_len = 0; if (_hcd->core_if->dma_enable) { hc->xfer_buff = (uint8_t *) _hcd->status_buf_dma; } else { hc->xfer_buff = (uint8_t *) _hcd->status_buf; } break; } break; case PIPE_BULK: hc->ep_type = DWC_OTG_EP_TYPE_BULK; break; case PIPE_INTERRUPT: hc->ep_type = DWC_OTG_EP_TYPE_INTR; break; case PIPE_ISOCHRONOUS: { struct usb_iso_packet_descriptor *frame_desc; frame_desc = &urb->iso_frame_desc[qtd->isoc_frame_index]; hc->ep_type = DWC_OTG_EP_TYPE_ISOC; if (_hcd->core_if->dma_enable) { hc->xfer_buff = (uint8_t *) urb->transfer_dma; } else { hc->xfer_buff = (uint8_t *) urb->transfer_buffer; } hc->xfer_buff += frame_desc->offset + qtd->isoc_split_offset; hc->xfer_len = frame_desc->length - qtd->isoc_split_offset; if (hc->xact_pos == DWC_HCSPLIT_XACTPOS_ALL) { if (hc->xfer_len <= 188) { hc->xact_pos = DWC_HCSPLIT_XACTPOS_ALL; } else { hc->xact_pos = DWC_HCSPLIT_XACTPOS_BEGIN; } } } break; } if (hc->ep_type == DWC_OTG_EP_TYPE_INTR || hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { /* * This value may be modified when the transfer is started to * reflect the actual transfer length. */ hc->multi_count = dwc_hb_mult(_qh->maxp); } dwc_otg_hc_init(_hcd->core_if, hc); hc->qh = _qh;}/** * This function selects transactions from the HCD transfer schedule and * assigns them to available host channels. It is called from HCD interrupt * handler functions. * * @param _hcd The HCD state structure. * * @return The types of new transactions that were assigned to host channels. */dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * _hcd){ struct list_head *qh_ptr; dwc_otg_qh_t *qh; int num_channels; dwc_otg_transaction_type_e ret_val = DWC_OTG_TRANSACTION_NONE;#ifdef DEBUG_SOF DWC_DEBUGPL(DBG_HCD, " Select Transactions\n");#endif /* Process entries in the periodic ready list. */ qh_ptr = _hcd->periodic_sched_ready.next; while (qh_ptr != &_hcd->periodic_sched_ready && !list_empty(&_hcd->free_hc_list)) { qh = list_entry(qh_ptr, dwc_otg_qh_t, qh_list_entry); assign_and_init_hc(_hcd, qh); /* * Move the QH from the periodic ready schedule to the * periodic assigned schedule. */ qh_ptr = qh_ptr->next; list_move(&qh->qh_list_entry, &_hcd->periodic_sched_assigned); ret_val = DWC_OTG_TRANSACTION_PERIODIC; } /* * Process entries in the inactive portion of the non-periodic * schedule. Some free host channels may not be used if they are * reserved for periodic transfers. */ qh_ptr = _hcd->non_periodic_sched_inactive.next; num_channels = _hcd->core_if->core_params->host_channels; while (qh_ptr != &_hcd->non_periodic_sched_inactive && (_hcd->non_periodic_channels < num_channels - _hcd->periodic_channels
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -