forked for unsupported target
Dependents: stm32-disco-example DISCO-F469NI_BD_SD_Card_Control
Fork of USBHOST by
Diff: USBHost/USBHALHost_NUC472.cpp
- Revision:
- 1:ab240722d7ef
diff -r c7d19e2470b1 -r ab240722d7ef USBHost/USBHALHost_NUC472.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHost/USBHALHost_NUC472.cpp Wed Feb 15 10:49:44 2017 +0100 @@ -0,0 +1,365 @@ +/* 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]; + + // Port1: ConnectStatusChange + 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; + } + // Port1: Reset completed + if (ints_port1 & OR_RH_PORT_PRSC) { + USBH->HcRhPortStatus[0] = OR_RH_PORT_PRSC; + } + // Port1: PortEnableStatusChange + if (ints_port1 & OR_RH_PORT_PESC) { + USBH->HcRhPortStatus[0] = OR_RH_PORT_PESC; + } + + 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