/****************************************************************************** | |
* | |
* (C)Copyright 2013 Marvell. All Rights Reserved. | |
* | |
* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF MARVELL. | |
* The copyright notice above does not evidence any actual or intended | |
* publication of such source code. | |
* This Module contains Proprietary Information of Marvell and should be | |
* treated as Confidential. | |
* The information in this file is provided for the exclusive use of the | |
* licensees of Marvell. | |
* Such users have the right to use, modify, and incorporate this code into | |
* products for purposes authorized by the license agreement provided they | |
* include this notice and the associated copyright notice with any such | |
* product. | |
* The information in this file is provided "AS IS" without warranty. | |
* | |
******************************************************************************/ | |
#include "Typedef.h" | |
#include "misc.h" | |
#include "timer.h" | |
#include "usb_descriptors.h" | |
#include "usb2_main.h" | |
#include "usb2_memory.h" | |
#include "usb2_enumeration.h" | |
#include "ProtocolManager.h" | |
UINT_T USB2D_Initialize(UINT base_address, UINT int_number) | |
{ | |
P_DC_Properties_T pDCProps; | |
#if !USB_DEVICE_ONLY | |
UINT session; | |
#endif | |
//Try and get a new USB 2 'Object' | |
pDCProps = Allocate_USB2_Device(int_number); | |
//Check for error | |
if(pDCProps == NULL) | |
return NotFoundError; //mdb make new error code | |
//store the base_address | |
pDCProps->pUSBRegs = (P_USB2Device_Regs_T) base_address; | |
//reset and restore the controller | |
USB2D_Controller_Setup(pDCProps); | |
//setup EP 0 | |
USB2D_Endpoint_Setup(pDCProps, USB_ENDPOINT_0, USB_IN, USB_CNTRL); | |
USB2D_Endpoint_Setup(pDCProps, USB_ENDPOINT_0, USB_OUT, USB_CNTRL); | |
//device only: there is no OTGSC register, so we cannot check B/A session valid bits | |
#if USB_DEVICE_ONLY | |
//no way to check if a device is connect. assume it is and hit the RUN bit | |
reg_bit_set(&pDCProps->pUSBRegs->USB_CMD, BIT0); | |
#else | |
//are we A or B device? | |
session = (pDCProps->pUSBRegs->OTGSC & BIT8) >> 8; | |
//is the session (A or B) valid? | |
if(pDCProps->pUSBRegs->OTGSC & (BIT10 << session)) | |
{ // -yes- set run bit | |
reg_bit_set(&pDCProps->pUSBRegs->USB_CMD, BIT0); | |
} | |
else | |
{ // -no- set session INT bit | |
reg_bit_set(&pDCProps->pUSBRegs->OTGSC, (BIT26 << session)); | |
} | |
#endif | |
return NoError; | |
} | |
void USB2D_Shutdown(UINT intnum) | |
{ | |
UINT32 start_time; | |
P_DC_Properties_T pDCProps = Get_DC_Properties(intnum); | |
//bogus input. caller screwed up. return | |
if (pDCProps == NULL) | |
return; | |
//First check for any primed endpoint and wait a short time for them to complete | |
start_time = GetOSCR0(); | |
while(pDCProps->pUSBRegs->ENDPTSTATUS) | |
{ | |
if(OSCR0IntervalInMilli(start_time, GetOSCR0()) > USB2D_SHUTDOWN_TIME_MS) | |
break; //timed out. break from loop | |
} | |
//Second, check for any remaining endpoint activity and flush it | |
if(pDCProps->pUSBRegs->ENDPTSTATUS) | |
pDCProps->pUSBRegs->ENDPTFLUSH = pDCProps->pUSBRegs->ENDPTSTATUS; | |
//third, clear up the controller | |
//Reset the usb address | |
reg_write(&pDCProps->pUSBRegs->DEVICE_ADDR, BIT24); | |
//clear the EP SETUP statuses (stati?) | |
reg_write(&pDCProps->pUSBRegs->ENDPT_SETUP_STAT, 0xFFFF); | |
//clear any complete bits | |
reg_write(&pDCProps->pUSBRegs->ENDPTCOMPLETE, 0xFFFFFFFF); | |
//make sure to clear ALL status bits (write 1 to clear register) | |
pDCProps->pUSBRegs->USB_STS |= 1; | |
//disable individual status enables | |
pDCProps->pUSBRegs->USB_INTR = 0; | |
//lastly, shut off the controller | |
reg_bit_clr(&pDCProps->pUSBRegs->USB_CMD, BIT0); | |
} | |
void USB2D_ISR(UINT intnum) | |
{ | |
UINT status_bits; | |
P_DC_Properties_T pDCProps = Get_DC_Properties(intnum); | |
//bogus input. caller screwed up. return | |
if (pDCProps == NULL) | |
return; | |
#if !USB_DEVICE_ONLY | |
//check and see if A or B session triggered the interrupt | |
if( pDCProps->pUSBRegs->OTGSC & (BIT19 | BIT18) ) | |
{ //clear the session interrupts | |
reg_bit_clr(&pDCProps->pUSBRegs->OTGSC, (BIT26 | BIT27)); | |
//hit GO on the controller | |
reg_bit_set(&pDCProps->pUSBRegs->USB_CMD, BIT0); | |
return; | |
} | |
#endif | |
//read the status bits and clear them | |
status_bits = pDCProps->pUSBRegs->USB_STS; | |
pDCProps->pUSBRegs->USB_STS |= status_bits; | |
//Normal Interrupt | |
if(status_bits & USB2D_INT_STS_INT) | |
{ | |
//Check for transaction complete ints | |
if(pDCProps->pUSBRegs->ENDPTCOMPLETE) | |
USB2D_Complete_Int(pDCProps); | |
//Check for any Setup Ints | |
if(pDCProps->pUSBRegs->ENDPT_SETUP_STAT) { | |
if (pDCProps->pUSBRegs->PORTSC & (BIT26 | BIT27) == 0x0) | |
UpdateUSBFsDeviceConfigDesc(); | |
USB2D_Setup_Int(pDCProps); | |
} | |
} | |
//Reset Interrupt | |
if(status_bits & USB2D_INT_STS_RESET) | |
USB2D_Reset_Int(pDCProps); | |
} | |
void USB2D_Controller_Setup(P_DC_Properties_T pDCProps) | |
{ | |
UINT start_time; | |
reg_bit_clr(&pDCProps->pUSBRegs->USB_CMD, BIT0); //STOP the controller | |
reg_bit_set(&pDCProps->pUSBRegs->USB_CMD, BIT1); //RESET the controller | |
//wait a bit for RESET bit to clear | |
start_time = GetOSCR0(); | |
while( (pDCProps->pUSBRegs->USB_CMD & BIT1) == BIT1) | |
{ | |
if(OSCR0IntervalInMilli(start_time, GetOSCR0()) > USB2D_CTRL_RESET_TIME_MS) | |
break; //timed out. break from loop | |
} | |
//set mode: DEVICE + SLOM | |
reg_write(&pDCProps->pUSBRegs->USB_MODE, BIT3 | BIT1); | |
//clear Endpoint Setup Status (Write 1 to clear) | |
reg_write(&pDCProps->pUSBRegs->ENDPT_SETUP_STAT, 0xFFFF); | |
#if FORCE_FULL_SPEED_USB && !SLE_TESTING | |
//force FULL SPEED: Port Control PFSC | |
reg_bit_set(&pDCProps->pUSBRegs->PORTSC, BIT24); | |
#endif | |
//store the Queue Head pointer into the List Register | |
pDCProps->pUSBRegs->EP_LIST_ADDR = ((UINT)pDCProps->pdQHHead) & 0xFFFFF800; //mask off lower 11 bits | |
//Reset Endpoint 0 RX and TX | |
reg_bit_set(&pDCProps->pUSBRegs->ENDPTCTRLX[0], BIT22 | BIT6); | |
//Clear the stall fields for Endpoint 0 | |
reg_bit_clr(&pDCProps->pUSBRegs->ENDPTCTRLX[0], BIT16 | BIT0); | |
//Set the value USB Interrupts: Reset and USB Int | |
reg_bit_set(&pDCProps->pUSBRegs->USB_INTR, USB2D_INT_STS_INT | USB2D_INT_STS_PORT_CHANGE | USB2D_INT_STS_RESET); | |
//Clear any latent status bits (Write 1 to clear) | |
reg_bit_set(&pDCProps->pUSBRegs->USB_STS, USB2D_INT_STS_INT | USB2D_INT_STS_PORT_CHANGE | USB2D_INT_STS_RESET); | |
return; | |
} | |
//Configures an endpoint | |
void USB2D_Endpoint_Setup(P_DC_Properties_T pDCProps, XLLP_USB_ENDPOINT_ID_T ep_num, XLLP_USB_EP_DIR_T direction, XLLP_USB_EP_TYPE_T type) | |
{ | |
UINT shift_amt; | |
//get the corresponding dQH | |
P_dQH_T pQH = (P_dQH_T)&(pDCProps->pdQHHead[ep_num*2+direction]); | |
//initialize the QH | |
pQH->dTD_Overlay.pNext_dTD = BIT0; //set the STOP bit for all QHs | |
//type specific settings | |
if(type == USB_BULK) | |
{ | |
pQH->EP_Caps.MaxPacketLen = (pDCProps->pUSBRegs->PORTSC & BIT27) ? 512 : 64; //initialize packet size based on speed | |
pQH->EP_Caps.ZLT = 1; //zero length terminate | |
} | |
if(type == USB_CNTRL) | |
{ | |
pQH->EP_Caps.MaxPacketLen = 64; //initialize packet size based on type | |
pQH->EP_Caps.IOS = 1; //set the IOS bit | |
} | |
//set the ENABLE + TYPE fields | |
shift_amt = (direction == USB_IN) ? 16 : 0; //helper to sort out RX/TX | |
reg_bit_clr(&pDCProps->pUSBRegs->ENDPTCTRLX[ep_num], 0xFFFF << shift_amt); | |
reg_bit_set(&pDCProps->pUSBRegs->ENDPTCTRLX[ep_num], ((type << 2) | BIT7 | BIT6) << shift_amt); | |
//for the other half of the EP, the TYPE must get changed to BULK (only if *not* enabled) | |
shift_amt = (direction == USB_IN) ? 0 : 16; //swap since checking other side | |
if (!( pDCProps->pUSBRegs->ENDPTCTRLX[ep_num] & (BIT7 << shift_amt) )) | |
{ reg_bit_clr(&pDCProps->pUSBRegs->ENDPTCTRLX[ep_num], 0xC << shift_amt); | |
reg_bit_set(&pDCProps->pUSBRegs->ENDPTCTRLX[ep_num], USB_BULK << (2+shift_amt)); | |
} | |
} | |
//reset interrupt handler | |
void USB2D_Reset_Int(P_DC_Properties_T pDCProps) | |
{ | |
//Flush all EPs | |
reg_write(&pDCProps->pUSBRegs->ENDPTFLUSH, 0xFFFFFFFF); | |
//Reset the usb address | |
reg_write(&pDCProps->pUSBRegs->DEVICE_ADDR, BIT24); | |
//clear the EP SETUP statuses (stati?) | |
reg_write(&pDCProps->pUSBRegs->ENDPT_SETUP_STAT, 0xFFFF); | |
//clear any complete bits | |
reg_write(&pDCProps->pUSBRegs->ENDPTCOMPLETE, 0xFFFFFFFF); | |
//Lastly, reset EP0 | |
USB2D_Endpoint_Setup(pDCProps, USB_ENDPOINT_0, USB_OUT, USB_CNTRL); | |
USB2D_Endpoint_Setup(pDCProps, USB_ENDPOINT_0, USB_IN, USB_CNTRL); | |
} | |
//endpoint setup interrupt handler - means an endpoint received a SETUP packet | |
void USB2D_Setup_Int(P_DC_Properties_T pDCProps) | |
{ | |
UINT status_bits; | |
XLLP_USB_SETUP_DATA_T setup_packet; | |
//read the status bits | |
status_bits = pDCProps->pUSBRegs->ENDPT_SETUP_STAT; | |
//Clear all other Setup packets (we only handles Setup packets on EP0!) | |
reg_bit_set(&pDCProps->pUSBRegs->ENDPT_SETUP_STAT, 0xFFFE); | |
//Check for EP0 Setup packet | |
if(status_bits & BIT0) | |
{ | |
//read the 8 setup bytes into the variable (all stuff inside '[]' will equal 0) | |
memcpy(&setup_packet, &pDCProps->pdQHHead[USB_ENDPOINT_0*2+USB_OUT].Setup, 8); | |
//make sure to clear the SETUP STATUS bit for EP0 | |
reg_bit_set(&pDCProps->pUSBRegs->ENDPT_SETUP_STAT, BIT0); | |
//call the handler to deal with the new packet | |
USB2D_EnumerationHandler(pDCProps, &setup_packet); | |
} | |
} | |
//endpoint complete interrupt handler - means a dTD finish (IOC bit) | |
void USB2D_Complete_Int(P_DC_Properties_T pDCProps) | |
{ | |
UINT counter, index; | |
UINT complete_bits; | |
//Read the EP Complete bits, then write 1 to clear them | |
complete_bits = pDCProps->pUSBRegs->ENDPTCOMPLETE; | |
pDCProps->pUSBRegs->ENDPTCOMPLETE = complete_bits; | |
//for each complete bit reset the QH and clear the used dTD chain | |
for(counter = 0; counter < (2*MAX_USB2_EPS); counter++) | |
{ //check for a complete bit and only then do the releasing | |
if( (1 << counter) & complete_bits) | |
{ //find the correct QH index | |
index = (counter > 15) ? (counter - 16) * 2 + 1 : counter * 2; | |
//now, make sure this isn't a false interrupt (check the status of the dToken) | |
if(pDCProps->pdQHHead[index].dTD_Overlay.dTDToken.Status & BIT7) | |
{ //false interrupt signature. | |
//ignore EPCOMPLETE for this EP | |
complete_bits &= ~(1 << counter); | |
} | |
else //real IOC interrupt | |
{ //find the dTD chain and release it back | |
ReleasedTDChain((P_dTD_T)(pDCProps->pdQHHead[index].initial_dTD)); | |
} | |
//update size counter for the last packet being a short packet | |
pDCProps->pdQHHead[index].tot_size -= pDCProps->pdQHHead[index].dTD_Overlay.dTDToken.Size; | |
} | |
} | |
//Endpoint 2 RX serve call here | |
if(complete_bits & BIT2) | |
USB2D_PM_Call(pDCProps); | |
} | |
void USB2D_PM_Call(P_DC_Properties_T pDCProps) | |
{ | |
UINT bytes, buffer; | |
P_USBAPI_T pUsbApi; | |
//grab info from the last RECEIVE operation | |
pUsbApi = GetUSBAPIhandle_InterruptNum(pDCProps->InterruptNum); | |
if (pUsbApi != NULL) | |
{ | |
bytes = pDCProps->pdQHHead[USB_ENDPOINT_B*2].tot_size; | |
buffer = pDCProps->pdQHHead[USB_ENDPOINT_B*2].buffer; | |
//call into Protocol Manager | |
PM_ISR(pUsbApi, bytes, (UINT*)buffer); | |
} | |
} | |
void USB2D_SendWrapper(UINT *buffer, UINT size, void * pUSBAPI) | |
{ | |
P_DC_Properties_T pDCProps; | |
//get the correct structure | |
pDCProps = Get_DC_Properties(((P_USBAPI_T)pUSBAPI)->interruptNum); | |
if (pDCProps != NULL) | |
{ | |
//call the trasmit routine | |
USB2D_EndpointTransmit(pDCProps, USB_ENDPOINT_A, USB_IN, (UINT)buffer, size); | |
} | |
return; | |
} | |
void USB2D_RecieveWrapper(UINT *buffer, UINT size, void * pUSBAPI) | |
{ | |
P_DC_Properties_T pDCProps; | |
//get the correct structure | |
pDCProps = Get_DC_Properties(((P_USBAPI_T)pUSBAPI)->interruptNum); | |
if (pDCProps != NULL) | |
{ | |
//call the trasmit routine | |
USB2D_EndpointTransmit(pDCProps, USB_ENDPOINT_B, USB_OUT, (UINT)buffer, size); | |
} | |
return; | |
} | |
void USB2D_EndpointTransmit(P_DC_Properties_T pDCProps, XLLP_USB_ENDPOINT_ID_T ep_num, XLLP_USB_EP_DIR_T direction, UINT buffer, UINT size) | |
{ | |
UINT qh_index, packet_size, dtd_size; | |
P_dTD_T pdTD, pdTD_first, pdTD_next; | |
//calculate which Queue Head to service | |
qh_index = ep_num*2+direction; | |
//find our packet size | |
packet_size = pDCProps->pdQHHead[qh_index].EP_Caps.MaxPacketLen;; | |
//grab an initial dTD | |
pdTD_first = pdTD = GetdTD(); | |
//check for memory shortage | |
if(pdTD == NULL) | |
{ | |
obm_printf("there is no dTD\n\r"); | |
return; | |
} | |
//save off info for software cleanup later on | |
pDCProps->pdQHHead[qh_index].initial_dTD = (UINT_T)(pdTD_first); | |
pDCProps->pdQHHead[qh_index].buffer = (UINT)buffer; | |
pDCProps->pdQHHead[qh_index].tot_size = 0; //will be updated in the loop below | |
//now lets build our dTD chain | |
do | |
{ | |
//adjust the size for this dTD | |
dtd_size = (size > MAX_LENGTH_TRANSFER) ? MAX_LENGTH_TRANSFER : size; | |
//input buffer | |
pdTD->BuffPtr[0] = (UINT)buffer; | |
pdTD->BuffPtr[1] = pdTD->BuffPtr[0] + 4096; | |
pdTD->BuffPtr[2] = pdTD->BuffPtr[1] + 4096; | |
pdTD->BuffPtr[3] = pdTD->BuffPtr[2] + 4096; | |
pdTD->BuffPtr[4] = pdTD->BuffPtr[3] + 4096; | |
//adjust counters | |
buffer += dtd_size; | |
size -= dtd_size; | |
pDCProps->pdQHHead[qh_index].tot_size += dtd_size; | |
//input size | |
pdTD->dTDToken.Size = dtd_size; | |
//set the dTD as active | |
pdTD->dTDToken.Status = 0x80; | |
//do we need to chain some descriptors? | |
if(size > 0) | |
{ | |
//Grab a new dTD (only if needed) | |
pdTD_next = GetdTD(); | |
//check for memory shortage | |
if(pdTD_next == NULL) | |
break; | |
//link the dTDs | |
pdTD->pNext_dTD = (UINT)pdTD_next; | |
//update dTD pointer | |
pdTD = pdTD_next; | |
} | |
} | |
while(size); | |
//for the LAST dTD, we need to set the stop bit and Interrupt on Complete | |
pdTD->pNext_dTD = BIT0; | |
pdTD->dTDToken.IOC = 1; | |
//link the FIRST dTD pointer to the Queue Head | |
pDCProps->pdQHHead[ep_num*2+direction].dTD_Overlay.pNext_dTD = (UINT)pdTD_first; | |
//make sure memory operation is finished before prime | |
dsb(); | |
//lastly prime the endpoint | |
pDCProps->pUSBRegs->ENDPTPRIME = 0x1 << (ep_num + direction*16); | |
} | |