1445 lines
41 KiB
C
1445 lines
41 KiB
C
//
|
|
// dwhcidevice.c
|
|
//
|
|
// Supports:
|
|
// internal DMA only,
|
|
// no ISO transfers
|
|
// no dynamic attachments
|
|
//
|
|
// USPi - An USB driver for Raspberry Pi written in C
|
|
// Copyright (C) 2014-2017 R. Stange <rsta2@o2online.de>
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
//
|
|
#include <uspi/dwhcidevice.h>
|
|
#include <uspios.h>
|
|
#include <uspi/bcm2835.h>
|
|
#include <uspi/synchronize.h>
|
|
#include <uspi/assert.h>
|
|
|
|
#define ARM_IRQ_USB 9 // for ConnectInterrupt()
|
|
|
|
#define DEVICE_ID_USB_HCD 3 // for SetPowerStateOn()
|
|
|
|
//
|
|
// Configuration
|
|
//
|
|
#define DWC_CFG_DYNAMIC_FIFO // re-program FIFOs with these sizes:
|
|
#define DWC_CFG_HOST_RX_FIFO_SIZE 1024 // number of 32 bit words
|
|
#define DWC_CFG_HOST_NPER_TX_FIFO_SIZE 1024 // number of 32 bit words
|
|
#define DWC_CFG_HOST_PER_TX_FIFO_SIZE 1024 // number of 32 bit words
|
|
|
|
#define MSEC2HZ(msec) ((msec) * HZ / 1000)
|
|
|
|
typedef enum
|
|
{
|
|
StageStateNoSplitTransfer,
|
|
StageStateStartSplit,
|
|
StageStateCompleteSplit,
|
|
StageStatePeriodicDelay,
|
|
StageStateUnknown
|
|
}
|
|
TStageState;
|
|
|
|
typedef enum
|
|
{
|
|
StageSubStateWaitForChannelDisable,
|
|
StageSubStateWaitForTransactionComplete,
|
|
StageSubStateUnknown
|
|
}
|
|
TStageSubState;
|
|
|
|
static const char FromDWHCI[] = "dwhci";
|
|
|
|
boolean DWHCIDeviceInitCore (TDWHCIDevice *pThis);
|
|
boolean DWHCIDeviceInitHost (TDWHCIDevice *pThis);
|
|
boolean DWHCIDeviceEnableRootPort (TDWHCIDevice *pThis);
|
|
boolean DWHCIDeviceReset (TDWHCIDevice *pThis);
|
|
void DWHCIDeviceEnableGlobalInterrupts (TDWHCIDevice *pThis);
|
|
void DWHCIDeviceEnableCommonInterrupts (TDWHCIDevice *pThis);
|
|
void DWHCIDeviceEnableHostInterrupts (TDWHCIDevice *pThis);
|
|
void DWHCIDeviceEnableChannelInterrupt (TDWHCIDevice *pThis, unsigned nChannel);
|
|
void DWHCIDeviceDisableChannelInterrupt (TDWHCIDevice *pThis, unsigned nChannel);
|
|
void DWHCIDeviceFlushTxFIFO (TDWHCIDevice *pThis, unsigned nFIFO);
|
|
void DWHCIDeviceFlushRxFIFO (TDWHCIDevice *pThis);
|
|
boolean DWHCIDeviceTransferStage (TDWHCIDevice *pThis, TUSBRequest *pURB, boolean bIn, boolean bStatusStage);
|
|
void DWHCIDeviceCompletionRoutine (TUSBRequest *pURB, void *pParam, void *pContext);
|
|
boolean DWHCIDeviceTransferStageAsync (TDWHCIDevice *pThis, TUSBRequest *pURB, boolean bIn, boolean bStatusStage);
|
|
void DWHCIDeviceStartTransaction (TDWHCIDevice *pThis, TDWHCITransferStageData *pStageData);
|
|
void DWHCIDeviceStartChannel (TDWHCIDevice *pThis, TDWHCITransferStageData *pStageData);
|
|
void DWHCIDeviceChannelInterruptHandler (TDWHCIDevice *pThis, unsigned nChannel);
|
|
void DWHCIDeviceInterruptHandler (void *pParam);
|
|
void DWHCIDeviceTimerHandler (unsigned hTimer, void *pParam, void *pContext);
|
|
unsigned DWHCIDeviceAllocateChannel (TDWHCIDevice *pThis);
|
|
void DWHCIDeviceFreeChannel (TDWHCIDevice *pThis, unsigned nChannel);
|
|
boolean DWHCIDeviceWaitForBit (TDWHCIDevice *pThis, TDWHCIRegister *pRegister, u32 nMask,boolean bWaitUntilSet, unsigned nMsTimeout);
|
|
#ifndef NDEBUG
|
|
void DWHCIDeviceDumpRegister (TDWHCIDevice *pThis, const char *pName, u32 nAddress);
|
|
void DWHCIDeviceDumpStatus (TDWHCIDevice *pThis, unsigned nChannel /* = 0 */);
|
|
#endif
|
|
|
|
void DWHCIDevice (TDWHCIDevice *pThis)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
pThis->m_nChannels = 0;
|
|
pThis->m_nChannelAllocated = 0;
|
|
pThis->m_bWaiting = FALSE;
|
|
DWHCIRootPort (&pThis->m_RootPort, pThis);
|
|
|
|
for (unsigned nChannel = 0; nChannel < DWHCI_MAX_CHANNELS; nChannel++)
|
|
{
|
|
pThis->m_pStageData[nChannel] = 0;
|
|
}
|
|
}
|
|
|
|
void _DWHCIDevice (TDWHCIDevice *pThis)
|
|
{
|
|
_DWHCIRootPort (&pThis->m_RootPort);
|
|
}
|
|
|
|
boolean DWHCIDeviceInitialize (TDWHCIDevice *pThis)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
DataMemBarrier ();
|
|
|
|
TDWHCIRegister VendorId;
|
|
DWHCIRegister (&VendorId, DWHCI_CORE_VENDOR_ID);
|
|
if (DWHCIRegisterRead (&VendorId) != 0x4F54280A)
|
|
{
|
|
LogWrite (FromDWHCI, LOG_ERROR, "Unknown vendor 0x%0X", DWHCIRegisterGet (&VendorId));
|
|
_DWHCIRegister (&VendorId);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!SetPowerStateOn (DEVICE_ID_USB_HCD))
|
|
{
|
|
LogWrite (FromDWHCI, LOG_ERROR, "Cannot power on");
|
|
_DWHCIRegister (&VendorId);
|
|
return FALSE;
|
|
}
|
|
|
|
// Disable all interrupts
|
|
TDWHCIRegister AHBConfig;
|
|
DWHCIRegister (&AHBConfig, DWHCI_CORE_AHB_CFG);
|
|
DWHCIRegisterRead (&AHBConfig);
|
|
DWHCIRegisterAnd (&AHBConfig, ~DWHCI_CORE_AHB_CFG_GLOBALINT_MASK);
|
|
DWHCIRegisterWrite (&AHBConfig);
|
|
|
|
ConnectInterrupt (ARM_IRQ_USB, DWHCIDeviceInterruptHandler, pThis);
|
|
|
|
if (!DWHCIDeviceInitCore (pThis))
|
|
{
|
|
LogWrite (FromDWHCI, LOG_ERROR, "Cannot initialize core");
|
|
_DWHCIRegister (&AHBConfig);
|
|
_DWHCIRegister (&VendorId);
|
|
return FALSE;
|
|
}
|
|
|
|
DWHCIDeviceEnableGlobalInterrupts (pThis);
|
|
|
|
if (!DWHCIDeviceInitHost (pThis))
|
|
{
|
|
LogWrite (FromDWHCI, LOG_ERROR, "Cannot initialize host");
|
|
_DWHCIRegister (&AHBConfig);
|
|
_DWHCIRegister (&VendorId);
|
|
return FALSE;
|
|
}
|
|
|
|
// The following calls will fail if there is no device or no supported device connected
|
|
// to root port. This is not an error because the system may run without an USB device.
|
|
|
|
if (!DWHCIDeviceEnableRootPort (pThis))
|
|
{
|
|
LogWrite (FromDWHCI, LOG_WARNING, "No device connected to root port");
|
|
_DWHCIRegister (&AHBConfig);
|
|
_DWHCIRegister (&VendorId);
|
|
return TRUE;
|
|
}
|
|
|
|
if (!DWHCIRootPortInitialize (&pThis->m_RootPort))
|
|
{
|
|
LogWrite (FromDWHCI, LOG_WARNING, "Cannot initialize root port");
|
|
_DWHCIRegister (&AHBConfig);
|
|
_DWHCIRegister (&VendorId);
|
|
return TRUE;
|
|
}
|
|
|
|
DataMemBarrier ();
|
|
|
|
_DWHCIRegister (&AHBConfig);
|
|
_DWHCIRegister (&VendorId);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int DWHCIDeviceGetDescriptor (TDWHCIDevice *pThis, TUSBEndpoint *pEndpoint,
|
|
unsigned char ucType, unsigned char ucIndex,
|
|
void *pBuffer, unsigned nBufSize,
|
|
unsigned char ucRequestType)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
return DWHCIDeviceControlMessage (pThis, pEndpoint,
|
|
ucRequestType, GET_DESCRIPTOR,
|
|
(ucType << 8) | ucIndex, 0,
|
|
pBuffer, nBufSize);
|
|
}
|
|
|
|
boolean DWHCIDeviceSetAddress (TDWHCIDevice *pThis, TUSBEndpoint *pEndpoint, u8 ucDeviceAddress)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
if (DWHCIDeviceControlMessage (pThis, pEndpoint, REQUEST_OUT, SET_ADDRESS, ucDeviceAddress, 0, 0, 0) < 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
MsDelay (50); // see USB 2.0 spec (tDSETADDR)
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
boolean DWHCIDeviceSetConfiguration (TDWHCIDevice *pThis, TUSBEndpoint *pEndpoint, u8 ucConfigurationValue)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
if (DWHCIDeviceControlMessage (pThis, pEndpoint, REQUEST_OUT, SET_CONFIGURATION, ucConfigurationValue, 0, 0, 0) < 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
MsDelay (50);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int DWHCIDeviceControlMessage (TDWHCIDevice *pThis, TUSBEndpoint *pEndpoint,
|
|
u8 ucRequestType, u8 ucRequest, u16 usValue, u16 usIndex,
|
|
void *pData, u16 usDataSize)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
TSetupData *pSetup = (TSetupData *) malloc (sizeof (TSetupData));
|
|
assert (pSetup != 0);
|
|
|
|
pSetup->bmRequestType = ucRequestType;
|
|
pSetup->bRequest = ucRequest;
|
|
pSetup->wValue = usValue;
|
|
pSetup->wIndex = usIndex;
|
|
pSetup->wLength = usDataSize;
|
|
|
|
TUSBRequest URB;
|
|
USBRequest (&URB, pEndpoint, pData, usDataSize, pSetup);
|
|
|
|
int nResult = -1;
|
|
|
|
if (DWHCIDeviceSubmitBlockingRequest (pThis, &URB))
|
|
{
|
|
nResult = USBRequestGetResultLength (&URB);
|
|
}
|
|
|
|
free (pSetup);
|
|
|
|
_USBRequest (&URB);
|
|
|
|
return nResult;
|
|
}
|
|
|
|
int DWHCIDeviceTransfer (TDWHCIDevice *pThis, TUSBEndpoint *pEndpoint, void *pBuffer, unsigned nBufSize)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
TUSBRequest URB;
|
|
USBRequest (&URB, pEndpoint, pBuffer, nBufSize, 0);
|
|
|
|
int nResult = -1;
|
|
|
|
if (DWHCIDeviceSubmitBlockingRequest (pThis, &URB))
|
|
{
|
|
nResult = USBRequestGetResultLength (&URB);
|
|
}
|
|
|
|
_USBRequest (&URB);
|
|
|
|
return nResult;
|
|
}
|
|
|
|
boolean DWHCIDeviceSubmitBlockingRequest (TDWHCIDevice *pThis, TUSBRequest *pURB)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
DataMemBarrier ();
|
|
|
|
assert (pURB != 0);
|
|
USBRequestSetStatus (pURB, 0);
|
|
|
|
if (USBEndpointGetType (USBRequestGetEndpoint (pURB)) == EndpointTypeControl)
|
|
{
|
|
TSetupData *pSetup = USBRequestGetSetupData (pURB);
|
|
assert (pSetup != 0);
|
|
|
|
if (pSetup->bmRequestType & REQUEST_IN)
|
|
{
|
|
assert (USBRequestGetBufLen (pURB) > 0);
|
|
|
|
if ( !DWHCIDeviceTransferStage (pThis, pURB, FALSE, FALSE)
|
|
|| !DWHCIDeviceTransferStage (pThis, pURB, TRUE, FALSE)
|
|
|| !DWHCIDeviceTransferStage (pThis, pURB, FALSE, TRUE))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (USBRequestGetBufLen (pURB) == 0)
|
|
{
|
|
if ( !DWHCIDeviceTransferStage (pThis, pURB, FALSE, FALSE)
|
|
|| !DWHCIDeviceTransferStage (pThis, pURB, TRUE, TRUE))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( !DWHCIDeviceTransferStage (pThis, pURB, FALSE, FALSE)
|
|
|| !DWHCIDeviceTransferStage (pThis, pURB, FALSE, FALSE)
|
|
|| !DWHCIDeviceTransferStage (pThis, pURB, TRUE, TRUE))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
assert ( USBEndpointGetType (USBRequestGetEndpoint (pURB)) == EndpointTypeBulk
|
|
|| USBEndpointGetType (USBRequestGetEndpoint (pURB)) == EndpointTypeInterrupt);
|
|
assert (USBRequestGetBufLen (pURB) > 0);
|
|
|
|
if (!DWHCIDeviceTransferStage (pThis, pURB, USBEndpointIsDirectionIn (USBRequestGetEndpoint (pURB)), FALSE))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
DataMemBarrier ();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
boolean DWHCIDeviceSubmitAsyncRequest (TDWHCIDevice *pThis, TUSBRequest *pURB)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
DataMemBarrier ();
|
|
|
|
assert (pURB != 0);
|
|
assert ( USBEndpointGetType (USBRequestGetEndpoint (pURB)) == EndpointTypeBulk
|
|
|| USBEndpointGetType (USBRequestGetEndpoint (pURB)) == EndpointTypeInterrupt);
|
|
assert (USBRequestGetBufLen (pURB) > 0);
|
|
|
|
USBRequestSetStatus (pURB, 0);
|
|
|
|
boolean bOK = DWHCIDeviceTransferStageAsync (pThis, pURB, USBEndpointIsDirectionIn (USBRequestGetEndpoint (pURB)), FALSE);
|
|
|
|
DataMemBarrier ();
|
|
|
|
return bOK;
|
|
}
|
|
|
|
boolean DWHCIDeviceInitCore (TDWHCIDevice *pThis)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
TDWHCIRegister USBConfig;
|
|
DWHCIRegister (&USBConfig, DWHCI_CORE_USB_CFG);
|
|
DWHCIRegisterRead (&USBConfig);
|
|
DWHCIRegisterAnd (&USBConfig, ~DWHCI_CORE_USB_CFG_ULPI_EXT_VBUS_DRV);
|
|
DWHCIRegisterAnd (&USBConfig, ~DWHCI_CORE_USB_CFG_TERM_SEL_DL_PULSE);
|
|
DWHCIRegisterWrite (&USBConfig);
|
|
|
|
if (!DWHCIDeviceReset (pThis))
|
|
{
|
|
LogWrite (FromDWHCI, LOG_ERROR, "Reset failed");
|
|
return FALSE;
|
|
}
|
|
|
|
DWHCIRegisterRead (&USBConfig);
|
|
DWHCIRegisterAnd (&USBConfig, ~DWHCI_CORE_USB_CFG_ULPI_UTMI_SEL); // select UTMI+
|
|
DWHCIRegisterAnd (&USBConfig, ~DWHCI_CORE_USB_CFG_PHYIF); // UTMI width is 8
|
|
DWHCIRegisterWrite (&USBConfig);
|
|
|
|
// Internal DMA mode only
|
|
TDWHCIRegister HWConfig2;
|
|
DWHCIRegister (&HWConfig2, DWHCI_CORE_HW_CFG2);
|
|
DWHCIRegisterRead (&HWConfig2);
|
|
assert (DWHCI_CORE_HW_CFG2_ARCHITECTURE (DWHCIRegisterGet (&HWConfig2)) == 2);
|
|
|
|
DWHCIRegisterRead (&USBConfig);
|
|
if ( DWHCI_CORE_HW_CFG2_HS_PHY_TYPE (DWHCIRegisterGet (&HWConfig2)) == DWHCI_CORE_HW_CFG2_HS_PHY_TYPE_ULPI
|
|
&& DWHCI_CORE_HW_CFG2_FS_PHY_TYPE (DWHCIRegisterGet (&HWConfig2)) == DWHCI_CORE_HW_CFG2_FS_PHY_TYPE_DEDICATED)
|
|
{
|
|
DWHCIRegisterOr (&USBConfig, DWHCI_CORE_USB_CFG_ULPI_FSLS);
|
|
DWHCIRegisterOr (&USBConfig, DWHCI_CORE_USB_CFG_ULPI_CLK_SUS_M);
|
|
}
|
|
else
|
|
{
|
|
DWHCIRegisterAnd (&USBConfig, ~DWHCI_CORE_USB_CFG_ULPI_FSLS);
|
|
DWHCIRegisterAnd (&USBConfig, ~DWHCI_CORE_USB_CFG_ULPI_CLK_SUS_M);
|
|
}
|
|
DWHCIRegisterWrite (&USBConfig);
|
|
|
|
assert (pThis->m_nChannels == 0);
|
|
pThis->m_nChannels = DWHCI_CORE_HW_CFG2_NUM_HOST_CHANNELS (DWHCIRegisterGet (&HWConfig2));
|
|
assert (4 <= pThis->m_nChannels && pThis->m_nChannels <= DWHCI_MAX_CHANNELS);
|
|
|
|
TDWHCIRegister AHBConfig;
|
|
DWHCIRegister (&AHBConfig, DWHCI_CORE_AHB_CFG);
|
|
DWHCIRegisterRead (&AHBConfig);
|
|
DWHCIRegisterOr (&AHBConfig, DWHCI_CORE_AHB_CFG_DMAENABLE);
|
|
//DWHCIRegisterOr (&AHBConfig, DWHCI_CORE_AHB_CFG_AHB_SINGLE); // if DMA single mode should be used
|
|
DWHCIRegisterOr (&AHBConfig, DWHCI_CORE_AHB_CFG_WAIT_AXI_WRITES);
|
|
DWHCIRegisterAnd (&AHBConfig, ~DWHCI_CORE_AHB_CFG_MAX_AXI_BURST__MASK);
|
|
//DWHCIRegisterOr (&AHBConfig, 0 << DWHCI_CORE_AHB_CFG_MAX_AXI_BURST__SHIFT); // max. AXI burst length 4
|
|
DWHCIRegisterWrite (&AHBConfig);
|
|
|
|
// HNP and SRP are not used
|
|
DWHCIRegisterRead (&USBConfig);
|
|
DWHCIRegisterAnd (&USBConfig, ~DWHCI_CORE_USB_CFG_HNP_CAPABLE);
|
|
DWHCIRegisterAnd (&USBConfig, ~DWHCI_CORE_USB_CFG_SRP_CAPABLE);
|
|
DWHCIRegisterWrite (&USBConfig);
|
|
|
|
DWHCIDeviceEnableCommonInterrupts (pThis);
|
|
|
|
_DWHCIRegister (&AHBConfig);
|
|
_DWHCIRegister (&HWConfig2);
|
|
_DWHCIRegister (&USBConfig);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
boolean DWHCIDeviceInitHost (TDWHCIDevice *pThis)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
// Restart the PHY clock
|
|
TDWHCIRegister Power;
|
|
DWHCIRegister2 (&Power, ARM_USB_POWER, 0);
|
|
DWHCIRegisterWrite (&Power);
|
|
|
|
TDWHCIRegister HostConfig;
|
|
DWHCIRegister (&HostConfig, DWHCI_HOST_CFG);
|
|
DWHCIRegisterRead (&HostConfig);
|
|
DWHCIRegisterAnd (&HostConfig, ~DWHCI_HOST_CFG_FSLS_PCLK_SEL__MASK);
|
|
|
|
TDWHCIRegister HWConfig2;
|
|
DWHCIRegister (&HWConfig2, DWHCI_CORE_HW_CFG2);
|
|
TDWHCIRegister USBConfig;
|
|
DWHCIRegister (&USBConfig, DWHCI_CORE_USB_CFG);
|
|
if ( DWHCI_CORE_HW_CFG2_HS_PHY_TYPE (DWHCIRegisterRead (&HWConfig2)) == DWHCI_CORE_HW_CFG2_HS_PHY_TYPE_ULPI
|
|
&& DWHCI_CORE_HW_CFG2_FS_PHY_TYPE (DWHCIRegisterGet (&HWConfig2)) == DWHCI_CORE_HW_CFG2_FS_PHY_TYPE_DEDICATED
|
|
&& (DWHCIRegisterRead (&USBConfig) & DWHCI_CORE_USB_CFG_ULPI_FSLS))
|
|
{
|
|
DWHCIRegisterOr (&HostConfig, DWHCI_HOST_CFG_FSLS_PCLK_SEL_48_MHZ);
|
|
}
|
|
else
|
|
{
|
|
DWHCIRegisterOr (&HostConfig, DWHCI_HOST_CFG_FSLS_PCLK_SEL_30_60_MHZ);
|
|
}
|
|
|
|
DWHCIRegisterWrite (&HostConfig);
|
|
|
|
#ifdef DWC_CFG_DYNAMIC_FIFO
|
|
TDWHCIRegister RxFIFOSize;
|
|
DWHCIRegister2 (&RxFIFOSize, DWHCI_CORE_RX_FIFO_SIZ, DWC_CFG_HOST_RX_FIFO_SIZE);
|
|
DWHCIRegisterWrite (&RxFIFOSize);
|
|
|
|
TDWHCIRegister NonPeriodicTxFIFOSize;
|
|
DWHCIRegister2 (&NonPeriodicTxFIFOSize, DWHCI_CORE_NPER_TX_FIFO_SIZ, 0);
|
|
DWHCIRegisterOr (&NonPeriodicTxFIFOSize, DWC_CFG_HOST_RX_FIFO_SIZE);
|
|
DWHCIRegisterOr (&NonPeriodicTxFIFOSize, DWC_CFG_HOST_NPER_TX_FIFO_SIZE << 16);
|
|
DWHCIRegisterWrite (&NonPeriodicTxFIFOSize);
|
|
|
|
TDWHCIRegister HostPeriodicTxFIFOSize;
|
|
DWHCIRegister2 (&HostPeriodicTxFIFOSize, DWHCI_CORE_HOST_PER_TX_FIFO_SIZ, 0);
|
|
DWHCIRegisterOr (&HostPeriodicTxFIFOSize, DWC_CFG_HOST_RX_FIFO_SIZE + DWC_CFG_HOST_NPER_TX_FIFO_SIZE);
|
|
DWHCIRegisterOr (&HostPeriodicTxFIFOSize, DWC_CFG_HOST_PER_TX_FIFO_SIZE << 16);
|
|
DWHCIRegisterWrite (&HostPeriodicTxFIFOSize);
|
|
#endif
|
|
|
|
DWHCIDeviceFlushTxFIFO (pThis, 0x10); // Flush all TX FIFOs
|
|
DWHCIDeviceFlushRxFIFO (pThis);
|
|
|
|
TDWHCIRegister HostPort;
|
|
DWHCIRegister (&HostPort, DWHCI_HOST_PORT);
|
|
DWHCIRegisterRead (&HostPort);
|
|
DWHCIRegisterAnd (&HostPort, ~DWHCI_HOST_PORT_DEFAULT_MASK);
|
|
if (!(DWHCIRegisterGet (&HostPort) & DWHCI_HOST_PORT_POWER))
|
|
{
|
|
DWHCIRegisterOr (&HostPort, DWHCI_HOST_PORT_POWER);
|
|
DWHCIRegisterWrite (&HostPort);
|
|
}
|
|
|
|
DWHCIDeviceEnableHostInterrupts (pThis);
|
|
|
|
_DWHCIRegister (&HostPort);
|
|
#ifdef DWC_CFG_DYNAMIC_FIFO
|
|
_DWHCIRegister (&HostPeriodicTxFIFOSize);
|
|
_DWHCIRegister (&NonPeriodicTxFIFOSize);
|
|
_DWHCIRegister (&RxFIFOSize);
|
|
#endif
|
|
_DWHCIRegister (&USBConfig);
|
|
_DWHCIRegister (&HWConfig2);
|
|
_DWHCIRegister (&HostConfig);
|
|
_DWHCIRegister (&Power);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
boolean DWHCIDeviceEnableRootPort (TDWHCIDevice *pThis)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
TDWHCIRegister HostPort;
|
|
DWHCIRegister (&HostPort, DWHCI_HOST_PORT);
|
|
if (!DWHCIDeviceWaitForBit (pThis, &HostPort, DWHCI_HOST_PORT_CONNECT, TRUE, 20))
|
|
{
|
|
_DWHCIRegister (&HostPort);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
MsDelay (100); // see USB 2.0 spec
|
|
|
|
DWHCIRegisterRead (&HostPort);
|
|
DWHCIRegisterAnd (&HostPort, ~DWHCI_HOST_PORT_DEFAULT_MASK);
|
|
DWHCIRegisterOr (&HostPort, DWHCI_HOST_PORT_RESET);
|
|
DWHCIRegisterWrite (&HostPort);
|
|
|
|
MsDelay (50); // see USB 2.0 spec (tDRSTR)
|
|
|
|
DWHCIRegisterRead (&HostPort);
|
|
DWHCIRegisterAnd (&HostPort, ~DWHCI_HOST_PORT_DEFAULT_MASK);
|
|
DWHCIRegisterAnd (&HostPort, ~DWHCI_HOST_PORT_RESET);
|
|
DWHCIRegisterWrite (&HostPort);
|
|
|
|
// normally 10ms, seems to be too short for some devices
|
|
MsDelay (20); // see USB 2.0 spec (tRSTRCY)
|
|
|
|
_DWHCIRegister (&HostPort);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
boolean DWHCIDeviceReset (TDWHCIDevice *pThis)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
TDWHCIRegister Reset;
|
|
DWHCIRegister2 (&Reset, DWHCI_CORE_RESET, 0);
|
|
|
|
// wait for AHB master IDLE state
|
|
if (!DWHCIDeviceWaitForBit (pThis, &Reset, DWHCI_CORE_RESET_AHB_IDLE, TRUE, 100))
|
|
{
|
|
_DWHCIRegister (&Reset);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// core soft reset
|
|
DWHCIRegisterOr (&Reset, DWHCI_CORE_RESET_SOFT_RESET);
|
|
DWHCIRegisterWrite (&Reset);
|
|
|
|
if (!DWHCIDeviceWaitForBit (pThis, &Reset, DWHCI_CORE_RESET_SOFT_RESET, FALSE, 10))
|
|
{
|
|
_DWHCIRegister (&Reset);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
MsDelay (100);
|
|
|
|
_DWHCIRegister (&Reset);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void DWHCIDeviceEnableGlobalInterrupts (TDWHCIDevice *pThis)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
TDWHCIRegister AHBConfig;
|
|
DWHCIRegister (&AHBConfig, DWHCI_CORE_AHB_CFG);
|
|
DWHCIRegisterRead (&AHBConfig);
|
|
DWHCIRegisterOr (&AHBConfig, DWHCI_CORE_AHB_CFG_GLOBALINT_MASK);
|
|
DWHCIRegisterWrite (&AHBConfig);
|
|
|
|
_DWHCIRegister (&AHBConfig);
|
|
}
|
|
|
|
void DWHCIDeviceEnableCommonInterrupts (TDWHCIDevice *pThis)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
TDWHCIRegister IntStatus;
|
|
DWHCIRegister (&IntStatus, DWHCI_CORE_INT_STAT); // Clear any pending interrupts
|
|
DWHCIRegisterSetAll (&IntStatus);
|
|
DWHCIRegisterWrite (&IntStatus);
|
|
|
|
#if 0 // not used
|
|
TDWHCIRegister IntMask;
|
|
DWHCIRegister2 (&IntMask, DWHCI_CORE_INT_MASK, DWHCI_CORE_INT_MASK_MODE_MISMATCH
|
|
| DWHCI_CORE_INT_MASK_USB_SUSPEND
|
|
| DWHCI_CORE_INT_MASK_CON_ID_STS_CHNG
|
|
| DWHCI_CORE_INT_MASK_SESS_REQ_INTR
|
|
| DWHCI_CORE_INT_MASK_WKUP_INTR);
|
|
DWHCIRegisterWrite (&IntMask);
|
|
|
|
_DWHCIRegister (&IntMask);
|
|
#endif
|
|
|
|
_DWHCIRegister (&IntStatus);
|
|
}
|
|
|
|
void DWHCIDeviceEnableHostInterrupts (TDWHCIDevice *pThis)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
TDWHCIRegister IntMask;
|
|
DWHCIRegister2 (&IntMask, DWHCI_CORE_INT_MASK, 0);
|
|
DWHCIRegisterWrite (&IntMask); // Disable all interrupts
|
|
|
|
DWHCIDeviceEnableCommonInterrupts (pThis);
|
|
|
|
DWHCIRegisterRead (&IntMask);
|
|
DWHCIRegisterOr (&IntMask, DWHCI_CORE_INT_MASK_HC_INTR
|
|
//| DWHCI_CORE_INT_MASK_PORT_INTR
|
|
//| DWHCI_CORE_INT_MASK_DISCONNECT
|
|
);
|
|
DWHCIRegisterWrite (&IntMask);
|
|
|
|
_DWHCIRegister (&IntMask);
|
|
}
|
|
|
|
void DWHCIDeviceEnableChannelInterrupt (TDWHCIDevice *pThis, unsigned nChannel)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
TDWHCIRegister AllChanInterruptMask;
|
|
DWHCIRegister (&AllChanInterruptMask, DWHCI_HOST_ALLCHAN_INT_MASK);
|
|
|
|
uspi_EnterCritical ();
|
|
|
|
DWHCIRegisterRead (&AllChanInterruptMask);
|
|
DWHCIRegisterOr (&AllChanInterruptMask, 1 << nChannel);
|
|
DWHCIRegisterWrite (&AllChanInterruptMask);
|
|
|
|
uspi_LeaveCritical ();
|
|
|
|
_DWHCIRegister (&AllChanInterruptMask);
|
|
}
|
|
|
|
void DWHCIDeviceDisableChannelInterrupt (TDWHCIDevice *pThis, unsigned nChannel)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
TDWHCIRegister AllChanInterruptMask;
|
|
DWHCIRegister (&AllChanInterruptMask, DWHCI_HOST_ALLCHAN_INT_MASK);
|
|
|
|
uspi_EnterCritical ();
|
|
|
|
DWHCIRegisterRead (&AllChanInterruptMask);
|
|
DWHCIRegisterAnd (&AllChanInterruptMask, ~(1 << nChannel));
|
|
DWHCIRegisterWrite (&AllChanInterruptMask);
|
|
|
|
uspi_LeaveCritical ();
|
|
|
|
_DWHCIRegister (&AllChanInterruptMask);
|
|
}
|
|
|
|
void DWHCIDeviceFlushTxFIFO (TDWHCIDevice *pThis, unsigned nFIFO)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
TDWHCIRegister Reset;
|
|
DWHCIRegister2 (&Reset, DWHCI_CORE_RESET, 0);
|
|
DWHCIRegisterOr (&Reset, DWHCI_CORE_RESET_TX_FIFO_FLUSH);
|
|
DWHCIRegisterAnd (&Reset, ~DWHCI_CORE_RESET_TX_FIFO_NUM__MASK);
|
|
DWHCIRegisterOr (&Reset, nFIFO << DWHCI_CORE_RESET_TX_FIFO_NUM__SHIFT);
|
|
DWHCIRegisterWrite (&Reset);
|
|
|
|
if (DWHCIDeviceWaitForBit (pThis, &Reset, DWHCI_CORE_RESET_TX_FIFO_FLUSH, FALSE, 10))
|
|
{
|
|
usDelay (1); // Wait for 3 PHY clocks
|
|
}
|
|
|
|
_DWHCIRegister (&Reset);
|
|
}
|
|
|
|
void DWHCIDeviceFlushRxFIFO (TDWHCIDevice *pThis)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
TDWHCIRegister Reset;
|
|
DWHCIRegister2 (&Reset, DWHCI_CORE_RESET, 0);
|
|
DWHCIRegisterOr (&Reset, DWHCI_CORE_RESET_RX_FIFO_FLUSH);
|
|
DWHCIRegisterWrite (&Reset);
|
|
|
|
if (DWHCIDeviceWaitForBit (pThis, &Reset, DWHCI_CORE_RESET_RX_FIFO_FLUSH, FALSE, 10))
|
|
{
|
|
usDelay (1); // Wait for 3 PHY clocks
|
|
}
|
|
|
|
_DWHCIRegister (&Reset);
|
|
}
|
|
|
|
boolean DWHCIDeviceTransferStage (TDWHCIDevice *pThis, TUSBRequest *pURB, boolean bIn, boolean bStatusStage)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
assert (pURB != 0);
|
|
USBRequestSetCompletionRoutine (pURB, DWHCIDeviceCompletionRoutine, 0, pThis);
|
|
|
|
assert (!pThis->m_bWaiting);
|
|
pThis->m_bWaiting = TRUE;
|
|
|
|
if (!DWHCIDeviceTransferStageAsync (pThis, pURB, bIn, bStatusStage))
|
|
{
|
|
pThis->m_bWaiting = FALSE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
while (pThis->m_bWaiting)
|
|
{
|
|
// do nothing
|
|
}
|
|
|
|
return USBRequestGetStatus (pURB);
|
|
}
|
|
|
|
void DWHCIDeviceCompletionRoutine (TUSBRequest *pURB, void *pParam, void *pContext)
|
|
{
|
|
TDWHCIDevice *pThis = (TDWHCIDevice *) pContext;
|
|
assert (pThis != 0);
|
|
|
|
pThis->m_bWaiting = FALSE;
|
|
}
|
|
|
|
boolean DWHCIDeviceTransferStageAsync (TDWHCIDevice *pThis, TUSBRequest *pURB, boolean bIn, boolean bStatusStage)
|
|
{
|
|
assert (pThis != 0);
|
|
assert (pURB != 0);
|
|
|
|
unsigned nChannel = DWHCIDeviceAllocateChannel (pThis);
|
|
if (nChannel >= pThis->m_nChannels)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
TDWHCITransferStageData *pStageData =
|
|
(TDWHCITransferStageData *) malloc (sizeof (TDWHCITransferStageData));
|
|
assert (pStageData != 0);
|
|
DWHCITransferStageData (pStageData, nChannel, pURB, bIn, bStatusStage);
|
|
|
|
assert (pThis->m_pStageData[nChannel] == 0);
|
|
pThis->m_pStageData[nChannel] = pStageData;
|
|
|
|
DWHCIDeviceEnableChannelInterrupt (pThis, nChannel);
|
|
|
|
if (!DWHCITransferStageDataIsSplit (pStageData))
|
|
{
|
|
DWHCITransferStageDataSetState (pStageData, StageStateNoSplitTransfer);
|
|
}
|
|
else
|
|
{
|
|
if (!DWHCITransferStageDataBeginSplitCycle (pStageData))
|
|
{
|
|
DWHCIDeviceDisableChannelInterrupt (pThis, nChannel);
|
|
|
|
_DWHCITransferStageData (pStageData);
|
|
free (pStageData);
|
|
|
|
pThis->m_pStageData[nChannel] = 0;
|
|
|
|
DWHCIDeviceFreeChannel (pThis, nChannel);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
DWHCITransferStageDataSetState (pStageData, StageStateStartSplit);
|
|
DWHCITransferStageDataSetSplitComplete (pStageData, FALSE);
|
|
|
|
TDWHCIFrameScheduler *pFrameScheduler =
|
|
DWHCITransferStageDataGetFrameScheduler (pStageData);
|
|
assert (pFrameScheduler != 0);
|
|
pFrameScheduler->StartSplit (pFrameScheduler);
|
|
}
|
|
|
|
DWHCIDeviceStartTransaction (pThis, pStageData);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void DWHCIDeviceStartTransaction (TDWHCIDevice *pThis, TDWHCITransferStageData *pStageData)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
assert (pStageData != 0);
|
|
unsigned nChannel = DWHCITransferStageDataGetChannelNumber (pStageData);
|
|
assert (nChannel < pThis->m_nChannels);
|
|
|
|
// channel must be disabled, if not already done but controller
|
|
TDWHCIRegister Character;
|
|
DWHCIRegister (&Character, DWHCI_HOST_CHAN_CHARACTER (nChannel));
|
|
DWHCIRegisterRead (&Character);
|
|
if (DWHCIRegisterIsSet (&Character, DWHCI_HOST_CHAN_CHARACTER_ENABLE))
|
|
{
|
|
DWHCITransferStageDataSetSubState (pStageData, StageSubStateWaitForChannelDisable);
|
|
|
|
DWHCIRegisterAnd (&Character, ~DWHCI_HOST_CHAN_CHARACTER_ENABLE);
|
|
DWHCIRegisterOr (&Character, DWHCI_HOST_CHAN_CHARACTER_DISABLE);
|
|
DWHCIRegisterWrite (&Character);
|
|
|
|
TDWHCIRegister ChanInterruptMask;
|
|
DWHCIRegister (&ChanInterruptMask, DWHCI_HOST_CHAN_INT_MASK (nChannel));
|
|
DWHCIRegisterSet (&ChanInterruptMask, DWHCI_HOST_CHAN_INT_HALTED);
|
|
DWHCIRegisterWrite (&ChanInterruptMask);
|
|
|
|
_DWHCIRegister (&ChanInterruptMask);
|
|
}
|
|
else
|
|
{
|
|
DWHCIDeviceStartChannel (pThis, pStageData);
|
|
}
|
|
|
|
_DWHCIRegister (&Character);
|
|
}
|
|
|
|
void DWHCIDeviceStartChannel (TDWHCIDevice *pThis, TDWHCITransferStageData *pStageData)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
assert (pStageData != 0);
|
|
unsigned nChannel = DWHCITransferStageDataGetChannelNumber (pStageData);
|
|
assert (nChannel < pThis->m_nChannels);
|
|
|
|
DWHCITransferStageDataSetSubState (pStageData, StageSubStateWaitForTransactionComplete);
|
|
|
|
// reset all pending channel interrupts
|
|
TDWHCIRegister ChanInterrupt;
|
|
DWHCIRegister (&ChanInterrupt, DWHCI_HOST_CHAN_INT (nChannel));
|
|
DWHCIRegisterSetAll (&ChanInterrupt);
|
|
DWHCIRegisterWrite (&ChanInterrupt);
|
|
|
|
// set transfer size, packet count and pid
|
|
TDWHCIRegister TransferSize;
|
|
DWHCIRegister2 (&TransferSize, DWHCI_HOST_CHAN_XFER_SIZ (nChannel), 0);
|
|
DWHCIRegisterOr (&TransferSize, DWHCITransferStageDataGetBytesToTransfer (pStageData) & DWHCI_HOST_CHAN_XFER_SIZ_BYTES__MASK);
|
|
DWHCIRegisterOr (&TransferSize, (DWHCITransferStageDataGetPacketsToTransfer (pStageData) << DWHCI_HOST_CHAN_XFER_SIZ_PACKETS__SHIFT)
|
|
& DWHCI_HOST_CHAN_XFER_SIZ_PACKETS__MASK);
|
|
DWHCIRegisterOr (&TransferSize, DWHCITransferStageDataGetPID (pStageData) << DWHCI_HOST_CHAN_XFER_SIZ_PID__SHIFT);
|
|
DWHCIRegisterWrite (&TransferSize);
|
|
|
|
// set DMA address
|
|
TDWHCIRegister DMAAddress;
|
|
DWHCIRegister2 (&DMAAddress, DWHCI_HOST_CHAN_DMA_ADDR (nChannel),
|
|
BUS_ADDRESS (DWHCITransferStageDataGetDMAAddress (pStageData)));
|
|
DWHCIRegisterWrite (&DMAAddress);
|
|
|
|
uspi_CleanAndInvalidateDataCacheRange (DWHCITransferStageDataGetDMAAddress (pStageData),
|
|
DWHCITransferStageDataGetBytesToTransfer (pStageData));
|
|
DataMemBarrier ();
|
|
|
|
// set split control
|
|
TDWHCIRegister SplitControl;
|
|
DWHCIRegister2 (&SplitControl, DWHCI_HOST_CHAN_SPLIT_CTRL (nChannel), 0);
|
|
if (DWHCITransferStageDataIsSplit (pStageData))
|
|
{
|
|
DWHCIRegisterOr (&SplitControl, DWHCITransferStageDataGetHubPortAddress (pStageData));
|
|
DWHCIRegisterOr (&SplitControl, DWHCITransferStageDataGetHubAddress (pStageData)
|
|
<< DWHCI_HOST_CHAN_SPLIT_CTRL_HUB_ADDRESS__SHIFT);
|
|
DWHCIRegisterOr (&SplitControl, DWHCITransferStageDataGetSplitPosition (pStageData)
|
|
<< DWHCI_HOST_CHAN_SPLIT_CTRL_XACT_POS__SHIFT);
|
|
if (DWHCITransferStageDataIsSplitComplete (pStageData))
|
|
{
|
|
DWHCIRegisterOr (&SplitControl, DWHCI_HOST_CHAN_SPLIT_CTRL_COMPLETE_SPLIT);
|
|
}
|
|
DWHCIRegisterOr (&SplitControl, DWHCI_HOST_CHAN_SPLIT_CTRL_SPLIT_ENABLE);
|
|
}
|
|
DWHCIRegisterWrite (&SplitControl);
|
|
|
|
// set channel parameters
|
|
TDWHCIRegister Character;
|
|
DWHCIRegister (&Character, DWHCI_HOST_CHAN_CHARACTER (nChannel));
|
|
DWHCIRegisterRead (&Character);
|
|
DWHCIRegisterAnd (&Character, ~DWHCI_HOST_CHAN_CHARACTER_MAX_PKT_SIZ__MASK);
|
|
DWHCIRegisterOr (&Character, DWHCITransferStageDataGetMaxPacketSize (pStageData) & DWHCI_HOST_CHAN_CHARACTER_MAX_PKT_SIZ__MASK);
|
|
|
|
DWHCIRegisterAnd (&Character, ~DWHCI_HOST_CHAN_CHARACTER_MULTI_CNT__MASK);
|
|
DWHCIRegisterOr (&Character, 1 << DWHCI_HOST_CHAN_CHARACTER_MULTI_CNT__SHIFT); // TODO: optimize
|
|
|
|
if (DWHCITransferStageDataIsDirectionIn (pStageData))
|
|
{
|
|
DWHCIRegisterOr (&Character, DWHCI_HOST_CHAN_CHARACTER_EP_DIRECTION_IN);
|
|
}
|
|
else
|
|
{
|
|
DWHCIRegisterAnd (&Character, ~DWHCI_HOST_CHAN_CHARACTER_EP_DIRECTION_IN);
|
|
}
|
|
|
|
if (DWHCITransferStageDataGetSpeed (pStageData) == USBSpeedLow)
|
|
{
|
|
DWHCIRegisterOr (&Character, DWHCI_HOST_CHAN_CHARACTER_LOW_SPEED_DEVICE);
|
|
}
|
|
else
|
|
{
|
|
DWHCIRegisterAnd (&Character, ~DWHCI_HOST_CHAN_CHARACTER_LOW_SPEED_DEVICE);
|
|
}
|
|
|
|
DWHCIRegisterAnd (&Character, ~DWHCI_HOST_CHAN_CHARACTER_DEVICE_ADDRESS__MASK);
|
|
DWHCIRegisterOr (&Character, DWHCITransferStageDataGetDeviceAddress (pStageData) << DWHCI_HOST_CHAN_CHARACTER_DEVICE_ADDRESS__SHIFT);
|
|
|
|
DWHCIRegisterAnd (&Character, ~DWHCI_HOST_CHAN_CHARACTER_EP_TYPE__MASK);
|
|
DWHCIRegisterOr (&Character, DWHCITransferStageDataGetEndpointType (pStageData) << DWHCI_HOST_CHAN_CHARACTER_EP_TYPE__SHIFT);
|
|
|
|
DWHCIRegisterAnd (&Character, ~DWHCI_HOST_CHAN_CHARACTER_EP_NUMBER__MASK);
|
|
DWHCIRegisterOr (&Character, DWHCITransferStageDataGetEndpointNumber (pStageData) << DWHCI_HOST_CHAN_CHARACTER_EP_NUMBER__SHIFT);
|
|
|
|
TDWHCIFrameScheduler *pFrameScheduler = DWHCITransferStageDataGetFrameScheduler (pStageData);
|
|
if (pFrameScheduler != 0)
|
|
{
|
|
pFrameScheduler->WaitForFrame (pFrameScheduler);
|
|
|
|
if (pFrameScheduler->IsOddFrame (pFrameScheduler))
|
|
{
|
|
DWHCIRegisterOr (&Character, DWHCI_HOST_CHAN_CHARACTER_PER_ODD_FRAME);
|
|
}
|
|
else
|
|
{
|
|
DWHCIRegisterAnd (&Character, ~DWHCI_HOST_CHAN_CHARACTER_PER_ODD_FRAME);
|
|
}
|
|
}
|
|
|
|
TDWHCIRegister ChanInterruptMask;
|
|
DWHCIRegister (&ChanInterruptMask, DWHCI_HOST_CHAN_INT_MASK (nChannel));
|
|
DWHCIRegisterSet (&ChanInterruptMask, DWHCITransferStageDataGetStatusMask (pStageData));
|
|
DWHCIRegisterWrite (&ChanInterruptMask);
|
|
|
|
DWHCIRegisterOr (&Character, DWHCI_HOST_CHAN_CHARACTER_ENABLE);
|
|
DWHCIRegisterAnd (&Character, ~DWHCI_HOST_CHAN_CHARACTER_DISABLE);
|
|
DWHCIRegisterWrite (&Character);
|
|
|
|
_DWHCIRegister (&ChanInterruptMask);
|
|
_DWHCIRegister (&Character);
|
|
_DWHCIRegister (&SplitControl);
|
|
_DWHCIRegister (&DMAAddress);
|
|
_DWHCIRegister (&TransferSize);
|
|
_DWHCIRegister (&ChanInterrupt);
|
|
}
|
|
|
|
void DWHCIDeviceChannelInterruptHandler (TDWHCIDevice *pThis, unsigned nChannel)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
TDWHCITransferStageData *pStageData = pThis->m_pStageData[nChannel];
|
|
assert (pStageData != 0);
|
|
TDWHCIFrameScheduler *pFrameScheduler = DWHCITransferStageDataGetFrameScheduler (pStageData);
|
|
TUSBRequest *pURB = DWHCITransferStageDataGetURB (pStageData);
|
|
assert (pURB != 0);
|
|
|
|
switch (DWHCITransferStageDataGetSubState (pStageData))
|
|
{
|
|
case StageSubStateWaitForChannelDisable:
|
|
DWHCIDeviceStartChannel (pThis, pStageData);
|
|
return;
|
|
|
|
case StageSubStateWaitForTransactionComplete: {
|
|
uspi_CleanAndInvalidateDataCacheRange (DWHCITransferStageDataGetDMAAddress (pStageData),
|
|
DWHCITransferStageDataGetBytesToTransfer (pStageData));
|
|
DataMemBarrier ();
|
|
|
|
TDWHCIRegister TransferSize;
|
|
DWHCIRegister (&TransferSize, DWHCI_HOST_CHAN_XFER_SIZ (nChannel));
|
|
DWHCIRegisterRead (&TransferSize);
|
|
|
|
TDWHCIRegister ChanInterrupt;
|
|
DWHCIRegister (&ChanInterrupt, DWHCI_HOST_CHAN_INT (nChannel));
|
|
|
|
// restart halted transaction
|
|
if (DWHCIRegisterRead (&ChanInterrupt) == DWHCI_HOST_CHAN_INT_HALTED)
|
|
{
|
|
DWHCIDeviceStartTransaction (pThis, pStageData);
|
|
return;
|
|
}
|
|
|
|
assert ( !DWHCITransferStageDataIsPeriodic (pStageData)
|
|
|| DWHCI_HOST_CHAN_XFER_SIZ_PID (DWHCIRegisterGet (&TransferSize))
|
|
!= DWHCI_HOST_CHAN_XFER_SIZ_PID_MDATA);
|
|
|
|
DWHCITransferStageDataTransactionComplete (pStageData, DWHCIRegisterRead (&ChanInterrupt),
|
|
DWHCI_HOST_CHAN_XFER_SIZ_PACKETS (DWHCIRegisterGet (&TransferSize)),
|
|
DWHCIRegisterGet (&TransferSize) & DWHCI_HOST_CHAN_XFER_SIZ_BYTES__MASK);
|
|
|
|
_DWHCIRegister (&ChanInterrupt);
|
|
_DWHCIRegister (&TransferSize);
|
|
} break;
|
|
|
|
default:
|
|
assert (0);
|
|
break;
|
|
}
|
|
|
|
unsigned nStatus;
|
|
|
|
switch (DWHCITransferStageDataGetState (pStageData))
|
|
{
|
|
case StageStateNoSplitTransfer:
|
|
nStatus = DWHCITransferStageDataGetTransactionStatus (pStageData);
|
|
if (nStatus & DWHCI_HOST_CHAN_INT_ERROR_MASK)
|
|
{
|
|
LogWrite (FromDWHCI, LOG_ERROR, "Transaction failed (status 0x%X)", nStatus);
|
|
|
|
USBRequestSetStatus (pURB, 0);
|
|
}
|
|
else if ( (nStatus & (DWHCI_HOST_CHAN_INT_NAK | DWHCI_HOST_CHAN_INT_NYET))
|
|
&& DWHCITransferStageDataIsPeriodic (pStageData))
|
|
{
|
|
DWHCITransferStageDataSetState (pStageData, StageStatePeriodicDelay);
|
|
|
|
unsigned nInterval = USBEndpointGetInterval (USBRequestGetEndpoint (pURB));
|
|
|
|
StartKernelTimer (MSEC2HZ (nInterval), DWHCIDeviceTimerHandler, pStageData, pThis);
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (!DWHCITransferStageDataIsStatusStage (pStageData))
|
|
{
|
|
USBRequestSetResultLen (pURB, DWHCITransferStageDataGetResultLen (pStageData));
|
|
}
|
|
|
|
USBRequestSetStatus (pURB, 1);
|
|
}
|
|
|
|
DWHCIDeviceDisableChannelInterrupt (pThis, nChannel);
|
|
|
|
_DWHCITransferStageData (pStageData);
|
|
free (pStageData);
|
|
pThis->m_pStageData[nChannel] = 0;
|
|
|
|
DWHCIDeviceFreeChannel (pThis, nChannel);
|
|
|
|
USBRequestCallCompletionRoutine (pURB);
|
|
break;
|
|
|
|
case StageStateStartSplit:
|
|
nStatus = DWHCITransferStageDataGetTransactionStatus (pStageData);
|
|
if ( (nStatus & DWHCI_HOST_CHAN_INT_ERROR_MASK)
|
|
|| (nStatus & DWHCI_HOST_CHAN_INT_NAK)
|
|
|| (nStatus & DWHCI_HOST_CHAN_INT_NYET))
|
|
{
|
|
LogWrite (FromDWHCI, LOG_ERROR, "Transaction failed (status 0x%X)", nStatus);
|
|
|
|
USBRequestSetStatus (pURB, 0);
|
|
|
|
DWHCIDeviceDisableChannelInterrupt (pThis, nChannel);
|
|
|
|
_DWHCITransferStageData (pStageData);
|
|
free (pStageData);
|
|
pThis->m_pStageData[nChannel] = 0;
|
|
|
|
DWHCIDeviceFreeChannel (pThis, nChannel);
|
|
|
|
USBRequestCallCompletionRoutine (pURB);
|
|
break;
|
|
}
|
|
|
|
pFrameScheduler->TransactionComplete (pFrameScheduler, nStatus);
|
|
|
|
DWHCITransferStageDataSetState (pStageData, StageStateCompleteSplit);
|
|
DWHCITransferStageDataSetSplitComplete (pStageData, TRUE);
|
|
|
|
if (!pFrameScheduler->CompleteSplit (pFrameScheduler))
|
|
{
|
|
goto LeaveCompleteSplit;
|
|
}
|
|
|
|
DWHCIDeviceStartTransaction (pThis, pStageData);
|
|
break;
|
|
|
|
case StageStateCompleteSplit:
|
|
nStatus = DWHCITransferStageDataGetTransactionStatus (pStageData);
|
|
if (nStatus & DWHCI_HOST_CHAN_INT_ERROR_MASK)
|
|
{
|
|
LogWrite (FromDWHCI, LOG_ERROR, "Transaction failed (status 0x%X)", nStatus);
|
|
|
|
USBRequestSetStatus (pURB, 0);
|
|
|
|
DWHCIDeviceDisableChannelInterrupt (pThis, nChannel);
|
|
|
|
_DWHCITransferStageData (pStageData);
|
|
free (pStageData);
|
|
pThis->m_pStageData[nChannel] = 0;
|
|
|
|
DWHCIDeviceFreeChannel (pThis, nChannel);
|
|
|
|
USBRequestCallCompletionRoutine (pURB);
|
|
break;
|
|
}
|
|
|
|
pFrameScheduler->TransactionComplete (pFrameScheduler, nStatus);
|
|
|
|
if (pFrameScheduler->CompleteSplit (pFrameScheduler))
|
|
{
|
|
DWHCIDeviceStartTransaction (pThis, pStageData);
|
|
break;
|
|
}
|
|
|
|
LeaveCompleteSplit:
|
|
if (!DWHCITransferStageDataIsStageComplete (pStageData))
|
|
{
|
|
if (!DWHCITransferStageDataBeginSplitCycle (pStageData))
|
|
{
|
|
USBRequestSetStatus (pURB, 0);
|
|
|
|
DWHCIDeviceDisableChannelInterrupt (pThis, nChannel);
|
|
|
|
_DWHCITransferStageData (pStageData);
|
|
free (pStageData);
|
|
pThis->m_pStageData[nChannel] = 0;
|
|
|
|
DWHCIDeviceFreeChannel (pThis, nChannel);
|
|
|
|
USBRequestCallCompletionRoutine (pURB);
|
|
break;
|
|
}
|
|
|
|
if (!DWHCITransferStageDataIsPeriodic (pStageData))
|
|
{
|
|
DWHCITransferStageDataSetState (pStageData, StageStateStartSplit);
|
|
DWHCITransferStageDataSetSplitComplete (pStageData, FALSE);
|
|
|
|
pFrameScheduler->StartSplit (pFrameScheduler);
|
|
|
|
DWHCIDeviceStartTransaction (pThis, pStageData);
|
|
}
|
|
else
|
|
{
|
|
DWHCITransferStageDataSetState (pStageData, StageStatePeriodicDelay);
|
|
|
|
unsigned nInterval = USBEndpointGetInterval (USBRequestGetEndpoint (pURB));
|
|
|
|
StartKernelTimer (MSEC2HZ (nInterval), DWHCIDeviceTimerHandler, pStageData, pThis);
|
|
}
|
|
break;
|
|
}
|
|
|
|
DWHCIDeviceDisableChannelInterrupt (pThis, nChannel);
|
|
|
|
if (!DWHCITransferStageDataIsStatusStage (pStageData))
|
|
{
|
|
USBRequestSetResultLen (pURB, DWHCITransferStageDataGetResultLen (pStageData));
|
|
}
|
|
USBRequestSetStatus (pURB, 1);
|
|
|
|
_DWHCITransferStageData (pStageData);
|
|
free (pStageData);
|
|
pThis->m_pStageData[nChannel] = 0;
|
|
|
|
DWHCIDeviceFreeChannel (pThis, nChannel);
|
|
|
|
USBRequestCallCompletionRoutine (pURB);
|
|
break;
|
|
|
|
default:
|
|
assert (0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void DWHCIDeviceInterruptHandler (void *pParam)
|
|
{
|
|
TDWHCIDevice *pThis = (TDWHCIDevice *) pParam;
|
|
assert (pThis != 0);
|
|
|
|
DataMemBarrier ();
|
|
|
|
TDWHCIRegister IntStatus;
|
|
DWHCIRegister (&IntStatus, DWHCI_CORE_INT_STAT);
|
|
DWHCIRegisterRead (&IntStatus);
|
|
|
|
if (DWHCIRegisterGet (&IntStatus) & DWHCI_CORE_INT_STAT_HC_INTR)
|
|
{
|
|
TDWHCIRegister AllChanInterrupt;
|
|
DWHCIRegister (&AllChanInterrupt, DWHCI_HOST_ALLCHAN_INT);
|
|
DWHCIRegisterRead (&AllChanInterrupt);
|
|
DWHCIRegisterWrite (&AllChanInterrupt);
|
|
|
|
unsigned nChannelMask = 1;
|
|
for (unsigned nChannel = 0; nChannel < pThis->m_nChannels; nChannel++)
|
|
{
|
|
if (DWHCIRegisterGet (&AllChanInterrupt) & nChannelMask)
|
|
{
|
|
TDWHCIRegister ChanInterruptMask;
|
|
DWHCIRegister2 (&ChanInterruptMask, DWHCI_HOST_CHAN_INT_MASK(nChannel), 0);
|
|
DWHCIRegisterWrite (&ChanInterruptMask);
|
|
|
|
DWHCIDeviceChannelInterruptHandler (pThis, nChannel);
|
|
|
|
_DWHCIRegister (&ChanInterruptMask);
|
|
}
|
|
|
|
nChannelMask <<= 1;
|
|
}
|
|
|
|
_DWHCIRegister (&AllChanInterrupt);
|
|
}
|
|
#if 0
|
|
if (IntStatus.Get () & DWHCI_CORE_INT_STAT_PORT_INTR)
|
|
{
|
|
CDWHCIRegister HostPort (DWHCI_HOST_PORT);
|
|
HostPort.Read ();
|
|
|
|
CLogger::Get ()->Write (FromDWHCI, LOG_DEBUG, "Port interrupt (status 0x%08X)", HostPort.Get ());
|
|
|
|
HostPort.And (~DWHCI_HOST_PORT_ENABLE);
|
|
HostPort.Or ( DWHCI_HOST_PORT_CONNECT_CHANGED
|
|
| DWHCI_HOST_PORT_ENABLE_CHANGED
|
|
| DWHCI_HOST_PORT_OVERCURRENT_CHANGED);
|
|
HostPort.Write ();
|
|
|
|
IntStatus.Or (DWHCI_CORE_INT_STAT_PORT_INTR);
|
|
}
|
|
#endif
|
|
DWHCIRegisterWrite (&IntStatus);
|
|
|
|
DataMemBarrier ();
|
|
|
|
_DWHCIRegister (&IntStatus);
|
|
}
|
|
|
|
void DWHCIDeviceTimerHandler (unsigned hTimer, void *pParam, void *pContext)
|
|
{
|
|
TDWHCIDevice *pThis = (TDWHCIDevice *) pContext;
|
|
assert (pThis != 0);
|
|
|
|
TDWHCITransferStageData *pStageData = (TDWHCITransferStageData *) pParam;
|
|
assert (pStageData != 0);
|
|
|
|
DataMemBarrier ();
|
|
|
|
assert (pStageData != 0);
|
|
assert (DWHCITransferStageDataGetState (pStageData) == StageStatePeriodicDelay);
|
|
|
|
if (DWHCITransferStageDataIsSplit (pStageData))
|
|
{
|
|
DWHCITransferStageDataSetState (pStageData, StageStateStartSplit);
|
|
|
|
DWHCITransferStageDataSetSplitComplete (pStageData, FALSE);
|
|
TDWHCIFrameScheduler *pFrameScheduler =
|
|
DWHCITransferStageDataGetFrameScheduler (pStageData);
|
|
assert (pFrameScheduler != 0);
|
|
pFrameScheduler->StartSplit (pFrameScheduler);
|
|
}
|
|
else
|
|
{
|
|
DWHCITransferStageDataSetState (pStageData, StageStateNoSplitTransfer);
|
|
}
|
|
|
|
DWHCIDeviceStartTransaction (pThis, pStageData);
|
|
|
|
DataMemBarrier ();
|
|
}
|
|
|
|
unsigned DWHCIDeviceAllocateChannel (TDWHCIDevice *pThis)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
uspi_EnterCritical ();
|
|
|
|
unsigned nChannelMask = 1;
|
|
for (unsigned nChannel = 0; nChannel < pThis->m_nChannels; nChannel++)
|
|
{
|
|
if (!(pThis->m_nChannelAllocated & nChannelMask))
|
|
{
|
|
pThis->m_nChannelAllocated |= nChannelMask;
|
|
|
|
uspi_LeaveCritical ();
|
|
|
|
return nChannel;
|
|
}
|
|
|
|
nChannelMask <<= 1;
|
|
}
|
|
|
|
uspi_LeaveCritical ();
|
|
|
|
return DWHCI_MAX_CHANNELS;
|
|
}
|
|
|
|
void DWHCIDeviceFreeChannel (TDWHCIDevice *pThis, unsigned nChannel)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
assert (nChannel < pThis->m_nChannels);
|
|
unsigned nChannelMask = 1 << nChannel;
|
|
|
|
uspi_EnterCritical ();
|
|
|
|
assert (pThis->m_nChannelAllocated & nChannelMask);
|
|
pThis->m_nChannelAllocated &= ~nChannelMask;
|
|
|
|
uspi_LeaveCritical ();
|
|
}
|
|
|
|
boolean DWHCIDeviceWaitForBit (TDWHCIDevice *pThis, TDWHCIRegister *pRegister, u32 nMask, boolean bWaitUntilSet, unsigned nMsTimeout)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
assert (pRegister != 0);
|
|
assert (nMask != 0);
|
|
assert (nMsTimeout > 0);
|
|
|
|
while ((DWHCIRegisterRead (pRegister) & nMask) ? !bWaitUntilSet : bWaitUntilSet)
|
|
{
|
|
MsDelay (1);
|
|
|
|
if (--nMsTimeout == 0)
|
|
{
|
|
//LogWrite (FromDWHCI, LOG_WARNING, "Timeout");
|
|
#ifndef NDEBUG
|
|
//DWHCIRegisterDump (pRegister);
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
TUSBSpeed DWHCIDeviceGetPortSpeed (TDWHCIDevice *pThis)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
TUSBSpeed Result = USBSpeedUnknown;
|
|
|
|
TDWHCIRegister HostPort;
|
|
DWHCIRegister (&HostPort, DWHCI_HOST_PORT);
|
|
|
|
switch (DWHCI_HOST_PORT_SPEED (DWHCIRegisterRead (&HostPort)))
|
|
{
|
|
case DWHCI_HOST_PORT_SPEED_HIGH:
|
|
Result = USBSpeedHigh;
|
|
break;
|
|
|
|
case DWHCI_HOST_PORT_SPEED_FULL:
|
|
Result = USBSpeedFull;
|
|
break;
|
|
|
|
case DWHCI_HOST_PORT_SPEED_LOW:
|
|
Result = USBSpeedLow;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
_DWHCIRegister (&HostPort);
|
|
|
|
return Result;
|
|
}
|
|
|
|
boolean DWHCIDeviceOvercurrentDetected (TDWHCIDevice *pThis)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
TDWHCIRegister HostPort;
|
|
DWHCIRegister (&HostPort, DWHCI_HOST_PORT);
|
|
|
|
if (DWHCIRegisterRead (&HostPort) & DWHCI_HOST_PORT_OVERCURRENT)
|
|
{
|
|
_DWHCIRegister (&HostPort);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
_DWHCIRegister (&HostPort);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void DWHCIDeviceDisableRootPort (TDWHCIDevice *pThis)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
TDWHCIRegister HostPort;
|
|
DWHCIRegister (&HostPort, DWHCI_HOST_PORT);
|
|
|
|
DWHCIRegisterRead (&HostPort);
|
|
DWHCIRegisterAnd (&HostPort, ~DWHCI_HOST_PORT_POWER);
|
|
DWHCIRegisterWrite (&HostPort);
|
|
|
|
_DWHCIRegister (&HostPort);
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
|
|
void DWHCIDeviceDumpRegister (TDWHCIDevice *pThis, const char *pName, u32 nAddress)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
TDWHCIRegister Register;
|
|
DWHCIRegister (&Register, nAddress);
|
|
|
|
DataMemBarrier ();
|
|
|
|
LogWrite (FromDWHCI, LOG_DEBUG, "0x%08X %s", DWHCIRegisterRead (&Register), pName);
|
|
|
|
_DWHCIRegister (&Register);
|
|
}
|
|
|
|
void DWHCIDeviceDumpStatus (TDWHCIDevice *pThis, unsigned nChannel)
|
|
{
|
|
assert (pThis != 0);
|
|
|
|
DWHCIDeviceDumpRegister (pThis, "OTG_CTRL", DWHCI_CORE_OTG_CTRL);
|
|
DWHCIDeviceDumpRegister (pThis, "AHB_CFG", DWHCI_CORE_AHB_CFG);
|
|
DWHCIDeviceDumpRegister (pThis, "USB_CFG", DWHCI_CORE_USB_CFG);
|
|
DWHCIDeviceDumpRegister (pThis, "RESET", DWHCI_CORE_RESET);
|
|
DWHCIDeviceDumpRegister (pThis, "INT_STAT", DWHCI_CORE_INT_STAT);
|
|
DWHCIDeviceDumpRegister (pThis, "INT_MASK", DWHCI_CORE_INT_MASK);
|
|
DWHCIDeviceDumpRegister (pThis, "RX_FIFO_SIZ", DWHCI_CORE_RX_FIFO_SIZ);
|
|
DWHCIDeviceDumpRegister (pThis, "NPER_TX_FIFO_SIZ", DWHCI_CORE_NPER_TX_FIFO_SIZ);
|
|
DWHCIDeviceDumpRegister (pThis, "NPER_TX_STAT", DWHCI_CORE_NPER_TX_STAT);
|
|
DWHCIDeviceDumpRegister (pThis, "HOST_PER_TX_FIFO_SIZ", DWHCI_CORE_HOST_PER_TX_FIFO_SIZ);
|
|
|
|
DWHCIDeviceDumpRegister (pThis, "HOST_CFG", DWHCI_HOST_CFG);
|
|
DWHCIDeviceDumpRegister (pThis, "HOST_PER_TX_FIFO_STAT", DWHCI_HOST_PER_TX_FIFO_STAT);
|
|
DWHCIDeviceDumpRegister (pThis, "HOST_ALLCHAN_INT", DWHCI_HOST_ALLCHAN_INT);
|
|
DWHCIDeviceDumpRegister (pThis, "HOST_ALLCHAN_INT_MASK", DWHCI_HOST_ALLCHAN_INT_MASK);
|
|
DWHCIDeviceDumpRegister (pThis, "HOST_PORT", DWHCI_HOST_PORT);
|
|
|
|
DWHCIDeviceDumpRegister (pThis, "HOST_CHAN_CHARACTER(n)", DWHCI_HOST_CHAN_CHARACTER (nChannel));
|
|
DWHCIDeviceDumpRegister (pThis, "HOST_CHAN_SPLIT_CTRL(n)", DWHCI_HOST_CHAN_SPLIT_CTRL (nChannel));
|
|
DWHCIDeviceDumpRegister (pThis, "HOST_CHAN_INT(n)", DWHCI_HOST_CHAN_INT (nChannel));
|
|
DWHCIDeviceDumpRegister (pThis, "HOST_CHAN_INT_MASK(n)", DWHCI_HOST_CHAN_INT_MASK (nChannel));
|
|
DWHCIDeviceDumpRegister (pThis, "HOST_CHAN_XFER_SIZ(n)", DWHCI_HOST_CHAN_XFER_SIZ (nChannel));
|
|
DWHCIDeviceDumpRegister (pThis, "HOST_CHAN_DMA_ADDR(n)", DWHCI_HOST_CHAN_DMA_ADDR (nChannel));
|
|
}
|
|
|
|
#endif
|