3572 lines
105 KiB
C
3572 lines
105 KiB
C
/** @file
|
|
Implementation of XDCI APIs.
|
|
|
|
@copyright
|
|
INTEL CONFIDENTIAL
|
|
Copyright 2015 - 2020 Intel Corporation.
|
|
|
|
The source code contained or described herein and all documents related to the
|
|
source code ("Material") are owned by Intel Corporation or its suppliers or
|
|
licensors. Title to the Material remains with Intel Corporation or its suppliers
|
|
and licensors. The Material may contain trade secrets and proprietary and
|
|
confidential information of Intel Corporation and its suppliers and licensors,
|
|
and is protected by worldwide copyright and trade secret laws and treaty
|
|
provisions. No part of the Material may be used, copied, reproduced, modified,
|
|
published, uploaded, posted, transmitted, distributed, or disclosed in any way
|
|
without Intel's prior express written permission.
|
|
|
|
No license under any patent, copyright, trade secret or other intellectual
|
|
property right is granted to or conferred upon you by disclosure or delivery
|
|
of the Materials, either expressly, by implication, inducement, estoppel or
|
|
otherwise. Any license under such intellectual property rights must be
|
|
express and approved by Intel in writing.
|
|
|
|
Unless otherwise agreed by Intel in writing, you may not remove or alter
|
|
this notice or any other notice embedded in Materials by Intel or
|
|
Intel's suppliers or licensors in any way.
|
|
|
|
This file contains a 'Sample Driver' and is licensed as such under the terms
|
|
of your license agreement with Intel or your vendor. This file may be modified
|
|
by the user, subject to the additional terms of the license agreement.
|
|
|
|
@par Specification Reference:
|
|
**/
|
|
|
|
#include "UsbDeviceMode.h"
|
|
#include "XdciInterface.h"
|
|
#include "XdciDWC.h"
|
|
|
|
#if defined(_MSC_VER) /* MSVC Compiler section */
|
|
#pragma optimize ("", off)
|
|
#endif
|
|
|
|
UINT32
|
|
usb_reg_read (
|
|
IN UINTN base,
|
|
IN UINT32 offset
|
|
)
|
|
{
|
|
volatile UINT32 *addr = (volatile UINT32 *)(UINTN)(base + offset);
|
|
return *addr;
|
|
}
|
|
|
|
VOID
|
|
usb_reg_write (
|
|
IN UINTN base,
|
|
IN UINT32 offset,
|
|
IN UINT32 val
|
|
)
|
|
{
|
|
volatile UINT32 *addr = (volatile UINT32 *)(UINTN)(base + offset);
|
|
*addr = val;
|
|
}
|
|
|
|
|
|
/**
|
|
Internal utility function:
|
|
This function is used to obtain physical endpoint number
|
|
xDCI needs physical endpoint number for EP registers
|
|
We also use it to index into our EP array
|
|
Note: Certain data structures/commands use logical EP numbers
|
|
as opposed to physical endpoint numbers so one should be
|
|
careful when interpreting EP numbers
|
|
@ep_num: Logical endpoint number
|
|
@ep_dir: Direction for the endpoint
|
|
|
|
**/
|
|
STATIC
|
|
UINT32
|
|
dwc_xdci_get_physical_ep_num (
|
|
IN UINT32 EndpointNum,
|
|
IN USB_EP_DIR EndpointDir
|
|
)
|
|
{
|
|
return EndpointDir? ((EndpointNum << 1) | EndpointDir) : (EndpointNum << 1);
|
|
}
|
|
|
|
|
|
/**
|
|
Internal utility function:
|
|
This function is used to obtain the MPS for control transfers
|
|
based on the speed. If this is called before bus reset completes
|
|
then it returns MPS based on desired speed. If it is after bus
|
|
reset then MPS returned is based on actual negotiated speed
|
|
@core_handle: xDCI controller handle address
|
|
@mps: address of 32-bit variable to return the MPS
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
dwc_xdci_core_get_ctrl_mps (
|
|
IN XDCI_CORE_HANDLE *core_handle,
|
|
IN UINT32 *mps
|
|
)
|
|
{
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_core_get_ctrl_mps: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
if (mps == NULL) {
|
|
DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_core_get_ctrl_mps: INVALID parameter\n"));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
switch (core_handle->actual_speed) {
|
|
case USB_SPEED_HIGH:
|
|
*mps = DWC_XDCI_HS_CTRL_EP_MPS;
|
|
break;
|
|
case USB_SPEED_FULL:
|
|
*mps = DWC_XDCI_FS_CTRL_EP_MPS;
|
|
break;
|
|
case USB_SPEED_LOW:
|
|
*mps = DWC_XDCI_LS_CTRL_EP_MPS;
|
|
break;
|
|
case USB_SPEED_SUPER:
|
|
*mps = DWC_XDCI_SS_CTRL_EP_MPS;
|
|
break;
|
|
default:
|
|
*mps = 0;
|
|
DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_core_get_ctrl_mps: UNKNOWN speed\n"));
|
|
break;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Internal utility function:
|
|
This function is used to initialize the parameters required
|
|
for executing endpoint command
|
|
@core_handle: xDCI controller handle address
|
|
@ep_info: EP info address
|
|
@config_action: Configuration action specific to EP command
|
|
@ep_cmd: xDCI EP command for which parameters are initialized
|
|
@ep_cmd_params: address of struct to return EP params
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
dwc_xdci_core_init_ep_cmd_params (
|
|
IN XDCI_CORE_HANDLE *core_handle,
|
|
IN USB_EP_INFO *ep_info,
|
|
IN UINT32 config_action,
|
|
IN DWC_XDCI_ENDPOINT_CMD ep_cmd,
|
|
IN DWC_XDCI_ENDPOINT_CMD_PARAMS *ep_cmd_params
|
|
)
|
|
{
|
|
EFI_STATUS status = EFI_SUCCESS;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_core_init_ep_cmd_params: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
/* Reset params */
|
|
ep_cmd_params->param0 = ep_cmd_params->param1 = ep_cmd_params->param2 = 0;
|
|
|
|
switch (ep_cmd) {
|
|
case EPCMD_SET_EP_CONFIG:
|
|
/* Issue DEPCFG command for EP */
|
|
/* Issue a DEPCFG (Command 1) command for endpoint */
|
|
|
|
if (ep_info->max_streams) {
|
|
ep_cmd_params->param1 = DWC_XDCI_PARAM1_SET_EP_CFG_STRM_CAP_MASK;
|
|
}
|
|
|
|
if (ep_info->interval) {
|
|
ep_cmd_params->param1 |= ((ep_info->interval-1) << DWC_XDCI_PARAM1_SET_EP_CFG_BINTM1_BIT_POS);
|
|
}
|
|
|
|
/* Set EP num */
|
|
ep_cmd_params->param1 |= (ep_info->ep_num << DWC_XDCI_PARAM1_SET_EP_CFG_EP_NUM_BIT_POS);
|
|
|
|
/* Set EP direction */
|
|
ep_cmd_params->param1 |= (ep_info->ep_dir << DWC_XDCI_PARAM1_SET_EP_CFG_EP_DIR_BIT_POS);
|
|
|
|
/* Set EP-specific Event enable for not ready and
|
|
* complete events
|
|
*/
|
|
ep_cmd_params->param1 &= ~DWC_XDCI_PARAM1_SET_EP_CFG_EVT_EN_MASK;
|
|
|
|
/* Setup the events we want enabled for this EP */
|
|
ep_cmd_params->param1 |= (DWC_XDCI_PARAM1_SET_EP_CFG_EVT_XFER_NRDY_MASK |
|
|
DWC_XDCI_PARAM1_SET_EP_CFG_EVT_XFER_IN_PRG_MASK |
|
|
DWC_XDCI_PARAM1_SET_EP_CFG_EVT_XFER_CMPLT_MASK);
|
|
|
|
/* We only have one interrupt line for this core.
|
|
* Set interrupt number to 0
|
|
*/
|
|
ep_cmd_params->param1 &= ~DWC_XDCI_PARAM1_SET_EP_CFG_INTR_NUM_MASK;
|
|
|
|
/* Set FIFOnum = 0 for control EP0 */
|
|
ep_cmd_params->param0 &= ~DWC_XDCI_PARAM0_SET_EP_CFG_FIFO_NUM_MASK;
|
|
|
|
/* Program FIFOnum for non-EP0 EPs */
|
|
if (ep_info->ep_num && ep_info->ep_dir) {
|
|
ep_cmd_params->param0 |= (ep_info->ep_num << DWC_XDCI_PARAM0_SET_EP_CFG_FIFO_NUM_BIT_POS);
|
|
}
|
|
|
|
/* Program max packet size */
|
|
ep_cmd_params->param0 &= ~DWC_XDCI_PARAM0_SET_EP_CFG_MPS_MASK;
|
|
ep_cmd_params->param0 |= (ep_info->max_pkt_size << DWC_XDCI_PARAM0_SET_EP_CFG_MPS_BIT_POS);
|
|
|
|
/* Set Burst size. 0 means burst size of 1 */
|
|
ep_cmd_params->param0 &= ~DWC_XDCI_PARAM0_SET_EP_CFG_BRST_SIZE_MASK;
|
|
ep_cmd_params->param0 |= (ep_info->burst_size << DWC_XDCI_PARAM0_SET_EP_CFG_BRST_SIZE_BIT_POS);
|
|
|
|
/* Set EP type */
|
|
ep_cmd_params->param0 &= ~DWC_XDCI_PARAM0_SET_EP_CFG_EP_TYPE_MASK;
|
|
ep_cmd_params->param0 |= (ep_info->ep_type << DWC_XDCI_PARAM0_SET_EP_CFG_EP_TYPE_BIT_POS);
|
|
|
|
/* Set config action */
|
|
ep_cmd_params->param0 &= ~DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_MASK;
|
|
ep_cmd_params->param0 |= (config_action << DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_BIT_POS);
|
|
break;
|
|
|
|
case EPCMD_SET_EP_XFER_RES_CONFIG:
|
|
/* Set param0 to 1. Same for all EPs when resource
|
|
* configuration is done
|
|
*/
|
|
ep_cmd_params->param0 = 1;
|
|
break;
|
|
|
|
case EPCMD_END_XFER:
|
|
/* Nothing to set. Already reset params for all cmds */
|
|
break;
|
|
|
|
case EPCMD_START_NEW_CONFIG:
|
|
/* Nothing to set. Already reset params for all cmds */
|
|
break;
|
|
|
|
default:
|
|
status = EFI_INVALID_PARAMETER;
|
|
DEBUG ((DEBUG_INFO, "\ndwc_xdci_core_init_ep_cmd_params: INVALID Parameter"));
|
|
break;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/**
|
|
Internal utility function:
|
|
This function is used to issue the xDCI endpoint command
|
|
@core_handle: xDCI controller handle address
|
|
@ep_num: Physical EP num
|
|
@ep_cmd: xDCI EP command
|
|
@ep_cmd_params: EP command parameters address
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
dwc_xdci_core_issue_ep_cmd (
|
|
IN XDCI_CORE_HANDLE *core_handle,
|
|
IN UINT32 ep_num,
|
|
IN UINT32 ep_cmd,
|
|
IN DWC_XDCI_ENDPOINT_CMD_PARAMS *ep_cmd_params
|
|
)
|
|
{
|
|
UINTN base_addr;
|
|
UINT32 max_delay_iter = 5000;//DWC_XDCI_MAX_DELAY_ITERATIONS;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_core_issue_ep_cmd: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
base_addr = core_handle->base_address;
|
|
|
|
/* Set EP command parameter values */
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_EPCMD_PARAM2_REG(ep_num),
|
|
ep_cmd_params->param2
|
|
);
|
|
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_EPCMD_PARAM1_REG(ep_num),
|
|
ep_cmd_params->param1
|
|
);
|
|
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_EPCMD_PARAM0_REG(ep_num),
|
|
ep_cmd_params->param0
|
|
);
|
|
|
|
/* Set the command code and activate it */
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_EPCMD_REG(ep_num),
|
|
ep_cmd | DWC_XDCI_EPCMD_CMD_ACTIVE_MASK
|
|
);
|
|
|
|
/* Wait until command completes */
|
|
do {
|
|
if (!(usb_reg_read (base_addr, DWC_XDCI_EPCMD_REG(ep_num)) & DWC_XDCI_EPCMD_CMD_ACTIVE_MASK))
|
|
break;
|
|
else
|
|
gBS->Stall (DWC_XDCI_MAX_DELAY_ITERATIONS);
|
|
} while (--max_delay_iter);
|
|
|
|
if (!max_delay_iter) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_issue_ep_cmd. ERROR: Failed to issue Command 0x%08x\n",\
|
|
usb_reg_read (base_addr, DWC_XDCI_EPCMD_REG(ep_num))
|
|
));
|
|
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Internal utility function:
|
|
This function is used to flush all FIFOs
|
|
@core_handle: xDCI controller handle address
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
dwc_xdci_core_flush_all_fifos (
|
|
IN XDCI_CORE_HANDLE *core_handle
|
|
)
|
|
{
|
|
UINTN base_addr;
|
|
UINT32 max_delay_iter = DWC_XDCI_MAX_DELAY_ITERATIONS;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_core_flush_all_fifos: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
base_addr = core_handle->base_address;
|
|
|
|
/* Write the command to flush all FIFOs */
|
|
usb_reg_write(
|
|
base_addr,
|
|
DWC_XDCI_DGCMD_REG,
|
|
(usb_reg_read (base_addr, DWC_XDCI_DGCMD_REG) | DWC_XDCI_DGCMD_CMD_ALL_FIFO_FLUSH | DWC_XDCI_DGCMD_CMD_ACTIVE_MASK)
|
|
);
|
|
|
|
/* Wait until command completes */
|
|
do {
|
|
if (!(usb_reg_read (base_addr, DWC_XDCI_DGCMD_REG) & DWC_XDCI_DGCMD_CMD_ACTIVE_MASK))
|
|
break;
|
|
else
|
|
gBS->Stall (DWC_XDCI_MAX_DELAY_ITERATIONS);
|
|
} while (--max_delay_iter);
|
|
|
|
if (!max_delay_iter) {
|
|
DEBUG ((DEBUG_INFO, "Failed to issue Command\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Internal utility function:
|
|
This function is used to flush Tx FIFO specific to an endpoint
|
|
@core_handle: xDCI controller handle address
|
|
@ep_num: Physical EP num
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
dwc_xdci_core_flush_ep_tx_fifo (
|
|
IN XDCI_CORE_HANDLE *core_handle,
|
|
IN UINT32 ep_num
|
|
)
|
|
{
|
|
UINTN base_addr;
|
|
UINT32 max_delay_iter = DWC_XDCI_MAX_DELAY_ITERATIONS;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_core_flush_ep_tx_fifo: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
base_addr = core_handle->base_address;
|
|
|
|
|
|
/* TODO: Currently we are only using TxFIFO 0. Later map these
|
|
* Write the FIFO num/dir param for the generic command.
|
|
*/
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_DGCMD_PARAM_REG,
|
|
((usb_reg_read (base_addr, DWC_XDCI_DGCMD_PARAM_REG) & ~DWC_XDCI_DGCMD_PARAM_TX_FIFO_NUM_MASK) | DWC_XDCI_DGCMD_PARAM_TX_FIFO_DIR_MASK)
|
|
);
|
|
|
|
/* Write the command to flush all FIFOs */
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_DGCMD_REG,
|
|
(usb_reg_read(base_addr, DWC_XDCI_DGCMD_REG) | DWC_XDCI_DGCMD_CMD_SEL_FIFO_FLUSH | DWC_XDCI_DGCMD_CMD_ACTIVE_MASK)
|
|
);
|
|
|
|
|
|
/* Wait until command completes */
|
|
do {
|
|
if (!(usb_reg_read(base_addr, DWC_XDCI_DGCMD_REG) & DWC_XDCI_DGCMD_CMD_ACTIVE_MASK))
|
|
break;
|
|
else
|
|
gBS->Stall (DWC_XDCI_MAX_DELAY_ITERATIONS);
|
|
} while (--max_delay_iter);
|
|
|
|
if (!max_delay_iter) {
|
|
DEBUG ((DEBUG_INFO, "Failed to issue Command\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
core_handle->ep_handles[ep_num].state = USB_EP_STATE_ENABLED;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
dwc_xdci_core_prepare_one_trb (
|
|
IN DWC_XDCI_TRB *trb,
|
|
IN DWC_XDCI_TRB_CONTROL trb_ctrl,
|
|
IN UINT32 LastBit,
|
|
IN UINT32 ChainBit,
|
|
IN UINT8 *buffer_ptr,
|
|
IN UINT32 size
|
|
)
|
|
{
|
|
DEBUG ((DEBUG_INFO, "trb is 0x%x, buffer_ptr is 0x%x, size is 0x%x\n", trb, buffer_ptr, size));
|
|
|
|
trb->buff_ptr_low = (UINT32)(UINTN)buffer_ptr;
|
|
trb->buff_ptr_high = 0;
|
|
trb->len_xfer_params = size;
|
|
trb->trb_ctrl = trb_ctrl << DWC_XDCI_TRB_CTRL_TYPE_BIT_POS;
|
|
|
|
if (ChainBit)
|
|
trb->trb_ctrl |= ChainBit << DWC_XDCI_TRB_CTRL_CHAIN_BUFF_BIT_POS;
|
|
|
|
if (LastBit)
|
|
trb->trb_ctrl |= LastBit << DWC_XDCI_TRB_CTRL_LST_TRB_BIT_POS;
|
|
|
|
trb->trb_ctrl |= DWC_XDCI_TRB_CTRL_IOSP_MISOCH_MASK| DWC_XDCI_TRB_CTRL_HWO_MASK;
|
|
|
|
DEBUG ((DEBUG_INFO, "(dwc_xdci_core_prepare_one_trb) ptr_low = 0x%x, ptr_high = 0x%x, len_params is 0x%x, trb_ctrl is 0x%x\n",
|
|
trb->buff_ptr_low, trb->buff_ptr_high, trb->len_xfer_params, (UINT32)trb->trb_ctrl));
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Internal utility function:
|
|
This function is used to initialize transfer request block
|
|
@core_handle: xDCI controller handle address
|
|
@trb: Address of TRB to initialize
|
|
@trb_ctrl: TRB control value
|
|
@buff_ptr: Transfer buffer address
|
|
@size: Size of the transfer
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
dwc_xdci_core_init_trb (
|
|
IN XDCI_CORE_HANDLE *core_handle,
|
|
IN DWC_XDCI_TRB *trb,
|
|
IN DWC_XDCI_TRB_CONTROL trb_ctrl,
|
|
IN UINT8 *buffer_ptr,
|
|
IN UINT32 size
|
|
)
|
|
{
|
|
#define ONE_TRB_SIZE (DWC_XDCI_TRB_BUFF_SIZE_MASK & 0x00F00000)
|
|
UINT8 *TrbBuffer;
|
|
UINT32 TrbCtrlLast;
|
|
UINT32 TrbCtrlChain;
|
|
UINT32 TrbIndex;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_core_init_trb: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
if (trb == NULL) {
|
|
DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_core_init_trb: INVALID handle\n"));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Init TRB fields
|
|
* NOTE: Assuming we are only using 32-bit addresses
|
|
* TODO: update for 64-bit addresses
|
|
*/
|
|
if (size <= DWC_XDCI_TRB_BUFF_SIZE_MASK) {
|
|
//
|
|
// Can transfer in one TRB
|
|
//
|
|
TrbCtrlChain = 0;
|
|
TrbCtrlLast = 1;
|
|
dwc_xdci_core_prepare_one_trb (trb, trb_ctrl, TrbCtrlLast, TrbCtrlChain, buffer_ptr, size);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Can't transfer in one TRB.
|
|
// Seperate it in every ONE_TRB_SIZE of TRB
|
|
//
|
|
TrbBuffer = buffer_ptr;
|
|
TrbIndex = 0;
|
|
while (size > ONE_TRB_SIZE) {
|
|
TrbCtrlChain = 1;
|
|
TrbCtrlLast = 0;
|
|
dwc_xdci_core_prepare_one_trb (trb, trb_ctrl, TrbCtrlLast, TrbCtrlChain, TrbBuffer, ONE_TRB_SIZE);
|
|
TrbBuffer += ONE_TRB_SIZE;
|
|
size -= ONE_TRB_SIZE;
|
|
trb++;
|
|
TrbIndex++;
|
|
if (TrbIndex >= DWC_XDCI_TRB_NUM)
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
TrbCtrlChain = 0;
|
|
TrbCtrlLast = 1;
|
|
dwc_xdci_core_prepare_one_trb (trb, trb_ctrl, TrbCtrlLast, TrbCtrlChain, TrbBuffer, size);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Internal function:
|
|
This function is used to start a SETUP phase on control endpoint
|
|
@core_handle: xDCI controller handle address
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
dwc_xdci_core_start_ep0_setup_xfer (
|
|
IN XDCI_CORE_HANDLE *core_handle
|
|
)
|
|
{
|
|
DWC_XDCI_ENDPOINT_CMD_PARAMS ep_cmd_params;
|
|
EFI_STATUS status = EFI_DEVICE_ERROR;
|
|
DWC_XDCI_TRB *trb;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_core_start_ep0_setup_xfer: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
if (core_handle->ep_handles[0].state == USB_EP_STATE_SETUP) {
|
|
DEBUG ((DEBUG_INFO, "EP0 was already in SETUP phase\n"));
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
core_handle->ep_handles[0].state = USB_EP_STATE_SETUP;
|
|
trb = core_handle->trbs;
|
|
DEBUG ((DEBUG_INFO, "(dwc_xdci_core_start_ep0_setup_xfer)\n"));
|
|
|
|
status = dwc_xdci_core_init_trb (
|
|
core_handle,
|
|
trb,
|
|
TRBCTL_SETUP,
|
|
core_handle->aligned_setup_buffer,
|
|
8
|
|
);
|
|
|
|
if (status)
|
|
return status;
|
|
|
|
//
|
|
// Issue a DEPSTRTXFER for EP0
|
|
// Reset params
|
|
//
|
|
ep_cmd_params.param0 = ep_cmd_params.param1 = ep_cmd_params.param2 = 0;
|
|
|
|
//
|
|
// Init the lower re-bits for TRB address
|
|
//
|
|
ep_cmd_params.param1 = (UINT32)(UINTN)trb;
|
|
|
|
//
|
|
// Issue the command to start transfer on physical
|
|
// endpoint 0
|
|
//
|
|
status = dwc_xdci_core_issue_ep_cmd (
|
|
core_handle,
|
|
0,
|
|
EPCMD_START_XFER,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
/* Save new resource index for this transfer */
|
|
core_handle->ep_handles[0].currentXferRscIdx = ((usb_reg_read (
|
|
core_handle->base_address,
|
|
DWC_XDCI_EPCMD_REG(0)) & DWC_XDCI_EPCMD_RES_IDX_MASK) >> DWC_XDCI_EPCMD_RES_IDX_BIT_POS
|
|
);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/**
|
|
Internal function:
|
|
This function is used to process the state change event
|
|
@core_handle: xDCI controller handle address
|
|
@event: device event dword
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
dwc_xdci_process_device_state_change_event (
|
|
IN XDCI_CORE_HANDLE *core_handle,
|
|
IN UINT32 event
|
|
)
|
|
{
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_process_device_state_change_event: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
core_handle->hird_val = (event & DWC_XDCI_EVENT_BUFF_DEV_HIRD_MASK) >> DWC_XDCI_EVENT_BUFF_DEV_HIRD_BIT_POS;
|
|
|
|
core_handle->link_state = ((event & DWC_XDCI_EVENT_BUFF_DEV_LINK_STATE_MASK) >> DWC_XDCI_EVENT_BUFF_DEV_LINK_STATE_BIT_POS);
|
|
|
|
if (core_handle->event_callbacks.dev_link_state_callback) {
|
|
core_handle->event_callbacks.cb_event_params.parent_handle = core_handle->parent_handle;
|
|
core_handle->event_callbacks.cb_event_params.link_state = core_handle->link_state;
|
|
core_handle->event_callbacks.cb_event_params.hird = core_handle->hird_val;
|
|
core_handle->event_callbacks.cb_event_params.ss_event = (event & DWC_XDCI_EVENT_BUFF_DEV_SS_EVENT_MASK) ? 1 : 0;
|
|
core_handle->event_callbacks.dev_link_state_callback (&core_handle->event_callbacks.cb_event_params);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Internal function:
|
|
This function is used to issue a command to end transfer
|
|
@core_handle: xDCI controller handle address
|
|
@ep_num: Physical EP num for which transfer is to be ended
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
dwc_xdci_end_xfer (
|
|
IN XDCI_CORE_HANDLE *core_handle,
|
|
IN UINT32 ep_num
|
|
)
|
|
{
|
|
EFI_STATUS status;
|
|
DWC_XDCI_ENDPOINT_CMD_PARAMS ep_cmd_params;
|
|
UINT32 cmd_params;
|
|
DWC_XDCI_TRB *TrbPtr;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_end_xfer: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
core_handle->ep_handles[ep_num].CheckFlag = FALSE;
|
|
|
|
/* Issue a DEPENDXFER for EP */
|
|
/* Reset params */
|
|
ep_cmd_params.param0 = ep_cmd_params.param1 = ep_cmd_params.param2 = 0;
|
|
|
|
cmd_params = ((core_handle->ep_handles[ep_num].currentXferRscIdx << DWC_XDCI_EPCMD_RES_IDX_BIT_POS) | DWC_XDCI_EPCMD_FORCE_RM_MASK);
|
|
|
|
if (core_handle->ep_handles[ep_num].currentXferRscIdx == 0) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
/* Issue the command */
|
|
status = dwc_xdci_core_issue_ep_cmd(
|
|
core_handle,
|
|
ep_num,
|
|
cmd_params | DWC_XDCI_EPCMD_END_XFER,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (!status) {
|
|
core_handle->ep_handles[ep_num].currentXferRscIdx = 0;
|
|
TrbPtr = core_handle->trbs + (ep_num * DWC_XDCI_TRB_NUM);
|
|
ZeroMem (TrbPtr, DWC_XDCI_TRB_NUM * sizeof (DWC_XDCI_TRB));
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/**
|
|
Internal function:
|
|
This function is used to process bus reset detection event
|
|
@core_handle: xDCI controller handle address
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
dwc_xdci_process_device_reset_det (
|
|
IN XDCI_CORE_HANDLE *core_handle
|
|
)
|
|
{
|
|
EFI_STATUS status = EFI_SUCCESS;
|
|
|
|
if (core_handle == NULL) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
/* Flush all FIFOs */
|
|
status = dwc_xdci_core_flush_all_fifos(core_handle);
|
|
if (status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_process_device_reset_det: Failed to flush FIFOs\n"));
|
|
}
|
|
|
|
/* NOTE: Not treating flush FIFOs status to be fatal */
|
|
|
|
/* Start SETUP phase on EP0 */
|
|
status = dwc_xdci_core_start_ep0_setup_xfer(core_handle);
|
|
|
|
if (status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_process_device_reset_det: Failed to start SETUP phase for EP0\n"));
|
|
return status;
|
|
}
|
|
|
|
/* Notify upper layer if a callback is registerd for
|
|
* this event
|
|
*/
|
|
if (core_handle->event_callbacks.dev_bus_reset_callback) {
|
|
core_handle->event_callbacks.cb_event_params.parent_handle = core_handle->parent_handle;
|
|
status = core_handle->event_callbacks.dev_bus_reset_callback (&core_handle->event_callbacks.cb_event_params);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/**
|
|
Internal function:
|
|
This function is used to process connection done (means reset
|
|
complete) event
|
|
@core_handle: xDCI controller handle address
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
dwc_xdci_process_device_reset_done (
|
|
IN XDCI_CORE_HANDLE *core_handle
|
|
)
|
|
{
|
|
DWC_XDCI_ENDPOINT_CMD_PARAMS ep_cmd_params;
|
|
UINTN base_addr;
|
|
EFI_STATUS status = EFI_SUCCESS;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_process_device_reset_done: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
base_addr = core_handle->base_address;
|
|
core_handle->actual_speed = (usb_reg_read (base_addr, DWC_XDCI_DSTS_REG) & DWC_XDCI_DSTS_CONN_SPEED_MASK);
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_process_device_reset_done core_handle->actual_speed is %x\n", core_handle->actual_speed));
|
|
|
|
/* Program MPS based on the negotiated speed */
|
|
dwc_xdci_core_get_ctrl_mps (core_handle, &core_handle->ep_handles[0].ep_info.max_pkt_size);
|
|
dwc_xdci_core_get_ctrl_mps (core_handle, &core_handle->ep_handles[1].ep_info.max_pkt_size);
|
|
|
|
/* Init DEPCFG cmd params for EP0 */
|
|
status = dwc_xdci_core_init_ep_cmd_params (
|
|
core_handle,
|
|
&core_handle->ep_handles[0].ep_info,
|
|
DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_MDFY_STATE,
|
|
EPCMD_SET_EP_CONFIG,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (status) {
|
|
return status;
|
|
}
|
|
|
|
/* Issue the command */
|
|
status = dwc_xdci_core_issue_ep_cmd (
|
|
core_handle,
|
|
0,
|
|
EPCMD_SET_EP_CONFIG,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (status) {
|
|
return status;
|
|
}
|
|
|
|
/* Init DEPCFG cmd params for EP1 */
|
|
status = dwc_xdci_core_init_ep_cmd_params (
|
|
core_handle,
|
|
&core_handle->ep_handles[1].ep_info,
|
|
DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_MDFY_STATE,
|
|
EPCMD_SET_EP_CONFIG,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
/* Issue the command */
|
|
status = dwc_xdci_core_issue_ep_cmd (
|
|
core_handle,
|
|
1,
|
|
EPCMD_SET_EP_CONFIG,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
/* Put the other PHY into suspend */
|
|
if (core_handle->actual_speed == USB_SPEED_SUPER) {
|
|
/* Put HS PHY to suspend */
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_GUSB2PHYCFG_REG (0),
|
|
(usb_reg_read (base_addr, DWC_XDCI_GUSB2PHYCFG_REG(0)) | DWC_XDCI_GUSB2PHYCFG_SUSPEND_PHY_MASK)
|
|
);
|
|
|
|
/* SS PHY's suspend mask */
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_GUSB3PIPECTL_REG (0),
|
|
(usb_reg_read (base_addr, DWC_XDCI_GUSB3PIPECTL_REG(0)) | DWC_XDCI_GUSB3PIPECTL_SUSPEND_PHY_MASK)
|
|
);
|
|
|
|
} else {
|
|
/* Put SS PHY to suspend */
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_GUSB3PIPECTL_REG(0),
|
|
(usb_reg_read(base_addr, DWC_XDCI_GUSB3PIPECTL_REG(0)) | DWC_XDCI_GUSB3PIPECTL_SUSPEND_PHY_MASK)
|
|
);
|
|
|
|
/* Clear HS PHY's suspend mask */
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_GUSB2PHYCFG_REG(0),
|
|
(usb_reg_read(base_addr, DWC_XDCI_GUSB2PHYCFG_REG(0)) & ~DWC_XDCI_GUSB2PHYCFG_SUSPEND_PHY_MASK)
|
|
);
|
|
}
|
|
|
|
/* Notify upper layer if callback is registered */
|
|
if (core_handle->event_callbacks.dev_reset_done_callback) {
|
|
core_handle->event_callbacks.cb_event_params.parent_handle = core_handle->parent_handle;
|
|
core_handle->event_callbacks.cb_event_params.speed = core_handle->actual_speed;
|
|
core_handle->event_callbacks.dev_reset_done_callback (&core_handle->event_callbacks.cb_event_params);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/**
|
|
Internal function:
|
|
This function is used to process device event
|
|
@core_handle: xDCI controller handle address
|
|
@int_line_event_buffer: event buffer pointing to device event
|
|
@processed_event_size: address of variable to save the size of
|
|
the event that was processed
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
dwc_xdci_process_device_event (
|
|
IN XDCI_CORE_HANDLE *core_handle,
|
|
IN DWC_XDCI_EVENT_BUFFER *int_line_event_buffer,
|
|
IN UINT32 *processed_event_size
|
|
)
|
|
{
|
|
UINT32 event;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_process_device_event: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
/* Extract device event */
|
|
event = (int_line_event_buffer->event & DWC_XDCI_EVENT_BUFF_DEV_EVT_MASK);
|
|
event >>= DWC_XDCI_EVENT_BUFF_DEV_EVT_BIT_POS;
|
|
|
|
/* Assume default event size. Change it in switch case if
|
|
* different
|
|
*/
|
|
*processed_event_size = DWC_XDCI_DEV_EVENT_DEFAULT_SIZE_IN_BYTES;
|
|
|
|
switch (event) {
|
|
case DWC_XDCI_EVENT_BUFF_DEV_DISCONN_EVENT:
|
|
DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_DISCONN_EVENT\n"));
|
|
break;
|
|
|
|
case DWC_XDCI_EVENT_BUFF_DEV_USB_RESET_EVENT:
|
|
DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_USB_RESET_EVENT\n"));
|
|
dwc_xdci_process_device_reset_det (core_handle);
|
|
break;
|
|
|
|
case DWC_XDCI_EVENT_BUFF_DEV_CONN_DONE_EVENT:
|
|
DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_CONN_DONE_EVENT\n"));
|
|
dwc_xdci_process_device_reset_done (core_handle);
|
|
break;
|
|
|
|
case DWC_XDCI_EVENT_BUFF_DEV_STATE_CHANGE_EVENT:
|
|
DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_STATE_CHANGE_EVENT\n"));
|
|
dwc_xdci_process_device_state_change_event (core_handle, int_line_event_buffer->event);
|
|
break;
|
|
|
|
case DWC_XDCI_EVENT_BUFF_DEV_WKUP_EVENT:
|
|
DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_WKUP_EVENT\n"));
|
|
break;
|
|
|
|
case DWC_XDCI_EVENT_BUFF_DEV_HBRNTN_REQ_EVENT:
|
|
DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_HBRNTN_REQ_EVENT\n"));
|
|
break;
|
|
|
|
case DWC_XDCI_EVENT_BUFF_DEV_SOF_EVENT:
|
|
DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_SOF_EVENT\n"));
|
|
break;
|
|
|
|
case DWC_XDCI_EVENT_BUFF_DEV_ERRATIC_ERR_EVENT:
|
|
DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_ERRATIC_ERR_EVENT\n"));
|
|
break;
|
|
|
|
case DWC_XDCI_EVENT_BUFF_DEV_CMD_CMPLT_EVENT:
|
|
DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_CMD_CMPLT_EVENT\n"));
|
|
break;
|
|
|
|
case DWC_XDCI_EVENT_BUFF_DEV_BUFF_OVFL_EVENT:
|
|
DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_BUFF_OVFL_EVENT\n"));
|
|
break;
|
|
|
|
case DWC_XDCI_EVENT_BUFF_DEV_TST_LMP_RX_EVENT:
|
|
DEBUG ((DEBUG_INFO, "Device DWC_XDCI_EVENT_BUFF_DEV_TST_LMP_RX_EVENT\n"));
|
|
*processed_event_size = DWC_XDCI_DEV_EVENT_TST_LMP_SIZE_IN_BYTES;
|
|
break;
|
|
|
|
default:
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_process_device_event: UNHANDLED device event: %x\n", event));
|
|
break;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Internal function:
|
|
This function is used to process EP not ready for
|
|
non-control endpoints
|
|
@core_handle: xDCI controller handle address
|
|
@ep_num: Physical endpoint number
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
dwc_xdci_process_ep_xfer_not_ready (
|
|
IN XDCI_CORE_HANDLE *core_handle,
|
|
IN UINT32 ep_num
|
|
)
|
|
{
|
|
/* TODO: Not doing on-demand transfers
|
|
* Revisit if required for later use
|
|
*/
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Internal function:
|
|
This function is used to process EP not ready for
|
|
control endpoints
|
|
@core_handle: xDCI controller handle address
|
|
@ep_num: Physical endpoint number
|
|
@data_stage: EP not ready when data stage token was received
|
|
@status_stage: EP not ready when status stage token was received
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
dwc_xdci_process_ep0_xfer_not_ready (
|
|
IN XDCI_CORE_HANDLE *core_handle,
|
|
IN UINT32 ep_num,
|
|
IN UINT32 ep_event_status
|
|
)
|
|
{
|
|
USB_EP_STATE ep_state = USB_EP_STATE_SETUP;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_process_ep0_xfer_not_ready: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
/* Is it data stage or status stage */
|
|
if (ep_event_status & DWC_XDCI_EVENT_BUFF_EP_CTRL_DATA_REQ_MASK) {
|
|
ep_state = USB_EP_STATE_DATA;
|
|
} else if (ep_event_status & DWC_XDCI_EVENT_BUFF_EP_CTRL_STATUS_REQ_MASK) {
|
|
ep_state = USB_EP_STATE_STATUS;
|
|
}
|
|
|
|
if ((ep_num == 0) && (ep_state == USB_EP_STATE_STATUS)) {
|
|
if (ep_event_status & DWC_XDCI_EVENT_BUFF_EP_XFER_ACTIVE_MASK) {
|
|
DEBUG ((DEBUG_INFO, "XFER_ACTIVE\n"));
|
|
} else {
|
|
DEBUG ((DEBUG_INFO, "XFER_NOT_ACTIVE\n"));
|
|
}
|
|
dwc_xdci_ep0_receive_status_pkt (core_handle);
|
|
}
|
|
|
|
/* Notify upper layer if a callback is registered for
|
|
* this event
|
|
*/
|
|
if (core_handle->event_callbacks.dev_xfer_nrdy_callback) {
|
|
core_handle->event_callbacks.cb_event_params.parent_handle = core_handle->parent_handle;
|
|
core_handle->event_callbacks.cb_event_params.ep_state = ep_state;
|
|
core_handle->event_callbacks.dev_xfer_nrdy_callback (&core_handle->event_callbacks.cb_event_params);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Internal function:
|
|
This function is used to process transfer phone done for EP0
|
|
@core_handle: xDCI controller handle address
|
|
@ep_num: Physical endpoint number (0 for OUT and 1 for IN)
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
dwc_xdci_process_ep0_xfer_phase_done (
|
|
IN XDCI_CORE_HANDLE *core_handle,
|
|
IN UINT32 ep_num
|
|
)
|
|
{
|
|
DWC_XDCI_ENDPOINT *ep_handle;
|
|
DWC_XDCI_TRB *trb;
|
|
EFI_STATUS status = EFI_SUCCESS;
|
|
UINT32 trb_sts;
|
|
UINT32 trb_ctrl;
|
|
UINT32 trb_bufsize;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_process_ep0_xfer_phase_done: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
ep_handle = &core_handle->ep_handles[ep_num];
|
|
trb = core_handle->trbs + (ep_num * DWC_XDCI_TRB_NUM);
|
|
DEBUG ((DEBUG_INFO, "(dwc_xdci_process_ep0_xfer_phase_done)ep_num is %d\n", ep_num));
|
|
|
|
if (trb->trb_ctrl & DWC_XDCI_TRB_CTRL_HWO_MASK) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_process_ep0_xfer_phase_done. HW owns TRB: %x!!!\n", (UINT32)(UINTN)trb));
|
|
}
|
|
|
|
ep_handle->currentXferRscIdx = 0;
|
|
ep_handle->state = USB_EP_STATE_ENABLED;
|
|
trb_ctrl = (trb->trb_ctrl & DWC_XDCI_TRB_CTRL_TYPE_MASK) >> DWC_XDCI_TRB_CTRL_TYPE_BIT_POS;
|
|
trb_sts = (trb->len_xfer_params & DWC_XDCI_TRB_STATUS_MASK) >> DWC_XDCI_TRB_STATUS_BIT_POS;
|
|
trb_bufsize = trb->len_xfer_params & DWC_XDCI_TRB_BUFF_SIZE_MASK;
|
|
|
|
switch (trb_ctrl) {
|
|
case DWC_XDCI_TRB_CTRL_TYPE_SETUP:
|
|
DEBUG ((DEBUG_INFO, "SETUP\n"));
|
|
if (core_handle->event_callbacks.dev_setup_pkt_received_callback) {
|
|
core_handle->event_callbacks.cb_event_params.parent_handle = core_handle->parent_handle;
|
|
core_handle->event_callbacks.cb_event_params.buffer = core_handle->aligned_setup_buffer;
|
|
status = core_handle->event_callbacks.dev_setup_pkt_received_callback (&core_handle->event_callbacks.cb_event_params);
|
|
}
|
|
|
|
if (!(core_handle->aligned_setup_buffer[0] & USB_SETUP_DATA_PHASE_DIRECTION_MASK)) {
|
|
/* Keep a buffer ready for setup phase */
|
|
dwc_xdci_core_start_ep0_setup_xfer (core_handle);
|
|
}
|
|
|
|
break;
|
|
|
|
case DWC_XDCI_TRB_CTRL_TYPE_STATUS2:
|
|
DEBUG ((DEBUG_INFO, "STATUS2\n"));
|
|
break;
|
|
|
|
case DWC_XDCI_TRB_CTRL_TYPE_STATUS3:
|
|
DEBUG ((DEBUG_INFO, "STATUS3\n"));
|
|
/* Notify upper layer of control transfer completion
|
|
* if a callback function was registerd
|
|
*/
|
|
if (core_handle->event_callbacks.dev_xfer_done_callback) {
|
|
core_handle->event_callbacks.cb_event_params.parent_handle = core_handle->parent_handle;
|
|
core_handle->event_callbacks.cb_event_params.ep_num = (ep_num >> 1);
|
|
core_handle->event_callbacks.cb_event_params.ep_dir = (ep_num & 1);
|
|
core_handle->event_callbacks.cb_event_params.buffer = (UINT8 *)(UINTN)(trb->buff_ptr_low);
|
|
core_handle->event_callbacks.dev_xfer_done_callback (&core_handle->event_callbacks.cb_event_params);
|
|
}
|
|
|
|
/* Status phase done. Queue next SETUP packet */
|
|
status = dwc_xdci_core_start_ep0_setup_xfer(core_handle);
|
|
|
|
if (status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_process_ep0_xfer_phase_done: FAILED to queue SETUP\n"));
|
|
}
|
|
break;
|
|
|
|
case DWC_XDCI_TRB_CTRL_TYPE_DATA:
|
|
DEBUG ((DEBUG_INFO, "DATA\n"));
|
|
if (trb_sts == DWC_XDCI_TRB_STATUS_SETUP_PENDING || trb_bufsize != 0) {
|
|
DEBUG ((DEBUG_INFO, "ERROR: Control transfert aborted by host: Setup pending\n"));
|
|
dwc_xdci_core_start_ep0_setup_xfer (core_handle);
|
|
}
|
|
|
|
if (core_handle->event_callbacks.dev_xfer_done_callback) {
|
|
core_handle->event_callbacks.cb_event_params.parent_handle = core_handle->parent_handle;
|
|
core_handle->event_callbacks.cb_event_params.ep_num = (ep_num >> 1);
|
|
core_handle->event_callbacks.cb_event_params.ep_dir = (ep_num & 1);
|
|
core_handle->event_callbacks.cb_event_params.buffer = (UINT8 *)(UINTN)(trb->buff_ptr_low);
|
|
core_handle->event_callbacks.dev_xfer_done_callback (&core_handle->event_callbacks.cb_event_params);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_process_ep0_xfer_phase_done: UNHANDLED STATE in TRB\n"));
|
|
break;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/**
|
|
Internal function:
|
|
This function is used to process transfer done for
|
|
non-control endpoints
|
|
@core_handle: xDCI controller handle address
|
|
@ep_num: Physical endpoint number
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
dwc_xdci_process_ep_xfer_done (
|
|
IN XDCI_CORE_HANDLE *core_handle,
|
|
IN UINT32 ep_num
|
|
)
|
|
{
|
|
DWC_XDCI_ENDPOINT *ep_handle;
|
|
DWC_XDCI_TRB *trb;
|
|
USB_XFER_REQUEST *xfer_req;
|
|
UINT32 remaining_len;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_process_ep_xfer_done: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
ep_handle = &core_handle->ep_handles[ep_num];
|
|
ep_handle->currentXferRscIdx = 0;
|
|
trb = ep_handle->trb;
|
|
xfer_req = &ep_handle->xfer_handle;
|
|
|
|
//
|
|
// if transfer done, set CheckFlag to FALSE for allow next transfer request.
|
|
//
|
|
ep_handle->CheckFlag = FALSE;
|
|
|
|
if ((trb == NULL) || (xfer_req == NULL)) {
|
|
DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_process_ep_xfer_done: INVALID parameter\n"));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Compute the actual transfer length */
|
|
xfer_req->actual_xfer_len = xfer_req->xfer_len;
|
|
remaining_len = (trb->len_xfer_params & DWC_XDCI_TRB_BUFF_SIZE_MASK);
|
|
|
|
if (remaining_len > xfer_req->xfer_len) {
|
|
/* Buffer overrun? This should never happen */
|
|
DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_process_ep_xfer_done: Possible buffer overrun\n"));
|
|
} else {
|
|
xfer_req->actual_xfer_len -= remaining_len;
|
|
}
|
|
|
|
/* Notify upper layer of request-specific transfer completion
|
|
* if there is a callback specifically for this request
|
|
*/
|
|
if (xfer_req->xfer_done) {
|
|
xfer_req->xfer_done(core_handle->parent_handle, xfer_req);
|
|
}
|
|
|
|
/* Notify upper layer if a callback was registered */
|
|
if (core_handle->event_callbacks.dev_xfer_done_callback) {
|
|
core_handle->event_callbacks.cb_event_params.parent_handle = core_handle->parent_handle;
|
|
core_handle->event_callbacks.cb_event_params.ep_num = (ep_num >> 1);
|
|
core_handle->event_callbacks.cb_event_params.ep_dir = (ep_num & 1);
|
|
core_handle->event_callbacks.cb_event_params.ep_type = ep_handle->ep_info.ep_type;
|
|
core_handle->event_callbacks.cb_event_params.buffer = (UINT8 *)(UINTN)(ep_handle->trb->buff_ptr_low);
|
|
core_handle->event_callbacks.dev_xfer_done_callback (&core_handle->event_callbacks.cb_event_params);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Internal function:
|
|
This function is used to process endpoint events
|
|
@core_handle: xDCI controller handle address
|
|
@int_line_event_buffer: address of buffer containing event
|
|
to process
|
|
@processed_event_size: address to save the size of event
|
|
processed
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
dwc_xdci_process_ep_event (
|
|
IN XDCI_CORE_HANDLE *core_handle,
|
|
IN DWC_XDCI_EVENT_BUFFER *int_line_event_buffer,
|
|
IN UINT32 *processed_event_size
|
|
)
|
|
{
|
|
UINT32 ep_num;
|
|
UINT32 ep_event;
|
|
UINT32 ep_event_status;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_process_ep_event: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
ep_event = int_line_event_buffer->event;
|
|
|
|
*processed_event_size = DWC_XDCI_DEV_EVENT_DEFAULT_SIZE_IN_BYTES;
|
|
|
|
/* Get EP num */
|
|
ep_num = ((ep_event & DWC_XDCI_EVENT_BUFF_EP_NUM_MASK) >> DWC_XDCI_EVENT_BUFF_EP_NUM_BIT_POS);
|
|
ep_event_status = (ep_event & DWC_XDCI_EVENT_BUFF_EP_EVENT_STATUS_MASK);
|
|
|
|
/* Interpret event and handle transfer completion here */
|
|
ep_event = ((ep_event & DWC_XDCI_EVENT_BUFF_EP_EVENT_MASK) >> DWC_XDCI_EVENT_BUFF_EP_EVENT_BIT_POS);
|
|
|
|
switch (ep_event) {
|
|
case DWC_XDCI_EVENT_BUFF_EP_XFER_CMPLT:
|
|
DEBUG ((DEBUG_INFO, "XFER_CMPLT ep %d\n", ep_num));
|
|
if (ep_num > 1) {
|
|
dwc_xdci_process_ep_xfer_done (core_handle, ep_num);
|
|
} else {
|
|
dwc_xdci_process_ep0_xfer_phase_done (core_handle, ep_num);
|
|
}
|
|
break;
|
|
|
|
case DWC_XDCI_EVENT_BUFF_EP_XFER_IN_PROGRESS:
|
|
DEBUG ((DEBUG_INFO, "IN_PROGRESS\n"));
|
|
break;
|
|
|
|
case DWC_XDCI_EVENT_BUFF_EP_XFER_NOT_READY:
|
|
DEBUG ((DEBUG_INFO, "NOT_READY ep %d\n", ep_num));
|
|
if (ep_num > 1) {
|
|
/* Endpoint transfer is not ready */
|
|
dwc_xdci_process_ep_xfer_not_ready (core_handle, ep_num);
|
|
} else {
|
|
dwc_xdci_process_ep0_xfer_not_ready (core_handle, ep_num, ep_event_status);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_process_ep_event: UNKNOWN EP event\n"));
|
|
break;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Internal function:
|
|
This function is used to process events on single interrupt line
|
|
@core_handle: xDCI controller handle address
|
|
@event_count: event bytes to process
|
|
@processed_event_count: address to save the size
|
|
(in bytes) of event processed
|
|
processed
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
dwc_xdci_process_interrupt_line_events (
|
|
IN XDCI_CORE_HANDLE *core_handle,
|
|
IN UINT32 event_count,
|
|
IN UINT32 *processed_event_count
|
|
)
|
|
{
|
|
UINT32 processed_event_size = 0;
|
|
UINT32 current_event_addr;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_process_interrupt_line_events: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
if (core_handle->current_event_buffer == NULL) {
|
|
DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_process_interrupt_line_events: INVALID event buffer\n"));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
current_event_addr = (UINT32)(UINTN)(core_handle->current_event_buffer);
|
|
|
|
/* Process event_count/event_size number of events
|
|
* in this run
|
|
*/
|
|
while (event_count) {
|
|
if (core_handle->current_event_buffer->event & DWC_XDCI_EVENT_DEV_MASK) {
|
|
dwc_xdci_process_device_event (
|
|
core_handle,
|
|
core_handle->current_event_buffer,
|
|
&processed_event_size
|
|
);
|
|
} else {
|
|
dwc_xdci_process_ep_event (
|
|
core_handle,
|
|
core_handle->current_event_buffer,
|
|
&processed_event_size);
|
|
}
|
|
|
|
event_count -= processed_event_size;
|
|
*processed_event_count += processed_event_size;
|
|
if ((current_event_addr + processed_event_size) >=
|
|
((UINT32)(UINTN)(core_handle->aligned_event_buffers) + (sizeof(DWC_XDCI_EVENT_BUFFER) * DWC_XDCI_MAX_EVENTS_PER_BUFFER))
|
|
) {
|
|
current_event_addr = (UINT32)(UINTN)(core_handle->aligned_event_buffers);
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_process_interrupt_line_events: Event buffer bound reached\n"));
|
|
} else {
|
|
current_event_addr += processed_event_size;
|
|
}
|
|
|
|
core_handle->current_event_buffer = (DWC_XDCI_EVENT_BUFFER *)(UINTN)current_event_addr;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/* DWC XDCI APIs */
|
|
|
|
/**
|
|
Interface:
|
|
|
|
This function is used to initialize the xDCI core
|
|
@config_params: Parameters from app to configure the core
|
|
@device_core_ptr: HW-independent APIs handle for device core
|
|
@core_handle: xDCI controller handle retured
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
dwc_xdci_core_init (
|
|
IN USB_DEV_CONFIG_PARAMS *ConfigParams,
|
|
IN VOID *device_core_ptr,
|
|
IN VOID **core_handle
|
|
)
|
|
{
|
|
EFI_STATUS status = EFI_DEVICE_ERROR;
|
|
UINTN base_addr;
|
|
XDCI_CORE_HANDLE *local_core_handle;
|
|
DWC_XDCI_ENDPOINT_CMD_PARAMS ep_cmd_params;
|
|
UINT32 max_delay_iter = DWC_XDCI_MAX_DELAY_ITERATIONS;
|
|
UINT8 i;
|
|
|
|
local_core_handle = (XDCI_CORE_HANDLE *)AllocateZeroPool (sizeof(XDCI_CORE_HANDLE));
|
|
|
|
if (core_handle == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (local_core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to allocate handle for xDCI\n"));
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
ZeroMem (local_core_handle, sizeof(XDCI_CORE_HANDLE));
|
|
|
|
local_core_handle->parent_handle = device_core_ptr;
|
|
|
|
*core_handle = (VOID *)local_core_handle;
|
|
|
|
local_core_handle->id = ConfigParams->ControllerId;
|
|
local_core_handle->base_address = base_addr = ConfigParams->BaseAddress;
|
|
local_core_handle->flags = ConfigParams->Flags;
|
|
local_core_handle->desired_speed = local_core_handle->actual_speed = ConfigParams->Speed;
|
|
local_core_handle->role = ConfigParams->Role;
|
|
|
|
DEBUG ((DEBUG_INFO, "Resetting the USB core\n"));
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_DCTL_REG,
|
|
usb_reg_read (base_addr, DWC_XDCI_DCTL_REG) | DWC_XDCI_DCTL_CSFTRST_MASK
|
|
);
|
|
|
|
/* Wait until core soft reset completes */
|
|
do {
|
|
if (!(usb_reg_read (base_addr, DWC_XDCI_DCTL_REG) & DWC_XDCI_DCTL_CSFTRST_MASK)) {
|
|
break;
|
|
} else {
|
|
gBS->Stall (DWC_XDCI_MAX_DELAY_ITERATIONS);
|
|
}
|
|
} while (--max_delay_iter);
|
|
|
|
if (!max_delay_iter) {
|
|
DEBUG ((DEBUG_INFO, "Failed to reset device controller\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "USB core has been reset\n"));
|
|
|
|
/* All FIFOs are flushed at this point */
|
|
|
|
/* Ensure we have EP0 Rx/Tx handles initialized */
|
|
local_core_handle->ep_handles[0].ep_info.ep_num = 0;
|
|
local_core_handle->ep_handles[0].ep_info.ep_dir = UsbEpDirOut;
|
|
local_core_handle->ep_handles[0].ep_info.ep_type = USB_ENDPOINT_CONTROL;
|
|
local_core_handle->ep_handles[0].ep_info.max_pkt_size = DWC_XDCI_SS_CTRL_EP_MPS;
|
|
/* 0 means burst size of 1 */
|
|
local_core_handle->ep_handles[0].ep_info.burst_size = 0;
|
|
|
|
local_core_handle->ep_handles[1].ep_info.ep_num = 0;
|
|
local_core_handle->ep_handles[1].ep_info.ep_dir = UsbEpDirIn;
|
|
local_core_handle->ep_handles[1].ep_info.ep_type = USB_ENDPOINT_CONTROL;
|
|
local_core_handle->ep_handles[1].ep_info.max_pkt_size = DWC_XDCI_SS_CTRL_EP_MPS;
|
|
/* 0 means burst size of 1 */
|
|
local_core_handle->ep_handles[1].ep_info.burst_size = 0;
|
|
|
|
local_core_handle->dev_state = UsbDevStateDefault;
|
|
|
|
/* Clear KeepConnect bit so we can allow disconnect and
|
|
* re-connect. Stay in RX_DETECT state
|
|
*/
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_DCTL_REG,
|
|
usb_reg_read (base_addr, DWC_XDCI_DCTL_REG) &
|
|
(~DWC_XDCI_DCTL_KEEP_CONNECT_MASK) &
|
|
((~DWC_XDCI_DCTL_STATE_CHANGE_REQ_MASK) | (DWC_XDCI_DCTL_STATE_CHANGE_REQ_RX_DETECT << DWC_XDCI_DCTL_STATE_CHANGE_REQ_BIT_POS))
|
|
);
|
|
|
|
DEBUG ((DEBUG_INFO, "Device controller Synopsys ID: %x\n", usb_reg_read (base_addr, DWC_XDCI_GSNPSID_REG)));
|
|
DEBUG ((DEBUG_INFO, "Default value of xDCI GSBUSCFG0 and GSBUSCFG1: %x, %x\n",
|
|
usb_reg_read (base_addr, DWC_XDCI_GSBUSCFG0_REG),
|
|
usb_reg_read (base_addr, DWC_XDCI_GSBUSCFG1_REG)));
|
|
|
|
DEBUG ((DEBUG_INFO, "Default value of xDCI GTXTHRCFG and GRXTHRCFG: %x, %x\n",
|
|
usb_reg_read (base_addr, DWC_XDCI_GTXTHRCFG_REG),
|
|
usb_reg_read (base_addr, DWC_XDCI_GRXTHRCFG_REG)));
|
|
|
|
/* Clear ULPI auto-resume bit */
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_GUSB2PHYCFG_REG (0),
|
|
(usb_reg_read (base_addr, DWC_XDCI_GUSB2PHYCFG_REG (0)) & ~DWC_XDCI_GUSB2PHYCFG_ULPI_AUTO_RESUME_MASK)
|
|
);
|
|
|
|
DEBUG ((DEBUG_INFO, "Default value of xDCI GUSB2PHYCFG and GUSB3PIPECTL: %x, %x\n",
|
|
usb_reg_read (base_addr, DWC_XDCI_GUSB2PHYCFG_REG (0)),
|
|
usb_reg_read (base_addr, DWC_XDCI_GUSB3PIPECTL_REG (0))));
|
|
|
|
/* Only one RxFIFO */
|
|
DEBUG ((DEBUG_INFO, "Default value of DWC_XDCI_GRXFIFOSIZ: %x\n",
|
|
usb_reg_read (base_addr, DWC_XDCI_GRXFIFOSIZ_REG (0))));
|
|
|
|
for (i = 0; i < DWC_XDCI_MAX_ENDPOINTS; i++) {
|
|
DEBUG ((DEBUG_INFO, "Default value of xDCI DWC_XDCI_GTXFIFOSIZ %d: %x\n",
|
|
i, usb_reg_read (base_addr, DWC_XDCI_GTXFIFOSIZ_REG (i))));
|
|
}
|
|
|
|
/* TODO: Need to check if TxFIFO should start where RxFIFO ends
|
|
* or default is correct i.e. TxFIFO starts at 0 just like RxFIFO
|
|
*/
|
|
|
|
/* Allocate and Initialize Event Buffers */
|
|
local_core_handle->max_dev_int_lines = ((usb_reg_read (base_addr, DWC_XDCI_GHWPARAMS1_REG) &
|
|
DWC_XDCI_GHWPARAMS1_NUM_INT_MASK) >>
|
|
DWC_XDCI_GHWPARAMS1_NUM_INT_BIT_POS);
|
|
|
|
DEBUG ((DEBUG_INFO, "Max dev int lines: %d\n", local_core_handle->max_dev_int_lines));
|
|
|
|
/* One event buffer per interrupt line.
|
|
* Need to align it to size of event buffer
|
|
* Buffer needs to be big enough. Otherwise the core
|
|
* won't operate
|
|
*/
|
|
local_core_handle->aligned_event_buffers = (DWC_XDCI_EVENT_BUFFER *)
|
|
((UINT32)(UINTN)(local_core_handle->event_buffers) +
|
|
((sizeof (DWC_XDCI_EVENT_BUFFER) * DWC_XDCI_MAX_EVENTS_PER_BUFFER) -
|
|
(((UINT32)(UINTN)(local_core_handle->event_buffers)) %
|
|
(sizeof (DWC_XDCI_EVENT_BUFFER) * DWC_XDCI_MAX_EVENTS_PER_BUFFER))));
|
|
|
|
for (i = 0; i < local_core_handle->max_dev_int_lines; i++) {
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_GEVNTADR_REG (i),
|
|
(UINT32)(UINTN)(local_core_handle->aligned_event_buffers + i * sizeof(DWC_XDCI_EVENT_BUFFER) * DWC_XDCI_MAX_EVENTS_PER_BUFFER)
|
|
);
|
|
|
|
//
|
|
// Clear High 32bit address register, GEVNTADR register is 64-bit register
|
|
// default is 0xffffffffffffffff
|
|
//
|
|
usb_reg_write (base_addr, DWC_XDCI_GEVNTADR_REG (i) + 4, 0x00000000);
|
|
|
|
local_core_handle->current_event_buffer = local_core_handle->aligned_event_buffers;
|
|
/* Write size and clear the mask */
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_EVNTSIZ_REG (i),
|
|
sizeof (DWC_XDCI_EVENT_BUFFER) * DWC_XDCI_MAX_EVENTS_PER_BUFFER
|
|
);
|
|
|
|
/* Write 0 to the event count register as the last step
|
|
* for event configuration
|
|
*/
|
|
usb_reg_write (base_addr, DWC_XDCI_EVNTCOUNT_REG (i), 0);
|
|
|
|
DEBUG ((DEBUG_INFO, "Value of xDCI Event buffer %d: %x, Size: %x, Count: %x\n",
|
|
i,
|
|
usb_reg_read (base_addr, DWC_XDCI_GEVNTADR_REG (i)),
|
|
usb_reg_read (base_addr, DWC_XDCI_EVNTSIZ_REG (i)),
|
|
usb_reg_read (base_addr, DWC_XDCI_EVNTCOUNT_REG (i))));
|
|
}
|
|
|
|
// /* Program Global Control Register to disable scaledown,
|
|
// * disable clock gating
|
|
// */
|
|
// usb_reg_write (
|
|
// base_addr,
|
|
// DWC_XDCI_GCTL_REG,
|
|
// ((usb_reg_read (base_addr, DWC_XDCI_GCTL_REG) & ~DWC_XDCI_GCTL_SCALE_DOWN_MODE_MASK) | DWC_XDCI_GCTL_DISABLE_CLK_GATING_MASK)
|
|
// );
|
|
/* Program Global Control Register to disable scaledown,
|
|
* disable clock gating
|
|
*/
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_GCTL_REG,
|
|
((usb_reg_read(base_addr, DWC_XDCI_GCTL_REG) &
|
|
//HSLE_DEBUG ~DWC_XDCI_GCTL_SCALE_DOWN_MODE_MASK) |
|
|
//HSLE_DEBUG DWC_XDCI_GCTL_DISABLE_CLK_GATING_MASK));
|
|
~(DWC_XDCI_GCTL_SCALE_DOWN_MODE_MASK + DWC_XDCI_GCTL_RAMCLKSEL_MASK + DWC_XDCI_GCTL_DISABLE_SCRAMB_MASK)) |
|
|
DWC_XDCI_GCTL_DISABLE_CLK_GATING_MASK |
|
|
(DWC_XDCI_GCTL_PRT_CAP_DEVICE << DWC_XDCI_GCTL_PRT_CAP_DIR_BIT_POS)));
|
|
|
|
// //
|
|
// //HSLE_DEBUG
|
|
// //
|
|
// usb_reg_write(base_addr, DWC_XDCI_GCTL_REG,
|
|
// ((usb_reg_read(base_addr, DWC_XDCI_GCTL_REG) &
|
|
// ~DWC_XDCI_GCTL_SCALE_DOWN_MODE_MASK) |
|
|
// DWC_XDCI_GCTL_DISABLE_CLK_GATING_MASK));
|
|
|
|
DEBUG ((DEBUG_INFO, "Setup value of xDCI DWC_XDCI_GCTL_REG: 0x%x\n", usb_reg_read (base_addr, DWC_XDCI_GCTL_REG)));
|
|
|
|
|
|
/* TODO: Program desired speed and set LPM capable
|
|
* We will do this when Superspeed works. For now,
|
|
* force into High-speed mode to aVOID anyone trying this
|
|
* on Super speed port
|
|
*/
|
|
#ifdef SUPPORT_SUPER_SPEED
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_DCFG_REG,
|
|
(usb_reg_read (base_addr, DWC_XDCI_DCFG_REG) & ~DWC_XDCI_DCFG_DESIRED_DEV_SPEED_MASK) | local_core_handle->desired_speed
|
|
);
|
|
#else
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_DCFG_REG,
|
|
(usb_reg_read (base_addr, DWC_XDCI_DCFG_REG) & ~DWC_XDCI_DCFG_DESIRED_DEV_SPEED_MASK) | DWC_XDCI_DCFG_DESIRED_HS_SPEED
|
|
);
|
|
#endif
|
|
|
|
DEBUG ((DEBUG_INFO, "Setup value of xDCI DWC_XDCI_DCFG_REG: 0x%x\n", usb_reg_read (base_addr, DWC_XDCI_DCFG_REG)));
|
|
DEBUG ((DEBUG_INFO, "Setup value of xDCI DWC_XDCI_DSTS_REG: 0x%x\n", usb_reg_read (base_addr, DWC_XDCI_DSTS_REG)));
|
|
|
|
/* Enable Device Interrupt Events */
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_DEVTEN_REG,
|
|
DWC_XDCI_DEVTEN_DEVICE_INTS
|
|
);
|
|
|
|
/* Program the desired role */
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_GCTL_REG,
|
|
(usb_reg_read (base_addr, DWC_XDCI_GCTL_REG) & ~DWC_XDCI_GCTL_PRT_CAP_DIR_MASK) | (local_core_handle->role << DWC_XDCI_GCTL_PRT_CAP_DIR_BIT_POS)
|
|
);
|
|
|
|
/* Clear USB2 suspend for start new config command */
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_GUSB2PHYCFG_REG (0),
|
|
(usb_reg_read (base_addr, DWC_XDCI_GUSB2PHYCFG_REG(0)) & ~DWC_XDCI_GUSB2PHYCFG_SUSPEND_PHY_MASK)
|
|
);
|
|
|
|
/* Clear USB3 suspend for start new config command */
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_GUSB3PIPECTL_REG (0),
|
|
(usb_reg_read (base_addr, DWC_XDCI_GUSB3PIPECTL_REG(0)) & ~DWC_XDCI_GUSB3PIPECTL_SUSPEND_PHY_MASK)
|
|
);
|
|
|
|
/* Issue DEPSTARTCFG command for EP0 */
|
|
status = dwc_xdci_core_init_ep_cmd_params (
|
|
local_core_handle,
|
|
&local_core_handle->ep_handles[0].ep_info,
|
|
DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_NONE,
|
|
EPCMD_START_NEW_CONFIG,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to init params for START_NEW_CONFIG EP command on xDCI\n"));
|
|
return status;
|
|
}
|
|
|
|
/* Issue the command */
|
|
status = dwc_xdci_core_issue_ep_cmd (
|
|
local_core_handle,
|
|
0,
|
|
EPCMD_START_NEW_CONFIG,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to issue START_NEW_CONFIG EP command on xDCI\n"));
|
|
return status;
|
|
}
|
|
|
|
/* Issue DEPCFG command for EP0 */
|
|
status = dwc_xdci_core_init_ep_cmd_params (
|
|
local_core_handle,
|
|
&local_core_handle->ep_handles[0].ep_info,
|
|
DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_INIT_STATE,
|
|
EPCMD_SET_EP_CONFIG,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to init params for SET_EP_CONFIG command on xDCI for EP0\n"));
|
|
return status;
|
|
}
|
|
|
|
/* Issue the command */
|
|
status = dwc_xdci_core_issue_ep_cmd (
|
|
local_core_handle,
|
|
0,
|
|
EPCMD_SET_EP_CONFIG,
|
|
&ep_cmd_params);
|
|
|
|
if (status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to issue SET_EP_CONFIG command on xDCI for EP0\n"));
|
|
return status;
|
|
}
|
|
|
|
/* Issue DEPCFG command for EP1 */
|
|
status = dwc_xdci_core_init_ep_cmd_params (
|
|
local_core_handle,
|
|
&local_core_handle->ep_handles[1].ep_info,
|
|
DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_INIT_STATE,
|
|
EPCMD_SET_EP_CONFIG,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to init params for SET_EP_CONFIG command on xDCI for EP1\n"));
|
|
return status;
|
|
}
|
|
|
|
/* Issue the command */
|
|
status = dwc_xdci_core_issue_ep_cmd (
|
|
local_core_handle,
|
|
1,
|
|
EPCMD_SET_EP_CONFIG,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to issue SET_EP_CONFIG command on xDCI for EP1\n"));
|
|
return status;
|
|
}
|
|
|
|
/* Issue DEPXFERCFG command for EP0 */
|
|
status = dwc_xdci_core_init_ep_cmd_params (
|
|
local_core_handle,
|
|
&local_core_handle->ep_handles[0].ep_info,
|
|
DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_NONE,
|
|
EPCMD_SET_EP_XFER_RES_CONFIG,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to init params for EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP0\n"));
|
|
return status;
|
|
}
|
|
|
|
/* Issue the command */
|
|
status = dwc_xdci_core_issue_ep_cmd (
|
|
local_core_handle,
|
|
0,
|
|
EPCMD_SET_EP_XFER_RES_CONFIG,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to issue EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP0\n"));
|
|
return status;
|
|
}
|
|
|
|
/* Issue DEPXFERCFG command for EP1 */
|
|
status = dwc_xdci_core_init_ep_cmd_params (
|
|
local_core_handle,
|
|
&local_core_handle->ep_handles[1].ep_info,
|
|
DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_NONE,
|
|
EPCMD_SET_EP_XFER_RES_CONFIG,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to init params for EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP1\n"));
|
|
return status;
|
|
}
|
|
|
|
/* Issue the command */
|
|
status = dwc_xdci_core_issue_ep_cmd (
|
|
local_core_handle,
|
|
1,
|
|
EPCMD_SET_EP_XFER_RES_CONFIG,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to issue EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP1\n"));
|
|
return status;
|
|
}
|
|
|
|
/* Prepare a buffer for SETUP packet */
|
|
local_core_handle->trbs = (DWC_XDCI_TRB *)(UINTN)((UINT32)(UINTN)
|
|
local_core_handle->unaligned_trbs +
|
|
(DWC_XDCI_TRB_BYTE_ALIGNMENT -
|
|
((UINT32)(UINTN)local_core_handle->unaligned_trbs %
|
|
DWC_XDCI_TRB_BYTE_ALIGNMENT)));
|
|
|
|
DEBUG ((DEBUG_INFO, "(dwc_xdci_core_init)@@@@@@@@@ unaligned_trbs address is 0x%x\n", local_core_handle->unaligned_trbs));
|
|
DEBUG ((DEBUG_INFO, "(dwc_xdci_core_init)@@@@@@@@@ TRB address is 0x%x\n", local_core_handle->trbs));
|
|
|
|
/* Allocate Setup buffer that is 8-byte aligned */
|
|
local_core_handle->aligned_setup_buffer = local_core_handle->default_setup_buffer +
|
|
(DWC_XDCI_SETUP_BUFF_SIZE -
|
|
((UINT32)(UINTN)(local_core_handle->default_setup_buffer) % DWC_XDCI_SETUP_BUFF_SIZE));
|
|
|
|
/* Aligned buffer for status phase */
|
|
local_core_handle->aligned_misc_buffer = local_core_handle->misc_buffer +
|
|
(DWC_XDCI_SETUP_BUFF_SIZE -
|
|
((UINT32)(UINTN)(local_core_handle->aligned_misc_buffer) % DWC_XDCI_SETUP_BUFF_SIZE));
|
|
|
|
/* We will queue SETUP request when we see bus reset */
|
|
|
|
/* Enable Physical Endpoints 0 */
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_EP_DALEPENA_REG,
|
|
usb_reg_read (base_addr, DWC_XDCI_EP_DALEPENA_REG) | (1 << 0)
|
|
);
|
|
|
|
/* Enable Physical Endpoints 1 */
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_EP_DALEPENA_REG,
|
|
usb_reg_read (base_addr, DWC_XDCI_EP_DALEPENA_REG) | (1 << 1)
|
|
);
|
|
|
|
DEBUG ((DEBUG_INFO, "Default value of xDCI DWC_XDCI_DEVTEN_REG: 0x%x\n", usb_reg_read (base_addr, DWC_XDCI_DEVTEN_REG)));
|
|
return status;
|
|
}
|
|
|
|
|
|
/**
|
|
Interface:
|
|
This function is used to de-initialize the xDCI core
|
|
@core_handle: xDCI controller handle
|
|
@flags: Special flags for de-initializing the core in
|
|
particular way
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
dwc_xdci_core_deinit (
|
|
IN VOID *core_handle,
|
|
IN UINT32 flags
|
|
)
|
|
{
|
|
/* TODO: Need to implement this */
|
|
FreePool (core_handle);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Interface:
|
|
This function is used to register event callback function
|
|
@core_handle: xDCI controller handle
|
|
@event: Event for which callback is to be registered
|
|
@callback_fn: Callback function to invoke after event occurs
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
dwc_xdci_core_register_callback (
|
|
IN VOID *core_handle,
|
|
IN USB_DEVICE_EVENT_ID event,
|
|
IN USB_DEVICE_CALLBACK_FUNC CallbackFunc
|
|
)
|
|
{
|
|
XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle;
|
|
|
|
if (local_core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_register_callback: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_register_callback: event is %d\n", event));
|
|
switch (event) {
|
|
case USB_DEVICE_DISCONNECT_EVENT:
|
|
local_core_handle->event_callbacks.dev_disconnect_callback = CallbackFunc;
|
|
break;
|
|
|
|
case USB_DEVICE_RESET_EVENT:
|
|
local_core_handle->event_callbacks.dev_bus_reset_callback = CallbackFunc;
|
|
break;
|
|
|
|
case USB_DEVICE_CONNECTION_DONE:
|
|
local_core_handle->event_callbacks.dev_reset_done_callback = CallbackFunc;
|
|
break;
|
|
|
|
case USB_DEVICE_STATE_CHANGE_EVENT:
|
|
local_core_handle->event_callbacks.dev_link_state_callback = CallbackFunc;
|
|
break;
|
|
|
|
case USB_DEVICE_WAKEUP_EVENT:
|
|
local_core_handle->event_callbacks.dev_wakeup_callback = CallbackFunc;
|
|
break;
|
|
|
|
case USB_DEVICE_HIBERNATION_REQ_EVENT:
|
|
local_core_handle->event_callbacks.dev_hibernation_callback = CallbackFunc;
|
|
break;
|
|
|
|
case USB_DEVICE_SOF_EVENT:
|
|
local_core_handle->event_callbacks.dev_sof_callback = CallbackFunc;
|
|
break;
|
|
|
|
case USB_DEVICE_ERRATIC_ERR_EVENT:
|
|
local_core_handle->event_callbacks.dev_erratic_err_callback = CallbackFunc;
|
|
break;
|
|
|
|
case USB_DEVICE_CMD_CMPLT_EVENT:
|
|
local_core_handle->event_callbacks.dev_cmd_cmplt_callback = CallbackFunc;
|
|
break;
|
|
|
|
case USB_DEVICE_BUFF_OVERFLOW_EVENT:
|
|
local_core_handle->event_callbacks.dev_buff_ovflw_callback = CallbackFunc;
|
|
break;
|
|
|
|
case USB_DEVICE_TEST_LMP_RX_EVENT:
|
|
local_core_handle->event_callbacks.dev_test_lmp_rx_callback = CallbackFunc;
|
|
break;
|
|
|
|
case USB_DEVICE_SETUP_PKT_RECEIVED:
|
|
local_core_handle->event_callbacks.dev_setup_pkt_received_callback = CallbackFunc;
|
|
break;
|
|
|
|
case USB_DEVICE_XFER_NRDY:
|
|
local_core_handle->event_callbacks.dev_xfer_nrdy_callback = CallbackFunc;
|
|
break;
|
|
|
|
case USB_DEVICE_XFER_DONE:
|
|
local_core_handle->event_callbacks.dev_xfer_done_callback = CallbackFunc;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Interface:
|
|
This function is used to unregister event callback function
|
|
@core_handle: xDCI controller handle
|
|
@event: Event for which callback function is to be unregistered
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
dwc_xdci_core_unregister_callback (
|
|
IN VOID *core_handle,
|
|
IN USB_DEVICE_EVENT_ID event
|
|
)
|
|
{
|
|
XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle;
|
|
|
|
if (local_core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_unregister_callback: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
switch (event) {
|
|
case USB_DEVICE_DISCONNECT_EVENT:
|
|
local_core_handle->event_callbacks.dev_disconnect_callback = NULL;
|
|
break;
|
|
|
|
case USB_DEVICE_RESET_EVENT:
|
|
local_core_handle->event_callbacks.dev_bus_reset_callback = NULL;
|
|
break;
|
|
|
|
case USB_DEVICE_CONNECTION_DONE:
|
|
local_core_handle->event_callbacks.dev_reset_done_callback = NULL;
|
|
break;
|
|
|
|
case USB_DEVICE_STATE_CHANGE_EVENT:
|
|
local_core_handle->event_callbacks.dev_link_state_callback = NULL;
|
|
break;
|
|
|
|
case USB_DEVICE_WAKEUP_EVENT:
|
|
local_core_handle->event_callbacks.dev_wakeup_callback = NULL;
|
|
break;
|
|
|
|
case USB_DEVICE_HIBERNATION_REQ_EVENT:
|
|
local_core_handle->event_callbacks.dev_hibernation_callback = NULL;
|
|
break;
|
|
|
|
case USB_DEVICE_SOF_EVENT:
|
|
local_core_handle->event_callbacks.dev_sof_callback = NULL;
|
|
break;
|
|
|
|
case USB_DEVICE_ERRATIC_ERR_EVENT:
|
|
local_core_handle->event_callbacks.dev_erratic_err_callback = NULL;
|
|
break;
|
|
|
|
case USB_DEVICE_CMD_CMPLT_EVENT:
|
|
local_core_handle->event_callbacks.dev_cmd_cmplt_callback = NULL;
|
|
break;
|
|
|
|
case USB_DEVICE_BUFF_OVERFLOW_EVENT:
|
|
local_core_handle->event_callbacks.dev_buff_ovflw_callback = NULL;
|
|
break;
|
|
|
|
case USB_DEVICE_TEST_LMP_RX_EVENT:
|
|
local_core_handle->event_callbacks.dev_test_lmp_rx_callback = NULL;
|
|
break;
|
|
|
|
case USB_DEVICE_SETUP_PKT_RECEIVED:
|
|
local_core_handle->event_callbacks.dev_setup_pkt_received_callback = NULL;
|
|
break;
|
|
|
|
case USB_DEVICE_XFER_NRDY:
|
|
local_core_handle->event_callbacks.dev_xfer_nrdy_callback = NULL;
|
|
break;
|
|
|
|
case USB_DEVICE_XFER_DONE:
|
|
local_core_handle->event_callbacks.dev_xfer_done_callback = NULL;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Interface:
|
|
This function is used as an interrupt service routine
|
|
@core_handle: xDCI controller handle
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
dwc_xdci_core_isr_routine (
|
|
IN VOID *core_handle
|
|
)
|
|
{
|
|
XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle;
|
|
UINTN base_addr;
|
|
UINT32 event_count;
|
|
UINT32 processed_event_count;
|
|
UINT32 i;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_isr_routine: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
if (local_core_handle->interrup_processing == TRUE) {
|
|
DEBUG ((DEBUG_INFO, "interrup_processing.........\n"));
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
base_addr = local_core_handle->base_address;
|
|
|
|
/* Event buffer corresponding to each interrupt line needs
|
|
* to be processed
|
|
*/
|
|
local_core_handle->interrup_processing = TRUE;
|
|
for (i = 0; i < local_core_handle->max_dev_int_lines; i++) {
|
|
/* Get the number of events HW has written for this
|
|
* interrupt line
|
|
*/
|
|
event_count = usb_reg_read (base_addr, DWC_XDCI_EVNTCOUNT_REG (i));
|
|
event_count &= DWC_XDCI_EVNTCOUNT_MASK;
|
|
processed_event_count = 0;
|
|
|
|
/* Process interrupt line buffer only if count is non-zero */
|
|
if (event_count) {
|
|
/* Process events in this buffer */
|
|
dwc_xdci_process_interrupt_line_events (local_core_handle, event_count, &processed_event_count);
|
|
|
|
/* Write back the processed number of events so HW decrements it from current
|
|
* event count
|
|
*/
|
|
usb_reg_write (base_addr, DWC_XDCI_EVNTCOUNT_REG (i), processed_event_count);
|
|
}
|
|
}
|
|
local_core_handle->interrup_processing = FALSE;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Interface:
|
|
This function is used as an interrupt service routine and it processes only one event at a time.
|
|
@core_handle: xDCI controller handle
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
dwc_xdci_core_isr_routine_timer_based (
|
|
IN VOID *core_handle
|
|
)
|
|
{
|
|
XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle;
|
|
UINTN base_addr;
|
|
UINT32 event_count;
|
|
UINT32 processed_event_count;
|
|
UINT32 current_event_addr;
|
|
UINT32 processed_event_size = 0;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_isr_routine_timer_based: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
if (local_core_handle->current_event_buffer == NULL) {
|
|
DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_core_isr_routine_timer_based: INVALID event buffer\n"));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
base_addr = local_core_handle->base_address;
|
|
|
|
event_count = usb_reg_read (base_addr, DWC_XDCI_EVNTCOUNT_REG (0)) & DWC_XDCI_EVNTCOUNT_MASK;
|
|
|
|
if (local_core_handle->interrup_processing == TRUE) {
|
|
DEBUG ((DEBUG_INFO, "interrup_processing.........\n"));
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
local_core_handle->interrup_processing = TRUE;
|
|
|
|
processed_event_count = 0;
|
|
current_event_addr = (UINT32)(UINTN)(local_core_handle->current_event_buffer);
|
|
|
|
if (local_core_handle->current_event_buffer->event & DWC_XDCI_EVENT_DEV_MASK) {
|
|
dwc_xdci_process_device_event (
|
|
local_core_handle,
|
|
local_core_handle->current_event_buffer,
|
|
&processed_event_size
|
|
);
|
|
} else {
|
|
dwc_xdci_process_ep_event (
|
|
local_core_handle,
|
|
local_core_handle->current_event_buffer,
|
|
&processed_event_size);
|
|
}
|
|
|
|
event_count -= processed_event_size;
|
|
processed_event_count += processed_event_size;
|
|
if ((current_event_addr + processed_event_size) >=
|
|
((UINT32)(UINTN)(local_core_handle->aligned_event_buffers) + (sizeof(DWC_XDCI_EVENT_BUFFER) * DWC_XDCI_MAX_EVENTS_PER_BUFFER))
|
|
) {
|
|
current_event_addr = (UINT32)(UINTN)(local_core_handle->aligned_event_buffers);
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_process_interrupt_line_events: Event buffer bound reached\n"));
|
|
} else {
|
|
current_event_addr += processed_event_size;
|
|
}
|
|
|
|
local_core_handle->current_event_buffer = (DWC_XDCI_EVENT_BUFFER *)(UINTN)current_event_addr;
|
|
usb_reg_write (base_addr, DWC_XDCI_EVNTCOUNT_REG (0), processed_event_count);
|
|
local_core_handle->interrup_processing = FALSE;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Interface:
|
|
This function is used to enable xDCI to connect to the host
|
|
@core_handle: xDCI controller handle
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
dwc_xdci_core_connect (
|
|
IN VOID *core_handle
|
|
)
|
|
{
|
|
XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle;
|
|
UINT32 max_delay_iter = DWC_XDCI_MAX_DELAY_ITERATIONS;
|
|
UINTN base_addr;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_connect: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
base_addr = local_core_handle->base_address;
|
|
|
|
/* Clear KeepConnect bit so we can allow disconnect and re-connect
|
|
* Also issue No action on state change to aVOID any link change
|
|
*/
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_DCTL_REG,
|
|
(usb_reg_read(base_addr, DWC_XDCI_DCTL_REG) & ~DWC_XDCI_DCTL_KEEP_CONNECT_MASK) & ~DWC_XDCI_DCTL_STATE_CHANGE_REQ_MASK
|
|
);
|
|
|
|
/* Set Run bit to connect to the host */
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_DCTL_REG,
|
|
usb_reg_read (base_addr, DWC_XDCI_DCTL_REG) | DWC_XDCI_DCTL_RUN_STOP_MASK
|
|
);
|
|
|
|
/* Wait until core starts running */
|
|
do {
|
|
if (!(usb_reg_read (base_addr, DWC_XDCI_DSTS_REG) & DWC_XDCI_DSTS_DEV_CTRL_HALTED_MASK)) {
|
|
break;
|
|
} else {
|
|
gBS->Stall (DWC_XDCI_MAX_DELAY_ITERATIONS);
|
|
}
|
|
} while (--max_delay_iter);
|
|
|
|
if (!max_delay_iter) {
|
|
DEBUG ((DEBUG_INFO, "Failed to run the device controller\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Interface:
|
|
This function is used to disconnect xDCI from the host
|
|
@core_handle: xDCI controller handle
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
dwc_xdci_core_disconnect (
|
|
IN VOID *core_handle
|
|
)
|
|
{
|
|
XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle;
|
|
UINT32 max_delay_iter = DWC_XDCI_MAX_DELAY_ITERATIONS;
|
|
UINTN base_addr;
|
|
UINT32 event_count;
|
|
UINT32 dsts;
|
|
UINT32 i;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_disconnect: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
base_addr = local_core_handle->base_address;
|
|
|
|
event_count = usb_reg_read (base_addr, DWC_XDCI_EVNTCOUNT_REG (0));
|
|
event_count &= DWC_XDCI_EVNTCOUNT_MASK;
|
|
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_disconnect: event_count=%d\n", event_count));
|
|
while (event_count) {
|
|
dwc_xdci_core_isr_routine(local_core_handle);
|
|
event_count = usb_reg_read (base_addr, DWC_XDCI_EVNTCOUNT_REG (0));
|
|
event_count &= DWC_XDCI_EVNTCOUNT_MASK;
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_disconnect: event_count=%d\n", event_count));
|
|
}
|
|
|
|
/* Issue DEPENDXFER for active transfers */
|
|
for (i = 0; i < DWC_XDCI_MAX_ENDPOINTS; i++){
|
|
if (local_core_handle->ep_handles[i].currentXferRscIdx){
|
|
dwc_xdci_end_xfer(local_core_handle, i);
|
|
}
|
|
}
|
|
/* Clear Run bit to disconnect from host */
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_DCTL_REG,
|
|
usb_reg_read(base_addr, DWC_XDCI_DCTL_REG) & ~DWC_XDCI_DCTL_RUN_STOP_MASK);
|
|
|
|
/* Wait until core is halted */
|
|
do {
|
|
// if ((usb_reg_read (base_addr, DWC_XDCI_DSTS_REG) & DWC_XDCI_DSTS_LINK_STATE_MASK) == DWC_XDCI_DSTS_LINK_STATE_DISCONNECT)
|
|
dsts = usb_reg_read (base_addr, DWC_XDCI_DSTS_REG);
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_disconnect: waiting halt: DSTS=0x%x\n", dsts));
|
|
if ((dsts & DWC_XDCI_DSTS_DEV_CTRL_HALTED_MASK) != 0){
|
|
break;
|
|
} else {
|
|
gBS->Stall (DWC_XDCI_MAX_DELAY_ITERATIONS);
|
|
}
|
|
} while (--max_delay_iter);
|
|
|
|
if (!max_delay_iter) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_disconnect: Failed to halt the device controller\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Interface:
|
|
This function is used to obtain current USB bus speed
|
|
@core_handle: xDCI controller handle
|
|
@speed: Address of variable to save the speed
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
dwc_xdci_core_get_speed (
|
|
IN VOID *core_handle,
|
|
IN USB_SPEED *speed
|
|
)
|
|
{
|
|
XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_get_speed: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
if (speed == NULL) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_get_speed: INVALID parameter\n"));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
*speed = usb_reg_read (local_core_handle->base_address, DWC_XDCI_DSTS_REG) & DWC_XDCI_DSTS_CONN_SPEED_MASK;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Interface:
|
|
This function is used to obtain current USB bus speed
|
|
@core_handle: xDCI controller handle
|
|
@address: USB address to set (assigned by USB host)
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
dwc_xdci_core_set_address (
|
|
IN VOID *core_handle,
|
|
IN UINT32 address
|
|
)
|
|
{
|
|
XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle;
|
|
UINTN base_addr;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_set_address: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
base_addr = local_core_handle->base_address;
|
|
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_set_address is 0x%x \n", address));
|
|
/* Program USB device address */
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_DCFG_REG,
|
|
(usb_reg_read(base_addr, DWC_XDCI_DCFG_REG) & ~DWC_XDCI_DCFG_DEV_ADDRESS_MASK) | (address << DWC_XDCI_DCFG_DEV_ADDRESS_BIT_POS)
|
|
);
|
|
|
|
local_core_handle->dev_state = UsbDevStateAddress;
|
|
DEBUG ((DEBUG_INFO, "Setup value of xDCI DWC_XDCI_GCTL_REG: 0x%x\n", usb_reg_read (base_addr, DWC_XDCI_GCTL_REG)));
|
|
DEBUG ((DEBUG_INFO, "Setup value of xDCI DWC_XDCI_DEVTEN_REG: 0x%x\n", usb_reg_read (base_addr, DWC_XDCI_DEVTEN_REG)));
|
|
DEBUG ((DEBUG_INFO, "Setup value of xDCI DWC_XDCI_DCFG_REG: 0x%x\n", usb_reg_read (base_addr, DWC_XDCI_DCFG_REG)));
|
|
DEBUG ((DEBUG_INFO, "Setup value of xDCI DWC_XDCI_DSTS_REG: 0x%x\n", usb_reg_read (base_addr, DWC_XDCI_DSTS_REG)));
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Interface:
|
|
This function is used to set configuration
|
|
@core_handle: xDCI controller handle
|
|
@config_num: config num to set (assigned by USB host)
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
dwc_xdci_core_set_config (
|
|
IN VOID *core_handle,
|
|
IN UINT32 config_num
|
|
)
|
|
{
|
|
XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle;
|
|
DWC_XDCI_ENDPOINT_CMD_PARAMS ep_cmd_params;
|
|
EFI_STATUS status;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_set_config: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
/* TODO: Disable all non-CTRL endpoints here if
|
|
* stack is not doing it
|
|
*/
|
|
/* TODO: Cancel all non-CTRL transfers here on all EPs
|
|
* if stack is not doing it
|
|
*/
|
|
/* TODO: Change EP1 TXFIFO allocation if necessary */
|
|
|
|
/* Re-initialize transfer resource allocation */
|
|
|
|
/* Issue DEPSTARTCFG command on EP0 (new config for
|
|
* non-control EPs)
|
|
*/
|
|
status = dwc_xdci_core_init_ep_cmd_params (
|
|
local_core_handle,
|
|
&local_core_handle->ep_handles[0].ep_info,
|
|
DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_NONE,
|
|
EPCMD_START_NEW_CONFIG,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_set_config: Failed to init params for EPCMD_START_NEW_CONFIG command\n"));
|
|
return status;
|
|
}
|
|
|
|
/* Issue the command */
|
|
status = dwc_xdci_core_issue_ep_cmd (
|
|
local_core_handle,
|
|
0,
|
|
(EPCMD_START_NEW_CONFIG | (2 << DWC_XDCI_EPCMD_RES_IDX_BIT_POS)),
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_set_config: Failed to issue EPCMD_START_NEW_CONFIG command\n"));
|
|
return status;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/**
|
|
Interface:
|
|
This function is used to set link state
|
|
@core_handle: xDCI controller handle
|
|
@state: Desired link state
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
dwc_xdci_set_link_state (
|
|
IN VOID *core_handle,
|
|
IN USB_DEVICE_SS_LINK_STATE state
|
|
)
|
|
{
|
|
XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle;
|
|
UINTN base_addr;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_set_link_state: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
base_addr = local_core_handle->base_address;
|
|
|
|
/* Clear old mask */
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_DCTL_REG,
|
|
usb_reg_read (base_addr, DWC_XDCI_DCTL_REG) & ~DWC_XDCI_DCTL_STATE_CHANGE_REQ_MASK
|
|
);
|
|
|
|
/* Request new state */
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_DCTL_REG,
|
|
usb_reg_read (base_addr, DWC_XDCI_DCTL_REG) | (state << DWC_XDCI_DCTL_STATE_CHANGE_REQ_BIT_POS)
|
|
);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Interface:
|
|
This function is used to initialize endpoint
|
|
@core_handle: xDCI controller handle
|
|
@ep_info: Address of structure describing properties of EP
|
|
to be initialized
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
dwc_xdci_init_ep (
|
|
IN VOID *core_handle,
|
|
IN USB_EP_INFO *ep_info
|
|
)
|
|
{
|
|
XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle;
|
|
DWC_XDCI_ENDPOINT_CMD_PARAMS ep_cmd_params;
|
|
EFI_STATUS status;
|
|
UINT32 ep_num;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_init_ep: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
/* Convert to physical endpoint */
|
|
ep_num = dwc_xdci_get_physical_ep_num (ep_info->ep_num, ep_info->ep_dir);
|
|
|
|
/* Save EP properties */
|
|
CopyMem (&(local_core_handle->ep_handles[ep_num].ep_info), ep_info, sizeof (USB_EP_INFO));
|
|
|
|
// Init CheckFlag
|
|
local_core_handle->ep_handles[ep_num].CheckFlag = FALSE;
|
|
|
|
/* Init DEPCFG cmd params for EP */
|
|
status = dwc_xdci_core_init_ep_cmd_params (
|
|
core_handle,
|
|
&local_core_handle->ep_handles[ep_num].ep_info,
|
|
DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_INIT_STATE,
|
|
EPCMD_SET_EP_CONFIG,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_init_ep: Failed to init params for EPCMD_SET_EP_CONFIG command\n"));
|
|
return status;
|
|
}
|
|
|
|
/* Issue the command */
|
|
status = dwc_xdci_core_issue_ep_cmd (
|
|
core_handle,
|
|
ep_num,
|
|
EPCMD_SET_EP_CONFIG,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_init_ep: Failed to issue EPCMD_SET_EP_CONFIG command\n"));
|
|
return status;
|
|
}
|
|
|
|
/* Issue a DEPXFERCFG command for endpoint */
|
|
status = dwc_xdci_core_init_ep_cmd_params (
|
|
local_core_handle,
|
|
&local_core_handle->ep_handles[ep_num].ep_info,
|
|
DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_NONE,
|
|
EPCMD_SET_EP_XFER_RES_CONFIG,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_init_ep: Failed to init params for EPCMD_SET_EP_XFER_RES_CONFIG command\n"));
|
|
return status;
|
|
}
|
|
|
|
/* Issue the command */
|
|
status = dwc_xdci_core_issue_ep_cmd (
|
|
local_core_handle,
|
|
ep_num,
|
|
EPCMD_SET_EP_XFER_RES_CONFIG,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_init_ep: Failed to issue EPCMD_SET_EP_XFER_RES_CONFIG command\n"));
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/**
|
|
Interface:
|
|
This function is used to enable non-Ep0 endpoint
|
|
@core_handle: xDCI controller handle
|
|
@ep_info: Address of structure describing properties of EP
|
|
to be enabled
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
dwc_xdci_ep_enable (
|
|
IN VOID *core_handle,
|
|
IN USB_EP_INFO *ep_info
|
|
)
|
|
{
|
|
XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle;
|
|
UINT32 ep_num;
|
|
UINTN base_addr;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_ep_enable: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
base_addr = local_core_handle->base_address;
|
|
|
|
/* Convert to physical endpoint */
|
|
ep_num = dwc_xdci_get_physical_ep_num (ep_info->ep_num, ep_info->ep_dir);
|
|
|
|
/* Enable Physical Endpoint ep_num */
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_EP_DALEPENA_REG,
|
|
usb_reg_read (base_addr, DWC_XDCI_EP_DALEPENA_REG) | (1 << ep_num)
|
|
);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Interface:
|
|
This function is used to disable non-Ep0 endpoint
|
|
@core_handle: xDCI controller handle
|
|
@ep_info: Address of structure describing properties of EP
|
|
to be enabled
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
dwc_xdci_ep_disable (
|
|
IN VOID *core_handle,
|
|
IN USB_EP_INFO *ep_info
|
|
)
|
|
{
|
|
XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle;
|
|
UINT32 ep_num;
|
|
UINTN base_addr;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_ep_disable: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
base_addr = local_core_handle->base_address;
|
|
|
|
/* Convert to physical endpoint */
|
|
ep_num = dwc_xdci_get_physical_ep_num (ep_info->ep_num, ep_info->ep_dir);
|
|
|
|
/* Disable Physical Endpoint ep_num */
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_EP_DALEPENA_REG,
|
|
usb_reg_read (base_addr, DWC_XDCI_EP_DALEPENA_REG) & ~(1 << ep_num)
|
|
);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Interface:
|
|
This function is used to STALL and endpoint
|
|
@core_handle: xDCI controller handle
|
|
@ep_info: Address of structure describing properties of EP
|
|
to be enabled
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
dwc_xdci_ep_stall (
|
|
IN VOID *core_handle,
|
|
IN USB_EP_INFO *ep_info
|
|
)
|
|
{
|
|
XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle;
|
|
DWC_XDCI_ENDPOINT_CMD_PARAMS ep_cmd_params;
|
|
EFI_STATUS Status;
|
|
UINT32 ep_num;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_ep_stall: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
/* Convert to physical endpoint */
|
|
ep_num = dwc_xdci_get_physical_ep_num (ep_info->ep_num, ep_info->ep_dir);
|
|
|
|
/* Set Ep State Info */
|
|
if (local_core_handle->ep_handles[ep_num].state != USB_EP_STATE_STALLED) {
|
|
local_core_handle->ep_handles[ep_num].Orgstate = local_core_handle->ep_handles[ep_num].state;
|
|
local_core_handle->ep_handles[ep_num].state = USB_EP_STATE_STALLED;
|
|
}
|
|
/* Issue a DWC_XDCI_EPCMD_SET_STALL for EP */
|
|
/* Reset params */
|
|
ep_cmd_params.param0 = ep_cmd_params.param1 = ep_cmd_params.param2 = 0;
|
|
|
|
/* Issue the command */
|
|
Status = dwc_xdci_core_issue_ep_cmd (
|
|
local_core_handle,
|
|
ep_num,
|
|
DWC_XDCI_EPCMD_SET_STALL,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_ep_stall: Failed to issue EP stall command\n"));
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Interface:
|
|
This function is used to clear endpoint STALL
|
|
@core_handle: xDCI controller handle
|
|
@ep_info: Address of structure describing properties of EP
|
|
to be enabled
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
dwc_xdci_ep_clear_stall (
|
|
IN VOID *core_handle,
|
|
IN USB_EP_INFO *ep_info
|
|
)
|
|
{
|
|
XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle;
|
|
DWC_XDCI_ENDPOINT_CMD_PARAMS ep_cmd_params;
|
|
EFI_STATUS Status;
|
|
UINT32 ep_num;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_ep_clear_stall: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
/* Convert to physical endpoint */
|
|
ep_num = dwc_xdci_get_physical_ep_num (ep_info->ep_num, ep_info->ep_dir);
|
|
|
|
/* Set Ep State Info */
|
|
local_core_handle->ep_handles[ep_num].state = local_core_handle->ep_handles[ep_num].Orgstate;
|
|
|
|
/* Issue a DWC_XDCI_EPCMD_CLEAR_STALL for EP */
|
|
/* Reset params */
|
|
ep_cmd_params.param0 = ep_cmd_params.param1 = ep_cmd_params.param2 = 0;
|
|
|
|
/* Issue the command */
|
|
Status = dwc_xdci_core_issue_ep_cmd (
|
|
local_core_handle,
|
|
ep_num,
|
|
DWC_XDCI_EPCMD_CLEAR_STALL,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_ep_stall: Failed to issue EP clea stall command\n"));
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Interface:
|
|
This function is used to set endpoint in NOT READY state
|
|
@core_handle: xDCI controller handle
|
|
@ep_info: Address of structure describing properties of EP
|
|
to be enabled
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
dwc_xdci_ep_set_nrdy (
|
|
IN VOID *core_handle,
|
|
IN USB_EP_INFO *ep_info
|
|
)
|
|
{
|
|
XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle;
|
|
UINT32 ep_num;
|
|
UINTN base_addr;
|
|
UINT32 max_delay_iter = DWC_XDCI_MAX_DELAY_ITERATIONS;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_ep_set_nrdy: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
base_addr = local_core_handle->base_address;
|
|
|
|
/* Convert to physical endpoint */
|
|
ep_num = dwc_xdci_get_physical_ep_num (ep_info->ep_num, ep_info->ep_dir);
|
|
|
|
/* Program the EP number in command's param reg */
|
|
usb_reg_write (base_addr, DWC_XDCI_DGCMD_PARAM_REG, ep_num);
|
|
|
|
/* Issue EP not ready generic device command */
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_DGCMD_REG,
|
|
(usb_reg_read (base_addr, DWC_XDCI_DGCMD_REG) | DWC_XDCI_DGCMD_CMD_SET_EP_NRDY)
|
|
);
|
|
|
|
/* Activate the command */
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_DGCMD_REG,
|
|
(usb_reg_read (base_addr, DWC_XDCI_DGCMD_REG) | DWC_XDCI_DGCMD_CMD_ACTIVE_MASK)
|
|
);
|
|
|
|
/* Wait until command completes */
|
|
do {
|
|
if (!(usb_reg_read (base_addr, DWC_XDCI_DGCMD_REG) & DWC_XDCI_DGCMD_CMD_ACTIVE_MASK))
|
|
break;
|
|
else
|
|
gBS->Stall (DWC_XDCI_MAX_DELAY_ITERATIONS);
|
|
} while (--max_delay_iter);
|
|
|
|
if (!max_delay_iter) {
|
|
DEBUG ((DEBUG_INFO, "Failed to issue Command\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Interface:
|
|
This function is used to queue receive SETUP packet request
|
|
@core_handle: xDCI controller handle
|
|
@buffer: Address of buffer to receive SETUP packet
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
dwc_xdci_ep0_receive_setup_pkt (
|
|
IN VOID *core_handle,
|
|
IN UINT8 *buffer
|
|
)
|
|
{
|
|
XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle;
|
|
DWC_XDCI_ENDPOINT_CMD_PARAMS ep_cmd_params;
|
|
EFI_STATUS Status = EFI_DEVICE_ERROR;
|
|
DWC_XDCI_TRB *trb;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_ep0_receive_setup_pkt: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
local_core_handle->ep_handles[0].ep_info.ep_num = 0;
|
|
local_core_handle->ep_handles[0].ep_info.ep_dir = 0;
|
|
local_core_handle->ep_handles[0].state = USB_EP_STATE_SETUP;
|
|
trb = local_core_handle->trbs;
|
|
DEBUG ((DEBUG_INFO, "(dwc_xdci_ep0_receive_setup_pkt)\n"));
|
|
|
|
Status = dwc_xdci_core_init_trb (
|
|
local_core_handle,
|
|
trb,
|
|
TRBCTL_SETUP,
|
|
buffer,
|
|
8
|
|
);
|
|
|
|
if (Status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_ep0_receive_setup_pkt: Init TRB Failed \n"));
|
|
return Status;
|
|
}
|
|
|
|
/* Issue a DEPSTRTXFER for EP0 */
|
|
/* Reset params */
|
|
ep_cmd_params.param0 = ep_cmd_params.param1 = ep_cmd_params.param2 = 0;
|
|
|
|
/* Init the lower re-bits for TRB address */
|
|
ep_cmd_params.param1 = (UINT32)(UINTN)trb;
|
|
|
|
/* Issue the command */
|
|
Status = dwc_xdci_core_issue_ep_cmd (
|
|
local_core_handle,
|
|
0,
|
|
EPCMD_START_XFER,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (Status) {
|
|
DEBUG ((DEBUG_INFO, "\ndwc_xdci_ep0_receive_setup_pkt: Failed to issue Start Transfer command"));
|
|
}
|
|
|
|
/* Save new resource index for this transfer */
|
|
local_core_handle->ep_handles[0].currentXferRscIdx = ((usb_reg_read(local_core_handle->base_address, DWC_XDCI_EPCMD_REG(0)) &
|
|
DWC_XDCI_EPCMD_RES_IDX_MASK) >> DWC_XDCI_EPCMD_RES_IDX_BIT_POS
|
|
);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Interface:
|
|
This function is used to queue receive status packet on EP0
|
|
@core_handle: xDCI controller handle
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
dwc_xdci_ep0_receive_status_pkt (
|
|
IN VOID *core_handle
|
|
)
|
|
{
|
|
XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle;
|
|
DWC_XDCI_TRB *trb;
|
|
DWC_XDCI_TRB_CONTROL trb_ctrl;
|
|
DWC_XDCI_ENDPOINT_CMD_PARAMS ep_cmd_params;
|
|
EFI_STATUS Status;
|
|
UINTN base_addr;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_ep0_receive_status_pkt: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
base_addr = local_core_handle->base_address;
|
|
|
|
/* We are receiving on EP0 so physical EP is 0 */
|
|
trb = local_core_handle->trbs;
|
|
DEBUG ((DEBUG_INFO, "(dwc_xdci_ep0_receive_status_pkt)\n"));
|
|
if (trb->trb_ctrl & DWC_XDCI_TRB_CTRL_HWO_MASK) {
|
|
DEBUG ((DEBUG_INFO, "status_pkt still not transferred.\n"));
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
local_core_handle->ep_handles[0].ep_info.ep_num = 0;
|
|
local_core_handle->ep_handles[0].ep_info.ep_dir = 0;
|
|
|
|
/* OUT data phase for 3-phased control transfer */
|
|
trb_ctrl = TRBCTL_3_PHASE;
|
|
|
|
/* Init TRB for the transfer */
|
|
Status = dwc_xdci_core_init_trb (
|
|
local_core_handle,
|
|
trb,
|
|
trb_ctrl,
|
|
local_core_handle->aligned_setup_buffer,
|
|
0
|
|
);
|
|
|
|
if (!Status) {
|
|
/* Issue a DEPSTRTXFER for EP0 */
|
|
/* Reset params */
|
|
ep_cmd_params.param0 = ep_cmd_params.param1 = ep_cmd_params.param2 = 0;
|
|
|
|
/* Init the lower bits for TRB address */
|
|
ep_cmd_params.param1 = (UINT32)(UINTN)trb;
|
|
|
|
/* Issue the command */
|
|
Status = dwc_xdci_core_issue_ep_cmd (
|
|
local_core_handle,
|
|
0,
|
|
EPCMD_START_XFER,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (Status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_ep0_receive_status_pkt: Failed to issue Start Transfer command for EP0\n"));
|
|
}
|
|
|
|
/* Save new resource index for this transfer */
|
|
local_core_handle->ep_handles[0].currentXferRscIdx = ((usb_reg_read(base_addr, DWC_XDCI_EPCMD_REG(0)) & DWC_XDCI_EPCMD_RES_IDX_MASK) >> DWC_XDCI_EPCMD_RES_IDX_BIT_POS);
|
|
|
|
/* TODO: We are not using the EP state for control transfers
|
|
* right now simply because we're only supporting IN
|
|
* data phase. For the current use case, we don't
|
|
* need OUT data phase. We can add that later and we will
|
|
* add some of the state and SETUP packet awareness code
|
|
*/
|
|
local_core_handle->ep_handles[0].state = USB_EP_STATE_STATUS;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Interface:
|
|
This function is used to send status packet on EP0
|
|
@core_handle: xDCI controller handle
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
dwc_xdci_ep0_send_status_pkt (
|
|
IN VOID *core_handle
|
|
)
|
|
{
|
|
XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle;
|
|
DWC_XDCI_TRB *trb;
|
|
DWC_XDCI_ENDPOINT_CMD_PARAMS ep_cmd_params;
|
|
EFI_STATUS Status;
|
|
UINTN base_addr;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_ep0_send_status_pkt: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
base_addr = local_core_handle->base_address;
|
|
|
|
/* We are sending on EP0 so physical EP is 1 */
|
|
trb = (local_core_handle->trbs + (1 * DWC_XDCI_TRB_NUM));
|
|
DEBUG ((DEBUG_INFO, "(dwc_xdci_ep0_send_status_pkt)\n"));
|
|
|
|
local_core_handle->ep_handles[0].state = USB_EP_STATE_STATUS;
|
|
Status = dwc_xdci_core_init_trb (
|
|
local_core_handle,
|
|
trb,
|
|
TRBCTL_2_PHASE,
|
|
local_core_handle->aligned_misc_buffer,
|
|
0
|
|
);
|
|
|
|
if (Status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_ep0_send_status_pkt: TRB failed during status phase\n"));
|
|
return Status;
|
|
}
|
|
|
|
/* Issue a DEPSTRTXFER for EP1 */
|
|
/* Reset params */
|
|
ep_cmd_params.param0 = ep_cmd_params.param1 = ep_cmd_params.param2 = 0;
|
|
|
|
/* Init the lower re-bits for TRB address */
|
|
ep_cmd_params.param1 = (UINT32)(UINTN)trb;
|
|
|
|
/* Issue the command */
|
|
Status = dwc_xdci_core_issue_ep_cmd (
|
|
local_core_handle,
|
|
1,
|
|
EPCMD_START_XFER,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (Status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_ep0_send_status_pkt: Failed to issue Start Transfer on EP0\n"));
|
|
}
|
|
|
|
/* Save new resource index for this transfer */
|
|
local_core_handle->ep_handles[1].currentXferRscIdx = ((usb_reg_read(base_addr, DWC_XDCI_EPCMD_REG(1)) & DWC_XDCI_EPCMD_RES_IDX_MASK) >> DWC_XDCI_EPCMD_RES_IDX_BIT_POS);
|
|
local_core_handle->ep_handles[0].state = USB_EP_STATE_STATUS;
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Interface:
|
|
This function is used to send data on non-EP0 endpoint
|
|
@core_handle: xDCI controller handle
|
|
@ep_info: Address of structure describing properties of EP
|
|
@buffer: buffer containing data to transmit
|
|
@size: Size of transfer (in bytes)
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
dwc_xdci_ep_tx_data (
|
|
IN VOID *core_handle,
|
|
IN USB_XFER_REQUEST *xfer_req
|
|
)
|
|
{
|
|
XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle;
|
|
DWC_XDCI_ENDPOINT_CMD_PARAMS ep_cmd_params;
|
|
DWC_XDCI_TRB *trb;
|
|
DWC_XDCI_TRB_CONTROL trb_ctrl;
|
|
EFI_STATUS Status;
|
|
UINT32 ep_num;
|
|
UINTN base_addr;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_ep_tx_data: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
if (xfer_req == NULL) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_ep_tx_data: INVALID transfer request\n"));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
base_addr = local_core_handle->base_address;
|
|
|
|
/* Convert to physical endpoint */
|
|
ep_num = dwc_xdci_get_physical_ep_num (
|
|
xfer_req->ep_info.ep_num,
|
|
xfer_req->ep_info.ep_dir
|
|
);
|
|
|
|
trb = (local_core_handle->trbs + (ep_num * DWC_XDCI_TRB_NUM));
|
|
DEBUG ((DEBUG_INFO, "(dwc_xdci_ep_tx_data)ep_num is %d\n", ep_num));
|
|
|
|
|
|
if (ep_num > 1)
|
|
trb_ctrl = TRBCTL_NORMAL;
|
|
else
|
|
trb_ctrl = TRBCTL_CTRL_DATA_PHASE;
|
|
|
|
if (trb->trb_ctrl & DWC_XDCI_TRB_CTRL_HWO_MASK) {
|
|
Status = dwc_xdci_end_xfer (local_core_handle, ep_num);
|
|
if (Status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_ep_tx_data: Failed to end previous transfer\n"));
|
|
}
|
|
|
|
Status = dwc_xdci_core_flush_ep_tx_fifo (local_core_handle, ep_num);
|
|
if (Status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_ep_tx_data: Failed to end previous transfer\n"));
|
|
}
|
|
}
|
|
|
|
/* Data phase */
|
|
// local_core_handle->ep_handles[ep_num].xfer_handle = *xfer_req;
|
|
CopyMem (&(local_core_handle->ep_handles[ep_num].xfer_handle), xfer_req, sizeof (USB_XFER_REQUEST));
|
|
local_core_handle->ep_handles[ep_num].state = USB_EP_STATE_DATA;
|
|
|
|
local_core_handle->ep_handles[ep_num].trb = trb;
|
|
|
|
Status = dwc_xdci_core_init_trb (
|
|
local_core_handle,
|
|
trb,
|
|
trb_ctrl,
|
|
xfer_req->xfer_buffer,
|
|
xfer_req->xfer_len
|
|
);
|
|
|
|
if (Status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_ep_tx_data: TRB failed\n"));
|
|
return Status;
|
|
}
|
|
|
|
/* Issue a DEPSTRTXFER for EP */
|
|
/* Reset params */
|
|
ep_cmd_params.param0 = ep_cmd_params.param1 = ep_cmd_params.param2 = 0;
|
|
|
|
/* Init the lower re-bits for TRB address */
|
|
ep_cmd_params.param1 = (UINT32)(UINTN)trb;
|
|
|
|
/* Issue the command */
|
|
Status = dwc_xdci_core_issue_ep_cmd (
|
|
local_core_handle,
|
|
ep_num,
|
|
EPCMD_START_XFER,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
/* Save new resource index for this transfer */
|
|
local_core_handle->ep_handles[ep_num].currentXferRscIdx = ((usb_reg_read (base_addr, DWC_XDCI_EPCMD_REG(ep_num)) & DWC_XDCI_EPCMD_RES_IDX_MASK) >> DWC_XDCI_EPCMD_RES_IDX_BIT_POS);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Interface:
|
|
This function is used to receive data on non-EP0 endpoint
|
|
@core_handle: xDCI controller handle
|
|
@ep_info: Address of structure describing properties of EP
|
|
@buffer: buffer containing data to transmit
|
|
@size: Size of transfer (in bytes)
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
dwc_xdci_ep_rx_data (
|
|
IN VOID *core_handle,
|
|
IN USB_XFER_REQUEST *xfer_req
|
|
)
|
|
{
|
|
XDCI_CORE_HANDLE *local_core_handle = (XDCI_CORE_HANDLE *)core_handle;
|
|
DWC_XDCI_ENDPOINT_CMD_PARAMS ep_cmd_params;
|
|
DWC_XDCI_TRB *trb;
|
|
DWC_XDCI_TRB_CONTROL trb_ctrl;
|
|
EFI_STATUS Status;
|
|
UINT32 ep_num;
|
|
UINTN base_addr;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_ep_rx_data: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
if (xfer_req == NULL) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_ep_rx_data: INVALID transfer request\n"));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
base_addr = local_core_handle->base_address;
|
|
|
|
/* Convert to physical endpoint */
|
|
ep_num = dwc_xdci_get_physical_ep_num (xfer_req->ep_info.ep_num, xfer_req->ep_info.ep_dir);
|
|
|
|
trb = (local_core_handle->trbs + (ep_num * DWC_XDCI_TRB_NUM));
|
|
DEBUG ((DEBUG_INFO, "(dwc_xdci_ep_rx_data)ep_num is %d\n", ep_num));
|
|
|
|
if (ep_num > 1)
|
|
trb_ctrl = TRBCTL_NORMAL;
|
|
else
|
|
trb_ctrl = TRBCTL_CTRL_DATA_PHASE;
|
|
|
|
//
|
|
// If CheckFlag didn't set to FALSE, means the previous transfer request didn't complete,
|
|
// need to wait the previous request done.
|
|
//
|
|
if (local_core_handle->ep_handles[ep_num].CheckFlag == TRUE) {
|
|
return EFI_NOT_READY;
|
|
}
|
|
|
|
local_core_handle->ep_handles[ep_num].CheckFlag = TRUE;
|
|
|
|
/* Data phase */
|
|
CopyMem (&(local_core_handle->ep_handles[ep_num].xfer_handle), xfer_req, sizeof (USB_XFER_REQUEST));
|
|
|
|
local_core_handle->ep_handles[ep_num].state = USB_EP_STATE_DATA;
|
|
|
|
local_core_handle->ep_handles[ep_num].trb = trb;
|
|
|
|
DEBUG ((DEBUG_INFO, "(dwc_xdci_ep_rx_data)xfer_req->xfer_len is 0x%x\n", xfer_req->xfer_len));
|
|
|
|
Status = dwc_xdci_core_init_trb (
|
|
local_core_handle,
|
|
trb,
|
|
trb_ctrl,
|
|
xfer_req->xfer_buffer,
|
|
xfer_req->xfer_len
|
|
);
|
|
|
|
if (Status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_ep_rx_data: TRB failed\n"));
|
|
return Status;
|
|
}
|
|
|
|
/* Issue a DEPSTRTXFER for EP */
|
|
/* Reset params */
|
|
ep_cmd_params.param0 = ep_cmd_params.param1 = ep_cmd_params.param2 = 0;
|
|
|
|
/* Init the lower re-bits for TRB address */
|
|
ep_cmd_params.param1 = (UINT32)(UINTN)trb;
|
|
|
|
/* Issue the command */
|
|
Status = dwc_xdci_core_issue_ep_cmd (
|
|
local_core_handle,
|
|
ep_num,
|
|
EPCMD_START_XFER,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (Status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_ep_rx_data: Failed to start transfer\n"));
|
|
}
|
|
|
|
/* Save new resource index for this transfer */
|
|
local_core_handle->ep_handles[ep_num].currentXferRscIdx = ((usb_reg_read(base_addr, DWC_XDCI_EPCMD_REG(ep_num)) & DWC_XDCI_EPCMD_RES_IDX_MASK) >> DWC_XDCI_EPCMD_RES_IDX_BIT_POS);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
dwc_xdci_core_flush_ep_fifo (
|
|
IN XDCI_CORE_HANDLE *core_handle,
|
|
IN UINT32 ep_num
|
|
)
|
|
{
|
|
UINTN base_addr;
|
|
UINT32 max_delay_iter = DWC_XDCI_MAX_DELAY_ITERATIONS;
|
|
UINT32 fifo_num;
|
|
UINT32 Param;
|
|
UINT32 Cmd;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "ERROR: dwc_xdci_core_flush_ep_tx_fifo: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
base_addr = core_handle->base_address;
|
|
|
|
/* Translate to FIFOnum
|
|
* NOTE: Assuming this is a Tx EP
|
|
*/
|
|
fifo_num = (ep_num >> 1);
|
|
|
|
/* TODO: Currently we are only using TxFIFO 0. Later map these
|
|
* Write the FIFO num/dir param for the generic command.
|
|
*/
|
|
|
|
Param = usb_reg_read (base_addr, DWC_XDCI_DGCMD_PARAM_REG);
|
|
Param &= ~(DWC_XDCI_DGCMD_PARAM_TX_FIFO_NUM_MASK | DWC_XDCI_DGCMD_PARAM_TX_FIFO_DIR_MASK);
|
|
|
|
if ((ep_num & 0x01) != 0) {
|
|
Param |= (fifo_num | DWC_XDCI_DGCMD_PARAM_TX_FIFO_DIR_MASK);
|
|
} else {
|
|
Param |= fifo_num;
|
|
}
|
|
|
|
Cmd = usb_reg_read(base_addr, DWC_XDCI_DGCMD_REG) & ~(DWC_XDCI_DGCMD_CMD_TYPE_MASK | DWC_XDCI_DGCMD_CMD_IOC_MASK);
|
|
|
|
DEBUG ((DEBUG_INFO, "USB FU Flash: CMD 0x%08x :: Param 0x%08x\n",
|
|
(Cmd | DWC_XDCI_DGCMD_CMD_SEL_FIFO_FLUSH | DWC_XDCI_DGCMD_CMD_ACTIVE_MASK),
|
|
Param));
|
|
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_DGCMD_PARAM_REG,
|
|
Param
|
|
);
|
|
|
|
/* Write the command to flush all FIFOs */
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_DGCMD_REG,
|
|
(Cmd | DWC_XDCI_DGCMD_CMD_SEL_FIFO_FLUSH | DWC_XDCI_DGCMD_CMD_ACTIVE_MASK)
|
|
);
|
|
|
|
|
|
/* Wait until command completes */
|
|
do {
|
|
if (!(usb_reg_read(base_addr, DWC_XDCI_DGCMD_REG) & DWC_XDCI_DGCMD_CMD_ACTIVE_MASK))
|
|
break;
|
|
else
|
|
gBS->Stall (DWC_XDCI_MAX_DELAY_ITERATIONS);
|
|
} while (--max_delay_iter);
|
|
|
|
if (!max_delay_iter) {
|
|
DEBUG ((DEBUG_INFO, "Failed to issue Command\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Interface:
|
|
This function is used to cancel a transfer on non-EP0 endpoint
|
|
@core_handle: xDCI controller handle
|
|
@ep_info: Address of structure describing properties of EP
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
dwc_xdci_ep_cancel_transfer (
|
|
IN VOID *core_handle,
|
|
IN USB_EP_INFO *ep_info
|
|
)
|
|
{
|
|
EFI_STATUS Status = EFI_DEVICE_ERROR;
|
|
UINT32 ep_num;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_ep_cancel_transfer: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
/* Get physical EP num */
|
|
ep_num = dwc_xdci_get_physical_ep_num (ep_info->ep_num, ep_info->ep_dir);
|
|
Status = dwc_xdci_end_xfer(core_handle, ep_num);
|
|
dwc_xdci_core_flush_ep_fifo(core_handle, ep_num);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
EFI_STATUS
|
|
usb_process_device_reset_det (
|
|
IN XDCI_CORE_HANDLE *core_handle
|
|
)
|
|
{
|
|
return dwc_xdci_process_device_reset_det (core_handle);
|
|
}
|
|
|
|
EFI_STATUS
|
|
usb_process_device_reset_done (
|
|
IN XDCI_CORE_HANDLE *core_handle
|
|
)
|
|
{
|
|
return dwc_xdci_process_device_reset_done (core_handle);
|
|
}
|
|
|
|
UINT32
|
|
usb_get_physical_ep_num (
|
|
IN UINT32 EndpointNum,
|
|
IN USB_EP_DIR EndpointDir
|
|
)
|
|
{
|
|
return dwc_xdci_get_physical_ep_num(
|
|
EndpointNum,
|
|
EndpointDir
|
|
);
|
|
}
|
|
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
usb_xdci_core_reinit (
|
|
// IN USB_DEV_CONFIG_PARAMS *ConfigParams,
|
|
// IN VOID *device_core_ptr,
|
|
IN VOID *core_handle
|
|
)
|
|
{
|
|
EFI_STATUS status = EFI_DEVICE_ERROR;
|
|
UINTN base_addr;
|
|
XDCI_CORE_HANDLE *local_core_handle;
|
|
DWC_XDCI_ENDPOINT_CMD_PARAMS ep_cmd_params;
|
|
|
|
local_core_handle = core_handle;
|
|
|
|
if (core_handle == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (local_core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to allocate handle for xDCI\n"));
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
base_addr = local_core_handle->base_address;
|
|
|
|
/* Issue DEPSTARTCFG command for EP0 */
|
|
status = dwc_xdci_core_init_ep_cmd_params (
|
|
local_core_handle,
|
|
&local_core_handle->ep_handles[0].ep_info,
|
|
DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_NONE,
|
|
EPCMD_START_NEW_CONFIG,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to init params for START_NEW_CONFIG EP command on xDCI\n"));
|
|
return status;
|
|
}
|
|
|
|
/* Issue the command */
|
|
status = dwc_xdci_core_issue_ep_cmd (
|
|
local_core_handle,
|
|
0,
|
|
EPCMD_START_NEW_CONFIG,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to issue START_NEW_CONFIG EP command on xDCI\n"));
|
|
return status;
|
|
}
|
|
|
|
/* Issue DEPCFG command for EP0 */
|
|
status = dwc_xdci_core_init_ep_cmd_params (
|
|
local_core_handle,
|
|
&local_core_handle->ep_handles[0].ep_info,
|
|
DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_INIT_STATE,
|
|
EPCMD_SET_EP_CONFIG,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to init params for SET_EP_CONFIG command on xDCI for EP0\n"));
|
|
return status;
|
|
}
|
|
|
|
/* Issue the command */
|
|
status = dwc_xdci_core_issue_ep_cmd (
|
|
local_core_handle,
|
|
0,
|
|
EPCMD_SET_EP_CONFIG,
|
|
&ep_cmd_params);
|
|
|
|
if (status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to issue SET_EP_CONFIG command on xDCI for EP0\n"));
|
|
return status;
|
|
}
|
|
|
|
/* Issue DEPCFG command for EP1 */
|
|
status = dwc_xdci_core_init_ep_cmd_params (
|
|
local_core_handle,
|
|
&local_core_handle->ep_handles[1].ep_info,
|
|
DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_INIT_STATE,
|
|
EPCMD_SET_EP_CONFIG,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to init params for SET_EP_CONFIG command on xDCI for EP1\n"));
|
|
return status;
|
|
}
|
|
|
|
/* Issue the command */
|
|
status = dwc_xdci_core_issue_ep_cmd (
|
|
local_core_handle,
|
|
1,
|
|
EPCMD_SET_EP_CONFIG,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to issue SET_EP_CONFIG command on xDCI for EP1\n"));
|
|
return status;
|
|
}
|
|
|
|
/* Issue DEPXFERCFG command for EP0 */
|
|
status = dwc_xdci_core_init_ep_cmd_params (
|
|
local_core_handle,
|
|
&local_core_handle->ep_handles[0].ep_info,
|
|
DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_NONE,
|
|
EPCMD_SET_EP_XFER_RES_CONFIG,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to init params for EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP0\n"));
|
|
return status;
|
|
}
|
|
|
|
/* Issue the command */
|
|
status = dwc_xdci_core_issue_ep_cmd (
|
|
local_core_handle,
|
|
0,
|
|
EPCMD_SET_EP_XFER_RES_CONFIG,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to issue EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP0\n"));
|
|
return status;
|
|
}
|
|
|
|
/* Issue DEPXFERCFG command for EP1 */
|
|
status = dwc_xdci_core_init_ep_cmd_params (
|
|
local_core_handle,
|
|
&local_core_handle->ep_handles[1].ep_info,
|
|
DWC_XDCI_PARAM0_SET_EP_CFG_ACTN_NONE,
|
|
EPCMD_SET_EP_XFER_RES_CONFIG,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to init params for EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP1\n"));
|
|
return status;
|
|
}
|
|
|
|
/* Issue the command */
|
|
status = dwc_xdci_core_issue_ep_cmd (
|
|
local_core_handle,
|
|
1,
|
|
EPCMD_SET_EP_XFER_RES_CONFIG,
|
|
&ep_cmd_params
|
|
);
|
|
|
|
if (status) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_core_init: Failed to issue EPCMD_SET_EP_XFER_RES_CONFIG command on xDCI for EP1\n"));
|
|
return status;
|
|
}
|
|
|
|
/* Prepare a buffer for SETUP packet */
|
|
local_core_handle->trbs = (DWC_XDCI_TRB *)(UINTN)((UINT32)(UINTN)
|
|
local_core_handle->unaligned_trbs +
|
|
(DWC_XDCI_TRB_BYTE_ALIGNMENT -
|
|
((UINT32)(UINTN)local_core_handle->unaligned_trbs %
|
|
DWC_XDCI_TRB_BYTE_ALIGNMENT)));
|
|
|
|
DEBUG ((DEBUG_INFO, "(dwc_xdci_core_init)@@@@@@@@@ unaligned_trbs address is 0x%x\n", local_core_handle->unaligned_trbs));
|
|
DEBUG ((DEBUG_INFO, "(dwc_xdci_core_init)@@@@@@@@@ TRB address is 0x%x\n", local_core_handle->trbs));
|
|
|
|
/* Allocate Setup buffer that is 8-byte aligned */
|
|
local_core_handle->aligned_setup_buffer = local_core_handle->default_setup_buffer +
|
|
(DWC_XDCI_SETUP_BUFF_SIZE -
|
|
((UINT32)(UINTN)(local_core_handle->default_setup_buffer) % DWC_XDCI_SETUP_BUFF_SIZE));
|
|
|
|
/* Aligned buffer for status phase */
|
|
local_core_handle->aligned_misc_buffer = local_core_handle->misc_buffer +
|
|
(DWC_XDCI_SETUP_BUFF_SIZE -
|
|
((UINT32)(UINTN)(local_core_handle->aligned_misc_buffer) % DWC_XDCI_SETUP_BUFF_SIZE));
|
|
|
|
/* We will queue SETUP request when we see bus reset */
|
|
|
|
/* Enable Physical Endpoints 0 */
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_EP_DALEPENA_REG,
|
|
usb_reg_read (base_addr, DWC_XDCI_EP_DALEPENA_REG) | (1 << 0)
|
|
);
|
|
|
|
/* Enable Physical Endpoints 1 */
|
|
usb_reg_write (
|
|
base_addr,
|
|
DWC_XDCI_EP_DALEPENA_REG,
|
|
usb_reg_read (base_addr, DWC_XDCI_EP_DALEPENA_REG) | (1 << 1)
|
|
);
|
|
|
|
DEBUG ((DEBUG_INFO, "Default value of xDCI DWC_XDCI_DEVTEN_REG: 0x%x\n", usb_reg_read (base_addr, DWC_XDCI_DEVTEN_REG)));
|
|
return status;
|
|
|
|
|
|
}
|
|
|
|
|
|
EFI_STATUS
|
|
usb_xdci_core_flush_ep_fifo (
|
|
IN VOID *core_handle,
|
|
IN USB_EP_INFO *ep_info
|
|
)
|
|
{
|
|
EFI_STATUS Status = EFI_DEVICE_ERROR;
|
|
UINT32 ep_num;
|
|
|
|
if (core_handle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "dwc_xdci_ep_cancel_transfer: INVALID handle\n"));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
/* Get physical EP num */
|
|
ep_num = dwc_xdci_get_physical_ep_num (ep_info->ep_num, ep_info->ep_dir);
|
|
dwc_xdci_core_flush_ep_fifo(core_handle, ep_num);
|
|
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
usb_process_device_disconnect (
|
|
IN XDCI_CORE_HANDLE *core_handle
|
|
)
|
|
{
|
|
return dwc_xdci_core_disconnect (core_handle);
|
|
}
|
|
|
|
#if defined(_MSC_VER) /* MSVC Compiler section */
|
|
#pragma optimize ("", on)
|
|
#endif
|