diff --git a/bsp/nuvoton/libraries/m480/rtt_port/drv_crc.c b/bsp/nuvoton/libraries/m480/rtt_port/drv_crc.c index cd16b33f70..dfea26708c 100644 --- a/bsp/nuvoton/libraries/m480/rtt_port/drv_crc.c +++ b/bsp/nuvoton/libraries/m480/rtt_port/drv_crc.c @@ -44,8 +44,10 @@ static rt_uint32_t nu_crc_run( { uint32_t u32CalChecksum = 0; uint32_t i = 0; + rt_err_t result; - rt_mutex_take(&s_CRC_mutex, RT_WAITING_FOREVER); + result = rt_mutex_take(&s_CRC_mutex, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); /* Configure CRC controller */ CRC_Open(u32OpMode, u32Attr, u32Seed, CRC_CPU_WDATA_8); @@ -85,16 +87,22 @@ static rt_uint32_t nu_crc_run( /* Get checksum value */ u32CalChecksum = CRC_GetChecksum(); - rt_mutex_release(&s_CRC_mutex); + + result = rt_mutex_release(&s_CRC_mutex); + RT_ASSERT(result == RT_EOK); return u32CalChecksum; } rt_err_t nu_crc_init(void) { + rt_err_t result; + SYS_ResetModule(CRC_RST); - rt_mutex_init(&s_CRC_mutex, NU_CRYPTO_CRC_NAME, RT_IPC_FLAG_FIFO); + result = rt_mutex_init(&s_CRC_mutex, NU_CRYPTO_CRC_NAME, RT_IPC_FLAG_FIFO); + RT_ASSERT(result == RT_EOK); + return RT_EOK; } diff --git a/bsp/nuvoton/libraries/m480/rtt_port/drv_crypto.c b/bsp/nuvoton/libraries/m480/rtt_port/drv_crypto.c index 7c84387b84..0c30ec520a 100644 --- a/bsp/nuvoton/libraries/m480/rtt_port/drv_crypto.c +++ b/bsp/nuvoton/libraries/m480/rtt_port/drv_crypto.c @@ -81,6 +81,8 @@ static volatile int s_SHA_done; static rt_err_t nu_crypto_init(void) { + rt_err_t result; + /* Enable Crypto engine interrupt */ NVIC_EnableIRQ(CRPT_IRQn); @@ -89,12 +91,19 @@ static rt_err_t nu_crypto_init(void) SHA_ENABLE_INT(CRPT); //init cipher mutex - rt_mutex_init(&s_AES_mutex, NU_HWCRYPTO_AES_NAME, RT_IPC_FLAG_FIFO); - rt_mutex_init(&s_TDES_mutex, NU_HWCRYPTO_TDES_NAME, RT_IPC_FLAG_FIFO); - rt_mutex_init(&s_SHA_mutex, NU_HWCRYPTO_SHA_NAME, RT_IPC_FLAG_FIFO); + result = rt_mutex_init(&s_AES_mutex, NU_HWCRYPTO_AES_NAME, RT_IPC_FLAG_FIFO); + RT_ASSERT(result == RT_EOK); + + result = rt_mutex_init(&s_TDES_mutex, NU_HWCRYPTO_TDES_NAME, RT_IPC_FLAG_FIFO); + RT_ASSERT(result == RT_EOK); + + result = rt_mutex_init(&s_SHA_mutex, NU_HWCRYPTO_SHA_NAME, RT_IPC_FLAG_FIFO); + RT_ASSERT(result == RT_EOK); + #if !defined(BSP_USING_TRNG) PRNG_ENABLE_INT(CRPT); - rt_mutex_init(&s_PRNG_mutex, NU_HWCRYPTO_PRNG_NAME, RT_IPC_FLAG_FIFO); + result = rt_mutex_init(&s_PRNG_mutex, NU_HWCRYPTO_PRNG_NAME, RT_IPC_FLAG_FIFO); + RT_ASSERT(result == RT_EOK); #endif return RT_EOK; @@ -146,6 +155,8 @@ static rt_err_t nu_aes_crypt_run( uint32_t u32DataLen ) { + rt_err_t result; + uint32_t au32SwapKey[8]; uint32_t au32SwapIV[4]; @@ -171,7 +182,8 @@ static rt_err_t nu_aes_crypt_run( au32SwapIV[2] = nu_get32_be(&pu8IV[8]); au32SwapIV[3] = nu_get32_be(&pu8IV[12]); - rt_mutex_take(&s_AES_mutex, RT_WAITING_FOREVER); + result = rt_mutex_take(&s_AES_mutex, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); //Using Channel 0 AES_Open(CRPT, 0, bEncrypt, u32OpMode, u32KeySize, AES_IN_OUT_SWAP); @@ -186,7 +198,8 @@ static rt_err_t nu_aes_crypt_run( AES_Start(CRPT, 0, CRYPTO_DMA_ONE_SHOT); while (!s_AES_done) {}; - rt_mutex_release(&s_AES_mutex); + result = rt_mutex_release(&s_AES_mutex); + RT_ASSERT(result == RT_EOK); return RT_EOK; } @@ -195,19 +208,26 @@ static rt_err_t nu_aes_crypt_run( //Using PRNG instead of TRNG static void nu_prng_open(uint32_t u32Seed) { - rt_mutex_take(&s_PRNG_mutex, RT_WAITING_FOREVER); + rt_err_t result; + + result = rt_mutex_take(&s_PRNG_mutex, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); //Open PRNG 64 bits. But always return 32 bits PRNG_Open(CRPT, PRNG_KEY_SIZE_64, PRNG_SEED_RELOAD, u32Seed); - rt_mutex_release(&s_PRNG_mutex); + result = rt_mutex_release(&s_PRNG_mutex); + RT_ASSERT(result == RT_EOK); + } static rt_uint32_t nu_prng_run(void) { + rt_err_t result; uint32_t au32RNGValue[2]; - rt_mutex_take(&s_PRNG_mutex, RT_WAITING_FOREVER); + result = rt_mutex_take(&s_PRNG_mutex, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); s_PRNG_done = 0; PRNG_Start(CRPT); @@ -215,7 +235,9 @@ static rt_uint32_t nu_prng_run(void) PRNG_Read(CRPT, au32RNGValue); - rt_mutex_release(&s_PRNG_mutex); + result = rt_mutex_release(&s_PRNG_mutex); + RT_ASSERT(result == RT_EOK); + return au32RNGValue[0]; } @@ -356,6 +378,8 @@ static rt_err_t nu_des_crypt_run( uint32_t u32DataLen ) { + rt_err_t result; + uint32_t au32SwapKey[3][2]; uint32_t au32SwapIV[2]; @@ -373,7 +397,8 @@ static rt_err_t nu_des_crypt_run( au32SwapIV[0] = nu_get32_be(&pu8IV[0]); au32SwapIV[1] = nu_get32_be(&pu8IV[4]); - rt_mutex_take(&s_TDES_mutex, RT_WAITING_FOREVER); + result = rt_mutex_take(&s_TDES_mutex, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); //Using Channel 0 TDES_Open(CRPT, 0, bEncrypt, (u32OpMode & CRPT_TDES_CTL_TMODE_Msk), u32KeySize, u32OpMode, TDES_IN_OUT_WHL_SWAP); @@ -388,7 +413,8 @@ static rt_err_t nu_des_crypt_run( TDES_Start(CRPT, 0, CRYPTO_DMA_ONE_SHOT); while (!s_TDES_done) {}; - rt_mutex_release(&s_TDES_mutex); + result = rt_mutex_release(&s_TDES_mutex); + RT_ASSERT(result == RT_EOK); return RT_EOK; } @@ -534,7 +560,10 @@ static rt_err_t nu_sha_hash_run( uint32_t u32DataLen ) { - rt_mutex_take(&s_SHA_mutex, RT_WAITING_FOREVER); + rt_err_t result; + + result = rt_mutex_take(&s_SHA_mutex, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); uint8_t *pu8SrcAddr = (uint8_t *)pu8InData; uint32_t u32CopyLen = 0; @@ -574,7 +603,10 @@ static rt_err_t nu_sha_hash_run( if (psSHACtx->pu8SHATempBuf == RT_NULL) { LOG_E("fun[%s] memory allocate %d bytes failed!", __FUNCTION__, psSHACtx->u32BlockSize); - rt_mutex_release(&s_SHA_mutex); + + result = rt_mutex_release(&s_SHA_mutex); + RT_ASSERT(result == RT_EOK); + return -RT_ENOMEM; } @@ -602,7 +634,10 @@ static rt_err_t nu_sha_hash_run( if (psSHACtx->pu8SHATempBuf == RT_NULL) { LOG_E("fun[%s] memory allocate %d bytes failed!", __FUNCTION__, psSHACtx->u32BlockSize); - rt_mutex_release(&s_SHA_mutex); + + result = rt_mutex_release(&s_SHA_mutex); + RT_ASSERT(result == RT_EOK); + return -RT_ENOMEM; } @@ -613,7 +648,8 @@ static rt_err_t nu_sha_hash_run( psSHACtx->u32SHATempBufLen += u32DataLen; } - rt_mutex_release(&s_SHA_mutex); + result = rt_mutex_release(&s_SHA_mutex); + RT_ASSERT(result == RT_EOK); return RT_EOK; } diff --git a/bsp/nuvoton/libraries/m480/rtt_port/drv_emac.c b/bsp/nuvoton/libraries/m480/rtt_port/drv_emac.c index b612e708d2..3729553746 100644 --- a/bsp/nuvoton/libraries/m480/rtt_port/drv_emac.c +++ b/bsp/nuvoton/libraries/m480/rtt_port/drv_emac.c @@ -242,6 +242,8 @@ static void link_monitor(void *param) static rt_err_t nu_emac_init(rt_device_t dev) { + rt_err_t result; + nu_emac_t psNuEMAC = (nu_emac_t)dev; EMAC_Close(); @@ -254,9 +256,12 @@ static rt_err_t nu_emac_init(rt_device_t dev) NVIC_SetPriority(EMAC_RX_IRQn, 1); NVIC_EnableIRQ(EMAC_RX_IRQn); - rt_sem_init(&psNuEMAC->eth_sem, "eth_sem", 0, RT_IPC_FLAG_FIFO); + result = rt_sem_init(&psNuEMAC->eth_sem, "eth_sem", 0, RT_IPC_FLAG_FIFO); + RT_ASSERT(result == RT_EOK); + + result = rt_thread_init(ð_tid, "eth", link_monitor, (void *)psNuEMAC, eth_stack, sizeof(eth_stack), RT_THREAD_PRIORITY_MAX - 2, 10); + RT_ASSERT(result == RT_EOK); - rt_thread_init(ð_tid, "eth", link_monitor, (void *)psNuEMAC, eth_stack, sizeof(eth_stack), RT_THREAD_PRIORITY_MAX - 2, 10); rt_thread_startup(ð_tid); #if defined(LWIP_IPV4) && defined(LWIP_IGMP) @@ -310,6 +315,7 @@ static rt_err_t nu_emac_control(rt_device_t dev, int cmd, void *args) static rt_err_t nu_emac_tx(rt_device_t dev, struct pbuf *p) { + rt_err_t result; nu_emac_t psNuEMAC = (nu_emac_t)dev; struct pbuf *q; rt_uint32_t offset = 0; @@ -319,14 +325,15 @@ static rt_err_t nu_emac_tx(rt_device_t dev, struct pbuf *p) /* Get free TX buffer */ if (buf == RT_NULL) { - rt_sem_control(&psNuEMAC->eth_sem, RT_IPC_CMD_RESET, 0); + result = rt_sem_control(&psNuEMAC->eth_sem, RT_IPC_CMD_RESET, 0); + RT_ASSERT(result == RT_EOK); EMAC_CLEAR_INT_FLAG(EMAC, EMAC_INTSTS_TXCPIF_Msk); EMAC_ENABLE_INT(EMAC, EMAC_INTEN_TXCPIEN_Msk); do { - rt_sem_take(&psNuEMAC->eth_sem, 1); + while (rt_sem_take(&psNuEMAC->eth_sem, 10) != RT_EOK) ; buf = (rt_uint8_t *)EMAC_ClaimFreeTXBuf(); } while (buf == RT_NULL); @@ -440,8 +447,11 @@ void EMAC_TX_IRQHandler(void) /* Wake-up suspended process to send */ if (EMAC_GET_INT_FLAG(EMAC, EMAC_INTSTS_TXCPIF_Msk)) { + rt_err_t result; + EMAC_DISABLE_INT(EMAC, EMAC_INTEN_TXCPIEN_Msk); - rt_sem_release(&nu_emac_dev.eth_sem); + result = rt_sem_release(&nu_emac_dev.eth_sem); + RT_ASSERT(result == RT_EOK); } if (EMAC_GET_INT_FLAG(EMAC, EMAC_INTSTS_TXBEIF_Msk)) diff --git a/bsp/nuvoton/libraries/m480/rtt_port/drv_epwm.c b/bsp/nuvoton/libraries/m480/rtt_port/drv_epwm.c index ea4d29339e..fed99b20e4 100644 --- a/bsp/nuvoton/libraries/m480/rtt_port/drv_epwm.c +++ b/bsp/nuvoton/libraries/m480/rtt_port/drv_epwm.c @@ -228,41 +228,4 @@ int rt_hw_epwm_init(void) INIT_DEVICE_EXPORT(rt_hw_epwm_init); -#ifdef RT_USING_FINSH -#include - -#ifdef FINSH_USING_MSH - -static int pwm_get(int argc, char **argv) -{ - int result = 0; - struct rt_device_pwm *device = RT_NULL; - struct rt_pwm_configuration configuration = {0}; - - if (argc != 3) - { - rt_kprintf("Usage: pwm_get pwm1 1\n"); - result = -RT_ERROR; - goto _exit; - } - - device = (struct rt_device_pwm *)rt_device_find(argv[1]); - if (!device) - { - result = -RT_EIO; - goto _exit; - } - - configuration.channel = atoi(argv[2]); - result = rt_device_control(&device->parent, PWM_CMD_GET, &configuration); - -_exit: - return result; -} - -MSH_CMD_EXPORT(pwm_get, pwm_get epwm1 1); - -#endif /* FINSH_USING_MSH */ -#endif /* RT_USING_FINSH */ - #endif diff --git a/bsp/nuvoton/libraries/m480/rtt_port/drv_fmc.c b/bsp/nuvoton/libraries/m480/rtt_port/drv_fmc.c index a5a97bea1b..2ec2bef071 100644 --- a/bsp/nuvoton/libraries/m480/rtt_port/drv_fmc.c +++ b/bsp/nuvoton/libraries/m480/rtt_port/drv_fmc.c @@ -54,10 +54,15 @@ const struct fal_flash_dev Onchip_ldrom_flash = { "OnChip_LDROM", FMC_LDROM_BASE int nu_fmc_read(long addr, uint8_t *buf, size_t size) { + rt_err_t result; + size_t read_size = 0; uint32_t addr_end = addr + size; uint32_t isp_rdata = 0; - rt_mutex_take(g_mutex_fmc, RT_WAITING_FOREVER); + + result = rt_mutex_take(g_mutex_fmc, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); + SYS_UnlockReg(); if (NU_GET_LSB2BIT(addr)) @@ -87,7 +92,9 @@ int nu_fmc_read(long addr, uint8_t *buf, size_t size) } SYS_LockReg(); - rt_mutex_release(g_mutex_fmc); + + result = rt_mutex_release(g_mutex_fmc); + RT_ASSERT(result == RT_EOK); return read_size; } @@ -97,8 +104,11 @@ int nu_fmc_write(long addr, const uint8_t *buf, size_t size) size_t write_size = 0; uint32_t addr_end = addr + size; uint32_t isp_rdata = 0; + rt_err_t result; + + result = rt_mutex_take(g_mutex_fmc, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); - rt_mutex_take(g_mutex_fmc, RT_WAITING_FOREVER); SYS_UnlockReg(); if (addr < FMC_APROM_END) @@ -144,9 +154,12 @@ int nu_fmc_write(long addr, const uint8_t *buf, size_t size) FMC_DISABLE_AP_UPDATE(); FMC_DISABLE_LD_UPDATE(); + Exit2: SYS_LockReg(); - rt_mutex_release(g_mutex_fmc); + + result = rt_mutex_release(g_mutex_fmc); + RT_ASSERT(result == RT_EOK); return write_size; @@ -157,12 +170,12 @@ int nu_fmc_erase(long addr, size_t size) size_t erased_size = 0; uint32_t addrptr; uint32_t addr_end = addr + size; + rt_err_t result; #if defined(NU_SUPPORT_NONALIGN) uint8_t *page_sdtemp = RT_NULL; uint8_t *page_edtemp = RT_NULL; - addrptr = addr & (FMC_FLASH_PAGE_SIZE - 1); if (addrptr) { @@ -205,7 +218,9 @@ int nu_fmc_erase(long addr, size_t size) } #endif - rt_mutex_take(g_mutex_fmc, RT_WAITING_FOREVER); + result = rt_mutex_take(g_mutex_fmc, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); + SYS_UnlockReg(); if (addr <= FMC_APROM_END) @@ -233,7 +248,9 @@ Exit1: FMC_DISABLE_LD_UPDATE(); Exit2: SYS_LockReg(); - rt_mutex_release(g_mutex_fmc); + + result = rt_mutex_release(g_mutex_fmc); + RT_ASSERT(result == RT_EOK); #if defined(NU_SUPPORT_NONALIGN) @@ -315,6 +332,7 @@ static int nu_fmc_init(void) SYS_LockReg(); g_mutex_fmc = rt_mutex_create("nu_fmc_lock", RT_IPC_FLAG_FIFO); + RT_ASSERT(g_mutex_fmc != RT_NULL); /* PKG_USING_FAL */ #if defined(PKG_USING_FAL) diff --git a/bsp/nuvoton/libraries/m480/rtt_port/drv_pdma.c b/bsp/nuvoton/libraries/m480/rtt_port/drv_pdma.c index 735c991089..dc7a67970f 100644 --- a/bsp/nuvoton/libraries/m480/rtt_port/drv_pdma.c +++ b/bsp/nuvoton/libraries/m480/rtt_port/drv_pdma.c @@ -256,11 +256,13 @@ void nu_pdma_channel_terminate(int i32ChannID) int i; uint32_t u32EnabledChans; int ch_mask = 0; + rt_err_t result; if (!(nu_pdma_chn_mask & (1 << i32ChannID))) goto exit_pdma_channel_terminate; - rt_mutex_take(g_mutex_res, RT_WAITING_FOREVER); + result = rt_mutex_take(g_mutex_res, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); // Suspend all channels. u32EnabledChans = nu_pdma_chn_mask & NU_PDMA_CH_Msk; @@ -299,7 +301,8 @@ void nu_pdma_channel_terminate(int i32ChannID) u32EnabledChans &= ~ch_mask; } - rt_mutex_release(g_mutex_res); + result = rt_mutex_release(g_mutex_res); + RT_ASSERT(result == RT_EOK); exit_pdma_channel_terminate: @@ -552,7 +555,7 @@ rt_err_t nu_pdma_desc_setup(int i32ChannID, nu_pdma_desc_t dma_desc, uint32_t u3 goto exit_nu_pdma_desc_setup; else if ((u32AddrSrc % (u32DataWidth / 8)) || (u32AddrDst % (u32DataWidth / 8))) goto exit_nu_pdma_desc_setup; - else if ( i32TransferCnt > NU_PDMA_MAX_TXCNT ) + else if (i32TransferCnt > NU_PDMA_MAX_TXCNT) goto exit_nu_pdma_desc_setup; psPeriphCtl = &nu_pdma_chn_arr[i32ChannID - NU_PDMA_CH_Pos].m_spPeripCtl; @@ -625,11 +628,13 @@ static void nu_pdma_sgtbls_token_free(nu_pdma_desc_t psSgtbls) rt_err_t nu_pdma_sgtbls_allocate(nu_pdma_desc_t *ppsSgtbls, int num) { int i, j, idx; + rt_err_t result; RT_ASSERT(ppsSgtbls != NULL); RT_ASSERT(num <= NU_PDMA_SG_TBL_MAXSIZE); - rt_mutex_take(g_mutex_sg, RT_WAITING_FOREVER); + result = rt_mutex_take(g_mutex_sg, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); for (i = 0; i < num; i++) { @@ -644,7 +649,8 @@ rt_err_t nu_pdma_sgtbls_allocate(nu_pdma_desc_t *ppsSgtbls, int num) ppsSgtbls[i] = (nu_pdma_desc_t)&nu_pdma_sgtbl_arr[idx]; } - rt_mutex_release(g_mutex_sg); + result = rt_mutex_release(g_mutex_sg); + RT_ASSERT(result == RT_EOK); return RT_EOK; @@ -660,18 +666,22 @@ fail_nu_pdma_sgtbls_allocate: ppsSgtbls[j] = NULL; } - rt_mutex_release(g_mutex_sg); + result = rt_mutex_release(g_mutex_sg); + RT_ASSERT(result == RT_EOK); + return -RT_ERROR; } void nu_pdma_sgtbls_free(nu_pdma_desc_t *ppsSgtbls, int num) { int i; + rt_err_t result; RT_ASSERT(ppsSgtbls != NULL); RT_ASSERT(num <= NU_PDMA_SG_TBL_MAXSIZE); - rt_mutex_take(g_mutex_sg, RT_WAITING_FOREVER); + result = rt_mutex_take(g_mutex_sg, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); for (i = 0; i < num; i++) { @@ -682,7 +692,8 @@ void nu_pdma_sgtbls_free(nu_pdma_desc_t *ppsSgtbls, int num) ppsSgtbls[i] = NULL; } - rt_mutex_release(g_mutex_sg); + result = rt_mutex_release(g_mutex_sg); + RT_ASSERT(result == RT_EOK); } static rt_err_t nu_pdma_sgtbls_valid(nu_pdma_desc_t head) @@ -870,6 +881,7 @@ static void nu_pdma_memfun_actor_init(void) if (-(RT_ERROR) != (nu_pdma_memfun_actor_arr[i].m_i32ChannID = nu_pdma_channel_allocate(PDMA_MEM))) { nu_pdma_memfun_actor_arr[i].m_psSemMemFun = rt_sem_create("memactor_sem", 0, RT_IPC_FLAG_FIFO); + RT_ASSERT(nu_pdma_memfun_actor_arr[i].m_psSemMemFun != RT_NULL); } else break; @@ -878,16 +890,23 @@ static void nu_pdma_memfun_actor_init(void) { nu_pdma_memfun_actor_maxnum = i; nu_pdma_memfun_actor_mask = ~(((1 << i) - 1)); + nu_pdma_memfun_actor_pool_sem = rt_sem_create("mempool_sem", nu_pdma_memfun_actor_maxnum, RT_IPC_FLAG_FIFO); + RT_ASSERT(nu_pdma_memfun_actor_pool_sem != RT_NULL); + nu_pdma_memfun_actor_pool_lock = rt_mutex_create("mempool_lock", RT_IPC_FLAG_FIFO); + RT_ASSERT(nu_pdma_memfun_actor_pool_lock != RT_NULL); } } static void nu_pdma_memfun_cb(void *pvUserData, uint32_t u32Events) { + rt_err_t result; nu_pdma_memfun_actor_t psMemFunActor = (nu_pdma_memfun_actor_t)pvUserData; + psMemFunActor->m_u32Result = u32Events; - rt_sem_release(psMemFunActor->m_psSemMemFun); + result = rt_sem_release(psMemFunActor->m_psSemMemFun); + RT_ASSERT(result == RT_EOK); } static int nu_pdma_memfun_employ(void) @@ -897,7 +916,10 @@ static int nu_pdma_memfun_employ(void) /* Headhunter */ if (nu_pdma_memfun_actor_pool_sem && (rt_sem_take(nu_pdma_memfun_actor_pool_sem, RT_WAITING_FOREVER) == RT_EOK)) { - rt_mutex_take(nu_pdma_memfun_actor_pool_lock, RT_WAITING_FOREVER); + rt_err_t result; + result = rt_mutex_take(nu_pdma_memfun_actor_pool_lock, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); + /* Find the position of first '0' in nu_pdma_memfun_actor_mask. */ idx = nu_cto(nu_pdma_memfun_actor_mask); if (idx != 32) @@ -908,7 +930,8 @@ static int nu_pdma_memfun_employ(void) { idx = -1; } - rt_mutex_release(nu_pdma_memfun_actor_pool_lock); + result = rt_mutex_release(nu_pdma_memfun_actor_pool_lock); + RT_ASSERT(result == RT_EOK); } return idx; @@ -924,6 +947,8 @@ static rt_size_t nu_pdma_memfun(void *dest, void *src, uint32_t u32DataWidth, un while (1) { + rt_err_t result; + /* Employ actor */ if ((idx = nu_pdma_memfun_employ()) < 0) continue; @@ -952,7 +977,8 @@ static rt_size_t nu_pdma_memfun(void *dest, void *src, uint32_t u32DataWidth, un 0); /* Wait it done. */ - rt_sem_take(psMemFunActor->m_psSemMemFun, RT_WAITING_FOREVER); + result = rt_sem_take(psMemFunActor->m_psSemMemFun, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); /* Give result if get NU_PDMA_EVENT_TRANSFER_DONE.*/ if (psMemFunActor->m_u32Result & NU_PDMA_EVENT_TRANSFER_DONE) @@ -976,12 +1002,16 @@ static rt_size_t nu_pdma_memfun(void *dest, void *src, uint32_t u32DataWidth, un } while (u32TransferCnt > 0); - rt_mutex_take(nu_pdma_memfun_actor_pool_lock, RT_WAITING_FOREVER); + result = rt_mutex_take(nu_pdma_memfun_actor_pool_lock, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); + nu_pdma_memfun_actor_mask &= ~(1 << idx); - rt_mutex_release(nu_pdma_memfun_actor_pool_lock); + result = rt_mutex_release(nu_pdma_memfun_actor_pool_lock); + RT_ASSERT(result == RT_EOK); /* Fire actor */ - rt_sem_release(nu_pdma_memfun_actor_pool_sem); + result = rt_sem_release(nu_pdma_memfun_actor_pool_sem); + RT_ASSERT(result == RT_EOK); break; } diff --git a/bsp/nuvoton/libraries/m480/rtt_port/drv_qspi.c b/bsp/nuvoton/libraries/m480/rtt_port/drv_qspi.c index 4d99dba711..69e384479a 100644 --- a/bsp/nuvoton/libraries/m480/rtt_port/drv_qspi.c +++ b/bsp/nuvoton/libraries/m480/rtt_port/drv_qspi.c @@ -61,7 +61,7 @@ static struct nu_spi nu_qspi_arr [] = .name = "qspi0", .spi_base = (SPI_T *)QSPI0, -#if defined(BSP_USING_QSPI_PDMA) +#if defined(BSP_USING_SPI_PDMA) #if defined(BSP_USING_QSPI0_PDMA) .pdma_perp_tx = PDMA_QSPI0_TX, .pdma_perp_rx = PDMA_QSPI0_RX, @@ -77,7 +77,7 @@ static struct nu_spi nu_qspi_arr [] = .name = "qspi1", .spi_base = (SPI_T *)QSPI1, -#if defined(BSP_USING_QSPI_PDMA) +#if defined(BSP_USING_SPI_PDMA) #if defined(BSP_USING_QSPI1_PDMA) .pdma_perp_tx = PDMA_QSPI1_TX, .pdma_perp_rx = PDMA_QSPI1_RX, diff --git a/bsp/nuvoton/libraries/m480/rtt_port/drv_sdh.c b/bsp/nuvoton/libraries/m480/rtt_port/drv_sdh.c index aa7d29b39a..d02017d0cf 100644 --- a/bsp/nuvoton/libraries/m480/rtt_port/drv_sdh.c +++ b/bsp/nuvoton/libraries/m480/rtt_port/drv_sdh.c @@ -270,11 +270,13 @@ static rt_size_t nu_sdh_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_siz { rt_uint32_t ret = 0; nu_sdh_t sdh = (nu_sdh_t)dev; + rt_err_t result; RT_ASSERT(dev != RT_NULL); RT_ASSERT(buffer != RT_NULL); - rt_sem_take(&sdh->lock, RT_WAITING_FOREVER); + result = rt_sem_take(&sdh->lock, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); /* Check alignment. */ if (((uint32_t)buffer & 0x03) != 0) @@ -315,7 +317,8 @@ exit_nu_sdh_read: sdh->pbuf = RT_NULL; } - rt_sem_release(&sdh->lock); + result = rt_sem_release(&sdh->lock); + RT_ASSERT(result == RT_EOK); if (ret == Successful) return blk_nb; @@ -329,11 +332,13 @@ static rt_size_t nu_sdh_write(rt_device_t dev, rt_off_t pos, const void *buffer, { rt_uint32_t ret = 0; nu_sdh_t sdh = (nu_sdh_t)dev; + rt_err_t result; RT_ASSERT(dev != RT_NULL); RT_ASSERT(buffer != RT_NULL); - rt_sem_take(&sdh->lock, RT_WAITING_FOREVER); + result = rt_sem_take(&sdh->lock, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); /* Check alignment. */ if (((uint32_t)buffer & 0x03) != 0) @@ -372,7 +377,8 @@ exit_nu_sdh_write: sdh->pbuf = RT_NULL; } - rt_sem_release(&sdh->lock); + result = rt_sem_release(&sdh->lock); + RT_ASSERT(result == RT_EOK); if (ret == Successful) return blk_nb; @@ -427,11 +433,13 @@ static int rt_hw_sdh_init(void) /* Private */ nu_sdh_arr[i].dev.user_data = (void *)&nu_sdh_arr[i]; - rt_sem_init(&nu_sdh_arr[i].lock, "sdhlock", 1, RT_IPC_FLAG_FIFO); + ret = rt_sem_init(&nu_sdh_arr[i].lock, "sdhlock", 1, RT_IPC_FLAG_FIFO); + RT_ASSERT(ret == RT_EOK); SDH_Open(nu_sdh_arr[i].base, CardDetect_From_GPIO); nu_sdh_arr[i].pbuf = RT_NULL; + ret = rt_device_register(&nu_sdh_arr[i].dev, nu_sdh_arr[i].name, flags); RT_ASSERT(ret == RT_EOK); } @@ -620,7 +628,11 @@ static void sdh_hotplugger(void *param) int mnt_init_sdcard_hotplug(void) { - rt_thread_init(&sdh_tid, "hotplug", sdh_hotplugger, NULL, sdh_stack, sizeof(sdh_stack), RT_THREAD_PRIORITY_MAX - 2, 10); + rt_err_t result; + + result = rt_thread_init(&sdh_tid, "hotplug", sdh_hotplugger, NULL, sdh_stack, sizeof(sdh_stack), RT_THREAD_PRIORITY_MAX - 2, 10); + RT_ASSERT(result == RT_EOK); + rt_thread_startup(&sdh_tid); return 0; diff --git a/bsp/nuvoton/libraries/m480/rtt_port/drv_spi.c b/bsp/nuvoton/libraries/m480/rtt_port/drv_spi.c index e6a12ecced..8f5f8cbe21 100644 --- a/bsp/nuvoton/libraries/m480/rtt_port/drv_spi.c +++ b/bsp/nuvoton/libraries/m480/rtt_port/drv_spi.c @@ -241,11 +241,13 @@ exit_nu_spi_bus_configure: #if defined(BSP_USING_SPI_PDMA) static void nu_pdma_spi_rx_cb(void *pvUserData, uint32_t u32EventFilter) { + rt_err_t result; struct nu_spi *spi_bus = (struct nu_spi *)pvUserData; RT_ASSERT(spi_bus != RT_NULL); - rt_sem_release(spi_bus->m_psSemBus); + result = rt_sem_release(spi_bus->m_psSemBus); + RT_ASSERT(result == RT_EOK); } static rt_err_t nu_pdma_spi_rx_config(struct nu_spi *spi_bus, uint8_t *pu8Buf, int32_t i32RcvLen, uint8_t bytes_per_word) { @@ -362,7 +364,8 @@ static rt_size_t nu_spi_pdma_transmit(struct nu_spi *spi_bus, const uint8_t *sen SPI_TRIGGER_TX_RX_PDMA(spi_base); /* Wait RX-PDMA transfer done */ - rt_sem_take(spi_bus->m_psSemBus, RT_WAITING_FOREVER); + result = rt_sem_take(spi_bus->m_psSemBus, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); /* Stop TX/RX DMA transfer. */ SPI_DISABLE_TX_RX_PDMA(spi_base); @@ -391,6 +394,7 @@ rt_err_t nu_hw_spi_pdma_allocate(struct nu_spi *spi_bus) } spi_bus->m_psSemBus = rt_sem_create("spibus_sem", 0, RT_IPC_FLAG_FIFO); + RT_ASSERT(spi_bus->m_psSemBus != RT_NULL); return RT_EOK; diff --git a/bsp/nuvoton/libraries/m480/rtt_port/drv_spii2s.c b/bsp/nuvoton/libraries/m480/rtt_port/drv_spii2s.c index bdb8e89fec..801c6262c0 100644 --- a/bsp/nuvoton/libraries/m480/rtt_port/drv_spii2s.c +++ b/bsp/nuvoton/libraries/m480/rtt_port/drv_spii2s.c @@ -243,10 +243,11 @@ exit_nu_spii2s_capacity_check: static rt_err_t nu_spii2s_dai_setup(nu_i2s_t psNuSPII2s, struct rt_audio_configure *pconfig) { rt_err_t result = RT_EOK; - nu_acodec_ops_t pNuACodecOps = RT_NULL; + nu_acodec_ops_t pNuACodecOps; + SPI_T *spii2s_base = (SPI_T *)psNuSPII2s->i2s_base; + RT_ASSERT(psNuSPII2s->AcodecOps != RT_NULL); pNuACodecOps = psNuSPII2s->AcodecOps; - SPI_T *spii2s_base = (SPI_T *)psNuSPII2s->i2s_base; /* Open SPII2S */ if (nu_spii2s_capacity_check(pconfig) == RT_TRUE) @@ -283,7 +284,7 @@ static rt_err_t nu_spii2s_dai_setup(nu_i2s_t psNuSPII2s, struct rt_audio_configu /* Set MCLK and enable MCLK */ SPII2S_EnableMCLK(spii2s_base, __HXT); - /* Set unmute */ + /* Set un-mute */ if (pNuACodecOps->nu_acodec_mixer_control) pNuACodecOps->nu_acodec_mixer_control(AUDIO_MIXER_MUTE, RT_FALSE); } @@ -298,14 +299,11 @@ exit_nu_spii2s_dai_setup: static rt_err_t nu_spii2s_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps) { rt_err_t result = RT_EOK; - nu_i2s_t psNuSPII2s; - nu_acodec_ops_t pNuACodecOps = RT_NULL; + nu_i2s_t psNuSPII2s = (nu_i2s_t)audio; + nu_acodec_ops_t pNuACodecOps; RT_ASSERT(audio != RT_NULL); RT_ASSERT(caps != RT_NULL); - - psNuSPII2s = (nu_i2s_t)audio; - RT_ASSERT(psNuSPII2s->AcodecOps != RT_NULL); pNuACodecOps = psNuSPII2s->AcodecOps; @@ -367,6 +365,10 @@ static rt_err_t nu_spii2s_getcaps(struct rt_audio_device *audio, struct rt_audio } // switch (caps->sub_type) break; + default: + result = -RT_ERROR; + break; + } // switch (caps->main_type) return result; @@ -375,16 +377,14 @@ static rt_err_t nu_spii2s_getcaps(struct rt_audio_device *audio, struct rt_audio static rt_err_t nu_spii2s_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps) { rt_err_t result = RT_EOK; - nu_i2s_t psNuSPII2s; - nu_acodec_ops_t pNuACodecOps = RT_NULL; + nu_i2s_t psNuSPII2s = (nu_i2s_t)audio; + nu_acodec_ops_t pNuACodecOps; int stream = -1; RT_ASSERT(audio != RT_NULL); RT_ASSERT(caps != RT_NULL); - - psNuSPII2s = (nu_i2s_t)audio; - RT_ASSERT(psNuSPII2s->AcodecOps != RT_NULL); + pNuACodecOps = psNuSPII2s->AcodecOps; switch (caps->main_type) @@ -394,7 +394,6 @@ static rt_err_t nu_spii2s_configure(struct rt_audio_device *audio, struct rt_aud psNuSPII2s->AcodecOps->nu_acodec_mixer_control(caps->sub_type, caps->udata.value); break; - case AUDIO_TYPE_INPUT: stream = AUDIO_STREAM_RECORD; case AUDIO_TYPE_OUTPUT: @@ -445,6 +444,7 @@ static rt_err_t nu_spii2s_configure(struct rt_audio_device *audio, struct rt_aud } } break; + default: result = -RT_ERROR; break; @@ -456,12 +456,10 @@ static rt_err_t nu_spii2s_configure(struct rt_audio_device *audio, struct rt_aud static rt_err_t nu_spii2s_init(struct rt_audio_device *audio) { rt_err_t result = RT_EOK; - nu_i2s_t psNuSPII2s; + nu_i2s_t psNuSPII2s = (nu_i2s_t)audio; RT_ASSERT(audio != RT_NULL); - psNuSPII2s = (nu_i2s_t)audio; - /* Reset this module */ SYS_ResetModule(psNuSPII2s->i2s_rst); @@ -470,13 +468,12 @@ static rt_err_t nu_spii2s_init(struct rt_audio_device *audio) static rt_err_t nu_spii2s_start(struct rt_audio_device *audio, int stream) { - nu_i2s_t psNuSPII2s; + nu_i2s_t psNuSPII2s = (nu_i2s_t)audio; + SPI_T *spii2s_base; RT_ASSERT(audio != RT_NULL); - psNuSPII2s = (nu_i2s_t)audio; - - SPI_T *spii2s_base = (SPI_T *)psNuSPII2s->i2s_base; + spii2s_base = (SPI_T *)psNuSPII2s->i2s_base; /* Restart all: SPII2S and codec. */ nu_spii2s_stop(audio, stream); @@ -512,6 +509,8 @@ static rt_err_t nu_spii2s_start(struct rt_audio_device *audio, int stream) LOG_I("Start record."); } break; + default: + return -RT_ERROR; } return RT_EOK; @@ -519,14 +518,13 @@ static rt_err_t nu_spii2s_start(struct rt_audio_device *audio, int stream) static rt_err_t nu_spii2s_stop(struct rt_audio_device *audio, int stream) { - nu_i2s_t psNuSPII2s; + nu_i2s_t psNuSPII2s = (nu_i2s_t)audio; nu_i2s_dai_t psNuSPII2sDai = RT_NULL; + SPI_T *spii2s_base; RT_ASSERT(audio != RT_NULL); - psNuSPII2s = (nu_i2s_t)audio; - - SPI_T *spii2s_base = (SPI_T *)psNuSPII2s->i2s_base; + spii2s_base = (SPI_T *)psNuSPII2s->i2s_base; switch (stream) { @@ -574,13 +572,11 @@ static rt_err_t nu_spii2s_stop(struct rt_audio_device *audio, int stream) static void nu_spii2s_buffer_info(struct rt_audio_device *audio, struct rt_audio_buf_info *info) { - nu_i2s_t psNuSPII2s; + nu_i2s_t psNuSPII2s = (nu_i2s_t)audio; RT_ASSERT(audio != RT_NULL); RT_ASSERT(info != RT_NULL); - psNuSPII2s = (nu_i2s_t)audio; - info->buffer = (rt_uint8_t *)psNuSPII2s->i2s_dais[NU_I2S_DAI_PLAYBACK].fifo ; info->total_size = NU_I2S_DMA_FIFO_SIZE; info->block_size = NU_I2S_DMA_BUF_BLOCK_SIZE; @@ -618,12 +614,12 @@ nu_hw_spii2s_pdma_allocate: int rt_hw_spii2s_init(void) { - int i = 0, j = 0; + int j = 0; nu_i2s_dai_t psNuSPII2sDai; - for (j = (SPII2S_START + 1); j < SPII2S_CNT; j++) { + int i = 0; for (i = 0; i < NU_I2S_DAI_CNT; i++) { uint8_t *pu8ptr = rt_malloc(NU_I2S_DMA_FIFO_SIZE); diff --git a/bsp/nuvoton/libraries/m480/rtt_port/drv_timer_capture.c b/bsp/nuvoton/libraries/m480/rtt_port/drv_timer_capture.c index 4630378760..4f9543664f 100644 --- a/bsp/nuvoton/libraries/m480/rtt_port/drv_timer_capture.c +++ b/bsp/nuvoton/libraries/m480/rtt_port/drv_timer_capture.c @@ -72,7 +72,7 @@ void timer_interrupt_handler(nu_capture_t *nu_timer_capture) { TIMER_ClearCaptureIntFlag(nu_timer_capture->timer); - /* Frist event is rising edge */ + /* First event is rising edge */ if (nu_timer_capture->first_edge == RT_TRUE) { nu_timer_capture->first_edge = RT_FALSE; diff --git a/bsp/nuvoton/libraries/m480/rtt_port/drv_trng.c b/bsp/nuvoton/libraries/m480/rtt_port/drv_trng.c index ec8f49d71b..8bba65221c 100644 --- a/bsp/nuvoton/libraries/m480/rtt_port/drv_trng.c +++ b/bsp/nuvoton/libraries/m480/rtt_port/drv_trng.c @@ -27,8 +27,10 @@ static int s_i32TRNGEnable = 0; static rt_uint32_t nu_trng_run(void) { uint32_t u32RNGValue; + rt_err_t result; - rt_mutex_take(&s_TRNG_mutex, RT_WAITING_FOREVER); + result = rt_mutex_take(&s_TRNG_mutex, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); TRNG_Open(); @@ -38,13 +40,18 @@ static rt_uint32_t nu_trng_run(void) u32RNGValue = rand(); } - rt_mutex_release(&s_TRNG_mutex); + result = rt_mutex_release(&s_TRNG_mutex); + RT_ASSERT(result == RT_EOK); + return u32RNGValue; } rt_err_t nu_trng_init(void) { - rt_mutex_init(&s_TRNG_mutex, NU_CRYPTO_TRNG_NAME, RT_IPC_FLAG_FIFO); + rt_err_t result; + + result = rt_mutex_init(&s_TRNG_mutex, NU_CRYPTO_TRNG_NAME, RT_IPC_FLAG_FIFO); + RT_ASSERT(result == RT_EOK); if ((SYS->CSERVER & SYS_CSERVER_VERSION_Msk) == 0x0) { diff --git a/bsp/nuvoton/libraries/m480/rtt_port/drv_ui2c.c b/bsp/nuvoton/libraries/m480/rtt_port/drv_ui2c.c index 04d25f453b..9130dc878b 100644 --- a/bsp/nuvoton/libraries/m480/rtt_port/drv_ui2c.c +++ b/bsp/nuvoton/libraries/m480/rtt_port/drv_ui2c.c @@ -22,6 +22,7 @@ #define DBG_ENABLE #define DBG_SECTION_NAME LOG_TAG #define DBG_LEVEL DBG_INFO +#define DBG_COLOR #include #define SLV_10BIT_ADDR (0x1E<<2) //1111+0xx+r/w @@ -382,4 +383,4 @@ int rt_hw_ui2c_init(void) INIT_DEVICE_EXPORT(rt_hw_ui2c_init); -#endif //#if (defined(BSP_USING_UI2C) && defined(RT_USING_I2C)) +#endif //#if defined(BSP_USING_UI2C) diff --git a/bsp/nuvoton/libraries/m480/rtt_port/drv_usbd.c b/bsp/nuvoton/libraries/m480/rtt_port/drv_usbd.c index b38d0b9c18..2df21ac8d4 100644 --- a/bsp/nuvoton/libraries/m480/rtt_port/drv_usbd.c +++ b/bsp/nuvoton/libraries/m480/rtt_port/drv_usbd.c @@ -288,7 +288,7 @@ __STATIC_INLINE void _USBD_IRQHandler(void) } else { - /* USB Un-plug */ + /* USB Unplug */ USBD_DISABLE_USB(); rt_usbd_disconnect_handler(&_rt_obj_udc); } @@ -447,7 +447,6 @@ void USBD_IRQHandler(void) _USBD_IRQHandler(); - /* leave interrupt */ rt_interrupt_leave(); } @@ -523,7 +522,7 @@ int nu_usbd_register(void) _rt_obj_udc.parent.user_data = &nu_usbd; _rt_obj_udc.ops = &_udc_ops; - /* Register endpoint infomation */ + /* Register endpoint information */ _rt_obj_udc.ep_pool = _ep_pool; _rt_obj_udc.ep0.id = &_ep_pool[0]; diff --git a/bsp/nuvoton/libraries/m480/rtt_port/drv_usbhost.c b/bsp/nuvoton/libraries/m480/rtt_port/drv_usbhost.c index f168a3fcd4..be19dac9e5 100644 --- a/bsp/nuvoton/libraries/m480/rtt_port/drv_usbhost.c +++ b/bsp/nuvoton/libraries/m480/rtt_port/drv_usbhost.c @@ -446,7 +446,6 @@ static void int_xfer_done_cb(UTR_T *psUTR) free_utr(psUTR); } - static int nu_pipe_xfer(upipe_t pipe, rt_uint8_t token, void *buffer, int nbytes, int timeouts) { S_NU_RH_PORT_CTRL *psPortCtrl; @@ -505,7 +504,6 @@ static int nu_pipe_xfer(upipe_t pipe, rt_uint8_t token, void *buffer, int nbytes } } - //others xfer rt_completion_init(&(psPortDev->utr_completion)); @@ -694,7 +692,6 @@ static void nu_hcd_disconnect_callback( rt_usbh_root_hub_disconnect_handler(s_sUSBHDev.uhcd, port_index); } - /* USB host operations -----------------------------------------------------------*/ static struct uhcd_ops nu_uhcd_ops = { @@ -706,7 +703,7 @@ static struct uhcd_ops nu_uhcd_ops = static rt_err_t nu_hcd_init(rt_device_t device) { - struct nu_usbh_dev * pNuUSBHDev = (struct nu_usbh_dev *)device; + struct nu_usbh_dev *pNuUSBHDev = (struct nu_usbh_dev *)device; usbh_core_init(); //install connect/disconnect callback @@ -716,11 +713,11 @@ static rt_err_t nu_hcd_init(rt_device_t device) //create thread for polling usbh port status /* create usb hub thread */ pNuUSBHDev->polling_thread = rt_thread_create("usbh_drv", nu_usbh_rh_thread_entry, RT_NULL, - NU_USBH_THREAD_STACK_SIZE, 8, 20); - if ( pNuUSBHDev->polling_thread != RT_NULL) + NU_USBH_THREAD_STACK_SIZE, 8, 20); + if (pNuUSBHDev->polling_thread != RT_NULL) { /* startup usb host thread */ - rt_thread_startup( pNuUSBHDev->polling_thread ); + rt_thread_startup(pNuUSBHDev->polling_thread); } else { @@ -751,15 +748,20 @@ uint32_t usbh_tick_from_millisecond(uint32_t msec) /* device pm suspend() entry. */ static int usbhost_pm_suspend(const struct rt_device *device, rt_uint8_t mode) { - struct nu_usbh_dev * pNuUSBHDev = (struct nu_usbh_dev *)device; + rt_err_t result; - RT_ASSERT(pNuUSBHDev!=RT_NULL); + struct nu_usbh_dev *pNuUSBHDev = (struct nu_usbh_dev *)device; + + RT_ASSERT(pNuUSBHDev != RT_NULL); switch (mode) { case PM_SLEEP_MODE_LIGHT: case PM_SLEEP_MODE_DEEP: + pNuUSBHDev->polling_thread->stat = RT_THREAD_READY; - rt_thread_suspend(pNuUSBHDev->polling_thread); + result = rt_thread_suspend(pNuUSBHDev->polling_thread); + RT_ASSERT(result == RT_EOK); + break; default: @@ -772,14 +774,16 @@ static int usbhost_pm_suspend(const struct rt_device *device, rt_uint8_t mode) /* device pm resume() entry. */ static void usbhost_pm_resume(const struct rt_device *device, rt_uint8_t mode) { - struct nu_usbh_dev * pNuUSBHDev = (struct nu_usbh_dev *)device; - RT_ASSERT(pNuUSBHDev!=RT_NULL); + rt_err_t result; + struct nu_usbh_dev *pNuUSBHDev = (struct nu_usbh_dev *)device; + RT_ASSERT(pNuUSBHDev != RT_NULL); switch (mode) { case PM_SLEEP_MODE_LIGHT: case PM_SLEEP_MODE_DEEP: - rt_thread_resume(pNuUSBHDev->polling_thread); + result = rt_thread_resume(pNuUSBHDev->polling_thread); + RT_ASSERT(result == RT_EOK); break; default: @@ -846,7 +850,7 @@ int nu_usbh_register(void) #if defined(RT_USING_PM) rt_pm_device_register(&uhcd->parent, &device_pm_ops); #endif - + return RT_EOK; } INIT_DEVICE_EXPORT(nu_usbh_register); diff --git a/bsp/nuvoton/libraries/m480/rtt_port/drv_uspi.c b/bsp/nuvoton/libraries/m480/rtt_port/drv_uspi.c index 9c19f3007a..656750144e 100644 --- a/bsp/nuvoton/libraries/m480/rtt_port/drv_uspi.c +++ b/bsp/nuvoton/libraries/m480/rtt_port/drv_uspi.c @@ -12,6 +12,14 @@ #include #if defined(BSP_USING_USPI) + +#define LOG_TAG "drv.uspi" +#define DBG_ENABLE +#define DBG_SECTION_NAME LOG_TAG +#define DBG_LEVEL DBG_INFO +#define DBG_COLOR +#include + #include #include #include @@ -56,7 +64,8 @@ typedef struct nu_uspi *uspi_t; /* Private functions ------------------------------------------------------------*/ static rt_err_t nu_uspi_bus_configure(struct rt_spi_device *device, struct rt_spi_configuration *configuration); static rt_uint32_t nu_uspi_bus_xfer(struct rt_spi_device *device, struct rt_spi_message *message); -static void nu_uspi_transmission_with_poll(struct nu_uspi *uspi_bus, uint8_t *send_addr, uint8_t *recv_addr, int length, uint8_t bytes_per_word); +static void nu_uspi_transmission_with_poll(struct nu_uspi *uspi_bus, + uint8_t *send_addr, uint8_t *recv_addr, int length, uint8_t bytes_per_word); static int nu_uspi_register_bus(struct nu_uspi *uspi_bus, const char *name); static void nu_uspi_drain_rxfifo(USPI_T *uspi_base); @@ -160,7 +169,7 @@ static rt_err_t nu_uspi_bus_configure(struct rt_spi_device *device, u32BusClock = USPI_SetBusClock(uspi_bus->uspi_base, configuration->max_hz); if (configuration->max_hz > u32BusClock) { - rt_kprintf("%s clock max frequency is %dHz (!= %dHz)\n", uspi_bus->name, u32BusClock, configuration->max_hz); + LOG_W("%s clock max frequency is %dHz (!= %dHz)\n", uspi_bus->name, u32BusClock, configuration->max_hz); configuration->max_hz = u32BusClock; } @@ -194,7 +203,7 @@ static rt_err_t nu_uspi_bus_configure(struct rt_spi_device *device, } } - /* Clear SPI RX FIFO */ + /* Clear USPI RX FIFO */ nu_uspi_drain_rxfifo(uspi_bus->uspi_base); exit_nu_uspi_bus_configure: @@ -205,16 +214,18 @@ exit_nu_uspi_bus_configure: #if defined(BSP_USING_USPI_PDMA) static void nu_pdma_uspi_rx_cb(void *pvUserData, uint32_t u32EventFilter) { - struct nu_uspi *uspi_bus; - uspi_bus = (struct nu_uspi *)pvUserData; + rt_err_t result; + struct nu_uspi *uspi_bus = (struct nu_uspi *)pvUserData; RT_ASSERT(uspi_bus != RT_NULL); - rt_sem_release(uspi_bus->m_psSemBus); + result = rt_sem_release(uspi_bus->m_psSemBus); + RT_ASSERT(result == RT_EOK); } + static rt_err_t nu_pdma_uspi_rx_config(struct nu_uspi *uspi_bus, uint8_t *pu8Buf, int32_t i32RcvLen, uint8_t bytes_per_word) { - rt_err_t result = RT_ERROR; + rt_err_t result; rt_uint8_t *dst_addr = NULL; nu_pdma_memctrl_t memctrl = eMemCtl_Undefined; @@ -263,7 +274,7 @@ exit_nu_pdma_uspi_rx_config: static rt_err_t nu_pdma_uspi_tx_config(struct nu_uspi *uspi_bus, const uint8_t *pu8Buf, int32_t i32SndLen, uint8_t bytes_per_word) { - rt_err_t result = RT_ERROR; + rt_err_t result; rt_uint8_t *src_addr = NULL; nu_pdma_memctrl_t memctrl = eMemCtl_Undefined; @@ -304,11 +315,11 @@ exit_nu_pdma_uspi_tx_config: /** - * SPI PDMA transfer - */ + * USPI PDMA transfer + **/ static rt_size_t nu_uspi_pdma_transmit(struct nu_uspi *uspi_bus, const uint8_t *send_addr, uint8_t *recv_addr, int length, uint8_t bytes_per_word) { - rt_err_t result = RT_ERROR; + rt_err_t result; /* Get base address of uspi register */ USPI_T *uspi_base = uspi_bus->uspi_base; @@ -322,7 +333,8 @@ static rt_size_t nu_uspi_pdma_transmit(struct nu_uspi *uspi_bus, const uint8_t * USPI_TRIGGER_TX_RX_PDMA(uspi_base); /* Wait PDMA transfer done */ - rt_sem_take(uspi_bus->m_psSemBus, RT_WAITING_FOREVER); + result = rt_sem_take(uspi_bus->m_psSemBus, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); /* Stop DMA TX/RX transfer */ USPI_DISABLE_TX_RX_PDMA(uspi_base); @@ -345,6 +357,7 @@ static rt_err_t nu_hw_uspi_pdma_allocate(struct nu_uspi *uspi_bus) } uspi_bus->m_psSemBus = rt_sem_create("uspibus_sem", 0, RT_IPC_FLAG_FIFO); + RT_ASSERT(uspi_bus->m_psSemBus != RT_NULL); return RT_EOK; @@ -373,10 +386,10 @@ static int nu_uspi_read(USPI_T *uspi_base, uint8_t *recv_addr, uint8_t bytes_per // Read RX data if (!USPI_GET_RX_EMPTY_FLAG(uspi_base)) { + uint32_t val; // Read data from USPI RX FIFO switch (bytes_per_word) { - uint32_t val; case 2: val = USPI_READ_RX(uspi_base); nu_set16_le(recv_addr, val); @@ -385,6 +398,7 @@ static int nu_uspi_read(USPI_T *uspi_base, uint8_t *recv_addr, uint8_t bytes_per *recv_addr = USPI_READ_RX(uspi_base); break; default: + LOG_E("Data length is not supported.\n"); break; } size = bytes_per_word; @@ -397,7 +411,7 @@ static int nu_uspi_write(USPI_T *uspi_base, const uint8_t *send_addr, uint8_t by // Wait USPI TX send data while (USPI_GET_TX_FULL_FLAG(uspi_base)); - // Input data to SPI TX + // Input data to USPI TX switch (bytes_per_word) { case 2: @@ -407,6 +421,7 @@ static int nu_uspi_write(USPI_T *uspi_base, const uint8_t *send_addr, uint8_t by USPI_WRITE_TX(uspi_base, *((uint8_t *)send_addr)); break; default: + LOG_E("Data length is not supported.\n"); break; } @@ -414,8 +429,8 @@ static int nu_uspi_write(USPI_T *uspi_base, const uint8_t *send_addr, uint8_t by } /** - * @brief SPI bus polling - * @param dev : The pointer of the specified SPI module. + * @brief USPI bus polling + * @param dev : The pointer of the specified USPI module. * @param send_addr : Source address * @param recv_addr : Destination address * @param length : Data length @@ -440,10 +455,10 @@ static void nu_uspi_transmission_with_poll(struct nu_uspi *uspi_bus, uspi_bus->dummy = 0; while (length > 0) { - /* Input data to SPI TX FIFO */ + /* Input data to USPI TX FIFO */ length -= nu_uspi_write(uspi_base, (const uint8_t *)&uspi_bus->dummy, bytes_per_word); - /* Read data from RX FIFO */ + /* Read data from USPI RX FIFO */ while (USPI_GET_RX_EMPTY_FLAG(uspi_base)); recv_addr += nu_uspi_read(uspi_base, recv_addr, bytes_per_word); } @@ -453,20 +468,20 @@ static void nu_uspi_transmission_with_poll(struct nu_uspi *uspi_bus, { while (length > 0) { - /* Input data to SPI TX FIFO */ + /* Input data to USPI TX FIFO */ send_addr += nu_uspi_write(uspi_base, send_addr, bytes_per_word); length -= bytes_per_word; - /* Read data from RX FIFO */ + /* Read data from USPI RX FIFO */ while (USPI_GET_RX_EMPTY_FLAG(uspi_base)); recv_addr += nu_uspi_read(uspi_base, recv_addr, bytes_per_word); } } // else - /* Wait RX or drain RX-FIFO */ + /* Wait USPI RX or drain USPI RX-FIFO */ if (recv_addr) { - // Wait SPI transmission done + // Wait USPI transmission done while (USPI_IS_BUSY(uspi_base)) { while (!USPI_GET_RX_EMPTY_FLAG(uspi_base)) @@ -482,7 +497,7 @@ static void nu_uspi_transmission_with_poll(struct nu_uspi *uspi_bus, } else { - /* Clear SPI RX FIFO */ + /* Clear USPI RX FIFO */ nu_uspi_drain_rxfifo(uspi_base); } } @@ -490,10 +505,10 @@ static void nu_uspi_transmission_with_poll(struct nu_uspi *uspi_bus, static void nu_uspi_transfer(struct nu_uspi *uspi_bus, uint8_t *tx, uint8_t *rx, int length, uint8_t bytes_per_word) { #if defined(BSP_USING_USPI_PDMA) - /* DMA transfer constrains */ + /* PDMA transfer constrains */ if ((uspi_bus->pdma_chanid_rx >= 0) && - !((uint32_t)tx % bytes_per_word) && - !((uint32_t)rx % bytes_per_word)) + (!((uint32_t)tx % bytes_per_word)) && + (!((uint32_t)rx % bytes_per_word))) nu_uspi_pdma_transmit(uspi_bus, tx, rx, length, bytes_per_word); else nu_uspi_transmission_with_poll(uspi_bus, tx, rx, length, bytes_per_word); @@ -519,7 +534,7 @@ static rt_uint32_t nu_uspi_bus_xfer(struct rt_spi_device *device, struct rt_spi_ if ((message->length % bytes_per_word) != 0) { /* Say bye. */ - rt_kprintf("%s: error payload length(%d%%%d != 0).\n", uspi_bus->name, message->length, bytes_per_word); + LOG_E("%s: error payload length(%d%%%d != 0).\n", uspi_bus->name, message->length, bytes_per_word); return 0; } @@ -578,7 +593,7 @@ static int rt_hw_uspi_init(void) { if (nu_hw_uspi_pdma_allocate(&nu_uspi_arr[i]) != RT_EOK) { - rt_kprintf("Failed to allocate DMA channels for %s. We will use poll-mode for this bus.\n", nu_uspi_arr[i].name); + LOG_E("Failed to allocate DMA channels for %s. We will use poll-mode for this bus.\n", nu_uspi_arr[i].name); } } #endif diff --git a/bsp/nuvoton/libraries/m480/rtt_port/drv_uuart.c b/bsp/nuvoton/libraries/m480/rtt_port/drv_uuart.c index 1be9643f47..6cd29f79fe 100644 --- a/bsp/nuvoton/libraries/m480/rtt_port/drv_uuart.c +++ b/bsp/nuvoton/libraries/m480/rtt_port/drv_uuart.c @@ -484,7 +484,7 @@ static int nu_hw_uuart_dma_allocate(nu_uuart_t puuart) static rt_err_t nu_uuart_control(struct rt_serial_device *serial, int cmd, void *arg) { rt_err_t result = RT_EOK; - rt_uint32_t flag; + rt_uint32_t flag = 0; rt_ubase_t ctrl_arg = (rt_ubase_t)arg; RT_ASSERT(serial != RT_NULL); diff --git a/bsp/nuvoton/libraries/nu_packages/AudioCodec/audio_test.c b/bsp/nuvoton/libraries/nu_packages/AudioCodec/audio_test.c index 9756f26ffd..d82641b0c7 100644 --- a/bsp/nuvoton/libraries/nu_packages/AudioCodec/audio_test.c +++ b/bsp/nuvoton/libraries/nu_packages/AudioCodec/audio_test.c @@ -23,10 +23,10 @@ - For record function: Run it w/o parameter. - For replay function: Run it with parameter. */ -static void audio_test(int argc, char **argv) +static int audio_test(int argc, char **argv) { #define DEF_MAX_ARGV_NUM 8 - int smplrate[] = {8000, 11025, 16000, 22050, 32000, 44100, 48000}; + int smplrate[] = {8000, 16000, 44100, 48000}; int smplbit[] = {16}; int chnum[] = {1, 2}; struct wavrecord_info info; @@ -74,6 +74,8 @@ static void audio_test(int argc, char **argv) } // k } // j } // i + + return 0; } #ifdef FINSH_USING_MSH diff --git a/bsp/nuvoton/libraries/nu_packages/Demo/usbd_cdc_vcom_echo.c b/bsp/nuvoton/libraries/nu_packages/Demo/usbd_cdc_vcom_echo.c index 8567c38dbc..0ff51c86bf 100644 --- a/bsp/nuvoton/libraries/nu_packages/Demo/usbd_cdc_vcom_echo.c +++ b/bsp/nuvoton/libraries/nu_packages/Demo/usbd_cdc_vcom_echo.c @@ -1,3 +1,15 @@ +/**************************************************************************//** +* +* @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved. +* +* SPDX-License-Identifier: Apache-2.0 +* +* Change Logs: +* Date Author Notes +* 2021-1-10 Wayne First version +* +******************************************************************************/ + #include #if defined(RT_USB_DEVICE_CDC) && (defined(BSP_USING_USBD) || defined(BSP_USING_HSUSBD)) @@ -6,7 +18,11 @@ static struct rt_semaphore rx_sem; static rt_err_t uart_input(rt_device_t dev, rt_size_t size) { - rt_sem_release(&rx_sem); + rt_err_t result = 0; + + result = rt_sem_release(&rx_sem); + RT_ASSERT(result == RT_EOK); + return RT_EOK; } @@ -21,10 +37,8 @@ static void serial_thread_entry(void *parameter) { if (rt_sem_take(&rx_sem, 3 * RT_TICK_PER_SECOND) == -RT_ETIMEOUT) { - time_t now; - /* output current time */ - now = time(RT_NULL); - rt_snprintf(szStr, sizeof(szStr), "%.*s\n", 25, ctime(&now)); + /* output current tick */ + rt_snprintf(szStr, sizeof(szStr), "%d\n", rt_tick_get()); rt_device_write(serial, 0, &szStr[0], rt_strlen(szStr)); continue; } @@ -35,7 +49,7 @@ static void serial_thread_entry(void *parameter) static int vcom_echo_init(void) { - int err = 0; + rt_err_t result = 0; rt_thread_t thread; rt_device_t serial; @@ -45,25 +59,33 @@ static int vcom_echo_init(void) rt_kprintf("find failed!\n"); return RT_ERROR; } - err = rt_device_init(serial); - if (err) + result = rt_device_init(serial); + if (result) { - rt_kprintf("find failed!\n"); - return -RT_ERROR; + rt_kprintf("init failed!\n"); + return -1; + } + result = rt_device_open(serial, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX/* | RT_DEVICE_FLAG_DMA_TX */); + if (result) + { + rt_kprintf("open failed!\n"); + return -1; } - err = rt_device_open(serial, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX/* | RT_DEVICE_FLAG_DMA_TX */); - rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO); + result = rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO); + RT_ASSERT(result == RT_EOK); - rt_device_set_rx_indicate(serial, uart_input); + result = rt_device_set_rx_indicate(serial, uart_input); + RT_ASSERT(result == RT_EOK); thread = rt_thread_create("serial", serial_thread_entry, (void *)serial, 1024, 25, 10); if (thread != RT_NULL) { - rt_thread_startup(thread); + result = rt_thread_startup(thread); + RT_ASSERT(result == RT_EOK); } - return RT_EOK; + return 0; } INIT_APP_EXPORT(vcom_echo_init); diff --git a/bsp/nuvoton/libraries/nu_packages/Demo/usbd_hid_dance_mouse.c b/bsp/nuvoton/libraries/nu_packages/Demo/usbd_hid_dance_mouse.c index 312183334a..8a57ffe6b1 100644 --- a/bsp/nuvoton/libraries/nu_packages/Demo/usbd_hid_dance_mouse.c +++ b/bsp/nuvoton/libraries/nu_packages/Demo/usbd_hid_dance_mouse.c @@ -35,15 +35,17 @@ static void usb_thread_entry(void *parameter) { int8_t i8MouseTable[] = { -16, -16, -16, 0, 16, 16, 16, 0}; uint8_t u8MouseIdx = 0; - uint8_t u8MoveLen=0, u8MouseMode = 1; + uint8_t u8MoveLen = 0, u8MouseMode = 1; uint8_t pu8Buf[4]; rt_err_t result = RT_EOK; rt_device_t device = (rt_device_t)parameter; - rt_sem_init(&tx_sem_complete, "tx_complete_sem_hid", 1, RT_IPC_FLAG_FIFO); + result = rt_sem_init(&tx_sem_complete, "tx_complete_sem_hid", 1, RT_IPC_FLAG_FIFO); + RT_ASSERT(result == RT_EOK); - rt_device_set_tx_complete(device, event_hid_in); + result = rt_device_set_tx_complete(device, event_hid_in); + RT_ASSERT(result == RT_EOK); LOG_I("Ready.\n"); @@ -79,7 +81,7 @@ static void usb_thread_entry(void *parameter) { /* Wait it done. */ result = rt_sem_take(&tx_sem_complete, RT_WAITING_FOREVER); - RT_ASSERT( result== RT_EOK ); + RT_ASSERT(result == RT_EOK); } } // while(1) @@ -96,10 +98,10 @@ static int dance_mouse_init(void) RT_ASSERT(ret == RT_EOK); ret = rt_thread_init(&usb_thread, - "hidd", - usb_thread_entry, device, - usb_thread_stack, sizeof(usb_thread_stack), - 10, 20); + "hidd", + usb_thread_entry, device, + usb_thread_stack, sizeof(usb_thread_stack), + 10, 20); RT_ASSERT(ret == RT_EOK); ret = rt_thread_startup(&usb_thread); diff --git a/bsp/nuvoton/libraries/nu_packages/Kconfig b/bsp/nuvoton/libraries/nu_packages/Kconfig index 0c2d5cd34a..04ae64e150 100644 --- a/bsp/nuvoton/libraries/nu_packages/Kconfig +++ b/bsp/nuvoton/libraries/nu_packages/Kconfig @@ -62,4 +62,10 @@ menu "Nuvoton Packages Config" endif + config NU_PKG_USING_SPINAND + bool "SPI NAND flash." + select BSP_USING_QSPI + select RT_USING_MTD_NAND + default n + endmenu diff --git a/bsp/nuvoton/libraries/nu_packages/SPINAND/SConscript b/bsp/nuvoton/libraries/nu_packages/SPINAND/SConscript new file mode 100644 index 0000000000..b455139402 --- /dev/null +++ b/bsp/nuvoton/libraries/nu_packages/SPINAND/SConscript @@ -0,0 +1,12 @@ +# RT-Thread building script for component + +from building import * + +cwd = GetCurrentDir() +group = [] +if GetDepend('NU_PKG_USING_SPINAND'): + src = Glob('*.c') + Glob('*.cpp') + CPPPATH = [cwd] + group = DefineGroup('nu_pkgs_spinand', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/nuvoton/libraries/nu_packages/SPINAND/drv_spinand.c b/bsp/nuvoton/libraries/nu_packages/SPINAND/drv_spinand.c new file mode 100644 index 0000000000..88e4ac80bc --- /dev/null +++ b/bsp/nuvoton/libraries/nu_packages/SPINAND/drv_spinand.c @@ -0,0 +1,912 @@ +/**************************************************************************//** +* +* @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved. +* +* SPDX-License-Identifier: Apache-2.0 +* +* Change Logs: +* Date Author Notes +* 2021-1-13 Wayne First version +* +******************************************************************************/ + +#include + +#if defined(NU_PKG_USING_SPINAND) && defined(RT_USING_MTD_NAND) + +#define LOG_TAG "drv_spinand" +#define DBG_ENABLE +#define DBG_SECTION_NAME LOG_TAG +#define DBG_LEVEL DBG_INFO +#define DBG_COLOR +#include + +#include "spinand.h" + +struct nu_spinand g_spinandflash_dev = {0}; + +rt_size_t nu_qspi_transfer_message(struct rt_qspi_device *device, struct rt_qspi_message *message) +{ + rt_err_t result; + struct rt_spi_message *index; + + RT_ASSERT(device != RT_NULL); + RT_ASSERT(message != RT_NULL); + + result = rt_mutex_take(&(device->parent.bus->lock), RT_WAITING_FOREVER); + if (result != RT_EOK) + { + rt_set_errno(-RT_EBUSY); + + return 0; + } + + /* reset errno */ + rt_set_errno(RT_EOK); + + /* configure SPI bus */ + if (device->parent.bus->owner != &device->parent) + { + /* not the same owner as current, re-configure SPI bus */ + result = device->parent.bus->ops->configure(&device->parent, &device->parent.config); + if (result == RT_EOK) + { + /* set SPI bus owner */ + device->parent.bus->owner = &device->parent; + } + else + { + /* configure SPI bus failed */ + rt_set_errno(-RT_EIO); + goto __exit; + } + } + + /* transmit each SPI message */ + index = &message->parent; + while (index) + { + if (device->parent.bus->ops->xfer(&device->parent, index) == 0) + { + result = -RT_EIO; + rt_set_errno(-RT_EIO); + goto __exit; + } + index = index->next; + } + + result = RT_EOK; + +__exit: + /* release bus lock */ + rt_mutex_release(&(device->parent.bus->lock)); + + return result; +} + +rt_err_t nu_qspi_send_then_recv(struct rt_qspi_device *device, const void *send_buf, rt_size_t send_length, void *recv_buf, rt_size_t recv_length) +{ + struct rt_qspi_message message[2] = {0}; + + RT_ASSERT(send_buf); + RT_ASSERT(recv_buf); + RT_ASSERT(send_length != 0); + + /* Send message */ + message[0].qspi_data_lines = 1; + + /* Set send buf and send size */ + message[0].parent.recv_buf = RT_NULL; + message[0].parent.send_buf = send_buf; + message[0].parent.length = send_length; + message[0].parent.cs_take = 1; + + message[0].parent.next = &message[1].parent; + + /* Receive message */ + message[1].qspi_data_lines = 1; + + /* Set recv buf and recv size */ + message[1].parent.recv_buf = recv_buf; + message[1].parent.send_buf = RT_NULL; + message[1].parent.length = recv_length; + message[1].parent.cs_release = 1; + + return nu_qspi_transfer_message(device, &message[0]); +} + +rt_err_t nu_qspi_send(struct rt_qspi_device *device, const void *send_buf, rt_size_t length) +{ + RT_ASSERT(send_buf); + RT_ASSERT(length != 0); + + struct rt_qspi_message message = {0}; + char *ptr = (char *)send_buf; + rt_size_t count = 0; + + message.instruction.content = ptr[0]; + message.instruction.qspi_lines = 1; + count++; + + /* set send buf and send size */ + message.qspi_data_lines = 1; + + message.parent.send_buf = ptr + count; + message.parent.recv_buf = RT_NULL; + message.parent.length = length - count; + message.parent.cs_take = 1; + message.parent.cs_release = 1; + + return nu_qspi_transfer_message(device, &message); +} + +static void spinand_dump_buffer(int page, rt_uint8_t *buf, int len, const char *title) +{ + if ((DBG_LEVEL) >= DBG_LOG) + { + int i; + + if (!buf) + { + return; + } + + /* Just print 64-bytes.*/ + len = (len < 64) ? len : 64; + + LOG_I("[%s-Page-%d]", title, page); + + for (i = 0; i < len; i ++) + { + rt_kprintf("%02X ", buf[i]); + if (i % 32 == 31) rt_kprintf("\n"); + } + rt_kprintf("\n"); + } +} + +static rt_err_t spinand_read_id(struct rt_mtd_nand_device *device) +{ + rt_err_t result = RT_EOK ; + uint32_t id = 0; + + result = rt_mutex_take(SPINAND_FLASH_LOCK, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); + + SPINAND_FLASH_OPS->jedecid_get(SPINAND_FLASH_QSPI, &id); + + result = rt_mutex_release(SPINAND_FLASH_LOCK); + RT_ASSERT(result == RT_EOK); + + return (id != 0x0) ? RT_EOK : -RT_ERROR; +} + +static rt_err_t spinand_read_page(struct rt_mtd_nand_device *device, + rt_off_t page, + rt_uint8_t *data, + rt_uint32_t data_len, + rt_uint8_t *spare, + rt_uint32_t spare_len) +{ + rt_err_t result = RT_EOK ; + + LOG_D("[R-%d]data: 0x%08x %d, spare: 0x%08x, %d", page, data, data_len, spare, spare_len); + + RT_ASSERT(device != RT_NULL); + + if (page / device->pages_per_block > device->block_end) + { + LOG_E("[EIO] read page:%d", page); + return -RT_MTD_EIO; + } + + result = rt_mutex_take(SPINAND_FLASH_LOCK, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); + + /* Data load, Read data from flash to cache */ + result = SPINAND_FLASH_OPS->read_dataload(SPINAND_FLASH_QSPI, (page >> 16) & 0xFF, (page >> 8) & 0xFF, (page & 0xFF)); + if (result != RT_EOK) + goto exit_spinand_read_page; + + if (data && data_len) + { + /* Read data: 0~data_len, Read cache to data */ + result = SPINAND_FLASH_OPS->read_quadoutput(SPINAND_FLASH_QSPI, 0, 0, data, data_len); + if (result != RT_EOK) + goto exit_spinand_read_page; + } + + if (spare && spare_len) + { + /* Read data: 2048~spare_len, Read cache to spare */ + result = SPINAND_FLASH_OPS->read_quadoutput(SPINAND_FLASH_QSPI, (SPINAND_FLASH_PAGE_SIZE >> 8) & 0xff, SPINAND_FLASH_PAGE_SIZE & 0xff, spare, spare_len); + if (result != RT_EOK) + goto exit_spinand_read_page; + } + +exit_spinand_read_page: + + rt_mutex_release(SPINAND_FLASH_LOCK); + + spinand_dump_buffer(page, data, data_len, "Read Data"); + spinand_dump_buffer(page, spare, spare_len, "Read Spare"); + + return result; +} + +static rt_err_t spinand_write_page(struct rt_mtd_nand_device *device, + rt_off_t page, + const rt_uint8_t *data, + rt_uint32_t data_len, + const rt_uint8_t *spare, + rt_uint32_t spare_len) +{ + rt_err_t result = RT_EOK ; + + LOG_D("[W-%d]data: 0x%08x %d, spare: 0x%08x, %d", page, data, data_len, spare, spare_len); + + RT_ASSERT(device != RT_NULL); + + if (page / device->pages_per_block > device->block_end) + { + LOG_E("[EIO] write page:%d", page); + return -RT_MTD_EIO; + } + + spinand_dump_buffer(page, (uint8_t *)data, data_len, "WRITE DATA"); + spinand_dump_buffer(page, (uint8_t *)spare, spare_len, "WRITE SPARE"); + + result = rt_mutex_take(SPINAND_FLASH_LOCK, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); + + if (SPINAND_FLASH_MCP == 1) + { + /* Select die. */ + if ((result = SPINAND_FLASH_OPS->die_select(SPINAND_FLASH_QSPI, SPINAND_DIE_ID0)) != RT_EOK) + goto exit_spinand_write_page; + } + + /* Read data: 0~2111, to cache */ + if (data && data_len) + result = SPINAND_FLASH_OPS->program_dataload(SPINAND_FLASH_QSPI, 0, 0, (uint8_t *)data, data_len, (uint8_t *)spare, spare_len); + else + result = SPINAND_FLASH_OPS->program_dataload(SPINAND_FLASH_QSPI, (SPINAND_FLASH_PAGE_SIZE >> 8) & 0xff, SPINAND_FLASH_PAGE_SIZE & 0xff, RT_NULL, 0, (uint8_t *)spare, spare_len); + + if (result != RT_EOK) + goto exit_spinand_write_page; + + /* Flush data in cache to flash */ + result = SPINAND_FLASH_OPS->program_execute(SPINAND_FLASH_QSPI, (((page) >> 16) & 0xFF), (((page) >> 8) & 0xFF), (page) & 0xFF); + if (result != RT_EOK) + goto exit_spinand_write_page; + + result = RT_EOK; + +exit_spinand_write_page: + + rt_mutex_release(SPINAND_FLASH_LOCK); + + return result; +} + +static rt_err_t spinand_move_page(struct rt_mtd_nand_device *device, rt_off_t src_page, rt_off_t dst_page) +{ + rt_err_t result = RT_EOK ; + uint8_t u8WECmd; + + RT_ASSERT(device != RT_NULL); + + if ((src_page / device->pages_per_block > device->block_end) || + (dst_page / device->pages_per_block > device->block_end)) + { + LOG_E("EIO src:%08x, dst:%08x!", src_page, dst_page); + return -RT_MTD_EIO; + } + + LOG_D("src_page: %d, dst_page: %d", src_page, dst_page); + + result = rt_mutex_take(SPINAND_FLASH_LOCK, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); + + /* Data load, Read data from flash to cache */ + result = SPINAND_FLASH_OPS->read_dataload(SPINAND_FLASH_QSPI, (src_page >> 16) & 0xFF, (src_page >> 8) & 0xFF, (src_page & 0xFF)); + if (result != RT_EOK) + goto exit_spinand_move_page; + + /* Enable WE before writting. */ + u8WECmd = 0x06; + if ((result = nu_qspi_send(SPINAND_FLASH_QSPI, &u8WECmd, sizeof(u8WECmd))) != RT_EOK) + goto exit_spinand_move_page; + + /* Flush cache to flash */ + result = SPINAND_FLASH_OPS->program_execute(SPINAND_FLASH_QSPI, (((dst_page) >> 16) & 0xFF), (((dst_page) >> 8) & 0xFF), (dst_page) & 0xFF); + if (result != RT_EOK) + goto exit_spinand_move_page; + + result = RT_EOK; + +exit_spinand_move_page: + + rt_mutex_release(SPINAND_FLASH_LOCK); + + return result; +} + +static rt_err_t spinand_erase_block_force(struct rt_mtd_nand_device *device, rt_uint32_t block) +{ + rt_err_t result = RT_EOK ; + uint32_t page; + + RT_ASSERT(device != RT_NULL); + + if (block > device->block_end) + { + LOG_E("[EIO] block:%d", block); + return -RT_MTD_EIO; + } + + page = block * SPINAND_FLASH_PAGE_PER_BLOCK_NUM; + + LOG_D("force erase block: %d -> page: %d", block, page); + + result = rt_mutex_take(SPINAND_FLASH_LOCK, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); + + result = SPINAND_FLASH_OPS->block_erase(SPINAND_FLASH_QSPI, (page >> 16) & 0xFF, (page >> 8) & 0xFF, page & 0xFF); + if (result != RT_EOK) + goto exit_spinand_erase_block_force; + + result = RT_EOK; + +exit_spinand_erase_block_force: + + rt_mutex_release(SPINAND_FLASH_LOCK); + + return result; +} + +static rt_err_t spinand_erase_block(struct rt_mtd_nand_device *device, rt_uint32_t block) +{ + rt_err_t result = RT_EOK ; + uint32_t page; + + RT_ASSERT(device != RT_NULL); + + if (block > device->block_end) + { + LOG_E("[EIO] block:%d", block); + return -RT_MTD_EIO; + } + + page = block * SPINAND_FLASH_PAGE_PER_BLOCK_NUM; + + LOG_D("erase block: %d -> page: %d", block, page); + + result = rt_mutex_take(SPINAND_FLASH_LOCK, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); + + /* Erase block after checking it is bad or not. */ + if (SPINAND_FLASH_OPS->block_isbad(SPINAND_FLASH_QSPI, page) != 0) + { + LOG_W("Block %d is bad.\n", block); + result = -RT_ERROR; + goto exit_spinand_erase_block; + } + else + { + result = SPINAND_FLASH_OPS->block_erase(SPINAND_FLASH_QSPI, (page >> 16) & 0xFF, (page >> 8) & 0xFF, page & 0xFF); + if (result != RT_EOK) + goto exit_spinand_erase_block; + } + + result = RT_EOK; + +exit_spinand_erase_block: + + rt_mutex_release(SPINAND_FLASH_LOCK); + + return result; +} + +static rt_err_t spinand_check_block(struct rt_mtd_nand_device *device, rt_uint32_t block) +{ + rt_err_t result = RT_EOK ; + uint32_t page = 0; + uint8_t isbad = 0; + + RT_ASSERT(device != RT_NULL); + + if (block > device->block_end) + { + LOG_E("[EIO] block:%d", block); + return -RT_MTD_EIO; + } + + page = block * SPINAND_FLASH_PAGE_PER_BLOCK_NUM; + + LOG_D("check block status: %d -> page: %d", block, page); + + result = rt_mutex_take(SPINAND_FLASH_LOCK, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); + + isbad = SPINAND_FLASH_OPS->block_isbad(SPINAND_FLASH_QSPI, page); + + result = rt_mutex_release(SPINAND_FLASH_LOCK); + RT_ASSERT(result == RT_EOK); + + return (isbad == 0) ? RT_EOK : -RT_ERROR ; +} + +static rt_err_t spinand_mark_badblock(struct rt_mtd_nand_device *device, rt_uint32_t block) +{ + rt_err_t result = RT_EOK ; + uint32_t page = 0; + + RT_ASSERT(device != RT_NULL); + + if (block > device->block_end) + { + LOG_E("[EIO] block:%d", block); + return -RT_MTD_EIO; + } + + page = block * SPINAND_FLASH_PAGE_PER_BLOCK_NUM; + + LOG_D("mark bad block: %d -> page: %d", block, page); + + result = rt_mutex_take(SPINAND_FLASH_LOCK, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); + + /* Erase block after checking it is bad or not. */ + if (SPINAND_FLASH_OPS->block_isbad(SPINAND_FLASH_QSPI, page) != 0) + { + LOG_W("Block %d is bad.\n", block); + result = RT_EOK; + } + else + { + result = SPINAND_FLASH_OPS->block_markbad(SPINAND_FLASH_QSPI, page); + } + + rt_mutex_release(SPINAND_FLASH_LOCK); + + return result; +} + +static struct rt_mtd_nand_driver_ops spinand_ops = +{ + spinand_read_id, + spinand_read_page, + spinand_write_page, + spinand_move_page, + spinand_erase_block, + spinand_check_block, + spinand_mark_badblock +}; + +static uint32_t u32IsInited = 0; +rt_err_t rt_hw_mtd_spinand_init(void) +{ + int i = 0; + rt_err_t result; + char szTmp[8]; + + if (u32IsInited) + return RT_EOK; + + result = rt_mutex_init(SPINAND_FLASH_LOCK, "spinand", RT_IPC_FLAG_FIFO); + RT_ASSERT(result == RT_EOK); + + result = spinand_flash_init(SPINAND_FLASH_QSPI); + RT_ASSERT(result == RT_EOK); + + for (i = 0; i < MTD_SPINAND_PARTITION_NUM; i++) + { + mtd_partitions[i].page_size = SPINAND_FLASH_PAGE_SIZE; /* The Page size in the flash */ + mtd_partitions[i].pages_per_block = SPINAND_FLASH_PAGE_PER_BLOCK_NUM; /* How many page number in a block */ + + mtd_partitions[i].oob_size = SPINAND_FLASH_OOB_SIZE; /* Out of bank size */ + mtd_partitions[i].oob_free = 32; /* the free area in oob that flash driver not use */ + mtd_partitions[i].plane_num = SPINAND_FLASH_MCP ; /* the number of plane in the NAND Flash */ + mtd_partitions[i].ops = &spinand_ops; + + rt_snprintf(szTmp, sizeof(szTmp), "nand%d", i); + + result = rt_mtd_nand_register_device(szTmp, &mtd_partitions[i]); + RT_ASSERT(result == RT_EOK); + } + + u32IsInited = 1; + + return result; +} + +rt_err_t rt_hw_mtd_spinand_register(const char *device_name) +{ + rt_device_t pDev; + rt_err_t result; + + if ((pDev = rt_device_find(device_name)) == RT_NULL) + return -RT_ERROR; + + SPINAND_FLASH_QSPI = (struct rt_qspi_device *)pDev; + + SPINAND_FLASH_QSPI->config.parent.mode = RT_SPI_MODE_0 | RT_SPI_MSB; + SPINAND_FLASH_QSPI->config.parent.data_width = 8; + SPINAND_FLASH_QSPI->config.parent.max_hz = 48000000; + SPINAND_FLASH_QSPI->config.ddr_mode = 0; + SPINAND_FLASH_QSPI->config.qspi_dl_width = 4; + + result = rt_spi_configure(&SPINAND_FLASH_QSPI->parent, &SPINAND_FLASH_QSPI->config.parent); + RT_ASSERT(result == RT_EOK); + + return rt_hw_mtd_spinand_init(); +} + +#if defined(RT_USING_DFS_UFFS) +#include "dfs_uffs.h" +void uffs_setup_storage(struct uffs_StorageAttrSt *attr, + struct rt_mtd_nand_device *nand) +{ + RT_ASSERT(attr != RT_NULL); + RT_ASSERT(nand != RT_NULL); + + rt_memset(attr, 0, sizeof(struct uffs_StorageAttrSt)); + + attr->page_data_size = nand->page_size; /* page data size */ + attr->pages_per_block = nand->pages_per_block; /* pages per block */ + attr->spare_size = nand->oob_size; /* page spare size */ + attr->ecc_opt = RT_CONFIG_UFFS_ECC_MODE; /* ecc option */ + attr->ecc_size = nand->oob_size - nand->oob_free; /* ecc size */ + attr->block_status_offs = 0; /* indicate block bad or good, offset in spare */ + attr->layout_opt = RT_CONFIG_UFFS_LAYOUT; /* let UFFS do the spare layout */ + + /* initialize _uffs_data_layout and _uffs_ecc_layout */ + rt_memcpy(attr->_uffs_data_layout, spinand_flash_data_layout, UFFS_SPARE_LAYOUT_SIZE); + rt_memcpy(attr->_uffs_ecc_layout, spinand_flash_ecc_layout, UFFS_SPARE_LAYOUT_SIZE); + + attr->data_layout = attr->_uffs_data_layout; + attr->ecc_layout = attr->_uffs_ecc_layout; +} +#endif + +#include +static int nread(int argc, char **argv) +{ + int ret = -1; + rt_uint8_t *spare = RT_NULL; + rt_uint8_t *data_ptr = RT_NULL; + struct rt_mtd_nand_device *device; + rt_uint32_t partition, page; + + if (argc != 3) + { + LOG_E("Usage %s: %s .\n", __func__, __func__); + goto exit_nread; + } + + page = atoi(argv[2]); + + partition = atoi(argv[1]); + + if (partition >= MTD_SPINAND_PARTITION_NUM) + goto exit_nread; + + device = &mtd_partitions[partition]; + data_ptr = (rt_uint8_t *) rt_malloc(SPINAND_FLASH_PAGE_SIZE); + if (data_ptr == RT_NULL) + { + LOG_E("data_ptr: no memory\n"); + goto exit_nread; + } + spare = (rt_uint8_t *) rt_malloc(SPINAND_FLASH_OOB_SIZE); + if (spare == RT_NULL) + { + LOG_E("spare: no memory\n"); + goto exit_nread; + } + + rt_memset(spare, 0, SPINAND_FLASH_OOB_SIZE); + rt_memset(data_ptr, 0, SPINAND_FLASH_PAGE_SIZE); + + page = page + device->block_start * device->pages_per_block; + + if (spinand_read_page(device, page, &data_ptr[0], SPINAND_FLASH_PAGE_SIZE, &spare[0], SPINAND_FLASH_OOB_SIZE) != RT_EOK) + goto exit_nread; + + LOG_I("Partion:%d page-%d", partition, page); + + ret = 0; + +exit_nread: + + /* release memory */ + if (data_ptr) + rt_free(data_ptr); + + if (spare) + rt_free(spare); + + return ret; +} + +static int nwrite(int argc, char **argv) +{ + int i, ret = -1; + rt_uint8_t *data_ptr = RT_NULL; + struct rt_mtd_nand_device *device; + rt_uint32_t partition, page; + + if (argc != 3) + { + LOG_E("Usage %s: %s .\n", __func__, __func__); + goto exit_nwrite; + } + + partition = atoi(argv[1]); + page = atoi(argv[2]); + + if (partition >= MTD_SPINAND_PARTITION_NUM) + goto exit_nwrite; + + device = &mtd_partitions[partition]; + + data_ptr = (rt_uint8_t *) rt_malloc(SPINAND_FLASH_PAGE_SIZE); + if (data_ptr == RT_NULL) + { + LOG_E("data_ptr: no memory\n"); + goto exit_nwrite; + } + + /* Need random data to test ECC */ + for (i = 0; i < SPINAND_FLASH_PAGE_SIZE; i ++) + data_ptr[i] = i / 5 - i; + + page = page + device->block_start * device->pages_per_block; + spinand_write_page(device, page, &data_ptr[0], SPINAND_FLASH_PAGE_SIZE, NULL, 0); + + LOG_I("Wrote data into %d in partition-index %d.", page, partition); + + ret = 0; + +exit_nwrite: + + /* release memory */ + if (data_ptr) + rt_free(data_ptr); + + return ret; +} + +static int nmove(int argc, char **argv) +{ + struct rt_mtd_nand_device *device; + rt_uint32_t partition, src, dst; + + if (argc != 4) + { + LOG_E("Usage %s: %s .\n", __func__, __func__); + goto exit_nmove; + } + + partition = atoi(argv[1]); + src = atoi(argv[2]); + dst = atoi(argv[3]); + + if (partition >= MTD_SPINAND_PARTITION_NUM) + return -1; + + device = &mtd_partitions[partition]; + + spinand_move_page(device, + src + device->block_start * device->pages_per_block, + dst + device->block_start * device->pages_per_block); + + LOG_I("Move data into %d from %d in partition-index %d.", dst, src, partition); + + return 0; + +exit_nmove: + return -1; +} + +static int nerase(int argc, char **argv) +{ + struct rt_mtd_nand_device *device; + int partition, block; + + if (argc != 3) + { + LOG_E("Usage %s: %s .\n", __func__, __func__); + goto exit_nerase; + } + + partition = atoi(argv[1]); + block = atoi(argv[2]); + + if (partition >= MTD_SPINAND_PARTITION_NUM) + goto exit_nerase; + + device = &mtd_partitions[partition]; + + if (spinand_erase_block(device, block + device->block_start) != RT_EOK) + goto exit_nerase; + + LOG_I("Erased block %d in partition-index %d.", block + device->block_start, partition); + + return 0; + +exit_nerase: + + return -1; +} + +static int nerase_force(int argc, char **argv) +{ + struct rt_mtd_nand_device *device; + int partition, block; + + if (argc != 2) + { + LOG_E("Usage %s: %s \n", __func__, __func__); + goto exit_nerase_force; + } + + partition = atoi(argv[1]); + + if (partition >= MTD_SPINAND_PARTITION_NUM) + goto exit_nerase_force; + + device = &mtd_partitions[partition]; + + for (block = 0; block <= device->block_end; block++) + { + if (spinand_erase_block_force(device, block + device->block_start) != RT_EOK) + goto exit_nerase_force; + LOG_I("Erased block %d in partition-index %d. forcely", block + device->block_start, partition); + } + return 0; + +exit_nerase_force: + + return -1; +} + +static rt_err_t nmarkbad(int argc, char **argv) +{ + struct rt_mtd_nand_device *device; + int partition, block; + + if (argc != 3) + { + LOG_E("Usage %s: %s .\n", __func__, __func__); + goto exit_nmarkbad; + } + + partition = atoi(argv[1]); + block = atoi(argv[2]); + + if (partition >= MTD_SPINAND_PARTITION_NUM) + goto exit_nmarkbad; + + device = &mtd_partitions[partition]; + + if (spinand_mark_badblock(device, block + device->block_start) != RT_EOK) + goto exit_nmarkbad; + + + LOG_I("Marked block %d in partition-index %d.", block + device->block_start, partition); + + return 0; + +exit_nmarkbad: + + return -1; +} + +static int nerase_all(int argc, char **argv) +{ + rt_uint32_t index; + rt_uint32_t partition; + struct rt_mtd_nand_device *device; + + if (argc != 2) + { + LOG_E("Usage %s: %s .\n", __func__, __func__); + goto exit_nerase_all; + } + + partition = atoi(argv[1]); + + if (partition >= MTD_SPINAND_PARTITION_NUM) + goto exit_nerase_all; + + device = &mtd_partitions[partition]; + + for (index = 0; index < device->block_total; index ++) + { + spinand_erase_block(device, index); + } + + LOG_I("Erased all block in partition-index %d.", partition); + + return 0; + +exit_nerase_all: + + return -1; +} + +static int ncheck_all(int argc, char **argv) +{ + rt_uint32_t index; + rt_uint32_t partition; + struct rt_mtd_nand_device *device; + + if (argc != 2) + { + LOG_E("Usage %s: %s .\n", __func__, __func__); + return -1; + } + + partition = atoi(argv[1]); + + if (partition >= MTD_SPINAND_PARTITION_NUM) + return -1; + + device = &mtd_partitions[partition]; + + for (index = 0; index < device->block_total; index ++) + { + LOG_I("Partion:%d Block-%d is %s", partition, index, spinand_check_block(device, index) ? "bad" : "good"); + } + + return 0; +} + +static int nid(int argc, char **argv) +{ + spinand_read_id(RT_NULL); + return 0; +} + +static int nlist(int argc, char **argv) +{ + rt_uint32_t index; + struct rt_mtd_nand_device *device; + + rt_kprintf("\n"); + for (index = 0 ; index < MTD_SPINAND_PARTITION_NUM ; index++) + { + device = &mtd_partitions[index]; + rt_kprintf("[Partition #%d]\n", index); + rt_kprintf("Name: %s\n", device->parent.parent.name); + rt_kprintf("Start block: %d\n", device->block_start); + rt_kprintf("End block: %d\n", device->block_end); + rt_kprintf("Block number: %d\n", device->block_total); + rt_kprintf("Plane number: %d\n", device->plane_num); + rt_kprintf("Pages per Block: %d\n", device->pages_per_block); + rt_kprintf("Page size: %d bytes\n", device->page_size); + rt_kprintf("Spare size: %d bytes\n", device->oob_size); + rt_kprintf("Total size: %d bytes (%d KB)\n", device->block_total * device->pages_per_block * device->page_size, + device->block_total * device->pages_per_block * device->page_size / 1024); + rt_kprintf("\n"); + } + return 0; +} + + +#ifdef FINSH_USING_MSH + MSH_CMD_EXPORT(nid, nand id); + MSH_CMD_EXPORT(nlist, list all partition information on nand); + MSH_CMD_EXPORT(nmove, nand copy page); + MSH_CMD_EXPORT(nerase, nand erase a block of one partiton); + MSH_CMD_EXPORT(nerase_force, nand erase a block of one partiton forcely); + MSH_CMD_EXPORT(nerase_all, erase all blocks of a partition); + MSH_CMD_EXPORT(ncheck_all, check all blocks of a partition); + MSH_CMD_EXPORT(nmarkbad, nand mark bad block of one partition); + MSH_CMD_EXPORT(nwrite, nand write page); + MSH_CMD_EXPORT(nread, nand read page); +#endif + +#endif diff --git a/bsp/nuvoton/libraries/nu_packages/SPINAND/spinand.c b/bsp/nuvoton/libraries/nu_packages/SPINAND/spinand.c new file mode 100644 index 0000000000..f700da1c72 --- /dev/null +++ b/bsp/nuvoton/libraries/nu_packages/SPINAND/spinand.c @@ -0,0 +1,693 @@ +/**************************************************************************//** +* +* @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved. +* +* SPDX-License-Identifier: Apache-2.0 +* +* Change Logs: +* Date Author Notes +* 2021-1-13 Wayne First version +* +******************************************************************************/ + +#include + +#if defined(NU_PKG_USING_SPINAND) + +#define LOG_TAG "spinand_flash" +#define DBG_ENABLE +#define DBG_SECTION_NAME LOG_TAG +#define DBG_LEVEL DBG_INFO +#define DBG_COLOR +#include + +#include "spinand.h" + +const struct nu_spinand_info g_spinandflash_list[] = +{ + /* Winbond */ + { 0xEFAA21, 2048, 64, 0x6b, 0xff, 0xff, 0xff, 0x1, 1024, 64, 0, "Winbond 128MB: 2048+64@64@1024" }, /* Only tested */ + +#if 0 + { 0xEFAA22, 2048, 64, 0x6b, 0xff, 0xff, 0xff, 0x1, 2048, 64, 0, "Winbond 256MB: 2048+64@64@1024" }, + { 0xEFAB21, 2048, 64, 0x6b, 0xff, 0xff, 0xff, 0x1, 1024, 64, 1, "Winbond 256MB: 2048+64@64@1024, MCP" }, + + /* Not test and supporting yet. */ + /* MXIC */ + { 0x00C212, 2048, 64, 0x6b, 0x05, 0x01, 0x40, 0x1, 1024, 64, 0, "MXIC 128MB: 2048+64@64@1024" }, + + /* XTX */ + { 0x0BE20B, 2048, 64, 0x6b, 0xff, 0xff, 0xff, 0x1, 2048, 64, 0, "XTX 256MB: 2048+64@64@2048" }, + { 0x0BF20B, 2048, 64, 0x6b, 0xff, 0xff, 0xff, 0x1, 2048, 64, 0, "XTX 256MB: 2048+64@64@2048" }, + { 0x0BE10B, 2048, 64, 0x6b, 0xff, 0xff, 0xff, 0x1, 1024, 64, 0, "XTX 256MB: 2048+64@64@1024" }, + { 0x0BF10B, 2048, 64, 0x6b, 0xff, 0xff, 0xff, 0x1, 1024, 64, 0, "XTX 256MB: 2048+64@64@1024" }, + + /* ATO */ + { 0x9B129B, 2048, 64, 0x6b, 0x0f, 0x1f, 0x01, 0x1, 1024, 64, 0, "ATO 128MB: 2048+64@64@1024" }, + + /* Micro */ + { 0x2C242C, 2048, 128, 0x6b, 0x0f, 0x1f, 0x01, 0x1, 2048, 64, 0, "Micro 256MB: 2048+128@64@2048" }, + + /* GigaDevice */ + { 0xB148C8, 2048, 128, 0x6b, 0x0f, 0x1f, 0x01, 0x1, 1024, 64, 0, "GD 128MB: 2048+128@64@1024" }, + + /* Unknown */ + { 0x00C8D1, 2048, 128, 0x6b, 0x0f, 0x1f, 0x01, 0x1, 1024, 64, 0, "Unknown 128MB: 2048+128@64@1024" }, + { 0x00C851, 2048, 128, 0x6b, 0x0f, 0x1f, 0x01, 0x1, 1024, 64, 0, "Unknown 128MB: 2048+128@64@1024" }, + { 0x98E240, 2048, 128, 0x6b, 0x0f, 0x1f, 0x01, 0x1, 1024, 64, 0, "Unknown 128MB: 2048+128@64@1024" } +#endif +}; +#define SPINAND_LIST_ELEMENT_NUM ( sizeof(g_spinandflash_list)/sizeof(struct nu_spinand_info) ) + + +/* +For 0xEFAA21 description: + +Data Area(2048-Byte) +----------------------------- +|Sect-0|Sect-1|Sect-2|Sect-3| +|(512B)|(512B)|(512B)|(512B)| +----------------------------- + + Spare Area(64-Byte) + --------------------------------- + |Spare-0|Spare-1|Spare-2|Spare-3| + | (16B) | (16B) | (16B) | (16B) | + --------------------------------- + + ----------------- Spare-0 ------------------- +/ \ +------------------------------------------------- +| BBM | UD2 | UD1 | ECC Sect-0 | ECC Spare | +| 0 1 | 2 3 | 4 5 6 7 | 8 9 A B C D | E F | +------------------------------------------------- +| NO ECC | ECC PROTECTED | ECC 4-D | + +BBM: Bad block marker. +UD1: User Data 1. +UD2: User Data 2. +ECC Sect-n: ECC for sector-n. +ECC Spare: ECC for spare 4-D. + + ---------------- Spare-1 ------------------- +/ \ +----------------------------------------------- +| UD2 | UD1 | ECC Sect-1 | ECC Spare | +| 0 1 2 3 | 4 5 6 7 | 8 9 A B C D | E F | +----------------------------------------------- +| NO ECC | ECC PROTECTED | ECC 14-1D | + + ---------------- Spare-2 ------------------- +/ \ +----------------------------------------------- +| UD2 | UD1 | ECC Sect-2 | ECC Spare | +| 0 1 2 3 | 4 5 6 7 | 8 9 A B C D | E F | +----------------------------------------------- +| NO ECC | ECC PROTECTED | ECC 24-2D | + + ---------------- Spare-3 ------------------- +/ \ +----------------------------------------------- +| UD2 | UD1 | ECC Sect-3 | ECC Spare | +| 0 1 2 3 | 4 5 6 7 | 8 9 A B C D | E F | +----------------------------------------------- +| NO ECC | ECC PROTECTED | ECC 34-3D | +*/ + +rt_uint8_t spinand_flash_data_layout[SPINAND_SPARE_LAYOUT_SIZE] = +{ +#if defined(RT_USING_DFS_UFFS) + /* For storing Seal-byte at 0x37. */ + 0x04, 0x04, 0x14, 0x04, 0x24, 0x04, 0x34, 0x03, 0xFF, 0x00 +#else + 0x04, 0x04, 0x14, 0x04, 0x24, 0x04, 0x34, 0x04, 0xFF, 0x00 +#endif +}; + +rt_uint8_t spinand_flash_ecc_layout[SPINAND_SPARE_LAYOUT_SIZE] = +{ +#if defined(RT_USING_DFS_UFFS) + /* For storing Seal-byte at 0x37 and not report latest ECC part in Spare-3 */ + 0x08, 0x08, 0x18, 0x08, 0x28, 0x08, /*0x38, 0x08,*/ 0xFF, 0x00 +#else + 0x08, 0x08, 0x18, 0x08, 0x28, 0x08, 0x38, 0x08, 0xFF, 0x00 +#endif +}; + +static rt_err_t spinand_info_read(struct rt_qspi_device *qspi); + +static rt_err_t spinand_die_select(struct rt_qspi_device *qspi, uint8_t select_die) +{ + uint8_t au8Cmd[2] = { 0xC2, 0x0 }; + au8Cmd[1] = select_die; + + return nu_qspi_send(qspi, &au8Cmd[0], sizeof(au8Cmd)); +} + +static uint8_t spinand_isbusy(struct rt_qspi_device *qspi) +{ +#define BUSY_CKECKING_TIMEOUT_MS 3000 + volatile uint8_t SR = 0xFF; + rt_err_t result; + uint8_t au8Cmd[2] = { 0x0F, 0xC0 }; + + uint32_t u32CheckingDuration = rt_tick_from_millisecond(BUSY_CKECKING_TIMEOUT_MS); + uint32_t u32Start = rt_tick_get(); + + do + { + result = nu_qspi_send_then_recv(qspi, &au8Cmd[0], sizeof(au8Cmd), (void *)&SR, 1); + if (result != RT_EOK) + goto timeout_spinand_isbusy; + + if ((rt_tick_get() - u32Start) >= u32CheckingDuration) + { + goto timeout_spinand_isbusy; + } + + } + while ((SR & 0x1) != 0x00); + + return 0; + +timeout_spinand_isbusy: + + LOG_E("Error: spinand timeout."); + + return 1; +} + +static rt_err_t spinand_program_dataload( + struct rt_qspi_device *qspi, + uint8_t u8AddrH, + uint8_t u8AddrL, + uint8_t *pu8DataBuff, + uint32_t u32DataCount, + uint8_t *pu8SpareBuff, + uint32_t u32SpareCount) +{ + uint32_t volatile i = 0; + uint8_t u8WECmd = 0x06; + rt_err_t result = RT_EOK; + + struct rt_qspi_message qspi_messages[2] = {0}; + + /* 1-bit mode */ + qspi_messages[0].instruction.content = 0x32; + qspi_messages[0].instruction.qspi_lines = 1; + + qspi_messages[0].address.content = (u8AddrH << 8) | (u8AddrL); + qspi_messages[0].address.size = 2 * 8; + qspi_messages[0].address.qspi_lines = 1; + + /* 4-bit mode */ + qspi_messages[0].qspi_data_lines = 4; + qspi_messages[0].parent.cs_take = 1; + qspi_messages[0].parent.cs_release = 0; + qspi_messages[0].parent.send_buf = pu8DataBuff; + qspi_messages[0].parent.length = u32DataCount; + qspi_messages[0].parent.next = &qspi_messages[1].parent; + + qspi_messages[1].qspi_data_lines = 4; + qspi_messages[1].parent.cs_take = 0; + qspi_messages[1].parent.cs_release = 1; + qspi_messages[1].parent.send_buf = pu8SpareBuff; + qspi_messages[1].parent.length = u32SpareCount; + + if ((result = nu_qspi_send(qspi, &u8WECmd, sizeof(u8WECmd))) != RT_EOK) + goto exit_spinand_program_dataload; + + result = nu_qspi_transfer_message(qspi, (struct rt_qspi_message *)&qspi_messages[0]); + +exit_spinand_program_dataload: + + return result; +} + +static uint8_t spinand_status_register_read(struct rt_qspi_device *qspi, uint8_t u8SRSel) +{ + uint8_t u8SR = 0; + uint8_t au8Cmd[2]; + + switch (u8SRSel) + { + case 0x01: + au8Cmd[0] = 0x05; + au8Cmd[1] = 0xA0; + break; + + case 0x02: + au8Cmd[0] = 0x0F; + au8Cmd[1] = 0xB0; + break; + + case 0x03: + au8Cmd[0] = 0x05; + au8Cmd[1] = 0xC0; + break; + + default: + RT_ASSERT(0); + break; + } + + if (nu_qspi_send_then_recv(qspi, &au8Cmd[0], sizeof(au8Cmd), &u8SR, 1) != RT_EOK) + RT_ASSERT(0); + + return u8SR; +} + +static rt_err_t spinand_status_register_write(struct rt_qspi_device *qspi, uint8_t u8SRSel, uint8_t u8Value) +{ + rt_err_t result = RT_EOK; + uint8_t au8Cmd[3]; + + switch (u8SRSel) + { + case 0x01: + au8Cmd[0] = 0x01; + au8Cmd[1] = 0xA0; + break; + + case 0x02: + au8Cmd[0] = 0x01; + au8Cmd[1] = 0xB0; + break; + + case 0x03: + au8Cmd[0] = 0x01; + au8Cmd[1] = 0xC0; + break; + + default: + result = RT_EINVAL; + goto exit_spinand_status_register_write; + } + + au8Cmd[2] = u8Value; + + if ((result = nu_qspi_send(qspi, &au8Cmd[0], sizeof(au8Cmd))) != RT_EOK) + goto exit_spinand_status_register_write; + + if (spinand_isbusy(qspi)) + { + result = RT_EIO; + goto exit_spinand_status_register_write; + } + +exit_spinand_status_register_write: + + return result; +} + +static rt_err_t spinand_program_execute(struct rt_qspi_device *qspi, uint8_t u8Addr2, uint8_t u8Addr1, uint8_t u8Addr0) +{ + rt_err_t result; + uint8_t au8Cmd[4], u8SR; + + au8Cmd[0] = 0x10 ; + au8Cmd[1] = u8Addr2; + au8Cmd[2] = u8Addr1; + au8Cmd[3] = u8Addr0; + + if ((result = nu_qspi_send(qspi, &au8Cmd, sizeof(au8Cmd))) != RT_EOK) + goto exit_spinand_program_execute; + + if (spinand_isbusy(qspi)) + { + result = -RT_MTD_EIO; + goto exit_spinand_program_execute; + } + + u8SR = (spinand_status_register_read(SPINAND_FLASH_QSPI, 3) & 0x0C) >> 2; + if (u8SR == 1) + { + result = -RT_MTD_EIO; + LOG_E("Error write status!"); + } + +exit_spinand_program_execute: + + return result; +} + +static rt_err_t spinand_normal_read(struct rt_qspi_device *qspi, uint8_t u8AddrH, uint8_t u8AddrL, uint8_t *pu8Buff, uint32_t u32Count) +{ + uint8_t au8Cmd[4]; + + au8Cmd[0] = 0x03; + au8Cmd[1] = u8AddrH; + au8Cmd[2] = u8AddrL; + au8Cmd[3] = 0x00; + + return nu_qspi_send_then_recv(qspi, &au8Cmd[0], sizeof(au8Cmd), pu8Buff, u32Count); +} + +static rt_err_t spinand_protect_set(struct rt_qspi_device *qspi, uint8_t u8Protect) +{ + /* Read status register 1 */ + uint8_t u8SR = spinand_status_register_read(qspi, 1); + + if (u8Protect) + { + /* protect */ + u8SR |= 0x7C; + } + else + { + /* unprotect */ + u8SR &= 0x83; + } + + return spinand_status_register_write(qspi, 1, u8SR); +} + +static uint8_t spinand_program_erase_isfail(struct rt_qspi_device *qspi) +{ + /* Read status register 3 */ + uint8_t u8SR = spinand_status_register_read(qspi, 3); + return (u8SR & 0x0C) >> 2; /* Check P-Fail, E-Fail bit */ +} + +static uint8_t spinand_hwecc_status_get(struct rt_qspi_device *qspi) +{ + /* Read status register 3 */ + uint8_t u8SR = spinand_status_register_read(qspi, 3); + return (u8SR & 0x30) >> 4; /* ECC-1, ECC0 bit */ +} + +static rt_err_t spinand_hwecc_set(struct rt_qspi_device *qspi, uint8_t u8Enable) +{ + uint8_t u8SR = spinand_status_register_read(qspi, 2); // Read status register 2 + + if (u8Enable) + { + u8SR |= 0x10; // Enable ECC-E bit + } + else + { + u8SR &= 0xEF; // Disable ECC-E bit + } + + return spinand_status_register_write(qspi, 2, u8SR); +} + +static uint8_t spinand_hwecc_get(struct rt_qspi_device *qspi) +{ + /* Read status register 2 */ + uint8_t u8SR = spinand_status_register_read(qspi, 2); + return (u8SR & 0x10) >> 4; +} + +static rt_err_t spinand_read_dataload(struct rt_qspi_device *qspi, uint8_t u8Addr2, uint8_t u8Addr1, uint8_t u8Addr0) +{ + rt_err_t result = RT_EOK; + uint8_t au8Cmd[4]; + uint8_t u8SR; + + au8Cmd[0] = 0x13 ; + au8Cmd[1] = u8Addr2; + au8Cmd[2] = u8Addr1; + au8Cmd[3] = u8Addr0; + + if ((result = nu_qspi_send(qspi, &au8Cmd[0], sizeof(au8Cmd))) != RT_EOK) + goto exit_spinand_read_dataload; + + if (spinand_isbusy(qspi)) + { + result = -RT_EIO; + goto exit_spinand_read_dataload; + } + + u8SR = spinand_hwecc_status_get(SPINAND_FLASH_QSPI); + if ((u8SR != 0x00) && (u8SR != 0x01)) + { + result = -RT_MTD_EECC; + LOG_E("Error ECC status error[0x%x].", u8SR); + } + +exit_spinand_read_dataload: + + return result; +} + +static uint8_t spinand_block_isbad(struct rt_qspi_device *qspi, uint32_t u32PageAddr) +{ + rt_err_t result; + uint8_t read_buf; + +again_spinand_block_isbad: + + result = spinand_read_dataload(qspi, (u32PageAddr >> 16) & 0xFF, (u32PageAddr >> 8) & 0xFF, u32PageAddr & 0xFF); // Read the first page of a block + RT_ASSERT(result == RT_EOK); + + result = spinand_normal_read(qspi, (SPINAND_FLASH_PAGE_SIZE >> 8) & 0xff, SPINAND_FLASH_PAGE_SIZE & 0xff, &read_buf, 1); // Read bad block mark at 0x800 update at v.1.0.8 + RT_ASSERT(result == RT_EOK); + + if (read_buf != 0xFF) + { + // update at v.1.0.7 + return 1; + } + + if (((u32PageAddr % (SPINAND_FLASH_PAGE_PER_BLOCK_NUM * SPINAND_FLASH_PAGE_SIZE)) == 0)) + { + /* Need check second page again. */ + u32PageAddr++; + goto again_spinand_block_isbad; + } + + return 0; +} + +static rt_err_t spinand_buffermode_set(struct rt_qspi_device *qspi, uint8_t u8Enable) +{ + uint8_t u8SR = spinand_status_register_read(qspi, 2); // Read status register 2 + + if (u8Enable) + { + u8SR |= 0x08; // Enable BUF bit + } + else + { + u8SR &= 0xF7; // Disable BUF bit + } + return spinand_status_register_write(qspi, 2, u8SR); +} + +static rt_err_t spinand_block_erase(struct rt_qspi_device *qspi, uint8_t u8Addr2, uint8_t u8Addr1, uint8_t u8Addr0) +{ + rt_err_t result; + uint8_t u8WECmd = 0x06; + uint8_t au8EraseCmd[4], u8SR; + + au8EraseCmd[0] = 0xD8; + au8EraseCmd[1] = u8Addr2; + au8EraseCmd[2] = u8Addr1; + au8EraseCmd[3] = u8Addr0; + + if ((result = nu_qspi_send(qspi, &u8WECmd, sizeof(u8WECmd))) != RT_EOK) + goto exit_spinand_block_erase; + + if ((result = nu_qspi_send(qspi, &au8EraseCmd[0], sizeof(au8EraseCmd))) != RT_EOK) + goto exit_spinand_block_erase; + + if (spinand_isbusy(qspi)) + return -RT_EIO; + + u8SR = spinand_program_erase_isfail(SPINAND_FLASH_QSPI); + if (u8SR != 0) + { + /* Fail to erase */ + LOG_E("Fail to erase. Will mark it bad."); + result = -RT_ERROR; + goto exit_spinand_block_erase; + } + +exit_spinand_block_erase: + + return result; +} + +static rt_err_t spinand_block_markbad(struct rt_qspi_device *qspi, uint32_t u32PageAddr) +{ + rt_err_t result = RT_EOK; + + uint8_t u8BadBlockMarker = 0xF0; + + result = spinand_block_erase(qspi, (u32PageAddr >> 16) & 0xFF, (u32PageAddr >> 8) & 0xFF, u32PageAddr & 0xFF); + if (result != RT_EOK) + return result; + + result = spinand_program_dataload(qspi, (SPINAND_FLASH_PAGE_SIZE >> 8) & 0xff, SPINAND_FLASH_PAGE_SIZE & 0xff, &u8BadBlockMarker, 1, 0, 0); + if (result != RT_EOK) + return result; + + return spinand_program_execute(qspi, (u32PageAddr >> 16) & 0xFF, (u32PageAddr >> 8) & 0xFF, u32PageAddr & 0xFF); +} + +static rt_err_t spinand_read_quadoutput( + struct rt_qspi_device *qspi, + uint8_t u8AddrH, + uint8_t u8AddrL, + uint8_t *pu8DataBuff, + uint32_t u32DataCount +) +{ + struct rt_qspi_message qspi_messages = {0}; + + /* 1-bit mode */ + qspi_messages.instruction.content = SPINAND_FLASH_QUADREAD_CMDID; + qspi_messages.instruction.qspi_lines = 1; + + qspi_messages.address.content = (u8AddrH << 8) | (u8AddrL); + qspi_messages.address.size = 2 * 8; + qspi_messages.address.qspi_lines = 1; + + qspi_messages.dummy_cycles = SPINAND_FLASH_DUMMYBYTE * 8; //In bit + + /* 4-bit mode */ + qspi_messages.qspi_data_lines = 4; + qspi_messages.parent.cs_take = 1; + qspi_messages.parent.cs_release = 1; + qspi_messages.parent.recv_buf = pu8DataBuff; + qspi_messages.parent.length = u32DataCount; + qspi_messages.parent.next = RT_NULL; + + return nu_qspi_transfer_message(qspi, (struct rt_qspi_message *) &qspi_messages); +} + +static rt_err_t spinand_jedecid_get(struct rt_qspi_device *qspi, uint32_t *pu32ID) +{ + uint32_t u32JedecId = 0; + uint32_t u32JedecId_real = 0; + uint8_t u8Cmd = 0x9F; + + if (nu_qspi_send_then_recv(qspi, &u8Cmd, 1, &u32JedecId, 4) != RT_EOK) + { + return -RT_ERROR; + } + + /* Reverse order. */ + nu_set32_be((uint8_t *)&u32JedecId_real, u32JedecId); + + /* Only keep 3-bytes. */ + u32JedecId_real &= 0x00ffffff; + + *pu32ID = u32JedecId_real; + + return RT_EOK; +} + +static rt_err_t spinand_reset(struct rt_qspi_device *qspi) +{ + rt_err_t result; + uint8_t u8Cmd = 0xFF; + + if ((result = nu_qspi_send(qspi, &u8Cmd, 1)) != RT_EOK) + goto exit_spinand_reset; + + if (spinand_isbusy(qspi)) + { + result = RT_EIO; + goto exit_spinand_reset; + } + +exit_spinand_reset: + + return result; +} + +rt_err_t spinand_flash_init(struct rt_qspi_device *qspi) +{ + rt_err_t result; + + if ((result = spinand_reset(qspi)) != RT_EOK) + goto exit_spinand_init; + + if ((result = spinand_info_read(qspi)) != RT_EOK) + goto exit_spinand_init; + + /* Un-protect */ + if ((result = spinand_protect_set(qspi, 0)) != RT_EOK) + goto exit_spinand_init; + + /* Enable BUF mode */ + if ((result = spinand_buffermode_set(qspi, 1)) != RT_EOK) + goto exit_spinand_init; + + /* Enable HWECC */ + if ((result = spinand_hwecc_set(qspi, 1)) != RT_EOK) + goto exit_spinand_init; + + /* Check HWECC */ + if (!(spinand_hwecc_get(qspi))) + goto exit_spinand_init; + + if (SPINAND_FLASH_MCP == 1) + { + /* Select die. */ + if ((result = spinand_die_select(qspi, SPINAND_DIE_ID1)) != RT_EOK) + goto exit_spinand_init; + + /* Unprotect */ + if ((result = spinand_protect_set(qspi, 0)) != RT_EOK) + goto exit_spinand_init; + } + + LOG_I("Enabled BUF, HWECC. Unprotected."); + +exit_spinand_init: + + return -result; +} + +struct spinand_ops spinand_ops_wb = +{ + .block_erase = spinand_block_erase, + .block_isbad = spinand_block_isbad, + .block_markbad = spinand_block_markbad, + .die_select = spinand_die_select, + .jedecid_get = spinand_jedecid_get, + .program_dataload = spinand_program_dataload, + .program_execute = spinand_program_execute, + .read_dataload = spinand_read_dataload, + .read_quadoutput = spinand_read_quadoutput +}; + +static rt_err_t spinand_info_read(struct rt_qspi_device *qspi) +{ + int i; + uint32_t u32JedecId = 0; + + if (spinand_jedecid_get(qspi, &u32JedecId) != RT_EOK) + goto exit_spinand_info_read; + + for (i = 0 ; i < SPINAND_LIST_ELEMENT_NUM; i++) + { + if (u32JedecId == g_spinandflash_list[i].u32JEDECID) /* Match JEDECID? */ + { + rt_memcpy(SPINAND_FLASH_INFO, &g_spinandflash_list[i], sizeof(struct nu_spinand_info)); + LOG_I("Found: [%08X] %s.", u32JedecId, SPINAND_FLASH_DESCRIPTION); + + switch (u32JedecId & 0xff0000) + { + case 0xEF0000: /* Winbond */ + SPINAND_FLASH_OPS = &spinand_ops_wb; + break; + + default: + goto exit_spinand_info_read; + } + + return RT_EOK; + } + } + +exit_spinand_info_read: + + LOG_E("Can't find the flash[%08X] in supported list.", u32JedecId); + return -RT_ERROR; +} + +#endif diff --git a/bsp/nuvoton/libraries/nu_packages/SPINAND/spinand.h b/bsp/nuvoton/libraries/nu_packages/SPINAND/spinand.h new file mode 100644 index 0000000000..3e307d23ea --- /dev/null +++ b/bsp/nuvoton/libraries/nu_packages/SPINAND/spinand.h @@ -0,0 +1,95 @@ +/**************************************************************************//** +* +* @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved. +* +* SPDX-License-Identifier: Apache-2.0 +* +* Change Logs: +* Date Author Notes +* 2021-1-13 Wayne First version +* +******************************************************************************/ + +#ifndef __SPINAND_H__ +#define __SPINAND_H__ + +#include + +#include +#include "drv_spi.h" +#include + +/* SPI NAND flash information */ +struct nu_spinand_info +{ + uint32_t u32JEDECID; + uint16_t u16PageSize; + uint16_t u16OOBSize; + uint8_t u8QuadReadCmdId; + uint8_t u8ReadStatusCmdId; + uint8_t u8WriteStatusCmdid; + uint8_t u8StatusValue; + uint8_t u8DummyByte; + uint32_t u32BlockPerFlash; + uint32_t u32PagePerBlock; + uint8_t u8IsDieSelect; + const char *szDescription; +}; +typedef struct nu_spinand_info *nu_spinand_info_t; + +struct spinand_ops +{ + rt_err_t (*block_erase)(struct rt_qspi_device *qspi, uint8_t u8Addr2, uint8_t u8Addr1, uint8_t u8Addr0); + uint8_t (*block_isbad)(struct rt_qspi_device *qspi, uint32_t u32PageAddr); + rt_err_t (*block_markbad)(struct rt_qspi_device *qspi, uint32_t u32PageAddr); + rt_err_t (*die_select)(struct rt_qspi_device *qspi, uint8_t select_die); + rt_err_t (*jedecid_get)(struct rt_qspi_device *qspi, uint32_t *pu32ID); + rt_err_t (*program_dataload)(struct rt_qspi_device *qspi, uint8_t u8AddrH, uint8_t u8AddrL, uint8_t *pu8DataBuff, + uint32_t u32DataCount, uint8_t *pu8SpareBuff, uint32_t u32SpareCount); + + rt_err_t (*program_execute)(struct rt_qspi_device *qspi, uint8_t u8Addr2, uint8_t u8Addr1, uint8_t u8Addr0); + rt_err_t (*read_dataload)(struct rt_qspi_device *qspi, uint8_t u8Addr2, uint8_t u8Addr1, uint8_t u8Addr0); + rt_err_t (*read_quadoutput)(struct rt_qspi_device *qspi, uint8_t u8AddrH, uint8_t u8AddrL, uint8_t *pu8DataBuff, uint32_t u32DataCount); +}; +typedef struct spinand_ops *nu_spinand_ops_t; + +struct nu_spinand +{ + struct nu_spinand_info info; + struct rt_qspi_device *qspi_device; + nu_spinand_ops_t ops; + struct rt_mutex lock; +}; +typedef struct nu_spinand *nu_spinand_t; + +#define SPINAND_FLASH_JEDECID g_spinandflash_dev.info.u32JEDECID +#define SPINAND_FLASH_PAGE_SIZE g_spinandflash_dev.info.u16PageSize +#define SPINAND_FLASH_OOB_SIZE g_spinandflash_dev.info.u16OOBSize +#define SPINAND_FLASH_QUADREAD_CMDID g_spinandflash_dev.info.u8QuadReadCmdId +#define SPINAND_FLASH_DUMMYBYTE g_spinandflash_dev.info.u8DummyByte +#define SPINAND_FLASH_BLOCK_NUM g_spinandflash_dev.info.u32BlockPerFlash +#define SPINAND_FLASH_PAGE_PER_BLOCK_NUM g_spinandflash_dev.info.u32PagePerBlock +#define SPINAND_FLASH_DESCRIPTION g_spinandflash_dev.info.szDescription +#define SPINAND_FLASH_MCP g_spinandflash_dev.info.u8IsDieSelect + +#define SPINAND_FLASH_INFO &g_spinandflash_dev.info +#define SPINAND_FLASH_QSPI g_spinandflash_dev.qspi_device +#define SPINAND_FLASH_LOCK &g_spinandflash_dev.lock +#define SPINAND_FLASH_OPS g_spinandflash_dev.ops + +#define SPINAND_DIE_ID0 (0) +#define SPINAND_DIE_ID1 (1) + +#define SPINAND_SPARE_LAYOUT_SIZE 16 + +rt_err_t rt_hw_mtd_spinand_register(const char *device_name); +rt_size_t nu_qspi_transfer_message(struct rt_qspi_device *device, struct rt_qspi_message *message); +rt_err_t nu_qspi_send_then_recv(struct rt_qspi_device *device, const void *send_buf, rt_size_t send_length, void *recv_buf, rt_size_t recv_length); +rt_err_t nu_qspi_send(struct rt_qspi_device *device, const void *send_buf, rt_size_t length); +rt_err_t spinand_flash_init(struct rt_qspi_device *qspi); + +extern struct nu_spinand g_spinandflash_dev; +extern rt_uint8_t spinand_flash_data_layout[SPINAND_SPARE_LAYOUT_SIZE]; +extern rt_uint8_t spinand_flash_ecc_layout[SPINAND_SPARE_LAYOUT_SIZE]; + +#endif /* __SPINAND_H__ */ diff --git a/bsp/nuvoton/libraries/nuc980/Driver/Include/nu_can.h b/bsp/nuvoton/libraries/nuc980/Driver/Include/nu_can.h index ee60db99e7..5ac83bf1cb 100644 --- a/bsp/nuvoton/libraries/nuc980/Driver/Include/nu_can.h +++ b/bsp/nuvoton/libraries/nuc980/Driver/Include/nu_can.h @@ -46,45 +46,45 @@ typedef struct { - uint32_t CREQ; /*!< [0x0020] IFn Command Request Register */ - uint32_t CMASK; /*!< [0x0024] IFn Command Mask Register */ - uint32_t MASK1; /*!< [0x0028] IFn Mask 1 Register */ - uint32_t MASK2; /*!< [0x002c] IFn Mask 2 Register */ - uint32_t ARB1; /*!< [0x0030] IFn Arbitration 1 Register */ - uint32_t ARB2; /*!< [0x0034] IFn Arbitration 2 Register */ - uint32_t MCON; /*!< [0x0038] IFn Message Control Register */ - uint32_t DAT_A1; /*!< [0x003c] IFn Data A1 Register */ - uint32_t DAT_A2; /*!< [0x0040] IFn Data A2 Register */ - uint32_t DAT_B1; /*!< [0x0044] IFn Data B1 Register */ - uint32_t DAT_B2; /*!< [0x0048] IFn Data B2 Register */ - uint32_t RESERVE0[13]; + __IO uint32_t CREQ; /*!< [0x0020] IFn Command Request Register */ + __IO uint32_t CMASK; /*!< [0x0024] IFn Command Mask Register */ + __IO uint32_t MASK1; /*!< [0x0028] IFn Mask 1 Register */ + __IO uint32_t MASK2; /*!< [0x002c] IFn Mask 2 Register */ + __IO uint32_t ARB1; /*!< [0x0030] IFn Arbitration 1 Register */ + __IO uint32_t ARB2; /*!< [0x0034] IFn Arbitration 2 Register */ + __IO uint32_t MCON; /*!< [0x0038] IFn Message Control Register */ + __IO uint32_t DAT_A1; /*!< [0x003c] IFn Data A1 Register */ + __IO uint32_t DAT_A2; /*!< [0x0040] IFn Data A2 Register */ + __IO uint32_t DAT_B1; /*!< [0x0044] IFn Data B1 Register */ + __IO uint32_t DAT_B2; /*!< [0x0048] IFn Data B2 Register */ + __I uint32_t RESERVE0[13]; } CAN_IF_T; typedef struct { - uint32_t CON; /*!< [0x0000] Control Register */ - uint32_t STATUS; /*!< [0x0004] Status Register */ - uint32_t ERR; /*!< [0x0008] Error Counter Register */ - uint32_t BTIME; /*!< [0x000c] Bit Timing Register */ - uint32_t IIDR; /*!< [0x0010] Interrupt Identifier Register */ - uint32_t TEST; /*!< [0x0014] Test Register */ - uint32_t BRPE; /*!< [0x0018] Baud Rate Prescaler Extension Register */ - uint32_t RESERVE0[1]; - CAN_IF_T IF[2]; - uint32_t RESERVE2[8]; - uint32_t TXREQ1; /*!< [0x0100] Transmission Request Register 1 */ - uint32_t TXREQ2; /*!< [0x0104] Transmission Request Register 2 */ - uint32_t RESERVE3[6]; - uint32_t NDAT1; /*!< [0x0120] New Data Register 1 */ - uint32_t NDAT2; /*!< [0x0124] New Data Register 2 */ - uint32_t RESERVE4[6]; - uint32_t IPND1; /*!< [0x0140] Interrupt Pending Register 1 */ - uint32_t IPND2; /*!< [0x0144] Interrupt Pending Register 2 */ - uint32_t RESERVE5[6]; - uint32_t MVLD1; /*!< [0x0160] Message Valid Register 1 */ - uint32_t MVLD2; /*!< [0x0164] Message Valid Register 2 */ - uint32_t WU_EN; /*!< [0x0168] Wake-up Enable Control Register */ - uint32_t WU_STATUS; /*!< [0x016c] Wake-up Status Register */ + __IO uint32_t CON; /*!< [0x0000] Control Register */ + __IO uint32_t STATUS; /*!< [0x0004] Status Register */ + __I uint32_t ERR; /*!< [0x0008] Error Counter Register */ + __IO uint32_t BTIME; /*!< [0x000c] Bit Timing Register */ + __I uint32_t IIDR; /*!< [0x0010] Interrupt Identifier Register */ + __IO uint32_t TEST; /*!< [0x0014] Test Register */ + __IO uint32_t BRPE; /*!< [0x0018] Baud Rate Prescaler Extension Register */ + __I uint32_t RESERVE0[1]; + __IO CAN_IF_T IF[2]; + __I uint32_t RESERVE2[8]; + __I uint32_t TXREQ1; /*!< [0x0100] Transmission Request Register 1 */ + __I uint32_t TXREQ2; /*!< [0x0104] Transmission Request Register 2 */ + __I uint32_t RESERVE3[6]; + __I uint32_t NDAT1; /*!< [0x0120] New Data Register 1 */ + __I uint32_t NDAT2; /*!< [0x0124] New Data Register 2 */ + __I uint32_t RESERVE4[6]; + __I uint32_t IPND1; /*!< [0x0140] Interrupt Pending Register 1 */ + __I uint32_t IPND2; /*!< [0x0144] Interrupt Pending Register 2 */ + __I uint32_t RESERVE5[6]; + __I uint32_t MVLD1; /*!< [0x0160] Message Valid Register 1 */ + __I uint32_t MVLD2; /*!< [0x0164] Message Valid Register 2 */ + __IO uint32_t WU_EN; /*!< [0x0168] Wake-up Enable Control Register */ + __IO uint32_t WU_STATUS; /*!< [0x016c] Wake-up Status Register */ } CAN_T; diff --git a/bsp/nuvoton/libraries/nuc980/Driver/Include/nu_etimer.h b/bsp/nuvoton/libraries/nuc980/Driver/Include/nu_etimer.h index d23e0321a1..445f02f53d 100644 --- a/bsp/nuvoton/libraries/nuc980/Driver/Include/nu_etimer.h +++ b/bsp/nuvoton/libraries/nuc980/Driver/Include/nu_etimer.h @@ -647,6 +647,42 @@ static __inline void ETIMER_ClearCaptureIntFlag(UINT timer) } } +/** +* @brief This function gets the Timer capture falling edge flag. +* @param[in] timer ETIMER number. Range from 0 ~ 5 +* @return None +*/ +static __inline UINT8 ETIMER_GetCaptureFallingEdgeFlag(UINT timer) +{ + UINT ret; + + if (timer == 0) + { + ret = inpw(REG_ETMR0_ISR); + } + else if (timer == 1) + { + ret = inpw(REG_ETMR1_ISR); + } + else if (timer == 2) + { + ret = inpw(REG_ETMR2_ISR); + } + else if (timer == 3) + { + ret = inpw(REG_ETMR3_ISR); + } + else if (timer == 4) + { + ret = inpw(REG_ETMR4_ISR); + } + else + { + ret = inpw(REG_ETMR5_ISR); + } + return (ret & (1 << 6)) >> 6; +} + /** * @brief This function indicates Timer has waked up system or not. * @param[in] timer ETIMER number. Range from 0 ~ 5 diff --git a/bsp/nuvoton/libraries/nuc980/Driver/Include/nu_rtc.h b/bsp/nuvoton/libraries/nuc980/Driver/Include/nu_rtc.h index a67820b0ef..5766422979 100644 --- a/bsp/nuvoton/libraries/nuc980/Driver/Include/nu_rtc.h +++ b/bsp/nuvoton/libraries/nuc980/Driver/Include/nu_rtc.h @@ -110,23 +110,23 @@ typedef struct typedef struct { - uint32_t INIT; /*!< [0x0000] RTC Initiation Register */ - uint32_t RWEN; /*!< [0x0004] RTC Access Enable Register */ - uint32_t FREQADJ; /*!< [0x0008] RTC Frequency Compensation Register */ - uint32_t TIME; /*!< [0x000c] RTC Time Loading Register */ - uint32_t CAL; /*!< [0x0010] RTC Calendar Loading Register */ - uint32_t CLKFMT; /*!< [0x0014] RTC Time Scale Selection Register */ - uint32_t WEEKDAY; /*!< [0x0018] RTC Day of the Week Register */ - uint32_t TALM; /*!< [0x001c] RTC Time Alarm Register */ - uint32_t CALM; /*!< [0x0020] RTC Calendar Alarm Register */ - uint32_t LEAPYEAR; /*!< [0x0024] RTC Leap Year Indicator Register */ - uint32_t INTEN; /*!< [0x0028] RTC Interrupt Enable Register */ - uint32_t INTSTS; /*!< [0x002c] RTC Interrupt Status Register */ - uint32_t TICK; /*!< [0x0030] RTC Time Tick Register */ - uint32_t PWRCTL; /*!< [0x0034] RTC Power Control Register */ - uint32_t PWRCNT; /*!< [0x0038] RTC Power Control Counter Register */ - uint32_t RESERVE0; /*!< [0x003c] RTC Spare Functional Control Register */ - uint32_t SPR[16]; /*!< [0x0040] ~ [0x007c] RTC Spare Register 0 ~ 15 */ + __IO uint32_t INIT; /*!< [0x0000] RTC Initiation Register */ + __IO uint32_t RWEN; /*!< [0x0004] RTC Access Enable Register */ + __IO uint32_t FREQADJ; /*!< [0x0008] RTC Frequency Compensation Register */ + __IO uint32_t TIME; /*!< [0x000c] RTC Time Loading Register */ + __IO uint32_t CAL; /*!< [0x0010] RTC Calendar Loading Register */ + __IO uint32_t CLKFMT; /*!< [0x0014] RTC Time Scale Selection Register */ + __IO uint32_t WEEKDAY; /*!< [0x0018] RTC Day of the Week Register */ + __IO uint32_t TALM; /*!< [0x001c] RTC Time Alarm Register */ + __IO uint32_t CALM; /*!< [0x0020] RTC Calendar Alarm Register */ + __I uint32_t LEAPYEAR; /*!< [0x0024] RTC Leap Year Indicator Register */ + __IO uint32_t INTEN; /*!< [0x0028] RTC Interrupt Enable Register */ + __IO uint32_t INTSTS; /*!< [0x002c] RTC Interrupt Status Register */ + __IO uint32_t TICK; /*!< [0x0030] RTC Time Tick Register */ + __IO uint32_t PWRCTL; /*!< [0x0034] RTC Power Control Register */ + __IO uint32_t PWRCNT; /*!< [0x0038] RTC Power Control Counter Register */ + __IO uint32_t RESERVE0; /*!< [0x003c] RTC Spare Functional Control Register */ + __I uint32_t SPR[16]; /*!< [0x0040] ~ [0x007c] RTC Spare Register 0 ~ 15 */ } RTC_T; #define RTC_INIT_ACTIVE_Pos (0) /*!< RTC_T::INIT: INIT_ACTIVE Position */ diff --git a/bsp/nuvoton/libraries/nuc980/Driver/Include/nu_uart.h b/bsp/nuvoton/libraries/nuc980/Driver/Include/nu_uart.h index c367a52a16..8cd49f68d8 100644 --- a/bsp/nuvoton/libraries/nuc980/Driver/Include/nu_uart.h +++ b/bsp/nuvoton/libraries/nuc980/Driver/Include/nu_uart.h @@ -116,25 +116,25 @@ typedef struct { - uint32_t DAT; /*!< [0x0000] UART Receive/Transmit Buffer Register */ - uint32_t INTEN; /*!< [0x0004] UART Interrupt Enable Register */ - uint32_t FIFO; /*!< [0x0008] UART FIFO Control Register */ - uint32_t LINE; /*!< [0x000c] UART Line Control Register */ - uint32_t MODEM; /*!< [0x0010] UART Modem Control Register */ - uint32_t MODEMSTS; /*!< [0x0014] UART Modem Status Register */ - uint32_t FIFOSTS; /*!< [0x0018] UART FIFO Status Register */ - uint32_t INTSTS; /*!< [0x001c] UART Interrupt Status Register */ - uint32_t TOUT; /*!< [0x0020] UART Time-out Register */ - uint32_t BAUD; /*!< [0x0024] UART Baud Rate Divider Register */ - uint32_t IRDA; /*!< [0x0028] UART IrDA Control Register */ - uint32_t ALTCTL; /*!< [0x002c] UART Alternate Control/Status Register */ - uint32_t FUNCSEL; /*!< [0x0030] UART Function Select Register */ - uint32_t LINCTL; /*!< [0x0034] UART LIN Control Register */ - uint32_t LINSTS; /*!< [0x0038] UART LIN Status Register */ - uint32_t BRCOMP; /*!< [0x003c] UART Baud Rate Compensation Register */ - uint32_t WKCTL; /*!< [0x0040] UART Wake-up Control Register */ - uint32_t WKSTS; /*!< [0x0044] UART Wake-up Status Register */ - uint32_t DWKCOMP; /*!< [0x0048] UART Incoming Data Wake-up Compensation Register */ + __IO uint32_t DAT; /*!< [0x0000] UART Receive/Transmit Buffer Register */ + __IO uint32_t INTEN; /*!< [0x0004] UART Interrupt Enable Register */ + __IO uint32_t FIFO; /*!< [0x0008] UART FIFO Control Register */ + __IO uint32_t LINE; /*!< [0x000c] UART Line Control Register */ + __IO uint32_t MODEM; /*!< [0x0010] UART Modem Control Register */ + __IO uint32_t MODEMSTS; /*!< [0x0014] UART Modem Status Register */ + __IO uint32_t FIFOSTS; /*!< [0x0018] UART FIFO Status Register */ + __IO uint32_t INTSTS; /*!< [0x001c] UART Interrupt Status Register */ + __IO uint32_t TOUT; /*!< [0x0020] UART Time-out Register */ + __IO uint32_t BAUD; /*!< [0x0024] UART Baud Rate Divider Register */ + __IO uint32_t IRDA; /*!< [0x0028] UART IrDA Control Register */ + __IO uint32_t ALTCTL; /*!< [0x002c] UART Alternate Control/Status Register */ + __IO uint32_t FUNCSEL; /*!< [0x0030] UART Function Select Register */ + __IO uint32_t LINCTL; /*!< [0x0034] UART LIN Control Register */ + __IO uint32_t LINSTS; /*!< [0x0038] UART LIN Status Register */ + __IO uint32_t BRCOMP; /*!< [0x003c] UART Baud Rate Compensation Register */ + __IO uint32_t WKCTL; /*!< [0x0040] UART Wake-up Control Register */ + __IO uint32_t WKSTS; /*!< [0x0044] UART Wake-up Status Register */ + __IO uint32_t DWKCOMP; /*!< [0x0048] UART Incoming Data Wake-up Compensation Register */ } UART_T; diff --git a/bsp/nuvoton/libraries/nuc980/Driver/SConscript b/bsp/nuvoton/libraries/nuc980/Driver/SConscript index e3ac575c7c..dfbcad6349 100644 --- a/bsp/nuvoton/libraries/nuc980/Driver/SConscript +++ b/bsp/nuvoton/libraries/nuc980/Driver/SConscript @@ -18,9 +18,9 @@ if not GetDepend('BSP_USE_STDDRIVER_SOURCE'): libs += ['libstddriver_gcc'] if not libs: - group = DefineGroup('nuc980_driver', src, depend = [''], CPPPATH = cpppath) + group = DefineGroup('Libraries', src, depend = [''], CPPPATH = cpppath) else: src = [] - group = DefineGroup('nuc980_driver', src, depend = [''], CPPPATH = cpppath, LIBS = libs, LIBPATH = libpath) + group = DefineGroup('Libraries', src, depend = [''], CPPPATH = cpppath, LIBS = libs, LIBPATH = libpath) Return('group') diff --git a/bsp/nuvoton/libraries/nuc980/README.md b/bsp/nuvoton/libraries/nuc980/README.md index 60eb3b538c..587fa27f05 100644 --- a/bsp/nuvoton/libraries/nuc980/README.md +++ b/bsp/nuvoton/libraries/nuc980/README.md @@ -16,6 +16,7 @@ | QSPI | RT_Device_Class_SPIBUS | ***qspi[0]*** | | RTC | RT_Device_Class_RTC | ***rtc*** | | PWM | RT_Device_Class_Miscellaneous (PWM) | ***pwm[0-1]*** | +| USBH | RT_Device_Class_USBHost | ***usbh*** | | USBD | RT_Device_Class_USBDevice | ***usbd*** | | SC (UART function) | RT_Device_Class_Char | ***scuart[0-1]*** | | SDH | RT_Device_Class_Block | ***sdh[0-1]*** | diff --git a/bsp/nuvoton/libraries/nuc980/UsbHostLib/SConscript b/bsp/nuvoton/libraries/nuc980/UsbHostLib/SConscript new file mode 100644 index 0000000000..c480e1d099 --- /dev/null +++ b/bsp/nuvoton/libraries/nuc980/UsbHostLib/SConscript @@ -0,0 +1,12 @@ +# RT-Thread building script for component + +from building import * + +cwd = GetCurrentDir() +group = [] +if GetDepend('BSP_USING_HSUSBH') or GetDepend('BSP_USING_USBH'): + src = Glob('*src/*.c') + Glob('src/*.cpp') + CPPPATH = [cwd + '/inc'] + group = DefineGroup('nuc980_usbhostlib', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/nuvoton/libraries/nuc980/UsbHostLib/inc/config.h b/bsp/nuvoton/libraries/nuc980/UsbHostLib/inc/config.h new file mode 100644 index 0000000000..690ace9952 --- /dev/null +++ b/bsp/nuvoton/libraries/nuc980/UsbHostLib/inc/config.h @@ -0,0 +1,1566 @@ +/**************************************************************************//** + * @file config.h + * @version V1.00 + * @brief This header file defines the configuration of USB Host library. + * @note + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2019 Nuvoton Technology Corp. All rights reserved. + *****************************************************************************/ + +#ifndef _USBH_CONFIG_H_ +#define _USBH_CONFIG_H_ + +/// @cond HIDDEN_SYMBOLS + +#include +#include "nuc980.h" +#include "nu_sys.h" +#include "drv_sys.h" + + +/*----------------------------------------------------------------------------------------*/ +/* Hardware settings */ +/*----------------------------------------------------------------------------------------*/ +#define HCLK_MHZ 300 /* used for loop-delay. must be larger than + true HCLK clock MHz */ + +#define NON_CACHE_MASK (0x80000000) + +#define ENABLE_OHCI_IRQ() rt_hw_interrupt_umask(IRQ_OHCI) +#define DISABLE_OHCI_IRQ() rt_hw_interrupt_mask(IRQ_OHCI) +#define IS_OHCI_IRQ_ENABLED() ((inpw(REG_AIC_INTMSK0)>>IRQ_OHCI) & 0x1) +#define ENABLE_EHCI_IRQ() rt_hw_interrupt_umask(IRQ_EHCI) +#define DISABLE_EHCI_IRQ() rt_hw_interrupt_mask(IRQ_EHCI) +#define IS_EHCI_IRQ_ENABLED() ((inpw(REG_AIC_INTMSK0)>>IRQ_EHCI) & 0x1) + +//#define ENABLE_OHCI /* Enable OHCI host controller */ +#define ENABLE_EHCI /* Enable EHCI host controller */ + +#define EHCI_PORT_CNT 2 /* Number of EHCI roothub ports */ +#define OHCI_PORT_CNT 8 /* Number of OHCI roothub ports */ +//#define OHCI_PER_PORT_POWER /* OHCI root hub per port powered */ + +#define OHCI_ISO_DELAY 4 /* preserved number frames while scheduling + OHCI isochronous transfer */ + +#define EHCI_ISO_DELAY 2 /* preserved number of frames while + scheduling EHCI isochronous transfer */ + +#define EHCI_ISO_RCLM_RANGE 32 /* When inspecting activated iTD/siTD, + unconditionally reclaim iTD/isTD scheduled + in just elapsed EHCI_ISO_RCLM_RANGE ms. */ + +#define MAX_DESC_BUFF_SIZE 1024 /* To hold the configuration descriptor, USB + core will allocate a buffer with this size + for each connected device. USB core does + not release it until device disconnected. */ + +/*----------------------------------------------------------------------------------------*/ +/* Memory allocation settings */ +/*----------------------------------------------------------------------------------------*/ + +#define STATIC_MEMORY_ALLOC 0 /* pre-allocate static memory blocks. No dynamic memory aloocation. + But the maximum number of connected devices and transfers are + limited. */ + +#define MAX_UDEV_DRIVER 8 /*!< Maximum number of registered drivers */ +#define MAX_ALT_PER_IFACE 8 /*!< maximum number of alternative interfaces per interface */ +#define MAX_EP_PER_IFACE 8 /*!< maximum number of endpoints per interface */ +#define MAX_HUB_DEVICE 8 /*!< Maximum number of hub devices */ + +/* Host controller hardware transfer descriptors memory pool. ED/TD/ITD of OHCI and QH/QTD of EHCI + are all allocated from this pool. Allocated unit size is determined by MEM_POOL_UNIT_SIZE. + May allocate one or more units depend on hardware descriptor type. */ + +#define MEM_POOL_UNIT_SIZE 128 /*!< A fixed hard coding setting. Do not change it! */ +#define MEM_POOL_UNIT_NUM 256 /*!< Increase this or heap size if memory allocate failed. */ + +/*----------------------------------------------------------------------------------------*/ +/* Re-defined staff for various compiler */ +/*----------------------------------------------------------------------------------------*/ +#ifdef __ICCARM__ + #define __inline inline +#endif + + +/*----------------------------------------------------------------------------------------*/ +/* Debug settings */ +/*----------------------------------------------------------------------------------------*/ +#define ENABLE_ERROR_MSG /* enable debug messages */ +//#define ENABLE_DEBUG_MSG /* enable debug messages */ +//#define ENABLE_VERBOSE_DEBUG /* verbos debug messages */ +//#define DUMP_DESCRIPTOR /* dump descriptors */ + +#ifdef ENABLE_ERROR_MSG + #define USB_error rt_kprintf +#else + #define USB_error(...) +#endif + +#ifdef ENABLE_DEBUG_MSG + #define USB_debug rt_kprintf + #ifdef ENABLE_VERBOSE_DEBUG + #define USB_vdebug rt_kprintf + #else + #define USB_vdebug(...) + #endif +#else + #define USB_debug(...) + #define USB_vdebug(...) +#endif + + +#define __I volatile const /*!< Defines 'read only' permissions */ +#define __IO volatile /*!< Defines 'read / write' permissions */ + + +//typedef unsigned int uint32_t; +//typedef unsigned short uint16_t; +//typedef unsigned char uint8_t; + + + +/*---------------------- USB Host Controller -------------------------*/ +/** + @addtogroup USBH USB Host Controller(USBH) + Memory Mapped Structure for USBH Controller +@{ */ + +typedef struct +{ + + /** + * @var USBH_T::HcRevision + * Offset: 0x00 Host Controller Revision Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[7:0] |REV |Revision Number + * | | |Indicates the Open HCI Specification revision number implemented by the Hardware + * | | |Host Controller supports 1.1 specification. + * | | |(X.Y = XYh). + * @var USBH_T::HcControl + * Offset: 0x04 Host Controller Control Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[1:0] |CBSR |Control Bulk Service Ratio + * | | |This specifies the service ratio between Control and Bulk EDs + * | | |Before processing any of the non-periodic lists, HC must compare the ratio specified with its internal count on how many nonempty Control EDs have been processed, in determining whether to continue serving another Control ED or switching to Bulk EDs + * | | |The internal count will be retained when crossing the frame boundary + * | | |In case of reset, HCD is responsible for restoring this + * | | |Value. + * | | |00 = Number of Control EDs over Bulk EDs served is 1:1. + * | | |01 = Number of Control EDs over Bulk EDs served is 2:1. + * | | |10 = Number of Control EDs over Bulk EDs served is 3:1. + * | | |11 = Number of Control EDs over Bulk EDs served is 4:1. + * |[2] |PLE |Periodic List Enable Bit + * | | |When set, this bit enables processing of the Periodic (interrupt and isochronous) list + * | | |The Host Controller checks this bit prior to attempting any periodic transfers in a frame. + * | | |0 = Processing of the Periodic (Interrupt and Isochronous) list after next SOF (Start-Of-Frame) Disabled. + * | | |1 = Processing of the Periodic (Interrupt and Isochronous) list in the next frame Enabled. + * | | |Note: To enable the processing of the Isochronous list, user has to set both PLE and IE (HcControl[3]) high. + * |[3] |IE |Isochronous List Enable Bit + * | | |Both ISOEn and PLE (HcControl[2]) high enables Host Controller to process the Isochronous list + * | | |Either ISOEn or PLE (HcControl[2]) is low disables Host Controller to process the Isochronous list. + * | | |0 = Processing of the Isochronous list after next SOF (Start-Of-Frame) Disabled. + * | | |1 = Processing of the Isochronous list in the next frame Enabled, if the PLE (HcControl[2]) is high, too. + * |[4] |CLE |Control List Enable Bit + * | | |0 = Processing of the Control list after next SOF (Start-Of-Frame) Disabled. + * | | |1 = Processing of the Control list in the next frame Enabled. + * |[5] |BLE |Bulk List Enable Bit + * | | |0 = Processing of the Bulk list after next SOF (Start-Of-Frame) Disabled. + * | | |1 = Processing of the Bulk list in the next frame Enabled. + * |[7:6] |HCFS |Host Controller Functional State + * | | |This field sets the Host Controller state + * | | |The Controller may force a state change from USBSUSPEND to USBRESUME after detecting resume signaling from a downstream port + * | | |States are: + * | | |00 = USBSUSPEND. + * | | |01 = USBOPERATIONAL. + * | | |10 = USBRESUME. + * | | |11 = USBRESET. + * @var USBH_T::HcCommandStatus + * Offset: 0x08 Host Controller Command Status Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[0] |HCR |Host Controller Reset + * | | |This bit is set to initiate the software reset of Host Controller + * | | |This bit is cleared by the Host Controller, upon completed of the reset operation. + * | | |This bit, when set, didn't reset the Root Hub and no subsequent reset signaling be asserted to its downstream ports. + * | | |0 = Host Controller is not in software reset state. + * | | |1 = Host Controller is in software reset state. + * |[1] |CLF |Control List Filled + * | | |Set high to indicate there is an active TD on the Control List + * | | |It may be set by either software or the Host Controller and cleared by the Host Controller each time it begins processing the head of the Control List. + * | | |0 = No active TD found or Host Controller begins to process the head of the Control list. + * | | |1 = An active TD added or found on the Control list. + * |[2] |BLF |Bulk List Filled + * | | |Set high to indicate there is an active TD on the Bulk list + * | | |This bit may be set by either software or the Host Controller and cleared by the Host Controller each time it begins processing the head of the Bulk list. + * | | |0 = No active TD found or Host Controller begins to process the head of the Bulk list. + * | | |1 = An active TD added or found on the Bulk list. + * |[17:16] |SOC |Schedule Overrun Count + * | | |These bits are incremented on each scheduling overrun error + * | | |It is initialized to 00b and wraps around at 11b + * | | |This will be incremented when a scheduling overrun is detected even if SO (HcInterruptStatus[0]) has already been set. + * @var USBH_T::HcInterruptStatus + * Offset: 0x0C Host Controller Interrupt Status Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[0] |SO |Scheduling Overrun + * | | |Set when the List Processor determines a Schedule Overrun has occurred. + * | | |0 = Schedule Overrun didn't occur. + * | | |1 = Schedule Overrun has occurred. + * |[1] |WDH |Write Back Done Head + * | | |Set after the Host Controller has written HcDoneHead to HccaDoneHead + * | | |Further updates of the HccaDoneHead will not occur until this bit has been cleared. + * | | |0 =.Host Controller didn't update HccaDoneHead. + * | | |1 =.Host Controller has written HcDoneHead to HccaDoneHead. + * |[2] |SF |Start of Frame + * | | |Set when the Frame Management functional block signals a 'Start of Frame' event + * | | |Host Control generates a SOF token at the same time. + * | | |0 =.Not the start of a frame. + * | | |1 =.Indicate the start of a frame and Host Controller generates a SOF token. + * |[3] |RD |Resume Detected + * | | |Set when Host Controller detects resume signaling on a downstream port. + * | | |0 = No resume signaling detected on a downstream port. + * | | |1 = Resume signaling detected on a downstream port. + * |[5] |FNO |Frame Number Overflow + * | | |This bit is set when bit 15 of Frame Number changes from 1 to 0 or from 0 to 1. + * | | |0 = The bit 15 of Frame Number didn't change. + * | | |1 = The bit 15 of Frame Number changes from 1 to 0 or from 0 to 1. + * |[6] |RHSC |Root Hub Status Change + * | | |This bit is set when the content of HcRhStatus or the content of HcRhPortStatus register has changed. + * | | |0 = The content of HcRhStatus and the content of HcRhPortStatus register didn't change. + * | | |1 = The content of HcRhStatus or the content of HcRhPortStatus register has changed. + * @var USBH_T::HcInterruptEnable + * Offset: 0x10 Host Controller Interrupt Enable Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[0] |SO |Scheduling Overrun Enable Bit + * | | |Write Operation: + * | | |0 = No effect. + * | | |1 = Interrupt generation due to SO (HcInterruptStatus[0]) Enabled. + * | | |Read Operation: + * | | |0 = Interrupt generation due to SO (HcInterruptStatus[0]) Disabled. + * | | |1 = Interrupt generation due to SO (HcInterruptStatus[0]) Enabled. + * |[1] |WDH |Write Back Done Head Enable Bit + * | | |Write Operation: + * | | |0 = No effect. + * | | |1 = Interrupt generation due to WDH (HcInterruptStatus[1]) Enabled. + * | | |Read Operation: + * | | |0 = Interrupt generation due to WDH (HcInterruptStatus[1]) Disabled. + * | | |1 = Interrupt generation due to WDH (HcInterruptStatus[1]) Enabled. + * |[2] |SF |Start of Frame Enable Bit + * | | |Write Operation: + * | | |0 = No effect. + * | | |1 = Interrupt generation due to SF (HcInterruptStatus[2]) Enabled. + * | | |Read Operation: + * | | |0 = Interrupt generation due to SF (HcInterruptStatus[2]) Disabled. + * | | |1 = Interrupt generation due to SF (HcInterruptStatus[2]) Enabled. + * |[3] |RD |Resume Detected Enable Bit + * | | |Write Operation: + * | | |0 = No effect. + * | | |1 = Interrupt generation due to RD (HcInterruptStatus[3]) Enabled. + * | | |Read Operation: + * | | |0 = Interrupt generation due to RD (HcInterruptStatus[3]) Disabled. + * | | |1 = Interrupt generation due to RD (HcInterruptStatus[3]) Enabled. + * |[5] |FNO |Frame Number Overflow Enable Bit + * | | |Write Operation: + * | | |0 = No effect. + * | | |1 = Interrupt generation due to FNO (HcInterruptStatus[5]) Enabled. + * | | |Read Operation: + * | | |0 = Interrupt generation due to FNO (HcInterruptStatus[5]) Disabled. + * | | |1 = Interrupt generation due to FNO (HcInterruptStatus[5]) Enabled. + * |[6] |RHSC |Root Hub Status Change Enable Bit + * | | |Write Operation: + * | | |0 = No effect. + * | | |1 = Interrupt generation due to RHSC (HcInterruptStatus[6]) Enabled. + * | | |Read Operation: + * | | |0 = Interrupt generation due to RHSC (HcInterruptStatus[6]) Disabled. + * | | |1 = Interrupt generation due to RHSC (HcInterruptStatus[6]) Enabled. + * |[31] |MIE |Master Interrupt Enable Bit + * | | |This bit is a global interrupt enable + * | | |A write of '1' allows interrupts to be enabled via the specific enable bits listed above. + * | | |Write Operation: + * | | |0 = No effect. + * | | |1 = Interrupt generation due to RHSC (HcInterruptStatus[6]), FNO (HcInterruptStatus[5]), RD (HcInterruptStatus[3]), SF (HcInterruptStatus[2]), WDH (HcInterruptStatus[1]) or SO (HcInterruptStatus[0]) Enabled if the corresponding bit in HcInterruptEnable is high. + * | | |Read Operation: + * | | |0 = Interrupt generation due to RHSC (HcInterruptStatus[6]), FNO (HcInterruptStatus[5]), RD (HcInterruptStatus[3]), SF (HcInterruptStatus[2]), WDH (HcInterruptStatus[1]) or SO (HcInterruptStatus[0]) Disabled even if the corresponding bit in HcInterruptEnable is high. + * | | |1 = Interrupt generation due to RHSC (HcInterruptStatus[6]), FNO (HcInterruptStatus[5]), RD (HcInterruptStatus[3]), SF (HcInterruptStatus[2]), WDH (HcInterruptStatus[1]) or SO (HcInterruptStatus[0]) Enabled if the corresponding bit in HcInterruptEnable is high. + * @var USBH_T::HcInterruptDisable + * Offset: 0x14 Host Controller Interrupt Disable Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[0] |SO |Scheduling Overrun Disable Bit + * | | |Write Operation: + * | | |0 = No effect. + * | | |1 = Interrupt generation due to SO (HcInterruptStatus[0]) Disabled. + * | | |Read Operation: + * | | |0 = Interrupt generation due to SO (HcInterruptStatus[0]) Disabled. + * | | |1 = Interrupt generation due to SO (HcInterruptStatus[0]) Enabled. + * |[1] |WDH |Write Back Done Head Disable Bit + * | | |Write Operation: + * | | |0 = No effect. + * | | |1 = Interrupt generation due to WDH (HcInterruptStatus[1]) Disabled. + * | | |Read Operation: + * | | |0 = Interrupt generation due to WDH (HcInterruptStatus[1]) Disabled. + * | | |1 = Interrupt generation due to WDH (HcInterruptStatus[1]) Enabled. + * |[2] |SF |Start of Frame Disable Bit + * | | |Write Operation: + * | | |0 = No effect. + * | | |1 = Interrupt generation due to SF (HcInterruptStatus[2]) Disabled. + * | | |Read Operation: + * | | |0 = Interrupt generation due to SF (HcInterruptStatus[2]) Disabled. + * | | |1 = Interrupt generation due to SF (HcInterruptStatus[2]) Enabled. + * |[3] |RD |Resume Detected Disable Bit + * | | |Write Operation: + * | | |0 = No effect. + * | | |1 = Interrupt generation due to RD (HcInterruptStatus[3]) Disabled. + * | | |Read Operation: + * | | |0 = Interrupt generation due to RD (HcInterruptStatus[3]) Disabled. + * | | |1 = Interrupt generation due to RD (HcInterruptStatus[3]) Enabled. + * |[5] |FNO |Frame Number Overflow Disable Bit + * | | |Write Operation: + * | | |0 = No effect. + * | | |1 = Interrupt generation due to FNO (HcInterruptStatus[5]) Disabled. + * | | |Read Operation: + * | | |0 = Interrupt generation due to FNO (HcInterruptStatus[5]) Disabled. + * | | |1 = Interrupt generation due to FNO (HcInterruptStatus[5]) Enabled. + * |[6] |RHSC |Root Hub Status Change Disable Bit + * | | |Write Operation: + * | | |0 = No effect. + * | | |1 = Interrupt generation due to RHSC (HcInterruptStatus[6]) Disabled. + * | | |Read Operation: + * | | |0 = Interrupt generation due to RHSC (HcInterruptStatus[6]) Disabled. + * | | |1 = Interrupt generation due to RHSC (HcInterruptStatus[6]) Enabled. + * |[31] |MIE |Master Interrupt Disable Bit + * | | |Global interrupt disable. Writing '1' to disable all interrupts. + * | | |Write Operation: + * | | |0 = No effect. + * | | |1 = Interrupt generation due to RHSC (HcInterruptStatus[6]), FNO (HcInterruptStatus[5]), RD (HcInterruptStatus[3]), SF (HcInterruptStatus[2]), WDH (HcInterruptStatus[1]) or SO (HcInterruptStatus[0]) Disabled if the corresponding bit in HcInterruptEnable is high. + * | | |Read Operation: + * | | |0 = Interrupt generation due to RHSC (HcInterruptStatus[6]), FNO (HcInterruptStatus[5]), RD (HcInterruptStatus[3]), SF (HcInterruptStatus[2]), WDH (HcInterruptStatus[1]) or SO (HcInterruptStatus[0]) Disabled even if the corresponding bit in HcInterruptEnable is high. + * | | |1 = Interrupt generation due to RHSC (HcInterruptStatus[6]), FNO (HcInterruptStatus[5]), RD (HcInterruptStatus[3]), SF (HcInterruptStatus[2]), WDH (HcInterruptStatus[1]) or SO (HcInterruptStatus[0]) Enabled if the corresponding bit in HcInterruptEnable is high. + * @var USBH_T::HcHCCA + * Offset: 0x18 Host Controller Communication Area Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[31:8] |HCCA |Host Controller Communication Area + * | | |Pointer to indicate base address of the Host Controller Communication Area (HCCA). + * @var USBH_T::HcPeriodCurrentED + * Offset: 0x1C Host Controller Period Current ED Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[31:4] |PCED |Periodic Current ED + * | | |Pointer to indicate physical address of the current Isochronous or Interrupt Endpoint Descriptor. + * @var USBH_T::HcControlHeadED + * Offset: 0x20 Host Controller Control Head ED Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[31:4] |CHED |Control Head ED + * | | |Pointer to indicate physical address of the first Endpoint Descriptor of the Control list. + * @var USBH_T::HcControlCurrentED + * Offset: 0x24 Host Controller Control Current ED Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[31:4] |CCED |Control Current Head ED + * | | |Pointer to indicate the physical address of the current Endpoint Descriptor of the Control list. + * @var USBH_T::HcBulkHeadED + * Offset: 0x28 Host Controller Bulk Head ED Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[31:4] |BHED |Bulk Head ED + * | | |Pointer to indicate the physical address of the first Endpoint Descriptor of the Bulk list. + * @var USBH_T::HcBulkCurrentED + * Offset: 0x2C Host Controller Bulk Current ED Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[31:4] |BCED |Bulk Current Head ED + * | | |Pointer to indicate the physical address of the current endpoint of the Bulk list. + * @var USBH_T::HcDoneHead + * Offset: 0x30 Host Controller Done Head Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[31:4] |DH |Done Head + * | | |Pointer to indicate the physical address of the last completed Transfer Descriptor that was added to the Done queue. + * @var USBH_T::HcFmInterval + * Offset: 0x34 Host Controller Frame Interval Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[13:0] |FI |Frame Interval + * | | |This field specifies the length of a frame as (bit times - 1) + * | | |For 12,000 bit times in a frame, a value of 11,999 is stored here. + * |[30:16] |FSMPS |FS Largest Data Packet + * | | |This field specifies a value that is loaded into the Largest Data Packet Counter at the beginning of each frame. + * |[31] |FIT |Frame Interval Toggle + * | | |This bit is toggled by Host Controller Driver when it loads a new value into FI (HcFmInterval[13:0]). + * | | |0 = Host Controller Driver didn't load new value into FI (HcFmInterval[13:0]). + * | | |1 = Host Controller Driver loads a new value into FI (HcFmInterval[13:0]). + * @var USBH_T::HcFmRemaining + * Offset: 0x38 Host Controller Frame Remaining Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[13:0] |FR |Frame Remaining + * | | |When the Host Controller is in the USBOPERATIONAL state, this 14-bit field decrements each 12 MHz clock period + * | | |When the count reaches 0, (end of frame) the counter reloads with Frame Interval + * | | |In addition, the counter loads when the Host Controller transitions into USBOPERATIONAL. + * |[31] |FRT |Frame Remaining Toggle + * | | |This bit is loaded from the FIT (HcFmInterval[31]) whenever FR (HcFmRemaining[13:0]) reaches 0. + * @var USBH_T::HcFmNumber + * Offset: 0x3C Host Controller Frame Number Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[15:0] |FN |Frame Number + * | | |This 16-bit incrementing counter field is incremented coincident with the re-load of FR (HcFmRemaining[13:0]) + * | | |The count rolls over from 'FFFFh' to '0h.' + * @var USBH_T::HcPeriodicStart + * Offset: 0x40 Host Controller Periodic Start Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[13:0] |PS |Periodic Start + * | | |This field contains a value used by the List Processor to determine where in a frame the Periodic List processing must begin. + * @var USBH_T::HcLSThreshold + * Offset: 0x44 Host Controller Low-speed Threshold Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[11:0] |LST |Low-speed Threshold + * | | |This field contains a value which is compared to the FR (HcFmRemaining[13:0]) field prior to initiating a Low-speed transaction + * | | |The transaction is started only if FR (HcFmRemaining[13:0]) >= this field + * | | |The value is calculated by Host Controller Driver with the consideration of transmission and setup overhead. + * @var USBH_T::HcRhDescriptorA + * Offset: 0x48 Host Controller Root Hub Descriptor A Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[7:0] |NDP |Number Downstream Ports + * | | |USB host control supports two downstream ports and only one port is available in this series of chip. + * |[8] |PSM |Power Switching Mode + * | | |This bit is used to specify how the power switching of the Root Hub ports is controlled. + * | | |0 = Global Switching. + * | | |1 = Individual Switching. + * |[11] |OCPM |over Current Protection Mode + * | | |This bit describes how the over current status for the Root Hub ports reported + * | | |This bit is only valid when NOCP (HcRhDescriptorA[12]) is cleared. + * | | |0 = Global Over current. + * | | |1 = Individual Over current. + * |[12] |NOCP |No over Current Protection + * | | |This bit describes how the over current status for the Root Hub ports reported. + * | | |0 = Over current status is reported. + * | | |1 = Over current status is not reported. + * @var USBH_T::HcRhDescriptorB + * Offset: 0x4C Host Controller Root Hub Descriptor B Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[31:16] |PPCM |Port Power Control Mask + * | | |Global power switching + * | | |This field is only valid if PowerSwitchingMode is set (individual port switching) + * | | |When set, the port only responds to individual port power switching commands (Set/ClearPortPower) + * | | |When cleared, the port only responds to global power switching commands (Set/ClearGlobalPower). + * | | |0 = Port power controlled by global power switching. + * | | |1 = Port power controlled by port power switching. + * | | |Note: PPCM[15:2] and PPCM[0] are reserved. + * @var USBH_T::HcRhStatus + * Offset: 0x50 Host Controller Root Hub Status Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[0] |LPS |Clear Global Power + * | | |In global power mode (PSM (HcRhDescriptorA[8]) = 0), this bit is written to one to clear all ports' power. + * | | |This bit always read as zero. + * | | |Write Operation: + * | | |0 = No effect. + * | | |1 = Clear global power. + * |[1] |OCI |over Current Indicator + * | | |This bit reflects the state of the over current status pin + * | | |This field is only valid if NOCP (HcRhDesA[12]) and OCPM (HcRhDesA[11]) are cleared. + * | | |0 = No over current condition. + * | | |1 = Over current condition. + * |[15] |DRWE |Device Remote Wakeup Enable Bit + * | | |This bit controls if port's Connect Status Change as a remote wake-up event. + * | | |Write Operation: + * | | |0 = No effect. + * | | |1 = Connect Status Change as a remote wake-up event Enabled. + * | | |Read Operation: + * | | |0 = Connect Status Change as a remote wake-up event Disabled. + * | | |1 = Connect Status Change as a remote wake-up event Enabled. + * |[16] |LPSC |Set Global Power + * | | |In global power mode (PSM (HcRhDescriptorA[8]) = 0), this bit is written to one to enable power to all ports. + * | | |This bit always read as zero. + * | | |Write Operation: + * | | |0 = No effect. + * | | |1 = Set global power. + * |[17] |OCIC |over Current Indicator Change + * | | |This bit is set by hardware when a change has occurred in OCI (HcRhStatus[1]). + * | | |Write 1 to clear this bit to zero. + * | | |0 = OCI (HcRhStatus[1]) didn't change. + * | | |1 = OCI (HcRhStatus[1]) change. + * |[31] |CRWE |Clear Remote Wake-up Enable Bit + * | | |This bit is use to clear DRWE (HcRhStatus[15]). + * | | |This bit always read as zero. + * | | |Write Operation: + * | | |0 = No effect. + * | | |1 = Clear DRWE (HcRhStatus[15]). + * @var USBH_T::HcRhPortStatus[2] + * Offset: 0x54 Host Controller Root Hub Port Status + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[0] |CCS |CurrentConnectStatus (Read) or ClearPortEnable Bit (Write) + * | | |Write Operation: + * | | |0 = No effect. + * | | |1 = Clear port enable. + * | | |Read Operation: + * | | |0 = No device connected. + * | | |1 = Device connected. + * |[1] |PES |Port Enable Status + * | | |Write Operation: + * | | |0 = No effect. + * | | |1 = Set port enable. + * | | |Read Operation: + * | | |0 = Port Disabled. + * | | |1 = Port Enabled. + * |[2] |PSS |Port Suspend Status + * | | |This bit indicates the port is suspended + * | | |Write Operation: + * | | |0 = No effect. + * | | |1 = Set port suspend. + * | | |Read Operation: + * | | |0 = Port is not suspended. + * | | |1 = Port is selectively suspended. + * |[3] |POCI |Port over Current Indicator (Read) or Clear Port Suspend (Write) + * | | |This bit reflects the state of the over current status pin dedicated to this port + * | | |This field is only valid if NOCP (HcRhDescriptorA[12]) is cleared and OCPM (HcRhDescriptorA[11]) is set. + * | | |This bit is also used to initiate the selective result sequence for the port. + * | | |Write Operation: + * | | |0 = No effect. + * | | |1 = Clear port suspend. + * | | |Read Operation: + * | | |0 = No over current condition. + * | | |1 = Over current condition. + * |[4] |PRS |Port Reset Status + * | | |This bit reflects the reset state of the port. + * | | |Write Operation: + * | | |0 = No effect. + * | | |1 = Set port reset. + * | | |Read Operation + * | | |0 = Port reset signal is not active. + * | | |1 = Port reset signal is active. + * |[8] |PPS |Port Power Status + * | | |This bit reflects the power state of the port regardless of the power switching mode. + * | | |Write Operation: + * | | |0 = No effect. + * | | |1 = Port Power Enabled. + * | | |Read Operation: + * | | |0 = Port power is Disabled. + * | | |1 = Port power is Enabled. + * |[9] |LSDA |Low Speed Device Attached (Read) or Clear Port Power (Write) + * | | |This bit defines the speed (and bud idle) of the attached device + * | | |It is only valid when CCS (HcRhPortStatus1[0]) is set. + * | | |This bit is also used to clear port power. + * | | |Write Operation: + * | | |0 = No effect. + * | | |1 = Clear PPS (HcRhPortStatus1[8]). + * | | |Read Operation: + * | | |0 = Full Speed device. + * | | |1 = Low-speed device. + * |[16] |CSC |Connect Status Change + * | | |This bit indicates connect or disconnect event has been detected (CCS (HcRhPortStatus1[0]) changed). + * | | |Write 1 to clear this bit to zero. + * | | |0 = No connect/disconnect event (CCS (HcRhPortStatus1[0]) didn't change). + * | | |1 = Hardware detection of connect/disconnect event (CCS (HcRhPortStatus1[0]) changed). + * |[17] |PESC |Port Enable Status Change + * | | |This bit indicates that the port has been disabled (PES (HcRhPortStatus1[1]) cleared) due to a hardware event. + * | | |Write 1 to clear this bit to zero. + * | | |0 = PES (HcRhPortStatus1[1]) didn't change. + * | | |1 = PES (HcRhPortStatus1[1]) changed. + * |[18] |PSSC |Port Suspend Status Change + * | | |This bit indicates the completion of the selective resume sequence for the port. + * | | |Write 1 to clear this bit to zero. + * | | |0 = Port resume is not completed. + * | | |1 = Port resume completed. + * |[19] |OCIC |Port over Current Indicator Change + * | | |This bit is set when POCI (HcRhPortStatus1[3]) changes. + * | | |Write 1 to clear this bit to zero. + * | | |0 = POCI (HcRhPortStatus1[3]) didn't change. + * | | |1 = POCI (HcRhPortStatus1[3]) changes. + * |[20] |PRSC |Port Reset Status Change + * | | |This bit indicates that the port reset signal has completed. + * | | |Write 1 to clear this bit to zero. + * | | |0 = Port reset is not complete. + * | | |1 = Port reset is complete. + * @var USBH_T::HcPhyControl + * Offset: 0x200 Host Controller PHY Control Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[27] |STBYEN |USB Transceiver Standby Enable Bit + * | | |This bit controls if USB transceiver could enter the standby mode to reduce power consumption. + * | | |0 = The USB transceiver would never enter the standby mode. + * | | |1 = The USB transceiver will enter standby mode while port is in power off state (port power is inactive). + * @var USBH_T::HcMiscControl + * Offset: 0x204 Host Controller Miscellaneous Control Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[1] |ABORT |AHB Bus ERROR Response + * | | |This bit indicates there is an ERROR response received in AHB bus. + * | | |0 = No ERROR response received. + * | | |1 = ERROR response received. + * |[3] |OCAL |over Current Active Low + * | | |This bit controls the polarity of over current flag from external power IC. + * | | |0 = Over current flag is high active. + * | | |1 = Over current flag is low active. + * |[16] |DPRT1 |Disable Port 1 + * | | |This bit controls if the connection between USB host controller and transceiver of port 1 is disabled + * | | |If the connection is disabled, the USB host controller will not recognize any event of USB bus. + * | | |Set this bit high, the transceiver of port 1 will also be forced into the standby mode no matter what USB host controller operation is. + * | | |0 = The connection between USB host controller and transceiver of port 1 Enabled. + * | | |1 = The connection between USB host controller and transceiver of port 1 Disabled and the transceiver of port 1 will also be forced into the standby mode. + */ + __I uint32_t HcRevision; /*!< [0x0000] Host Controller Revision Register */ + __IO uint32_t HcControl; /*!< [0x0004] Host Controller Control Register */ + __IO uint32_t HcCommandStatus; /*!< [0x0008] Host Controller Command Status Register */ + __IO uint32_t HcInterruptStatus; /*!< [0x000c] Host Controller Interrupt Status Register */ + __IO uint32_t HcInterruptEnable; /*!< [0x0010] Host Controller Interrupt Enable Register */ + __IO uint32_t HcInterruptDisable; /*!< [0x0014] Host Controller Interrupt Disable Register */ + __IO uint32_t HcHCCA; /*!< [0x0018] Host Controller Communication Area Register */ + __IO uint32_t HcPeriodCurrentED; /*!< [0x001c] Host Controller Period Current ED Register */ + __IO uint32_t HcControlHeadED; /*!< [0x0020] Host Controller Control Head ED Register */ + __IO uint32_t HcControlCurrentED; /*!< [0x0024] Host Controller Control Current ED Register */ + __IO uint32_t HcBulkHeadED; /*!< [0x0028] Host Controller Bulk Head ED Register */ + __IO uint32_t HcBulkCurrentED; /*!< [0x002c] Host Controller Bulk Current ED Register */ + __IO uint32_t HcDoneHead; /*!< [0x0030] Host Controller Done Head Register */ + __IO uint32_t HcFmInterval; /*!< [0x0034] Host Controller Frame Interval Register */ + __I uint32_t HcFmRemaining; /*!< [0x0038] Host Controller Frame Remaining Register */ + __I uint32_t HcFmNumber; /*!< [0x003c] Host Controller Frame Number Register */ + __IO uint32_t HcPeriodicStart; /*!< [0x0040] Host Controller Periodic Start Register */ + __IO uint32_t HcLSThreshold; /*!< [0x0044] Host Controller Low-speed Threshold Register */ + __IO uint32_t HcRhDescriptorA; /*!< [0x0048] Host Controller Root Hub Descriptor A Register */ + __IO uint32_t HcRhDescriptorB; /*!< [0x004c] Host Controller Root Hub Descriptor B Register */ + __IO uint32_t HcRhStatus; /*!< [0x0050] Host Controller Root Hub Status Register */ + __IO uint32_t HcRhPortStatus[8]; /*!< [0x0054] Host Controller Root Hub Port Status [1] */ + __I uint32_t RESERVE0[99]; + __IO uint32_t HcPhyControl; /*!< [0x0200] Host Controller PHY Control Register */ + __IO uint32_t HcMiscControl; /*!< [0x0204] Host Controller Miscellaneous Control Register */ + +} USBH_T; + +/** + @addtogroup USBH_CONST USBH Bit Field Definition + Constant Definitions for USBH Controller +@{ */ + +#define USBH_HcRevision_REV_Pos (0) /*!< USBH_T::HcRevision: REV Position */ +#define USBH_HcRevision_REV_Msk (0xfful << USBH_HcRevision_REV_Pos) /*!< USBH_T::HcRevision: REV Mask */ + +#define USBH_HcControl_CBSR_Pos (0) /*!< USBH_T::HcControl: CBSR Position */ +#define USBH_HcControl_CBSR_Msk (0x3ul << USBH_HcControl_CBSR_Pos) /*!< USBH_T::HcControl: CBSR Mask */ + +#define USBH_HcControl_PLE_Pos (2) /*!< USBH_T::HcControl: PLE Position */ +#define USBH_HcControl_PLE_Msk (0x1ul << USBH_HcControl_PLE_Pos) /*!< USBH_T::HcControl: PLE Mask */ + +#define USBH_HcControl_IE_Pos (3) /*!< USBH_T::HcControl: IE Position */ +#define USBH_HcControl_IE_Msk (0x1ul << USBH_HcControl_IE_Pos) /*!< USBH_T::HcControl: IE Mask */ + +#define USBH_HcControl_CLE_Pos (4) /*!< USBH_T::HcControl: CLE Position */ +#define USBH_HcControl_CLE_Msk (0x1ul << USBH_HcControl_CLE_Pos) /*!< USBH_T::HcControl: CLE Mask */ + +#define USBH_HcControl_BLE_Pos (5) /*!< USBH_T::HcControl: BLE Position */ +#define USBH_HcControl_BLE_Msk (0x1ul << USBH_HcControl_BLE_Pos) /*!< USBH_T::HcControl: BLE Mask */ + +#define USBH_HcControl_HCFS_Pos (6) /*!< USBH_T::HcControl: HCFS Position */ +#define USBH_HcControl_HCFS_Msk (0x3ul << USBH_HcControl_HCFS_Pos) /*!< USBH_T::HcControl: HCFS Mask */ + +#define USBH_HcCommandStatus_HCR_Pos (0) /*!< USBH_T::HcCommandStatus: HCR Position */ +#define USBH_HcCommandStatus_HCR_Msk (0x1ul << USBH_HcCommandStatus_HCR_Pos) /*!< USBH_T::HcCommandStatus: HCR Mask */ + +#define USBH_HcCommandStatus_CLF_Pos (1) /*!< USBH_T::HcCommandStatus: CLF Position */ +#define USBH_HcCommandStatus_CLF_Msk (0x1ul << USBH_HcCommandStatus_CLF_Pos) /*!< USBH_T::HcCommandStatus: CLF Mask */ + +#define USBH_HcCommandStatus_BLF_Pos (2) /*!< USBH_T::HcCommandStatus: BLF Position */ +#define USBH_HcCommandStatus_BLF_Msk (0x1ul << USBH_HcCommandStatus_BLF_Pos) /*!< USBH_T::HcCommandStatus: BLF Mask */ + +#define USBH_HcCommandStatus_SOC_Pos (16) /*!< USBH_T::HcCommandStatus: SOC Position */ +#define USBH_HcCommandStatus_SOC_Msk (0x3ul << USBH_HcCommandStatus_SOC_Pos) /*!< USBH_T::HcCommandStatus: SOC Mask */ + +#define USBH_HcInterruptStatus_SO_Pos (0) /*!< USBH_T::HcInterruptStatus: SO Position */ +#define USBH_HcInterruptStatus_SO_Msk (0x1ul << USBH_HcInterruptStatus_SO_Pos) /*!< USBH_T::HcInterruptStatus: SO Mask */ + +#define USBH_HcInterruptStatus_WDH_Pos (1) /*!< USBH_T::HcInterruptStatus: WDH Position*/ +#define USBH_HcInterruptStatus_WDH_Msk (0x1ul << USBH_HcInterruptStatus_WDH_Pos) /*!< USBH_T::HcInterruptStatus: WDH Mask */ + +#define USBH_HcInterruptStatus_SF_Pos (2) /*!< USBH_T::HcInterruptStatus: SF Position */ +#define USBH_HcInterruptStatus_SF_Msk (0x1ul << USBH_HcInterruptStatus_SF_Pos) /*!< USBH_T::HcInterruptStatus: SF Mask */ + +#define USBH_HcInterruptStatus_RD_Pos (3) /*!< USBH_T::HcInterruptStatus: RD Position */ +#define USBH_HcInterruptStatus_RD_Msk (0x1ul << USBH_HcInterruptStatus_RD_Pos) /*!< USBH_T::HcInterruptStatus: RD Mask */ + +#define USBH_HcInterruptStatus_FNO_Pos (5) /*!< USBH_T::HcInterruptStatus: FNO Position*/ +#define USBH_HcInterruptStatus_FNO_Msk (0x1ul << USBH_HcInterruptStatus_FNO_Pos) /*!< USBH_T::HcInterruptStatus: FNO Mask */ + +#define USBH_HcInterruptStatus_RHSC_Pos (6) /*!< USBH_T::HcInterruptStatus: RHSC Position*/ +#define USBH_HcInterruptStatus_RHSC_Msk (0x1ul << USBH_HcInterruptStatus_RHSC_Pos) /*!< USBH_T::HcInterruptStatus: RHSC Mask */ + +#define USBH_HcInterruptEnable_SO_Pos (0) /*!< USBH_T::HcInterruptEnable: SO Position */ +#define USBH_HcInterruptEnable_SO_Msk (0x1ul << USBH_HcInterruptEnable_SO_Pos) /*!< USBH_T::HcInterruptEnable: SO Mask */ + +#define USBH_HcInterruptEnable_WDH_Pos (1) /*!< USBH_T::HcInterruptEnable: WDH Position*/ +#define USBH_HcInterruptEnable_WDH_Msk (0x1ul << USBH_HcInterruptEnable_WDH_Pos) /*!< USBH_T::HcInterruptEnable: WDH Mask */ + +#define USBH_HcInterruptEnable_SF_Pos (2) /*!< USBH_T::HcInterruptEnable: SF Position */ +#define USBH_HcInterruptEnable_SF_Msk (0x1ul << USBH_HcInterruptEnable_SF_Pos) /*!< USBH_T::HcInterruptEnable: SF Mask */ + +#define USBH_HcInterruptEnable_RD_Pos (3) /*!< USBH_T::HcInterruptEnable: RD Position */ +#define USBH_HcInterruptEnable_RD_Msk (0x1ul << USBH_HcInterruptEnable_RD_Pos) /*!< USBH_T::HcInterruptEnable: RD Mask */ + +#define USBH_HcInterruptEnable_FNO_Pos (5) /*!< USBH_T::HcInterruptEnable: FNO Position*/ +#define USBH_HcInterruptEnable_FNO_Msk (0x1ul << USBH_HcInterruptEnable_FNO_Pos) /*!< USBH_T::HcInterruptEnable: FNO Mask */ + +#define USBH_HcInterruptEnable_RHSC_Pos (6) /*!< USBH_T::HcInterruptEnable: RHSC Position*/ +#define USBH_HcInterruptEnable_RHSC_Msk (0x1ul << USBH_HcInterruptEnable_RHSC_Pos) /*!< USBH_T::HcInterruptEnable: RHSC Mask */ + +#define USBH_HcInterruptEnable_MIE_Pos (31) /*!< USBH_T::HcInterruptEnable: MIE Position*/ +#define USBH_HcInterruptEnable_MIE_Msk (0x1ul << USBH_HcInterruptEnable_MIE_Pos) /*!< USBH_T::HcInterruptEnable: MIE Mask */ + +#define USBH_HcInterruptDisable_SO_Pos (0) /*!< USBH_T::HcInterruptDisable: SO Position*/ +#define USBH_HcInterruptDisable_SO_Msk (0x1ul << USBH_HcInterruptDisable_SO_Pos) /*!< USBH_T::HcInterruptDisable: SO Mask */ + +#define USBH_HcInterruptDisable_WDH_Pos (1) /*!< USBH_T::HcInterruptDisable: WDH Position*/ +#define USBH_HcInterruptDisable_WDH_Msk (0x1ul << USBH_HcInterruptDisable_WDH_Pos) /*!< USBH_T::HcInterruptDisable: WDH Mask */ + +#define USBH_HcInterruptDisable_SF_Pos (2) /*!< USBH_T::HcInterruptDisable: SF Position*/ +#define USBH_HcInterruptDisable_SF_Msk (0x1ul << USBH_HcInterruptDisable_SF_Pos) /*!< USBH_T::HcInterruptDisable: SF Mask */ + +#define USBH_HcInterruptDisable_RD_Pos (3) /*!< USBH_T::HcInterruptDisable: RD Position*/ +#define USBH_HcInterruptDisable_RD_Msk (0x1ul << USBH_HcInterruptDisable_RD_Pos) /*!< USBH_T::HcInterruptDisable: RD Mask */ + +#define USBH_HcInterruptDisable_FNO_Pos (5) /*!< USBH_T::HcInterruptDisable: FNO Position*/ +#define USBH_HcInterruptDisable_FNO_Msk (0x1ul << USBH_HcInterruptDisable_FNO_Pos) /*!< USBH_T::HcInterruptDisable: FNO Mask */ + +#define USBH_HcInterruptDisable_RHSC_Pos (6) /*!< USBH_T::HcInterruptDisable: RHSC Position*/ +#define USBH_HcInterruptDisable_RHSC_Msk (0x1ul << USBH_HcInterruptDisable_RHSC_Pos) /*!< USBH_T::HcInterruptDisable: RHSC Mask */ + +#define USBH_HcInterruptDisable_MIE_Pos (31) /*!< USBH_T::HcInterruptDisable: MIE Position*/ +#define USBH_HcInterruptDisable_MIE_Msk (0x1ul << USBH_HcInterruptDisable_MIE_Pos) /*!< USBH_T::HcInterruptDisable: MIE Mask */ + +#define USBH_HcHCCA_HCCA_Pos (8) /*!< USBH_T::HcHCCA: HCCA Position */ +#define USBH_HcHCCA_HCCA_Msk (0xfffffful << USBH_HcHCCA_HCCA_Pos) /*!< USBH_T::HcHCCA: HCCA Mask */ + +#define USBH_HcPeriodCurrentED_PCED_Pos (4) /*!< USBH_T::HcPeriodCurrentED: PCED Position*/ +#define USBH_HcPeriodCurrentED_PCED_Msk (0xffffffful << USBH_HcPeriodCurrentED_PCED_Pos) /*!< USBH_T::HcPeriodCurrentED: PCED Mask */ + +#define USBH_HcControlHeadED_CHED_Pos (4) /*!< USBH_T::HcControlHeadED: CHED Position */ +#define USBH_HcControlHeadED_CHED_Msk (0xffffffful << USBH_HcControlHeadED_CHED_Pos) /*!< USBH_T::HcControlHeadED: CHED Mask */ + +#define USBH_HcControlCurrentED_CCED_Pos (4) /*!< USBH_T::HcControlCurrentED: CCED Position*/ +#define USBH_HcControlCurrentED_CCED_Msk (0xffffffful << USBH_HcControlCurrentED_CCED_Pos) /*!< USBH_T::HcControlCurrentED: CCED Mask */ + +#define USBH_HcBulkHeadED_BHED_Pos (4) /*!< USBH_T::HcBulkHeadED: BHED Position */ +#define USBH_HcBulkHeadED_BHED_Msk (0xffffffful << USBH_HcBulkHeadED_BHED_Pos) /*!< USBH_T::HcBulkHeadED: BHED Mask */ + +#define USBH_HcBulkCurrentED_BCED_Pos (4) /*!< USBH_T::HcBulkCurrentED: BCED Position */ +#define USBH_HcBulkCurrentED_BCED_Msk (0xffffffful << USBH_HcBulkCurrentED_BCED_Pos) /*!< USBH_T::HcBulkCurrentED: BCED Mask */ + +#define USBH_HcDoneHead_DH_Pos (4) /*!< USBH_T::HcDoneHead: DH Position */ +#define USBH_HcDoneHead_DH_Msk (0xffffffful << USBH_HcDoneHead_DH_Pos) /*!< USBH_T::HcDoneHead: DH Mask */ + +#define USBH_HcFmInterval_FI_Pos (0) /*!< USBH_T::HcFmInterval: FI Position */ +#define USBH_HcFmInterval_FI_Msk (0x3ffful << USBH_HcFmInterval_FI_Pos) /*!< USBH_T::HcFmInterval: FI Mask */ + +#define USBH_HcFmInterval_FSMPS_Pos (16) /*!< USBH_T::HcFmInterval: FSMPS Position */ +#define USBH_HcFmInterval_FSMPS_Msk (0x7ffful << USBH_HcFmInterval_FSMPS_Pos) /*!< USBH_T::HcFmInterval: FSMPS Mask */ + +#define USBH_HcFmInterval_FIT_Pos (31) /*!< USBH_T::HcFmInterval: FIT Position */ +#define USBH_HcFmInterval_FIT_Msk (0x1ul << USBH_HcFmInterval_FIT_Pos) /*!< USBH_T::HcFmInterval: FIT Mask */ + +#define USBH_HcFmRemaining_FR_Pos (0) /*!< USBH_T::HcFmRemaining: FR Position */ +#define USBH_HcFmRemaining_FR_Msk (0x3ffful << USBH_HcFmRemaining_FR_Pos) /*!< USBH_T::HcFmRemaining: FR Mask */ + +#define USBH_HcFmRemaining_FRT_Pos (31) /*!< USBH_T::HcFmRemaining: FRT Position */ +#define USBH_HcFmRemaining_FRT_Msk (0x1ul << USBH_HcFmRemaining_FRT_Pos) /*!< USBH_T::HcFmRemaining: FRT Mask */ + +#define USBH_HcFmNumber_FN_Pos (0) /*!< USBH_T::HcFmNumber: FN Position */ +#define USBH_HcFmNumber_FN_Msk (0xfffful << USBH_HcFmNumber_FN_Pos) /*!< USBH_T::HcFmNumber: FN Mask */ + +#define USBH_HcPeriodicStart_PS_Pos (0) /*!< USBH_T::HcPeriodicStart: PS Position */ +#define USBH_HcPeriodicStart_PS_Msk (0x3ffful << USBH_HcPeriodicStart_PS_Pos) /*!< USBH_T::HcPeriodicStart: PS Mask */ + +#define USBH_HcLSThreshold_LST_Pos (0) /*!< USBH_T::HcLSThreshold: LST Position */ +#define USBH_HcLSThreshold_LST_Msk (0xffful << USBH_HcLSThreshold_LST_Pos) /*!< USBH_T::HcLSThreshold: LST Mask */ + +#define USBH_HcRhDescriptorA_NDP_Pos (0) /*!< USBH_T::HcRhDescriptorA: NDP Position */ +#define USBH_HcRhDescriptorA_NDP_Msk (0xfful << USBH_HcRhDescriptorA_NDP_Pos) /*!< USBH_T::HcRhDescriptorA: NDP Mask */ + +#define USBH_HcRhDescriptorA_PSM_Pos (8) /*!< USBH_T::HcRhDescriptorA: PSM Position */ +#define USBH_HcRhDescriptorA_PSM_Msk (0x1ul << USBH_HcRhDescriptorA_PSM_Pos) /*!< USBH_T::HcRhDescriptorA: PSM Mask */ + +#define USBH_HcRhDescriptorA_OCPM_Pos (11) /*!< USBH_T::HcRhDescriptorA: OCPM Position */ +#define USBH_HcRhDescriptorA_OCPM_Msk (0x1ul << USBH_HcRhDescriptorA_OCPM_Pos) /*!< USBH_T::HcRhDescriptorA: OCPM Mask */ + +#define USBH_HcRhDescriptorA_NOCP_Pos (12) /*!< USBH_T::HcRhDescriptorA: NOCP Position */ +#define USBH_HcRhDescriptorA_NOCP_Msk (0x1ul << USBH_HcRhDescriptorA_NOCP_Pos) /*!< USBH_T::HcRhDescriptorA: NOCP Mask */ + +#define USBH_HcRhDescriptorB_PPCM_Pos (16) /*!< USBH_T::HcRhDescriptorB: PPCM Position */ +#define USBH_HcRhDescriptorB_PPCM_Msk (0xfffful << USBH_HcRhDescriptorB_PPCM_Pos) /*!< USBH_T::HcRhDescriptorB: PPCM Mask */ + +#define USBH_HcRhStatus_LPS_Pos (0) /*!< USBH_T::HcRhStatus: LPS Position */ +#define USBH_HcRhStatus_LPS_Msk (0x1ul << USBH_HcRhStatus_LPS_Pos) /*!< USBH_T::HcRhStatus: LPS Mask */ + +#define USBH_HcRhStatus_OCI_Pos (1) /*!< USBH_T::HcRhStatus: OCI Position */ +#define USBH_HcRhStatus_OCI_Msk (0x1ul << USBH_HcRhStatus_OCI_Pos) /*!< USBH_T::HcRhStatus: OCI Mask */ + +#define USBH_HcRhStatus_DRWE_Pos (15) /*!< USBH_T::HcRhStatus: DRWE Position */ +#define USBH_HcRhStatus_DRWE_Msk (0x1ul << USBH_HcRhStatus_DRWE_Pos) /*!< USBH_T::HcRhStatus: DRWE Mask */ + +#define USBH_HcRhStatus_LPSC_Pos (16) /*!< USBH_T::HcRhStatus: LPSC Position */ +#define USBH_HcRhStatus_LPSC_Msk (0x1ul << USBH_HcRhStatus_LPSC_Pos) /*!< USBH_T::HcRhStatus: LPSC Mask */ + +#define USBH_HcRhStatus_OCIC_Pos (17) /*!< USBH_T::HcRhStatus: OCIC Position */ +#define USBH_HcRhStatus_OCIC_Msk (0x1ul << USBH_HcRhStatus_OCIC_Pos) /*!< USBH_T::HcRhStatus: OCIC Mask */ + +#define USBH_HcRhStatus_CRWE_Pos (31) /*!< USBH_T::HcRhStatus: CRWE Position */ +#define USBH_HcRhStatus_CRWE_Msk (0x1ul << USBH_HcRhStatus_CRWE_Pos) /*!< USBH_T::HcRhStatus: CRWE Mask */ + +#define USBH_HcRhPortStatus_CCS_Pos (0) /*!< USBH_T::HcRhPortStatus1: CCS Position */ +#define USBH_HcRhPortStatus_CCS_Msk (0x1ul << USBH_HcRhPortStatus_CCS_Pos) /*!< USBH_T::HcRhPortStatus1: CCS Mask */ + +#define USBH_HcRhPortStatus_PES_Pos (1) /*!< USBH_T::HcRhPortStatus1: PES Position */ +#define USBH_HcRhPortStatus_PES_Msk (0x1ul << USBH_HcRhPortStatus_PES_Pos) /*!< USBH_T::HcRhPortStatus1: PES Mask */ + +#define USBH_HcRhPortStatus_PSS_Pos (2) /*!< USBH_T::HcRhPortStatus1: PSS Position */ +#define USBH_HcRhPortStatus_PSS_Msk (0x1ul << USBH_HcRhPortStatus_PSS_Pos) /*!< USBH_T::HcRhPortStatus1: PSS Mask */ + +#define USBH_HcRhPortStatus_POCI_Pos (3) /*!< USBH_T::HcRhPortStatus1: POCI Position */ +#define USBH_HcRhPortStatus_POCI_Msk (0x1ul << USBH_HcRhPortStatus_POCI_Pos) /*!< USBH_T::HcRhPortStatus1: POCI Mask */ + +#define USBH_HcRhPortStatus_PRS_Pos (4) /*!< USBH_T::HcRhPortStatus1: PRS Position */ +#define USBH_HcRhPortStatus_PRS_Msk (0x1ul << USBH_HcRhPortStatus_PRS_Pos) /*!< USBH_T::HcRhPortStatus1: PRS Mask */ + +#define USBH_HcRhPortStatus_PPS_Pos (8) /*!< USBH_T::HcRhPortStatus1: PPS Position */ +#define USBH_HcRhPortStatus_PPS_Msk (0x1ul << USBH_HcRhPortStatus_PPS_Pos) /*!< USBH_T::HcRhPortStatus1: PPS Mask */ + +#define USBH_HcRhPortStatus_LSDA_Pos (9) /*!< USBH_T::HcRhPortStatus1: LSDA Position */ +#define USBH_HcRhPortStatus_LSDA_Msk (0x1ul << USBH_HcRhPortStatus_LSDA_Pos) /*!< USBH_T::HcRhPortStatus1: LSDA Mask */ + +#define USBH_HcRhPortStatus_CSC_Pos (16) /*!< USBH_T::HcRhPortStatus1: CSC Position */ +#define USBH_HcRhPortStatus_CSC_Msk (0x1ul << USBH_HcRhPortStatus_CSC_Pos) /*!< USBH_T::HcRhPortStatus1: CSC Mask */ + +#define USBH_HcRhPortStatus_PESC_Pos (17) /*!< USBH_T::HcRhPortStatus1: PESC Position */ +#define USBH_HcRhPortStatus_PESC_Msk (0x1ul << USBH_HcRhPortStatus_PESC_Pos) /*!< USBH_T::HcRhPortStatus1: PESC Mask */ + +#define USBH_HcRhPortStatus_PSSC_Pos (18) /*!< USBH_T::HcRhPortStatus1: PSSC Position */ +#define USBH_HcRhPortStatus_PSSC_Msk (0x1ul << USBH_HcRhPortStatus_PSSC_Pos) /*!< USBH_T::HcRhPortStatus1: PSSC Mask */ + +#define USBH_HcRhPortStatus_OCIC_Pos (19) /*!< USBH_T::HcRhPortStatus1: OCIC Position */ +#define USBH_HcRhPortStatus_OCIC_Msk (0x1ul << USBH_HcRhPortStatus_OCIC_Pos) /*!< USBH_T::HcRhPortStatus1: OCIC Mask */ + +#define USBH_HcRhPortStatus_PRSC_Pos (20) /*!< USBH_T::HcRhPortStatus1: PRSC Position */ +#define USBH_HcRhPortStatus_PRSC_Msk (0x1ul << USBH_HcRhPortStatus_PRSC_Pos) /*!< USBH_T::HcRhPortStatus1: PRSC Mask */ + +#define USBH_HcPhyControl_STBYEN_Pos (27) /*!< USBH_T::HcPhyControl: STBYEN Position */ +#define USBH_HcPhyControl_STBYEN_Msk (0x1ul << USBH_HcPhyControl_STBYEN_Pos) /*!< USBH_T::HcPhyControl: STBYEN Mask */ + +#define USBH_HcMiscControl_ABORT_Pos (1) /*!< USBH_T::HcMiscControl: ABORT Position */ +#define USBH_HcMiscControl_ABORT_Msk (0x1ul << USBH_HcMiscControl_ABORT_Pos) /*!< USBH_T::HcMiscControl: ABORT Mask */ + +#define USBH_HcMiscControl_OCAL_Pos (3) /*!< USBH_T::HcMiscControl: OCAL Position */ +#define USBH_HcMiscControl_OCAL_Msk (0x1ul << USBH_HcMiscControl_OCAL_Pos) /*!< USBH_T::HcMiscControl: OCAL Mask */ + +#define USBH_HcMiscControl_DPRT1_Pos (16) /*!< USBH_T::HcMiscControl: DPRT1 Position */ +#define USBH_HcMiscControl_DPRT1_Msk (0x1ul << USBH_HcMiscControl_DPRT1_Pos) /*!< USBH_T::HcMiscControl: DPRT1 Mask */ + +/**@}*/ /* USBH_CONST */ +/**@}*/ /* end of USBH register group */ + + +/*---------------------- HSUSBH HSUSB Host Controller -------------------------*/ +/** + @addtogroup HSUSBH High Speed USB Host Controller (HSUSBH) + Memory Mapped Structure for HSUSBH Controller +@{ */ + +typedef struct +{ + + + /** + * @var HSUSBH_T::EHCVNR + * Offset: 0x00 EHCI Version Number Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[7:0] |CRLEN |Capability Registers Length + * | | |This register is used as an offset to add to register base to find the beginning of the Operational Register Space. + * |[31:16] |VERSION |Host Controller Interface Version Number + * | | |This is a two-byte register containing a BCD encoding of the EHCI revision number supported by this host controller + * | | |The most significant byte of this register represents a major revision and the least significant byte is the minor revision. + * @var HSUSBH_T::EHCSPR + * Offset: 0x04 EHCI Structural Parameters Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[3:0] |N_PORTS |Number of Physical Downstream Ports + * | | |This field specifies the number of physical downstream ports implemented on this host controller + * | | |The value of this field determines how many port registers are addressable in the Operational Register Space (see Table 2-8) + * | | |Valid values are in the range of 1H to FH. + * | | |A zero in this field is undefined. + * |[4] |PPC |Port Power Control + * | | |This field indicates whether the host controller implementation includes port power control + * | | |A one in this bit indicates the ports have port power switches + * | | |A zero in this bit indicates the port do not have port power stitches + * | | |The value of this field affects the functionality of the Port Power field in each port status and control register. + * |[11:8] |N_PCC |Number of Ports Per Companion Controller + * | | |This field indicates the number of ports supported per companion host controller + * | | |It is used to indicate the port routing configuration to system software. + * | | |For example, if N_PORTS has a value of 6 and N_CC has a value of 2 then N_PCC could have a value of 3 + * | | |The convention is that the first N_PCC ports are assumed to be routed to companion controller 1, the next N_PCC ports to companion controller 2, etc + * | | |In the previous example, the N_PCC could have been 4, where the first 4 are routed to companion controller 1 and the last two are routed to companion controller 2. + * | | |The number in this field must be consistent with N_PORTS and N_CC. + * |[15:12] |N_CC |Number of Companion Controller + * | | |This field indicates the number of companion controllers associated with this USB 2.0 host controller. + * | | |A zero in this field indicates there are no companion host controllers + * | | |Port-ownership hand-off is not supported + * | | |Only high-speed devices are supported on the host controller root ports. + * | | |A value larger than zero in this field indicates there are companion USB 1.1 host controller(s) + * | | |Port-ownership hand-offs are supported + * | | |High, Full- and Low-speed devices are supported on the host controller root ports. + * @var HSUSBH_T::EHCCPR + * Offset: 0x08 EHCI Capability Parameters Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[0] |AC64 |64-bit Addressing Capability + * | | |0 = Data structure using 32-bit address memory pointers. + * |[1] |PFLF |Programmable Frame List Flag + * | | |0 = System software must use a frame list length of 1024 elements with this EHCI host controller. + * |[2] |ASPC |Asynchronous Schedule Park Capability + * | | |0 = This EHCI host controller doesn't support park feature of high-speed queue heads in the Asynchronous Schedule. + * |[7:4] |IST |Isochronous Scheduling Threshold + * | | |This field indicates, relative to the current position of the executing host controller, where software can reliably update the isochronous schedule. + * | | |When bit [7] is zero, the value of the least significant 3 bits indicates the number of micro-frames a host controller can hold a set of isochronous data structures (one or more) before flushing the state. + * |[15:8] |EECP |EHCI Extended Capabilities Pointer (EECP) + * | | |0 = No extended capabilities are implemented. + * @var HSUSBH_T::UCMDR + * Offset: 0x20 USB Command Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[0] |RUN |Run/Stop (R/W) + * | | |When set to a 1, the Host Controller proceeds with execution of the schedule + * | | |The Host Controller continues execution as long as this bit is set to a 1 + * | | |When this bit is set to 0, the Host Controller completes the current and any actively pipelined transactions on the USB and then halts + * | | |The Host Controller must halt within 16 micro-frames after software clears the Run bit + * | | |The HC Halted bit in the status register indicates when the Host Controller has finished its pending pipelined transactions and has entered the stopped state + * | | |Software must not write a one to this field unless the host controller is in the Halted state (i.e. + * | | |HCHalted in the USBSTS register is a one) + * | | |Doing so will yield undefined results. + * | | |0 = Stop. + * | | |1 = Run. + * |[1] |HCRST |Host Controller Reset (HCRESET) (R/W) + * | | |This control bit is used by software to reset the host controller + * | | |The effects of this on Root Hub registers are similar to a Chip Hardware Reset. + * | | |When software writes a one to this bit, the Host Controller resets its internal pipelines, timers, counters, state machines, etc + * | | |to their initial value + * | | |Any transaction currently in progress on USB is immediately terminated + * | | |A USB reset is not driven on downstream ports. + * | | |All operational registers, including port registers and port state machines are set to their initial values + * | | |Port ownership reverts to the companion host controller(s), with the side effects + * | | |Software must reinitialize the host controller in order to return the host controller to an operational state. + * | | |This bit is set to zero by the Host Controller when the reset process is complete + * | | |Software cannot terminate the reset process early by writing a zero to this register. + * | | |Software should not set this bit to a one when the HCHalted bit in the USBSTS register is a zero + * | | |Attempting to reset an actively running host controller will result in undefined behavior. + * |[3:2] |FLSZ |Frame List Size (R/W or RO) + * | | |This field is R/W only if Programmable Frame List Flag in the HCCPARAMS registers is set to a one + * | | |This field specifies the size of the frame list + * | | |The size the frame list controls which bits in the Frame Index Register should be used for the Frame List Current index + * | | |Values mean: + * | | |00 = 1024 elements (4096 bytes) Default value. + * | | |01 = 512 elements (2048 bytes). + * | | |10 = 256 elements (1024 bytes) u2013 for resource-constrained environment. + * | | |11 = Reserved. + * |[4] |PSEN |Periodic Schedule Enable (R/W) + * | | |This bit controls whether the host controller skips processing the Periodic Schedule. Values mean: + * | | |0 = Do not process the Periodic Schedule. + * | | |1 = Use the PERIODICLISTBASE register to access the Periodic Schedule. + * |[5] |ASEN |Asynchronous Schedule Enable (R/W) + * | | |This bit controls whether the host controller skips processing the Asynchronous Schedule. Values mean: + * | | |0 = Do not process the Asynchronous Schedule. + * | | |1 = Use the ASYNCLISTADDR register to access the Asynchronous Schedule. + * |[6] |IAAD |Interrupt on Asynchronous Advance Doorbell (R/W) + * | | |This bit is used as a doorbell by software to tell the host controller to issue an interrupt the next time it advances asynchronous schedule + * | | |Software must write a 1 to this bit to ring the doorbell. + * | | |When the host controller has evicted all appropriate cached schedule state, it sets the Interrupt on Asynchronous Advance status bit in the USBSTS register + * | | |If the Interrupt on Asynchronous Advance Enable bit in the USBINTR register is a one then the host controller will assert an interrupt at the next interrupt threshold. + * | | |The host controller sets this bit to a zero after it has set the Interrupt on Asynchronous Advance status bit in the USBSTS register to a one. + * | | |Software should not write a one to this bit when the asynchronous schedule is disabled + * | | |Doing so will yield undefined results. + * |[23:16] |ITC |Interrupt Threshold Control (R/W) + * | | |This field is used by system software to select the maximum rate at which the host controller will issue interrupts + * | | |The only valid values are defined below + * | | |If software writes an invalid value to this register, the results are undefined + * | | |Value Maximum Interrupt Interval + * | | |0x00 = Reserved. + * | | |0x01 = 1 micro-frame. + * | | |0x02 = 2 micro-frames. + * | | |0x04 = 4 micro-frames. + * | | |0x08 = 8 micro-frames (default, equates to 1 ms). + * | | |0x10 = 16 micro-frames (2 ms). + * | | |0x20 = 32 micro-frames (4 ms). + * | | |0x40 = 64 micro-frames (8 ms). + * | | |Any other value in this register yields undefined results. + * | | |Software modifications to this bit while HCHalted bit is equal to zero results in undefined behavior. + * @var HSUSBH_T::USTSR + * Offset: 0x24 USB Status Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[0] |USBINT |USB Interrupt (USBINT) (R/WC) + * | | |The Host Controller sets this bit to 1 on the completion of a USB transaction, which results in the retirement of a Transfer Descriptor that had its IOC bit set. + * | | |The Host Controller also sets this bit to 1 when a short packet is detected (actual number of bytes received was less than the expected number of bytes). + * |[1] |UERRINT |USB Error Interrupt (USBERRINT) (R/WC) + * | | |The Host Controller sets this bit to 1 when completion of a USB transaction results in an error condition (e.g., error counter underflow) + * | | |If the TD on which the error interrupt occurred also had its IOC bit set, both this bit and USBINT bit are set. + * |[2] |PCD |Port Change Detect (R/WC) + * | | |The Host Controller sets this bit to a one when any port for which the Port Owner bit is set to zero has a change bit transition from a zero to a one or a Force Port Resume bit transition from a zero to a one as a result of a J-K transition detected on a suspended port + * | | |This bit will also be set as a result of the Connect Status Change being set to a one after system software has relinquished ownership of a connected port by writing a one to a port's Port Owner bit. + * | | |This bit is allowed to be maintained in the Auxiliary power well + * | | |Alternatively, it is also acceptable that on a D3 to D0 transition of the EHCI HC device, this bit is loaded with the OR of all of the PORTSC change bits (including: Force port resume, over-current change, enable/disable change and connect status change). + * |[3] |FLR |Frame List Rollover (R/WC) + * | | |The Host Controller sets this bit to a one when the Frame List Index rolls over from its maximum value to zero + * | | |The exact value at which the rollover occurs depends on the frame list size + * | | |For example, if the frame list size (as programmed in the Frame List Size field of the USBCMD register) is 1024, the Frame Index Register rolls over every time FRINDEX[13] toggles + * | | |Similarly, if the size is 512, the Host Controller sets this bit to a one every time FRINDEX[12] toggles. + * |[4] |HSERR |Host System Error (R/WC) + * | | |The Host Controller sets this bit to 1 when a serious error occurs during a host system access involving the Host Controller module. + * |[5] |IAA |Interrupt on Asynchronous Advance (R/WC) + * | | |System software can force the host controller to issue an interrupt the next time the host controller advances the asynchronous schedule by writing a one to the Interrupt on Asynchronous Advance Doorbell bit in the USBCMD register + * | | |This status bit indicates the assertion of that interrupt source. + * |[12] |HCHalted |HCHalted (RO) + * | | |This bit is a zero whenever the Run/Stop bit is a one + * | | |The Host Controller sets this bit to one after it has stopped executing as a result of the Run/Stop bit being set to 0, either by software or by the Host Controller hardware (e.g. + * | | |internal error). + * |[13] |RECLA |Reclamation (RO) + * | | |This is a read-only status bit, which is used to detect an empty asynchronous schedule. + * |[14] |PSS |Periodic Schedule Status (RO) + * | | |The bit reports the current real status of the Periodic Schedule + * | | |If this bit is a zero then the status of the Periodic Schedule is disabled + * | | |If this bit is a one then the status of the Periodic Schedule is enabled + * | | |The Host Controller is not required to immediately disable or enable the Periodic Schedule when software transitions the Periodic Schedule Enable bit in the USBCMD register + * | | |When this bit and the Periodic Schedule Enable bit are the same value, the Periodic Schedule is either enabled (1) or disabled (0). + * |[15] |ASS |Asynchronous Schedule Status (RO) + * | | |The bit reports the current real status of the Asynchronous Schedule + * | | |If this bit is a zero then the status of them Asynchronous Schedule is disabled + * | | |If this bit is a one then the status of the Asynchronous Schedule is enabled + * | | |The Host Controller is not required to immediately disable or enable the Asynchronous Schedule when software transitions the Asynchronous Schedule Enable bit in the USBCMD register + * | | |When this bit and the Asynchronous Schedule Enable bit are the same value, the Asynchronous Schedule is either enabled (1) or disabled (0). + * @var HSUSBH_T::UIENR + * Offset: 0x28 USB Interrupt Enable Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[0] |USBIEN |USB Interrupt Enable or Disable Bit + * | | |When this bit is a one, and the USBINT bit in the USBSTS register is a one, the host controller will issue an interrupt at the next interrupt threshold + * | | |The interrupt is acknowledged by software clearing the USBINT bit. + * | | |0 = USB interrupt Disabled. + * | | |1 = USB interrupt Enabled. + * |[1] |UERRIEN |USB Error Interrupt Enable or Disable Bit + * | | |When this bit is a one, and the USBERRINT bit in the USBSTS register is a one, the host t controller will issue an interrupt at the next interrupt threshold + * | | |The interrupt is acknowledged by software clearing the USBERRINT bit. + * | | |0 = USB Error interrupt Disabled. + * | | |1 = USB Error interrupt Enabled. + * |[2] |PCIEN |Port Change Interrupt Enable or Disable Bit + * | | |When this bit is a one, and the Port Change Detect bit in the USBSTS register is a one, the host controller will issue an interrupt + * | | |The interrupt is acknowledged by software clearing the Port Change Detect bit. + * | | |0 = Port Change interrupt Disabled. + * | | |1 = Port Change interrupt Enabled. + * |[3] |FLREN |Frame List Rollover Enable or Disable Bit + * | | |When this bit is a one, and the Frame List Rollover bit in the USBSTS register is a one, the host controller will issue an interrupt + * | | |The interrupt is acknowledged by software clearing the Frame List Rollover bit. + * | | |0 = Frame List Rollover interrupt Disabled. + * | | |1 = Frame List Rollover interrupt Enabled. + * |[4] |HSERREN |Host System Error Enable or Disable Bit + * | | |When this bit is a one, and the Host System Error Status bit in the USBSTS register is a one, the host controller will issue an interrupt + * | | |The interrupt is acknowledged by software clearing the Host System Error bit. + * | | |0 = Host System Error interrupt Disabled. + * | | |1 = Host System Error interrupt Enabled. + * |[5] |IAAEN |Interrupt on Asynchronous Advance Enable or Disable Bit + * | | |When this bit is a one, and the Interrupt on Asynchronous Advance bit in the USBSTS register is a one, the host controller will issue an interrupt at the next interrupt threshold + * | | |The interrupt is acknowledged by software clearing the Interrupt on Asynchronous Advance bit. + * | | |0 = Interrupt on Asynchronous Advance Disabled. + * | | |1 = Interrupt on Asynchronous Advance Enabled. + * @var HSUSBH_T::UFINDR + * Offset: 0x2C USB Frame Index Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[13:0] |FI |Frame Index + * | | |The value in this register increment at the end of each time frame (e.g. + * | | |micro-frame) + * | | |Bits [N:3] are used for the Frame List current index + * | | |This means that each location of the frame list is accessed 8 times (frames or micro-frames) before moving to the next index + * | | |The following illustrates values of N based on the value of the Frame List Size field in the USBCMD register. + * | | |FLSZ (UCMDR[3:2] Number Elements N + * | | |0x0 1024 12 + * | | |0x1 512 11 + * | | |0x2 256 10 + * | | |0x3 Reserved + * @var HSUSBH_T::UPFLBAR + * Offset: 0x34 USB Periodic Frame List Base Address Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[31:12] |BADDR |Base Address + * | | |These bits correspond to memory address signals [31:12], respectively. + * @var HSUSBH_T::UCALAR + * Offset: 0x38 USB Current Asynchronous List Address Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[31:5] |LPL |Link Pointer Low (LPL) + * | | |These bits correspond to memory address signals [31:5], respectively + * | | |This field may only reference a Queue Head (QH). + * @var HSUSBH_T::UASSTR + * Offset: 0x3C USB Asynchronous Schedule Sleep Timer Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[11:0] |ASSTMR |Asynchronous Schedule Sleep Timer + * | | |This field defines the AsyncSchedSleepTime of EHCI spec. + * | | |The asynchronous schedule sleep timer is used to control how often the host controller fetches asynchronous schedule list from system memory while the asynchronous schedule is empty. + * | | |The default value of this timer is 12'hBD6 + * | | |Because this timer is implemented in UTMI clock (30MHz) domain, the default sleeping time will be about 100us. + * @var HSUSBH_T::UCFGR + * Offset: 0x60 USB Configure Flag Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[0] |CF |Configure Flag (CF) + * | | |Host software sets this bit as the last action in its process of configuring the Host Controller + * | | |This bit controls the default port-routing control logic + * | | |Bit values and side-effects are listed below. + * | | |0 = Port routing control logic default-routes each port to an implementation dependent classic host controller. + * | | |1 = Port routing control logic default-routes all ports to this host controller. + * @var HSUSBH_T::UPSCR[2] + * Offset: 0x64~0x68 USB Port 0~1 Status and Control Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[0] |CCS |Current Connect Status (RO) + * | | |This value reflects the current state of the port, and may not correspond directly to the event that caused the Connect Status Change bit (Bit 1) to be set. + * | | |This field is zero if Port Power is zero. + * | | |0 = No device is present. + * | | |1 = Device is present on port. + * |[1] |CSC |Connect Status Change (R/W) + * | | |Indicates a change has occurred in the port's Current Connect Status + * | | |The host controller sets this bit for all changes to the port device connect status, even if system software has not cleared an existing connect status change + * | | |For example, the insertion status changes twice before system software has cleared the changed condition, hub hardware will be "setting" an already-set bit (i.e., the bit will remain set).Software sets this bit to 0 by writing a 1 to it. + * | | |This field is zero if Port Power is zero. + * | | |0 = No change. + * | | |1 = Change in Current Connect Status. + * |[2] |PE |Port Enabled/Disabled (R/W) + * | | |Ports can only be enabled by the host controller as a part of the reset and enable + * | | |Software cannot enable a port by writing a one to this field + * | | |The host controller will only set this bit to a one when the reset sequence determines that the attached device is a high-speed device. + * | | |Ports can be disabled by either a fault condition (disconnect event or other fault condition) or by host software + * | | |Note that the bit status does not change until the port state actually changes + * | | |There may be a delay in disabling or enabling a port due to other host controller and bus events. + * | | |When the port is disabled (0b) downstream propagation of data is blocked on this port, except for reset. + * | | |This field is zero if Port Power is zero. + * | | |0 = Port Disabled. + * | | |1 = Port Enabled. + * |[3] |PEC |Port Enable/Disable Change (R/WC) + * | | |For the root hub, this bit gets set to a one only when a port is disabled due to the appropriate conditions existing at the EOF2 point (See Chapter 11 of the USB Specification for the definition of a Port Error) + * | | |Software clears this bit by writing a 1 to it. + * | | |This field is zero if Port Power is zero. + * | | |0 = No change. + * | | |1 = Port enabled/disabled status has changed. + * |[4] |OCA |Over-current Active (RO) + * | | |This bit will automatically transition from a one to a zero when the over current condition is removed. + * | | |0 = This port does not have an over-current condition. + * | | |1 = This port currently has an over-current condition. + * |[5] |OCC |Over-current Change (R/WC) + * | | |1 = This bit gets set to a one when there is a change to Over-current Active + * | | |Software clears this bit by writing a one to this bit position. + * |[6] |FPR |Force Port Resume (R/W) + * | | |This functionality defined for manipulating this bit depends on the value of the Suspend bit + * | | |For example, if the port is not suspended (Suspend and Enabled bits are a one) and software transitions this bit to a one, then the effects on the bus are undefined. + * | | |Software sets this bit to a 1 to drive resume signaling + * | | |The Host Controller sets this bit to a 1 if a J-to-K transition is detected while the port is in the Suspend state + * | | |When this bit transitions to a one because a J-to-K transition is detected, the Port Change Detect bit in the USBSTS register is also set to a one + * | | |If software sets this bit to a one, the host controller must not set the Port Change Detect bit. + * | | |Note that when the EHCI controller owns the port, the resume sequence follows the defined sequence documented in the USB Specification Revision 2.0 + * | | |The resume signaling (Full-speed 'K') is driven on the port as long as this bit remains a one + * | | |Software must appropriately time the Resume and set this bit to a zero when the appropriate amount of time has elapsed + * | | |Writing a zero (from one) causes the port to return to high-speed mode (forcing the bus below the port into a high-speed idle) + * | | |This bit will remain a one until the port has switched to the high-speed idle + * | | |The host controller must complete this transition within 2 milliseconds of software setting this bit to a zero. + * | | |This field is zero if Port Power is zero. + * | | |0 = No resume (K-state) detected/driven on port. + * | | |1 = Resume detected/driven on port. + * |[7] |SUSPEND |Suspend (R/W) + * | | |Port Enabled Bit and Suspend bit of this register define the port states as follows: + * | | |Port enable is 0 and suspend is 0 = Disable. + * | | |Port enable is 0 and suspend is 1 = Disable. + * | | |Port enable is 1 and suspend is 0 = Enable. + * | | |Port enable is 1 and suspend is 1 = Suspend. + * | | |When in suspend state, downstream propagation of data is blocked on this port, except for port reset + * | | |The blocking occurs at the end of the current transaction, if a transaction was in progress when this bit was written to 1 + * | | |In the suspend state, the port is sensitive to resume detection + * | | |Note that the bit status does not change until the port is suspended and that there may be a delay in suspending a port if there is a transaction currently in progress on the USB. + * | | |A write of zero to this bit is ignored by the host controller + * | | |The host controller will unconditionally set this bit to a zero when: + * | | |Software sets the Force Port Resume bit to a zero (from a one). + * | | |Software sets the Port Reset bit to a one (from a zero). + * | | |If host software sets this bit to a one when the port is not enabled (i.e. + * | | |Port enabled bit is a zero) the results are undefined. + * | | |This field is zero if Port Power is zero. + * | | |0 = Port not in suspend state. + * | | |1 = Port in suspend state. + * |[8] |PRST |Port Reset (R/W) + * | | |When software writes a one to this bit (from a zero), the bus reset sequence as defined in the USB Specification Revision 2.0 is started + * | | |Software writes a zero to this bit to terminate the bus reset sequence + * | | |Software must keep this bit at a one long enough to ensure the reset sequence, as specified in the USB Specification Revision 2.0, completes + * | | |Note: when software writes this bit to a one, it must also write a zero to the Port Enable bit. + * | | |Note that when software writes a zero to this bit there may be a delay before the bit status changes to a zero + * | | |The bit status will not read as a zero until after the reset has completed + * | | |If the port is in high-speed mode after reset is complete, the host controller will automatically enable this port (e.g. + * | | |set the Port Enable bit to a one) + * | | |A host controller must terminate the reset and stabilize the state of the port within 2 milliseconds of software transitioning this bit from a one to a zero + * | | |For example: if the port detects that the attached device is high-speed during reset, then the host controller must have the port in the enabled state within 2ms of software writing this bit to a zero. + * | | |The HCHalted bit in the USBSTS register should be a zero before software attempts to use this bit + * | | |The host controller may hold Port Reset asserted to a one when the HCHalted bit is a one. + * | | |This field is zero if Port Power is zero. + * | | |0 = Port is not in Reset. + * | | |1 = Port is in Reset. + * |[11:10] |LSTS |Line Status (RO) + * | | |These bits reflect the current logical levels of the D+ (bit 11) and D- (bit 10) signal lines + * | | |These bits are used for detection of low-speed USB devices prior to the port reset and enable sequence + * | | |This field is valid only when the port enable bit is zero and the current connect status bit is set to a one. + * | | |The encoding of the bits are: + * | | |Bits[11:10] USB State Interpretation + * | | |00 = SE0 Not Low-speed device, perform EHCI reset. + * | | |01 = K-state Low-speed device, release ownership of port. + * | | |10 = J-state Not Low-speed device, perform EHCI reset. + * | | |11 = Undefined Not Low-speed device, perform EHCI reset. + * | | |This value of this field is undefined if Port Power is zero. + * |[12] |PP |Port Power (PP) + * | | |Host controller has port power control switches + * | | |This bit represents the Current setting of the switch (0 = off, 1 = on) + * | | |When power is not available on a port (i.e. + * | | |PP equals a 0), the port is nonfunctional and will not report attaches, detaches, etc. + * | | |When an over-current condition is detected on a powered port and PPC is a one, the PP bit in each affected port may be transitioned by the host controller from a 1 to 0 (removing power from the port). + * |[13] |PO |Port Owner (R/W) + * | | |This bit unconditionally goes to a 0b when the Configured bit in the CONFIGFLAG register makes a 0 to 1 transition + * | | |This bit unconditionally goes to 1 whenever the Configured bit is zero. + * | | |System software uses this field to release ownership of the port to a selected host controller (in the event that the attached device is not a high-speed device) + * | | |Software writes a one to this bit when the attached device is not a high-speed device + * | | |A one in this bit means that a companion host controller owns and controls the port. + * |[19:16] |PTC |Port Test Control (R/W) + * | | |When this field is zero, the port is NOT operating in a test mode + * | | |A non-zero value indicates that it is operating in test mode and the specific test mode is indicated by the specific value + * | | |The encoding of the test mode bits are (0x6 ~ 0xF are reserved): + * | | |Bits Test Mode + * | | |0x0 = Test mode not enabled. + * | | |0x1 = Test J_STATE. + * | | |0x2 = Test K_STATE. + * | | |0x3 = Test SE0_NAK. + * | | |0x4 = Test Packet. + * | | |0x5 = Test FORCE_ENABLE. + * @var HSUSBH_T::USBPCR0 + * Offset: 0xC4 USB PHY 0 Control Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[8] |SUSPEND |Suspend Assertion + * | | |This bit controls the suspend mode of USB PHY 0. + * | | |While PHY was suspended, all circuits of PHY were powered down and outputs are tri-state. + * | | |This bit is 1'b0 in default + * | | |This means the USB PHY 0 is suspended in default + * | | |It is necessary to set this bit 1'b1 to make USB PHY 0 leave suspend mode before doing configuration of USB host. + * | | |0 = USB PHY 0 was suspended. + * | | |1 = USB PHY 0 was not suspended. + * |[11] |CLKVALID |UTMI Clock Valid + * | | |This bit is a flag to indicate if the UTMI clock from USB 2.0 PHY is ready + * | | |S/W program must prevent to write other control registers before this UTMI clock valid flag is active. + * | | |0 = UTMI clock is not valid. + * | | |1 = UTMI clock is valid. + * @var HSUSBH_T::USBPCR1 + * Offset: 0xC8 USB PHY 1 Control Register + * --------------------------------------------------------------------------------------------------- + * |Bits |Field |Descriptions + * | :----: | :----: | :---- | + * |[8] |SUSPEND |Suspend Assertion + * | | |This bit controls the suspend mode of USB PHY 1. + * | | |While PHY was suspended, all circuits of PHY were powered down and outputs are tri-state. + * | | |This bit is 1'b0 in default + * | | |This means the USB PHY 0 is suspended in default + * | | |It is necessary to set this bit 1'b1 to make USB PHY 0 leave suspend mode before doing configuration of USB host. + * | | |0 = USB PHY 1 was suspended. + * | | |1 = USB PHY 1 was not suspended. + */ + __I uint32_t EHCVNR; /*!< [0x0000] EHCI Version Number Register */ + __I uint32_t EHCSPR; /*!< [0x0004] EHCI Structural Parameters Register */ + __I uint32_t EHCCPR; /*!< [0x0008] EHCI Capability Parameters Register */ + __I uint32_t RESERVE0[5]; + __IO uint32_t UCMDR; /*!< [0x0020] USB Command Register */ + __IO uint32_t USTSR; /*!< [0x0024] USB Status Register */ + __IO uint32_t UIENR; /*!< [0x0028] USB Interrupt Enable Register */ + __IO uint32_t UFINDR; /*!< [0x002c] USB Frame Index Register */ + __I uint32_t RESERVE1[1]; + __IO uint32_t UPFLBAR; /*!< [0x0034] USB Periodic Frame List Base Address Register */ + __IO uint32_t UCALAR; /*!< [0x0038] USB Current Asynchronous List Address Register */ + __IO uint32_t UASSTR; /*!< [0x003c] USB Asynchronous Schedule Sleep Timer Register */ + __I uint32_t RESERVE2[8]; + __IO uint32_t UCFGR; /*!< [0x0060] USB Configure Flag Register */ + __IO uint32_t UPSCR[2]; /*!< [0x0064] ~ [0x0068] USB Port 0 & 1 Status and Control Register */ + __I uint32_t RESERVE3[22]; + __IO uint32_t USBPCR0; /*!< [0x00c4] USB PHY 0 Control Register */ + __IO uint32_t USBPCR1; /*!< [0x00c8] USB PHY 1 Control Register */ + +} HSUSBH_T; + +/** + @addtogroup HSUSBH_CONST HSUSBH Bit Field Definition + Constant Definitions for HSUSBH Controller +@{ */ + +#define HSUSBH_EHCVNR_CRLEN_Pos (0) /*!< HSUSBH_T::EHCVNR: CRLEN Position */ +#define HSUSBH_EHCVNR_CRLEN_Msk (0xfful << HSUSBH_EHCVNR_CRLEN_Pos) /*!< HSUSBH_T::EHCVNR: CRLEN Mask */ + +#define HSUSBH_EHCVNR_VERSION_Pos (16) /*!< HSUSBH_T::EHCVNR: VERSION Position */ +#define HSUSBH_EHCVNR_VERSION_Msk (0xfffful << HSUSBH_EHCVNR_VERSION_Pos) /*!< HSUSBH_T::EHCVNR: VERSION Mask */ + +#define HSUSBH_EHCSPR_N_PORTS_Pos (0) /*!< HSUSBH_T::EHCSPR: N_PORTS Position */ +#define HSUSBH_EHCSPR_N_PORTS_Msk (0xful << HSUSBH_EHCSPR_N_PORTS_Pos) /*!< HSUSBH_T::EHCSPR: N_PORTS Mask */ + +#define HSUSBH_EHCSPR_PPC_Pos (4) /*!< HSUSBH_T::EHCSPR: PPC Position */ +#define HSUSBH_EHCSPR_PPC_Msk (0x1ul << HSUSBH_EHCSPR_PPC_Pos) /*!< HSUSBH_T::EHCSPR: PPC Mask */ + +#define HSUSBH_EHCSPR_N_PCC_Pos (8) /*!< HSUSBH_T::EHCSPR: N_PCC Position */ +#define HSUSBH_EHCSPR_N_PCC_Msk (0xful << HSUSBH_EHCSPR_N_PCC_Pos) /*!< HSUSBH_T::EHCSPR: N_PCC Mask */ + +#define HSUSBH_EHCSPR_N_CC_Pos (12) /*!< HSUSBH_T::EHCSPR: N_CC Position */ +#define HSUSBH_EHCSPR_N_CC_Msk (0xful << HSUSBH_EHCSPR_N_CC_Pos) /*!< HSUSBH_T::EHCSPR: N_CC Mask */ + +#define HSUSBH_EHCCPR_AC64_Pos (0) /*!< HSUSBH_T::EHCCPR: AC64 Position */ +#define HSUSBH_EHCCPR_AC64_Msk (0x1ul << HSUSBH_EHCCPR_AC64_Pos) /*!< HSUSBH_T::EHCCPR: AC64 Mask */ + +#define HSUSBH_EHCCPR_PFLF_Pos (1) /*!< HSUSBH_T::EHCCPR: PFLF Position */ +#define HSUSBH_EHCCPR_PFLF_Msk (0x1ul << HSUSBH_EHCCPR_PFLF_Pos) /*!< HSUSBH_T::EHCCPR: PFLF Mask */ + +#define HSUSBH_EHCCPR_ASPC_Pos (2) /*!< HSUSBH_T::EHCCPR: ASPC Position */ +#define HSUSBH_EHCCPR_ASPC_Msk (0x1ul << HSUSBH_EHCCPR_ASPC_Pos) /*!< HSUSBH_T::EHCCPR: ASPC Mask */ + +#define HSUSBH_EHCCPR_IST_Pos (4) /*!< HSUSBH_T::EHCCPR: IST Position */ +#define HSUSBH_EHCCPR_IST_Msk (0xful << HSUSBH_EHCCPR_IST_Pos) /*!< HSUSBH_T::EHCCPR: IST Mask */ + +#define HSUSBH_EHCCPR_EECP_Pos (8) /*!< HSUSBH_T::EHCCPR: EECP Position */ +#define HSUSBH_EHCCPR_EECP_Msk (0xfful << HSUSBH_EHCCPR_EECP_Pos) /*!< HSUSBH_T::EHCCPR: EECP Mask */ + +#define HSUSBH_UCMDR_RUN_Pos (0) /*!< HSUSBH_T::UCMDR: RUN Position */ +#define HSUSBH_UCMDR_RUN_Msk (0x1ul << HSUSBH_UCMDR_RUN_Pos) /*!< HSUSBH_T::UCMDR: RUN Mask */ + +#define HSUSBH_UCMDR_HCRST_Pos (1) /*!< HSUSBH_T::UCMDR: HCRST Position */ +#define HSUSBH_UCMDR_HCRST_Msk (0x1ul << HSUSBH_UCMDR_HCRST_Pos) /*!< HSUSBH_T::UCMDR: HCRST Mask */ + +#define HSUSBH_UCMDR_FLSZ_Pos (2) /*!< HSUSBH_T::UCMDR: FLSZ Position */ +#define HSUSBH_UCMDR_FLSZ_Msk (0x3ul << HSUSBH_UCMDR_FLSZ_Pos) /*!< HSUSBH_T::UCMDR: FLSZ Mask */ + +#define HSUSBH_UCMDR_PSEN_Pos (4) /*!< HSUSBH_T::UCMDR: PSEN Position */ +#define HSUSBH_UCMDR_PSEN_Msk (0x1ul << HSUSBH_UCMDR_PSEN_Pos) /*!< HSUSBH_T::UCMDR: PSEN Mask */ + +#define HSUSBH_UCMDR_ASEN_Pos (5) /*!< HSUSBH_T::UCMDR: ASEN Position */ +#define HSUSBH_UCMDR_ASEN_Msk (0x1ul << HSUSBH_UCMDR_ASEN_Pos) /*!< HSUSBH_T::UCMDR: ASEN Mask */ + +#define HSUSBH_UCMDR_IAAD_Pos (6) /*!< HSUSBH_T::UCMDR: IAAD Position */ +#define HSUSBH_UCMDR_IAAD_Msk (0x1ul << HSUSBH_UCMDR_IAAD_Pos) /*!< HSUSBH_T::UCMDR: IAAD Mask */ + +#define HSUSBH_UCMDR_ITC_Pos (16) /*!< HSUSBH_T::UCMDR: ITC Position */ +#define HSUSBH_UCMDR_ITC_Msk (0xfful << HSUSBH_UCMDR_ITC_Pos) /*!< HSUSBH_T::UCMDR: ITC Mask */ + +#define HSUSBH_USTSR_USBINT_Pos (0) /*!< HSUSBH_T::USTSR: USBINT Position */ +#define HSUSBH_USTSR_USBINT_Msk (0x1ul << HSUSBH_USTSR_USBINT_Pos) /*!< HSUSBH_T::USTSR: USBINT Mask */ + +#define HSUSBH_USTSR_UERRINT_Pos (1) /*!< HSUSBH_T::USTSR: UERRINT Position */ +#define HSUSBH_USTSR_UERRINT_Msk (0x1ul << HSUSBH_USTSR_UERRINT_Pos) /*!< HSUSBH_T::USTSR: UERRINT Mask */ + +#define HSUSBH_USTSR_PCD_Pos (2) /*!< HSUSBH_T::USTSR: PCD Position */ +#define HSUSBH_USTSR_PCD_Msk (0x1ul << HSUSBH_USTSR_PCD_Pos) /*!< HSUSBH_T::USTSR: PCD Mask */ + +#define HSUSBH_USTSR_FLR_Pos (3) /*!< HSUSBH_T::USTSR: FLR Position */ +#define HSUSBH_USTSR_FLR_Msk (0x1ul << HSUSBH_USTSR_FLR_Pos) /*!< HSUSBH_T::USTSR: FLR Mask */ + +#define HSUSBH_USTSR_HSERR_Pos (4) /*!< HSUSBH_T::USTSR: HSERR Position */ +#define HSUSBH_USTSR_HSERR_Msk (0x1ul << HSUSBH_USTSR_HSERR_Pos) /*!< HSUSBH_T::USTSR: HSERR Mask */ + +#define HSUSBH_USTSR_IAA_Pos (5) /*!< HSUSBH_T::USTSR: IAA Position */ +#define HSUSBH_USTSR_IAA_Msk (0x1ul << HSUSBH_USTSR_IAA_Pos) /*!< HSUSBH_T::USTSR: IAA Mask */ + +#define HSUSBH_USTSR_HCHalted_Pos (12) /*!< HSUSBH_T::USTSR: HCHalted Position */ +#define HSUSBH_USTSR_HCHalted_Msk (0x1ul << HSUSBH_USTSR_HCHalted_Pos) /*!< HSUSBH_T::USTSR: HCHalted Mask */ + +#define HSUSBH_USTSR_RECLA_Pos (13) /*!< HSUSBH_T::USTSR: RECLA Position */ +#define HSUSBH_USTSR_RECLA_Msk (0x1ul << HSUSBH_USTSR_RECLA_Pos) /*!< HSUSBH_T::USTSR: RECLA Mask */ + +#define HSUSBH_USTSR_PSS_Pos (14) /*!< HSUSBH_T::USTSR: PSS Position */ +#define HSUSBH_USTSR_PSS_Msk (0x1ul << HSUSBH_USTSR_PSS_Pos) /*!< HSUSBH_T::USTSR: PSS Mask */ + +#define HSUSBH_USTSR_ASS_Pos (15) /*!< HSUSBH_T::USTSR: ASS Position */ +#define HSUSBH_USTSR_ASS_Msk (0x1ul << HSUSBH_USTSR_ASS_Pos) /*!< HSUSBH_T::USTSR: ASS Mask */ + +#define HSUSBH_UIENR_USBIEN_Pos (0) /*!< HSUSBH_T::UIENR: USBIEN Position */ +#define HSUSBH_UIENR_USBIEN_Msk (0x1ul << HSUSBH_UIENR_USBIEN_Pos) /*!< HSUSBH_T::UIENR: USBIEN Mask */ + +#define HSUSBH_UIENR_UERRIEN_Pos (1) /*!< HSUSBH_T::UIENR: UERRIEN Position */ +#define HSUSBH_UIENR_UERRIEN_Msk (0x1ul << HSUSBH_UIENR_UERRIEN_Pos) /*!< HSUSBH_T::UIENR: UERRIEN Mask */ + +#define HSUSBH_UIENR_PCIEN_Pos (2) /*!< HSUSBH_T::UIENR: PCIEN Position */ +#define HSUSBH_UIENR_PCIEN_Msk (0x1ul << HSUSBH_UIENR_PCIEN_Pos) /*!< HSUSBH_T::UIENR: PCIEN Mask */ + +#define HSUSBH_UIENR_FLREN_Pos (3) /*!< HSUSBH_T::UIENR: FLREN Position */ +#define HSUSBH_UIENR_FLREN_Msk (0x1ul << HSUSBH_UIENR_FLREN_Pos) /*!< HSUSBH_T::UIENR: FLREN Mask */ + +#define HSUSBH_UIENR_HSERREN_Pos (4) /*!< HSUSBH_T::UIENR: HSERREN Position */ +#define HSUSBH_UIENR_HSERREN_Msk (0x1ul << HSUSBH_UIENR_HSERREN_Pos) /*!< HSUSBH_T::UIENR: HSERREN Mask */ + +#define HSUSBH_UIENR_IAAEN_Pos (5) /*!< HSUSBH_T::UIENR: IAAEN Position */ +#define HSUSBH_UIENR_IAAEN_Msk (0x1ul << HSUSBH_UIENR_IAAEN_Pos) /*!< HSUSBH_T::UIENR: IAAEN Mask */ + +#define HSUSBH_UFINDR_FI_Pos (0) /*!< HSUSBH_T::UFINDR: FI Position */ +#define HSUSBH_UFINDR_FI_Msk (0x3ffful << HSUSBH_UFINDR_FI_Pos) /*!< HSUSBH_T::UFINDR: FI Mask */ + +#define HSUSBH_UPFLBAR_BADDR_Pos (12) /*!< HSUSBH_T::UPFLBAR: BADDR Position */ +#define HSUSBH_UPFLBAR_BADDR_Msk (0xffffful << HSUSBH_UPFLBAR_BADDR_Pos) /*!< HSUSBH_T::UPFLBAR: BADDR Mask */ + +#define HSUSBH_UCALAR_LPL_Pos (5) /*!< HSUSBH_T::UCALAR: LPL Position */ +#define HSUSBH_UCALAR_LPL_Msk (0x7fffffful << HSUSBH_UCALAR_LPL_Pos) /*!< HSUSBH_T::UCALAR: LPL Mask */ + +#define HSUSBH_UASSTR_ASSTMR_Pos (0) /*!< HSUSBH_T::UASSTR: ASSTMR Position */ +#define HSUSBH_UASSTR_ASSTMR_Msk (0xffful << HSUSBH_UASSTR_ASSTMR_Pos) /*!< HSUSBH_T::UASSTR: ASSTMR Mask */ + +#define HSUSBH_UCFGR_CF_Pos (0) /*!< HSUSBH_T::UCFGR: CF Position */ +#define HSUSBH_UCFGR_CF_Msk (0x1ul << HSUSBH_UCFGR_CF_Pos) /*!< HSUSBH_T::UCFGR: CF Mask */ + +#define HSUSBH_UPSCR_CCS_Pos (0) /*!< HSUSBH_T::UPSCR[2]: CCS Position */ +#define HSUSBH_UPSCR_CCS_Msk (0x1ul << HSUSBH_UPSCR_CCS_Pos) /*!< HSUSBH_T::UPSCR[2]: CCS Mask */ + +#define HSUSBH_UPSCR_CSC_Pos (1) /*!< HSUSBH_T::UPSCR[2]: CSC Position */ +#define HSUSBH_UPSCR_CSC_Msk (0x1ul << HSUSBH_UPSCR_CSC_Pos) /*!< HSUSBH_T::UPSCR[2]: CSC Mask */ + +#define HSUSBH_UPSCR_PE_Pos (2) /*!< HSUSBH_T::UPSCR[2]: PE Position */ +#define HSUSBH_UPSCR_PE_Msk (0x1ul << HSUSBH_UPSCR_PE_Pos) /*!< HSUSBH_T::UPSCR[2]: PE Mask */ + +#define HSUSBH_UPSCR_PEC_Pos (3) /*!< HSUSBH_T::UPSCR[2]: PEC Position */ +#define HSUSBH_UPSCR_PEC_Msk (0x1ul << HSUSBH_UPSCR_PEC_Pos) /*!< HSUSBH_T::UPSCR[2]: PEC Mask */ + +#define HSUSBH_UPSCR_OCA_Pos (4) /*!< HSUSBH_T::UPSCR[2]: OCA Position */ +#define HSUSBH_UPSCR_OCA_Msk (0x1ul << HSUSBH_UPSCR_OCA_Pos) /*!< HSUSBH_T::UPSCR[2]: OCA Mask */ + +#define HSUSBH_UPSCR_OCC_Pos (5) /*!< HSUSBH_T::UPSCR[2]: OCC Position */ +#define HSUSBH_UPSCR_OCC_Msk (0x1ul << HSUSBH_UPSCR_OCC_Pos) /*!< HSUSBH_T::UPSCR[2]: OCC Mask */ + +#define HSUSBH_UPSCR_FPR_Pos (6) /*!< HSUSBH_T::UPSCR[2]: FPR Position */ +#define HSUSBH_UPSCR_FPR_Msk (0x1ul << HSUSBH_UPSCR_FPR_Pos) /*!< HSUSBH_T::UPSCR[2]: FPR Mask */ + +#define HSUSBH_UPSCR_SUSPEND_Pos (7) /*!< HSUSBH_T::UPSCR[2]: SUSPEND Position */ +#define HSUSBH_UPSCR_SUSPEND_Msk (0x1ul << HSUSBH_UPSCR_SUSPEND_Pos) /*!< HSUSBH_T::UPSCR[2]: SUSPEND Mask */ + +#define HSUSBH_UPSCR_PRST_Pos (8) /*!< HSUSBH_T::UPSCR[2]: PRST Position */ +#define HSUSBH_UPSCR_PRST_Msk (0x1ul << HSUSBH_UPSCR_PRST_Pos) /*!< HSUSBH_T::UPSCR[2]: PRST Mask */ + +#define HSUSBH_UPSCR_LSTS_Pos (10) /*!< HSUSBH_T::UPSCR[2]: LSTS Position */ +#define HSUSBH_UPSCR_LSTS_Msk (0x3ul << HSUSBH_UPSCR_LSTS_Pos) /*!< HSUSBH_T::UPSCR[2]: LSTS Mask */ + +#define HSUSBH_UPSCR_PP_Pos (12) /*!< HSUSBH_T::UPSCR[2]: PP Position */ +#define HSUSBH_UPSCR_PP_Msk (0x1ul << HSUSBH_UPSCR_PP_Pos) /*!< HSUSBH_T::UPSCR[2]: PP Mask */ + +#define HSUSBH_UPSCR_PO_Pos (13) /*!< HSUSBH_T::UPSCR[2]: PO Position */ +#define HSUSBH_UPSCR_PO_Msk (0x1ul << HSUSBH_UPSCR_PO_Pos) /*!< HSUSBH_T::UPSCR[2]: PO Mask */ + +#define HSUSBH_UPSCR_PTC_Pos (16) /*!< HSUSBH_T::UPSCR[2]: PTC Position */ +#define HSUSBH_UPSCR_PTC_Msk (0xful << HSUSBH_UPSCR_PTC_Pos) /*!< HSUSBH_T::UPSCR[2]: PTC Mask */ + +#define HSUSBH_USBPCR0_SUSPEND_Pos (8) /*!< HSUSBH_T::USBPCR0: SUSPEND Position */ +#define HSUSBH_USBPCR0_SUSPEND_Msk (0x1ul << HSUSBH_USBPCR0_SUSPEND_Pos) /*!< HSUSBH_T::USBPCR0: SUSPEND Mask */ + +#define HSUSBH_USBPCR0_CLKVALID_Pos (11) /*!< HSUSBH_T::USBPCR0: CLKVALID Position */ +#define HSUSBH_USBPCR0_CLKVALID_Msk (0x1ul << HSUSBH_USBPCR0_CLKVALID_Pos) /*!< HSUSBH_T::USBPCR0: CLKVALID Mask */ + +#define HSUSBH_USBPCR1_SUSPEND_Pos (8) /*!< HSUSBH_T::USBPCR1: SUSPEND Position */ +#define HSUSBH_USBPCR1_SUSPEND_Msk (0x1ul << HSUSBH_USBPCR1_SUSPEND_Pos) /*!< HSUSBH_T::USBPCR1: SUSPEND Mask */ + +/**@}*/ /* HSUSBH_CONST */ +/**@}*/ /* end of HSUSBH register group */ + +#define USBH ((USBH_T *)0xB0017000) +#define HSUSBH ((HSUSBH_T *)0xB0015000) + +/*----------------------------------------------------------------------------------------*/ +/* Select USBH Lite multi-function pin */ +/*----------------------------------------------------------------------------------------*/ +#define USBHL0_MFP_DP_B4 outpw(REG_SYS_GPB_MFPL, ((inpw(REG_SYS_GPB_MFPL) & 0xFFF0FFFF) | 0x00040000)) +#define USBHL0_MFP_DM_B6 outpw(REG_SYS_GPB_MFPL, ((inpw(REG_SYS_GPB_MFPL) & 0xF0FFFFFF) | 0x04000000)) +#define USBHL0_MFP_DP_B5 outpw(REG_SYS_GPB_MFPL, ((inpw(REG_SYS_GPB_MFPL) & 0xFF0FFFFF) | 0x00400000)) +#define USBHL0_MFP_DM_B7 outpw(REG_SYS_GPB_MFPL, ((inpw(REG_SYS_GPB_MFPL) & 0x0FFFFFFF) | 0x40000000)) +#define USBHL0_MFP_DP_B10 outpw(REG_SYS_GPB_MFPH, ((inpw(REG_SYS_GPB_MFPH) & 0xFFFFF0FF) | 0x00000400)) +#define USBHL0_MFP_DM_B9 outpw(REG_SYS_GPB_MFPH, ((inpw(REG_SYS_GPB_MFPH) & 0xFFFFFF0F) | 0x00000040)) +#define USBHL0_MFP_DP_D15 outpw(REG_SYS_GPD_MFPH, ((inpw(REG_SYS_GPD_MFPH) & 0x0FFFFFFF) | 0x50000000)) +#define USBHL0_MFP_DM_D14 outpw(REG_SYS_GPD_MFPH, ((inpw(REG_SYS_GPD_MFPH) & 0xF0FFFFFF) | 0x05000000)) + +#define USBHL1_MFP_DP_F1 outpw(REG_SYS_GPF_MFPL, ((inpw(REG_SYS_GPF_MFPL) & 0xFFFFFF0F) | 0x00000060)) +#define USBHL1_MFP_DM_F0 outpw(REG_SYS_GPF_MFPL, ((inpw(REG_SYS_GPF_MFPL) & 0xFFFFFFF0) | 0x00000006)) +#define USBHL1_MFP_DP_E1 outpw(REG_SYS_GPE_MFPL, ((inpw(REG_SYS_GPE_MFPL) & 0xFFFFFF0F) | 0x00000060)) +#define USBHL1_MFP_DM_E0 outpw(REG_SYS_GPE_MFPL, ((inpw(REG_SYS_GPE_MFPL) & 0xFFFFFFF0) | 0x00000006)) + +#define USBHL2_MFP_DP_F3 outpw(REG_SYS_GPF_MFPL, ((inpw(REG_SYS_GPF_MFPL) & 0xFFFF0FFF) | 0x00006000)) +#define USBHL2_MFP_DM_F2 outpw(REG_SYS_GPF_MFPL, ((inpw(REG_SYS_GPF_MFPL) & 0xFFFFF0FF) | 0x00000600)) +#define USBHL2_MFP_DP_E3 outpw(REG_SYS_GPE_MFPL, ((inpw(REG_SYS_GPE_MFPL) & 0xFFFF0FFF) | 0x00006000)) +#define USBHL2_MFP_DM_E2 outpw(REG_SYS_GPE_MFPL, ((inpw(REG_SYS_GPE_MFPL) & 0xFFFFF0FF) | 0x00000600)) + +#define USBHL3_MFP_DP_F5 outpw(REG_SYS_GPF_MFPL, ((inpw(REG_SYS_GPF_MFPL) & 0xFF0FFFFF) | 0x00600000)) +#define USBHL3_MFP_DM_F4 outpw(REG_SYS_GPF_MFPL, ((inpw(REG_SYS_GPF_MFPL) & 0xFFF0FFFF) | 0x00060000)) +#define USBHL3_MFP_DP_E5 outpw(REG_SYS_GPE_MFPL, ((inpw(REG_SYS_GPE_MFPL) & 0xFF0FFFFF) | 0x00600000)) +#define USBHL3_MFP_DM_E4 outpw(REG_SYS_GPE_MFPL, ((inpw(REG_SYS_GPE_MFPL) & 0xFFF0FFFF) | 0x00060000)) + +#define USBHL4_MFP_DP_E7 outpw(REG_SYS_GPE_MFPL, ((inpw(REG_SYS_GPE_MFPL) & 0x0FFFFFFF) | 0x60000000)) +#define USBHL4_MFP_DM_E6 outpw(REG_SYS_GPE_MFPL, ((inpw(REG_SYS_GPE_MFPL) & 0xF0FFFFFF) | 0x06000000)) +#define USBHL4_MFP_DP_G10 outpw(REG_SYS_GPG_MFPH, ((inpw(REG_SYS_GPG_MFPH) & 0xFFFFF0FF) | 0x00000400)) +#define USBHL4_MFP_DM_A15 outpw(REG_SYS_GPA_MFPH, ((inpw(REG_SYS_GPA_MFPH) & 0x0FFFFFFF) | 0x40000000)) +#define USBHL4_MFP_DP_B13 outpw(REG_SYS_GPB_MFPH, ((inpw(REG_SYS_GPB_MFPH) & 0xFF0FFFFF) | 0x00600000)) +#define USBHL4_MFP_DM_F6 outpw(REG_SYS_GPF_MFPL, ((inpw(REG_SYS_GPF_MFPL) & 0xF0FFFFFF) | 0x06000000)) +#define USBHL4_MFP_DP_F7 outpw(REG_SYS_GPF_MFPL, ((inpw(REG_SYS_GPF_MFPL) & 0x0FFFFFFF) | 0x60000000)) + +#define USBHL5_MFP_DP_F9 outpw(REG_SYS_GPF_MFPH, ((inpw(REG_SYS_GPF_MFPH) & 0xFFFFFF0F) | 0x00000060)) +#define USBHL5_MFP_DM_F8 outpw(REG_SYS_GPF_MFPH, ((inpw(REG_SYS_GPF_MFPH) & 0xFFFFFFF0) | 0x00000006)) +#define USBHL5_MFP_DP_E9 outpw(REG_SYS_GPE_MFPH, ((inpw(REG_SYS_GPE_MFPH) & 0xFFFFFF0F) | 0x00000060)) +#define USBHL5_MFP_DM_E8 outpw(REG_SYS_GPE_MFPH, ((inpw(REG_SYS_GPE_MFPH) & 0xFFFFFFF0) | 0x00000006)) +#define USBHL5_MFP_DP_A14 outpw(REG_SYS_GPA_MFPH, ((inpw(REG_SYS_GPA_MFPH) & 0xF0FFFFFF) | 0x04000000)) +#define USBHL5_MFP_DM_A13 outpw(REG_SYS_GPA_MFPH, ((inpw(REG_SYS_GPA_MFPH) & 0xFF0FFFFF) | 0x00400000)) +#define USBHL5_MFP_DP_B12 outpw(REG_SYS_GPB_MFPH, ((inpw(REG_SYS_GPB_MFPH) & 0xFFF0FFFF) | 0x00040000)) +#define USBHL5_MFP_DM_B11 outpw(REG_SYS_GPB_MFPH, ((inpw(REG_SYS_GPB_MFPH) & 0xFFFF0FFF) | 0x00004000)) + +/// @endcond HIDDEN_SYMBOLS + +#endif /* _USBH_CONFIG_H_ */ + +/*** (C) COPYRIGHT 2019 Nuvoton Technology Corp. ***/ + diff --git a/bsp/nuvoton/libraries/nuc980/UsbHostLib/inc/ehci.h b/bsp/nuvoton/libraries/nuc980/UsbHostLib/inc/ehci.h new file mode 100644 index 0000000000..4d09ed9b8f --- /dev/null +++ b/bsp/nuvoton/libraries/nuc980/UsbHostLib/inc/ehci.h @@ -0,0 +1,279 @@ +/**************************************************************************//** + * @file ehci.h + * @version V1.00 + * @brief USB EHCI host controller driver header file. + * @note + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2017 Nuvoton Technology Corp. All rights reserved. + *****************************************************************************/ + +#ifndef _USBH_EHCI_H_ +#define _USBH_EHCI_H_ + +/// @cond HIDDEN_SYMBOLS + +struct utr_t; +struct udev_t; +struct qh_t; +struct iso_ep_t; +struct ep_info_t; + +/*----------------------------------------------------------------------------------------*/ +/* Periodic Frame List Size (256, 512, or 1024) */ +/*----------------------------------------------------------------------------------------*/ +#define FL_SIZE 1024 /* frame list size can be 256, 512, or 1024 */ +#define NUM_IQH 11 /* depends on FL_SIZE, 256:9, 512:10, 1024:11 */ + + +/*----------------------------------------------------------------------------------------*/ +/* Interrupt Threshold Control (1, 2, 4, 6, .. 64) */ +/*----------------------------------------------------------------------------------------*/ +#define UCMDR_INT_THR_CTRL (0x1< of QH */ +} qTD_T; + + +#define QTD_LIST_END 0x1 /* Indicate the terminate of qTD list. */ +#define QTD_PTR(x) ((qTD_T *)((uint32_t)(x) & ~0x1F)) + +/* + * Status: qTD Token[7:0] + */ +#define QTD_STS_PS_OUT (0<<0) /* directs the HC to issue an OUT PID */ +#define QTD_STS_PS_PING (1<<0) /* directs the HC to issue an PING PID */ +#define QTD_STS_SPLIT_STRAT (0<<1) /* directs the HC to issue an Start split */ +#define QTD_STS_SPLIT_COMPLETE (1<<1) /* directs the HC to issue an Complete split */ +#define QTD_STS_MISS_MF (1<<2) /* miss a required complete-split transaction */ +#define QTD_STS_XactErr (1<<3) /* Transaction Error occurred */ +#define QTD_STS_BABBLE (1<<4) /* Babble Detected */ +#define QTD_STS_DATA_BUFF_ERR (1<<5) /* Data Buffer Error */ +#define QTD_STS_HALT (1<<6) /* Halted */ +#define QTD_STS_ACTIVE (1<<7) /* Active */ + +/* + * PID: qTD Token[9:8] + */ +#define QTD_PID_Msk (0x3<<8) +#define QTD_PID_OUT (0<<8) /* generates token (E1H) */ +#define QTD_PID_IN (1<<8) /* generates token (69H) */ +#define QTD_PID_SETUP (2<<8) /* generates token (2DH) */ + +#define QTD_ERR_COUNTER (3<<10) /* Token[11:10] */ +#define QTD_IOC (1<<15) /* Token[15] - Interrupt On Complete */ +#define QTD_TODO_LEN_Pos 16 /* Token[31:16] - Total Bytes to Transfer */ +#define QTD_TODO_LEN(x) (((x)>>16) & 0x7FFF) +#define QTD_DT (1UL<<31) /* Token[31] - Data Toggle */ + +/*----------------------------------------------------------------------------------------*/ +/* Queue Head (QH) */ +/*----------------------------------------------------------------------------------------*/ +typedef struct qh_t +{ + /* OHCI spec. Endpoint descriptor */ + uint32_t HLink; /* Queue Head Horizontal Link Pointer */ + uint32_t Chrst; /* Endpoint Characteristics: QH DWord 1 */ + uint32_t Cap; /* Endpoint Capabilities: QH DWord 2 */ + uint32_t Curr_qTD; /* Current qTD Pointer */ + /* + * The followings are qTD Transfer Overlay + */ + uint32_t OL_Next_qTD; /* Next qTD Pointer */ + uint32_t OL_Alt_Next_qTD; /* Alternate Next qTD Pointer */ + uint32_t OL_Token; /* qTD Token */ + uint32_t OL_Bptr[5]; /* qTD Buffer Page Pointer List */ + /* + * The following members are used by USB Host libary. + */ + qTD_T *qtd_list; /* currently linked qTD transfers */ + qTD_T *done_list; /* currently linked qTD transfers */ + struct qh_t *next; /* point to the next QH in remove list */ +} QH_T; + +/* HLink[0] T field of "Queue Head Horizontal Link Pointer" */ +#define QH_HLNK_END 0x1 + +/* + * HLink[2:1] Typ field of "Queue Head Horizontal Link Pointer" + */ +#define QH_HLNK_ITD(x) (((uint32_t)(x) & ~0x1F) | 0x0) +#define QH_HLNK_QH(x) (((uint32_t)(x) & ~0x1F) | 0x2) +#define QH_HLNK_SITD(x) (((uint32_t)(x) & ~0x1F) | 0x4) +#define QH_HLNK_FSTN(x) (((uint32_t)(x) & ~0x1F) | 0x6) +#define QH_PTR(x) ((QH_T *)((uint32_t)(x) & ~0x1F)) + +/* + * Bit fields of "Endpoint Characteristics" + */ +#define QH_NAK_RL (4L<<28) /* Chrst[31:28] - NAK Count Reload */ +#define QH_CTRL_EP_FLAG (1<<27) /* Chrst[27] - Control Endpoint Flag */ +#define QH_RCLM_LIST_HEAD (1<<15) /* Chrst[15] - Head of Reclamation List Flag */ +#define QH_DTC (1<<14) /* Chrst[14] - Data Toggle Control */ +#define QH_EPS_FULL (0<<12) /* Chrst[13:12] - Endpoint Speed (Full) */ +#define QH_EPS_LOW (1<<12) /* Chrst[13:12] - Endpoint Speed (Low) */ +#define QH_EPS_HIGH (2<<12) /* Chrst[13:12] - Endpoint Speed (High) */ +#define QH_I_NEXT (1<<7) /* Chrst[7] - Inactivate on Next Transaction */ + +/* + * Bit fields of "Endpoint Capabilities" + */ +#define QH_MULT_Pos 30 /* Cap[31:30] - High-Bandwidth Pipe Multiplier */ +#define QH_HUB_PORT_Pos 23 /* Cap[29:23] - Hub Port Number */ +#define QH_HUB_ADDR_Pos 16 /* Cap[22:16] - Hub Addr */ +#define QH_C_MASK_Msk 0xFF00 /* Cap[15:8] - uFrame C-mask */ +#define QH_S_MASK_Msk 0x00FF /* Cap[7:0] - uFrame S-mask */ + + +/*----------------------------------------------------------------------------------------*/ +/* Isochronous (High-Speed) Transfer Descriptor (iTD) */ +/*----------------------------------------------------------------------------------------*/ +typedef struct itd_t +{ + uint32_t Next_Link; /* Next Link Pointer */ + uint32_t Transaction[8]; /* Transaction Status and Control */ + uint32_t Bptr[7]; /* Buffer Page Pointer List */ + /* + * The following members are used by USB Host libary. + */ + struct iso_ep_t *iso_ep; /* associated isochronous information block */ + struct utr_t *utr; /* associated UTR */ + uint32_t buff_base; /* buffer base address */ + uint8_t fidx; /* iTD's first index to UTR iso frames */ + uint8_t trans_mask; /* mask of activated transactions in iTD */ + uint32_t sched_frnidx; /* scheduled frame index */ + struct itd_t *next; /* used by software to maintain iTD list */ +} iTD_T; + +/* + * Next_Link[2:1] Typ field of "Next Schedule Element Pointer" Typ field + */ +#define ITD_HLNK_ITD(x) (((uint32_t)(x) & ~0x1F) | 0x0) +#define ITD_HLNK_QH(x) (((uint32_t)(x) & ~0x1F) | 0x2) +#define ITD_HLNK_SITD(x) (((uint32_t)(x) & ~0x1F) | 0x4) +#define ITD_HLNK_FSTN(x) (((uint32_t)(x) & ~0x1F) | 0x6) +#define ITD_PTR(x) ((iTD_T *)((uint32_t)(x) & ~0x1F)) + +/* + * Transaction[8] + */ +#define ITD_STATUS(x) (((x)>>28)&0xF) +#define ITD_STATUS_ACTIVE (0x80000000UL) /* Active */ +#define ITD_STATUS_BUFF_ERR (0x40000000UL) /* Data Buffer Error */ +#define ITD_STATUS_BABBLE (0x20000000UL) /* Babble Detected */ +#define ITD_STATUS_XACT_ERR (0x10000000UL) /* Transcation Error */ + +#define ITD_XLEN_Pos 16 +#define ITD_XFER_LEN(x) (((x)>>16)&0xFFF) +#define ITD_IOC (1<<15) +#define ITD_PG_Pos 12 +#define ITD_XFER_OFF_Msk 0xFFF + +/* + * Bptr[7] + */ +#define ITD_BUFF_PAGE_Pos 12 +/* Bptr[0] */ +#define ITD_EP_NUM_Pos 8 +#define ITD_EP_NUM(itd) (((itd)->Bptr[0]>>8)&0xF) +#define ITD_DEV_ADDR_Pos 0 +#define ITD_DEV_ADDR(itd) ((itd)->Bptr[0]&0x7F) +/* Bptr[1] */ +#define ITD_DIR_IN (1<<11) +#define ITD_DIR_OUT (0<<11) +#define ITD_MAX_PKTSZ_Pos 0 +#define ITD_MAX_PKTSZ(itd) ((itd)->Bptr[1]&0x7FF) + +/*----------------------------------------------------------------------------------------*/ +/* Split Isochronous (Full-Speed) Transfer Descriptor (siTD) */ +/*----------------------------------------------------------------------------------------*/ +typedef struct sitd_t +{ + uint32_t Next_Link; /* Next Link Pointer */ + uint32_t Chrst; /* Endpoint and Transaction Translator Characteristics */ + uint32_t Sched; /* Micro-frame Schedule Control */ + uint32_t StsCtrl; /* siTD Transfer Status and Control */ + uint32_t Bptr[2]; /* Buffer Page Pointer List */ + uint32_t BackLink; /* siTD Back Link Pointer */ + /* + * The following members are used by USB Host libary. + */ + struct iso_ep_t *iso_ep; /* associated isochronous information block */ + struct utr_t *utr; /* associated UTR */ + uint8_t fidx; /* iTD's first index to UTR iso frames */ + uint32_t sched_frnidx; /* scheduled frame index */ + struct sitd_t *next; /* used by software to maintain siTD list */ +} siTD_T; + +#define SITD_LIST_END 0x1 /* Indicate the terminate of siTD list. */ + +#define SITD_XFER_IO_Msk (1UL<<31) +#define SITD_XFER_IN (1UL<<31) +#define SITD_XFER_OUT (0UL<<31) + +#define SITD_PORT_NUM_Pos 24 +#define SITD_HUB_ADDR_Pos 16 +#define SITD_EP_NUM_Pos 8 +#define SITD_DEV_ADDR_Pos 0 + +#define SITD_IOC (1UL<<31) +#define SITD_XFER_CNT_Pos 16 +#define SITD_XFER_CNT_Msk (0x3FF<>28) & 0x0F) +#define TD_CC_SET(td, cc) (td) = ((td) & 0x0FFFFFFF) | (((cc) & 0x0F) << 28) +#define TD_T_DATA0 0x02000000 +#define TD_T_DATA1 0x03000000 +#define TD_R 0x00040000 +#define TD_DP 0x00180000 +#define TD_DP_IN 0x00100000 +#define TD_DP_OUT 0x00080000 +#define MAXPSW 8 +/* steel TD reserved bits to keep driver data */ +#define TD_TYPE_Msk (0x3<<16) +#define TD_TYPE_CTRL (0x0<<16) +#define TD_TYPE_BULK (0x1<<16) +#define TD_TYPE_INT (0x2<<16) +#define TD_TYPE_ISO (0x3<<16) +#define TD_CTRL_Msk (0x7<<15) +#define TD_CTRL_DATA (1<<15) + + +/* + * The HCCA (Host Controller Communications Area) is a 256 byte + * structure defined in the OHCI spec. that the host controller is + * told the base address of. It must be 256-byte aligned. + */ +typedef struct +{ + uint32_t int_table[32]; /* Interrupt ED table */ + uint16_t frame_no; /* current frame number */ + uint16_t pad1; /* set to 0 on each frame_no change */ + uint32_t done_head; /* info returned for an interrupt */ + uint8_t reserved_for_hc[116]; +} HCCA_T; + + +/// @endcond + +#endif /* _USBH_OHCI_H_ */ diff --git a/bsp/nuvoton/libraries/nuc980/UsbHostLib/inc/usb.h b/bsp/nuvoton/libraries/nuc980/UsbHostLib/inc/usb.h new file mode 100644 index 0000000000..4f99d4e6c9 --- /dev/null +++ b/bsp/nuvoton/libraries/nuc980/UsbHostLib/inc/usb.h @@ -0,0 +1,394 @@ +/**************************************************************************//** + * @file usb.h + * @version V1.00 + * @brief USB Host library header file. + * @note + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2017 Nuvoton Technology Corp. All rights reserved. + *****************************************************************************/ + +#ifndef _USBH_H_ +#define _USBH_H_ + +#include "config.h" +#include "usbh_lib.h" +#include "ehci.h" +#include "ohci.h" + +/// @cond HIDDEN_SYMBOLS + +struct utr_t; +struct udev_t; +struct hub_dev_t; +struct iface_t; +struct ep_info_t; + +/*----------------------------------------------------------------------------------*/ +/* USB device request setup packet */ +/*----------------------------------------------------------------------------------*/ +typedef struct __attribute__((__packed__)) +{ + uint8_t bmRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +} +DEV_REQ_T; + +/* + * bmRequestType[7] - Data transfer direction + */ +#define REQ_TYPE_OUT 0x00 +#define REQ_TYPE_IN 0x80 +/* + * bmRequestType[6:5] - Type + */ +#define REQ_TYPE_STD_DEV 0x00 +#define REQ_TYPE_CLASS_DEV 0x20 +#define REQ_TYPE_VENDOR_DEV 0x40 +/* + * bmRequestType[4:0] - Recipient + */ +#define REQ_TYPE_TO_DEV 0x00 +#define REQ_TYPE_TO_IFACE 0x01 +#define REQ_TYPE_TO_EP 0x02 +#define REQ_TYPE_TO_OTHER 0x03 +/* + * Standard Requests + */ +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +#define USB_REQ_SET_FEATURE 0x03 +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_SET_INTERFACE 0x0B +/* + * Descriptor Types + */ +#define USB_DT_STANDARD 0x00 +#define USB_DT_CLASS 0x20 +#define USB_DT_VENDOR 0x40 + +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIGURATION 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 +#define USB_DT_DEVICE_QUALIFIER 0x06 +#define USB_DT_OTHER_SPEED_CONF 0x07 +#define USB_DT_IFACE_POWER 0x08 + + + +/*----------------------------------------------------------------------------------*/ +/* USB standard descriptors */ +/*----------------------------------------------------------------------------------*/ + +/* Descriptor header */ +typedef struct __attribute__((__packed__)) +{ + uint8_t bLength; + uint8_t bDescriptorType; +} +DESC_HDR_T; + +/*----------------------------------------------------------------------------------*/ +/* USB device descriptor */ +/*----------------------------------------------------------------------------------*/ +typedef struct __attribute__((__packed__)) /*!< device descriptor structure */ +{ + uint8_t bLength; /*!< Length of device descriptor */ + uint8_t bDescriptorType; /*!< Device descriptor type */ + uint16_t bcdUSB; /*!< USB version number */ + uint8_t bDeviceClass; /*!< Device class code */ + uint8_t bDeviceSubClass; /*!< Device subclass code */ + uint8_t bDeviceProtocol; /*!< Device protocol code */ + uint8_t bMaxPacketSize0; /*!< Maximum packet size of control endpoint*/ + uint16_t idVendor; /*!< Vendor ID */ + uint16_t idProduct; /*!< Product ID */ + uint16_t bcdDevice; /*!< Device ID */ + uint8_t iManufacturer; /*!< Manufacture description string ID */ + uint8_t iProduct; /*!< Product description string ID */ + uint8_t iSerialNumber; /*!< Serial number description string ID */ + uint8_t bNumConfigurations; /*!< Total number of configurations */ +} +DESC_DEV_T; /*!< device descriptor structure */ + +/* + * Configuration Descriptor + */ +typedef struct __attribute__((__packed__)) usb_config_descriptor /*!< Configuration descriptor structure */ +{ + uint8_t bLength; /*!< Length of configuration descriptor */ + uint8_t bDescriptorType; /*!< Descriptor type */ + uint16_t wTotalLength; /*!< Total length of this configuration */ + uint8_t bNumInterfaces; /*!< Total number of interfaces */ + uint8_t bConfigurationValue; /*!< Configuration descriptor number */ + uint8_t iConfiguration; /*!< String descriptor ID */ + uint8_t bmAttributes; /*!< Configuration characteristics */ + uint8_t MaxPower; /*!< Maximum power consumption */ +} DESC_CONF_T; /*!< Configuration descriptor structure */ + +/* + * Interface Descriptor + */ +typedef struct __attribute__((__packed__))usb_interface_descriptor /*!< Interface descriptor structure */ +{ + uint8_t bLength; /*!< Length of interface descriptor */ + uint8_t bDescriptorType; /*!< Descriptor type */ + uint8_t bInterfaceNumber; /*!< Interface number */ + uint8_t bAlternateSetting; /*!< Alternate setting number */ + uint8_t bNumEndpoints; /*!< Number of endpoints */ + uint8_t bInterfaceClass; /*!< Interface class code */ + uint8_t bInterfaceSubClass; /*!< Interface subclass code */ + uint8_t bInterfaceProtocol; /*!< Interface protocol code */ + uint8_t iInterface; /*!< Interface ID */ +} DESC_IF_T; /*!< Interface descriptor structure */ + +/* + * Endpoint Descriptor + */ +typedef struct __attribute__((__packed__)) usb_endpoint_descriptor /*!< Endpoint descriptor structure */ +{ + uint8_t bLength; /*!< Length of endpoint descriptor */ + uint8_t bDescriptorType; /*!< Descriptor type */ + uint8_t bEndpointAddress; /*!< Endpoint address */ + uint8_t bmAttributes; /*!< Endpoint attribute */ + uint16_t wMaxPacketSize; /*!< Maximum packet size */ + uint8_t bInterval; /*!< Synchronous transfer interval */ + uint8_t bRefresh; /*!< Refresh */ + uint8_t bSynchAddress; /*!< Sync address */ +} DESC_EP_T; /*!< Endpoint descriptor structure */ + +/* + * Endpoint descriptor bEndpointAddress[7] - direction + */ +#define EP_ADDR_DIR_MASK 0x80 +#define EP_ADDR_DIR_IN 0x80 +#define EP_ADDR_DIR_OUT 0x00 + +/* + * Endpoint descriptor bmAttributes[1:0] - transfer type + */ +#define EP_ATTR_TT_MASK 0x03 +#define EP_ATTR_TT_CTRL 0x00 +#define EP_ATTR_TT_ISO 0x01 +#define EP_ATTR_TT_BULK 0x02 +#define EP_ATTR_TT_INT 0x03 + + +/*----------------------------------------------------------------------------------*/ +/* USB Host controller driver */ +/*----------------------------------------------------------------------------------*/ +typedef struct +{ + int (*init)(void); + void (*shutdown)(void); + void (*suspend)(void); + void (*resume)(void); + int (*ctrl_xfer)(struct utr_t *utr); + int (*bulk_xfer)(struct utr_t *utr); + int (*int_xfer)(struct utr_t *utr); + int (*iso_xfer)(struct utr_t *utr); + int (*quit_xfer)(struct utr_t *utr, struct ep_info_t *ep); + + /* root hub support */ + int (*rthub_port_reset)(int port); + int (*rthub_polling)(void); +} HC_DRV_T; + + +/*----------------------------------------------------------------------------------*/ +/* USB device driver */ +/*----------------------------------------------------------------------------------*/ +typedef struct +{ + int (*probe)(struct iface_t *iface); + void (*disconnect)(struct iface_t *iface); + void (*suspend)(struct iface_t *iface); + void (*resume)(struct iface_t *iface); +} UDEV_DRV_T; + + +/*----------------------------------------------------------------------------------*/ +/* USB device */ +/*----------------------------------------------------------------------------------*/ + +typedef enum +{ + SPEED_LOW, + SPEED_FULL, + SPEED_HIGH +} SPEED_E; + +typedef struct ep_info_t +{ + uint8_t bEndpointAddress; + uint8_t bmAttributes; + uint8_t bInterval; + uint8_t bToggle; + uint16_t wMaxPacketSize; + void *hw_pipe; /*!< point to the HC assocaied endpoint \hideinitializer */ +} EP_INFO_T; + +typedef struct udev_t +{ + DESC_DEV_T descriptor; /*!< Device descriptor. \hideinitializer */ + struct hub_dev_t *parent; /*!< parent hub device \hideinitializer */ + uint8_t port_num; /*!< The hub port this device connected on \hideinitializer */ + uint8_t dev_num; /*!< device number \hideinitializer */ + int8_t cur_conf; /*!< Currentll selected configuration \hideinitializer */ + SPEED_E speed; /*!< device speed (low/full/high) \hideinitializer */ + /* + * The followings are lightweight USB stack internal used . + */ + uint8_t *cfd_buff; /*!< Configuration descriptor buffer. \hideinitializer */ + EP_INFO_T ep0; /*!< Endpoint 0 \hideinitializer */ + HC_DRV_T *hc_driver; /*!< host controller driver \hideinitializer */ + struct iface_t *iface_list; /*!< Working interface list \hideinitializer */ + struct udev_t *next; /*!< link for global usb device list \hideinitializer */ +} UDEV_T; + +typedef struct alt_iface_t +{ + DESC_IF_T *ifd; /*!< point to the location of this alternative interface descriptor in UDEV_T->cfd_buff */ + EP_INFO_T ep[MAX_EP_PER_IFACE]; /*!< endpoints of this alternative interface */ +} ALT_IFACE_T; + +typedef struct iface_t +{ + UDEV_T *udev; /*!< USB device \hideinitializer */ + uint8_t if_num; /*!< Interface number \hideinitializer */ + uint8_t num_alt; /*!< Number of alternative interface \hideinitializer */ + ALT_IFACE_T *aif; /*!< Point to the active alternative interface */ + ALT_IFACE_T alt[MAX_ALT_PER_IFACE]; /*!< List of alternative interface \hideinitializer */ + UDEV_DRV_T *driver; /*!< Interface associated driver \hideinitializer */ + void *context; /*!< Reference to device context \hideinitializer */ + struct iface_t *next; /*!< Point to next interface of the same device. Started from UDEV_T->iface_list \hideinitializer */ +} IFACE_T; + + +/*----------------------------------------------------------------------------------*/ +/* URB (USB Request Block) */ +/*----------------------------------------------------------------------------------*/ + +#define IF_PER_UTR 8 /* number of frames per UTR isochronous transfer (DO NOT modify it!) */ + +typedef void (*FUNC_UTR_T)(struct utr_t *); + +typedef struct utr_t +{ + UDEV_T *udev; /*!< point to associated USB device \hideinitializer */ + DEV_REQ_T setup; /*!< buffer for setup packet \hideinitializer */ + EP_INFO_T *ep; /*!< associated endpoint \hideinitializer */ + uint8_t *buff; /*!< transfer buffer \hideinitializer */ + uint8_t bIsTransferDone; /*!< tansfer done? \hideinitializer */ + uint32_t data_len; /*!< length of data to be transferred \hideinitializer */ + uint32_t xfer_len; /*!< length of transferred data \hideinitializer */ + uint8_t bIsoNewSched; /*!< New schedule isochronous transfer \hideinitializer */ + uint16_t iso_sf; /*!< Isochronous start frame number \hideinitializer */ + uint16_t iso_xlen[IF_PER_UTR]; /*!< transfer length of isochronous frames \hideinitializer */ + uint8_t *iso_buff[IF_PER_UTR]; /*!< transfer buffer address of isochronous frames \hideinitializer */ + int iso_status[IF_PER_UTR]; /*!< transfer status of isochronous frames \hideinitializer */ + int td_cnt; /*!< number of transfer descriptors \hideinitializer */ + int status; /*!< return status \hideinitializer */ + int interval; /*!< interrupt/isochronous interval \hideinitializer */ + void *context; /*!< point to deivce proprietary data area \hideinitializer */ + FUNC_UTR_T func; /*!< tansfer done call-back function \hideinitializer */ + struct utr_t *next; /* point to the next UTR of the same endpoint. \hideinitializer */ +} UTR_T; + + +/*----------------------------------------------------------------------------------*/ +/* Global variables */ +/*----------------------------------------------------------------------------------*/ +extern USBH_T *_ohci; +extern HSUSBH_T *_ehci; + +extern HC_DRV_T ohci_driver; +extern HC_DRV_T ehci_driver; + +extern UDEV_T *g_udev_list; + +extern volatile int _IsInUsbInterrupt; + +/*----------------------------------------------------------------------------------*/ +/* USB stack exported functions */ +/*----------------------------------------------------------------------------------*/ +extern void usbh_delay_ms(int msec); + +extern void dump_ohci_regs(void); +extern void dump_ohci_ports(void); +extern void dump_ohci_int_table(void); +extern void dump_ehci_regs(void); +extern void dump_ehci_qtd(qTD_T *qtd); +extern void dump_ehci_asynclist(void); +extern void dump_ehci_period_frame_list_simple(void); +extern void usbh_dump_buff_bytes(uint8_t *buff, int nSize); +extern void usbh_dump_interface_descriptor(DESC_IF_T *if_desc); +extern void usbh_dump_endpoint_descriptor(DESC_EP_T *ep_desc); +extern void usbh_dump_iface(IFACE_T *iface); +extern void usbh_dump_ep_info(EP_INFO_T *ep); + +/* + * Memory management functions + */ +extern void USB_InitializeMemoryPool(void); +extern void *USB_malloc(int wanted_size, int boundary); +extern void USB_free(void *); +extern int USB_available_memory(void); +extern int USB_allocated_memory(void); +extern void usbh_memory_init(void); +extern uint32_t usbh_memory_used(void); +extern void *usbh_alloc_mem(int size); +extern void usbh_free_mem(void *p, int size); +extern int alloc_dev_address(void); +extern void free_dev_address(int dev_addr); +extern UDEV_T *alloc_device(void); +extern void free_device(UDEV_T *udev); +extern UTR_T *alloc_utr(UDEV_T *udev); +extern void free_utr(UTR_T *utr); +extern ED_T *alloc_ohci_ED(void); +extern void free_ohci_ED(ED_T *ed); +extern TD_T *alloc_ohci_TD(UTR_T *utr); +extern void free_ohci_TD(TD_T *td); +extern QH_T *alloc_ehci_QH(void); +extern void free_ehci_QH(QH_T *qh); +extern qTD_T *alloc_ehci_qTD(UTR_T *utr); +extern void free_ehci_qTD(qTD_T *qtd); +extern iTD_T *alloc_ehci_iTD(void); +extern void free_ehci_iTD(iTD_T *itd); +extern siTD_T *alloc_ehci_siTD(void); +extern void free_ehci_siTD(siTD_T *sitd); + + +extern void usbh_hub_init(void); +extern int usbh_connect_device(UDEV_T *); +extern void usbh_disconnect_device(UDEV_T *); +extern int usbh_register_driver(UDEV_DRV_T *driver); +extern EP_INFO_T *usbh_iface_find_ep(IFACE_T *iface, uint8_t ep_addr, uint8_t dir_type); +extern int usbh_reset_device(UDEV_T *); +extern int usbh_reset_port(UDEV_T *); + +/* + * USB Standard Request functions + */ +extern int usbh_get_device_descriptor(UDEV_T *udev, DESC_DEV_T *desc_buff); +extern int usbh_get_config_descriptor(UDEV_T *udev, uint8_t *desc_buff, int buff_len); +extern int usbh_set_configuration(UDEV_T *udev, uint8_t conf_val); +extern int usbh_set_interface(IFACE_T *iface, uint16_t alt_setting); +extern int usbh_clear_halt(UDEV_T *udev, uint16_t ep_addr); + +extern int usbh_ctrl_xfer(UDEV_T *udev, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength, uint8_t *buff, uint32_t *xfer_len, uint32_t timeout); +extern int usbh_bulk_xfer(UTR_T *utr); +extern int usbh_int_xfer(UTR_T *utr); +extern int usbh_iso_xfer(UTR_T *utr); +extern int usbh_quit_utr(UTR_T *utr); +extern int usbh_quit_xfer(UDEV_T *udev, EP_INFO_T *ep); + + +/// @endcond HIDDEN_SYMBOLS + +#endif /* _USBH_H_ */ diff --git a/bsp/nuvoton/libraries/nuc980/UsbHostLib/inc/usbh_lib.h b/bsp/nuvoton/libraries/nuc980/UsbHostLib/inc/usbh_lib.h new file mode 100644 index 0000000000..fd14a96cec --- /dev/null +++ b/bsp/nuvoton/libraries/nuc980/UsbHostLib/inc/usbh_lib.h @@ -0,0 +1,188 @@ +/**************************************************************************//** + * @file usbh_lib.h + * @version V1.10 + * $Revision: 4 $ + * $Date: 15/06/10 2:06p $ + * @brief USB Host library exported header file. + * + * @note + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2017 Nuvoton Technology Corp. All rights reserved. + ******************************************************************************/ +#ifndef _USBH_LIB_H_ +#define _USBH_LIB_H_ + +#include "nuc980.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** @addtogroup Library Library + @{ +*/ + +/** @addtogroup USBH_Library USB Host Library + @{ +*/ + +/** @addtogroup USBH_EXPORTED_CONSTANTS USB Host Exported Constants + @{ +*/ + +#define USBH_OK 0 /*!< No error. */ +#define USBH_ERR_MEMORY_OUT -10 /*!< Out of memory. */ +#define USBH_ERR_IF_ALT_LIMIT -11 /*!< Number of alternative interface > MAX_ALT_PER_IFACE */ +#define USBH_ERR_IF_EP_LIMIT -15 /*!< Number of endpoints > MAX_EP_PER_IFACE */ +#define USBH_ERR_NOT_SUPPORTED -101 /*!< Device/Class/Transfer not supported */ +#define USBH_ERR_NOT_MATCHED -103 /*!< Not macthed */ +#define USBH_ERR_NOT_EXPECTED -104 /*!< Unknown or unexpected */ +#define USBH_ERR_INVALID_PARAM -105 /*!< Invalid parameter */ +#define USBH_ERR_NOT_FOUND -106 /*!< Device or interface not found */ +#define USBH_ERR_EP_NOT_FOUND -107 /*!< Endpoint not found */ +#define USBH_ERR_DESCRIPTOR -137 /*!< Failed to parse USB descriptors */ +#define USBH_ERR_SET_DEV_ADDR -139 /*!< Failed to set device address */ +#define USBH_ERR_SET_CONFIG -151 /*!< Failed to set device configuration */ + +#define USBH_ERR_TRANSFER -201 /*!< USB transfer error */ +#define USBH_ERR_TIMEOUT -203 /*!< USB transfer time-out */ +#define USBH_ERR_ABORT -205 /*!< USB transfer aborted due to disconnect or reset */ +#define USBH_ERR_PORT_RESET -255 /*!< Hub port reset failed */ +#define USBH_ERR_SCH_OVERRUN -257 /*!< USB isochronous schedule overrun */ +#define USBH_ERR_DISCONNECTED -259 /*!< USB device was disconnected */ + +#define USBH_ERR_TRANSACTION -271 /*!< USB transaction timeout, CRC, Bad PID, etc. */ +#define USBH_ERR_BABBLE_DETECTED -272 /*!< A ¡§babble¡¨ is detected during the transaction */ +#define USBH_ERR_DATA_BUFF -274 /*!< Data buffer overrun or underrun */ + +#define USBH_ERR_CC_NO_ERR -280 /*!< OHCI CC code - no error */ +#define USBH_ERR_CRC -281 /*!< USB trasfer CRC error */ +#define USBH_ERR_BIT_STUFF -282 /*!< USB transfer bit stuffing error */ +#define USBH_ERR_DATA_TOGGLE -283 /*!< USB trasfer data toggle error */ +#define USBH_ERR_STALL -284 /*!< USB trasfer STALL error */ +#define USBH_ERR_DEV_NO_RESP -285 /*!< USB trasfer device no response error */ +#define USBH_ERR_PID_CHECK -286 /*!< USB trasfer PID check failure */ +#define USBH_ERR_UNEXPECT_PID -287 /*!< USB trasfer unexpected PID error */ +#define USBH_ERR_DATA_OVERRUN -288 /*!< USB trasfer data overrun error */ +#define USBH_ERR_DATA_UNDERRUN -289 /*!< USB trasfer data underrun error */ +#define USBH_ERR_BUFF_OVERRUN -292 /*!< USB trasfer buffer overrun error */ +#define USBH_ERR_BUFF_UNDERRUN -293 /*!< USB trasfer buffer underrun error */ +#define USBH_ERR_NOT_ACCESS0 -294 /*!< USB trasfer not accessed error */ +#define USBH_ERR_NOT_ACCESS1 -295 /*!< USB trasfer not accessed error */ + +#define USBH_ERR_OHCI_INIT -301 /*!< Failed to initialize OHIC controller. */ +#define USBH_ERR_OHCI_EP_BUSY -303 /*!< The endpoint is under transfer. */ + +#define USBH_ERR_EHCI_INIT -501 /*!< Failed to initialize EHCI controller. */ +#define USBH_ERR_EHCI_QH_BUSY -503 /*!< the Queue Head is busy. */ + +#define UMAS_OK 0 /*!< No error. */ +#define UMAS_ERR_NO_DEVICE -1031 /*!< No Mass Stroage Device found. */ +#define UMAS_ERR_IO -1033 /*!< Device read/write failed. */ +#define UMAS_ERR_INIT_DEVICE -1035 /*!< failed to init MSC device */ +#define UMAS_ERR_CMD_STATUS -1037 /*!< SCSI command status failed */ +#define UMAS_ERR_IVALID_PARM -1038 /*!< Invalid parameter. */ +#define UMAS_ERR_DRIVE_NOT_FOUND -1039 /*!< drive not found */ + +#define HID_RET_OK 0 /*!< Return with no errors. */ +#define HID_RET_DEV_NOT_FOUND -1081 /*!< HID device not found or removed. */ +#define HID_RET_IO_ERR -1082 /*!< USB transfer failed. */ +#define HID_RET_INVALID_PARAMETER -1083 /*!< Invalid parameter. */ +#define HID_RET_OUT_OF_MEMORY -1084 /*!< Out of memory. */ +#define HID_RET_NOT_SUPPORTED -1085 /*!< Function not supported. */ +#define HID_RET_EP_NOT_FOUND -1086 /*!< Endpoint not found. */ +#define HID_RET_PARSING -1087 /*!< Failed to parse HID descriptor */ +#define HID_RET_XFER_IS_RUNNING -1089 /*!< The transfer has been enabled. */ +#define HID_RET_REPORT_NOT_FOUND -1090 /*!< The transfer has been enabled. */ + +#define UAC_RET_OK 0 /*!< Return with no errors. */ +#define UAC_RET_DEV_NOT_FOUND -2001 /*!< Audio Class device not found or removed. */ +#define UAC_RET_FUNC_NOT_FOUND -2002 /*!< Audio device has no this function. */ +#define UAC_RET_IO_ERR -2003 /*!< USB transfer failed. */ +#define UAC_RET_DATA_LEN -2004 /*!< Unexpected transfer length */ +#define UAC_RET_INVALID -2005 /*!< Invalid parameter or usage. */ +#define UAC_RET_OUT_OF_MEMORY -2007 /*!< Out of memory. */ +#define UAC_RET_DRV_NOT_SUPPORTED -2009 /*!< Function not supported by this UAC driver. */ +#define UAC_RET_DEV_NOT_SUPPORTED -2011 /*!< Function not supported by the UAC device. */ +#define UAC_RET_PARSER -2013 /*!< Failed to parse UAC descriptor */ +#define UAC_RET_IS_STREAMING -2015 /*!< Audio pipe is on streaming. */ + + +/*@}*/ /* end of group USBH_EXPORTED_CONSTANTS */ + + +/** @addtogroup USBH_EXPORTED_TYPEDEF USB Host Typedef + @{ +*/ +struct udev_t; +typedef void (CONN_FUNC)(struct udev_t *udev, int param); + +struct line_coding_t; +struct cdc_dev_t; +typedef void (CDC_CB_FUNC)(struct cdc_dev_t *cdev, uint8_t *rdata, int data_len); + +struct usbhid_dev; +typedef void (HID_IR_FUNC)(struct usbhid_dev *hdev, uint16_t ep_addr, int status, uint8_t *rdata, uint32_t data_len); /*!< interrupt in callback function \hideinitializer */ +typedef void (HID_IW_FUNC)(struct usbhid_dev *hdev, uint16_t ep_addr, int status, uint8_t *wbuff, uint32_t *data_len); /*!< interrupt out callback function \hideinitializer */ + +struct uac_dev_t; +typedef int (UAC_CB_FUNC)(struct uac_dev_t *dev, uint8_t *data, int len); /*!< audio in callback function \hideinitializer */ + +/*@}*/ /* end of group USBH_EXPORTED_STRUCT */ + + + +/** @addtogroup USBH_EXPORTED_FUNCTIONS USB Host Exported Functions + @{ +*/ + +/*------------------------------------------------------------------*/ +/* */ +/* USB Core Library APIs */ +/* */ +/*------------------------------------------------------------------*/ +extern void usbh_core_init(void); +extern int usbh_polling_root_hubs(void); +extern void usbh_install_conn_callback(CONN_FUNC *conn_func, CONN_FUNC *disconn_func); +extern void usbh_suspend(void); +extern void usbh_resume(void); +extern struct udev_t *usbh_find_device(char *hub_id, int port); + +/** + * @brief A function return current tick count. + * @return Current tick. + * @details User application must provide this function to return current tick. + * The tick should increase by 1 for every 10 ms. + */ +extern uint32_t usbh_get_ticks(void); /* This function must be provided by user application. */ +extern uint32_t usbh_tick_from_millisecond(uint32_t msec); /* This function must be provided by user application. */ + + +/// @cond HIDDEN_SYMBOLS + +extern void dump_ohci_regs(void); +extern void dump_ehci_regs(void); +extern void dump_ohci_ports(void); +extern void dump_ehci_ports(void); +extern uint32_t usbh_memory_used(void); + +/// @endcond HIDDEN_SYMBOLS + + +/*@}*/ /* end of group USBH_EXPORTED_FUNCTIONS */ + +/*@}*/ /* end of group USBH_Library */ + +/*@}*/ /* end of group LIBRARY */ + +#ifdef __cplusplus +} +#endif + +#endif /* _USBH_LIB_H_ */ + +/*** (C) COPYRIGHT 2017 Nuvoton Technology Corp. ***/ + + + diff --git a/bsp/nuvoton/libraries/nuc980/UsbHostLib/src/ehci.c b/bsp/nuvoton/libraries/nuc980/UsbHostLib/src/ehci.c new file mode 100644 index 0000000000..dbcda52196 --- /dev/null +++ b/bsp/nuvoton/libraries/nuc980/UsbHostLib/src/ehci.c @@ -0,0 +1,1288 @@ +/**************************************************************************//** + * @file ehci.c + * @version V1.10 + * $Revision: 11 $ + * $Date: 14/10/03 1:54p $ + * @brief USB Host library EHCI (USB 2.0) host controller driver. + * + * @note + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2017 Nuvoton Technology Corp. All rights reserved. +*****************************************************************************/ + +#include +#include +#include + +#include "usb.h" +#include "hub.h" + + +/// @cond HIDDEN_SYMBOLS + +static QH_T *_H_qh; /* head of reclamation list */ +static qTD_T *_ghost_qtd; /* used as a terminator qTD */ +static QH_T *qh_remove_list; + +extern ISO_EP_T *iso_ep_list; /* list of activated isochronous pipes */ +extern int ehci_iso_xfer(UTR_T *utr); /* EHCI isochronous transfer function */ +extern int ehci_quit_iso_xfer(UTR_T *utr, EP_INFO_T *ep); + +uint32_t _PFList_mem[FL_SIZE] __attribute__((aligned(4096)));/* Periodic frame list (Keil) */ + +uint32_t *_PFList; + + +QH_T *_Iqh[NUM_IQH]; + + +#ifdef ENABLE_ERROR_MSG +void dump_ehci_regs() +{ + USB_debug("Dump HSUSBH(EHCI) registers:\n"); + USB_debug(" UCMDR = 0x%x\n", _ehci->UCMDR); + USB_debug(" USTSR = 0x%x\n", _ehci->USTSR); + USB_debug(" UIENR = 0x%x\n", _ehci->UIENR); + USB_debug(" UFINDR = 0x%x\n", _ehci->UFINDR); + USB_debug(" UPFLBAR = 0x%x\n", _ehci->UPFLBAR); + USB_debug(" UCALAR = 0x%x\n", _ehci->UCALAR); + USB_debug(" UASSTR = 0x%x\n", _ehci->UASSTR); + USB_debug(" UCFGR = 0x%x\n", _ehci->UCFGR); + USB_debug(" UPSCR = 0x%x\n", _ehci->UPSCR[0]); + USB_debug(" PHYCTL0 = 0x%x\n", _ehci->USBPCR0); + USB_debug(" PHYCTL1 = 0x%x\n", _ehci->USBPCR1); +} + +void dump_ehci_ports() +{ + USB_debug("_ehci port0=0x%x, port1=0x%x\n", _ehci->UPSCR[0], _ehci->UPSCR[1]); +} + +void dump_ehci_qtd(qTD_T *qtd) +{ + USB_debug(" [qTD] - 0x%08x\n", (int)qtd); + USB_debug(" 0x%08x (Next qtd Pointer)\n", qtd->Next_qTD); + USB_debug(" 0x%08x (Alternate Next qtd Pointer)\n", qtd->Alt_Next_qTD); + USB_debug(" 0x%08x (qtd Token) PID: %s, Bytes: %d, IOC: %d\n", qtd->Token, (((qtd->Token >> 8) & 0x3) == 0) ? "OUT" : ((((qtd->Token >> 8) & 0x3) == 1) ? "IN" : "SETUP"), (qtd->Token >> 16) & 0x7FFF, (qtd->Token >> 15) & 0x1); + USB_debug(" 0x%08x (Buffer Pointer (page 0))\n", qtd->Bptr[0]); + //USB_debug(" 0x%08x (Buffer Pointer (page 1))\n", qtd->Bptr[1]); + //USB_debug(" 0x%08x (Buffer Pointer (page 2))\n", qtd->Bptr[2]); + //USB_debug(" 0x%08x (Buffer Pointer (page 3))\n", qtd->Bptr[3]); + //USB_debug(" 0x%08x (Buffer Pointer (page 4))\n", qtd->Bptr[4]); + USB_debug("\n"); +} + +void dump_ehci_asynclist(void) +{ + QH_T *qh = _H_qh; + qTD_T *qtd; + + USB_debug(">>> Dump EHCI Asynchronous List <<<\n"); + do + { + USB_debug("[QH] - 0x%08x\n", (int)qh); + USB_debug(" 0x%08x (Queue Head Horizontal Link Pointer, Queue Head DWord 0)\n", qh->HLink); + USB_debug(" 0x%08x (Endpoint Characteristics) DevAddr: %d, EP: 0x%x, PktSz: %d, Speed: %s\n", qh->Chrst, qh->Chrst & 0x7F, (qh->Chrst >> 8) & 0xF, (qh->Chrst >> 16) & 0x7FF, ((qh->Chrst >> 12) & 0x3 == 0) ? "Full" : (((qh->Chrst >> 12) & 0x3 == 1) ? "Low" : "High")); + USB_debug(" 0x%08x (Endpoint Capabilities: Queue Head DWord 2)\n", qh->Cap); + USB_debug(" 0x%08x (Current qtd Pointer)\n", qh->Curr_qTD); + USB_debug(" --- Overlay Area ---\n"); + USB_debug(" 0x%08x (Next qtd Pointer)\n", qh->OL_Next_qTD); + USB_debug(" 0x%08x (Alternate Next qtd Pointer)\n", qh->OL_Alt_Next_qTD); + USB_debug(" 0x%08x (qtd Token)\n", qh->OL_Token); + USB_debug(" 0x%08x (Buffer Pointer (page 0))\n", qh->OL_Bptr[0]); + USB_debug("\n"); + + qtd = QTD_PTR(qh->Curr_qTD); + while (qtd != NULL) + { + dump_ehci_qtd(qtd); + qtd = QTD_PTR(qtd->Next_qTD); + } + qh = QH_PTR(qh->HLink); + } + while (qh != _H_qh); +} + +void dump_ehci_asynclist_simple(void) +{ + QH_T *qh = _H_qh; + + USB_debug(">>> EHCI Asynchronous List <<<\n"); + USB_debug("[QH] => "); + do + { + USB_debug("0x%08x ", (int)qh); + qh = QH_PTR(qh->HLink); + } + while (qh != _H_qh); + USB_debug("\n"); +} + +void dump_ehci_period_frame_list_simple(void) +{ + QH_T *qh = _Iqh[NUM_IQH - 1]; + + USB_debug(">>> EHCI period frame list simple <<<\n"); + USB_debug("[FList] => "); + do + { + USB_debug("0x%08x ", (int)qh); + qh = QH_PTR(qh->HLink); + } + while (qh != NULL); + USB_debug("\n"); +} + +void dump_ehci_period_frame_list() +{ + int i; + QH_T *qh; + + for (i = 0; i < FL_SIZE; i++) + { + USB_debug("!%02d: ", i); + qh = QH_PTR(_PFList[i]);; + while (qh != NULL) + { + // USB_debug("0x%x (0x%x) => ", (int)qh, qh->HLink); + USB_debug("0x%x => ", (int)qh); + qh = QH_PTR(qh->HLink); + } + USB_debug("0\n"); + } +} + +#endif /* ENABLE_ERROR_MSG */ + +static void init_periodic_frame_list() +{ + QH_T *qh_p; + int i, idx, interval; + + _PFList = (uint32_t *)((uint32_t)_PFList_mem | NON_CACHE_MASK); + memset(_PFList, 0, sizeof(_PFList_mem)); + + iso_ep_list = NULL; + + for (i = NUM_IQH - 1; i >= 0; i--) /* interval = i^2 */ + { + _Iqh[i] = alloc_ehci_QH(); + + _Iqh[i]->HLink = QH_HLNK_END; + _Iqh[i]->Curr_qTD = (uint32_t)_ghost_qtd; + _Iqh[i]->OL_Next_qTD = QTD_LIST_END; + _Iqh[i]->OL_Alt_Next_qTD = (uint32_t)_ghost_qtd; + _Iqh[i]->OL_Token = QTD_STS_HALT; + + interval = 0x1 << i; + + for (idx = interval - 1; idx < FL_SIZE; idx += interval) + { + if (_PFList[idx] == 0) /* is empty list, insert directly */ + { + _PFList[idx] = QH_HLNK_QH(_Iqh[i]); + } + else + { + qh_p = QH_PTR(_PFList[idx]); + + while (1) + { + if (qh_p == _Iqh[i]) + break; /* already chained by previous visit */ + + if (qh_p->HLink == QH_HLNK_END) /* reach end of list? */ + { + qh_p->HLink = QH_HLNK_QH(_Iqh[i]); + break; + } + qh_p = QH_PTR(qh_p->HLink); + } + } + } + } +} + +static QH_T *get_int_tree_head_node(int interval) +{ + int i; + + interval /= 8; /* each frame list entry for 8 micro-frame */ + + for (i = 0; i < NUM_IQH - 1; i++) + { + interval >>= 1; + if (interval == 0) + return _Iqh[i]; + } + return _Iqh[NUM_IQH - 1]; +} + +static int make_int_s_mask(int bInterval) +{ + int order, interval; + + interval = 1; + while (bInterval > 1) + { + interval *= 2; + bInterval--; + } + + if (interval < 2) + return 0xFF; /* interval 1 */ + if (interval < 4) + return 0x55; /* interval 2 */ + if (interval < 8) + return 0x22; /* interval 4 */ + for (order = 0; (interval > 1); order++) + { + interval >>= 1; + } + return (0x1 << (order % 8)); +} + +static int ehci_init(void) +{ + int timeout = 250 * 1000; /* EHCI reset time-out 250 ms */ + + /*------------------------------------------------------------------------------------*/ + /* Reset EHCI host controller */ + /*------------------------------------------------------------------------------------*/ + _ehci->UCMDR = HSUSBH_UCMDR_HCRST_Msk; + while ((_ehci->UCMDR & HSUSBH_UCMDR_HCRST_Msk) && (timeout > 0)) + { + usbh_delay_ms(1); + timeout -= 1000; + } + if (_ehci->UCMDR & HSUSBH_UCMDR_HCRST_Msk) + return USBH_ERR_EHCI_INIT; + + _ehci->UCMDR = UCMDR_INT_THR_CTRL | HSUSBH_UCMDR_RUN_Msk; + + _ghost_qtd = alloc_ehci_qTD(NULL); + _ghost_qtd->Token = 0x11197B7F; //QTD_STS_HALT; visit_qtd() will not remove a qTD with this mark. It represents a qhost qTD. + + /*------------------------------------------------------------------------------------*/ + /* Initialize asynchronous list */ + /*------------------------------------------------------------------------------------*/ + qh_remove_list = NULL; + + /* Create the QH list head with H-bit 1 */ + _H_qh = alloc_ehci_QH(); + _H_qh->HLink = QH_HLNK_QH(_H_qh); /* circular link to itself, the only one QH */ + _H_qh->Chrst = QH_RCLM_LIST_HEAD; /* it's the head of reclamation list */ + _H_qh->Curr_qTD = (uint32_t)_ghost_qtd; + _H_qh->OL_Next_qTD = QTD_LIST_END; + _H_qh->OL_Alt_Next_qTD = (uint32_t)_ghost_qtd; + _H_qh->OL_Token = QTD_STS_HALT; + _ehci->UCALAR = (uint32_t)_H_qh; + + /*------------------------------------------------------------------------------------*/ + /* Initialize periodic list */ + /*------------------------------------------------------------------------------------*/ + if (FL_SIZE == 256) + _ehci->UCMDR |= (0x2 << HSUSBH_UCMDR_FLSZ_Pos); + else if (FL_SIZE == 512) + _ehci->UCMDR |= (0x1 << HSUSBH_UCMDR_FLSZ_Pos); + else if (FL_SIZE == 1024) + _ehci->UCMDR |= (0x0 << HSUSBH_UCMDR_FLSZ_Pos); + else + return USBH_ERR_EHCI_INIT; /* Invalid FL_SIZE setting! */ + + /*------------------------------------------------------------------------------------*/ + /* start run */ + /*------------------------------------------------------------------------------------*/ + + _ehci->UCFGR = 0x1; /* enable port routing to EHCI */ + _ehci->UIENR = HSUSBH_UIENR_USBIEN_Msk | HSUSBH_UIENR_UERRIEN_Msk | HSUSBH_UIENR_HSERREN_Msk | HSUSBH_UIENR_IAAEN_Msk; + + usbh_delay_ms(1); /* delay 1 ms */ + + _ehci->UPSCR[0] = HSUSBH_UPSCR_PP_Msk; /* enable port 1 port power */ + _ehci->UPSCR[1] = HSUSBH_UPSCR_PP_Msk; /* enable port 2 port power */ + + init_periodic_frame_list(); + + _ehci->UPFLBAR = (uint32_t)_PFList; + usbh_delay_ms(10); /* delay 10 ms */ + + return 0; +} + +static void ehci_suspend(void) +{ + if (_ehci->UPSCR[0] & 0x1) + _ehci->UPSCR[0] |= HSUSBH_UPSCR_SUSPEND_Msk; +} + +static void ehci_resume(void) +{ + if (_ehci->UPSCR[0] & 0x1) + _ehci->UPSCR[0] = (HSUSBH->UPSCR[0] & ~HSUSBH_UPSCR_SUSPEND_Msk) | HSUSBH_UPSCR_FPR_Msk; +} + +static void ehci_shutdown(void) +{ + ehci_suspend(); +} + +static void move_qh_to_remove_list(QH_T *qh) +{ + QH_T *q; + + // USB_debug("move_qh_to_remove_list - 0x%x (0x%x)\n", (int)qh, qh->Chrst); + + /* check if this ED found in ed_remove_list */ + q = qh_remove_list; + while (q) + { + if (q == qh) /* This QH found in qh_remove_list. */ + { + return; /* Do nothing, return... */ + } + q = q->next; + } + + DISABLE_EHCI_IRQ(); + + /*------------------------------------------------------------------------------------*/ + /* Search asynchronous frame list and remove qh if found in list. */ + /*------------------------------------------------------------------------------------*/ + q = _H_qh; /* find and remove it from asynchronous list */ + while (QH_PTR(q->HLink) != _H_qh) + { + if (QH_PTR(q->HLink) == qh) + { + /* q's next QH is qh, found... */ + q->HLink = qh->HLink; /* remove qh from list */ + + qh->next = qh_remove_list; /* add qh to qh_remove_list */ + qh_remove_list = qh; + _ehci->UCMDR |= HSUSBH_UCMDR_IAAD_Msk; /* trigger IAA interrupt */ + ENABLE_EHCI_IRQ(); + return; /* done */ + } + q = QH_PTR(q->HLink); /* advance to next QH in asynchronous list */ + } + + /*------------------------------------------------------------------------------------*/ + /* Search periodic frame list and remove qh if found in list. */ + /*------------------------------------------------------------------------------------*/ + q = _Iqh[NUM_IQH - 1]; + while (q->HLink != QH_HLNK_END) + { + if (QH_PTR(q->HLink) == qh) + { + /* q's next QH is qh, found... */ + q->HLink = qh->HLink; /* remove qh from list */ + + qh->next = qh_remove_list; /* add qh to qh_remove_list */ + qh_remove_list = qh; + _ehci->UCMDR |= HSUSBH_UCMDR_IAAD_Msk; /* trigger IAA interrupt */ + ENABLE_EHCI_IRQ(); + return; /* done */ + } + q = QH_PTR(q->HLink); /* advance to next QH in asynchronous list */ + } + ENABLE_EHCI_IRQ(); +} + +static void append_to_qtd_list_of_QH(QH_T *qh, qTD_T *qtd) +{ + qTD_T *q; + + if (qh->qtd_list == NULL) + { + qh->qtd_list = qtd; + } + else + { + q = qh->qtd_list; + while (q->next != NULL) + { + q = q->next; + } + q->next = qtd; + } +} + +/* + * If ep==NULL, it's a control endpoint QH. + */ +static void write_qh(UDEV_T *udev, EP_INFO_T *ep, QH_T *qh) +{ + uint32_t chrst, cap; + + /*------------------------------------------------------------------------------------*/ + /* Write QH DWord 1 - Endpoint Characteristics */ + /*------------------------------------------------------------------------------------*/ + if (ep == NULL) /* is control endpoint? */ + { + if (udev->descriptor.bMaxPacketSize0 == 0) + { + if (udev->speed == SPEED_LOW) /* give a default maximum packet size */ + udev->descriptor.bMaxPacketSize0 = 8; + else + udev->descriptor.bMaxPacketSize0 = 64; + } + chrst = QH_DTC | QH_NAK_RL | (udev->descriptor.bMaxPacketSize0 << 16); + if (udev->speed != SPEED_HIGH) + chrst |= QH_CTRL_EP_FLAG; /* non-high-speed control endpoint */ + } + else /* not a control endpoint */ + { + chrst = QH_NAK_RL | (ep->wMaxPacketSize << 16); + chrst |= ((ep->bEndpointAddress & 0xf) << 8); /* Endpoint Address */ + } + + if (udev->speed == SPEED_LOW) + chrst |= QH_EPS_LOW; + else if (udev->speed == SPEED_FULL) + chrst |= QH_EPS_FULL; + else + chrst |= QH_EPS_HIGH; + + chrst |= udev->dev_num; + + qh->Chrst = chrst; + + /*------------------------------------------------------------------------------------*/ + /* Write QH DWord 2 - Endpoint Capabilities */ + /*------------------------------------------------------------------------------------*/ + if (udev->speed == SPEED_HIGH) + { + cap = 0; + } + else + { + /* + * Backtrace device tree until the USB 2.0 hub found + */ + HUB_DEV_T *hub; + int port_num; + + port_num = udev->port_num; + hub = udev->parent; + + while ((hub != NULL) && (hub->iface->udev->speed != SPEED_HIGH)) + { + port_num = hub->iface->udev->port_num; + hub = hub->iface->udev->parent; + } + + cap = (port_num << QH_HUB_PORT_Pos) | + (hub->iface->udev->dev_num << QH_HUB_ADDR_Pos); + } + + qh->Cap = cap; +} + +static void write_qtd_bptr(qTD_T *qtd, uint32_t buff_addr, int xfer_len) +{ + int i; + + qtd->xfer_len = xfer_len; + qtd->Bptr[0] = buff_addr; + + buff_addr = (buff_addr + 0x1000) & ~0xFFF; + + for (i = 1; i < 5; i++) + { + qtd->Bptr[i] = buff_addr; + buff_addr += 0x1000; + } +} + +static int ehci_ctrl_xfer(UTR_T *utr) +{ + UDEV_T *udev; + QH_T *qh; + qTD_T *qtd_setup, *qtd_data, *qtd_status; + uint32_t token; + int is_new_qh = 0; + + udev = utr->udev; + + if (utr->data_len > 0) + { + if (((uint32_t)utr->buff + utr->data_len) > (((uint32_t)utr->buff & ~0xFFF) + 0x5000)) + return USBH_ERR_BUFF_OVERRUN; + } + + /*------------------------------------------------------------------------------------*/ + /* Allocate and link QH */ + /*------------------------------------------------------------------------------------*/ + if (udev->ep0.hw_pipe != NULL) + { + qh = (QH_T *)udev->ep0.hw_pipe; + if (qh->qtd_list) + return USBH_ERR_EHCI_QH_BUSY; + } + else + { + qh = alloc_ehci_QH(); + if (qh == NULL) + return USBH_ERR_MEMORY_OUT; + + udev->ep0.hw_pipe = (void *)qh; /* driver can find QH from EP */ + is_new_qh = 1; + } + write_qh(udev, NULL, qh); + utr->ep = &udev->ep0; /* driver can find EP from UTR */ + + /*------------------------------------------------------------------------------------*/ + /* Allocate qTDs */ + /*------------------------------------------------------------------------------------*/ + qtd_setup = alloc_ehci_qTD(utr); /* allocate qTD for SETUP */ + + if (utr->data_len > 0) + qtd_data = alloc_ehci_qTD(utr); /* allocate qTD for DATA */ + else + qtd_data = NULL; + + qtd_status = alloc_ehci_qTD(utr); /* allocate qTD for USTSR */ + + if (qtd_status == NULL) /* out of memory? */ + { + if (qtd_setup) + free_ehci_qTD(qtd_setup); /* free memory */ + if (qtd_data) + free_ehci_qTD(qtd_data); /* free memory */ + return USBH_ERR_MEMORY_OUT; /* out of memory */ + } + + //USB_debug("qh=0x%x, qtd_setup=0x%x, qtd_data=0x%x, qtd_status=0x%x\n", (int)qh, (int)qtd_setup, (int)qtd_data, (int)qtd_status); + + /*------------------------------------------------------------------------------------*/ + /* prepare SETUP stage qTD */ + /*------------------------------------------------------------------------------------*/ + qtd_setup->qh = qh; + //qtd_setup->utr = utr; + write_qtd_bptr(qtd_setup, (uint32_t)&utr->setup, 8); + append_to_qtd_list_of_QH(qh, qtd_setup); + qtd_setup->Token = (8 << 16) | QTD_ERR_COUNTER | QTD_PID_SETUP | QTD_STS_ACTIVE; + + /*------------------------------------------------------------------------------------*/ + /* prepare DATA stage qTD */ + /*------------------------------------------------------------------------------------*/ + if (utr->data_len > 0) + { + qtd_setup->Next_qTD = (uint32_t)qtd_data; + qtd_data->Next_qTD = (uint32_t)qtd_status; + + if ((utr->setup.bmRequestType & 0x80) == REQ_TYPE_OUT) + token = QTD_ERR_COUNTER | QTD_PID_OUT | QTD_STS_ACTIVE; + else + token = QTD_ERR_COUNTER | QTD_PID_IN | QTD_STS_ACTIVE; + + qtd_data->qh = qh; + //qtd_data->utr = utr; + write_qtd_bptr(qtd_data, (uint32_t)utr->buff, utr->data_len); + append_to_qtd_list_of_QH(qh, qtd_data); + qtd_data->Token = QTD_DT | (utr->data_len << 16) | token; + } + else + { + qtd_setup->Next_qTD = (uint32_t)qtd_status; + } + + /*------------------------------------------------------------------------------------*/ + /* prepare USTSR stage qTD */ + /*------------------------------------------------------------------------------------*/ + qtd_status->Next_qTD = (uint32_t)_ghost_qtd; + qtd_status->Alt_Next_qTD = QTD_LIST_END; + + if ((utr->setup.bmRequestType & 0x80) == REQ_TYPE_OUT) + token = QTD_ERR_COUNTER | QTD_PID_IN | QTD_STS_ACTIVE; + else + token = QTD_ERR_COUNTER | QTD_PID_OUT | QTD_STS_ACTIVE; + + qtd_status->qh = qh; + //qtd_status->utr = utr; + append_to_qtd_list_of_QH(qh, qtd_status); + qtd_status->Token = QTD_DT | QTD_IOC | token; + + /*------------------------------------------------------------------------------------*/ + /* Update QH overlay */ + /*------------------------------------------------------------------------------------*/ + qh->Curr_qTD = 0; + qh->OL_Next_qTD = (uint32_t)qtd_setup; + qh->OL_Alt_Next_qTD = QTD_LIST_END; + qh->OL_Token = 0; + + /*------------------------------------------------------------------------------------*/ + /* Link QH and start asynchronous transfer */ + /*------------------------------------------------------------------------------------*/ + if (is_new_qh) + { + qh->HLink = _H_qh->HLink; + _H_qh->HLink = QH_HLNK_QH(qh); + } + + /* Start transfer */ + _ehci->UCMDR |= HSUSBH_UCMDR_ASEN_Msk; /* start asynchronous transfer */ + return 0; +} + +static int ehci_bulk_xfer(UTR_T *utr) +{ + UDEV_T *udev; + EP_INFO_T *ep = utr->ep; + QH_T *qh; + qTD_T *qtd, *qtd_pre; + uint32_t data_len, xfer_len; + uint8_t *buff; + uint32_t token; + int is_new_qh = 0; + + //USB_debug("Bulk XFER =>\n"); + // dump_ehci_asynclist_simple(); + + udev = utr->udev; + + if (ep->hw_pipe != NULL) + { + qh = (QH_T *)ep->hw_pipe ; + if (qh->qtd_list) + { + return USBH_ERR_EHCI_QH_BUSY; + } + } + else + { + qh = alloc_ehci_QH(); + if (qh == NULL) + return USBH_ERR_MEMORY_OUT; + is_new_qh = 1; + write_qh(udev, ep, qh); + ep->hw_pipe = (void *)qh; /* associate QH with endpoint */ + } + + /*------------------------------------------------------------------------------------*/ + /* Prepare qTDs */ + /*------------------------------------------------------------------------------------*/ + data_len = utr->data_len; + buff = utr->buff; + qtd_pre = NULL; + + while (data_len > 0) + { + qtd = alloc_ehci_qTD(utr); + if (qtd == NULL) /* failed to allocate a qTD */ + { + qtd = qh->qtd_list; + while (qtd != NULL) + { + qtd_pre = qtd; + qtd = qtd->next; + free_ehci_qTD(qtd_pre); + } + if (is_new_qh) + { + free_ehci_QH(qh); + ep->hw_pipe = NULL; + } + return USBH_ERR_MEMORY_OUT; + } + + if ((ep->bEndpointAddress & EP_ADDR_DIR_MASK) == EP_ADDR_DIR_OUT) + token = QTD_ERR_COUNTER | QTD_PID_OUT | QTD_STS_ACTIVE; + else + token = QTD_ERR_COUNTER | QTD_PID_IN | QTD_STS_ACTIVE; + + if (data_len > 0x4000) /* force maximum x'fer length 16K per qTD */ + xfer_len = 0x4000; + else + xfer_len = data_len; /* remaining data length < 4K */ + + qtd->qh = qh; + qtd->Next_qTD = (uint32_t)_ghost_qtd; + qtd->Alt_Next_qTD = QTD_LIST_END; //(uint32_t)_ghost_qtd; + write_qtd_bptr(qtd, (uint32_t)buff, xfer_len); + append_to_qtd_list_of_QH(qh, qtd); + qtd->Token = (xfer_len << 16) | token; + + buff += xfer_len; /* advanced buffer pointer */ + data_len -= xfer_len; + + if (data_len == 0) /* is this the latest qTD? */ + { + qtd->Token |= QTD_IOC; /* ask to raise an interrupt on the last qTD */ + qtd->Next_qTD = (uint32_t)_ghost_qtd; /* qTD list end */ + } + + if (qtd_pre != NULL) + qtd_pre->Next_qTD = (uint32_t)qtd; + qtd_pre = qtd; + } + + //USB_debug("utr=0x%x, qh=0x%x, qtd=0x%x\n", (int)utr, (int)qh, (int)qh->qtd_list); + + qtd = qh->qtd_list; + + qh->OL_Next_qTD = (uint32_t)qtd; + + /*------------------------------------------------------------------------------------*/ + /* Link QH and start asynchronous transfer */ + /*------------------------------------------------------------------------------------*/ + if (is_new_qh) + { + memcpy(&(qh->OL_Bptr[0]), &(qtd->Bptr[0]), 20); + qh->Curr_qTD = (uint32_t)qtd; + + qh->OL_Token = 0; //qtd->Token; + + if (utr->ep->bToggle) + qh->OL_Token |= QTD_DT; + + qh->HLink = _H_qh->HLink; + _H_qh->HLink = QH_HLNK_QH(qh); + } + + /* Start transfer */ + _ehci->UCMDR |= HSUSBH_UCMDR_ASEN_Msk; /* start asynchronous transfer */ + + return 0; +} + +static int ehci_int_xfer(UTR_T *utr) +{ + UDEV_T *udev = utr->udev; + EP_INFO_T *ep = utr->ep; + QH_T *qh, *iqh; + qTD_T *qtd; + uint32_t token; + int8_t is_new_qh = 0; + + if (ep->hw_pipe != NULL) + { + qh = (QH_T *)ep->hw_pipe ; + if (qh->qtd_list) + return USBH_ERR_EHCI_QH_BUSY; + } + else + { + qh = alloc_ehci_QH(); + if (qh == NULL) + return USBH_ERR_MEMORY_OUT; + is_new_qh = 1; + write_qh(udev, ep, qh); + qh->Chrst &= ~0xF0000000; + + if (udev->speed == SPEED_HIGH) + { + qh->Cap = (0x1 << QH_MULT_Pos) | (qh->Cap & 0xff) | make_int_s_mask(ep->bInterval); + } + else + { + qh->Cap = (0x1 << QH_MULT_Pos) | (qh->Cap & ~(QH_C_MASK_Msk | QH_S_MASK_Msk)) | 0x7802; + } + ep->hw_pipe = (void *)qh; /* associate QH with endpoint */ + } + + /*------------------------------------------------------------------------------------*/ + /* Prepare qTD */ + /*------------------------------------------------------------------------------------*/ + qtd = alloc_ehci_qTD(utr); + if (qtd == NULL) /* failed to allocate a qTD */ + { + if (is_new_qh) + { + free_ehci_QH(qh); + ep->hw_pipe = NULL; + } + return USBH_ERR_MEMORY_OUT; + } + + if ((ep->bEndpointAddress & EP_ADDR_DIR_MASK) == EP_ADDR_DIR_OUT) + token = QTD_ERR_COUNTER | QTD_PID_OUT | QTD_STS_ACTIVE; + else + token = QTD_ERR_COUNTER | QTD_PID_IN | QTD_STS_ACTIVE; + + qtd->qh = qh; + qtd->Next_qTD = QTD_LIST_END; //(uint32_t)_ghost_qtd; + qtd->Alt_Next_qTD = QTD_LIST_END; //(uint32_t)_ghost_qtd; + write_qtd_bptr(qtd, (uint32_t)utr->buff, utr->data_len); + append_to_qtd_list_of_QH(qh, qtd); + qtd->Token = QTD_IOC | (utr->data_len << 16) | token; + + DISABLE_EHCI_IRQ(); + + USB_debug("ehci_int_xfer - qh: 0x%x, 0x%x, 0x%x, qtd: 0x%x\n", (int)qh, (int)qh->Chrst, (int)qh->Cap, (int)qtd); + + qh->OL_Next_qTD = (uint32_t)qtd; + + if (is_new_qh) + { + memcpy(&(qh->OL_Bptr[0]), &(qtd->Bptr[0]), 20); + qh->Curr_qTD = (uint32_t)qtd; + qh->OL_Token = qtd->Token; + + if (udev->speed == SPEED_HIGH) /* get head node of this interval */ + iqh = get_int_tree_head_node(ep->bInterval); + else + iqh = get_int_tree_head_node(ep->bInterval * 8); + qh->HLink = iqh->HLink; /* Add to list of the same interval */ + iqh->HLink = QH_HLNK_QH(qh); + } + + ENABLE_EHCI_IRQ(); + + _ehci->UCMDR |= HSUSBH_UCMDR_PSEN_Msk; /* periodic list enable */ + return 0; +} + +/* + * Quit current trasnfer via UTR or hardware EP. + */ +static int ehci_quit_xfer(UTR_T *utr, EP_INFO_T *ep) +{ + QH_T *qh; + + // USB_debug("ehci_quit_xfer - utr: 0x%x, ep: 0x%x\n", (int)utr, (int)ep); + + DISABLE_EHCI_IRQ(); + if (ehci_quit_iso_xfer(utr, ep) == 0) + { + ENABLE_EHCI_IRQ(); + return 0; + } + ENABLE_EHCI_IRQ(); + + if (utr != NULL) + { + if (utr->ep == NULL) + return USBH_ERR_NOT_FOUND; + + qh = (QH_T *)(utr->ep->hw_pipe); + + if (!qh) + return USBH_ERR_NOT_FOUND; + + /* add the QH to remove list, it will be removed on the next IAAD interrupt */ + move_qh_to_remove_list(qh); + utr->ep->hw_pipe = NULL; + } + + if ((ep != NULL) && (ep->hw_pipe != NULL)) + { + qh = (QH_T *)(ep->hw_pipe); + /* add the QH to remove list, it will be removed on the next IAAD interrupt */ + move_qh_to_remove_list(qh); + ep->hw_pipe = NULL; + } + usbh_delay_ms(2); + + return 0; +} + +static int visit_qtd(qTD_T *qtd) +{ + if ((qtd->Token == 0x11197B7F) || (qtd->Token == 0x1197B7F)) + return 0; /* A Dummy qTD or qTD on writing, don't touch it. */ + + // USB_debug("Visit qtd 0x%x - 0x%x\n", (int)qtd, qtd->Token); + + if ((qtd->Token & QTD_STS_ACTIVE) == 0) + { + if (qtd->Token & (QTD_STS_HALT | QTD_STS_DATA_BUFF_ERR | QTD_STS_BABBLE | QTD_STS_XactErr | QTD_STS_MISS_MF)) + { + USB_error("qTD 0x%x error token=0x%x! 0x%x\n", (int)qtd, qtd->Token, qtd->Bptr[0]); + if (qtd->utr->status == 0) + qtd->utr->status = USBH_ERR_TRANSACTION; + } + else + { + if ((qtd->Token & QTD_PID_Msk) != QTD_PID_SETUP) + { + qtd->utr->xfer_len += qtd->xfer_len - QTD_TODO_LEN(qtd->Token); + // USB_debug("0x%x utr->xfer_len += %d\n", qtd->Token, qtd->xfer_len - QTD_TODO_LEN(qtd->Token)); + } + } + return 1; + } + return 0; +} + +static void scan_asynchronous_list() +{ + QH_T *qh, *qh_tmp; + qTD_T *q_pre, *qtd, *qtd_tmp; + UTR_T *utr; + + qh = QH_PTR(_H_qh->HLink); + while (qh != _H_qh) + { + // USB_debug("Scan qh=0x%x, 0x%x\n", (int)qh, qh->OL_Token); + + utr = NULL; + qtd = qh->qtd_list; + while (qtd != NULL) + { + if (visit_qtd(qtd)) /* if TRUE, reclaim this qtd */ + { + /* qTD is completed, will remove it */ + utr = qtd->utr; + if (qtd == qh->qtd_list) + qh->qtd_list = qtd->next; /* unlink the qTD from qtd_list */ + else + q_pre->next = qtd->next; /* unlink the qTD from qtd_list */ + + qtd_tmp = qtd; /* remember this qTD for freeing later */ + qtd = qtd->next; /* advance to the next qTD */ + + qtd_tmp->next = qh->done_list; /* push this qTD to QH's done list */ + qh->done_list = qtd_tmp; + } + else + { + q_pre = qtd; /* remember this qTD as a preceder */ + qtd = qtd->next; /* advance to next qTD */ + } + } + + qh_tmp = qh; + qh = QH_PTR(qh->HLink); /* advance to the next QH */ + + /* If all TDs are done, call-back to requester and then remove this QH. */ + if ((qh_tmp->qtd_list == NULL) && utr) + { + // printf("T %d [%d]\n", (qh_tmp->Chrst>>8)&0xf, (qh_tmp->OL_Token&QTD_DT) ? 1 : 0); + if (qh_tmp->OL_Token & QTD_DT) + utr->ep->bToggle = 1; + else + utr->ep->bToggle = 0; + + utr->bIsTransferDone = 1; + if (utr->func) + utr->func(utr); + + _ehci->UCMDR |= HSUSBH_UCMDR_IAAD_Msk; /* trigger IAA to reclaim done_list */ + } + } +} + +static void scan_periodic_frame_list() +{ + QH_T *qh; + qTD_T *qtd; + UTR_T *utr; + + /*------------------------------------------------------------------------------------*/ + /* Scan interrupt frame list */ + /*------------------------------------------------------------------------------------*/ + qh = _Iqh[NUM_IQH - 1]; + while (qh != NULL) + { + qtd = qh->qtd_list; /* There's only one qTD in list at most. */ + + if (qtd == NULL) + { + /* empty QH */ + qh = QH_PTR(qh->HLink); /* advance to the next QH */ + continue; + } + + if (visit_qtd(qtd)) /* if TRUE, reclaim this qtd */ + { + qtd->next = qh->done_list; /* push qTD into the done list */ + qh->done_list = qtd; + qh->qtd_list = NULL; /* qtd_list becomes empty */ + } + + qtd = qh->done_list; + + /* If all TDs are done, call-back to requester and then remove this QH. */ + if ((qtd != NULL) && (qh->qtd_list == NULL)) + { + utr = qtd->utr; + + if (qh->OL_Token & QTD_DT) + utr->ep->bToggle = 1; + else + utr->ep->bToggle = 0; + + utr->bIsTransferDone = 1; + if (utr->func) + utr->func(utr); + + _ehci->UCMDR |= HSUSBH_UCMDR_IAAD_Msk; /* trigger IAA to reclaim done_list */ + } + + qh = QH_PTR(qh->HLink); /* advance to the next QH */ + } + + /*------------------------------------------------------------------------------------*/ + /* Scan isochronous frame list */ + /*------------------------------------------------------------------------------------*/ + + scan_isochronous_list(); +} + +void iaad_remove_qh() +{ + QH_T *qh; + qTD_T *qtd; + UTR_T *utr; + + /*------------------------------------------------------------------------------------*/ + /* Remove all QHs in qh_remove_list... */ + /*------------------------------------------------------------------------------------*/ + while (qh_remove_list != NULL) + { + qh = qh_remove_list; + qh_remove_list = qh->next; + + // USB_debug("iaad_remove_qh - remove QH 0x%x\n", (int)qh); + + while (qh->done_list) /* we can free the qTDs now */ + { + qtd = qh->done_list; + qh->done_list = qtd->next; + free_ehci_qTD(qtd); + } + + if (qh->qtd_list != NULL) /* still have incomplete qTDs? */ + { + utr = qh->qtd_list->utr; + while (qh->qtd_list) + { + qtd = qh->qtd_list; + qh->qtd_list = qtd->next; + free_ehci_qTD(qtd); + } + utr->status = USBH_ERR_ABORT; + utr->bIsTransferDone = 1; + if (utr->func) + utr->func(utr); /* call back */ + } + free_ehci_QH(qh); /* free the QH */ + } + + /*------------------------------------------------------------------------------------*/ + /* Free all qTD in done_list of each asynchronous QH */ + /*------------------------------------------------------------------------------------*/ + qh = QH_PTR(_H_qh->HLink); + while (qh != _H_qh) + { + while (qh->done_list) /* we can free the qTDs now */ + { + qtd = qh->done_list; + qh->done_list = qtd->next; + free_ehci_qTD(qtd); + } + qh = QH_PTR(qh->HLink); /* advance to the next QH */ + } + + /*------------------------------------------------------------------------------------*/ + /* Free all qTD in done_list of each QH of periodic frame list */ + /*------------------------------------------------------------------------------------*/ + qh = _Iqh[NUM_IQH - 1]; + while (qh != NULL) + { + while (qh->done_list) /* we can free the qTDs now */ + { + qtd = qh->done_list; + qh->done_list = qtd->next; + free_ehci_qTD(qtd); + } + qh = QH_PTR(qh->HLink); /* advance to the next QH */ + } +} + +//void EHCI_IRQHandler(void) +void nu_ehci_isr(int vector, void *param) +{ + uint32_t intsts; + + intsts = _ehci->USTSR; + _ehci->USTSR = intsts; /* clear interrupt status */ + + //USB_debug("ehci int_sts = 0x%x\n", intsts); + + if (intsts & HSUSBH_USTSR_UERRINT_Msk) + { + USB_error("Transfer error!\n"); + } + + if (intsts & HSUSBH_USTSR_USBINT_Msk) + { + /* some transfers completed, travel asynchronous */ + /* and periodic lists to find and reclaim them. */ + scan_asynchronous_list(); + + scan_periodic_frame_list(); + } + + if (intsts & HSUSBH_USTSR_IAA_Msk) + { + iaad_remove_qh(); + } +} + +static UDEV_T *ehci_find_device_by_port(int port) +{ + UDEV_T *udev; + + udev = g_udev_list; + while (udev != NULL) + { + if ((udev->parent == NULL) && (udev->port_num == port) && (udev->speed == SPEED_HIGH)) + return udev; + udev = udev->next; + } + return NULL; +} + +static int ehci_rh_port_reset(int port) +{ + int retry; + int reset_time; + uint32_t t0; + + reset_time = usbh_tick_from_millisecond(PORT_RESET_TIME_MS); + + for (retry = 0; retry < PORT_RESET_RETRY; retry++) + { + _ehci->UPSCR[port] = (_ehci->UPSCR[port] | HSUSBH_UPSCR_PRST_Msk) & ~HSUSBH_UPSCR_PE_Msk; + + t0 = usbh_get_ticks(); + while (usbh_get_ticks() - t0 < (reset_time) + 1) ; /* wait at least 50 ms */ + + _ehci->UPSCR[port] &= ~HSUSBH_UPSCR_PRST_Msk; + + t0 = usbh_get_ticks(); + while (usbh_get_ticks() - t0 < (reset_time) + 1) + { + if (!(_ehci->UPSCR[port] & HSUSBH_UPSCR_CCS_Msk) || + ((_ehci->UPSCR[port] & (HSUSBH_UPSCR_CCS_Msk | HSUSBH_UPSCR_PE_Msk)) == (HSUSBH_UPSCR_CCS_Msk | HSUSBH_UPSCR_PE_Msk))) + goto port_reset_done; + } + reset_time += PORT_RESET_RETRY_INC_MS; + } + + USB_debug("EHCI port %d - port reset failed!\n", port + 1); + return USBH_ERR_PORT_RESET; + +port_reset_done: + if ((_ehci->UPSCR[port] & HSUSBH_UPSCR_CCS_Msk) == 0) /* check again if device disconnected */ + { + _ehci->UPSCR[port] |= HSUSBH_UPSCR_CSC_Msk; /* clear CSC */ + return USBH_ERR_DISCONNECTED; + } + _ehci->UPSCR[port] |= HSUSBH_UPSCR_PEC_Msk; /* clear port enable change status */ + return USBH_OK; /* port reset success */ +} + +static int ehci_rh_polling(void) +{ + UDEV_T *udev; + int ret, change = 0; + int port; + int connect_status, t0, debounce_tick; + + for (port = 0; port < EHCI_PORT_CNT; port++) + { + if (!(_ehci->UPSCR[port] & HSUSBH_UPSCR_CSC_Msk)) + continue; + + change = 1; + USB_debug("EHCI port%d status change: 0x%x\n", port + 1, _ehci->UPSCR[port]); + + /*--------------------------------------------------------------------------------*/ + /* Disconnect the devices attached to this port. */ + /*--------------------------------------------------------------------------------*/ + while (1) + { + udev = ehci_find_device_by_port(port + 1); + if (udev == NULL) + break; + usbh_disconnect_device(udev); + } + + /*--------------------------------------------------------------------------------*/ + /* Port de-bounce */ + /*--------------------------------------------------------------------------------*/ + t0 = usbh_get_ticks(); + debounce_tick = usbh_tick_from_millisecond(HUB_DEBOUNCE_TIME); + connect_status = _ehci->UPSCR[port] & HSUSBH_UPSCR_CCS_Msk; + while (usbh_get_ticks() - t0 < debounce_tick) + { + if (connect_status != (_ehci->UPSCR[port] & HSUSBH_UPSCR_CCS_Msk)) + { + /* reset stable time counting */ + t0 = usbh_get_ticks(); + connect_status = _ehci->UPSCR[port] & HSUSBH_UPSCR_CCS_Msk; + } + } + + _ehci->UPSCR[port] |= HSUSBH_UPSCR_CSC_Msk; /* clear connect status change bit */ + + if (connect_status == HSUSBH_UPSCR_CCS_Msk) + { + /*----------------------------------------------------------------------------*/ + /* A new device connected. */ + /*----------------------------------------------------------------------------*/ + if (ehci_rh_port_reset(port) != USBH_OK) + { + /* port reset failed, maybe an USB 1.1 device */ + _ehci->UPSCR[port] |= HSUSBH_UPSCR_PO_Msk; /* change port owner to OHCI */ + _ehci->UPSCR[port] |= HSUSBH_UPSCR_CSC_Msk; /* clear all status change bits */ + return 0; + } + + /* + * Port reset success. Start to enumerate this new device. + */ + udev = alloc_device(); + if (udev == NULL) + return 0; /* out-of-memory, do nothing... */ + + udev->parent = NULL; + udev->port_num = port + 1; + udev->speed = SPEED_HIGH; + udev->hc_driver = &ehci_driver; + + ret = usbh_connect_device(udev); + if (ret < 0) + { + USB_error("connect_device error! [%d]\n", ret); + free_device(udev); + } + } + else + { + /* Device disconnected */ + while (1) + { + udev = ehci_find_device_by_port(port + 1); + if (udev == NULL) + break; + usbh_disconnect_device(udev); + } + } + } + return change; +} + + +HC_DRV_T ehci_driver = +{ + ehci_init, /* init */ + ehci_shutdown, /* shutdown */ + ehci_suspend, /* suspend */ + ehci_resume, /* resume */ + ehci_ctrl_xfer, /* ctrl_xfer */ + ehci_bulk_xfer, /* bulk_xfer */ + ehci_int_xfer, /* int_xfer */ + ehci_iso_xfer, /* iso_xfer */ + ehci_quit_xfer, /* quit_xfer */ + ehci_rh_port_reset, /* rthub_port_reset */ + ehci_rh_polling /* rthub_polling */ +}; + + +/// @endcond HIDDEN_SYMBOLS + +/*** (C) COPYRIGHT 2017 Nuvoton Technology Corp. ***/ diff --git a/bsp/nuvoton/libraries/nuc980/UsbHostLib/src/ehci_iso.c b/bsp/nuvoton/libraries/nuc980/UsbHostLib/src/ehci_iso.c new file mode 100644 index 0000000000..749314cca9 --- /dev/null +++ b/bsp/nuvoton/libraries/nuc980/UsbHostLib/src/ehci_iso.c @@ -0,0 +1,918 @@ +/**************************************************************************//** + * @file ehci_iso.c + * @version V1.10 + * $Revision: 11 $ + * $Date: 14/10/03 1:54p $ + * @brief USB EHCI isochornous transfer driver. + * + * @note + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2017 Nuvoton Technology Corp. All rights reserved. +*****************************************************************************/ + +#include +#include +#include + +#include "nuc980.h" + +#include "usb.h" +#include "hub.h" + + +/// @cond HIDDEN_SYMBOLS + +uint32_t g_flr_cnt; /* frame list rollover counter */ + +ISO_EP_T *iso_ep_list; /* list of activated isochronous pipes */ + +extern uint32_t *_PFList; /* Periodic frame list */ + +static const uint16_t sitd_OUT_Smask [] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f }; + +static int ehci_iso_split_xfer(UTR_T *utr, ISO_EP_T *iso_ep); + +/* + * Inspect the iTD can be reclaimed or not. If yes, collect the transaction results. + * Return: 1 - reclaimed + * 0 - not completed + */ +static int review_itd(iTD_T *itd) +{ + UTR_T *utr; + uint32_t frnidx = itd->sched_frnidx; + uint32_t now_frame = (_ehci->UFINDR >> 3) & 0x3FF; + int i, fidx; + + // printf("R - %d %d, 0x%x\n", now_frame, frnidx, itd->Transaction[0]); + + if (now_frame == frnidx) + { + for (i = 0; i < 8; i++) + { + if (itd->Transaction[i] & ITD_STATUS_ACTIVE) + return 0; /* have any not completed frames */ + } + } + else if (now_frame > frnidx) + { + if ((now_frame - frnidx) > EHCI_ISO_RCLM_RANGE) + return 0; /* don't touch it */ + } + else + { + if (now_frame + FL_SIZE - frnidx > EHCI_ISO_RCLM_RANGE) + return 0; /* don't touch it */ + } + + /* + * Reclaim this iTD + */ + utr = itd->utr; + fidx = itd->fidx; + for (i = 0; i < 8; i++) + { + if (!(itd->trans_mask & (0x1 << i))) + continue; /* not scheduled micro-frame */ + + if (ITD_STATUS(itd->Transaction[i])) + { + if (itd->Transaction[i] & ITD_STATUS_ACTIVE) + { + utr->iso_status[fidx] = USBH_ERR_NOT_ACCESS0; + utr->status = USBH_ERR_NOT_ACCESS0; + } + else if (itd->Transaction[i] & ITD_STATUS_BABBLE) + { + utr->iso_status[fidx] = USBH_ERR_BABBLE_DETECTED; + utr->status = USBH_ERR_TRANSFER; + } + else if (itd->Transaction[i] & ITD_STATUS_BUFF_ERR) + { + utr->iso_status[fidx] = USBH_ERR_DATA_BUFF; + utr->status = USBH_ERR_TRANSFER; + } + else + { + utr->iso_status[fidx] = USBH_ERR_TRANSACTION; + utr->status = USBH_ERR_TRANSFER; + } + } + else + { + utr->iso_status[fidx] = 0; + utr->iso_xlen[fidx] = ITD_XFER_LEN(itd->Transaction[i]); + } + fidx++; + } + utr->td_cnt--; + + if (utr->td_cnt == 0) /* All iTD of this UTR done */ + { + utr->bIsTransferDone = 1; + if (utr->func) + utr->func(utr); + } + + return 1; /* to be reclaimed */ +} + +/* + * Inspect the siTD can be reclaimed or not. If yes, collect the transaction results. + * Return: 1 - reclaimed + * 0 - not completed + */ +static int review_sitd(siTD_T *sitd) +{ + UTR_T *utr; + uint32_t frnidx = sitd->sched_frnidx; + uint32_t now_frame = (_ehci->UFINDR >> 3) & 0x3FF; + int fidx; + uint32_t TotalBytesToTransfer; + + if (now_frame == frnidx) + { + if (SITD_STATUS(sitd->StsCtrl) == SITD_STATUS_ACTIVE) + return 0; + } + else if (now_frame > frnidx) + { + if ((now_frame - frnidx) > EHCI_ISO_RCLM_RANGE) + return 0; /* don't touch it */ + } + else + { + if (now_frame + FL_SIZE - frnidx > EHCI_ISO_RCLM_RANGE) + return 0; /* don't touch it */ + } + + /* + * Reclaim this siTD + */ + utr = sitd->utr; + fidx = sitd->fidx; + + if (SITD_STATUS(sitd->StsCtrl)) + { + if (sitd->StsCtrl & SITD_STATUS_ACTIVE) + { + utr->iso_status[fidx] = USBH_ERR_NOT_ACCESS0; + } + else if (sitd->StsCtrl & SITD_BABBLE_DETECTED) + { + utr->iso_status[fidx] = USBH_ERR_BABBLE_DETECTED; + utr->status = USBH_ERR_TRANSFER; + } + else if (sitd->StsCtrl & SITD_STATUS_BUFF_ERR) + { + utr->iso_status[fidx] = USBH_ERR_DATA_BUFF; + utr->status = USBH_ERR_TRANSFER; + } + else + { + utr->iso_status[fidx] = USBH_ERR_TRANSACTION; + utr->status = USBH_ERR_TRANSFER; + } + } + else + { + TotalBytesToTransfer = (sitd->StsCtrl & SITD_XFER_CNT_Msk) >> SITD_XFER_CNT_Pos; + utr->iso_xlen[fidx] = utr->iso_xlen[fidx] - TotalBytesToTransfer; + utr->iso_status[fidx] = 0; + } + utr->td_cnt--; + + if (utr->td_cnt == 0) /* All iTD of this UTR done */ + { + utr->bIsTransferDone = 1; + if (utr->func) + utr->func(utr); + } + return 1; /* to be reclaimed */ +} + +/* + * Some iTD/siTD may be scheduled but not serviced due to time missed. + * This function scan several earlier frames and drop unserviced iTD/siTD if found. + */ +void scan_isochronous_list(void) +{ + ISO_EP_T *iso_ep = iso_ep_list; + iTD_T *itd, *itd_pre, *p; + siTD_T *sitd, *sitd_pre, *sp; + uint32_t frnidx; + + DISABLE_EHCI_IRQ(); + + while (iso_ep != NULL) /* Search all activated iso endpoints */ + { + /*--------------------------------------------------------------------------------*/ + /* Scan all iTDs */ + /*--------------------------------------------------------------------------------*/ + itd = iso_ep->itd_list; /* get the first iTD from iso_ep's iTD list */ + itd_pre = NULL; + while (itd != NULL) /* traverse all iTDs of itd list */ + { + if (review_itd(itd)) /* inspect and reclaim iTD */ + { + /*------------------------------------------------------------------------*/ + /* Remove this iTD from period frame list */ + /*------------------------------------------------------------------------*/ + frnidx = itd->sched_frnidx; + if (_PFList[frnidx] == ITD_HLNK_ITD(itd)) + { + /* is the first entry, just change to next */ + _PFList[frnidx] = itd->Next_Link; + } + else + { + p = ITD_PTR(_PFList[frnidx]); /* find the preceding iTD */ + while ((ITD_PTR(p->Next_Link) != itd) && (p != NULL)) + { + p = ITD_PTR(p->Next_Link); + } + + if (p == NULL) /* link list out of control! */ + { + USB_error("An iTD lost refernece to periodic frame list! 0x%x -> %d\n", (int)itd, frnidx); + } + else /* remove iTD from list */ + { + p->Next_Link = itd->Next_Link; + } + } + + /*------------------------------------------------------------------------*/ + /* Remove this iTD from iso_ep's iTD list */ + /*------------------------------------------------------------------------*/ + if (itd_pre == NULL) + { + iso_ep->itd_list = itd->next; + } + else + { + itd_pre->next = itd->next; + } + p = itd->next; + free_ehci_iTD(itd); + itd = p; + } + else + { + itd_pre = itd; + itd = itd->next; /* traverse to the next iTD of iTD list */ + } + } + + /*--------------------------------------------------------------------------------*/ + /* Scan all siTDs */ + /*--------------------------------------------------------------------------------*/ + sitd = iso_ep->sitd_list; /* get the first siTD from iso_ep's siTD list */ + sitd_pre = NULL; + while (sitd != NULL) /* traverse all siTDs of sitd list */ + { + if (review_sitd(sitd)) /* inspect and reclaim siTD */ + { + /*------------------------------------------------------------------------*/ + /* Remove this siTD from period frame list */ + /*------------------------------------------------------------------------*/ + frnidx = sitd->sched_frnidx; + if (_PFList[frnidx] == SITD_HLNK_SITD(sitd)) + { + /* is the first entry, just change to next */ + _PFList[frnidx] = sitd->Next_Link; + } + else + { + sp = SITD_PTR(_PFList[frnidx]); /* find the preceding siTD */ + while ((SITD_PTR(sp->Next_Link) != sitd) && (sp != NULL)) + { + sp = SITD_PTR(sp->Next_Link); + } + + if (sp == NULL) /* link list out of control! */ + { + USB_error("An siTD lost reference to periodic frame list! 0x%x -> %d\n", (int)sitd, frnidx); + } + else /* remove iTD from list */ + { + sp->Next_Link = sitd->Next_Link; + } + } + + /*------------------------------------------------------------------------*/ + /* Remove this siTD from iso_ep's siTD list */ + /*------------------------------------------------------------------------*/ + if (sitd_pre == NULL) + { + iso_ep->sitd_list = sitd->next; + } + else + { + sitd_pre->next = sitd->next; + } + sp = sitd->next; + free_ehci_siTD(sitd); + sitd = sp; + } + else + { + sitd_pre = sitd; + sitd = sitd->next; /* traverse to the next siTD of siTD list */ + } + } + + iso_ep = iso_ep->next; + } + + ENABLE_EHCI_IRQ(); +} + + +static void write_itd_info(UTR_T *utr, iTD_T *itd) +{ + UDEV_T *udev = utr->udev; + EP_INFO_T *ep = utr->ep; /* reference to isochronous endpoint */ + uint32_t buff_page_addr; + int i; + + buff_page_addr = itd->buff_base & 0xFFFFF000; /* 4K page */ + + for (i = 0; i < 7; i++) + { + itd->Bptr[i] = buff_page_addr + (0x1000 * i); + } + /* EndPtr R Device Address */ + itd->Bptr[0] |= (udev->dev_num) | ((ep->bEndpointAddress & 0xF) << ITD_EP_NUM_Pos); + itd->Bptr[1] |= ep->wMaxPacketSize; /* Maximum Packet Size */ + + if ((ep->bEndpointAddress & EP_ADDR_DIR_MASK) == EP_ADDR_DIR_IN) /* I/O */ + itd->Bptr[1] |= ITD_DIR_IN; + else + itd->Bptr[1] |= ITD_DIR_OUT; + + itd->Bptr[2] |= (ep->wMaxPacketSize + 1023) / 1024; /* Mult */ +} + +static void write_itd_micro_frame(UTR_T *utr, int fidx, iTD_T *itd, int mf) +{ + uint32_t buff_addr; + + buff_addr = (uint32_t)(utr->iso_buff[fidx]); /* xfer buffer start address of this frame */ + + itd->Transaction[mf] = ITD_STATUS_ACTIVE | /* Status */ + ((utr->iso_xlen[fidx] & 0xFFF) << ITD_XLEN_Pos) | /* Transaction Length */ + ((buff_addr & 0xFFFFF000) - (itd->buff_base & 0xFFFFF000)) | /* PG */ + (buff_addr & 0xFFF); /* Transaction offset */ +} + + +static void remove_iso_ep_from_list(ISO_EP_T *iso_ep) +{ + ISO_EP_T *p; + + if (iso_ep_list == iso_ep) + { + iso_ep_list = iso_ep->next; /* it's the first entry, remove it */ + return; + } + + p = iso_ep_list; /* find the previous entry of iso_ep */ + while (p->next != NULL) + { + if (p->next == iso_ep) + { + break; + } + p = p->next; + } + + if (p->next == NULL) + { + return; /* not found */ + } + p->next = iso_ep->next; /* remove iso_ep from list */ +} + + +static __inline void add_itd_to_iso_ep(ISO_EP_T *iso_ep, iTD_T *itd) +{ + iTD_T *p; + + itd->next = NULL; + + if (iso_ep->itd_list == NULL) + { + iso_ep->itd_list = itd; + return; + } + + /* + * Find the tail entry of iso_ep->itd_list + */ + p = iso_ep->itd_list; + while (p->next != NULL) + { + p = p->next; + } + p->next = itd; +} + +int ehci_iso_xfer(UTR_T *utr) +{ + EP_INFO_T *ep = utr->ep; /* reference to isochronous endpoint */ + ISO_EP_T *iso_ep; /* software iso endpoint descriptor */ + iTD_T *itd, *itd_next, *itd_list = NULL; + int i, itd_cnt; + int trans_mask; /* bit mask of used xfer in an iTD */ + int fidx; /* index to the 8 iso frames of UTR */ + int interval; /* frame interval of iTD */ + + if (ep->hw_pipe != NULL) + { + iso_ep = (ISO_EP_T *)ep->hw_pipe; /* get reference of the isochronous endpoint */ + + if (utr->bIsoNewSched) + iso_ep->next_frame = (((_ehci->UFINDR + (EHCI_ISO_DELAY * 8)) & HSUSBH_UFINDR_FI_Msk) >> 3) & 0x3FF; + } + else + { + /* first time transfer of this iso endpoint */ + iso_ep = usbh_alloc_mem(sizeof(*iso_ep)); + if (iso_ep == NULL) + return USBH_ERR_MEMORY_OUT; + + memset(iso_ep, 0, sizeof(*iso_ep)); + iso_ep->ep = ep; + iso_ep->next_frame = (((_ehci->UFINDR + (EHCI_ISO_DELAY * 8)) & HSUSBH_UFINDR_FI_Msk) >> 3) & 0x3FF; + + ep->hw_pipe = iso_ep; + + /* + * Add this iso_ep into iso_ep_list + */ + DISABLE_EHCI_IRQ(); + iso_ep->next = iso_ep_list; + iso_ep_list = iso_ep; + ENABLE_EHCI_IRQ(); + } + + if (utr->udev->speed == SPEED_FULL) + return ehci_iso_split_xfer(utr, iso_ep); + + /*------------------------------------------------------------------------------------*/ + /* Allocate iTDs */ + /*------------------------------------------------------------------------------------*/ + + if (ep->bInterval < 2) /* transfer interval is 1 micro-frame */ + { + trans_mask = 0xFF; + itd_cnt = 1; /* required 1 iTD for one UTR */ + interval = 1; /* iTD frame interval of this endpoint */ + } + else if (ep->bInterval < 4) /* transfer interval is 2 micro-frames */ + { + trans_mask = 0x55; + itd_cnt = 2; /* required 2 iTDs for one UTR */ + interval = 1; /* iTD frame interval of this endpoint */ + } + else if (ep->bInterval < 8) /* transfer interval is 4 micro-frames */ + { + trans_mask = 0x44; + itd_cnt = 4; /* required 4 iTDs for one UTR */ + interval = 1; /* iTD frame interval of this endpoint */ + } + else if (ep->bInterval < 16) /* transfer interval is 8 micro-frames */ + { + trans_mask = 0x08; /* there's 1 transfer in one iTD */ + itd_cnt = 8; /* required 8 iTDs for one UTR */ + interval = 1; /* iTD frame interval of this endpoint */ + } + else if (ep->bInterval < 32) /* transfer interval is 16 micro-frames */ + { + trans_mask = 0x10; /* there's 1 transfer in one iTD */ + itd_cnt = 8; /* required 8 iTDs for one UTR */ + interval = 2; /* iTD frame interval of this endpoint */ + } + else if (ep->bInterval < 64) /* transfer interval is 32 micro-frames */ + { + trans_mask = 0x02; /* there's 1 transfer in one iTD */ + itd_cnt = 8; /* required 8 iTDs for one UTR */ + interval = 4; /* iTD frame interval of this endpoint */ + } + else /* transfer interval is 64 micro-frames */ + { + trans_mask = 0x04; /* there's 1 transfer in one iTD */ + itd_cnt = 8; /* required 8 iTDs for one UTR */ + interval = 8; /* iTD frame interval of this endpoint */ + } + + for (i = 0; i < itd_cnt; i++) /* allocate all iTDs required by UTR */ + { + itd = alloc_ehci_iTD(); + if (itd == NULL) + goto malloc_failed; + + if (itd_list == NULL) /* link all iTDs */ + { + itd_list = itd; + } + else + { + itd->next = itd_list; + itd_list = itd; + } + } + + utr->td_cnt = itd_cnt; + + /*------------------------------------------------------------------------------------*/ + /* Fill and link all iTDs */ + /*------------------------------------------------------------------------------------*/ + + utr->iso_sf = iso_ep->next_frame; + fidx = 0; /* index to UTR iso frmes (total IF_PER_UTR) */ + + for (itd = itd_list; (itd != NULL);) + { + if (fidx >= IF_PER_UTR) /* unlikely */ + { + USB_error("EHCI driver ITD bug!?\n"); + goto malloc_failed; + } + + itd->utr = utr; + itd->fidx = fidx; /* index to UTR's n'th IF_PER_UTR frame */ + itd->buff_base = (uint32_t)(utr->iso_buff[fidx]); /* iTD buffer base is buffer of the first UTR iso frame serviced by this iTD */ + itd->trans_mask = trans_mask; + + write_itd_info(utr, itd); + + for (i = 0; i < 8; i++) /* settle xfer into micro-frames */ + { + if (!(trans_mask & (0x1 << i))) + { + itd->Transaction[i] = 0; /* not accesed */ + continue; /* not scheduled micro-frame */ + } + + write_itd_micro_frame(utr, fidx, itd, i); + + fidx++; /* preceed to next UTR iso frame */ + + if (fidx == IF_PER_UTR) /* is the last scheduled micro-frame? */ + { + /* raise interrupt on completed */ + itd->Transaction[i] |= ITD_IOC; + break; + } + } + + itd_next = itd->next; /* remember the next itd */ + + // USB_debug("Link iTD 0x%x, %d\n", (int)itd, iso_ep->next_frame); + /* + * Link iTD to period frame list + */ + DISABLE_EHCI_IRQ(); + itd->sched_frnidx = iso_ep->next_frame; /* remember it for reclamation scan */ + add_itd_to_iso_ep(iso_ep, itd); /* add to software itd list */ + itd->Next_Link = _PFList[itd->sched_frnidx]; /* keep the next link */ + _PFList[itd->sched_frnidx] = ITD_HLNK_ITD(itd); + iso_ep->next_frame = (iso_ep->next_frame + interval) % FL_SIZE; + ENABLE_EHCI_IRQ(); + + itd = itd_next; + } + + _ehci->UCMDR |= HSUSBH_UCMDR_PSEN_Msk; /* periodic list enable */ + return 0; + +malloc_failed: + + while (itd_list != NULL) + { + itd = itd_list; + itd_list = itd->next; + free_ehci_iTD(itd); + } + return USBH_ERR_MEMORY_OUT; +} + +static __inline void add_sitd_to_iso_ep(ISO_EP_T *iso_ep, siTD_T *sitd) +{ + siTD_T *p; + + sitd->next = NULL; + + if (iso_ep->sitd_list == NULL) + { + iso_ep->sitd_list = sitd; + return; + } + + /* + * Find the tail entry of iso_ep->itd_list + */ + p = iso_ep->sitd_list; + while (p->next != NULL) + { + p = p->next; + } + p->next = sitd; +} + +static void write_sitd_info(UTR_T *utr, siTD_T *sitd) +{ + UDEV_T *udev = utr->udev; + EP_INFO_T *ep = utr->ep; /* reference to isochronous endpoint */ + uint32_t buff_page_addr; + int xlen = utr->iso_xlen[sitd->fidx]; + int scnt; + + sitd->Chrst = (udev->port_num << SITD_PORT_NUM_Pos) | + (udev->parent->iface->udev->dev_num << SITD_HUB_ADDR_Pos) | + ((ep->bEndpointAddress & 0xF) << SITD_EP_NUM_Pos) | + (udev->dev_num << SITD_DEV_ADDR_Pos); + + buff_page_addr = ((uint32_t)utr->iso_buff[sitd->fidx]) & 0xFFFFF000; + sitd->Bptr[0] = (uint32_t)(utr->iso_buff[sitd->fidx]); + sitd->Bptr[1] = buff_page_addr + 0x1000; + + scnt = (xlen + 187) / 188; + + if ((ep->bEndpointAddress & EP_ADDR_DIR_MASK) == EP_ADDR_DIR_IN) /* I/O */ + { + sitd->Chrst |= SITD_XFER_IN; + sitd->Sched = (1 << (scnt + 2)) - 1; + sitd->Sched = (sitd->Sched << 10) | 0x1; + //sitd->Sched <<= 1; + } + else + { + sitd->Chrst |= SITD_XFER_OUT; + sitd->Sched = sitd_OUT_Smask[scnt - 1]; + if (scnt > 1) + { + sitd->Bptr[1] |= (0x1 << 3); /* Transaction position (TP) 01b: Begin */ + } + sitd->Bptr[1] |= scnt; /* Transaction count (T-Count) */ + } + + if (sitd->fidx == IF_PER_UTR) + { + sitd->Sched |= SITD_IOC; + } + + sitd->StsCtrl = (xlen << SITD_XFER_CNT_Pos) | SITD_STATUS_ACTIVE; + + sitd->BackLink = SITD_LIST_END; +} + + +static void ehci_sitd_adjust_schedule(siTD_T *sitd) +{ + siTD_T *hlink = (siTD_T *)_PFList[sitd->sched_frnidx]; + uint32_t uframe_mask = 0x00; + + while (hlink && !HLINK_IS_TERMINATED(hlink) && HLINK_IS_SITD(hlink)) + { + hlink = SITD_PTR(hlink); + if (hlink != sitd) + { + if ((hlink->Chrst & SITD_XFER_IO_Msk) == SITD_XFER_IN) + { + uframe_mask |= (hlink->Sched & 0xFF); /* mark micro-frames used by IN S-mask */ + uframe_mask |= ((hlink->Sched >> 8) & 0xFF); /* mark micro-frames used by IN C-mask */ + } + else + { + uframe_mask |= (hlink->Sched & 0xFF); /* mark micro-frames used by OUT S-mask */ + } + } + hlink = SITD_PTR(hlink->Next_Link); + } + + uframe_mask = uframe_mask | (uframe_mask << 8); /* mark both S-mask and C-mask */ + + if (uframe_mask) + { + /* + * Shift afterward one micro-frame until no conflicts. + */ + while (1) + { + if (sitd->Sched & uframe_mask) + { + sitd->Sched = (sitd->Sched & 0xFFFF0000) | ((sitd->Sched << 1) & 0xFFFF); + } + else + { + break; /* no conflit, done. */ + } + } + } +} + + +static int ehci_iso_split_xfer(UTR_T *utr, ISO_EP_T *iso_ep) +{ + EP_INFO_T *ep = utr->ep; /* reference to isochronous endpoint */ + siTD_T *sitd, *sitd_next, *sitd_list = NULL; + int i; + int fidx; /* index to the 8 iso frames of UTR */ + + if (utr->udev->parent == NULL) + { + USB_error("siso xfer - parent lost!\n"); + return USBH_ERR_INVALID_PARAM; + } + + /*------------------------------------------------------------------------------------*/ + /* Allocate siTDs */ + /*------------------------------------------------------------------------------------*/ + for (i = 0; i < IF_PER_UTR; i++) /* allocate all siTDs required by UTR */ + { + sitd = alloc_ehci_siTD(); + if (sitd == NULL) + goto malloc_failed; + + if (sitd_list == NULL) /* link all siTDs */ + { + sitd_list = sitd; + } + else + { + sitd->next = sitd_list; + sitd_list = sitd; + } + } + + utr->td_cnt = IF_PER_UTR; + + /*------------------------------------------------------------------------------------*/ + /* Fill and link all siTDs */ + /*------------------------------------------------------------------------------------*/ + + utr->iso_sf = iso_ep->next_frame; + fidx = 0; /* index to UTR iso frmes (total IF_PER_UTR) */ + + for (sitd = sitd_list; (sitd != NULL); fidx++) + { + if (fidx >= IF_PER_UTR) /* unlikely */ + { + USB_error("EHCI driver siTD bug!?\n"); + goto malloc_failed; + } + + sitd->utr = utr; + sitd->fidx = fidx; /* index to UTR's n'th IF_PER_UTR frame */ + + write_sitd_info(utr, sitd); + + sitd_next = sitd->next; /* remember the next itd */ + + // USB_debug("Link iTD 0x%x, %d\n", (int)itd, iso_ep->next_frame); + /* + * Link iTD to period frame list + */ + sitd->sched_frnidx = iso_ep->next_frame; /* remember it for reclamation scan */ + DISABLE_EHCI_IRQ(); + ehci_sitd_adjust_schedule(sitd); + add_sitd_to_iso_ep(iso_ep, sitd); /* add to software itd list */ + sitd->Next_Link = _PFList[sitd->sched_frnidx];/* keep the next link */ + _PFList[sitd->sched_frnidx] = SITD_HLNK_SITD(sitd); + iso_ep->next_frame = (iso_ep->next_frame + ep->bInterval) % FL_SIZE; + ENABLE_EHCI_IRQ(); + + sitd = sitd_next; + } + + _ehci->UCMDR |= HSUSBH_UCMDR_PSEN_Msk; /* periodic list enable */ + return 0; + +malloc_failed: + + while (sitd_list != NULL) + { + sitd = sitd_list; + sitd_list = sitd->next; + free_ehci_siTD(sitd); + } + return USBH_ERR_MEMORY_OUT; +} + +/* + * If it's an isochronous endpoint, quit current transfer via UTR or hardware EP. + */ +int ehci_quit_iso_xfer(UTR_T *utr, EP_INFO_T *ep) +{ + ISO_EP_T *iso_ep; + iTD_T *itd, *itd_next, *p; + uint32_t frnidx; + uint32_t now_frame; + + if (ep == NULL) + { + if (utr == NULL) + return USBH_ERR_NOT_FOUND; + + if (utr->ep == NULL) + return USBH_ERR_NOT_FOUND; + + ep = utr->ep; + } + + if ((ep->bmAttributes & EP_ATTR_TT_MASK) != EP_ATTR_TT_ISO) + return USBH_ERR_NOT_FOUND; /* not isochronous endpoint */ + + /*------------------------------------------------------------------------------------*/ + /* It's an iso endpoint. Remove it as required. */ + /*------------------------------------------------------------------------------------*/ + iso_ep = iso_ep_list; + while (iso_ep != NULL) /* Search all activated iso endpoints */ + { + if (iso_ep->ep == ep) + break; + iso_ep = iso_ep->next; + } + if (iso_ep == NULL) + return 0; /* should have been removed */ + + itd = iso_ep->itd_list; /* get the first iTD from iso_ep's iTD list */ + + while (itd != NULL) /* traverse all iTDs of itd list */ + { + itd_next = itd->next; /* remember the next iTD */ + utr = itd->utr; + + /*--------------------------------------------------------------------------------*/ + /* Remove this iTD from period frame list */ + /*--------------------------------------------------------------------------------*/ + frnidx = itd->sched_frnidx; + + /* + * Prevent to race with Host Controller. If the iTD to be removed is located in + * current or next frame, wait until HC passed through it. + */ + while (1) + { + now_frame = (_ehci->UFINDR >> 3) & 0x3FF; + if ((now_frame == frnidx) || (((now_frame + 1) % 1024) == frnidx)) + continue; + break; + } + + if (_PFList[frnidx] == ITD_HLNK_ITD(itd)) + { + /* is the first entry, just change to next */ + _PFList[frnidx] = itd->Next_Link; + } + else + { + p = ITD_PTR(_PFList[frnidx]); /* find the preceding iTD */ + while ((ITD_PTR(p->Next_Link) != itd) && (p != NULL)) + { + p = ITD_PTR(p->Next_Link); + } + + if (p == NULL) /* link list out of control! */ + { + USB_error("ehci_quit_iso_xfer - An iTD lost reference to periodic frame list! 0x%x on %d\n", (int)itd, frnidx); + } + else /* remove iTD from list */ + { + p->Next_Link = itd->Next_Link; + } + } + + utr->td_cnt--; + + if (utr->td_cnt == 0) /* All iTD of this UTR done */ + { + utr->bIsTransferDone = 1; + if (utr->func) + utr->func(utr); + utr->status = USBH_ERR_ABORT; + } + free_ehci_iTD(itd); + itd = itd_next; + } + + /* + * Remove iso_ep from iso_ep_list + */ + remove_iso_ep_from_list(iso_ep); + usbh_free_mem(iso_ep, sizeof(*iso_ep)); /* free this iso_ep */ + ep->hw_pipe = NULL; + + if (iso_ep_list == NULL) + _ehci->UCMDR &= ~HSUSBH_UCMDR_PSEN_Msk; + + return 0; +} + + +/// @endcond HIDDEN_SYMBOLS + +/*** (C) COPYRIGHT 2017 Nuvoton Technology Corp. ***/ diff --git a/bsp/nuvoton/libraries/nuc980/UsbHostLib/src/mem_alloc.c b/bsp/nuvoton/libraries/nuc980/UsbHostLib/src/mem_alloc.c new file mode 100644 index 0000000000..1ad3cabcb1 --- /dev/null +++ b/bsp/nuvoton/libraries/nuc980/UsbHostLib/src/mem_alloc.c @@ -0,0 +1,540 @@ +/**************************************************************************//** + * @file mem_alloc.c + * @version V1.10 + * $Revision: 11 $ + * $Date: 14/10/03 1:54p $ + * @brief USB host library memory allocation functions. + * + * @note + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2018 Nuvoton Technology Corp. All rights reserved. +*****************************************************************************/ + +#include +#include +#include + +#include "usb.h" + + +/// @cond HIDDEN_SYMBOLS + +//#define MEM_DEBUG + +#ifdef MEM_DEBUG + #define mem_debug rt_kprintf +#else + #define mem_debug(...) +#endif + +#ifdef __ICCARM__ + #pragma data_alignment=1024 + uint8_t _mem_pool_buff[MEM_POOL_UNIT_NUM][MEM_POOL_UNIT_SIZE]; +#else + uint8_t _mem_pool_buff[MEM_POOL_UNIT_NUM][MEM_POOL_UNIT_SIZE] __attribute__((aligned(1024))); +#endif + +static uint8_t *_mem_pool[MEM_POOL_UNIT_NUM]; +static uint8_t _unit_used[MEM_POOL_UNIT_NUM]; + +static volatile int _usbh_mem_used; +static volatile int _usbh_max_mem_used; +static volatile int _mem_pool_used; + + +UDEV_T *g_udev_list; + +uint8_t _dev_addr_pool[128]; +static volatile int _device_addr; + +static int _sidx = 0;; + +/*--------------------------------------------------------------------------*/ +/* Memory alloc/free recording */ +/*--------------------------------------------------------------------------*/ + +void usbh_memory_init(void) +{ + int i; + + if (sizeof(TD_T) > MEM_POOL_UNIT_SIZE) + { + USB_error("TD_T - MEM_POOL_UNIT_SIZE too small!\n"); + while (1); + } + + if (sizeof(ED_T) > MEM_POOL_UNIT_SIZE) + { + USB_error("ED_T - MEM_POOL_UNIT_SIZE too small!\n"); + while (1); + } + + for (i = 0; i < MEM_POOL_UNIT_NUM; i++) + { + _unit_used[i] = 0; + _mem_pool[i] = (uint8_t *)((uint32_t)&_mem_pool_buff[i] | NON_CACHE_MASK); + } + + _usbh_mem_used = 0L; + _usbh_max_mem_used = 0L; + + _mem_pool_used = 0; + _sidx = 0; + + g_udev_list = NULL; + + memset(_dev_addr_pool, 0, sizeof(_dev_addr_pool)); + _device_addr = 1; + + USB_InitializeMemoryPool(); +} + +uint32_t usbh_memory_used(void) +{ + mem_debug("USB static memory: %d/%d, heap used: %d\n", _mem_pool_used, MEM_POOL_UNIT_NUM, _usbh_mem_used); + return _usbh_mem_used; +} + +static void memory_counter(int size) +{ + _usbh_mem_used += size; + if (_usbh_mem_used > _usbh_max_mem_used) + _usbh_max_mem_used = _usbh_mem_used; +} + +void *usbh_alloc_mem(int size) +{ + void *p; + + p = USB_malloc(size, 16); + if (p == NULL) + { + USB_error("usbh_alloc_mem failed! %d\n", size); + return NULL; + } + + memset(p, 0, size); + memory_counter(size); + return p; +} + +void usbh_free_mem(void *p, int size) +{ + USB_free(p); + memory_counter(0 - size); +} + + +/*--------------------------------------------------------------------------*/ +/* USB device allocate/free */ +/*--------------------------------------------------------------------------*/ + +UDEV_T *alloc_device(void) +{ + UDEV_T *udev; + + udev = (UDEV_T *)USB_malloc(sizeof(*udev), 16); + if (udev == NULL) + { + USB_error("alloc_device failed!\n"); + return NULL; + } + + memset(udev, 0, sizeof(*udev)); + memory_counter(sizeof(*udev)); + udev->cur_conf = -1; /* must! used to identify the first SET CONFIGURATION */ + udev->next = g_udev_list; /* chain to global device list */ + g_udev_list = udev; + return udev; +} + +void free_device(UDEV_T *udev) +{ + UDEV_T *d; + + if (udev == NULL) + return; + + if (udev->cfd_buff != NULL) + usbh_free_mem(udev->cfd_buff, MAX_DESC_BUFF_SIZE); + + /* + * Remove it from the global device list + */ + if (g_udev_list == udev) + { + g_udev_list = g_udev_list->next; + } + else + { + d = g_udev_list; + while (d != NULL) + { + if (d->next == udev) + { + d->next = udev->next; + break; + } + d = d->next; + } + } + USB_free(udev); + memory_counter(-sizeof(*udev)); +} + +int alloc_dev_address(void) +{ + _device_addr++; + + if (_device_addr >= 128) + _device_addr = 1; + + while (1) + { + if (_dev_addr_pool[_device_addr] == 0) + { + _dev_addr_pool[_device_addr] = 1; + return _device_addr; + } + _device_addr++; + if (_device_addr >= 128) + _device_addr = 1; + } +} + +void free_dev_address(int dev_addr) +{ + if (dev_addr < 128) + _dev_addr_pool[dev_addr] = 0; +} + +/*--------------------------------------------------------------------------*/ +/* UTR (USB Transfer Request) allocate/free */ +/*--------------------------------------------------------------------------*/ + +UTR_T *alloc_utr(UDEV_T *udev) +{ +#if 0 + UTR_T *utr, *utr_noncache; + + utr = (UTR_T *)USB_malloc(sizeof(*utr), 16); + if (utr == NULL) + { + USB_error("alloc_utr failed!\n"); + return NULL; + } + + utr_noncache = (UTR_T *)((uint32_t)utr | NONCACHEABLE); + + memory_counter(sizeof(*utr)); + memset(utr_noncache, 0, sizeof(*utr)); + utr_noncache->udev = udev; + mem_debug("[ALLOC] [UTR] - 0x%x\n", (int)utr_noncache); + return utr_noncache; +#else + UTR_T *utr; + + utr = (UTR_T *)USB_malloc(sizeof(*utr), 16); + if (utr == NULL) + { + USB_error("alloc_utr failed!\n"); + return NULL; + } + + memory_counter(sizeof(*utr)); + memset(utr, 0, sizeof(*utr)); + utr->udev = udev; + mem_debug("[ALLOC] [UTR] - 0x%x\n", (int)utr_noncache); + return utr; +#endif +} + +void free_utr(UTR_T *utr) +{ + if (utr == NULL) + return; + + mem_debug("[FREE] [UTR] - 0x%x\n", (int)utr); + +#if 0 + if ((uint32_t)utr & NONCACHEABLE) + utr = (UTR_T *)((uint32_t)utr & ~NONCACHEABLE); +#endif + + USB_free(utr); + memory_counter(0 - (int)sizeof(*utr)); +} + +/*--------------------------------------------------------------------------*/ +/* OHCI ED allocate/free */ +/*--------------------------------------------------------------------------*/ + +ED_T *alloc_ohci_ED(void) +{ + int i; + ED_T *ed; + + for (i = 0; i < MEM_POOL_UNIT_NUM; i++) + { + if (_unit_used[i] == 0) + { + _unit_used[i] = 1; + _mem_pool_used++; + ed = (ED_T *)_mem_pool[i]; + memset(ed, 0, sizeof(*ed)); + mem_debug("[ALLOC] [ED] - 0x%x\n", (int)ed); + return ed; + } + } + USB_error("alloc_ohci_ED failed!\n"); + return NULL; +} + +void free_ohci_ED(ED_T *ed) +{ + int i; + + for (i = 0; i < MEM_POOL_UNIT_NUM; i++) + { + if ((uint32_t)_mem_pool[i] == (uint32_t)ed) + { + mem_debug("[FREE] [ED] - 0x%x\n", (int)ed); + _unit_used[i] = 0; + _mem_pool_used--; + return; + } + } + USB_debug("free_ohci_ED - not found! (ignored in case of multiple UTR)\n"); +} + +/*--------------------------------------------------------------------------*/ +/* OHCI TD allocate/free */ +/*--------------------------------------------------------------------------*/ +TD_T *alloc_ohci_TD(UTR_T *utr) +{ + int i; + TD_T *td; + + for (i = 0; i < MEM_POOL_UNIT_NUM; i++) + { + if (_unit_used[i] == 0) + { + _unit_used[i] = 1; + _mem_pool_used++; + td = (TD_T *)_mem_pool[i]; + + memset(td, 0, sizeof(*td)); + td->utr = utr; + mem_debug("[ALLOC] [TD] - 0x%x\n", (int)td); + return td; + } + } + USB_error("alloc_ohci_TD failed!\n"); + return NULL; +} + +void free_ohci_TD(TD_T *td) +{ + int i; + + for (i = 0; i < MEM_POOL_UNIT_NUM; i++) + { + if ((uint32_t)_mem_pool[i] == (uint32_t)td) + { + mem_debug("[FREE] [TD] - 0x%x\n", (int)td); + _unit_used[i] = 0; + _mem_pool_used--; + return; + } + } + USB_error("free_ohci_TD - not found!\n"); +} + +/*--------------------------------------------------------------------------*/ +/* EHCI QH allocate/free */ +/*--------------------------------------------------------------------------*/ +QH_T *alloc_ehci_QH(void) +{ + int i; + QH_T *qh = NULL; + + for (i = (_sidx + 1) % MEM_POOL_UNIT_NUM; i != _sidx; i = (i + 1) % MEM_POOL_UNIT_NUM) + { + if (_unit_used[i] == 0) + { + _unit_used[i] = 1; + _sidx = i; + _mem_pool_used++; + qh = (QH_T *)_mem_pool[i]; + memset(qh, 0, sizeof(*qh)); + mem_debug("[ALLOC] [QH] - 0x%x\n", (int)qh); + break; + } + } + if (qh == NULL) + { + USB_error("alloc_ehci_QH failed!\n"); + return NULL; + } + qh->Curr_qTD = QTD_LIST_END; + qh->OL_Next_qTD = QTD_LIST_END; + qh->OL_Alt_Next_qTD = QTD_LIST_END; + qh->OL_Token = QTD_STS_HALT; + return qh; +} + +void free_ehci_QH(QH_T *qh) +{ + int i; + + for (i = 0; i < MEM_POOL_UNIT_NUM; i++) + { + if ((uint32_t)_mem_pool[i] == (uint32_t)qh) + { + mem_debug("[FREE] [QH] - 0x%x\n", (int)qh); + _unit_used[i] = 0; + _mem_pool_used--; + return; + } + } + USB_debug("free_ehci_QH - not found! (ignored in case of multiple UTR)\n"); +} + +/*--------------------------------------------------------------------------*/ +/* EHCI qTD allocate/free */ +/*--------------------------------------------------------------------------*/ +qTD_T *alloc_ehci_qTD(UTR_T *utr) +{ + int i; + qTD_T *qtd; + + for (i = (_sidx + 1) % MEM_POOL_UNIT_NUM; i != _sidx; i = (i + 1) % MEM_POOL_UNIT_NUM) + { + if (_unit_used[i] == 0) + { + _unit_used[i] = 1; + _sidx = i; + _mem_pool_used++; + qtd = (qTD_T *)_mem_pool[i]; + + memset(qtd, 0, sizeof(*qtd)); + qtd->Next_qTD = QTD_LIST_END; + qtd->Alt_Next_qTD = QTD_LIST_END; + qtd->Token = 0x1197B7F; // QTD_STS_HALT; visit_qtd() will not remove a qTD with this mark. It means the qTD still not ready for transfer. + qtd->utr = utr; + mem_debug("[ALLOC] [qTD] - 0x%x\n", (int)qtd); + return qtd; + } + } + USB_error("alloc_ehci_qTD failed!\n"); + return NULL; +} + +void free_ehci_qTD(qTD_T *qtd) +{ + int i; + + for (i = 0; i < MEM_POOL_UNIT_NUM; i++) + { + if ((uint32_t)_mem_pool[i] == (uint32_t)qtd) + { + mem_debug("[FREE] [qTD] - 0x%x\n", (int)qtd); + _unit_used[i] = 0; + _mem_pool_used--; + return; + } + } + USB_error("free_ehci_qTD 0x%x - not found!\n", (int)qtd); +} + +/*--------------------------------------------------------------------------*/ +/* EHCI iTD allocate/free */ +/*--------------------------------------------------------------------------*/ +iTD_T *alloc_ehci_iTD(void) +{ + int i; + iTD_T *itd; + + for (i = (_sidx + 1) % MEM_POOL_UNIT_NUM; i != _sidx; i = (i + 1) % MEM_POOL_UNIT_NUM) + { + if (i + 2 >= MEM_POOL_UNIT_NUM) + continue; + + if ((_unit_used[i] == 0) && (_unit_used[i + 1] == 0)) + { + _unit_used[i] = _unit_used[i + 1] = 1; + _sidx = i + 1; + _mem_pool_used += 2; + itd = (iTD_T *)_mem_pool[i]; + memset(itd, 0, sizeof(*itd)); + mem_debug("[ALLOC] [iTD] - 0x%x\n", (int)itd); + return itd; + } + } + USB_error("alloc_ehci_iTD failed!\n"); + return NULL; +} + +void free_ehci_iTD(iTD_T *itd) +{ + int i; + + for (i = 0; i + 1 < MEM_POOL_UNIT_NUM; i++) + { + if ((uint32_t)_mem_pool[i] == (uint32_t)itd) + { + mem_debug("[FREE] [iTD] - 0x%x\n", (int)itd); + _unit_used[i] = _unit_used[i + 1] = 0; + _mem_pool_used -= 2; + return; + } + } + USB_error("free_ehci_iTD 0x%x - not found!\n", (int)itd); +} + +/*--------------------------------------------------------------------------*/ +/* EHCI iTD allocate/free */ +/*--------------------------------------------------------------------------*/ +siTD_T *alloc_ehci_siTD(void) +{ + int i; + siTD_T *sitd; + + for (i = (_sidx + 1) % MEM_POOL_UNIT_NUM; i != _sidx; i = (i + 1) % MEM_POOL_UNIT_NUM) + { + if (_unit_used[i] == 0) + { + _unit_used[i] = 1; + _sidx = i; + _mem_pool_used ++; + sitd = (siTD_T *)_mem_pool[i]; + memset(sitd, 0, sizeof(*sitd)); + mem_debug("[ALLOC] [siTD] - 0x%x\n", (int)sitd); + return sitd; + } + } + USB_error("alloc_ehci_siTD failed!\n"); + return NULL; +} + +void free_ehci_siTD(siTD_T *sitd) +{ + int i; + + for (i = 0; i < MEM_POOL_UNIT_NUM; i++) + { + if ((uint32_t)_mem_pool[i] == (uint32_t)sitd) + { + mem_debug("[FREE] [siTD] - 0x%x\n", (int)sitd); + _unit_used[i] = 0; + _mem_pool_used--; + return; + } + } + USB_error("free_ehci_siTD 0x%x - not found!\n", (int)sitd); +} + +/// @endcond HIDDEN_SYMBOLS + +/*** (C) COPYRIGHT 2018 Nuvoton Technology Corp. ***/ + diff --git a/bsp/nuvoton/libraries/nuc980/UsbHostLib/src/ohci.c b/bsp/nuvoton/libraries/nuc980/UsbHostLib/src/ohci.c new file mode 100644 index 0000000000..045c44d47d --- /dev/null +++ b/bsp/nuvoton/libraries/nuc980/UsbHostLib/src/ohci.c @@ -0,0 +1,1301 @@ +/**************************************************************************//** + * @file ohci.c + * @version V1.10 + * $Revision: 11 $ + * $Date: 14/10/03 1:54p $ + * @brief USB Host library OHCI (USB 1.1) host controller driver. + * + * @note + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2017 Nuvoton Technology Corp. All rights reserved. +*****************************************************************************/ + +#include +#include +#include + +#include "nuc980.h" + +#include "usb.h" +#include "hub.h" +#include "ohci.h" + +/// @cond HIDDEN_SYMBOLS + +//#define TD_debug rt_kprintf +#define TD_debug(...) + +//#define ED_debug rt_kprintf +#define ED_debug(...) + +uint8_t _hcca_mem[256] __attribute__((aligned(256))); + +HCCA_T *_hcca; + +ED_T *_Ied[6]; + + +static ED_T *ed_remove_list; + +static void add_to_ED_remove_list(ED_T *ed) +{ + ED_T *p; + + ED_debug("add_to_ED_remove_list - 0x%x (0x%x)\n", (int)ed, ed->Info); + DISABLE_OHCI_IRQ(); + + /* check if this ED found in ed_remove_list */ + p = ed_remove_list; + while (p) + { + if (p == ed) + { + ENABLE_OHCI_IRQ(); /* This ED found in ed_remove_list */ + return; /* do nothing */ + } + p = p->next; + } + + ed->Info |= ED_SKIP; /* ask OHCI controller skip this ED */ + ed->next = ed_remove_list; + ed_remove_list = ed; /* insert to the head of ed_remove_list */ + ENABLE_OHCI_IRQ(); + _ohci->HcInterruptStatus = USBH_HcInterruptStatus_SF_Msk; + _ohci->HcInterruptEnable |= USBH_HcInterruptEnable_SF_Msk; + usbh_delay_ms(2); /* Full speed wait 2 ms is enough */ +} + +static int ohci_reset(void) +{ + volatile int t0; + + /* Disable HC interrupts */ + _ohci->HcInterruptDisable = USBH_HcInterruptDisable_MIE_Msk; + + /* HC Reset requires max 10 ms delay */ + _ohci->HcControl = 0; + _ohci->HcCommandStatus = USBH_HcCommandStatus_HCR_Msk; + + usbh_delay_ms(10); + + /* Check if OHCI reset completed? */ + if ((USBH->HcCommandStatus & USBH_HcCommandStatus_HCR_Msk) != 0) + { + USB_error("Error! - USB OHCI reset timed out!\n"); + return -1; + } + + USBH->HcRhStatus = USBH_HcRhStatus_OCI_Msk | USBH_HcRhStatus_LPS_Msk; + + USBH->HcControl = HCFS_RESET; + + usbh_delay_ms(10); + + /* Check if OHCI reset completed? */ + if ((USBH->HcCommandStatus & USBH_HcCommandStatus_HCR_Msk) != 0) + { + USB_error("Error! - USB HC reset timed out!\n"); + return -1; + } + return 0; +} + +static void init_hcca_int_table() +{ + ED_T *ed_p; + int i, idx, interval; + + memset(_hcca->int_table, 0, sizeof(_hcca->int_table)); + + for (i = 5; i >= 0; i--) /* interval = i^2 */ + { + _Ied[i] = alloc_ohci_ED(); + _Ied[i]->Info = ED_SKIP; + + interval = 0x1 << i; + + for (idx = interval - 1; idx < 32; idx += interval) + { + if (_hcca->int_table[idx] == 0) /* is empty list, insert directly */ + { + _hcca->int_table[idx] = (uint32_t)_Ied[i]; + } + else + { + ed_p = (ED_T *)_hcca->int_table[idx]; + + while (1) + { + if (ed_p == _Ied[i]) + break; /* already chained by previous visit */ + + if (ed_p->NextED == 0) /* reach end of list? */ + { + ed_p->NextED = (uint32_t)_Ied[i]; + break; + } + ed_p = (ED_T *)ed_p->NextED; + } + } + } + } +} + +static ED_T *get_int_tree_head_node(int interval) +{ + int i; + + for (i = 0; i < 5; i++) + { + interval >>= 1; + if (interval == 0) + return _Ied[i]; + } + return _Ied[5]; /* for interval >= 32 */ +} + +static int get_ohci_interval(int interval) +{ + int i, bInterval = 1; + + for (i = 0; i < 5; i++) + { + interval >>= 1; + if (interval == 0) + return bInterval; + bInterval *= 2; + } + return 32; /* for interval >= 32 */ +} + + +static int ohci_init(void) +{ + uint32_t fminterval; + volatile int i; + + _hcca = (HCCA_T *)((uint32_t)_hcca_mem | NON_CACHE_MASK); + + if (ohci_reset() < 0) + return -1; + + ed_remove_list = NULL; + + init_hcca_int_table(); + + /* Tell the controller where the control and bulk lists are + * The lists are empty now. */ + _ohci->HcControlHeadED = 0; /* control ED list head */ + _ohci->HcBulkHeadED = 0; /* bulk ED list head */ + + _ohci->HcHCCA = (uint32_t)_hcca; /* HCCA area */ + + /* periodic start 90% of frame interval */ + fminterval = 0x2edf; /* 11,999 */ + _ohci->HcPeriodicStart = (fminterval * 9) / 10; + + /* set FSLargestDataPacket, 10,104 for 0x2edf frame interval */ + fminterval |= ((((fminterval - 210) * 6) / 7) << 16); + _ohci->HcFmInterval = fminterval; + + _ohci->HcLSThreshold = 0x628; + + /* start controller operations */ + _ohci->HcControl = HCFS_OPER | (0x3 << USBH_HcControl_CBSR_Pos); + +#ifdef OHCI_PER_PORT_POWER + _ohci->HcRhDescriptorB = 0x60000; + for (i = 0; i < OHCI_PORT_CNT; i++) + _ohci->HcRhPortStatus[i] = USBH_HcRhPortStatus_PPS_Msk; +#else + _ohci->HcRhDescriptorA = (USBH->HcRhDescriptorA | (1 << 9)) & ~USBH_HcRhDescriptorA_PSM_Msk; + _ohci->HcRhStatus = USBH_HcRhStatus_LPSC_Msk; +#endif + + _ohci->HcInterruptEnable = USBH_HcInterruptEnable_MIE_Msk | USBH_HcInterruptEnable_WDH_Msk | USBH_HcInterruptEnable_SF_Msk; + + /* POTPGT delay is bits 24-31, in 20 ms units. */ + usbh_delay_ms(20); + return 0; +} + +static void ohci_suspend(void) +{ + int i; + + for (i = 0; i < OHCI_PORT_CNT; i++) + { + /* set port suspend if connected */ + if (_ohci->HcRhPortStatus[i] & 0x1) + _ohci->HcRhPortStatus[i] = 0x4; + } + + /* enable Device Remote Wakeup */ + _ohci->HcRhStatus |= USBH_HcRhStatus_DRWE_Msk; + + /* enable USBH RHSC interrupt for system wakeup */ + _ohci->HcInterruptEnable |= USBH_HcInterruptEnable_RHSC_Msk | USBH_HcInterruptEnable_RD_Msk; + + /* set Host Controller enter suspend state */ + _ohci->HcControl = (USBH->HcControl & ~USBH_HcControl_HCFS_Msk) | (3 << USBH_HcControl_HCFS_Pos); +} + +static void ohci_resume(void) +{ + int i; + + _ohci->HcControl = (USBH->HcControl & ~USBH_HcControl_HCFS_Msk) | (1 << USBH_HcControl_HCFS_Pos); + _ohci->HcControl = (USBH->HcControl & ~USBH_HcControl_HCFS_Msk) | (2 << USBH_HcControl_HCFS_Pos); + + for (i = 0; i < OHCI_PORT_CNT; i++) + { + if (_ohci->HcRhPortStatus[i] & 0x4) + _ohci->HcRhPortStatus[i] = 0x8; + } +} + +static void ohci_shutdown(void) +{ + ohci_suspend(); + DISABLE_OHCI_IRQ(); +#ifndef OHCI_PER_PORT_POWER + _ohci->HcRhStatus = USBH_HcRhStatus_LPS_Msk; +#endif +} + + +/* + * Quit current trasnfer via UTR or hardware EP. + */ +static int ohci_quit_xfer(UTR_T *utr, EP_INFO_T *ep) +{ + ED_T *ed; + + if (utr != NULL) + { + if (utr->ep == NULL) + return USBH_ERR_NOT_FOUND; + + ed = (ED_T *)(utr->ep->hw_pipe); + + if (!ed) + return USBH_ERR_NOT_FOUND; + + /* add the endpoint to remove list, it will be removed on the next start of frame */ + add_to_ED_remove_list(ed); + utr->ep->hw_pipe = NULL; + } + + if ((ep != NULL) && (ep->hw_pipe != NULL)) + { + ed = (ED_T *)(ep->hw_pipe); + /* add the endpoint to remove list, it will be removed on the next start of frame */ + add_to_ED_remove_list(ed); + ep->hw_pipe = NULL; + } + + return 0; +} + +uint32_t ed_make_info(UDEV_T *udev, EP_INFO_T *ep) +{ + uint32_t info; + + if (ep == NULL) /* is a control endpoint */ + { + /* control endpoint direction is from TD */ + if (udev->descriptor.bMaxPacketSize0 == 0) /* is 0 if device descriptor still not obtained. */ + { + if (udev->speed == SPEED_LOW) /* give a default maximum packet size */ + udev->descriptor.bMaxPacketSize0 = 8; + else + udev->descriptor.bMaxPacketSize0 = 64; + } + info = (udev->descriptor.bMaxPacketSize0 << 16) /* Control endpoint Maximum Packet Size from device descriptor */ + | ED_DIR_BY_TD /* Direction (Get direction From TD) */ + | ED_FORMAT_GENERAL /* General format */ + | (0 << ED_CTRL_EN_Pos); /* Endpoint address 0 */ + } + else /* Other endpoint direction is from endpoint descriptor */ + { + info = (ep->wMaxPacketSize << 16); /* Maximum Packet Size from endpoint */ + + info |= ((ep->bEndpointAddress & 0xf) << ED_CTRL_EN_Pos); /* Endpoint Number */ + + if ((ep->bEndpointAddress & EP_ADDR_DIR_MASK) == EP_ADDR_DIR_IN) + info |= ED_DIR_IN; + else + info |= ED_DIR_OUT; + + if ((ep->bmAttributes & EP_ATTR_TT_MASK) == EP_ATTR_TT_ISO) + info |= ED_FORMAT_ISO; + else + info |= ED_FORMAT_GENERAL; + } + + info |= ((udev->speed == SPEED_LOW) ? ED_SPEED_LOW : ED_SPEED_FULL); /* Speed */ + info |= (udev->dev_num); /* Function Address */ + + return info; +} + +static void write_td(TD_T *td, uint32_t info, uint8_t *buff, uint32_t data_len) +{ + td->Info = info; + td->CBP = (uint32_t)((!buff || !data_len) ? 0 : buff); + td->BE = (uint32_t)((!buff || !data_len) ? 0 : (uint32_t)buff + data_len - 1); + td->buff_start = td->CBP; + // TD_debug("TD [0x%x]: 0x%x, 0x%x, 0x%x\n", (int)td, td->Info, td->CBP, td->BE); +} + +static int ohci_ctrl_xfer(UTR_T *utr) +{ + UDEV_T *udev; + ED_T *ed; + TD_T *td_setup, *td_data, *td_status; + uint32_t info; + + udev = utr->udev; + + /*------------------------------------------------------------------------------------*/ + /* Allocate ED and TDs */ + /*------------------------------------------------------------------------------------*/ + td_setup = alloc_ohci_TD(utr); + + if (utr->data_len > 0) + td_data = alloc_ohci_TD(utr); + else + td_data = NULL; + + td_status = alloc_ohci_TD(utr); + + if (td_status == NULL) + { + free_ohci_TD(td_setup); + if (utr->data_len > 0) + free_ohci_TD(td_data); + return USBH_ERR_MEMORY_OUT; + } + + /* Check if there's any transfer pending on this endpoint... */ + if (udev->ep0.hw_pipe == NULL) + { + ed = alloc_ohci_ED(); + if (ed == NULL) + { + free_ohci_TD(td_setup); + free_ohci_TD(td_status); + if (utr->data_len > 0) + free_ohci_TD(td_data); + return USBH_ERR_MEMORY_OUT; + } + } + else + ed = (ED_T *)udev->ep0.hw_pipe; + + /*------------------------------------------------------------------------------------*/ + /* prepare SETUP stage TD */ + /*------------------------------------------------------------------------------------*/ + info = TD_CC | TD_T_DATA0 | TD_TYPE_CTRL; + write_td(td_setup, info, (uint8_t *)&utr->setup, 8); + td_setup->ed = ed; + + /*------------------------------------------------------------------------------------*/ + /* prepare DATA stage TD */ + /*------------------------------------------------------------------------------------*/ + if (utr->data_len > 0) + { + if ((utr->setup.bmRequestType & 0x80) == REQ_TYPE_OUT) + info = (TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 | TD_TYPE_CTRL | TD_CTRL_DATA); + else + info = (TD_CC | TD_R | TD_DP_IN | TD_T_DATA1 | TD_TYPE_CTRL | TD_CTRL_DATA); + + write_td(td_data, info, utr->buff, utr->data_len); + td_data->ed = ed; + td_setup->NextTD = (uint32_t)td_data; + td_setup->next = td_data; + td_data->NextTD = (uint32_t)td_status; + td_data->next = td_status; + } + else + { + td_setup->NextTD = (uint32_t)td_status; + td_setup->next = td_status; + } + + /*------------------------------------------------------------------------------------*/ + /* prepare STATUS stage TD */ + /*------------------------------------------------------------------------------------*/ + ed->Info = ed_make_info(udev, NULL); + if ((utr->setup.bmRequestType & 0x80) == REQ_TYPE_OUT) + info = (TD_CC | TD_DP_IN | TD_T_DATA1 | TD_TYPE_CTRL); + else + info = (TD_CC | TD_DP_OUT | TD_T_DATA1 | TD_TYPE_CTRL); + + write_td(td_status, info, NULL, 0); + td_status->ed = ed; + td_status->NextTD = 0; + td_status->next = 0; + + /*------------------------------------------------------------------------------------*/ + /* prepare ED */ + /*------------------------------------------------------------------------------------*/ + ed->TailP = 0; + ed->HeadP = (uint32_t)td_setup; + ed->Info = ed_make_info(udev, NULL); + ed->NextED = 0; + + //TD_debug("TD SETUP [0x%x]: 0x%x, 0x%x, 0x%x, 0x%x\n", (int)td_setup, td_setup->Info, td_setup->CBP, td_setup->BE, td_setup->NextTD); + //if (td_data) + // TD_debug("TD DATA [0x%x]: 0x%x, 0x%x, 0x%x, 0x%x\n", (int)td_data, td_data->Info, td_data->CBP, td_data->BE, td_data->NextTD); + //TD_debug("TD STATUS [0x%x]: 0x%x, 0x%x, 0x%x, 0x%x\n", (int)td_status, td_status->Info, td_status->CBP, td_status->BE, td_status->NextTD); + ED_debug("Xfer ED 0x%x: 0x%x 0x%x 0x%x 0x%x\n", (int)ed, ed->Info, ed->TailP, ed->HeadP, ed->NextED); + + if (utr->data_len > 0) + utr->td_cnt = 3; + else + utr->td_cnt = 2; + + utr->ep = &udev->ep0; /* driver can find EP from UTR */ + udev->ep0.hw_pipe = (void *)ed; /* driver can find ED from EP */ + + /*------------------------------------------------------------------------------------*/ + /* Start transfer */ + /*------------------------------------------------------------------------------------*/ + DISABLE_OHCI_IRQ(); + _ohci->HcControlHeadED = (uint32_t)ed; /* Link ED to OHCI */ + _ohci->HcControl |= USBH_HcControl_CLE_Msk; /* enable control list */ + ENABLE_OHCI_IRQ(); + _ohci->HcCommandStatus = USBH_HcCommandStatus_CLF_Msk; /* start Control list */ + + return 0; +} + +static int ohci_bulk_xfer(UTR_T *utr) +{ + UDEV_T *udev = utr->udev; + EP_INFO_T *ep = utr->ep; + ED_T *ed; + TD_T *td, *td_p, *td_list = NULL; + uint32_t info; + uint32_t data_len, xfer_len; + int8_t bIsNewED = 0; + uint8_t *buff; + + /*------------------------------------------------------------------------------------*/ + /* Check if there's uncompleted transfer on this endpoint... */ + /* Prepare ED */ + /*------------------------------------------------------------------------------------*/ + info = ed_make_info(udev, ep); + + /* Check if there's any transfer pending on this endpoint... */ + ed = (ED_T *)_ohci->HcBulkHeadED; /* get the head of bulk endpoint list */ + while (ed != NULL) + { + if (ed->Info == info) /* have transfer of this EP not completed? */ + { + if ((ed->HeadP & 0xFFFFFFF0) != (ed->TailP & 0xFFFFFFF0)) + return USBH_ERR_OHCI_EP_BUSY; /* endpoint is busy */ + else + break; /* ED already there... */ + } + ed = (ED_T *)ed->NextED; + } + + if (ed == NULL) + { + bIsNewED = 1; + ed = alloc_ohci_ED(); /* allocate an Endpoint Descriptor */ + if (ed == NULL) + return USBH_ERR_MEMORY_OUT; + ed->Info = info; + ed->HeadP = 0; + ED_debug("Link BULK ED 0x%x: 0x%x 0x%x 0x%x 0x%x\n", (int)ed, ed->Info, ed->TailP, ed->HeadP, ed->NextED); + } + + ep->hw_pipe = (void *)ed; + + /*------------------------------------------------------------------------------------*/ + /* Prepare TDs */ + /*------------------------------------------------------------------------------------*/ + utr->td_cnt = 0; + data_len = utr->data_len; + buff = utr->buff; + + do + { + if ((ep->bEndpointAddress & EP_ADDR_DIR_MASK) == EP_ADDR_DIR_OUT) + info = (TD_CC | TD_R | TD_DP_OUT | TD_TYPE_BULK); + else + info = (TD_CC | TD_R | TD_DP_IN | TD_TYPE_BULK); + + info &= ~(1 << 25); /* Data toggle from ED toggleCarry bit */ + + if (data_len > 4096) /* maximum transfer length is 4K for each TD */ + xfer_len = 4096; + else + xfer_len = data_len; /* remaining data length < 4K */ + + td = alloc_ohci_TD(utr); /* allocate a TD */ + if (td == NULL) + goto mem_out; + /* fill this TD */ + write_td(td, info, buff, xfer_len); + td->ed = ed; + + utr->td_cnt++; /* increase TD count, for recalim counter */ + + buff += xfer_len; /* advanced buffer pointer */ + data_len -= xfer_len; + + /* chain to end of TD list */ + if (td_list == NULL) + { + td_list = td; + } + else + { + td_p = td_list; + while (td_p->NextTD != 0) + td_p = (TD_T *)td_p->NextTD; + td_p->NextTD = (uint32_t)td; + } + + } + while (data_len > 0); + + /*------------------------------------------------------------------------------------*/ + /* Start transfer */ + /*------------------------------------------------------------------------------------*/ + utr->status = 0; + DISABLE_OHCI_IRQ(); + ed->HeadP = (ed->HeadP & 0x2) | (uint32_t)td_list; /* keep toggleCarry bit */ + if (bIsNewED) + { + ed->HeadP = (uint32_t)td_list; + /* Link ED to OHCI Bulk List */ + ed->NextED = _ohci->HcBulkHeadED; + _ohci->HcBulkHeadED = (uint32_t)ed; + } + ENABLE_OHCI_IRQ(); + _ohci->HcControl |= USBH_HcControl_BLE_Msk; /* enable bulk list */ + _ohci->HcCommandStatus = USBH_HcCommandStatus_BLF_Msk; /* start bulk list */ + + return 0; + +mem_out: + while (td_list != NULL) + { + td = td_list; + td_list = (TD_T *)td_list->NextTD; + free_ohci_TD(td); + } + free_ohci_ED(ed); + return USBH_ERR_MEMORY_OUT; +} + +static int ohci_int_xfer(UTR_T *utr) +{ + UDEV_T *udev = utr->udev; + EP_INFO_T *ep = utr->ep; + ED_T *ed, *ied; + TD_T *td, *td_new; + uint32_t info; + int8_t bIsNewED = 0; + + if (utr->data_len > 64) /* USB 1.1 interrupt transfer maximum packet size is 64 */ + return USBH_ERR_INVALID_PARAM; + + td_new = alloc_ohci_TD(utr); /* allocate a TD for dummy TD */ + if (td_new == NULL) + return USBH_ERR_MEMORY_OUT; + + ied = get_int_tree_head_node(ep->bInterval); /* get head node of this interval */ + + /*------------------------------------------------------------------------------------*/ + /* Find if this ED was already in the list */ + /*------------------------------------------------------------------------------------*/ + info = ed_make_info(udev, ep); + ed = ied; + while (ed != NULL) + { + if (ed->Info == info) + break; /* Endpoint found */ + ed = (ED_T *)ed->NextED; + } + + if (ed == NULL) /* ED not found, create it */ + { + bIsNewED = 1; + ed = alloc_ohci_ED(); /* allocate an Endpoint Descriptor */ + if (ed == NULL) + return USBH_ERR_MEMORY_OUT; + ed->Info = info; + ed->HeadP = 0; + ed->bInterval = ep->bInterval; + + td = alloc_ohci_TD(NULL); /* allocate the initial dummy TD for ED */ + if (td == NULL) + { + free_ohci_ED(ed); + free_ohci_TD(td_new); + return USBH_ERR_MEMORY_OUT; + } + ed->HeadP = (uint32_t)td; /* Let both HeadP and TailP point to dummy TD */ + ed->TailP = ed->HeadP; + } + else + { + td = (TD_T *)(ed->TailP & ~0xf); /* TailP always point to the dummy TD */ + } + ep->hw_pipe = (void *)ed; + + /*------------------------------------------------------------------------------------*/ + /* Prepare TD */ + /*------------------------------------------------------------------------------------*/ + if ((ep->bEndpointAddress & EP_ADDR_DIR_MASK) == EP_ADDR_DIR_OUT) + info = (TD_CC | TD_R | TD_DP_OUT | TD_TYPE_INT); + else + info = (TD_CC | TD_R | TD_DP_IN | TD_TYPE_INT); + + /* Keep data toggle */ + info = (info & ~(1 << 25)) | (td->Info & (1 << 25)); + + /* fill this TD */ + write_td(td, info, utr->buff, utr->data_len); + td->ed = ed; + td->NextTD = (uint32_t)td_new; + td->utr = utr; + utr->td_cnt = 1; /* increase TD count, for recalim counter */ + utr->status = 0; + + /*------------------------------------------------------------------------------------*/ + /* Hook ED and TD list to HCCA interrupt table */ + /*------------------------------------------------------------------------------------*/ + DISABLE_OHCI_IRQ(); + + ed->TailP = (uint32_t)td_new; + if (bIsNewED) + { + /* Add to list of the same interval */ + ed->NextED = ied->NextED; + ied->NextED = (uint32_t)ed; + } + + ENABLE_OHCI_IRQ(); + + //printf("Link INT ED 0x%x: 0x%x 0x%x 0x%x 0x%x\n", (int)ed, ed->Info, ed->TailP, ed->HeadP, ed->NextED); + + _ohci->HcControl |= USBH_HcControl_PLE_Msk; /* periodic list enable */ + return 0; +} + +static int ohci_iso_xfer(UTR_T *utr) +{ + UDEV_T *udev = utr->udev; + EP_INFO_T *ep = utr->ep; + ED_T *ed, *ied; + TD_T *td, *td_list, *last_td; + int i; + uint32_t info; + uint32_t buff_addr; + int8_t bIsNewED = 0; + + ied = get_int_tree_head_node(ep->bInterval); /* get head node of this interval */ + + /*------------------------------------------------------------------------------------*/ + /* Find if this ED was already in the list */ + /*------------------------------------------------------------------------------------*/ + info = ed_make_info(udev, ep); + ed = ied; + while (ed != NULL) + { + if (ed->Info == info) + break; /* Endpoint found */ + ed = (ED_T *)ed->NextED; + } + + if (ed == NULL) /* ED not found, create it */ + { + bIsNewED = 1; + ed = alloc_ohci_ED(); /* allocate an Endpoint Descriptor */ + if (ed == NULL) + return USBH_ERR_MEMORY_OUT; + ed->Info = info; + ed->HeadP = 0; + ed->bInterval = ep->bInterval; + } + else + + ep->hw_pipe = (void *)ed; + + /*------------------------------------------------------------------------------------*/ + /* Prepare TDs */ + /*------------------------------------------------------------------------------------*/ + if (utr->bIsoNewSched) /* Is the starting of isochronous streaming? */ + ed->next_sf = _hcca->frame_no + OHCI_ISO_DELAY; + + utr->td_cnt = 0; + utr->iso_sf = ed->next_sf; + + last_td = NULL; + td_list = NULL; + + for (i = 0; i < IF_PER_UTR; i++) + { + utr->iso_status[i] = USBH_ERR_NOT_ACCESS1; + + td = alloc_ohci_TD(utr); /* allocate a TD */ + if (td == NULL) + goto mem_out; + /* fill this TD */ + buff_addr = (uint32_t)(utr->iso_buff[i]); + td->Info = (TD_CC | TD_TYPE_ISO) | ed->next_sf; + ed->next_sf += get_ohci_interval(ed->bInterval); + td->CBP = buff_addr & ~0xFFF; + td->BE = buff_addr + utr->iso_xlen[i] - 1; + td->PSW[0] = 0xE000 | (buff_addr & 0xFFF); + + td->ed = ed; + utr->td_cnt++; /* increase TD count, for recalim counter */ + + /* chain to end of TD list */ + if (td_list == NULL) + td_list = td; + else + last_td->NextTD = (uint32_t)td; + + last_td = td; + }; + + /*------------------------------------------------------------------------------------*/ + /* Hook ED and TD list to HCCA interrupt table */ + /*------------------------------------------------------------------------------------*/ + utr->status = 0; + DISABLE_OHCI_IRQ(); + + if ((ed->HeadP & ~0x3) == 0) + ed->HeadP = (ed->HeadP & 0x2) | (uint32_t)td_list; /* keep toggleCarry bit */ + else + { + /* find the tail of TDs under this ED */ + td = (TD_T *)(ed->HeadP & ~0x3); + while (td->NextTD != 0) + { + td = (TD_T *)td->NextTD; + } + td->NextTD = (uint32_t)td_list; + } + + if (bIsNewED) + { + /* Add to list of the same interval */ + ed->NextED = ied->NextED; + ied->NextED = (uint32_t)ed; + } + + ENABLE_OHCI_IRQ(); + ED_debug("Link ISO ED 0x%x: 0x%x 0x%x 0x%x 0x%x\n", (int)ed, ed->Info, ed->TailP, ed->HeadP, ed->NextED); + _ohci->HcControl |= USBH_HcControl_PLE_Msk | USBH_HcControl_IE_Msk; /* enable periodic list and isochronous transfer */ + + return 0; + +mem_out: + while (td_list != NULL) + { + td = td_list; + td_list = (TD_T *)td_list->NextTD; + free_ohci_TD(td); + } + free_ohci_ED(ed); + return USBH_ERR_MEMORY_OUT; +} + +static UDEV_T *ohci_find_device_by_port(int port) +{ + UDEV_T *udev; + + udev = g_udev_list; + while (udev != NULL) + { + if ((udev->parent == NULL) && (udev->port_num == port) && + ((udev->speed == SPEED_LOW) || (udev->speed == SPEED_FULL))) + return udev; + udev = udev->next; + } + return NULL; +} + +static int ohci_rh_port_reset(int port) +{ + int retry; + int reset_time; + uint32_t t0; + + reset_time = usbh_tick_from_millisecond(PORT_RESET_TIME_MS); + + for (retry = 0; retry < PORT_RESET_RETRY; retry++) + { + _ohci->HcRhPortStatus[port] = USBH_HcRhPortStatus_PRS_Msk; + + t0 = usbh_get_ticks(); + while (usbh_get_ticks() - t0 < (reset_time) + 1) + { + /* + * If device is disconnected or port enabled, we can stop port reset. + */ + if (((_ohci->HcRhPortStatus[port] & USBH_HcRhPortStatus_CCS_Msk) == 0) || + ((_ohci->HcRhPortStatus[port] & (USBH_HcRhPortStatus_PES_Msk | USBH_HcRhPortStatus_CCS_Msk)) == (USBH_HcRhPortStatus_PES_Msk | USBH_HcRhPortStatus_CCS_Msk))) + goto port_reset_done; + } + reset_time += PORT_RESET_RETRY_INC_MS; + } + + USB_debug("OHCI port %d - port reset failed!\n", port + 1); + return USBH_ERR_PORT_RESET; + +port_reset_done: + if ((_ohci->HcRhPortStatus[port] & USBH_HcRhPortStatus_CCS_Msk) == 0) /* check again if device disconnected */ + { + _ohci->HcRhPortStatus[port] = USBH_HcRhPortStatus_CSC_Msk; /* clear CSC */ + return USBH_ERR_DISCONNECTED; + } + return USBH_OK; /* port reset success */ +} + +static int ohci_rh_polling(void) +{ + int i, change = 0; + UDEV_T *udev; + int ret; + + for (i = 0; i < OHCI_PORT_CNT; i++) + { + /* clear unwanted port change status */ + _ohci->HcRhPortStatus[i] = USBH_HcRhPortStatus_OCIC_Msk | USBH_HcRhPortStatus_PRSC_Msk | + USBH_HcRhPortStatus_PSSC_Msk | USBH_HcRhPortStatus_PESC_Msk; + + if ((_ohci->HcRhPortStatus[i] & USBH_HcRhPortStatus_CSC_Msk) == 0) + continue; + rt_kprintf("OHCI port%d status change: 0x%x\n", i + 1, _ohci->HcRhPortStatus[i]); + + /*--------------------------------------------------------------------------------*/ + /* connect status change */ + /*--------------------------------------------------------------------------------*/ + + _ohci->HcRhPortStatus[i] = USBH_HcRhPortStatus_CSC_Msk; /* clear CSC */ + + if (_ohci->HcRhPortStatus[i] & USBH_HcRhPortStatus_CCS_Msk) + { + /*----------------------------------------------------------------------------*/ + /* First of all, check if there's any previously connected device. */ + /*----------------------------------------------------------------------------*/ + while (1) + { + udev = ohci_find_device_by_port(i + 1); + if (udev == NULL) + break; + usbh_disconnect_device(udev); + } + + rt_kprintf("OHCI connect device.\n"); + + if (ohci_rh_port_reset(i) != USBH_OK) + continue; + + /* + * Port reset success... + */ + udev = alloc_device(); + if (udev == NULL) + continue; + + udev->parent = NULL; + udev->port_num = i + 1; + if (_ohci->HcRhPortStatus[i] & USBH_HcRhPortStatus_LSDA_Msk) + udev->speed = SPEED_LOW; + else + udev->speed = SPEED_FULL; + udev->hc_driver = &ohci_driver; + + ret = usbh_connect_device(udev); + if (ret < 0) + { + USB_error("connect_device error! [%d]\n", ret); + free_device(udev); + } + + change = 1; + } + else + { + /* + * Device disconnected + */ + rt_kprintf("OHCI disconnect device.\n"); + while (1) + { + udev = ohci_find_device_by_port(i + 1); + if (udev == NULL) + break; + usbh_disconnect_device(udev); + } + change = 1; + } + } + return change; +} + +void td_done(TD_T *td) +{ + UTR_T *utr = td->utr; + uint32_t info; + int cc; + + info = td->Info; + + TD_debug("td_done: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n", (int)td, td->Info, td->CBP, td->NextTD, td->BE); + + /* ISO ... drivers see per-TD length/status */ + if ((info & TD_TYPE_Msk) == TD_TYPE_ISO) + { + uint16_t sf; + int idx; + + sf = info & 0xFFFF; + idx = ((sf + 0x10000 - utr->iso_sf) & 0xFFFF) / get_ohci_interval(td->ed->bInterval); + if (idx >= IF_PER_UTR) + { + USB_error("ISO invalid index!! %d, %d\n", sf, utr->iso_sf); + goto td_out; + } + + cc = (td->PSW[0] >> 12) & 0xF; + if (cc == 0xF) /* this frame was not transferred */ + { + USB_debug("ISO F %d N/A!\n", sf); + utr->iso_status[idx] = USBH_ERR_SCH_OVERRUN; + goto td_out; + } + if ((cc != 0) && (cc != CC_DATA_UNDERRUN)) + { + utr->iso_status[idx] = USBH_ERR_CC_NO_ERR - cc; + goto td_out; + } + utr->iso_status[idx] = 0; + utr->iso_xlen[idx] = td->PSW[0] & 0x7FF; + } + else + { + cc = TD_CC_GET(info); + + /* short packet is fine */ + if ((cc != CC_NOERROR) && (cc != CC_DATA_UNDERRUN)) + { + USB_error("TD error, CC = 0x%x\n", cc); + if (cc == CC_STALL) + utr->status = USBH_ERR_STALL; + else + utr->status = USBH_ERR_TRANSFER; + } + + switch (info & TD_TYPE_Msk) + { + case TD_TYPE_CTRL: + if (info & TD_CTRL_DATA) + { + if (td->CBP == 0) + utr->xfer_len += td->BE - td->buff_start + 1; + else + utr->xfer_len += td->CBP - td->buff_start; + } + break; + + case TD_TYPE_BULK: + case TD_TYPE_INT: + if (td->CBP == 0) + utr->xfer_len += td->BE - td->buff_start + 1; + else + utr->xfer_len += td->CBP - td->buff_start; + break; + } + } + +td_out: + + utr->td_cnt--; + + /* If all TDs are done, call-back to requester. */ + if (utr->td_cnt == 0) + { + utr->bIsTransferDone = 1; + if (utr->func) + utr->func(utr); + } +} + +/* in IRQ context */ +static void remove_ed() +{ + ED_T *ed, *ed_p, *ied; + TD_T *td, *td_next; + UTR_T *utr; + int found; + + while (ed_remove_list != NULL) + { + ED_debug("Remove ED: 0x%x, %d\n", (int)ed_remove_list, ed_remove_list->bInterval); + ed_p = ed_remove_list; + found = 0; + + /*--------------------------------------------------------------------------------*/ + /* Remove endpoint from Control List if found */ + /*--------------------------------------------------------------------------------*/ + if ((ed_p->Info & ED_EP_ADDR_Msk) == 0) + { + if (_ohci->HcControlHeadED == (uint32_t)ed_p) + { + _ohci->HcControlHeadED = (uint32_t)ed_p->NextED; + found = 1; + } + else + { + ed = (ED_T *)_ohci->HcControlHeadED; + while (ed != NULL) + { + if (ed->NextED == (uint32_t)ed_p) + { + ed->NextED = ed_p->NextED; + found = 1; + } + ed = (ED_T *)ed->NextED; + } + } + } + + /*--------------------------------------------------------------------------------*/ + /* Remove INT or ISO endpoint from HCCA interrupt table */ + /*--------------------------------------------------------------------------------*/ + else if (ed_p->bInterval > 0) + { + ied = get_int_tree_head_node(ed_p->bInterval); + + ed = ied; + while (ed != NULL) + { + if (ed->NextED == (uint32_t)ed_p) + { + ed->NextED = ed_p->NextED; + found = 1; + break; + } + ed = (ED_T *)ed->NextED; + } + } + + /*--------------------------------------------------------------------------------*/ + /* Remove endpoint from Bulk List if found */ + /*--------------------------------------------------------------------------------*/ + else + { + if (_ohci->HcBulkHeadED == (uint32_t)ed_p) + { + ed = (ED_T *)ed_p; + _ohci->HcBulkHeadED = ed_p->NextED; + found = 1; + } + else + { + ed = (ED_T *)_ohci->HcBulkHeadED; + while (ed != NULL) + { + if (ed->NextED == (uint32_t)ed_p) + { + ed->NextED = ed_p->NextED; + found = 1; + } + ed = (ED_T *)ed->NextED; + } + } + } + + /*--------------------------------------------------------------------------------*/ + /* Remove and free all TDs under this endpoint */ + /*--------------------------------------------------------------------------------*/ + if (found) + { + td = (TD_T *)(ed_p->HeadP & ~0x3); + if (td != NULL) + { + while (td != NULL) + { + utr = td->utr; + td_next = (TD_T *)td->NextTD; + free_ohci_TD(td); + td = td_next; + + utr->td_cnt--; + if (utr->td_cnt == 0) + { + utr->status = USBH_ERR_ABORT; + utr->bIsTransferDone = 1; + if (utr->func) + utr->func(utr); + } + } + } + } + + /* + * Done. Remove this ED from [ed_remove_list] and free it. + */ + ed_remove_list = ed_p->next; + free_ohci_ED(ed_p); + } +} + + +//static irqreturn_t ohci_irq (struct usb_hcd *hcd) +//void OHCI_IRQHandler(void) +void nu_ohci_isr(int vector, void *param) +{ + TD_T *td, *td_prev, *td_next; + uint32_t int_sts; + + //if ( nu_sys_usb0_role() != USB0_ID_HOST ) return; + + int_sts = _ohci->HcInterruptStatus; + + //USB_debug("ohci int_sts = 0x%x\n", int_sts); + + if ((_ohci->HcInterruptEnable & USBH_HcInterruptEnable_SF_Msk) && + (int_sts & USBH_HcInterruptStatus_SF_Msk)) + { + int_sts &= ~USBH_HcInterruptStatus_SF_Msk; + + _ohci->HcInterruptDisable = USBH_HcInterruptDisable_SF_Msk; + remove_ed(); + _ohci->HcInterruptStatus = USBH_HcInterruptStatus_SF_Msk; + } + + if (int_sts & USBH_HcInterruptStatus_WDH_Msk) + { + int_sts &= ~USBH_HcInterruptStatus_WDH_Msk; + /* + * reverse done list + */ + td = (TD_T *)(_hcca->done_head & TD_ADDR_MASK); + _hcca->done_head = 0; + td_prev = NULL; + _ohci->HcInterruptStatus = USBH_HcInterruptStatus_WDH_Msk; + + while (td != NULL) + { + //TD_debug("Done list TD 0x%x => 0x%x\n", (int)td, (int)td->NextTD); + td_next = (TD_T *)(td->NextTD & TD_ADDR_MASK); + td->NextTD = (uint32_t)td_prev; + td_prev = td; + td = td_next; + } + td = td_prev; /* first TD of the reversed done list */ + + /* + * reclaim TDs + */ + while (td != NULL) + { + TD_debug("Reclaim TD 0x%x, next 0x%x\n", (int)td, td->NextTD); + td_next = (TD_T *)td->NextTD; + td_done(td); + free_ohci_TD(td); + td = td_next; + } + } + + if (int_sts & USBH_HcInterruptStatus_RHSC_Msk) + { + _ohci->HcInterruptDisable = USBH_HcInterruptDisable_RHSC_Msk; + } + + _ohci->HcInterruptStatus = int_sts; +} + +#ifdef ENABLE_DEBUG_MSG + +void dump_ohci_int_table() +{ + int i; + ED_T *ed; + + for (i = 0; i < 32; i++) +// for (i = 0; i < 1; i++) + + { + USB_debug("%02d: ", i); + + ed = (ED_T *)_hcca->int_table[i]; + + while (ed != NULL) + { + USB_debug("0x%x (0x%x) => ", (int)ed, ed->HeadP); + ed = (ED_T *)ed->NextED; + } + rt_kprintf("0\n"); + } +} + +void dump_ohci_regs() +{ + USB_debug("Dump OCHI registers:\n"); + USB_debug(" HcRevision = 0x%x\n", _ohci->HcRevision); + USB_debug(" HcControl = 0x%x\n", _ohci->HcControl); + USB_debug(" HcCommandStatus = 0x%x\n", _ohci->HcCommandStatus); + USB_debug(" HcInterruptStatus = 0x%x\n", _ohci->HcInterruptStatus); + USB_debug(" HcInterruptEnable = 0x%x\n", _ohci->HcInterruptEnable); + USB_debug(" HcInterruptDisable = 0x%x\n", _ohci->HcInterruptDisable); + USB_debug(" HcHCCA = 0x%x\n", _ohci->HcHCCA); + USB_debug(" HcPeriodCurrentED = 0x%x\n", _ohci->HcPeriodCurrentED); + USB_debug(" HcControlHeadED = 0x%x\n", _ohci->HcControlHeadED); + USB_debug(" HcControlCurrentED = 0x%x\n", _ohci->HcControlCurrentED); + USB_debug(" HcBulkHeadED = 0x%x\n", _ohci->HcBulkHeadED); + USB_debug(" HcBulkCurrentED = 0x%x\n", _ohci->HcBulkCurrentED); + USB_debug(" HcDoneHead = 0x%x\n", _ohci->HcDoneHead); + USB_debug(" HcFmInterval = 0x%x\n", _ohci->HcFmInterval); + USB_debug(" HcFmRemaining = 0x%x\n", _ohci->HcFmRemaining); + USB_debug(" HcFmNumber = 0x%x\n", _ohci->HcFmNumber); + USB_debug(" HcPeriodicStart = 0x%x\n", _ohci->HcPeriodicStart); + USB_debug(" HcLSThreshold = 0x%x\n", _ohci->HcLSThreshold); + USB_debug(" HcRhDescriptorA = 0x%x\n", _ohci->HcRhDescriptorA); + USB_debug(" HcRhDescriptorB = 0x%x\n", _ohci->HcRhDescriptorB); + USB_debug(" HcRhStatus = 0x%x\n", _ohci->HcRhStatus); + USB_debug(" HcRhPortStatus0 = 0x%x\n", _ohci->HcRhPortStatus[0]); + USB_debug(" HcRhPortStatus1 = 0x%x\n", _ohci->HcRhPortStatus[1]); + USB_debug(" HcPhyControl = 0x%x\n", _ohci->HcPhyControl); + USB_debug(" HcMiscControl = 0x%x\n", _ohci->HcMiscControl); +} + +void dump_ohci_ports() +{ + USB_debug("_ohci port0=0x%x, port1=0x%x\n", _ohci->HcRhPortStatus[0], _ohci->HcRhPortStatus[1]); +} + +#endif // ENABLE_DEBUG_MSG + +HC_DRV_T ohci_driver = +{ + ohci_init, /* init */ + ohci_shutdown, /* shutdown */ + ohci_suspend, /* suspend */ + ohci_resume, /* resume */ + ohci_ctrl_xfer, /* ctrl_xfer */ + ohci_bulk_xfer, /* bulk_xfer */ + ohci_int_xfer, /* int_xfer */ + ohci_iso_xfer, /* iso_xfer */ + ohci_quit_xfer, /* quit_xfer */ + ohci_rh_port_reset, /* rthub_port_reset */ + ohci_rh_polling /* rthub_polling */ +}; + +/// @endcond HIDDEN_SYMBOLS + +/*** (C) COPYRIGHT 2017 Nuvoton Technology Corp. ***/ diff --git a/bsp/nuvoton/libraries/nuc980/UsbHostLib/src/support.c b/bsp/nuvoton/libraries/nuc980/UsbHostLib/src/support.c new file mode 100644 index 0000000000..5006034c50 --- /dev/null +++ b/bsp/nuvoton/libraries/nuc980/UsbHostLib/src/support.c @@ -0,0 +1,324 @@ +/**************************************************************************//** + * @file support.c + * @version V1.10 + * $Revision: 11 $ + * $Date: 14/10/03 1:54p $ + * @brief Functions to support USB host driver. + * + * @note + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2018 Nuvoton Technology Corp. All rights reserved. +*****************************************************************************/ + +#include +#include +#include + +#include "usb.h" + +/// @cond HIDDEN_SYMBOLS + + +#define USB_MEMORY_POOL_SIZE (32*1024) +#define USB_MEM_BLOCK_SIZE 128 + +#define BOUNDARY_WORD 4 + + +static uint32_t _FreeMemorySize; +uint32_t _AllocatedMemorySize; + + +#define USB_MEM_ALLOC_MAGIC 0x19685788 /* magic number in leading block */ + +typedef struct USB_mhdr +{ + uint32_t flag; /* 0:free, 1:allocated, 0x3:first block */ + uint32_t bcnt; /* if allocated, the block count of allocated memory block */ + uint32_t magic; + uint32_t reserved; +} USB_MHDR_T; + +uint8_t _USBMemoryPool[USB_MEMORY_POOL_SIZE] __attribute__((aligned(USB_MEM_BLOCK_SIZE))); + + +static USB_MHDR_T *_pCurrent; +uint32_t *_USB_pCurrent = (uint32_t *) &_pCurrent; + +static uint32_t _MemoryPoolBase, _MemoryPoolEnd; + + +void USB_InitializeMemoryPool() +{ + _MemoryPoolBase = (UINT32)&_USBMemoryPool[0] | NON_CACHE_MASK; + _MemoryPoolEnd = _MemoryPoolBase + USB_MEMORY_POOL_SIZE; + _FreeMemorySize = _MemoryPoolEnd - _MemoryPoolBase; + _AllocatedMemorySize = 0; + _pCurrent = (USB_MHDR_T *)_MemoryPoolBase; + memset((char *)_MemoryPoolBase, 0, _FreeMemorySize); +} + + +int USB_available_memory() +{ + return _FreeMemorySize; +} + + +int USB_allocated_memory() +{ + return _AllocatedMemorySize; +} + + +void *USB_malloc(INT wanted_size, INT boundary) +{ + USB_MHDR_T *pPrimitivePos = _pCurrent; + USB_MHDR_T *pFound; + INT found_size = -1; + INT i, block_count; + INT wrap = 0; + int disable_ohci_irq, disable_ehci_irq; + + if (IS_OHCI_IRQ_ENABLED()) + disable_ohci_irq = 1; + else + disable_ohci_irq = 0; + + if (IS_EHCI_IRQ_ENABLED()) + disable_ehci_irq = 1; + else + disable_ehci_irq = 0; + + if (disable_ohci_irq) + DISABLE_OHCI_IRQ(); + if (disable_ehci_irq) + DISABLE_EHCI_IRQ(); + + if (wanted_size >= _FreeMemorySize) + { + rt_kprintf("USB_malloc - want=%d, free=%d\n", wanted_size, _FreeMemorySize); + if (disable_ohci_irq) + ENABLE_OHCI_IRQ(); + if (disable_ehci_irq) + ENABLE_EHCI_IRQ(); + return NULL; + } + + if ((UINT32)_pCurrent >= _MemoryPoolEnd) + _pCurrent = (USB_MHDR_T *)_MemoryPoolBase; /* wrapped */ + + do + { + if (_pCurrent->flag) /* is not a free block */ + { + if (_pCurrent->magic != USB_MEM_ALLOC_MAGIC) + { + rt_kprintf("\nUSB_malloc - incorrect magic number! C:%x F:%x, wanted:%d, Base:0x%x, End:0x%x\n", (UINT32)_pCurrent, _FreeMemorySize, wanted_size, (UINT32)_MemoryPoolBase, (UINT32)_MemoryPoolEnd); + if (disable_ohci_irq) + ENABLE_OHCI_IRQ(); + if (disable_ehci_irq) + ENABLE_EHCI_IRQ(); + return NULL; + } + + if (_pCurrent->flag == 0x3) + _pCurrent = (USB_MHDR_T *)((UINT32)_pCurrent + _pCurrent->bcnt * USB_MEM_BLOCK_SIZE); + else + { + rt_kprintf("USB_malloc warning - not the first block!\n"); + _pCurrent = (USB_MHDR_T *)((UINT32)_pCurrent + USB_MEM_BLOCK_SIZE); + } + + if ((UINT32)_pCurrent > _MemoryPoolEnd) + rt_kprintf("USB_malloc - behind limit!!\n"); + + if ((UINT32)_pCurrent == _MemoryPoolEnd) + { + //rt_kprintf("USB_alloc - warp!!\n"); + wrap = 1; + _pCurrent = (USB_MHDR_T *)_MemoryPoolBase; /* wrapped */ + } + + found_size = -1; /* reset the accumlator */ + } + else /* is a free block */ + { + if (found_size == -1) /* the leading block */ + { + pFound = _pCurrent; + block_count = 1; + + if (boundary > BOUNDARY_WORD) + found_size = 0; /* not use the data area of the leading block */ + else + found_size = USB_MEM_BLOCK_SIZE - sizeof(USB_MHDR_T); + + /* check boundary - + * If boundary > BOUNDARY_WORD, the start of next block should + * be the beginning address of allocated memory. Thus, we check + * the boundary of the next block. The leading block will be + * used as a header only. + */ + if ((boundary > BOUNDARY_WORD) && + ((((UINT32)_pCurrent) + USB_MEM_BLOCK_SIZE >= _MemoryPoolEnd) || + ((((UINT32)_pCurrent) + USB_MEM_BLOCK_SIZE) % boundary != 0))) + found_size = -1; /* violate boundary, reset the accumlator */ + } + else /* not the leading block */ + { + found_size += USB_MEM_BLOCK_SIZE; + block_count++; + } + + if (found_size >= wanted_size) + { + pFound->bcnt = block_count; + pFound->magic = USB_MEM_ALLOC_MAGIC; + _FreeMemorySize -= block_count * USB_MEM_BLOCK_SIZE; + _AllocatedMemorySize += block_count * USB_MEM_BLOCK_SIZE; + _pCurrent = pFound; + for (i = 0; i < block_count; i++) + { + _pCurrent->flag = 1; /* allocate block */ + _pCurrent = (USB_MHDR_T *)((UINT32)_pCurrent + USB_MEM_BLOCK_SIZE); + } + pFound->flag = 0x3; + + if (boundary > BOUNDARY_WORD) + { + if (disable_ohci_irq) + ENABLE_OHCI_IRQ(); + if (disable_ehci_irq) + ENABLE_EHCI_IRQ(); + //rt_kprintf("- 0x%x, %d\n", (int)pFound, wanted_size); + return (void *)((UINT32)pFound + USB_MEM_BLOCK_SIZE); + } + else + { + //USB_debug("USB_malloc(%d,%d):%x\tsize:%d, C:0x%x, %d\n", wanted_size, boundary, (UINT32)pFound + sizeof(USB_MHDR_T), block_count * USB_MEM_BLOCK_SIZE, _pCurrent, block_count); + if (disable_ohci_irq) + ENABLE_OHCI_IRQ(); + if (disable_ehci_irq) + ENABLE_EHCI_IRQ(); + //rt_kprintf("- 0x%x, %d\n", (int)pFound, wanted_size); + return (void *)((UINT32)pFound + sizeof(USB_MHDR_T)); + } + } + + /* advance to the next block */ + _pCurrent = (USB_MHDR_T *)((UINT32)_pCurrent + USB_MEM_BLOCK_SIZE); + if ((UINT32)_pCurrent >= _MemoryPoolEnd) + { + wrap = 1; + _pCurrent = (USB_MHDR_T *)_MemoryPoolBase; /* wrapped */ + found_size = -1; /* reset accumlator */ + } + } + } + while ((wrap == 0) || (_pCurrent < pPrimitivePos)); + + rt_kprintf("USB_malloc - No free memory!\n"); + if (disable_ohci_irq) + ENABLE_OHCI_IRQ(); + if (disable_ehci_irq) + ENABLE_EHCI_IRQ(); + return NULL; +} + + +void USB_free(void *alloc_addr) +{ + USB_MHDR_T *pMblk; + UINT32 addr = (UINT32)alloc_addr; + INT i, count; + int disable_ohci_irq, disable_ehci_irq; + + if (IS_OHCI_IRQ_ENABLED()) + disable_ohci_irq = 1; + else + disable_ohci_irq = 0; + + if (IS_EHCI_IRQ_ENABLED()) + disable_ehci_irq = 1; + else + disable_ehci_irq = 0; + + //rt_kprintf("USB_free: 0x%x\n", (int)alloc_addr); + + if ((addr < _MemoryPoolBase) || (addr >= _MemoryPoolEnd)) + { + if (addr) + { + rt_kprintf("[%s]Wrong!!\n", __func__); + //free(alloc_addr); + } + return; + } + + if (disable_ohci_irq) + DISABLE_OHCI_IRQ(); + if (disable_ehci_irq) + DISABLE_EHCI_IRQ(); + + //rt_kprintf("USB_free:%x\n", (INT)addr+USB_MEM_BLOCK_SIZE); + + /* get the leading block address */ + if (addr % USB_MEM_BLOCK_SIZE == 0) + addr -= USB_MEM_BLOCK_SIZE; + else + addr -= sizeof(USB_MHDR_T); + + if (addr % USB_MEM_BLOCK_SIZE != 0) + { + rt_kprintf("USB_free fatal error on address: %x!!\n", (UINT32)alloc_addr); + if (disable_ohci_irq) + ENABLE_OHCI_IRQ(); + if (disable_ehci_irq) + ENABLE_EHCI_IRQ(); + return; + } + + pMblk = (USB_MHDR_T *)addr; + if (pMblk->flag == 0) + { + rt_kprintf("USB_free(), warning - try to free a free block: %x\n", (UINT32)alloc_addr); + if (disable_ohci_irq) + ENABLE_OHCI_IRQ(); + if (disable_ehci_irq) + ENABLE_EHCI_IRQ(); + return; + } + if (pMblk->magic != USB_MEM_ALLOC_MAGIC) + { + rt_kprintf("USB_free(), warning - try to free an unknow block at address:%x.\n", addr); + if (disable_ohci_irq) + ENABLE_OHCI_IRQ(); + if (disable_ehci_irq) + ENABLE_EHCI_IRQ(); + return; + } + + //_pCurrent = pMblk; + + //rt_kprintf("+ 0x%x, %d\n", (int)pMblk, pMblk->bcnt); + + count = pMblk->bcnt; + for (i = 0; i < count; i++) + { + pMblk->flag = 0; /* release block */ + pMblk = (USB_MHDR_T *)((UINT32)pMblk + USB_MEM_BLOCK_SIZE); + } + + _FreeMemorySize += count * USB_MEM_BLOCK_SIZE; + _AllocatedMemorySize -= count * USB_MEM_BLOCK_SIZE; + if (disable_ohci_irq) + ENABLE_OHCI_IRQ(); + if (disable_ehci_irq) + ENABLE_EHCI_IRQ(); + return; +} + + +/// @endcond HIDDEN_SYMBOLS + diff --git a/bsp/nuvoton/libraries/nuc980/UsbHostLib/src/usb_core.c b/bsp/nuvoton/libraries/nuc980/UsbHostLib/src/usb_core.c new file mode 100644 index 0000000000..7bb6c30bbe --- /dev/null +++ b/bsp/nuvoton/libraries/nuc980/UsbHostLib/src/usb_core.c @@ -0,0 +1,312 @@ +/**************************************************************************//** + * @file usb_core.c + * @version V1.10 + * $Revision: 11 $ + * $Date: 14/10/03 1:54p $ + * @brief USB Host library core. + * + * @note + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2018 Nuvoton Technology Corp. All rights reserved. +*****************************************************************************/ + +#include +#include +#include + +#include "usb.h" +#include "hub.h" + + +/// @cond HIDDEN_SYMBOLS + +USBH_T *_ohci; +HSUSBH_T *_ehci; + +static UDEV_DRV_T *_drivers[MAX_UDEV_DRIVER]; + +static CONN_FUNC *g_conn_func, *g_disconn_func; + + +//extern void EHCI_IRQHandler(void); +//extern void OHCI_IRQHandler(void); +extern void nu_ohci_isr(int vector, void *param); +extern void nu_ehci_isr(int vector, void *param); + + +/// @endcond HIDDEN_SYMBOLS + + +/** + * @brief Initialize NUC980 USB Host controller and USB stack. + * + * @return None. + */ +void usbh_core_init() +{ + DISABLE_EHCI_IRQ(); + DISABLE_OHCI_IRQ(); + + _ohci = USBH; + _ehci = HSUSBH; + + memset(_drivers, 0, sizeof(_drivers)); + + g_conn_func = NULL; + g_disconn_func = NULL; + +// usbh_hub_init(); + + _ehci->USBPCR0 = 0x160; /* enable PHY 0 */ + _ehci->USBPCR1 = 0x520; /* enable PHY 1 */ + usbh_memory_init(); + + //_ohci->HcMiscControl |= USBH_HcMiscControl_OCAL_Msk; /* Over-current active low */ + _ohci->HcMiscControl &= ~USBH_HcMiscControl_OCAL_Msk; /* Over-current active high */ + +#ifdef ENABLE_OHCI + //sysInstallISR(IRQ_LEVEL_1, IRQ_OHCI, (PVOID)OHCI_IRQHandler); + rt_hw_interrupt_install(IRQ_OHCI, nu_ohci_isr, NULL, "ohci"); + rt_hw_interrupt_set_priority(IRQ_OHCI, IRQ_LEVEL_1); + + ohci_driver.init(); + ENABLE_OHCI_IRQ(); +#endif + +#ifdef ENABLE_EHCI + //sysInstallISR(IRQ_LEVEL_1, IRQ_EHCI, (PVOID)EHCI_IRQHandler); + rt_hw_interrupt_install(IRQ_EHCI, nu_ehci_isr, NULL, "ehci"); + rt_hw_interrupt_set_priority(IRQ_EHCI, IRQ_LEVEL_1); + + ehci_driver.init(); + ENABLE_EHCI_IRQ(); +#endif +} + + +/** + * @brief Let USB stack polls all root hubs. If there's any hub port + * change found, USB stack will manage the hub events in this function call. + * In this function, USB stack enumerates newly connected devices and remove staff + * of disconnected devices. User's application should periodically invoke this + * function. + * @return There's hub port change or not. + * @retval 0 No any hub port status changes found. + * @retval 1 There's hub port status changes. + */ +int usbh_polling_root_hubs(void) +{ + int ret, change = 0; + +#ifdef ENABLE_EHCI + do + { + ret = ehci_driver.rthub_polling(); + if (ret) + change = 1; + } + while (ret == 1); + + // scan_isochronous_list(); + +#endif + +#ifdef ENABLE_OHCI + do + { + ret = ohci_driver.rthub_polling(); + if (ret) + change = 1; + } + while (ret == 1); +#endif + + return change; +} + +/** + * @brief Force to quit an endpoint transfer. + * @param[in] udev The USB device. + * @param[in] ep The endpoint to be quit. + * @retval 0 Transfer success + * @retval < 0 Failed. Refer to error code definitions. + */ +int usbh_quit_xfer(UDEV_T *udev, EP_INFO_T *ep) +{ + return udev->hc_driver->quit_xfer(NULL, ep); +} + + +int usbh_connect_device(UDEV_T *udev) +{ + usbh_delay_ms(100); /* initially, give 100 ms delay */ + + if (g_conn_func) + g_conn_func(udev, 0); + + return 0; +} + + +void usbh_disconnect_device(UDEV_T *udev) +{ + USB_debug("disconnect device...\n"); + + if (g_disconn_func) + g_disconn_func(udev, 0); + + +#if 1 //CHECK: Maybe create a new API to quit_xfer and free udev for application + usbh_quit_xfer(udev, &(udev->ep0)); /* Quit control transfer if hw_pipe is not NULL. */ + + /* remove device from global device list */ +// free_dev_address(udev->dev_num); + free_device(udev); + +// usbh_memory_used(); +#endif +} + +/** + * @brief Install device connect and disconnect callback function. + * + * @param[in] conn_func Device connect callback function. + * @param[in] disconn_func Device disconnect callback function. + * @return None. + */ +void usbh_install_conn_callback(CONN_FUNC *conn_func, CONN_FUNC *disconn_func) +{ + g_conn_func = conn_func; + g_disconn_func = disconn_func; +} + +int usbh_reset_port(UDEV_T *udev) +{ + if (udev->parent == NULL) + { + if (udev->hc_driver) + return udev->hc_driver->rthub_port_reset(udev->port_num - 1); + else + return USBH_ERR_NOT_FOUND; + } + else + { + return udev->parent->port_reset(udev->parent, udev->port_num); + } +} + + +/** + * @brief Force to quit an UTR transfer. + * @param[in] utr The UTR transfer to be quit. + * @retval 0 Transfer success + * @retval < 0 Failed. Refer to error code definitions. + */ +int usbh_quit_utr(UTR_T *utr) +{ + if (!utr || !utr->udev) + return USBH_ERR_NOT_FOUND; + + return utr->udev->hc_driver->quit_xfer(utr, NULL); +} + + +/** + * @brief Execute an USB request in control transfer. This function returns after the request + * was done or aborted. + * @param[in] udev The target USB device. + * @param[in] bmRequestType Characteristics of request + * @param[in] bRequest Specific request + * @param[in] wValue Word-sized field that varies according to request + * @param[in] wIndex Word-sized field that varies according to request + * @param[in] wLength Number of bytes to transfer if there is a Data stage + * @param[in] buff Data buffer used in data stage + * @param[out] xfer_len Transmitted/received length of data + * @param[in] timeout Time-out limit (in 10ms - timer tick) of this transfer + * @retval 0 Transfer success + * @retval < 0 Transfer failed. Refer to error code definitions. + */ +int usbh_ctrl_xfer(UDEV_T *udev, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + uint16_t wLength, uint8_t *buff, uint32_t *xfer_len, uint32_t timeout) +{ + UTR_T *utr; + uint32_t t0, timeout_tick; + int status; + + *xfer_len = 0; + + //if (check_device(udev)) + // return USBH_ERR_INVALID_PARAM; + + utr = alloc_utr(udev); + if (utr == NULL) + return USBH_ERR_MEMORY_OUT; + + utr->setup.bmRequestType = bmRequestType; + utr->setup.bRequest = bRequest; + utr->setup.wValue = wValue; + utr->setup.wIndex = wIndex; + utr->setup.wLength = wLength; + + utr->buff = buff; + utr->data_len = wLength; + utr->bIsTransferDone = 0; + status = udev->hc_driver->ctrl_xfer(utr); + if (status < 0) + { + udev->ep0.hw_pipe = NULL; + free_utr(utr); + return status; + } + + timeout_tick = usbh_tick_from_millisecond(timeout); + t0 = usbh_get_ticks(); + while (utr->bIsTransferDone == 0) + { + if (usbh_get_ticks() - t0 > timeout_tick) + { + usbh_quit_utr(utr); + free_utr(utr); + udev->ep0.hw_pipe = NULL; + return USBH_ERR_TIMEOUT; + } + } + + status = utr->status; + + if (status == 0) + { + *xfer_len = utr->xfer_len; + } + free_utr(utr); + + return status; +} + +/** + * @brief Execute a bulk transfer request. This function will return immediately after + * issued the bulk transfer. USB stack will later call back utr->func() once the bulk + * transfer was done or aborted. + * @param[in] utr The bulk transfer request. + * @retval 0 Transfer success + * @retval < 0 Failed. Refer to error code definitions. + */ +int usbh_bulk_xfer(UTR_T *utr) +{ + return utr->udev->hc_driver->bulk_xfer(utr); +} + +/** + * @brief Execute an interrupt transfer request. This function will return immediately after + * issued the interrupt transfer. USB stack will later call back utr->func() once the + * interrupt transfer was done or aborted. + * @param[in] utr The interrupt transfer request. + * @retval 0 Transfer success + * @retval < 0 Failed. Refer to error code definitions. + */ +int usbh_int_xfer(UTR_T *utr) +{ + return utr->udev->hc_driver->int_xfer(utr); +} + + diff --git a/bsp/nuvoton/libraries/nuc980/rtt_port/Kconfig b/bsp/nuvoton/libraries/nuc980/rtt_port/Kconfig index e1633de341..d2c34d6461 100644 --- a/bsp/nuvoton/libraries/nuc980/rtt_port/Kconfig +++ b/bsp/nuvoton/libraries/nuc980/rtt_port/Kconfig @@ -114,13 +114,6 @@ config SOC_SERIES_NUC980 help Choose this option if you need TIMER function mode. - config BSP_USING_TPWM0 - select BSP_USING_TPWM - select RT_USING_PWM - bool "TIMER PWM" - help - Choose this option if you need PWM function mode. - config BSP_USING_TIMER0_CAPTURE select BSP_USING_TIMER_CAPTURE select RT_USING_INPUT_CAPTURE @@ -146,13 +139,6 @@ config SOC_SERIES_NUC980 help Choose this option if you need TIMER function mode. - config BSP_USING_TPWM1 - select BSP_USING_TPWM - select RT_USING_PWM - bool "TIMER PWM" - help - Choose this option if you need PWM function mode. - config BSP_USING_TIMER1_CAPTURE select BSP_USING_TIMER_CAPTURE select RT_USING_INPUT_CAPTURE @@ -177,13 +163,6 @@ config SOC_SERIES_NUC980 help Choose this option if you need TIMER function mode. - config BSP_USING_TPWM2 - select BSP_USING_TPWM - select RT_USING_PWM - bool "TIMER PWM" - help - Choose this option if you need PWM function mode. - config BSP_USING_TIMER2_CAPTURE select BSP_USING_TIMER_CAPTURE select RT_USING_INPUT_CAPTURE @@ -208,13 +187,6 @@ config SOC_SERIES_NUC980 help Choose this option if you need TIMER function mode. - config BSP_USING_TPWM3 - select BSP_USING_TPWM - select RT_USING_PWM - bool "TIMER PWM" - help - Choose this option if you need PWM function mode. - config BSP_USING_TIMER3_CAPTURE select BSP_USING_TIMER_CAPTURE select RT_USING_INPUT_CAPTURE @@ -239,13 +211,6 @@ config SOC_SERIES_NUC980 help Choose this option if you need TIMER function mode. - config BSP_USING_TPWM4 - select BSP_USING_TPWM - select RT_USING_PWM - bool "TIMER PWM" - help - Choose this option if you need PWM function mode. - config BSP_USING_TIMER4_CAPTURE select BSP_USING_TIMER_CAPTURE select RT_USING_INPUT_CAPTURE @@ -634,9 +599,4 @@ config SOC_SERIES_NUC980 config BSP_USING_USBH bool "Enable USB Host Controller(USBH)" select RT_USING_USB_HOST - select RT_USBH_MSTORAGE - - config BSP_USING_OTG - bool "Enable USB On-The-Go(OTG)" - select BSP_USING_USBH - select BSP_USING_USBD + select RT_USBH_MSTORAGE \ No newline at end of file diff --git a/bsp/nuvoton/libraries/nuc980/rtt_port/SConscript b/bsp/nuvoton/libraries/nuc980/rtt_port/SConscript index b92729931c..e1b907cc3c 100644 --- a/bsp/nuvoton/libraries/nuc980/rtt_port/SConscript +++ b/bsp/nuvoton/libraries/nuc980/rtt_port/SConscript @@ -9,6 +9,6 @@ CPPPATH = [cwd] group = [] # USB driver constrain -group = DefineGroup('nuc980_rttport', src, depend = [''], CPPPATH = CPPPATH) +group = DefineGroup('Drivers', src, depend = [''], CPPPATH = CPPPATH) Return('group') diff --git a/bsp/nuvoton/libraries/nuc980/rtt_port/drv_common.c b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_common.c index f13b271ea9..a7dbe15720 100644 --- a/bsp/nuvoton/libraries/nuc980/rtt_port/drv_common.c +++ b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_common.c @@ -22,22 +22,11 @@ static struct mem_desc hw_mem_desc[] = { 0x00000000, 0xFFFFFFFF, 0x00000000, RW_NCNB }, /* None cached for 4G memory */ { 0x00000000, BOARD_SDRAM_SIZE - 1, 0x00000000, RW_CB }, /* 64M cached DDR memory */ { BIT31, (BIT31 | BOARD_SDRAM_SIZE) - 1, BIT31, RW_NCNB }, /* Shadow DDR Map */ - { 0x3C000000, 0x3C00E000 - 1, 0x3C000000, RW_NCNB }, /* 56K SRAM memory */ - { 0xBC000000, 0xBC00E000 - 1, 0xBC000000, RW_NCNB } /* 56K Shadow memory */ + { 0x3C000000, 0x3C004000 - 1, 0x3C000000, RW_NCNB }, /* 16K SRAM memory */ + { 0xBC000000, 0xBC004000 - 1, 0xBC000000, RW_NCNB } /* 16K Shadow memory */ }; #endif -void nu_clock_base_dump(void) -{ - rt_kprintf("SYS_UPLL = %d\n", sysGetClock(SYS_UPLL)); - rt_kprintf("SYS_APLL = %d\n", sysGetClock(SYS_APLL)); - rt_kprintf("SYS_SYSTEM = %d\n", sysGetClock(SYS_SYSTEM)); - rt_kprintf("SYS_HCLK = %d\n", sysGetClock(SYS_HCLK)); - rt_kprintf("SYS_PCLK01 = %d\n", sysGetClock(SYS_PCLK01)); - rt_kprintf("SYS_PCLK2 = %d\n", sysGetClock(SYS_PCLK2)); - rt_kprintf("SYS_CPU = %d\n", sysGetClock(SYS_CPU)); -} - /** * This function will initial M487 board. */ @@ -52,6 +41,12 @@ RT_WEAK void rt_hw_board_init(void) #if defined(BSP_USING_MMU) /* initialize mmu */ rt_hw_mmu_init(&hw_mem_desc[0], sizeof(hw_mem_desc) / sizeof(hw_mem_desc[0])); +#else + /* disable I/D cache */ + mmu_disable_dcache(); + mmu_disable_icache(); + mmu_disable(); + mmu_invalidate_tlb(); #endif /* initialize hardware interrupt */ @@ -72,9 +67,6 @@ RT_WEAK void rt_hw_board_init(void) rt_console_set_device(RT_CONSOLE_DEVICE_NAME); #endif - nu_clock_base_dump(); - rt_kprintf("HEAP_START=0x%08x, HEAP_END=0x%08x\n", BOARD_HEAP_START, BOARD_HEAP_END); - #ifdef RT_USING_COMPONENTS_INIT rt_components_board_init(); #endif diff --git a/bsp/nuvoton/libraries/nuc980/rtt_port/drv_crypto.c b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_crypto.c index ddf8f00c3d..3b2963640d 100644 --- a/bsp/nuvoton/libraries/nuc980/rtt_port/drv_crypto.c +++ b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_crypto.c @@ -75,6 +75,7 @@ static rt_err_t nu_aes_crypt_run( { uint32_t au32SwapKey[8]; uint32_t au32SwapIV[4]; + rt_err_t result; au32SwapKey[0] = nu_get32_be(&pu8Key[0]); au32SwapKey[1] = nu_get32_be(&pu8Key[4]); @@ -98,7 +99,8 @@ static rt_err_t nu_aes_crypt_run( au32SwapIV[2] = nu_get32_be(&pu8IV[8]); au32SwapIV[3] = nu_get32_be(&pu8IV[12]); - rt_mutex_take(&s_AES_mutex, RT_WAITING_FOREVER); + result = rt_mutex_take(&s_AES_mutex, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); AES_Open(CRPT, bEncrypt, u32OpMode, u32KeySize, AES_IN_OUT_SWAP); AES_SetKey(CRPT, (uint32_t *)&au32SwapKey[0], u32KeySize); @@ -137,7 +139,8 @@ static rt_err_t nu_aes_crypt_run( /* Clear AES interrupt status */ AES_CLR_INT_FLAG(CRPT); - rt_mutex_release(&s_AES_mutex); + result = rt_mutex_release(&s_AES_mutex); + RT_ASSERT(result == RT_EOK); return RT_EOK; } @@ -145,19 +148,25 @@ static rt_err_t nu_aes_crypt_run( //Using PRNG instead of TRNG static void nu_prng_open(uint32_t u32Seed) { - rt_mutex_take(&s_PRNG_mutex, RT_WAITING_FOREVER); + rt_err_t result; + + result = rt_mutex_take(&s_PRNG_mutex, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); //Open PRNG 64 bits. But always return 32 bits PRNG_Open(CRPT, PRNG_KEY_SIZE_64, PRNG_SEED_RELOAD, u32Seed); - rt_mutex_release(&s_PRNG_mutex); + result = rt_mutex_release(&s_PRNG_mutex); + RT_ASSERT(result == RT_EOK); } static rt_uint32_t nu_prng_run(void) { uint32_t au32RNGValue[2]; + rt_err_t result; - rt_mutex_take(&s_PRNG_mutex, RT_WAITING_FOREVER); + result = rt_mutex_take(&s_PRNG_mutex, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); PRNG_Start(CRPT); while ((CRPT->PRNG_CTL & CRPT_PRNG_CTL_BUSY_Msk)) {}; @@ -167,7 +176,8 @@ static rt_uint32_t nu_prng_run(void) PRNG_Read(CRPT, &au32RNGValue[0]); - rt_mutex_release(&s_PRNG_mutex); + result = rt_mutex_release(&s_PRNG_mutex); + RT_ASSERT(result == RT_EOK); return au32RNGValue[0]; } @@ -340,10 +350,13 @@ static rt_err_t nu_sha_hash_run( uint32_t u32DataLen ) { + rt_err_t result; + RT_ASSERT(psSHACtx != RT_NULL); RT_ASSERT(pu8InData != RT_NULL); - rt_mutex_take(&s_SHA_mutex, RT_WAITING_FOREVER); + result = rt_mutex_take(&s_SHA_mutex, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); uint8_t *pu8SrcAddr = (uint8_t *)pu8InData; uint32_t u32CopyLen = 0; @@ -383,7 +396,8 @@ static rt_err_t nu_sha_hash_run( if (psSHACtx->pu8SHATempBuf == RT_NULL) { LOG_E("fun[%s] memory allocate %d bytes failed!", __FUNCTION__, psSHACtx->u32BlockSize); - rt_mutex_release(&s_SHA_mutex); + result = rt_mutex_release(&s_SHA_mutex); + RT_ASSERT(result == RT_EOK); return -RT_ENOMEM; } @@ -411,7 +425,8 @@ static rt_err_t nu_sha_hash_run( if (psSHACtx->pu8SHATempBuf == RT_NULL) { LOG_E("fun[%s] memory allocate %d bytes failed!", __FUNCTION__, psSHACtx->u32BlockSize); - rt_mutex_release(&s_SHA_mutex); + result = rt_mutex_release(&s_SHA_mutex); + RT_ASSERT(result == RT_EOK); return -RT_ENOMEM; } @@ -422,7 +437,8 @@ static rt_err_t nu_sha_hash_run( psSHACtx->u32SHATempBufLen += u32DataLen; } - rt_mutex_release(&s_SHA_mutex); + result = rt_mutex_release(&s_SHA_mutex); + RT_ASSERT(result == RT_EOK); return RT_EOK; } diff --git a/bsp/nuvoton/libraries/nuc980/rtt_port/drv_emac.c b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_emac.c index 771130a326..be0451ec5e 100644 --- a/bsp/nuvoton/libraries/nuc980/rtt_port/drv_emac.c +++ b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_emac.c @@ -232,7 +232,6 @@ static void link_monitor(void *param) nu_emac_t psNuEmac = (nu_emac_t)param; EMAC_T *EMAC = psNuEmac->memmgr.psEmac; uint32_t LinkStatus_Last = EMAC_LINK_DOWN; - rt_err_t result = RT_EOK; EMAC_PhyInit(EMAC); @@ -274,9 +273,6 @@ static void link_monitor(void *param) else { eth_device_linkchange(&psNuEmac->eth, RT_TRUE); - - result = rt_sem_release(&psNuEmac->eth_sem); - RT_ASSERT(result == RT_EOK); } LinkStatus_Last = LinkStatus_Current; @@ -317,7 +313,8 @@ static rt_err_t nu_emac_init(rt_device_t dev) snprintf(szTmp, sizeof(szTmp), "%sphy", psNuEmac->name); - rt_sem_init(&psNuEmac->eth_sem, "eth_sem", 0, RT_IPC_FLAG_FIFO); + ret = rt_sem_init(&psNuEmac->eth_sem, "eth_sem", 0, RT_IPC_FLAG_FIFO); + RT_ASSERT(ret == RT_EOK); EMAC_Reset(EMAC); @@ -402,26 +399,21 @@ static rt_err_t nu_emac_tx(rt_device_t dev, struct pbuf *p) rt_uint32_t offset = 0; rt_uint8_t *buf; - if (psNuEmac->eth.link_status == RT_FALSE) - { - rt_kprintf("[%s]Stand here.\n", psNuEmac->name); - while (rt_sem_take(&psNuEmac->eth_sem, RT_WAITING_FOREVER) != RT_EOK); - rt_kprintf("[%s]Leave.\n", psNuEmac->name); - } - buf = (rt_uint8_t *)EMAC_ClaimFreeTXBuf(&psNuEmac->memmgr); - /* Get free TX buffer */ if (buf == RT_NULL) { - rt_sem_control(&psNuEmac->eth_sem, RT_IPC_CMD_RESET, 0); + rt_err_t result; + + result = rt_sem_control(&psNuEmac->eth_sem, RT_IPC_CMD_RESET, 0); + RT_ASSERT(result != RT_EOK); EMAC_CLEAR_INT_FLAG(EMAC, EMAC_INTSTS_TXCPIF_Msk); EMAC_ENABLE_INT(EMAC, EMAC_INTEN_TXCPIEN_Msk); do { - rt_sem_take(&psNuEmac->eth_sem, 1); + result = rt_sem_take(&psNuEmac->eth_sem, 10); buf = (rt_uint8_t *)EMAC_ClaimFreeTXBuf(&psNuEmac->memmgr); } while (buf == RT_NULL); diff --git a/bsp/nuvoton/libraries/nuc980/rtt_port/drv_etimer_capture.c b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_etimer_capture.c new file mode 100644 index 0000000000..0e2ae9384e --- /dev/null +++ b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_etimer_capture.c @@ -0,0 +1,240 @@ +/**************************************************************************//** +* +* @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved. +* +* SPDX-License-Identifier: Apache-2.0 +* +* Change Logs: +* Date Author Notes +* 2021-1-28 Wayne First version +* +******************************************************************************/ + +#include + +#if defined(BSP_USING_TIMER_CAPTURE) + +#include +#include +#include + +/* Private define ---------------------------------------------------------------*/ + +/* Timer prescale setting. Since it will affect the pulse width of measure, it is recommended to set to 2. */ +#define PSC_DIV (2) + +#define NU_TCAP_DEVICE(etimer) (nu_capture_t*)(etimer) +enum +{ + TCAP_START = -1, +#if defined(BSP_USING_TIMER0_CAPTURE) + TCAP0_IDX, +#endif +#if defined(BSP_USING_TIMER1_CAPTURE) + TCAP1_IDX, +#endif +#if defined(BSP_USING_TIMER2_CAPTURE) + TCAP2_IDX, +#endif +#if defined(BSP_USING_TIMER3_CAPTURE) + TCAP3_IDX, +#endif +#if defined(BSP_USING_TIMER4_CAPTURE) + TCAP4_IDX, +#endif + /* BSP_USING_TIMER5 is reserved for Systick usage. */ + TCAP_CNT +}; + +/* Private typedef --------------------------------------------------------------*/ +typedef struct _timer +{ + struct rt_inputcapture_device parent; + char *name; + uint32_t idx; + IRQn_Type irqn; + E_SYS_IPRST rstidx; + E_SYS_IPCLK clkidx; + + uint32_t u32CurrentCnt; +} nu_capture_t; + +/* Private functions ------------------------------------------------------------*/ +static rt_err_t nu_capture_init(struct rt_inputcapture_device *inputcapture); +static rt_err_t nu_capture_open(struct rt_inputcapture_device *inputcapture); +static rt_err_t nu_capture_close(struct rt_inputcapture_device *inputcapture); +static rt_err_t nu_capture_get_pulsewidth(struct rt_inputcapture_device *inputcapture, rt_uint32_t *pulsewidth_us); + +/* Public functions -------------------------------------------------------------*/ + +/* Private variables ------------------------------------------------------------*/ +static nu_capture_t nu_etcap_arr [] = +{ +#if defined(BSP_USING_TIMER0_CAPTURE) + { + .name = "timer0i0", + .idx = 0, + .irqn = IRQ_TIMER0, + .rstidx = TIMER0RST, + .clkidx = TIMER0CKEN, + }, +#endif +#if defined(BSP_USING_TIMER1_CAPTURE) + { + .name = "timer1i0", + .idx = 1, + .irqn = IRQ_TIMER1, + .rstidx = TIMER1RST, + .clkidx = TIMER1CKEN, + }, +#endif +#if defined(BSP_USING_TIMER2_CAPTURE) + { + .name = "timer2i0", + .idx = 2, + .irqn = IRQ_TIMER2, + .rstidx = TIMER2RST, + .clkidx = TIMER2CKEN, + }, +#endif +#if defined(BSP_USING_TIMER3_CAPTURE) + { + .name = "timer3i0", + .idx = 3, + .irqn = IRQ_TIMER3, + .rstidx = TIMER3RST, + .clkidx = TIMER3CKEN, + }, +#endif +#if defined(BSP_USING_TIMER4_CAPTURE) + { + .name = "timer4i0", + .idx = 4, + .irqn = IRQ_TIMER4, + .rstidx = TIMER4RST, + .clkidx = TIMER4CKEN, + }, +#endif + /* BSP_USING_TIMER5 is reserved for Systick usage. */ +}; + +static struct rt_inputcapture_ops nu_capture_ops = +{ + .init = nu_capture_init, + .open = nu_capture_open, + .close = nu_capture_close, + .get_pulsewidth = nu_capture_get_pulsewidth, +}; + +/* Functions define ------------------------------------------------------------*/ +static void nu_tcap_isr(int vector, void *param) +{ + nu_capture_t *psNuTCap = NU_TCAP_DEVICE(param); + + RT_ASSERT(psNuTCap != RT_NULL); + + ETIMER_ClearCaptureIntFlag(psNuTCap->idx); + + /* Report caputring data and level. */ + psNuTCap->u32CurrentCnt = ETIMER_GetCaptureData(psNuTCap->idx); + rt_hw_inputcapture_isr(&psNuTCap->parent, ETIMER_GetCaptureFallingEdgeFlag(psNuTCap->idx)); +} + +static rt_err_t nu_capture_get_pulsewidth(struct rt_inputcapture_device *inputcapture, rt_uint32_t *pulsewidth_us) +{ + nu_capture_t *psNuTCap = NU_TCAP_DEVICE(inputcapture); + + RT_ASSERT(inputcapture != RT_NULL); + RT_ASSERT(pulsewidth_us != RT_NULL); + + *pulsewidth_us = psNuTCap->u32CurrentCnt / PSC_DIV; + + return RT_EOK; +} + +static rt_err_t nu_capture_init(struct rt_inputcapture_device *inputcapture) +{ + nu_capture_t *psNuTCap = NU_TCAP_DEVICE(inputcapture); + + RT_ASSERT(inputcapture != RT_NULL); + + nu_sys_ipclk_enable(psNuTCap->clkidx); + nu_sys_ip_reset(psNuTCap->rstidx); + + return RT_EOK; +} + +static uint8_t cal_time_prescale(nu_capture_t *psNuTCap) +{ + uint32_t u32Clk = ETIMER_GetModuleClock(psNuTCap->idx); + + /* 1 tick = 1/PSC_DIV us */ + return (u32Clk / 1000000 / PSC_DIV) - 1; +} + +static rt_err_t nu_capture_open(struct rt_inputcapture_device *inputcapture) +{ + nu_capture_t *psNuTCap = NU_TCAP_DEVICE(inputcapture); + + RT_ASSERT(inputcapture != RT_NULL); + + /* Enable Timer Interrupt */ + rt_hw_interrupt_umask(psNuTCap->irqn); + + /* Clear counter before openning. */ + ETIMER_ClearCounter(psNuTCap->idx); + + ETIMER_Open(psNuTCap->idx, ETIMER_CONTINUOUS_MODE, 1); + + ETIMER_SET_PRESCALE_VALUE(psNuTCap->idx, cal_time_prescale(psNuTCap)); + + ETIMER_SET_CMP_VALUE(psNuTCap->idx, 0xFFFFFF); + + ETIMER_EnableCapture(psNuTCap->idx, ETIMER_CAPTURE_COUNTER_RESET_MODE, ETIMER_CAPTURE_RISING_THEN_FALLING_EDGE); + + ETIMER_EnableCaptureInt(psNuTCap->idx); + + ETIMER_Start(psNuTCap->idx); + + return RT_EOK; +} + +static rt_err_t nu_capture_close(struct rt_inputcapture_device *inputcapture) +{ + nu_capture_t *psNuTCap = NU_TCAP_DEVICE(inputcapture); + + RT_ASSERT(inputcapture != RT_NULL); + + ETIMER_Stop(psNuTCap->idx); + + ETIMER_DisableCaptureInt(psNuTCap->idx); + + ETIMER_Close(psNuTCap->idx); + + rt_hw_interrupt_mask(psNuTCap->irqn); + + return RT_EOK; +} + +/* Init and register timer capture */ +static int nu_timer_capture_device_init(void) +{ + int i; + + for (i = (TCAP_START + 1); i < TCAP_CNT; i++) + { + /* Register operations */ + nu_etcap_arr[i].parent.ops = &nu_capture_ops; + + /* Install ISR */ + rt_hw_interrupt_install(nu_etcap_arr[i].irqn, nu_tcap_isr, &nu_etcap_arr[i], nu_etcap_arr[i].name); + + /* Register inputcapture device */ + rt_device_inputcapture_register(&nu_etcap_arr[i].parent, nu_etcap_arr[i].name, &nu_etcap_arr[i]); + } + + return 0; +} +INIT_DEVICE_EXPORT(nu_timer_capture_device_init); + +#endif //#if defined(BSP_USING_TIMER_CAPTURE) diff --git a/bsp/nuvoton/libraries/nuc980/rtt_port/drv_pdma.c b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_pdma.c index dc83fe5efe..979d2b1f01 100644 --- a/bsp/nuvoton/libraries/nuc980/rtt_port/drv_pdma.c +++ b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_pdma.c @@ -968,8 +968,12 @@ static void nu_pdma_memfun_actor_init(void) { nu_pdma_memfun_actor_maxnum = i; nu_pdma_memfun_actor_mask = ~(((1 << i) - 1)); + nu_pdma_memfun_actor_pool_sem = rt_sem_create("mempool_sem", nu_pdma_memfun_actor_maxnum, RT_IPC_FLAG_FIFO); + RT_ASSERT(nu_pdma_memfun_actor_pool_sem != RT_NULL); + nu_pdma_memfun_actor_pool_lock = rt_mutex_create("mempool_lock", RT_IPC_FLAG_FIFO); + RT_ASSERT(nu_pdma_memfun_actor_pool_lock != RT_NULL); } } @@ -987,13 +991,15 @@ static void nu_pdma_memfun_cb(void *pvUserData, uint32_t u32Events) static int nu_pdma_memfun_employ(void) { int idx = -1 ; - rt_err_t result = 0; + rt_err_t result = RT_EOK; /* Headhunter */ if (nu_pdma_memfun_actor_pool_sem && ((result = rt_sem_take(nu_pdma_memfun_actor_pool_sem, RT_WAITING_FOREVER)) == RT_EOK)) { - rt_mutex_take(nu_pdma_memfun_actor_pool_lock, RT_WAITING_FOREVER); + result = rt_mutex_take(nu_pdma_memfun_actor_pool_lock, RT_WAITING_FOREVER); + RT_ASSERT(result == RT_EOK); + /* Find the position of first '0' in nu_pdma_memfun_actor_mask. */ idx = nu_cto(nu_pdma_memfun_actor_mask); if (idx != 32) @@ -1004,9 +1010,9 @@ static int nu_pdma_memfun_employ(void) { idx = -1; } - rt_mutex_release(nu_pdma_memfun_actor_pool_lock); + result = rt_mutex_release(nu_pdma_memfun_actor_pool_lock); + RT_ASSERT(result == RT_EOK); } - RT_ASSERT(result == RT_EOK); return idx; } diff --git a/bsp/nuvoton/libraries/nuc980/rtt_port/drv_pwm.c b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_pwm.c index f39ec1c70b..9ebdfa518e 100644 --- a/bsp/nuvoton/libraries/nuc980/rtt_port/drv_pwm.c +++ b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_pwm.c @@ -301,42 +301,4 @@ int rt_hw_pwm_init(void) INIT_DEVICE_EXPORT(rt_hw_pwm_init); -#if defined(RT_USING_FINSH) - -#include - -#ifdef FINSH_USING_MSH - -static int pwm_get(int argc, char **argv) -{ - int result = 0; - struct rt_device_pwm *device = RT_NULL; - struct rt_pwm_configuration configuration = {0}; - - if (argc != 3) - { - rt_kprintf("Usage: pwm_get pwm1 1\n"); - result = -RT_ERROR; - goto _exit; - } - - device = (struct rt_device_pwm *)rt_device_find(argv[1]); - if (!device) - { - result = -RT_EIO; - goto _exit; - } - - configuration.channel = atoi(argv[2]); - result = rt_device_control(&device->parent, PWM_CMD_GET, &configuration); - -_exit: - return result; -} - -MSH_CMD_EXPORT(pwm_get, pwm_get pwm1 1); - -#endif /* FINSH_USING_MSH */ -#endif /* RT_USING_FINSH */ - #endif diff --git a/bsp/nuvoton/libraries/nuc980/rtt_port/drv_qspi.c b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_qspi.c index 96e6110057..64b5dc874c 100644 --- a/bsp/nuvoton/libraries/nuc980/rtt_port/drv_qspi.c +++ b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_qspi.c @@ -60,7 +60,7 @@ static struct nu_spi nu_qspi_arr [] = .rstidx = QSPI0RST, .clkidx = QSPI0CKEN, -#if defined(BSP_USING_QSPI_PDMA) +#if defined(BSP_USING_SPI_PDMA) #if defined(BSP_USING_QSPI0_PDMA) .pdma_perp_tx = PDMA_QSPI0_TX, .pdma_perp_rx = PDMA_QSPI0_RX, @@ -153,7 +153,6 @@ exit_nu_qspi_bus_configure: return -(ret); } -#if defined(RT_SFUD_USING_QSPI) static int nu_qspi_mode_config(struct nu_spi *qspi_bus, rt_uint8_t *tx, rt_uint8_t *rx, int qspi_lines) { QSPI_T *qspi_base = (QSPI_T *)qspi_bus->spi_base; @@ -197,16 +196,13 @@ static int nu_qspi_mode_config(struct nu_spi *qspi_bus, rt_uint8_t *tx, rt_uint8 } return qspi_lines; } -#endif static rt_uint32_t nu_qspi_bus_xfer(struct rt_spi_device *device, struct rt_spi_message *message) { struct nu_spi *qspi_bus; struct rt_qspi_configuration *qspi_configuration; -#if defined(RT_SFUD_USING_QSPI) struct rt_qspi_message *qspi_message; rt_uint8_t u8last = 1; -#endif rt_uint8_t bytes_per_word; QSPI_T *qspi_base; rt_uint32_t u32len = 0; @@ -232,7 +228,6 @@ static rt_uint32_t nu_qspi_bus_xfer(struct rt_spi_device *device, struct rt_spi_ } } -#if defined(RT_SFUD_USING_QSPI) qspi_message = (struct rt_qspi_message *)message; /* Command + Address + Dummy + Data */ @@ -248,7 +243,7 @@ static rt_uint32_t nu_qspi_bus_xfer(struct rt_spi_device *device, struct rt_spi_ } /* Address stage */ - if (qspi_message->address.size != 0) + if (qspi_message->address.size > 0) { rt_uint32_t u32ReversedAddr = 0; rt_uint32_t u32AddrNumOfByte = qspi_message->address.size / 8; @@ -278,8 +273,39 @@ static rt_uint32_t nu_qspi_bus_xfer(struct rt_spi_device *device, struct rt_spi_ 1); } + /* alternate_bytes stage */ + if ((qspi_message->alternate_bytes.size > 0) && (qspi_message->alternate_bytes.size <= 4)) + { + rt_uint32_t u32AlternateByte = 0; + rt_uint32_t u32NumOfByte = qspi_message->alternate_bytes.size / 8; + switch (u32NumOfByte) + { + case 1: + u32AlternateByte = (qspi_message->alternate_bytes.content & 0xff); + break; + case 2: + nu_set16_be((rt_uint8_t *)&u32AlternateByte, qspi_message->alternate_bytes.content); + break; + case 3: + nu_set24_be((rt_uint8_t *)&u32AlternateByte, qspi_message->alternate_bytes.content); + break; + case 4: + nu_set32_be((rt_uint8_t *)&u32AlternateByte, qspi_message->alternate_bytes.content); + break; + default: + RT_ASSERT(0); + break; + } + u8last = nu_qspi_mode_config(qspi_bus, (rt_uint8_t *)&u32AlternateByte, RT_NULL, qspi_message->alternate_bytes.qspi_lines); + nu_spi_transfer((struct nu_spi *)qspi_bus, + (rt_uint8_t *) &u32AlternateByte, + RT_NULL, + u32NumOfByte, + 1); + } + /* Dummy_cycles stage */ - if (qspi_message->dummy_cycles != 0) + if (qspi_message->dummy_cycles > 0) { qspi_bus->dummy = 0x00; @@ -291,12 +317,10 @@ static rt_uint32_t nu_qspi_bus_xfer(struct rt_spi_device *device, struct rt_spi_ 1); } - /* Data stage */ - nu_qspi_mode_config(qspi_bus, (rt_uint8_t *) message->send_buf, (rt_uint8_t *) message->recv_buf, qspi_message->qspi_data_lines); -#endif //#if defined(RT_SFUD_USING_QSPI) - - if (message->length != 0) + if (message->length > 0) { + /* Data stage */ + nu_qspi_mode_config(qspi_bus, (rt_uint8_t *) message->send_buf, (rt_uint8_t *) message->recv_buf, qspi_message->qspi_data_lines); nu_spi_transfer((struct nu_spi *)qspi_bus, (rt_uint8_t *) message->send_buf, (rt_uint8_t *) message->recv_buf, diff --git a/bsp/nuvoton/libraries/nuc980/rtt_port/drv_sdh.c b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_sdh.c index 2493e1ad71..85f4878518 100644 --- a/bsp/nuvoton/libraries/nuc980/rtt_port/drv_sdh.c +++ b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_sdh.c @@ -412,7 +412,8 @@ static int rt_hw_sdh_init(void) rt_err_t ret = RT_EOK; rt_uint32_t flags = RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE | RT_DEVICE_FLAG_STANDALONE; - rt_event_init(&sdh_event, "sdh_event", RT_IPC_FLAG_FIFO); + ret = rt_event_init(&sdh_event, "sdh_event", RT_IPC_FLAG_FIFO); + RT_ASSERT(ret == RT_EOK); for (i = (SDH_START + 1); i < SDH_CNT; i++) { @@ -428,7 +429,8 @@ static int rt_hw_sdh_init(void) /* Private */ nu_sdh_arr[i].dev.user_data = (void *)&nu_sdh_arr[i]; - rt_sem_init(&nu_sdh_arr[i].lock, "sdhlock", 1, RT_IPC_FLAG_FIFO); + ret = rt_sem_init(&nu_sdh_arr[i].lock, "sdhlock", 1, RT_IPC_FLAG_FIFO); + RT_ASSERT(ret == RT_EOK); rt_hw_interrupt_install(nu_sdh_arr[i].irqn, SDH_IRQHandler, (void *)&nu_sdh_arr[i], nu_sdh_arr[i].name); rt_hw_interrupt_umask(nu_sdh_arr[i].irqn); @@ -437,9 +439,8 @@ static int rt_hw_sdh_init(void) nu_sys_ip_reset(nu_sdh_arr[i].rstidx); - SDH_Open(nu_sdh_arr[i].base, CardDetect_From_GPIO); - nu_sdh_arr[i].pbuf = RT_NULL; + ret = rt_device_register(&nu_sdh_arr[i].dev, nu_sdh_arr[i].name, flags); RT_ASSERT(ret == RT_EOK); } @@ -591,7 +592,10 @@ static void sdh_hotplugger(void *param) for (i = (SDH_START + 1); i < SDH_CNT; i++) { - if (SDH_IS_CARD_PRESENT(nu_sdh_arr[i].base)) + /* Try to detect SD card on selected port. */ + SDH_Open(nu_sdh_arr[i].base, CardDetect_From_GPIO); + if (!SDH_Probe(nu_sdh_arr[i].base) && + SDH_IS_CARD_PRESENT(nu_sdh_arr[i].base)) { nu_sdh_hotplug_mount(&nu_sdh_arr[i]); } diff --git a/bsp/nuvoton/libraries/nuc980/rtt_port/drv_sys.c b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_sys.c index 1bf574d835..97b4fff76c 100644 --- a/bsp/nuvoton/libraries/nuc980/rtt_port/drv_sys.c +++ b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_sys.c @@ -287,6 +287,18 @@ void nu_sys_ipclk_disable(E_SYS_IPCLK eIPClkIdx) _nu_sys_ipclk(eIPClkIdx, 0); } +E_SYS_USB0_ID nu_sys_usb0_role(void) +{ + /* Check Role on USB0 dual-role port. */ + /* + [17] USB0_IDS + USB0_ID Status + 0 = USB port 0 used as a USB device port. + 1 = USB port 0 used as a USB host port. + */ + return ((inpw(REG_SYS_MISCISR) & (1 << 17)) > 0) ? USB0_ID_HOST : USB0_ID_DEVICE; +} + #ifdef RT_USING_FINSH #include @@ -308,6 +320,32 @@ int cmd_shutdown(int argc, char **argv) FINSH_FUNCTION_EXPORT_ALIAS(cmd_reset, __cmd_reset, restart the system.); FINSH_FUNCTION_EXPORT_ALIAS(cmd_shutdown, __cmd_shutdown, shutdown the system.); +int nu_clocks(int argc, char **argv) +{ + rt_kprintf("SYS_UPLL = %d MHz\n", sysGetClock(SYS_UPLL)); + rt_kprintf("SYS_APLL = %d MHz\n", sysGetClock(SYS_APLL)); + rt_kprintf("SYS_SYSTEM = %d MHz\n", sysGetClock(SYS_SYSTEM)); + rt_kprintf("SYS_HCLK = %d MHz\n", sysGetClock(SYS_HCLK)); + rt_kprintf("SYS_PCLK01 = %d MHz\n", sysGetClock(SYS_PCLK01)); + rt_kprintf("SYS_PCLK2 = %d MHz\n", sysGetClock(SYS_PCLK2)); + rt_kprintf("SYS_CPU = %d MHz\n", sysGetClock(SYS_CPU)); + + rt_kprintf("CLK_HCLKEN = %08X\n", inpw(REG_CLK_HCLKEN)); + rt_kprintf("CLK_PCLKEN0 = %08X\n", inpw(REG_CLK_PCLKEN0)); + rt_kprintf("CLK_PCLKEN1 = %08X\n", inpw(REG_CLK_PCLKEN1)); + + rt_kprintf("AIC_INTMSK0 = %08X\n", inpw(REG_AIC_INTMSK0)); + rt_kprintf("AIC_INTMSK1 = %08X\n", inpw(REG_AIC_INTMSK1)); + + rt_kprintf("AIC_INTEN0 = %08X\n", inpw(REG_AIC_INTEN0)); + rt_kprintf("AIC_INTEN1 = %08X\n", inpw(REG_AIC_INTEN1)); + + rt_kprintf("AIC_INTDIS0 = %08X\n", inpw(REG_AIC_INTDIS0)); + rt_kprintf("AIC_INTDIS1 = %08X\n", inpw(REG_AIC_INTDIS1)); + + return 0; +} +MSH_CMD_EXPORT(nu_clocks, Get all system clocks); #endif #endif diff --git a/bsp/nuvoton/libraries/nuc980/rtt_port/drv_sys.h b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_sys.h index f70797e188..5e9c707442 100644 --- a/bsp/nuvoton/libraries/nuc980/rtt_port/drv_sys.h +++ b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_sys.h @@ -243,6 +243,13 @@ typedef enum } E_SYS_IPCLK; +typedef enum +{ + USB0_ID_DEVICE, + USB0_ID_HOST, + USB0_ID_CNT +} E_SYS_USB0_ID; + void rt_hw_interrupt_init(void); void rt_hw_interrupt_set_priority(int vector, int priority); void rt_hw_interrupt_set_type(int vector, int type); @@ -255,5 +262,6 @@ void nu_systick_udelay(uint32_t delay_us); void nu_sys_ip_reset(E_SYS_IPRST eIPRstIdx); void nu_sys_ipclk_enable(E_SYS_IPCLK eIPClkIdx); void nu_sys_ipclk_disable(E_SYS_IPCLK eIPClkIdx); +E_SYS_USB0_ID nu_sys_usb0_role(void); #endif diff --git a/bsp/nuvoton/libraries/nuc980/rtt_port/drv_uart.c b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_uart.c index ba211aa746..c4b7f3a393 100644 --- a/bsp/nuvoton/libraries/nuc980/rtt_port/drv_uart.c +++ b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_uart.c @@ -572,7 +572,6 @@ static void nu_pdma_uart_rx_cb(void *pvOwner, uint32_t u32Events) struct rt_serial_device *serial = (struct rt_serial_device *)pvOwner; nu_uart_t puart = (nu_uart_t)serial; RT_ASSERT(serial != RT_NULL); - struct rt_serial_rx_fifo *rx_fifo = (struct rt_serial_rx_fifo *)serial->serial_rx; /* Get base address of uart register */ UART_T *uart_base = puart->uart_base; @@ -581,6 +580,9 @@ static void nu_pdma_uart_rx_cb(void *pvOwner, uint32_t u32Events) if (u32Events & (NU_PDMA_EVENT_TRANSFER_DONE | NU_PDMA_EVENT_TIMEOUT)) { +#if defined(BSP_USING_MMU) + struct rt_serial_rx_fifo *rx_fifo = (struct rt_serial_rx_fifo *)serial->serial_rx; +#endif if (u32Events & NU_PDMA_EVENT_TRANSFER_DONE) { transferred_rxbyte = puart->rxdma_trigger_len; diff --git a/bsp/nuvoton/libraries/nuc980/rtt_port/drv_usbd.c b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_usbd.c index e3ec1f6b58..2065ba35a1 100644 --- a/bsp/nuvoton/libraries/nuc980/rtt_port/drv_usbd.c +++ b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_usbd.c @@ -23,9 +23,9 @@ #include "drv_sys.h" #define LOG_TAG "drv.usbd" -#define DBG_ENABLE +//#define DBG_ENABLE #define DBG_SECTION_NAME "drv.usbd" -#define DBG_LEVEL DBG_INFO +//#define DBG_LEVEL DBG_ERROR #define DBG_COLOR #include @@ -124,7 +124,8 @@ typedef struct _nu_usbd_t IRQn_Type irqn; E_SYS_IPRST rstidx; E_SYS_IPCLK clkidx; - uint8_t address_tmp; /* Keep assigned address for flow control */ + uint8_t address_tmp; /* Keep assigned address for flow control */ + uint8_t plugging_status; /* For debounce, 0: Unplug, 1: plug-in */ } nu_usbd_t; @@ -149,6 +150,7 @@ static nu_usbd_t nu_usbd = .rstidx = USBDRST, .clkidx = USBDCKEN, .address_tmp = 0, + .plugging_status = 0, }; static struct udcd _rt_obj_udc; @@ -278,6 +280,27 @@ static void NU_SetupStageCallback(nu_usbd_t *nu_udc) rt_usbd_ep0_setup_handler(&_rt_obj_udc, (struct urequest *)&setup_packet); } +__STATIC_INLINE void nu_udc_enable(void) +{ + USBD_ENABLE_USB(); +} + +__STATIC_INLINE void nu_udc_disable(void) +{ + int i; + + USBD_ENABLE_CEP_INT(0); + USBD_CLR_CEP_INT_FLAG(0xffff); + + USBD_SET_CEP_STATE(inpw(REG_USBD_CEPCTL) | USB_CEPCTL_FLUSH); + + for (i = 0; i < USBD_MAX_EP; i++) + USBD->EP[i].EPRSPCTL = USB_EP_RSPCTL_FLUSH | USB_EP_RSPCTL_TOGGLE; + + USBD_DISABLE_USB(); +} + + static rt_err_t _ep_set_stall(rt_uint8_t address) { @@ -543,6 +566,9 @@ static void nu_usbd_isr(int vector, void *param) int i; int IrqStAllEP; + /* Igrone event if role is USBH*/ + if (nu_sys_usb0_role() != USB0_ID_DEVICE) return; + IrqStL = USBD->GINTSTS & USBD->GINTEN; /* get interrupt status */ if (!IrqStL) return; @@ -635,17 +661,25 @@ static void nu_usbd_isr(int vector, void *param) { if (USBD_IS_ATTACHED()) { - LOG_I("PLUG IN"); - /* USB Plug In */ - USBD_ENABLE_USB(); - rt_usbd_connect_handler(&_rt_obj_udc); + if (!nu_usbd.plugging_status) + { + LOG_I("PLUG IN"); + /* USB Plug In */ + nu_udc_enable(); + rt_usbd_connect_handler(&_rt_obj_udc); + nu_usbd.plugging_status = 1; + } } else { - LOG_I("Un-Plug"); - /* USB Un-plug */ - USBD_DISABLE_USB(); - rt_usbd_disconnect_handler(&_rt_obj_udc); + if (nu_usbd.plugging_status) + { + LOG_I("Un-Plug"); + /* USB Un-plug */ + nu_udc_disable(); + rt_usbd_disconnect_handler(&_rt_obj_udc); + nu_usbd.plugging_status = 0; + } } USBD_CLR_BUS_INT_FLAG(USBD_BUSINTSTS_VBUSDETIF_Msk); } @@ -791,29 +825,15 @@ static void nu_usbd_isr(int vector, void *param) static rt_err_t _init(rt_device_t device) { -#if !defined(BSP_USING_OTG) - uint32_t volatile i; - - /* Initialize USB PHY */ - SYS_UnlockReg(); - SYS->USBPHY &= ~SYS_USBPHY_HSUSBROLE_Msk; /* select USBD */ - - /* Enable USB PHY */ - SYS->USBPHY = (SYS->USBPHY & ~(SYS_USBPHY_HSUSBROLE_Msk | SYS_USBPHY_HSUSBACT_Msk)) | SYS_USBPHY_HSUSBEN_Msk; - - for (i = 0; i < 0x1000; i++) - __NOP(); // delay > 10 us - - SYS->USBPHY |= SYS_USBPHY_HSUSBACT_Msk; - SYS_LockReg(); -#endif - nu_sys_ipclk_enable(nu_usbd.clkidx); nu_sys_ip_reset(nu_usbd.rstidx); + nu_systick_udelay(1000); + /* USBD Open */ - USBD->PHYCTL |= (USBD_PHYCTL_PHYEN_Msk | USBD_PHYCTL_DPPUEN_Msk); + USBD_ENABLE_USB(); + while (1) { USBD->EP[EPA].EPMPS = 0x20ul; @@ -842,7 +862,11 @@ static rt_err_t _init(rt_device_t device) rt_hw_interrupt_umask(nu_usbd.irqn); /* Start transaction */ - USBD_Start(); + USBD_CLR_SE0(); + + /* Get currect cable status */ + nu_usbd.plugging_status = USBD_IS_ATTACHED() >> USBD_PHYCTL_VBUSDET_Pos; + return RT_EOK; } diff --git a/bsp/nuvoton/libraries/nuc980/rtt_port/drv_usbhost.c b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_usbhost.c new file mode 100644 index 0000000000..0fbb6d24c8 --- /dev/null +++ b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_usbhost.c @@ -0,0 +1,893 @@ +/**************************************************************************//** +* +* @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved. +* +* SPDX-License-Identifier: Apache-2.0 +* +* Change Logs: +* Date Author Notes +* 2020-12-30 Wayne First version +* +******************************************************************************/ +#include + +#if defined(BSP_USING_USBH) + +#include +#include +#include + +#include "usb.h" +#include "usbh_lib.h" + +#if !defined(NU_USBHOST_HUB_POLLING_INTERVAL) + #define NU_USBHOST_HUB_POLLING_INTERVAL (100) +#endif + +#define NU_MAX_USBH_PORT 2 //2* USB2.0 port +#define NU_MAX_USBH_PIPE 16 +#define NU_USBH_THREAD_STACK_SIZE 2048 + +#define NU_MAX_USBH_HUB_PORT_DEV USB_HUB_PORT_NUM + +/* Private typedef --------------------------------------------------------------*/ +typedef struct nu_port_dev +{ + rt_bool_t bRHParent; + UDEV_T *pUDev; + EP_INFO_T *apsEPInfo[NU_MAX_USBH_PIPE]; + struct urequest asSetupReq[NU_MAX_USBH_PIPE]; + struct rt_completion utr_completion; + int port_num; + rt_bool_t bEnumDone; +#if defined(BSP_USING_MMU) + void *asPipePktBuf[NU_MAX_USBH_PIPE]; +#endif +} S_NU_PORT_DEV; + + +typedef struct nu_port_ctrl +{ + S_NU_PORT_DEV sRHPortDev; + S_NU_PORT_DEV asHubPortDev[NU_MAX_USBH_HUB_PORT_DEV]; +} S_NU_RH_PORT_CTRL; + + +struct nu_usbh_dev +{ + struct uhcd uhcd; + E_SYS_IPRST rstidx; + E_SYS_IPCLK clkidx; + rt_thread_t polling_thread; + S_NU_RH_PORT_CTRL asPortCtrl[NU_MAX_USBH_PORT]; +}; + +/* Private variables ------------------------------------------------------------*/ +static struct nu_usbh_dev s_sUSBHDev = +{ + .rstidx = USBHRST, + .clkidx = USBHCKEN, +}; + +static S_NU_RH_PORT_CTRL * +GetRHPortControlFromPipe( + upipe_t pipe) +{ + uinst_t inst; + int port; + if (pipe->inst->parent_hub->is_roothub) + { + //case: device ---> root hub + inst = pipe->inst; + port = inst->port; + } + else + { + //case: device ---> hub ---> root hub + inst = pipe->inst->parent_hub->self; + port = inst->port; + } + + if (port > NU_MAX_USBH_PORT) + { + RT_DEBUG_LOG(RT_DEBUG_USB, ("nu_open_pipe ERROR: port index over NU_MAX_USBH_PORT\n")); + return RT_NULL; + } + + return &s_sUSBHDev.asPortCtrl[port - 1];; +} + +static S_NU_PORT_DEV * +GetPortDevFromPipe( + upipe_t pipe) +{ + S_NU_RH_PORT_CTRL *psRHPortCtrl = GetRHPortControlFromPipe(pipe); + int i; + + if (psRHPortCtrl == RT_NULL) + return RT_NULL; + + if (pipe->inst->parent_hub->is_roothub) + { + //case: device ---> root hub + return &psRHPortCtrl->sRHPortDev; + } + + //case: device ---> hub ---> root hub + for (i = 0 ; i < NU_MAX_USBH_HUB_PORT_DEV; i ++) + { + if (psRHPortCtrl->asHubPortDev[i].port_num == pipe->inst->port) + break; + } + + if (i >= NU_MAX_USBH_HUB_PORT_DEV) + return RT_NULL; + + return &psRHPortCtrl->asHubPortDev[i]; +} + +static rt_err_t nu_reset_port(rt_uint8_t port) +{ + S_NU_RH_PORT_CTRL *psPortCtrl; + + if (port > NU_MAX_USBH_PORT) + { + RT_DEBUG_LOG(RT_DEBUG_USB, ("%s ERROR: port index over NU_MAX_USBH_PORT\n", __func__)); + return RT_EIO; + } + + psPortCtrl = &s_sUSBHDev.asPortCtrl[port - 1]; + if (psPortCtrl->sRHPortDev.pUDev == NULL) + { + RT_DEBUG_LOG(RT_DEBUG_USB, ("%s ERROR: udev not found\n", __func__)); + return RT_EIO; + } + + usbh_reset_port(psPortCtrl->sRHPortDev.pUDev); + + return RT_EOK; +} + +static EP_INFO_T *GetFreePipe( + S_NU_RH_PORT_CTRL *psPortCtrl, + S_NU_PORT_DEV *psPortDev, + rt_uint8_t *pu8PipeIndex) +{ + if (psPortCtrl != NULL) + { + int i; + /* Find free Pipe */ + for (i = 0; i < NU_MAX_USBH_PIPE; i ++) + { + if (psPortDev->apsEPInfo[i] == NULL) + break; + } + + if (i < NU_MAX_USBH_PIPE) + { + EP_INFO_T *psEPInfo = rt_malloc(sizeof(EP_INFO_T)); + if (psEPInfo != RT_NULL) + { + psPortDev->apsEPInfo[i] = psEPInfo; + *pu8PipeIndex = i; + return psEPInfo; + } + } + } + return RT_NULL; +} + +static void FreePipe( + S_NU_RH_PORT_CTRL *psPortCtrl, + S_NU_PORT_DEV *psPortDev, + rt_uint8_t u8PipeIndex) +{ + if ((psPortCtrl != RT_NULL) && + (u8PipeIndex < NU_MAX_USBH_PIPE) && + (psPortDev->apsEPInfo[u8PipeIndex] != RT_NULL)) + { + rt_free(psPortDev->apsEPInfo[u8PipeIndex]); + psPortDev->apsEPInfo[u8PipeIndex] = RT_NULL; + } +} + +static S_NU_PORT_DEV * +AllocateNewUDev( + S_NU_RH_PORT_CTRL *psRHPortCtrl) +{ + if (psRHPortCtrl != RT_NULL) + { + int i; + /* Find free Dev */ + for (i = 0 ; i < NU_MAX_USBH_HUB_PORT_DEV; i ++) + { + if (psRHPortCtrl->asHubPortDev[i].pUDev == NULL) + break; + } + + if (i < NU_MAX_USBH_HUB_PORT_DEV) + { + psRHPortCtrl->asHubPortDev[i].pUDev = alloc_device(); + if (psRHPortCtrl->asHubPortDev[i].pUDev == NULL) + { + return RT_NULL; + } + else + { + return &psRHPortCtrl->asHubPortDev[i]; + } + } + } + return RT_NULL; +} + +static rt_err_t nu_open_pipe(upipe_t pipe) +{ + S_NU_RH_PORT_CTRL *psPortCtrl; + S_NU_PORT_DEV *psPortDev; + + psPortCtrl = GetRHPortControlFromPipe(pipe); + if (psPortCtrl == RT_NULL) + { + RT_DEBUG_LOG(RT_DEBUG_USB, ("%s ERROR: RHPort not found\n", __func__)); + goto exit_nu_open_pipe; + } + + if (psPortCtrl->sRHPortDev.pUDev == NULL) + { + RT_DEBUG_LOG(RT_DEBUG_USB, ("%s ERROR: udev not found\n", __func__)); + goto exit_nu_open_pipe; + } + + psPortDev = GetPortDevFromPipe(pipe); + + if ((psPortDev == NULL) || (psPortDev->pUDev == NULL)) + { + //allocate new dev for hub device + psPortDev = AllocateNewUDev(psPortCtrl); + + if (psPortDev == RT_NULL) + { + RT_DEBUG_LOG(RT_DEBUG_USB, ("nu_open_pipe ERROR: udev allocate failed\n")); + goto exit_nu_open_pipe; + } + + if (pipe->inst->speed) + { + psPortDev->pUDev->speed = SPEED_FULL; + } + else + { + psPortDev->pUDev->speed = SPEED_HIGH; + } + + psPortDev->pUDev->parent = NULL; + psPortDev->pUDev->hc_driver = psPortCtrl->sRHPortDev.pUDev->hc_driver; + psPortDev->port_num = pipe->inst->port; + psPortDev->pUDev->port_num = pipe->inst->port; + psPortDev->bEnumDone = FALSE; + } + + //For ep0 control transfer + if ((pipe->ep.bEndpointAddress & 0x7F) == 0) + { + pipe->pipe_index = 0; + } + else + { + int pksz; + EP_INFO_T *psEPInfo = GetFreePipe(psPortCtrl, psPortDev, &pipe->pipe_index); + if (psEPInfo == RT_NULL) + { + RT_DEBUG_LOG(RT_DEBUG_USB, ("%s ERROR: get free pipe failed\n", __func__)); + goto exit_nu_open_pipe; + } + + psEPInfo->bEndpointAddress = pipe->ep.bEndpointAddress; + psEPInfo->bmAttributes = pipe->ep.bmAttributes; + + pksz = pipe->ep.wMaxPacketSize; + pksz = (pksz & 0x07ff) * (1 + ((pksz >> 11) & 3)); + psEPInfo->wMaxPacketSize = pksz; + + psEPInfo->bInterval = pipe->ep.bInterval; + psEPInfo->hw_pipe = NULL; + psEPInfo->bToggle = 0; + } + +#if defined(BSP_USING_MMU) + if (!psPortDev->asPipePktBuf[pipe->pipe_index]) + { + psPortDev->asPipePktBuf[pipe->pipe_index] = rt_malloc_align(512ul, CACHE_LINE_SIZE); + RT_ASSERT(psPortDev->asPipePktBuf[pipe->pipe_index] != RT_NULL); + } +#endif + + return RT_EOK; + +exit_nu_open_pipe: + + return -RT_ERROR; +} + +static rt_err_t nu_close_pipe(upipe_t pipe) +{ + S_NU_RH_PORT_CTRL *psPortCtrl; + S_NU_PORT_DEV *psPortDev; + + psPortCtrl = GetRHPortControlFromPipe(pipe); + if (psPortCtrl == RT_NULL) + { + return RT_EIO; + } + + psPortDev = GetPortDevFromPipe(pipe); + + //For ep0 control transfer + if ((pipe->ep.bEndpointAddress & 0x7F) == 0) + { + if ((psPortDev) && (psPortDev->bRHParent == FALSE) && (psPortDev->bEnumDone == TRUE)) + { + if (psPortDev->pUDev) + { + int i; + for (i = 0; i < NU_MAX_USBH_PIPE; i++) + { + if (psPortDev->apsEPInfo[i] != NULL) + { + usbh_quit_xfer(psPortDev->pUDev, psPortDev->apsEPInfo[i]); + } + } + + free_device(psPortDev->pUDev); + psPortDev->pUDev = NULL; + } + } + } + + if (psPortDev != NULL) + { +#if defined(BSP_USING_MMU) + if (psPortDev->asPipePktBuf[pipe->pipe_index]) + { + rt_free_align(psPortDev->asPipePktBuf[pipe->pipe_index]); + psPortDev->asPipePktBuf[pipe->pipe_index] = RT_NULL; + } +#endif + + FreePipe(psPortCtrl, psPortDev, pipe->pipe_index); + } + return RT_EOK; +} + +static int nu_ctrl_xfer( + S_NU_PORT_DEV *psPortDev, + struct urequest *psSetup, + void *buffer, + int timeouts) +{ + uint32_t xfer_len = 0; + int ret; + + ret = usbh_ctrl_xfer(psPortDev->pUDev, psSetup->request_type, psSetup->bRequest, psSetup->wValue, psSetup->wIndex, psSetup->wLength, buffer, &xfer_len, timeouts * 10); + if (ret < 0) + { + RT_DEBUG_LOG(RT_DEBUG_USB, ("nu_ctrl_xfer ERROR: xfer failed %d\n", ret)); + return ret; + } + + if (xfer_len != psSetup->wLength) + { + RT_DEBUG_LOG(RT_DEBUG_USB, ("nu_ctrl_xfer ERROR: xfer length %d %d\n", psSetup->wLength, xfer_len)); + } + + if ((psSetup->bRequest == USB_REQ_SET_ADDRESS) && ((psSetup->request_type & 0x60) == REQ_TYPE_STD_DEV)) + psPortDev->pUDev->dev_num = psSetup->wValue; + + if ((psSetup->bRequest == USB_REQ_SET_CONFIGURATION) && ((psSetup->request_type & 0x60) == REQ_TYPE_STD_DEV)) + { + psPortDev->pUDev->cur_conf = psSetup->wValue; + psPortDev->bEnumDone = TRUE; + } + + return xfer_len; +} + +static int nu_bulk_xfer( + S_NU_PORT_DEV *psPortDev, + UTR_T *psUTR, + int timeouts) +{ + int ret; + + ret = usbh_bulk_xfer(psUTR); + + if (ret < 0) + return ret; + + //wait transfer done + rt_completion_wait(&(psPortDev->utr_completion), timeouts); + return 0; +} + +static int nu_int_xfer( + upipe_t pipe, + S_NU_PORT_DEV *psPortDev, + UTR_T *psUTR, + int timeouts) +{ + int ret; + int retry = 3; + + while (retry > 0) + { + ret = usbh_int_xfer(psUTR); + if (ret == 0) + break; + + RT_DEBUG_LOG(RT_DEBUG_USB, ("nu_int_xfer ERROR: failed to submit interrupt request\n")); + rt_thread_delay((pipe->ep.bInterval * RT_TICK_PER_SECOND / 1000) > 0 ? (pipe->ep.bInterval * RT_TICK_PER_SECOND / 1000) : 1); + retry --; + } + + if (ret < 0) + return ret; + + return 0; +} + +static void xfer_done_cb(UTR_T *psUTR) +{ + S_NU_PORT_DEV *psPortDev = (S_NU_PORT_DEV *)psUTR->context; + + //transfer done, signal utr_completion + rt_completion_done(&(psPortDev->utr_completion)); +} + +static void int_xfer_done_cb(UTR_T *psUTR) +{ + upipe_t pipe = (upipe_t)psUTR->context; + + if (psUTR->status != 0) + { + RT_DEBUG_LOG(RT_DEBUG_USB, ("Interrupt xfer failed %d\n", psUTR->status)); + goto exit_int_xfer_done_cb; + } + + if (pipe->callback != RT_NULL) + { + struct uhost_msg msg; + msg.type = USB_MSG_CALLBACK; + msg.content.cb.function = pipe->callback; + msg.content.cb.context = pipe; + rt_usbh_event_signal(&msg); + } + +exit_int_xfer_done_cb: + + free_utr(psUTR); +} + +static int nu_pipe_xfer(upipe_t pipe, rt_uint8_t token, void *buffer, int nbytes, int timeouts) +{ + S_NU_RH_PORT_CTRL *psPortCtrl; + S_NU_PORT_DEV *psPortDev; + UTR_T *psUTR = NULL; + int i32XferLen = -1; + + void *buffer_nonch = buffer; + + psPortCtrl = GetRHPortControlFromPipe(pipe); + if (psPortCtrl == RT_NULL) + { + goto exit_nu_pipe_xfer; + } + + psPortDev = GetPortDevFromPipe(pipe); + if (psPortDev->pUDev == NULL) + { + RT_DEBUG_LOG(RT_DEBUG_USB, ("nu_pipe_xfer ERROR: udev not found\n")); + goto exit_nu_pipe_xfer; + } + +#if defined(BSP_USING_MMU) + if (buffer_nonch && nbytes) + { + buffer_nonch = psPortDev->asPipePktBuf[pipe->pipe_index]; + rt_memcpy(buffer_nonch, buffer, nbytes); + mmu_clean_invalidated_dcache((uint32_t)buffer_nonch, nbytes); + } +#endif + + //ctrl xfer + if (pipe->ep.bmAttributes == USB_EP_ATTR_CONTROL) + { + int ret; + if (token == USBH_PID_SETUP) + { + struct urequest *psSetup = (struct urequest *)buffer_nonch; + RT_ASSERT(buffer_nonch != RT_NULL); + + /* Read data from USB device. */ + if (psSetup->request_type & USB_REQ_TYPE_DIR_IN) + { + //Store setup request + rt_memcpy(&psPortCtrl->asHubPortDev->asSetupReq[pipe->pipe_index], psSetup, sizeof(struct urequest)); + } + else + { + /* Write data to USB device. */ + //Trigger USBHostLib Ctril_Xfer + ret = nu_ctrl_xfer(psPortDev, psSetup, NULL, timeouts); + if (ret != psSetup->wLength) + goto exit_nu_pipe_xfer; + } + } + else + { + //token == USBH_PID_DATA + if (buffer_nonch && ((pipe->ep.bEndpointAddress & USB_DIR_MASK) == USB_DIR_IN)) + { + /* Read data from USB device. */ + //Trigger USBHostLib Ctril_Xfer + ret = nu_ctrl_xfer(psPortDev, &psPortCtrl->asHubPortDev->asSetupReq[pipe->pipe_index], buffer_nonch, timeouts); + if (ret != nbytes) + goto exit_nu_pipe_xfer; + } + else + { + RT_DEBUG_LOG(RT_DEBUG_USB, ("%d == USBH_PID_DATA, nil buf-%d \n", token, nbytes)); + } + + } //else + i32XferLen = nbytes; + goto exit_nu_pipe_xfer; + } // if ( pipe->ep.bmAttributes == USB_EP_ATTR_CONTROL ) + else + { + + psUTR = alloc_utr(psPortDev->pUDev); + + if (!psUTR) + { + RT_DEBUG_LOG(RT_DEBUG_USB, ("nu_pipe_xfer ERROR: unable alloc UTR\n")); + goto exit_nu_pipe_xfer; + } + + psUTR->ep = psPortDev->apsEPInfo[pipe->pipe_index]; + psUTR->buff = buffer_nonch; + psUTR->data_len = nbytes; + psUTR->xfer_len = 0; + psUTR->func = xfer_done_cb; + psUTR->context = psPortDev; + psUTR->bIsTransferDone = 0; + psUTR->status = 0; + + //others xfer + rt_completion_init(&(psPortDev->utr_completion)); + + if (pipe->ep.bmAttributes == USB_EP_ATTR_BULK) + { + if (nu_bulk_xfer(psPortDev, psUTR, timeouts) < 0) + { + RT_DEBUG_LOG(RT_DEBUG_USB, ("nu_pipe_xfer ERROR: bulk transfer failed\n")); + goto exit_nu_pipe_xfer; + } + } + else if (pipe->ep.bmAttributes == USB_EP_ATTR_INT) + { + psUTR->func = int_xfer_done_cb; + psUTR->context = pipe; + + if (nu_int_xfer(pipe, psPortDev, psUTR, timeouts) < 0) + { + RT_DEBUG_LOG(RT_DEBUG_USB, ("nu_pipe_xfer ERROR: int transfer failed\n")); + //goto exit_nu_pipe_xfer; + } + else + { + i32XferLen = nbytes; + } + return i32XferLen; + } + else if (pipe->ep.bmAttributes == USB_EP_ATTR_ISOC) + { + //TODO: ISO transfer + RT_DEBUG_LOG(RT_DEBUG_USB, ("nu_pipe_xfer ERROR: isoc transfer not support\n")); + goto exit_nu_pipe_xfer; + } + + } //else + + if (psUTR->bIsTransferDone == 0) + { + //Timeout + RT_DEBUG_LOG(RT_DEBUG_USB, ("nu_pipe_xfer ERROR: timeout\n")); + pipe->status = UPIPE_STATUS_ERROR; + usbh_quit_utr(psUTR); + } + else + { + // Transfer Done. Get status + if (psUTR->status == 0) + { + pipe->status = UPIPE_STATUS_OK; + } + else if (psUTR->status == USBH_ERR_STALL) + { + pipe->status = UPIPE_STATUS_STALL; + } + else + { + pipe->status = UPIPE_STATUS_ERROR; + } + } + + i32XferLen = psUTR->xfer_len; + + //Call callback + if (pipe->callback != RT_NULL) + { + struct uhost_msg msg; + msg.type = USB_MSG_CALLBACK; + msg.content.cb.function = pipe->callback; + msg.content.cb.context = pipe->user_data; + rt_usbh_event_signal(&msg); + } + + if (pipe->status != UPIPE_STATUS_OK) + goto exit_nu_pipe_xfer; + +exit_nu_pipe_xfer: + +#if defined(BSP_USING_MMU) + if ((nbytes) && + (buffer_nonch != buffer)) + { + mmu_invalidate_dcache((uint32_t)buffer_nonch, nbytes); + rt_memcpy(buffer, buffer_nonch, nbytes); + } +#endif + + if (psUTR) + free_utr(psUTR); + + return i32XferLen; +} + +/* Polling USB root hub status task */ +static void nu_usbh_rh_thread_entry(void *parameter) +{ + while (1) + { + usbh_polling_root_hubs(); + rt_thread_mdelay(NU_USBHOST_HUB_POLLING_INTERVAL); + } +} + +static void nu_hcd_connect_callback( + struct udev_t *udev, + int param) +{ + int i; + int port_index; + S_NU_RH_PORT_CTRL *psPortCtrl; + + /* Igrone event if role is USBD*/ + if (nu_sys_usb0_role() == USB0_ID_HOST) + { + SYS_UnlockReg(); + outpw(REG_SYS_MISCFCR, (inpw(REG_SYS_MISCFCR) | (1 << 11))); /* Set USRHDSEN as 1; USB host/device role selection decided by USBID (SYS_PWRON[16]) */ + outpw(REG_SYS_PWRON, (inpw(REG_SYS_PWRON) | (1 << 16))); /* Set USB port 0 used for Host */ + SYS_LockReg(); + } + + for (i = 0; i < NU_MAX_USBH_PORT; i++) + { + psPortCtrl = &s_sUSBHDev.asPortCtrl[i]; + if (psPortCtrl->sRHPortDev.pUDev == NULL) + break; + } + + if (i >= NU_MAX_USBH_PORT) + { + RT_DEBUG_LOG(RT_DEBUG_USB, ("ERROR: port connect slot is full\n")); + return; + } + + port_index = i + 1; + psPortCtrl->sRHPortDev.pUDev = udev; + psPortCtrl->sRHPortDev.bRHParent = TRUE; + + RT_DEBUG_LOG(RT_DEBUG_USB, ("usb connected\n")); + + if (udev->speed == SPEED_HIGH) + rt_usbh_root_hub_connect_handler(&s_sUSBHDev.uhcd, port_index, RT_TRUE); + else + rt_usbh_root_hub_connect_handler(&s_sUSBHDev.uhcd, port_index, RT_FALSE); +} + +static void nu_hcd_disconnect_callback( + struct udev_t *udev, + int param) +{ + int i; + int port_index; + S_NU_RH_PORT_CTRL *psPortCtrl; + + for (i = 0; i < NU_MAX_USBH_PORT; i++) + { + psPortCtrl = &s_sUSBHDev.asPortCtrl[i]; + if (psPortCtrl->sRHPortDev.pUDev == udev) + break; + } + + if (i >= NU_MAX_USBH_PORT) + { + RT_DEBUG_LOG(RT_DEBUG_USB, ("ERROR: udev not found\n")); + return; + } + + port_index = i + 1; + + for (i = 0; i < NU_MAX_USBH_PIPE; i++) + { + if (psPortCtrl->sRHPortDev.apsEPInfo[i] != NULL) + { + usbh_quit_xfer(psPortCtrl->sRHPortDev.pUDev, psPortCtrl->sRHPortDev.apsEPInfo[i]); + } + } + + psPortCtrl->sRHPortDev.pUDev = NULL; + + RT_DEBUG_LOG(RT_DEBUG_USB, ("usb disconnect\n")); + + rt_usbh_root_hub_disconnect_handler(&s_sUSBHDev.uhcd, port_index); + + if (nu_sys_usb0_role() != USB0_ID_HOST) + { + SYS_UnlockReg(); + outpw(REG_SYS_MISCFCR, (inpw(REG_SYS_MISCFCR) & (~(1 << 11)))); + outpw(REG_SYS_PWRON, (inpw(REG_SYS_PWRON) & (~(1 << 16)))); + SYS_LockReg(); + } + +} + + +/* USB host operations -----------------------------------------------------------*/ +static struct uhcd_ops nu_uhcd_ops = +{ + nu_reset_port, + nu_pipe_xfer, + nu_open_pipe, + nu_close_pipe, +}; + +static rt_err_t nu_hcd_init(rt_device_t device) +{ + struct nu_usbh_dev *pNuUSBHDev = (struct nu_usbh_dev *)device; + usbh_core_init(); + + //install connect/disconnect callback + usbh_install_conn_callback(nu_hcd_connect_callback, nu_hcd_disconnect_callback); + usbh_polling_root_hubs(); + + //create thread for polling usbh port status + /* create usb hub thread */ + pNuUSBHDev->polling_thread = rt_thread_create("usbh_drv", nu_usbh_rh_thread_entry, RT_NULL, + NU_USBH_THREAD_STACK_SIZE, 8, 20); + RT_ASSERT(pNuUSBHDev->polling_thread != RT_NULL); + + /* startup usb host thread */ + rt_thread_startup(pNuUSBHDev->polling_thread); + + return RT_EOK; +} + +/* global function for USB host library -----------------------------*/ +uint32_t usbh_get_ticks(void) +{ + return rt_tick_get(); +} + +void usbh_delay_ms(int msec) +{ + rt_thread_mdelay(msec); +} + +uint32_t usbh_tick_from_millisecond(uint32_t msec) +{ + return rt_tick_from_millisecond(msec); +} + +#if defined(RT_USING_PM) + +/* device pm suspend() entry. */ +static int usbhost_pm_suspend(const struct rt_device *device, rt_uint8_t mode) +{ + rt_err_t result; + + struct nu_usbh_dev *pNuUSBHDev = (struct nu_usbh_dev *)device; + + RT_ASSERT(pNuUSBHDev != RT_NULL); + switch (mode) + { + case PM_SLEEP_MODE_LIGHT: + case PM_SLEEP_MODE_DEEP: + + pNuUSBHDev->polling_thread->stat = RT_THREAD_READY; + result = rt_thread_suspend(pNuUSBHDev->polling_thread); + RT_ASSERT(result == RT_EOK); + + break; + + default: + break; + } + + return (int)RT_EOK; +} + +/* device pm resume() entry. */ +static void usbhost_pm_resume(const struct rt_device *device, rt_uint8_t mode) +{ + rt_err_t result; + struct nu_usbh_dev *pNuUSBHDev = (struct nu_usbh_dev *)device; + RT_ASSERT(pNuUSBHDev != RT_NULL); + + switch (mode) + { + case PM_SLEEP_MODE_LIGHT: + case PM_SLEEP_MODE_DEEP: + result = rt_thread_resume(pNuUSBHDev->polling_thread); + RT_ASSERT(result == RT_EOK); + break; + + default: + break; + } +} + +static struct rt_device_pm_ops device_pm_ops = +{ + .suspend = usbhost_pm_suspend, + .resume = usbhost_pm_resume, + .frequency_change = RT_NULL +}; +#endif + +int nu_usbh_register(void) +{ + rt_err_t res; + uhcd_t psUHCD; + + psUHCD = (uhcd_t)&s_sUSBHDev.uhcd; + + psUHCD->parent.type = RT_Device_Class_USBHost; + psUHCD->parent.init = nu_hcd_init; + psUHCD->parent.user_data = &s_sUSBHDev; + + psUHCD->ops = &nu_uhcd_ops; + psUHCD->num_ports = NU_MAX_USBH_PORT; + + res = rt_device_register(&psUHCD->parent, "usbh", RT_DEVICE_FLAG_DEACTIVATE); + RT_ASSERT(res == RT_EOK); + + nu_sys_ipclk_enable(s_sUSBHDev.clkidx); + nu_sys_ip_reset(s_sUSBHDev.rstidx); + + + /*initialize the usb host function */ + res = rt_usb_host_init(); + RT_ASSERT(res == RT_EOK); + +#if defined(RT_USING_PM) + rt_pm_device_register(&psUHCD->parent, &device_pm_ops); +#endif + + return 0; +} +INIT_DEVICE_EXPORT(nu_usbh_register); + +#endif diff --git a/bsp/nuvoton/nk-980iot/.config b/bsp/nuvoton/nk-980iot/.config index 1366c6c4ca..a69c66a0ba 100644 --- a/bsp/nuvoton/nk-980iot/.config +++ b/bsp/nuvoton/nk-980iot/.config @@ -125,6 +125,11 @@ CONFIG_RT_DFS_ELM_WORD_ACCESS=y # CONFIG_RT_DFS_ELM_USE_LFN_2 is not set CONFIG_RT_DFS_ELM_USE_LFN_3=y CONFIG_RT_DFS_ELM_USE_LFN=3 +CONFIG_RT_DFS_ELM_LFN_UNICODE_0=y +# CONFIG_RT_DFS_ELM_LFN_UNICODE_1 is not set +# CONFIG_RT_DFS_ELM_LFN_UNICODE_2 is not set +# CONFIG_RT_DFS_ELM_LFN_UNICODE_3 is not set +CONFIG_RT_DFS_ELM_LFN_UNICODE=0 CONFIG_RT_DFS_ELM_MAX_LFN=255 CONFIG_RT_DFS_ELM_DRIVES=8 CONFIG_RT_DFS_ELM_MAX_SECTOR_SIZE=4096 @@ -133,7 +138,12 @@ CONFIG_RT_DFS_ELM_REENTRANT=y CONFIG_RT_USING_DFS_DEVFS=y # CONFIG_RT_USING_DFS_ROMFS is not set # CONFIG_RT_USING_DFS_RAMFS is not set -# CONFIG_RT_USING_DFS_UFFS is not set +CONFIG_RT_USING_DFS_UFFS=y +# CONFIG_RT_UFFS_ECC_MODE_0 is not set +# CONFIG_RT_UFFS_ECC_MODE_1 is not set +# CONFIG_RT_UFFS_ECC_MODE_2 is not set +CONFIG_RT_UFFS_ECC_MODE_3=y +CONFIG_RT_UFFS_ECC_MODE=3 # CONFIG_RT_USING_DFS_JFFS2 is not set # CONFIG_RT_USING_DFS_NFS is not set @@ -162,7 +172,8 @@ CONFIG_RT_USING_ADC=y # CONFIG_RT_USING_DAC is not set CONFIG_RT_USING_PWM=y # CONFIG_RT_USING_MTD_NOR is not set -# CONFIG_RT_USING_MTD_NAND is not set +CONFIG_RT_USING_MTD_NAND=y +CONFIG_RT_MTD_NAND_DEBUG=y # CONFIG_RT_USING_PM is not set CONFIG_RT_USING_RTC=y CONFIG_RT_USING_ALARM=y @@ -174,12 +185,7 @@ CONFIG_RTC_NTP_SYNC_PERIOD=3600 CONFIG_RT_USING_SPI=y CONFIG_RT_USING_QSPI=y # CONFIG_RT_USING_SPI_MSD is not set -CONFIG_RT_USING_SFUD=y -CONFIG_RT_SFUD_USING_SFDP=y -CONFIG_RT_SFUD_USING_FLASH_INFO_TABLE=y -CONFIG_RT_SFUD_USING_QSPI=y -CONFIG_RT_SFUD_SPI_MAX_HZ=50000000 -# CONFIG_RT_DEBUG_SFUD is not set +# CONFIG_RT_USING_SFUD is not set # CONFIG_RT_USING_ENC28J60 is not set # CONFIG_RT_USING_SPI_WIFI is not set CONFIG_RT_USING_WDT=y @@ -222,7 +228,7 @@ CONFIG_RT_HWCRYPTO_USING_RNG=y # CONFIG_RT_USING_USB_HOST=y CONFIG_RT_USBH_MSTORAGE=y -CONFIG_UDISK_MOUNTPOINT="/" +CONFIG_UDISK_MOUNTPOINT="/mnt/udisk" CONFIG_RT_USING_USB_DEVICE=y CONFIG_RT_USBD_THREAD_STACK_SZ=4096 CONFIG_USB_VENDOR_ID=0x0FFE @@ -264,6 +270,7 @@ CONFIG_RT_USING_POSIX=y # Socket abstraction layer # CONFIG_RT_USING_SAL=y +CONFIG_SAL_INTERNET_CHECK=y # # protocol stack implement @@ -523,21 +530,7 @@ CONFIG_PKG_NETUTILS_VER="v1.2.0" # CONFIG_PKG_USING_PIXMAN is not set # CONFIG_PKG_USING_LWEXT4 is not set # CONFIG_PKG_USING_PARTITION is not set -CONFIG_PKG_USING_FAL=y -CONFIG_PKG_FAL_PATH="/packages/system/fal" -CONFIG_FAL_DEBUG_CONFIG=y -CONFIG_FAL_DEBUG=1 -CONFIG_FAL_PART_HAS_TABLE_CFG=y -CONFIG_FAL_USING_SFUD_PORT=y -CONFIG_FAL_USING_NOR_FLASH_DEV_NAME="norflash0" -# CONFIG_PKG_USING_FAL_V00500 is not set -# CONFIG_PKG_USING_FAL_V00400 is not set -# CONFIG_PKG_USING_FAL_V00300 is not set -# CONFIG_PKG_USING_FAL_V00200 is not set -# CONFIG_PKG_USING_FAL_V00100 is not set -CONFIG_PKG_USING_FAL_LATEST_VERSION=y -CONFIG_PKG_FAL_VER="latest" -CONFIG_PKG_FAL_VER_NUM=0x99999 +# CONFIG_PKG_USING_FAL is not set # CONFIG_PKG_USING_FLASHDB is not set # CONFIG_PKG_USING_SQLITE is not set # CONFIG_PKG_USING_RTI is not set @@ -707,6 +700,7 @@ CONFIG_NU_PKG_USING_DEMO=y # CONFIG_NU_PKG_USING_NAU88L25 is not set CONFIG_NU_PKG_USING_NAU8822=y # CONFIG_NU_PKG_USING_ILI9341 is not set +CONFIG_NU_PKG_USING_SPINAND=y # # Hardware Drivers Config @@ -734,23 +728,18 @@ CONFIG_BSP_USING_TMR=y CONFIG_BSP_USING_TIMER=y CONFIG_BSP_USING_TMR0=y CONFIG_BSP_USING_TIMER0=y -# CONFIG_BSP_USING_TPWM0 is not set # CONFIG_BSP_USING_TIMER0_CAPTURE is not set CONFIG_BSP_USING_TMR1=y CONFIG_BSP_USING_TIMER1=y -# CONFIG_BSP_USING_TPWM1 is not set # CONFIG_BSP_USING_TIMER1_CAPTURE is not set CONFIG_BSP_USING_TMR2=y CONFIG_BSP_USING_TIMER2=y -# CONFIG_BSP_USING_TPWM2 is not set # CONFIG_BSP_USING_TIMER2_CAPTURE is not set CONFIG_BSP_USING_TMR3=y CONFIG_BSP_USING_TIMER3=y -# CONFIG_BSP_USING_TPWM3 is not set # CONFIG_BSP_USING_TIMER3_CAPTURE is not set CONFIG_BSP_USING_TMR4=y CONFIG_BSP_USING_TIMER4=y -# CONFIG_BSP_USING_TPWM4 is not set # CONFIG_BSP_USING_TIMER4_CAPTURE is not set CONFIG_BSP_USING_UART=y CONFIG_BSP_USING_UART0=y @@ -793,7 +782,7 @@ CONFIG_BSP_USING_I2S=y CONFIG_NU_I2S_DMA_FIFO_SIZE=4096 CONFIG_BSP_USING_QSPI=y CONFIG_BSP_USING_QSPI0=y -# CONFIG_BSP_USING_QSPI0_PDMA is not set +CONFIG_BSP_USING_QSPI0_PDMA=y # CONFIG_BSP_USING_SCUART is not set CONFIG_BSP_USING_CRYPTO=y # CONFIG_NU_PRNG_USE_SEED is not set @@ -802,7 +791,6 @@ CONFIG_BSP_USING_WDT=y # CONFIG_BSP_USING_EBI is not set CONFIG_BSP_USING_USBD=y CONFIG_BSP_USING_USBH=y -CONFIG_BSP_USING_OTG=y # # On-board Peripheral Drivers @@ -811,11 +799,10 @@ CONFIG_BSP_USING_CONSOLE=y CONFIG_BOARD_USING_IP101GR=y CONFIG_BOARD_USING_NAU8822=y CONFIG_BOARD_USING_STORAGE_SDCARD=y -CONFIG_BOARD_USING_STORAGE_SPIFLASH=y -# CONFIG_BOARD_USING_USB_NONE is not set -# CONFIG_BOARD_USING_USB_HOST is not set -# CONFIG_BOARD_USING_USB_OTG is not set -CONFIG_BOARD_USING_USB_OTG_HOST=y +# CONFIG_BOARD_USING_STORAGE_SPIFLASH is not set +CONFIG_BOARD_USING_STORAGE_SPINAND=y +CONFIG_BOARD_USING_USB0_DEVICE_HOST=y +CONFIG_BOARD_USING_USB1_HOST=y # # Board extended module drivers diff --git a/bsp/nuvoton/nk-980iot/README.md b/bsp/nuvoton/nk-980iot/README.md index 883545a556..610ca09a55 100644 --- a/bsp/nuvoton/nk-980iot/README.md +++ b/bsp/nuvoton/nk-980iot/README.md @@ -43,8 +43,8 @@ Nuvoton Technology provides industrial IoT development platform using NUC980DK61 |LEDs | | Supported | |Audio Codec | NAU8822, Supports MIC and earphone | Supported | |USB Device | VCOM + MStorage | Supported | -|2xUSB Host | MStorage | Not ready | -|SPI NAND flash | W25N01GVZE1G | Not ready | +|2xUSB Host | MStorage | Supported | +|SPI NAND flash | W25N01GVZE1G | Supported | |VCOM | For console | Ready.(Need to install VCOM driver) | ## 2. Supported compiler @@ -88,6 +88,43 @@ Execute Address: 0x0
Enjoy!!

+### 3.3 SPI NAND flash using NuWriter +You can use NuWriter to program rtthread.bin into SPI NAND flash. +[![SPI NAND flash](https://i.imgur.com/p9LudBK.gif "SPI NAND flash")](https://i.imgur.com/p9LudBK.gif "SPI NAND flash using NuWriter") +
+Choose type: SPINAND
+<< Press Re-Connect >>
+ +<< Press Erase >>
+<< Select Erase All >>
+<< Press OK >>
+ +Choose file: Specify your uboot-spl.bin file.
+Image Type: Loader
+Execute Address: 0x200
+<< Press Program >>
+ +Choose file: Specify your uboot.bin file.
+Image Type: Data
+Image start address: 0x100000
+<< Press Program >>
+ +Choose file: Specify your rtthread.bin file.
+Image Type: Data
+Image start address: 0x200000
+<< Press Program >>
+ +Choose file: Specify your env.txt file.
+Image Type: Environment
+Image start address: 0x80000
+<< Press Program >>
+ +<< Press OK & Wait it down >>
+<< Set Power-on setting to SPI NAND booting >>
+<< Press Reset button on board >>
+Enjoy!!
+
+ ## 4. Test You can use Tera Term terminate emulator (or other software) to type commands of RTT. All parameters of serial communication are shown in below image. Here, you can find out the corresponding port number of Nuvoton Virtual Com Port in window device manager. diff --git a/bsp/nuvoton/nk-980iot/applications/main.c b/bsp/nuvoton/nk-980iot/applications/main.c index fc076010b1..53de4f85dc 100644 --- a/bsp/nuvoton/nk-980iot/applications/main.c +++ b/bsp/nuvoton/nk-980iot/applications/main.c @@ -22,6 +22,7 @@ /* defined the LED_Y pin: PB8 */ #define LED_Y NU_GET_PININDEX(NU_PB, 8) +#if !defined(BSP_USING_USBH) /* defined the Key1 pin: PE10 */ #define KEY_1 NU_GET_PININDEX(NU_PE, 10) @@ -47,6 +48,8 @@ void nu_button_cb(void *args) } #endif +#endif + int main(int argc, char **argv) { #if defined(RT_USING_PIN) @@ -58,6 +61,7 @@ int main(int argc, char **argv) /* set LED_Y pin mode to output */ rt_pin_mode(LED_Y, PIN_MODE_OUTPUT); +#if !defined(BSP_USING_USBH) /* set KEY_1 pin mode to input */ rt_pin_mode(KEY_1, PIN_MODE_INPUT_PULLUP); @@ -69,6 +73,7 @@ int main(int argc, char **argv) rt_pin_attach_irq(KEY_2, PIN_IRQ_MODE_FALLING, nu_button_cb, &u32Key2); rt_pin_irq_enable(KEY_2, PIN_IRQ_ENABLE); +#endif while (counter--) { diff --git a/bsp/nuvoton/nk-980iot/applications/mnt.c b/bsp/nuvoton/nk-980iot/applications/mnt.c index f93229af9b..746d1e9a36 100644 --- a/bsp/nuvoton/nk-980iot/applications/mnt.c +++ b/bsp/nuvoton/nk-980iot/applications/mnt.c @@ -53,7 +53,10 @@ const void *data; const struct dfs_mount_tbl mount_table[] = { - { RAMDISK_UDC, "/ramdisk_udc", "elm", 0, RT_NULL }, + { RAMDISK_UDC, "/mnt/ram_usbd", "elm", 0, RT_NULL }, +#if defined(RT_USING_DFS_UFFS) + { "nand1", "/mnt/filesystem", "uffs", 0, RT_NULL }, +#endif {0}, }; #endif @@ -171,10 +174,11 @@ int filesystem_init(void) LOG_I("ramdisk mounted on \"/\"."); /* now you can create dir dynamically. */ - mkdir_p("/ramdisk_udc", 0x777); mkdir_p("/mnt", 0x777); mkdir_p("/cache", 0x777); mkdir_p("/download", 0x777); + mkdir_p("/mnt/ram_usbd", 0x777); + mkdir_p("/mnt/filesystem", 0x777); #if defined(RT_USBH_MSTORAGE) && defined(UDISK_MOUNTPOINT) mkdir_p(UDISK_MOUNTPOINT, 0x777); #endif @@ -219,11 +223,6 @@ int mnt_init_spiflash0(void) rt_kprintf("Failed to create block device for %s.\n", PARTITION_NAME_FILESYSTEM); goto exit_mnt_init_spiflash0; } - else if (mkdir(MOUNT_POINT_SPIFLASH0, 0x777) < 0) - { - rt_kprintf("Failed to make folder for %s.\n", MOUNT_POINT_SPIFLASH0); - goto exit_mnt_init_spiflash0; - } else if (dfs_mount(psNorFlash->parent.name, MOUNT_POINT_SPIFLASH0, "elm", 0, 0) != 0) { rt_kprintf("Failed to mount elm on %s.\n", MOUNT_POINT_SPIFLASH0); diff --git a/bsp/nuvoton/nk-980iot/board/Kconfig b/bsp/nuvoton/nk-980iot/board/Kconfig index c14b95b0ae..98e24aeba6 100644 --- a/bsp/nuvoton/nk-980iot/board/Kconfig +++ b/bsp/nuvoton/nk-980iot/board/Kconfig @@ -33,39 +33,31 @@ menu "Hardware Drivers Config" default y config BOARD_USING_STORAGE_SPIFLASH - bool "SPIFLASH supporting(over qspi0)" + bool "SPI NOR FLASH supporting(over qspi0)" select BSP_USING_QSPI select BSP_USING_QSPI0 + default n + + config BOARD_USING_STORAGE_SPINAND + bool "SPI NAND FLASH supporting(over qspi0)" + select BSP_USING_QSPI + select BSP_USING_QSPI0 + select NU_PKG_USING_SPINAND default y - choice - prompt "Select HS USB Ports" + config BOARD_USING_USB0_DEVICE_HOST + select BSP_USING_USBH + select BSP_USING_USBD + bool "Enable USB0 Device/Host" + help + Choose this option if you need USB device or host function mode. + If you need USB host, please remember short to ground on JP1 jumper. - config BOARD_USING_USB_NONE - bool "Without any USB function" - help - Choose this option if you need not USB functions. - - config BOARD_USING_USB_HOST - select BSP_USING_USBH - bool "Enable USBH" - help - Choose this option if you need USB HOST function mode. - - config BOARD_USING_USB_OTG - select BSP_USING_OTG - bool "Enable OTG" - help - Choose this option if you need USB HOST function mode. - - config BOARD_USING_USB_OTG_HOST - select BSP_USING_USBH - select BSP_USING_OTG - bool "Enable OTG AND USBH" - help - Choose this option if you need both USB ports function mode. - - endchoice + config BOARD_USING_USB1_HOST + select BSP_USING_USBH + bool "Enable USB1 Host" + help + Choose this option if you need USB1 HOST. endmenu diff --git a/bsp/nuvoton/nk-980iot/board/board.h b/bsp/nuvoton/nk-980iot/board/board.h index f5392785a3..38762245cd 100644 --- a/bsp/nuvoton/nk-980iot/board/board.h +++ b/bsp/nuvoton/nk-980iot/board/board.h @@ -28,6 +28,12 @@ #define BOARD_SDRAM_SIZE 0x04000000 #define BOARD_HEAP_END ((void*)BOARD_SDRAM_SIZE) +#if defined(RT_USING_MTD_NAND) + #include + #define MTD_SPINAND_PARTITION_NUM 3 + extern struct rt_mtd_nand_device mtd_partitions[MTD_SPINAND_PARTITION_NUM]; +#endif + extern void rt_hw_board_init(void); extern void nu_clock_init(void); extern void nu_clock_deinit(void); diff --git a/bsp/nuvoton/nk-980iot/board/board_dev.c b/bsp/nuvoton/nk-980iot/board/board_dev.c index e7ba364d66..f9ef7a2b88 100644 --- a/bsp/nuvoton/nk-980iot/board/board_dev.c +++ b/bsp/nuvoton/nk-980iot/board/board_dev.c @@ -30,8 +30,11 @@ static rt_uint8_t SpiFlash_ReadStatusReg(struct rt_qspi_device *qspi_device) { rt_uint8_t u8Val; + rt_err_t result = RT_EOK; rt_uint8_t w25x_txCMD1 = W25X_REG_READSTATUS; - rt_qspi_send_then_recv(qspi_device, &w25x_txCMD1, 1, &u8Val, 1); + + result = rt_qspi_send_then_recv(qspi_device, &w25x_txCMD1, 1, &u8Val, 1); + RT_ASSERT(result > 0); return u8Val; } @@ -39,24 +42,53 @@ static rt_uint8_t SpiFlash_ReadStatusReg(struct rt_qspi_device *qspi_device) static rt_uint8_t SpiFlash_ReadStatusReg2(struct rt_qspi_device *qspi_device) { rt_uint8_t u8Val; + rt_err_t result = RT_EOK; rt_uint8_t w25x_txCMD1 = W25X_REG_READSTATUS2; - rt_qspi_send_then_recv(qspi_device, &w25x_txCMD1, 1, &u8Val, 1); + + result = rt_qspi_send_then_recv(qspi_device, &w25x_txCMD1, 1, &u8Val, 1); + RT_ASSERT(result > 0); return u8Val; } -static void SpiFlash_WriteStatusReg(struct rt_qspi_device *qspi_device, uint8_t u8Value1, uint8_t u8Value2) +static rt_err_t SpiFlash_WriteStatusReg(struct rt_qspi_device *qspi_device, uint8_t u8Value1, uint8_t u8Value2) { rt_uint8_t w25x_txCMD1; - rt_uint8_t u8Val[3]; + rt_uint8_t au8Val[2]; + rt_err_t result; + struct rt_qspi_message qspi_message = {0}; + /* Enable WE */ w25x_txCMD1 = W25X_REG_WRITEENABLE; - rt_qspi_send(qspi_device, &w25x_txCMD1, 1); + result = rt_qspi_send(qspi_device, &w25x_txCMD1, sizeof(w25x_txCMD1)); + if (result != sizeof(w25x_txCMD1)) + goto exit_SpiFlash_WriteStatusReg; - u8Val[0] = W25X_REG_WRITESTATUS; - u8Val[1] = u8Value1; - u8Val[2] = u8Value2; - rt_qspi_send(qspi_device, &u8Val, 3); + /* Prepare status-1, 2 data */ + au8Val[0] = u8Value1; + au8Val[1] = u8Value2; + + /* 1-bit mode: Instruction+payload */ + qspi_message.instruction.content = W25X_REG_WRITESTATUS; + qspi_message.instruction.qspi_lines = 1; + + qspi_message.qspi_data_lines = 1; + qspi_message.parent.cs_take = 1; + qspi_message.parent.cs_release = 1; + qspi_message.parent.send_buf = &au8Val[0]; + qspi_message.parent.length = sizeof(au8Val); + qspi_message.parent.next = RT_NULL; + + if (rt_qspi_transfer_message(qspi_device, &qspi_message) != sizeof(au8Val)) + { + result = -RT_ERROR; + } + + result = RT_EOK; + +exit_SpiFlash_WriteStatusReg: + + return result; } static void SpiFlash_WaitReady(struct rt_qspi_device *qspi_device) @@ -73,21 +105,30 @@ static void SpiFlash_WaitReady(struct rt_qspi_device *qspi_device) static void SpiFlash_EnterQspiMode(struct rt_qspi_device *qspi_device) { + rt_err_t result = RT_EOK; + uint8_t u8Status1 = SpiFlash_ReadStatusReg(qspi_device); uint8_t u8Status2 = SpiFlash_ReadStatusReg2(qspi_device); u8Status2 |= W25X_REG_QUADENABLE; - SpiFlash_WriteStatusReg(qspi_device, u8Status1, u8Status2); + + result = SpiFlash_WriteStatusReg(qspi_device, u8Status1, u8Status2); + RT_ASSERT(result == RT_EOK); + SpiFlash_WaitReady(qspi_device); } static void SpiFlash_ExitQspiMode(struct rt_qspi_device *qspi_device) { + rt_err_t result = RT_EOK; uint8_t u8Status1 = SpiFlash_ReadStatusReg(qspi_device); uint8_t u8Status2 = SpiFlash_ReadStatusReg2(qspi_device); u8Status2 &= ~W25X_REG_QUADENABLE; - SpiFlash_WriteStatusReg(qspi_device, u8Status1, u8Status2); + + result = SpiFlash_WriteStatusReg(qspi_device, u8Status1, u8Status2); + RT_ASSERT(result == RT_EOK); + SpiFlash_WaitReady(qspi_device); } @@ -105,7 +146,49 @@ static int rt_hw_spiflash_init(void) return 0; } INIT_COMPONENT_EXPORT(rt_hw_spiflash_init); -#endif /* BOARD_USING_STORAGE_SPIFLASH */ +#endif + +#if defined(BOARD_USING_STORAGE_SPINAND) && defined(NU_PKG_USING_SPINAND) + +#include "drv_qspi.h" +#include "spinand.h" + +struct rt_mtd_nand_device mtd_partitions[MTD_SPINAND_PARTITION_NUM] = +{ + [0] = + { + .block_start = 0, + .block_end = 23, + .block_total = 24, + }, + [1] = + { + .block_start = 24, + .block_end = 1023, + .block_total = 1000, + }, + [2] = + { + .block_start = 0, + .block_end = 1023, + .block_total = 1024, + } +}; + +static int rt_hw_spinand_init(void) +{ + if (nu_qspi_bus_attach_device("qspi0", "qspi01", 4, RT_NULL, RT_NULL) != RT_EOK) + return -1; + + if (rt_hw_mtd_spinand_register("qspi01") != RT_EOK) + return -1; + + return 0; +} + +INIT_COMPONENT_EXPORT(rt_hw_spinand_init); +#endif + #if defined(BOARD_USING_LCD_ILI9341) && defined(NU_PKG_USING_ILI9341_SPI) #include @@ -161,9 +244,9 @@ static int rt_hw_esp8266_port(void) AT_DEVICE_CLASS_ESP8266, (void *) esp8266); } -//INIT_APP_EXPORT(rt_hw_esp8266_port); +INIT_APP_EXPORT(rt_hw_esp8266_port); -static void at_wifi_set(int argc, char **argv) +static int at_wifi_set(int argc, char **argv) { struct at_device_ssid_pwd sATDConf; struct at_device *at_dev = RT_NULL; @@ -173,7 +256,7 @@ static void at_wifi_set(int argc, char **argv) { rt_kprintf("\n"); rt_kprintf("at_wifi_set \n"); - return ; + return -1; } sATDConf.ssid = argv[1]; //ssid @@ -185,6 +268,8 @@ static void at_wifi_set(int argc, char **argv) { rt_kprintf("Can't find any initialized AT device.\n"); } + + return 0; } #ifdef FINSH_USING_MSH MSH_CMD_EXPORT(at_wifi_set, AT device wifi set ssid / password function); diff --git a/bsp/nuvoton/nk-980iot/board/nu_pin_init.c b/bsp/nuvoton/nk-980iot/board/nu_pin_init.c index 1c03b7eb60..002919896a 100644 --- a/bsp/nuvoton/nk-980iot/board/nu_pin_init.c +++ b/bsp/nuvoton/nk-980iot/board/nu_pin_init.c @@ -78,10 +78,16 @@ static void nu_pin_can_init(void) } +#if defined(BSP_USING_USBD) static void nu_pin_usbd_init(void) { /* USB0_VBUSVLD, PE.11 */ - //outpw(REG_SYS_GPE_MFPH, (inpw(REG_SYS_GPE_MFPH) & ~0x0000F000) | 0x00001000); + outpw(REG_SYS_GPE_MFPH, (inpw(REG_SYS_GPE_MFPH) & ~0x0000F000) | 0x00001000); +} +#endif + +static void nu_pin_usbh_init(void) +{ } void nu_pin_init(void) @@ -95,7 +101,12 @@ void nu_pin_init(void) nu_pin_pwm_init(); nu_pin_i2s_init(); nu_pin_can_init(); + +#if defined(BSP_USING_USBD) nu_pin_usbd_init(); +#endif + + nu_pin_usbh_init(); } void nu_pin_deinit(void) diff --git a/bsp/nuvoton/nk-980iot/linking_scripts/nuc980.ld b/bsp/nuvoton/nk-980iot/linking_scripts/nuc980.ld index 19699374b8..1004f039fb 100644 --- a/bsp/nuvoton/nk-980iot/linking_scripts/nuc980.ld +++ b/bsp/nuvoton/nk-980iot/linking_scripts/nuc980.ld @@ -50,7 +50,7 @@ SECTIONS . = ALIGN(4); /* section information for utest */ - . = ALIGN(4); + . = ALIGN(4); __rt_utest_tc_tab_start = .; KEEP(*(UtestTcTab)) __rt_utest_tc_tab_end = .; diff --git a/bsp/nuvoton/nk-980iot/rtconfig.py b/bsp/nuvoton/nk-980iot/rtconfig.py index df4a99bcee..c246b656ad 100644 --- a/bsp/nuvoton/nk-980iot/rtconfig.py +++ b/bsp/nuvoton/nk-980iot/rtconfig.py @@ -53,10 +53,10 @@ if PLATFORM == 'gcc': LPATH = '' if BUILD == 'debug': - CFLAGS += ' -O0 -gdwarf-2' + CFLAGS += ' -O2 -gdwarf-2' AFLAGS += ' -gdwarf-2' else: - CFLAGS += ' -O0' + CFLAGS += ' -O2' POST_ACTION = OBJCPY + ' -O binary $TARGET ' + TARGET_NAME + '\n' POST_ACTION += SIZE + ' $TARGET\n' diff --git a/bsp/nuvoton/numaker-iot-m487/Nu_Link_Driver.ini b/bsp/nuvoton/numaker-iot-m487/Nu_Link_Driver.ini index 8e9352bcef..f702d5c1aa 100644 --- a/bsp/nuvoton/numaker-iot-m487/Nu_Link_Driver.ini +++ b/bsp/nuvoton/numaker-iot-m487/Nu_Link_Driver.ini @@ -1,9 +1,9 @@ [Version] Nu_LinkVersion=V5.14 [Process] -ProcessID=0x00003a80 -ProcessCreationTime_L=0xac7abbce -ProcessCreationTime_H=0x01d642ca +ProcessID=0x00004840 +ProcessCreationTime_L=0xb55cd40e +ProcessCreationTime_H=0x01d6e4e3 NuLinkID=0x77885acd NuLinkIDs_Count=0x00000001 NuLinkID0=0x77885acd diff --git a/bsp/nuvoton/numaker-iot-m487/board/board_dev.c b/bsp/nuvoton/numaker-iot-m487/board/board_dev.c index 068573e8fe..d90d503f43 100644 --- a/bsp/nuvoton/numaker-iot-m487/board/board_dev.c +++ b/bsp/nuvoton/numaker-iot-m487/board/board_dev.c @@ -30,8 +30,11 @@ static rt_uint8_t SpiFlash_ReadStatusReg(struct rt_qspi_device *qspi_device) { rt_uint8_t u8Val; + rt_err_t result = RT_EOK; rt_uint8_t w25x_txCMD1 = W25X_REG_READSTATUS; - rt_qspi_send_then_recv(qspi_device, &w25x_txCMD1, 1, &u8Val, 1); + + result = rt_qspi_send_then_recv(qspi_device, &w25x_txCMD1, 1, &u8Val, 1); + RT_ASSERT(result > 0); return u8Val; } @@ -39,24 +42,53 @@ static rt_uint8_t SpiFlash_ReadStatusReg(struct rt_qspi_device *qspi_device) static rt_uint8_t SpiFlash_ReadStatusReg2(struct rt_qspi_device *qspi_device) { rt_uint8_t u8Val; + rt_err_t result = RT_EOK; rt_uint8_t w25x_txCMD1 = W25X_REG_READSTATUS2; - rt_qspi_send_then_recv(qspi_device, &w25x_txCMD1, 1, &u8Val, 1); + + result = rt_qspi_send_then_recv(qspi_device, &w25x_txCMD1, 1, &u8Val, 1); + RT_ASSERT(result > 0); return u8Val; } -static void SpiFlash_WriteStatusReg(struct rt_qspi_device *qspi_device, uint8_t u8Value1, uint8_t u8Value2) +static rt_err_t SpiFlash_WriteStatusReg(struct rt_qspi_device *qspi_device, uint8_t u8Value1, uint8_t u8Value2) { rt_uint8_t w25x_txCMD1; - rt_uint8_t u8Val[3]; + rt_uint8_t au8Val[2]; + rt_err_t result; + struct rt_qspi_message qspi_message = {0}; + /* Enable WE */ w25x_txCMD1 = W25X_REG_WRITEENABLE; - rt_qspi_send(qspi_device, &w25x_txCMD1, 1); + result = rt_qspi_send(qspi_device, &w25x_txCMD1, sizeof(w25x_txCMD1)); + if (result != sizeof(w25x_txCMD1)) + goto exit_SpiFlash_WriteStatusReg; - u8Val[0] = W25X_REG_WRITESTATUS; - u8Val[1] = u8Value1; - u8Val[2] = u8Value2; - rt_qspi_send(qspi_device, &u8Val, 3); + /* Prepare status-1, 2 data */ + au8Val[0] = u8Value1; + au8Val[1] = u8Value2; + + /* 1-bit mode: Instruction+payload */ + qspi_message.instruction.content = W25X_REG_WRITESTATUS; + qspi_message.instruction.qspi_lines = 1; + + qspi_message.qspi_data_lines = 1; + qspi_message.parent.cs_take = 1; + qspi_message.parent.cs_release = 1; + qspi_message.parent.send_buf = &au8Val[0]; + qspi_message.parent.length = sizeof(au8Val); + qspi_message.parent.next = RT_NULL; + + if (rt_qspi_transfer_message(qspi_device, &qspi_message) != sizeof(au8Val)) + { + result = -RT_ERROR; + } + + result = RT_EOK; + +exit_SpiFlash_WriteStatusReg: + + return result; } static void SpiFlash_WaitReady(struct rt_qspi_device *qspi_device) @@ -73,21 +105,30 @@ static void SpiFlash_WaitReady(struct rt_qspi_device *qspi_device) static void SpiFlash_EnterQspiMode(struct rt_qspi_device *qspi_device) { + rt_err_t result = RT_EOK; + uint8_t u8Status1 = SpiFlash_ReadStatusReg(qspi_device); uint8_t u8Status2 = SpiFlash_ReadStatusReg2(qspi_device); u8Status2 |= W25X_REG_QUADENABLE; - SpiFlash_WriteStatusReg(qspi_device, u8Status1, u8Status2); + + result = SpiFlash_WriteStatusReg(qspi_device, u8Status1, u8Status2); + RT_ASSERT(result == RT_EOK); + SpiFlash_WaitReady(qspi_device); } static void SpiFlash_ExitQspiMode(struct rt_qspi_device *qspi_device) { + rt_err_t result = RT_EOK; uint8_t u8Status1 = SpiFlash_ReadStatusReg(qspi_device); uint8_t u8Status2 = SpiFlash_ReadStatusReg2(qspi_device); u8Status2 &= ~W25X_REG_QUADENABLE; - SpiFlash_WriteStatusReg(qspi_device, u8Status1, u8Status2); + + result = SpiFlash_WriteStatusReg(qspi_device, u8Status1, u8Status2); + RT_ASSERT(result == RT_EOK); + SpiFlash_WaitReady(qspi_device); } diff --git a/bsp/nuvoton/numaker-pfm-m487/board/board_dev.c b/bsp/nuvoton/numaker-pfm-m487/board/board_dev.c index 5a039f6b30..045eed9139 100644 --- a/bsp/nuvoton/numaker-pfm-m487/board/board_dev.c +++ b/bsp/nuvoton/numaker-pfm-m487/board/board_dev.c @@ -30,8 +30,11 @@ static rt_uint8_t SpiFlash_ReadStatusReg(struct rt_qspi_device *qspi_device) { rt_uint8_t u8Val; + rt_err_t result = RT_EOK; rt_uint8_t w25x_txCMD1 = W25X_REG_READSTATUS; - rt_qspi_send_then_recv(qspi_device, &w25x_txCMD1, 1, &u8Val, 1); + + result = rt_qspi_send_then_recv(qspi_device, &w25x_txCMD1, 1, &u8Val, 1); + RT_ASSERT(result > 0); return u8Val; } @@ -39,24 +42,53 @@ static rt_uint8_t SpiFlash_ReadStatusReg(struct rt_qspi_device *qspi_device) static rt_uint8_t SpiFlash_ReadStatusReg2(struct rt_qspi_device *qspi_device) { rt_uint8_t u8Val; + rt_err_t result = RT_EOK; rt_uint8_t w25x_txCMD1 = W25X_REG_READSTATUS2; - rt_qspi_send_then_recv(qspi_device, &w25x_txCMD1, 1, &u8Val, 1); + + result = rt_qspi_send_then_recv(qspi_device, &w25x_txCMD1, 1, &u8Val, 1); + RT_ASSERT(result > 0); return u8Val; } -static void SpiFlash_WriteStatusReg(struct rt_qspi_device *qspi_device, uint8_t u8Value1, uint8_t u8Value2) +static rt_err_t SpiFlash_WriteStatusReg(struct rt_qspi_device *qspi_device, uint8_t u8Value1, uint8_t u8Value2) { rt_uint8_t w25x_txCMD1; - rt_uint8_t u8Val[3]; + rt_uint8_t au8Val[2]; + rt_err_t result; + struct rt_qspi_message qspi_message = {0}; + /* Enable WE */ w25x_txCMD1 = W25X_REG_WRITEENABLE; - rt_qspi_send(qspi_device, &w25x_txCMD1, 1); + result = rt_qspi_send(qspi_device, &w25x_txCMD1, sizeof(w25x_txCMD1)); + if (result != sizeof(w25x_txCMD1)) + goto exit_SpiFlash_WriteStatusReg; - u8Val[0] = W25X_REG_WRITESTATUS; - u8Val[1] = u8Value1; - u8Val[2] = u8Value2; - rt_qspi_send(qspi_device, &u8Val, 3); + /* Prepare status-1, 2 data */ + au8Val[0] = u8Value1; + au8Val[1] = u8Value2; + + /* 1-bit mode: Instruction+payload */ + qspi_message.instruction.content = W25X_REG_WRITESTATUS; + qspi_message.instruction.qspi_lines = 1; + + qspi_message.qspi_data_lines = 1; + qspi_message.parent.cs_take = 1; + qspi_message.parent.cs_release = 1; + qspi_message.parent.send_buf = &au8Val[0]; + qspi_message.parent.length = sizeof(au8Val); + qspi_message.parent.next = RT_NULL; + + if (rt_qspi_transfer_message(qspi_device, &qspi_message) != sizeof(au8Val)) + { + result = -RT_ERROR; + } + + result = RT_EOK; + +exit_SpiFlash_WriteStatusReg: + + return result; } static void SpiFlash_WaitReady(struct rt_qspi_device *qspi_device) @@ -73,21 +105,30 @@ static void SpiFlash_WaitReady(struct rt_qspi_device *qspi_device) static void SpiFlash_EnterQspiMode(struct rt_qspi_device *qspi_device) { + rt_err_t result = RT_EOK; + uint8_t u8Status1 = SpiFlash_ReadStatusReg(qspi_device); uint8_t u8Status2 = SpiFlash_ReadStatusReg2(qspi_device); u8Status2 |= W25X_REG_QUADENABLE; - SpiFlash_WriteStatusReg(qspi_device, u8Status1, u8Status2); + + result = SpiFlash_WriteStatusReg(qspi_device, u8Status1, u8Status2); + RT_ASSERT(result == RT_EOK); + SpiFlash_WaitReady(qspi_device); } static void SpiFlash_ExitQspiMode(struct rt_qspi_device *qspi_device) { + rt_err_t result = RT_EOK; uint8_t u8Status1 = SpiFlash_ReadStatusReg(qspi_device); uint8_t u8Status2 = SpiFlash_ReadStatusReg2(qspi_device); u8Status2 &= ~W25X_REG_QUADENABLE; - SpiFlash_WriteStatusReg(qspi_device, u8Status1, u8Status2); + + result = SpiFlash_WriteStatusReg(qspi_device, u8Status1, u8Status2); + RT_ASSERT(result == RT_EOK); + SpiFlash_WaitReady(qspi_device); } @@ -265,7 +306,7 @@ static void PlayRingTone(void) rt_pwm_set(bpwm_dev, BPWM_DEV_CHANNEL, period, period); rt_thread_mdelay(50); - rt_pwm_set(bpwm_dev, BPWM_DEV_CHANNEL, period, period/2); + rt_pwm_set(bpwm_dev, BPWM_DEV_CHANNEL, period, period / 2); rt_thread_mdelay(50); }