blob: ab678ce4494ab8404156ba8e0856e5c02e246be6 [file] [log] [blame]
/******************************************************************************
*
* (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);
}