USBHOST lib for STM

Dependents:   door-access-controller-dev

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers USBHALHost_NUC472.cpp Source File

USBHALHost_NUC472.cpp

00001 /* mbed Microcontroller Library
00002  * Copyright (c) 2015-2016 Nuvoton
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 #if defined(TARGET_NUC472)
00018 
00019 #include "mbed.h"
00020 #include "USBHALHost.h"
00021 #include "dbg.h"
00022 #include "pinmap.h"
00023 
00024 #define HCCA_SIZE   sizeof(HCCA)
00025 #define ED_SIZE     sizeof(HCED)
00026 #define TD_SIZE     sizeof(HCTD)
00027 
00028 #define TOTAL_SIZE (HCCA_SIZE + (MAX_ENDPOINT*ED_SIZE) + (MAX_TD*TD_SIZE))
00029 
00030 static volatile MBED_ALIGN(256) uint8_t usb_buf[TOTAL_SIZE];  // 256 bytes aligned!
00031 
00032 USBHALHost * USBHALHost::instHost;
00033 
00034 USBHALHost::USBHALHost()
00035 {
00036     instHost = this;
00037     memInit();
00038     memset((void*)usb_hcca, 0, HCCA_SIZE);
00039     for (int i = 0; i < MAX_ENDPOINT; i++) {
00040         edBufAlloc[i] = false;
00041     }
00042     for (int i = 0; i < MAX_TD; i++) {
00043         tdBufAlloc[i] = false;
00044     }
00045 }
00046 
00047 void USBHALHost::init()
00048 {
00049     // Unlock protected registers
00050     SYS_UnlockReg();
00051     
00052     // NOTE: Configure as OTG device first; otherwise, program will trap in wait loop CLK_STATUS_PLL2STB_Msk below.
00053     SYS->USBPHY = SYS_USBPHY_LDO33EN_Msk | SYS_USBPHY_USBROLE_ON_THE_GO;
00054     
00055     // NOTE: Enable OTG here; otherwise, program will trap in wait loop CLK_STATUS_PLL2STB_Msk below.
00056     CLK_EnableModuleClock(OTG_MODULE);
00057     OTG->PHYCTL = (OTG->PHYCTL | OTG_PHYCTL_OTGPHYEN_Msk) & ~OTG_PHYCTL_IDDETEN_Msk;
00058     //OTG->CTL |= OTG_CTL_OTGEN_Msk | OTG_CTL_BUSREQ_Msk;
00059     
00060     // PB.0: USB0 external VBUS regulator status
00061     // USB_OC
00062     // PB.1: USB0 external VBUS regulator enable
00063     // NCT3520U low active (USB_PWR_EN)
00064     pin_function(PB_0, SYS_GPB_MFPL_PB0MFP_USB0_OTG5V_ST);
00065     pin_function(PB_1, SYS_GPB_MFPL_PB1MFP_USB0_OTG5V_EN);
00066 
00067     // PB.2: USB1 differential signal D-
00068     // PB.3: USB1 differential signal D+
00069     //pin_function(PB_2, SYS_GPB_MFPL_PB2MFP_USB1_D_N);
00070     //pin_function(PB_3, SYS_GPB_MFPL_PB3MFP_USB1_D_P);
00071 
00072     // Set PB.4 output high to enable USB power
00073     //gpio_t gpio;
00074     //gpio_init_out_ex(&gpio, PB_4, 1);
00075     
00076     // NOTE:
00077     // 1. Set USBH clock source to PLL2; otherwise, program will trap in wait loop CLK_STATUS_PLL2STB_Msk below.
00078     // 2. Don't set CLK_PLL2CTL_PLL2CKEN_Msk. USBH will work abnormally with it enabled.
00079     CLK->CLKSEL0 &= ~CLK_CLKSEL0_USBHSEL_Msk;
00080     // Enable PLL2, 480 MHz / 2 / (1+4) => 48 MHz output
00081     CLK->PLL2CTL = /*CLK_PLL2CTL_PLL2CKEN_Msk | */ (4 << CLK_PLL2CTL_PLL2DIV_Pos);
00082     // Wait PLL2 stable ...
00083     while (!(CLK->STATUS & CLK_STATUS_PLL2STB_Msk));
00084     
00085     // Select USB Host clock source from PLL2, clock divied by 1
00086     CLK_SetModuleClock(USBH_MODULE, CLK_CLKSEL0_USBHSEL_PLL2, CLK_CLKDIV0_USB(1));
00087     
00088     // Enable USB Host clock
00089     CLK_EnableModuleClock(USBH_MODULE);
00090     
00091     // Lock protected registers
00092     SYS_LockReg();
00093     
00094     // Overcurrent flag is high active
00095     USBH->HcMiscControl &= ~USBH_HcMiscControl_OCAL_Msk;
00096     
00097     // Disable HC interrupts
00098     USBH->HcInterruptDisable = OR_INTR_ENABLE_MIE;
00099 
00100     // Needed by some controllers
00101     USBH->HcControl = 0;
00102 
00103     // Software reset
00104     USBH->HcCommandStatus = OR_CMD_STATUS_HCR;
00105     while (USBH->HcCommandStatus & OR_CMD_STATUS_HCR);
00106 
00107     // Put HC in reset state
00108     USBH->HcControl = (USBH->HcControl & ~OR_CONTROL_HCFS) | OR_CONTROL_HC_RSET;
00109     // HCD must wait 10ms for HC reset complete
00110     wait_ms(100);
00111     
00112     USBH->HcControlHeadED = 0;                      // Initialize Control ED list head to 0
00113     USBH->HcBulkHeadED = 0;                         // Initialize Bulk ED list head to 0
00114     USBH->HcHCCA = (uint32_t) usb_hcca;
00115 
00116     USBH->HcFmInterval = DEFAULT_FMINTERVAL;        // Frame interval = 12000 - 1
00117                                                     // MPS = 10,104
00118     USBH->HcPeriodicStart = FI * 90 / 100;          // 90% of frame interval
00119     USBH->HcLSThreshold = 0x628;                    // Low speed threshold
00120 
00121     // Put HC in operational state
00122     USBH->HcControl  = (USBH->HcControl & (~OR_CONTROL_HCFS)) | OR_CONTROL_HC_OPER;
00123 
00124     // FIXME: Ports are power switched. All ports are powered at the same time. Doesn't match BSP sample.
00125     USBH->HcRhDescriptorA = USBH->HcRhDescriptorA & ~USBH_HcRhDescriptorA_NPS_Msk & ~USBH_HcRhDescriptorA_PSM_Msk;
00126     // Issue SetGlobalPower command
00127     USBH->HcRhStatus = USBH_HcRhStatus_LPSC_Msk;
00128     // Power On To Power Good Time, in 2 ms units
00129     wait_ms(((USBH->HcRhDescriptorA & USBH_HcRhDescriptorA_POTPGT_Msk) >> USBH_HcRhDescriptorA_POTPGT_Pos) * 2);
00130     
00131     // Clear Interrrupt Status
00132     USBH->HcInterruptStatus |= USBH->HcInterruptStatus;
00133     // Enable interrupts we care about
00134     USBH->HcInterruptEnable  = OR_INTR_ENABLE_MIE | OR_INTR_ENABLE_WDH | OR_INTR_ENABLE_RHSC;
00135 
00136 
00137     // Unlock protected registers
00138     SYS_UnlockReg();
00139     
00140     // NOTE: Configure as USB host after USBH init above; otherwise system will crash.
00141     SYS->USBPHY = (SYS->USBPHY & ~SYS_USBPHY_USBROLE_Msk) | SYS_USBPHY_USBROLE_STD_USBH;
00142     
00143     // Lock protected registers
00144     SYS_LockReg();
00145     
00146     NVIC_SetVector(USBH_IRQn, (uint32_t)(_usbisr));
00147     NVIC_EnableIRQ(USBH_IRQn);
00148     
00149     // Check for any connected devices
00150     if (USBH->HcRhPortStatus[0] & OR_RH_PORT_CCS) {
00151         // Device connected
00152         wait_ms(150);
00153         deviceConnected(0, 1, USBH->HcRhPortStatus[0] & OR_RH_PORT_LSDA);
00154     }
00155 }
00156 
00157 uint32_t USBHALHost::controlHeadED()
00158 {
00159     return USBH->HcControlHeadED;
00160 }
00161 
00162 uint32_t USBHALHost::bulkHeadED()
00163 {
00164     return USBH->HcBulkHeadED;
00165 }
00166 
00167 uint32_t USBHALHost::interruptHeadED()
00168 {
00169     // FIXME: Only support one INT ED?
00170     return usb_hcca->IntTable[0];
00171 }
00172 
00173 void USBHALHost::updateBulkHeadED(uint32_t addr)
00174 {
00175     USBH->HcBulkHeadED = addr;
00176 }
00177 
00178 
00179 void USBHALHost::updateControlHeadED(uint32_t addr)
00180 {
00181     USBH->HcControlHeadED = addr;
00182 }
00183 
00184 void USBHALHost::updateInterruptHeadED(uint32_t addr)
00185 {
00186     // FIXME: Only support one INT ED?
00187     usb_hcca->IntTable[0] = addr;
00188 }
00189 
00190 
00191 void USBHALHost::enableList(ENDPOINT_TYPE type)
00192 {
00193     switch(type) {
00194         case CONTROL_ENDPOINT:
00195             USBH->HcCommandStatus = OR_CMD_STATUS_CLF;
00196             USBH->HcControl |= OR_CONTROL_CLE;
00197             break;
00198         case ISOCHRONOUS_ENDPOINT:
00199             // FIXME
00200             break;
00201         case BULK_ENDPOINT:
00202             USBH->HcCommandStatus = OR_CMD_STATUS_BLF;
00203             USBH->HcControl |= OR_CONTROL_BLE;
00204             break;
00205         case INTERRUPT_ENDPOINT:
00206             USBH->HcControl |= OR_CONTROL_PLE;
00207             break;
00208     }
00209 }
00210 
00211 
00212 bool USBHALHost::disableList(ENDPOINT_TYPE type)
00213 {
00214     switch(type) {
00215         case CONTROL_ENDPOINT:
00216             if(USBH->HcControl & OR_CONTROL_CLE) {
00217                 USBH->HcControl &= ~OR_CONTROL_CLE;
00218                 return true;
00219             }
00220             return false;
00221         case ISOCHRONOUS_ENDPOINT:
00222             // FIXME
00223             return false;
00224         case BULK_ENDPOINT:
00225             if(USBH->HcControl & OR_CONTROL_BLE){
00226                 USBH->HcControl &= ~OR_CONTROL_BLE;
00227                 return true;
00228             }
00229             return false;
00230         case INTERRUPT_ENDPOINT:
00231             if(USBH->HcControl & OR_CONTROL_PLE) {
00232                 USBH->HcControl &= ~OR_CONTROL_PLE;
00233                 return true;
00234             }
00235             return false;
00236     }
00237     return false;
00238 }
00239 
00240 
00241 void USBHALHost::memInit()
00242 {
00243     usb_hcca = (volatile HCCA *)usb_buf;
00244     usb_edBuf = usb_buf + HCCA_SIZE;
00245     usb_tdBuf = usb_buf + HCCA_SIZE + (MAX_ENDPOINT*ED_SIZE);
00246 }
00247 
00248 volatile uint8_t * USBHALHost::getED()
00249 {
00250     for (int i = 0; i < MAX_ENDPOINT; i++) {
00251         if ( !edBufAlloc[i] ) {
00252             edBufAlloc[i] = true;
00253             return (volatile uint8_t *)(usb_edBuf + i*ED_SIZE);
00254         }
00255     }
00256     perror("Could not allocate ED\r\n");
00257     return NULL; //Could not alloc ED
00258 }
00259 
00260 volatile uint8_t * USBHALHost::getTD()
00261 {
00262     int i;
00263     for (i = 0; i < MAX_TD; i++) {
00264         if ( !tdBufAlloc[i] ) {
00265             tdBufAlloc[i] = true;
00266             return (volatile uint8_t *)(usb_tdBuf + i*TD_SIZE);
00267         }
00268     }
00269     perror("Could not allocate TD\r\n");
00270     return NULL; //Could not alloc TD
00271 }
00272 
00273 
00274 void USBHALHost::freeED(volatile uint8_t * ed)
00275 {
00276     int i;
00277     i = (ed - usb_edBuf) / ED_SIZE;
00278     edBufAlloc[i] = false;
00279 }
00280 
00281 void USBHALHost::freeTD(volatile uint8_t * td)
00282 {
00283     int i;
00284     i = (td - usb_tdBuf) / TD_SIZE;
00285     tdBufAlloc[i] = false;
00286 }
00287 
00288 
00289 void USBHALHost::resetRootHub()
00290 {
00291     // Reset port1
00292     USBH->HcRhPortStatus[0] = OR_RH_PORT_PRS;
00293     while (USBH->HcRhPortStatus[0] & OR_RH_PORT_PRS);
00294     USBH->HcRhPortStatus[0] = OR_RH_PORT_PRSC;
00295 }
00296 
00297 
00298 void USBHALHost::_usbisr(void)
00299 {
00300     if (instHost) {
00301         instHost->UsbIrqhandler();
00302     }
00303 }
00304 
00305 void USBHALHost::UsbIrqhandler()
00306 {
00307     uint32_t ints = USBH->HcInterruptStatus;
00308     
00309     // Root hub status change interrupt
00310     if (ints & OR_INTR_STATUS_RHSC) {
00311         uint32_t ints_roothub = USBH->HcRhStatus;
00312         uint32_t ints_port1 = USBH->HcRhPortStatus[0];
00313         
00314         // Port1: ConnectStatusChange
00315         if (ints_port1 & OR_RH_PORT_CSC) {
00316             if (ints_roothub & OR_RH_STATUS_DRWE) {
00317                 // When DRWE is on, Connect Status Change means a remote wakeup event.
00318             } else {
00319                 if (ints_port1 & OR_RH_PORT_CCS) {
00320                     // Root device connected
00321                     
00322                     // wait 150ms to avoid bounce
00323                     wait_ms(150);
00324 
00325                     //Hub 0 (root hub), Port 1 (count starts at 1), Low or High speed
00326                     deviceConnected(0, 1, ints_port1 & OR_RH_PORT_LSDA);
00327                 } else {
00328                     // Root device disconnected
00329                     
00330                     if (!(ints & OR_INTR_STATUS_WDH)) {
00331                         usb_hcca->DoneHead = 0;
00332                     }
00333 
00334                     // wait 200ms to avoid bounce
00335                     wait_ms(200);
00336 
00337                     deviceDisconnected(0, 1, NULL, usb_hcca->DoneHead & 0xFFFFFFFE);
00338 
00339                     if (ints & OR_INTR_STATUS_WDH) {
00340                         usb_hcca->DoneHead = 0;
00341                         USBH->HcInterruptStatus = OR_INTR_STATUS_WDH;
00342                     }
00343                 }
00344             }
00345             USBH->HcRhPortStatus[0] = OR_RH_PORT_CSC;
00346         }
00347         // Port1: Reset completed
00348         if (ints_port1 & OR_RH_PORT_PRSC) {
00349             USBH->HcRhPortStatus[0] = OR_RH_PORT_PRSC;
00350         }
00351         // Port1: PortEnableStatusChange
00352         if (ints_port1 & OR_RH_PORT_PESC) {
00353             USBH->HcRhPortStatus[0] = OR_RH_PORT_PESC;
00354         }
00355         
00356         USBH->HcInterruptStatus = OR_INTR_STATUS_RHSC;
00357     }
00358 
00359     // Writeback Done Head interrupt
00360     if (ints & OR_INTR_STATUS_WDH) {
00361         transferCompleted(usb_hcca->DoneHead & 0xFFFFFFFE);
00362         USBH->HcInterruptStatus = OR_INTR_STATUS_WDH;
00363     }
00364 }
00365 #endif