mirror of https://github.com/ARMmbed/mbed-os.git
Add NUC472 support for USBH
parent
f5fb485dcd
commit
69f4a200c0
|
@ -72,7 +72,10 @@ enum ENDPOINT_TYPE {
|
|||
#define OR_CONTROL_CLE 0x00000010
|
||||
#define OR_CONTROL_BLE 0x00000020
|
||||
#define OR_CONTROL_HCFS 0x000000C0
|
||||
#define OR_CONTROL_HC_RSET 0x00000000
|
||||
#define OR_CONTROL_HC_RES 0x00000040
|
||||
#define OR_CONTROL_HC_OPER 0x00000080
|
||||
#define OR_CONTROL_HC_SUSP 0x000000C0
|
||||
// ----------------- HcCommandStatus Register -----------------
|
||||
#define OR_CMD_STATUS_HCR 0x00000001
|
||||
#define OR_CMD_STATUS_CLF 0x00000002
|
||||
|
|
|
@ -0,0 +1,361 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2015-2016 Nuvoton
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#if defined(TARGET_NUC472)
|
||||
|
||||
#include "mbed.h"
|
||||
#include "USBHALHost.h"
|
||||
#include "dbg.h"
|
||||
#include "pinmap.h"
|
||||
|
||||
#define HCCA_SIZE sizeof(HCCA)
|
||||
#define ED_SIZE sizeof(HCED)
|
||||
#define TD_SIZE sizeof(HCTD)
|
||||
|
||||
#define TOTAL_SIZE (HCCA_SIZE + (MAX_ENDPOINT*ED_SIZE) + (MAX_TD*TD_SIZE))
|
||||
|
||||
static volatile MBED_ALIGN(256) uint8_t usb_buf[TOTAL_SIZE]; // 256 bytes aligned!
|
||||
|
||||
USBHALHost * USBHALHost::instHost;
|
||||
|
||||
USBHALHost::USBHALHost()
|
||||
{
|
||||
instHost = this;
|
||||
memInit();
|
||||
memset((void*)usb_hcca, 0, HCCA_SIZE);
|
||||
for (int i = 0; i < MAX_ENDPOINT; i++) {
|
||||
edBufAlloc[i] = false;
|
||||
}
|
||||
for (int i = 0; i < MAX_TD; i++) {
|
||||
tdBufAlloc[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
void USBHALHost::init()
|
||||
{
|
||||
// Unlock protected registers
|
||||
SYS_UnlockReg();
|
||||
|
||||
// NOTE: Configure as OTG device first; otherwise, program will trap in wait loop CLK_STATUS_PLL2STB_Msk below.
|
||||
SYS->USBPHY = SYS_USBPHY_LDO33EN_Msk | SYS_USBPHY_USBROLE_ON_THE_GO;
|
||||
|
||||
// NOTE: Enable OTG here; otherwise, program will trap in wait loop CLK_STATUS_PLL2STB_Msk below.
|
||||
CLK_EnableModuleClock(OTG_MODULE);
|
||||
OTG->PHYCTL = (OTG->PHYCTL | OTG_PHYCTL_OTGPHYEN_Msk) & ~OTG_PHYCTL_IDDETEN_Msk;
|
||||
//OTG->CTL |= OTG_CTL_OTGEN_Msk | OTG_CTL_BUSREQ_Msk;
|
||||
|
||||
// PB.0: USB0 external VBUS regulator status
|
||||
// USB_OC
|
||||
// PB.1: USB0 external VBUS regulator enable
|
||||
// NCT3520U low active (USB_PWR_EN)
|
||||
pin_function(PB_0, SYS_GPB_MFPL_PB0MFP_USB0_OTG5V_ST);
|
||||
pin_function(PB_1, SYS_GPB_MFPL_PB1MFP_USB0_OTG5V_EN);
|
||||
|
||||
// PB.2: USB1 differential signal D-
|
||||
// PB.3: USB1 differential signal D+
|
||||
//pin_function(PB_2, SYS_GPB_MFPL_PB2MFP_USB1_D_N);
|
||||
//pin_function(PB_3, SYS_GPB_MFPL_PB3MFP_USB1_D_P);
|
||||
|
||||
// Set PB.4 output high to enable USB power
|
||||
//gpio_t gpio;
|
||||
//gpio_init_out_ex(&gpio, PB_4, 1);
|
||||
|
||||
// NOTE:
|
||||
// 1. Set USBH clock source to PLL2; otherwise, program will trap in wait loop CLK_STATUS_PLL2STB_Msk below.
|
||||
// 2. Don't set CLK_PLL2CTL_PLL2CKEN_Msk. USBH will work abnormally with it enabled.
|
||||
CLK->CLKSEL0 &= ~CLK_CLKSEL0_USBHSEL_Msk;
|
||||
// Enable PLL2, 480 MHz / 2 / (1+4) => 48 MHz output
|
||||
CLK->PLL2CTL = /*CLK_PLL2CTL_PLL2CKEN_Msk | */ (4 << CLK_PLL2CTL_PLL2DIV_Pos);
|
||||
// Wait PLL2 stable ...
|
||||
while (!(CLK->STATUS & CLK_STATUS_PLL2STB_Msk));
|
||||
|
||||
// Select USB Host clock source from PLL2, clock divied by 1
|
||||
CLK_SetModuleClock(USBH_MODULE, CLK_CLKSEL0_USBHSEL_PLL2, CLK_CLKDIV0_USB(1));
|
||||
|
||||
// Enable USB Host clock
|
||||
CLK_EnableModuleClock(USBH_MODULE);
|
||||
|
||||
// Lock protected registers
|
||||
SYS_LockReg();
|
||||
|
||||
// Overcurrent flag is high active
|
||||
USBH->HcMiscControl &= ~USBH_HcMiscControl_OCAL_Msk;
|
||||
|
||||
// Disable HC interrupts
|
||||
USBH->HcInterruptDisable = OR_INTR_ENABLE_MIE;
|
||||
|
||||
// Needed by some controllers
|
||||
USBH->HcControl = 0;
|
||||
|
||||
// Software reset
|
||||
USBH->HcCommandStatus = OR_CMD_STATUS_HCR;
|
||||
while (USBH->HcCommandStatus & OR_CMD_STATUS_HCR);
|
||||
|
||||
// Put HC in reset state
|
||||
USBH->HcControl = (USBH->HcControl & ~OR_CONTROL_HCFS) | OR_CONTROL_HC_RSET;
|
||||
// HCD must wait 10ms for HC reset complete
|
||||
wait_ms(100);
|
||||
|
||||
USBH->HcControlHeadED = 0; // Initialize Control ED list head to 0
|
||||
USBH->HcBulkHeadED = 0; // Initialize Bulk ED list head to 0
|
||||
USBH->HcHCCA = (uint32_t) usb_hcca;
|
||||
|
||||
USBH->HcFmInterval = DEFAULT_FMINTERVAL; // Frame interval = 12000 - 1
|
||||
// MPS = 10,104
|
||||
USBH->HcPeriodicStart = FI * 90 / 100; // 90% of frame interval
|
||||
USBH->HcLSThreshold = 0x628; // Low speed threshold
|
||||
|
||||
// Put HC in operational state
|
||||
USBH->HcControl = (USBH->HcControl & (~OR_CONTROL_HCFS)) | OR_CONTROL_HC_OPER;
|
||||
|
||||
// FIXME: Ports are power switched. All ports are powered at the same time. Doesn't match BSP sample.
|
||||
USBH->HcRhDescriptorA = USBH->HcRhDescriptorA & ~USBH_HcRhDescriptorA_NPS_Msk & ~USBH_HcRhDescriptorA_PSM_Msk;
|
||||
// Issue SetGlobalPower command
|
||||
USBH->HcRhStatus = USBH_HcRhStatus_LPSC_Msk;
|
||||
// Power On To Power Good Time, in 2 ms units
|
||||
wait_ms(((USBH->HcRhDescriptorA & USBH_HcRhDescriptorA_POTPGT_Msk) >> USBH_HcRhDescriptorA_POTPGT_Pos) * 2);
|
||||
|
||||
// Clear Interrrupt Status
|
||||
USBH->HcInterruptStatus |= USBH->HcInterruptStatus;
|
||||
// Enable interrupts we care about
|
||||
USBH->HcInterruptEnable = OR_INTR_ENABLE_MIE | OR_INTR_ENABLE_WDH | OR_INTR_ENABLE_RHSC;
|
||||
|
||||
|
||||
// Unlock protected registers
|
||||
SYS_UnlockReg();
|
||||
|
||||
// NOTE: Configure as USB host after USBH init above; otherwise system will crash.
|
||||
SYS->USBPHY = (SYS->USBPHY & ~SYS_USBPHY_USBROLE_Msk) | SYS_USBPHY_USBROLE_STD_USBH;
|
||||
|
||||
// Lock protected registers
|
||||
SYS_LockReg();
|
||||
|
||||
NVIC_SetVector(USBH_IRQn, (uint32_t)(_usbisr));
|
||||
NVIC_EnableIRQ(USBH_IRQn);
|
||||
|
||||
// Check for any connected devices
|
||||
if (USBH->HcRhPortStatus[0] & OR_RH_PORT_CCS) {
|
||||
// Device connected
|
||||
wait_ms(150);
|
||||
deviceConnected(0, 1, USBH->HcRhPortStatus[0] & OR_RH_PORT_LSDA);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t USBHALHost::controlHeadED()
|
||||
{
|
||||
return USBH->HcControlHeadED;
|
||||
}
|
||||
|
||||
uint32_t USBHALHost::bulkHeadED()
|
||||
{
|
||||
return USBH->HcBulkHeadED;
|
||||
}
|
||||
|
||||
uint32_t USBHALHost::interruptHeadED()
|
||||
{
|
||||
// FIXME: Only support one INT ED?
|
||||
return usb_hcca->IntTable[0];
|
||||
}
|
||||
|
||||
void USBHALHost::updateBulkHeadED(uint32_t addr)
|
||||
{
|
||||
USBH->HcBulkHeadED = addr;
|
||||
}
|
||||
|
||||
|
||||
void USBHALHost::updateControlHeadED(uint32_t addr)
|
||||
{
|
||||
USBH->HcControlHeadED = addr;
|
||||
}
|
||||
|
||||
void USBHALHost::updateInterruptHeadED(uint32_t addr)
|
||||
{
|
||||
// FIXME: Only support one INT ED?
|
||||
usb_hcca->IntTable[0] = addr;
|
||||
}
|
||||
|
||||
|
||||
void USBHALHost::enableList(ENDPOINT_TYPE type)
|
||||
{
|
||||
switch(type) {
|
||||
case CONTROL_ENDPOINT:
|
||||
USBH->HcCommandStatus = OR_CMD_STATUS_CLF;
|
||||
USBH->HcControl |= OR_CONTROL_CLE;
|
||||
break;
|
||||
case ISOCHRONOUS_ENDPOINT:
|
||||
// FIXME
|
||||
break;
|
||||
case BULK_ENDPOINT:
|
||||
USBH->HcCommandStatus = OR_CMD_STATUS_BLF;
|
||||
USBH->HcControl |= OR_CONTROL_BLE;
|
||||
break;
|
||||
case INTERRUPT_ENDPOINT:
|
||||
USBH->HcControl |= OR_CONTROL_PLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool USBHALHost::disableList(ENDPOINT_TYPE type)
|
||||
{
|
||||
switch(type) {
|
||||
case CONTROL_ENDPOINT:
|
||||
if(USBH->HcControl & OR_CONTROL_CLE) {
|
||||
USBH->HcControl &= ~OR_CONTROL_CLE;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case ISOCHRONOUS_ENDPOINT:
|
||||
// FIXME
|
||||
return false;
|
||||
case BULK_ENDPOINT:
|
||||
if(USBH->HcControl & OR_CONTROL_BLE){
|
||||
USBH->HcControl &= ~OR_CONTROL_BLE;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case INTERRUPT_ENDPOINT:
|
||||
if(USBH->HcControl & OR_CONTROL_PLE) {
|
||||
USBH->HcControl &= ~OR_CONTROL_PLE;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void USBHALHost::memInit()
|
||||
{
|
||||
usb_hcca = (volatile HCCA *)usb_buf;
|
||||
usb_edBuf = usb_buf + HCCA_SIZE;
|
||||
usb_tdBuf = usb_buf + HCCA_SIZE + (MAX_ENDPOINT*ED_SIZE);
|
||||
}
|
||||
|
||||
volatile uint8_t * USBHALHost::getED()
|
||||
{
|
||||
for (int i = 0; i < MAX_ENDPOINT; i++) {
|
||||
if ( !edBufAlloc[i] ) {
|
||||
edBufAlloc[i] = true;
|
||||
return (volatile uint8_t *)(usb_edBuf + i*ED_SIZE);
|
||||
}
|
||||
}
|
||||
perror("Could not allocate ED\r\n");
|
||||
return NULL; //Could not alloc ED
|
||||
}
|
||||
|
||||
volatile uint8_t * USBHALHost::getTD()
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < MAX_TD; i++) {
|
||||
if ( !tdBufAlloc[i] ) {
|
||||
tdBufAlloc[i] = true;
|
||||
return (volatile uint8_t *)(usb_tdBuf + i*TD_SIZE);
|
||||
}
|
||||
}
|
||||
perror("Could not allocate TD\r\n");
|
||||
return NULL; //Could not alloc TD
|
||||
}
|
||||
|
||||
|
||||
void USBHALHost::freeED(volatile uint8_t * ed)
|
||||
{
|
||||
int i;
|
||||
i = (ed - usb_edBuf) / ED_SIZE;
|
||||
edBufAlloc[i] = false;
|
||||
}
|
||||
|
||||
void USBHALHost::freeTD(volatile uint8_t * td)
|
||||
{
|
||||
int i;
|
||||
i = (td - usb_tdBuf) / TD_SIZE;
|
||||
tdBufAlloc[i] = false;
|
||||
}
|
||||
|
||||
|
||||
void USBHALHost::resetRootHub()
|
||||
{
|
||||
// Reset port1
|
||||
USBH->HcRhPortStatus[0] = OR_RH_PORT_PRS;
|
||||
while (USBH->HcRhPortStatus[0] & OR_RH_PORT_PRS);
|
||||
USBH->HcRhPortStatus[0] = OR_RH_PORT_PRSC;
|
||||
}
|
||||
|
||||
|
||||
void USBHALHost::_usbisr(void)
|
||||
{
|
||||
if (instHost) {
|
||||
instHost->UsbIrqhandler();
|
||||
}
|
||||
}
|
||||
|
||||
void USBHALHost::UsbIrqhandler()
|
||||
{
|
||||
uint32_t ints = USBH->HcInterruptStatus;
|
||||
|
||||
// Root hub status change interrupt
|
||||
if (ints & OR_INTR_STATUS_RHSC) {
|
||||
uint32_t ints_roothub = USBH->HcRhStatus;
|
||||
uint32_t ints_port1 = USBH->HcRhPortStatus[0];
|
||||
|
||||
if (ints_port1 & OR_RH_PORT_CSC) {
|
||||
if (ints_roothub & OR_RH_STATUS_DRWE) {
|
||||
// When DRWE is on, Connect Status Change means a remote wakeup event.
|
||||
} else {
|
||||
if (ints_port1 & OR_RH_PORT_CCS) {
|
||||
// Root device connected
|
||||
|
||||
// wait 150ms to avoid bounce
|
||||
wait_ms(150);
|
||||
|
||||
//Hub 0 (root hub), Port 1 (count starts at 1), Low or High speed
|
||||
deviceConnected(0, 1, ints_port1 & OR_RH_PORT_LSDA);
|
||||
} else {
|
||||
// Root device disconnected
|
||||
|
||||
if (!(ints & OR_INTR_STATUS_WDH)) {
|
||||
usb_hcca->DoneHead = 0;
|
||||
}
|
||||
|
||||
// wait 200ms to avoid bounce
|
||||
wait_ms(200);
|
||||
|
||||
deviceDisconnected(0, 1, NULL, usb_hcca->DoneHead & 0xFFFFFFFE);
|
||||
|
||||
if (ints & OR_INTR_STATUS_WDH) {
|
||||
usb_hcca->DoneHead = 0;
|
||||
USBH->HcInterruptStatus = OR_INTR_STATUS_WDH;
|
||||
}
|
||||
}
|
||||
}
|
||||
USBH->HcRhPortStatus[0] = OR_RH_PORT_CSC;
|
||||
}
|
||||
|
||||
// Reset completed
|
||||
if (ints_port1 & OR_RH_PORT_PRSC) {
|
||||
USBH->HcRhPortStatus[0] = OR_RH_PORT_PRSC;
|
||||
}
|
||||
|
||||
USBH->HcInterruptStatus = OR_INTR_STATUS_RHSC;
|
||||
}
|
||||
|
||||
// Writeback Done Head interrupt
|
||||
if (ints & OR_INTR_STATUS_WDH) {
|
||||
transferCompleted(usb_hcca->DoneHead & 0xFFFFFFFE);
|
||||
USBH->HcInterruptStatus = OR_INTR_STATUS_WDH;
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -92,8 +92,8 @@ extern "C"
|
|||
#define CLK_CLKSEL0_PCLKSEL_HCLK (0x00UL<<CLK_CLKSEL0_PCLKSEL_Pos) /*!< Setting clock source as HCLK \hideinitializer */
|
||||
#define CLK_CLKSEL0_PCLKSEL_HCLK_DIV2 (0x01UL<<CLK_CLKSEL0_PCLKSEL_Pos) /*!< Setting clock source as HCLK/2 \hideinitializer */
|
||||
|
||||
#define CLK_CLKSEL0_USBHSEL_PLL (0x00UL<<CLK_CLKSEL0_USBH_Pos) /*!< Setting clock source as PLL \hideinitializer */
|
||||
#define CLK_CLKSEL0_USBHSEL_PLL2 (0x01UL<<CLK_CLKSEL0_USBH_Pos) /*!< Setting clock source as PLL2 \hideinitializer */
|
||||
#define CLK_CLKSEL0_USBHSEL_PLL2 (0x00UL<<CLK_CLKSEL0_USBHSEL_Pos) /*!< Setting clock source as PLL2 \hideinitializer */
|
||||
#define CLK_CLKSEL0_USBHSEL_PLL (0x01UL<<CLK_CLKSEL0_USBHSEL_Pos) /*!< Setting clock source as PLL \hideinitializer */
|
||||
|
||||
#define CLK_CLKSEL0_EMACSEL_PLL (0x01UL<<10) /*!< Setting clock source as PLL \hideinitializer */
|
||||
|
||||
|
|
Loading…
Reference in New Issue