diff options
Diffstat (limited to 'tinyusb/src/portable/nordic/nrf5x/dcd_nrf5x.c')
m--------- | tinyusb | 0 | ||||
-rwxr-xr-x | tinyusb/src/portable/nordic/nrf5x/dcd_nrf5x.c | 1050 |
2 files changed, 0 insertions, 1050 deletions
diff --git a/tinyusb b/tinyusb new file mode 160000 +Subproject f8288be03f28ad7b944e6925f49422dfa39202c diff --git a/tinyusb/src/portable/nordic/nrf5x/dcd_nrf5x.c b/tinyusb/src/portable/nordic/nrf5x/dcd_nrf5x.c deleted file mode 100755 index f6b64c98..00000000 --- a/tinyusb/src/portable/nordic/nrf5x/dcd_nrf5x.c +++ /dev/null @@ -1,1050 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2019 Ha Thach (tinyusb.org) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * This file is part of the TinyUSB stack. - */ - -#include "tusb_option.h" - -#if TUSB_OPT_DEVICE_ENABLED && CFG_TUSB_MCU == OPT_MCU_NRF5X - -#include "nrf.h" -#include "nrf_clock.h" -#include "nrf_power.h" -#include "nrfx_usbd_errata.h" -#include "device/dcd.h" - -// TODO remove later -#include "device/usbd.h" -#include "device/usbd_pvt.h" // to use defer function helper - -/*------------------------------------------------------------------*/ -/* MACRO TYPEDEF CONSTANT ENUM - *------------------------------------------------------------------*/ -enum -{ - // Max allowed by USB specs - MAX_PACKET_SIZE = 64, - - // Mask of all END event (IN & OUT) for all endpoints. ENDEPIN0-7, ENDEPOUT0-7, ENDISOIN, ENDISOOUT - EDPT_END_ALL_MASK = (0xff << USBD_INTEN_ENDEPIN0_Pos) | (0xff << USBD_INTEN_ENDEPOUT0_Pos) | - USBD_INTENCLR_ENDISOIN_Msk | USBD_INTEN_ENDISOOUT_Msk -}; - -enum -{ - EP_ISO_NUM = 8, // Endpoint number is fixed (8) for ISOOUT and ISOIN - EP_CBI_COUNT = 8 // Control Bulk Interrupt endpoints count -}; - -// Transfer Descriptor -typedef struct -{ - uint8_t* buffer; - uint16_t total_len; - volatile uint16_t actual_len; - uint16_t mps; // max packet size - - // nRF will auto accept OUT packet after DMA is done - // indicate packet is already ACK - volatile bool data_received; - - // Set to true when data was transferred from RAM to ISO IN output buffer. - // New data can be put in ISO IN output buffer after SOF. - bool iso_in_transfer_ready; - -} xfer_td_t; - -// Data for managing dcd -static struct -{ - // All 8 endpoints including control IN & OUT (offset 1) - // +1 for ISO endpoints - xfer_td_t xfer[EP_CBI_COUNT + 1][2]; - - // Number of pending DMA that is started but not handled yet by dcd_int_handler(). - // Since nRF can only carry one DMA can run at a time, this value is normally be either 0 or 1. - // However, in critical section with interrupt disabled, the DMA can be finished and added up - // until handled by dcd_int_handler() when exiting critical section. - volatile uint8_t dma_pending; -}_dcd; - -/*------------------------------------------------------------------*/ -/* Control / Bulk / Interrupt (CBI) Transfer - *------------------------------------------------------------------*/ - -// NVIC_GetEnableIRQ is only available in CMSIS v5 -#ifndef NVIC_GetEnableIRQ -static inline uint32_t NVIC_GetEnableIRQ(IRQn_Type IRQn) -{ - if ((int32_t)(IRQn) >= 0) - { - return((uint32_t)(((NVIC->ISER[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); - } - else - { - return(0U); - } -} -#endif - -// check if we are in ISR -TU_ATTR_ALWAYS_INLINE static inline bool is_in_isr(void) -{ - return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) ? true : false; -} - -// helper to start DMA -// TODO use Cortex M4 LDREX and STREX command (atomic) to have better mutex access to EasyDMA -// since current implementation does not 100% guarded against race condition -static void edpt_dma_start(volatile uint32_t* reg_startep) -{ - // Only one dma can be active - if ( _dcd.dma_pending ) - { - if (is_in_isr()) - { - // Called within ISR, use usbd task to defer later - usbd_defer_func( (osal_task_func_t) edpt_dma_start, (void*) reg_startep, true ); - return; - } - else - { - if ( __get_PRIMASK() || !NVIC_GetEnableIRQ(USBD_IRQn) ) - { - // Called in critical section with interrupt disabled. We have to manually check - // for the DMA complete by comparing current pending DMA with number of ENDED Events - uint32_t ended = 0; - - while ( _dcd.dma_pending > ((uint8_t) ended) ) - { - ended = NRF_USBD->EVENTS_ENDISOIN + NRF_USBD->EVENTS_ENDISOOUT; - - for (uint8_t i=0; i<EP_CBI_COUNT; i++) - { - ended += NRF_USBD->EVENTS_ENDEPIN[i] + NRF_USBD->EVENTS_ENDEPOUT[i]; - } - } - }else - { - // Called in non-critical thread-mode, should be 99% of the time. - // Should be safe to blocking wait until previous DMA transfer complete - while ( _dcd.dma_pending ) { } - } - } - } - - _dcd.dma_pending++; - - (*reg_startep) = 1; - __ISB(); __DSB(); -} - -// DMA is complete -static void edpt_dma_end(void) -{ - TU_ASSERT(_dcd.dma_pending, ); - _dcd.dma_pending = 0; -} - -// helper to set TASKS_EP0STATUS / TASKS_EP0RCVOUT since they also need EasyDMA -// However TASKS_EP0STATUS doesn't trigger any DMA transfer and got ENDED event subsequently -// Therefore dma_running state will be corrected right away -void start_ep0_task(volatile uint32_t* reg_task) -{ - edpt_dma_start(reg_task); - - // correct the dma_running++ in dma start - if (_dcd.dma_pending) _dcd.dma_pending--; -} - -// helper getting td -static inline xfer_td_t* get_td(uint8_t epnum, uint8_t dir) -{ - return &_dcd.xfer[epnum][dir]; -} - -// Start DMA to move data from Endpoint -> RAM -static void xact_out_dma(uint8_t epnum) -{ - xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT); - uint32_t xact_len; - - if (epnum == EP_ISO_NUM) - { - xact_len = NRF_USBD->SIZE.ISOOUT; - // If ZERO bit is set, ignore ISOOUT length - if (xact_len & USBD_SIZE_ISOOUT_ZERO_Msk) xact_len = 0; - else - { - // Trigger DMA move data from Endpoint -> SRAM - NRF_USBD->ISOOUT.PTR = (uint32_t) xfer->buffer; - NRF_USBD->ISOOUT.MAXCNT = xact_len; - - edpt_dma_start(&NRF_USBD->TASKS_STARTISOOUT); - } - } - else - { - xact_len = (uint8_t)NRF_USBD->SIZE.EPOUT[epnum]; - - // Trigger DMA move data from Endpoint -> SRAM - NRF_USBD->EPOUT[epnum].PTR = (uint32_t) xfer->buffer; - NRF_USBD->EPOUT[epnum].MAXCNT = xact_len; - - edpt_dma_start(&NRF_USBD->TASKS_STARTEPOUT[epnum]); - } - - xfer->buffer += xact_len; - xfer->actual_len += xact_len; -} - -// Prepare for a CBI transaction IN, call at the start -// it start DMA to transfer data from RAM -> Endpoint -static void xact_in_dma(uint8_t epnum) -{ - xfer_td_t* xfer = get_td(epnum, TUSB_DIR_IN); - - // Each transaction is up to Max Packet Size - uint16_t const xact_len = tu_min16(xfer->total_len - xfer->actual_len, xfer->mps); - - NRF_USBD->EPIN[epnum].PTR = (uint32_t) xfer->buffer; - NRF_USBD->EPIN[epnum].MAXCNT = xact_len; - - xfer->buffer += xact_len; - - edpt_dma_start(&NRF_USBD->TASKS_STARTEPIN[epnum]); -} - -//--------------------------------------------------------------------+ -// Controller API -//--------------------------------------------------------------------+ -void dcd_init (uint8_t rhport) -{ - (void) rhport; -} - -void dcd_int_enable(uint8_t rhport) -{ - (void) rhport; - NVIC_EnableIRQ(USBD_IRQn); -} - -void dcd_int_disable(uint8_t rhport) -{ - (void) rhport; - NVIC_DisableIRQ(USBD_IRQn); -} - -void dcd_set_address (uint8_t rhport, uint8_t dev_addr) -{ - (void) rhport; - (void) dev_addr; - // Set Address is automatically update by hw controller, nothing to do - - // Enable usbevent for suspend and resume detection - // Since the bus signal D+/D- are stable now. - - // Clear current pending first - NRF_USBD->EVENTCAUSE |= NRF_USBD->EVENTCAUSE; - NRF_USBD->EVENTS_USBEVENT = 0; - - NRF_USBD->INTENSET = USBD_INTEN_USBEVENT_Msk; -} - -void dcd_remote_wakeup(uint8_t rhport) -{ - (void) rhport; - - // Bring controller out of low power mode - // will start wakeup when USBWUALLOWED is set - NRF_USBD->LOWPOWER = 0; -} - -// disconnect by disabling internal pull-up resistor on D+/D- -void dcd_disconnect(uint8_t rhport) -{ - (void) rhport; - NRF_USBD->USBPULLUP = 0; - - // Disable Pull-up does not trigger Power USB Removed, in fact it have no - // impact on the USB Power status at all -> need to submit unplugged event to the stack. - dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, false); -} - -// connect by enabling internal pull-up resistor on D+/D- -void dcd_connect(uint8_t rhport) -{ - (void) rhport; - NRF_USBD->USBPULLUP = 1; -} - -//--------------------------------------------------------------------+ -// Endpoint API -//--------------------------------------------------------------------+ -bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) -{ - (void) rhport; - - uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress); - uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress); - - _dcd.xfer[epnum][dir].mps = desc_edpt->wMaxPacketSize.size; - - if (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS) - { - if (dir == TUSB_DIR_OUT) - { - NRF_USBD->INTENSET = TU_BIT(USBD_INTEN_ENDEPOUT0_Pos + epnum); - NRF_USBD->EPOUTEN |= TU_BIT(epnum); - - // Write any value to SIZE register will allow nRF to ACK/accept data - NRF_USBD->SIZE.EPOUT[epnum] = 0; - }else - { - NRF_USBD->INTENSET = TU_BIT(USBD_INTEN_ENDEPIN0_Pos + epnum); - NRF_USBD->EPINEN |= TU_BIT(epnum); - } - } - else - { - TU_ASSERT(epnum == EP_ISO_NUM); - if (dir == TUSB_DIR_OUT) - { - // SPLIT ISO buffer when ISO IN endpoint is already opened. - if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_IN].mps) NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN; - - // Clear old events - NRF_USBD->EVENTS_ENDISOOUT = 0; - - // Clear SOF event in case interrupt was not enabled yet. - if ((NRF_USBD->INTEN & USBD_INTEN_SOF_Msk) == 0) NRF_USBD->EVENTS_SOF = 0; - - // Enable SOF and ISOOUT interrupts, and ISOOUT endpoint. - NRF_USBD->INTENSET = USBD_INTENSET_ENDISOOUT_Msk | USBD_INTENSET_SOF_Msk; - NRF_USBD->EPOUTEN |= USBD_EPOUTEN_ISOOUT_Msk; - } - else - { - NRF_USBD->EVENTS_ENDISOIN = 0; - - // SPLIT ISO buffer when ISO OUT endpoint is already opened. - if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_OUT].mps) NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN; - - // Clear SOF event in case interrupt was not enabled yet. - if ((NRF_USBD->INTEN & USBD_INTEN_SOF_Msk) == 0) NRF_USBD->EVENTS_SOF = 0; - - // Enable SOF and ISOIN interrupts, and ISOIN endpoint. - NRF_USBD->INTENSET = USBD_INTENSET_ENDISOIN_Msk | USBD_INTENSET_SOF_Msk; - NRF_USBD->EPINEN |= USBD_EPINEN_ISOIN_Msk; - } - } - __ISB(); __DSB(); - - return true; -} - -void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr) -{ - (void) rhport; - - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - if (epnum != EP_ISO_NUM) - { - // CBI - if (dir == TUSB_DIR_OUT) - { - NRF_USBD->INTENCLR = TU_BIT(USBD_INTEN_ENDEPOUT0_Pos + epnum); - NRF_USBD->EPOUTEN &= ~TU_BIT(epnum); - } - else - { - NRF_USBD->INTENCLR = TU_BIT(USBD_INTEN_ENDEPIN0_Pos + epnum); - NRF_USBD->EPINEN &= ~TU_BIT(epnum); - } - } - else - { - _dcd.xfer[EP_ISO_NUM][dir].mps = 0; - // ISO - if (dir == TUSB_DIR_OUT) - { - NRF_USBD->INTENCLR = USBD_INTENCLR_ENDISOOUT_Msk; - NRF_USBD->EPOUTEN &= ~USBD_EPOUTEN_ISOOUT_Msk; - NRF_USBD->EVENTS_ENDISOOUT = 0; - } - else - { - NRF_USBD->INTENCLR = USBD_INTENCLR_ENDISOIN_Msk; - NRF_USBD->EPINEN &= ~USBD_EPINEN_ISOIN_Msk; - } - // One of the ISO endpoints closed, no need to split buffers any more. - NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_OneDir; - // When both ISO endpoint are close there is no need for SOF any more. - if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_IN].mps + _dcd.xfer[EP_ISO_NUM][TUSB_DIR_OUT].mps == 0) NRF_USBD->INTENCLR = USBD_INTENCLR_SOF_Msk; - } - __ISB(); __DSB(); -} - -bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) -{ - (void) rhport; - - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - xfer_td_t* xfer = get_td(epnum, dir); - - xfer->buffer = buffer; - xfer->total_len = total_bytes; - xfer->actual_len = 0; - - // Control endpoint with zero-length packet and opposite direction to 1st request byte --> status stage - bool const control_status = (epnum == 0 && total_bytes == 0 && dir != tu_edpt_dir(NRF_USBD->BMREQUESTTYPE)); - - if ( control_status ) - { - // Status Phase also requires EasyDMA has to be available as well !!!! - start_ep0_task(&NRF_USBD->TASKS_EP0STATUS); - - // The nRF doesn't interrupt on status transmit so we queue up a success response. - dcd_event_xfer_complete(0, ep_addr, 0, XFER_RESULT_SUCCESS, is_in_isr()); - } - else if ( dir == TUSB_DIR_OUT ) - { - if ( epnum == 0 ) - { - // Accept next Control Out packet. TASKS_EP0RCVOUT also require EasyDMA - start_ep0_task(&NRF_USBD->TASKS_EP0RCVOUT); - }else - { - if ( xfer->data_received ) - { - // Data may already be received previously - xfer->data_received = false; - - // start DMA to copy to SRAM - xact_out_dma(epnum); - } - else - { - // nRF auto accept next Bulk/Interrupt OUT packet - // nothing to do - } - } - } - else - { - // Start DMA to copy data from RAM -> Endpoint - xact_in_dma(epnum); - } - - return true; -} - -void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr) -{ - (void) rhport; - uint8_t const epnum = tu_edpt_number(ep_addr); - - if ( epnum == 0 ) - { - NRF_USBD->TASKS_EP0STALL = 1; - }else if (epnum != EP_ISO_NUM) - { - NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_Stall << USBD_EPSTALL_STALL_Pos) | ep_addr; - } - - __ISB(); __DSB(); -} - -void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr) -{ - (void) rhport; - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - if ( epnum != 0 && epnum != EP_ISO_NUM ) - { - // clear stall - NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_UnStall << USBD_EPSTALL_STALL_Pos) | ep_addr; - - // reset data toggle to DATA0 - NRF_USBD->DTOGGLE = (USBD_DTOGGLE_VALUE_Data0 << USBD_DTOGGLE_VALUE_Pos) | ep_addr; - - // Write any value to SIZE register will allow nRF to ACK/accept data - // Drop any pending data - if (dir == TUSB_DIR_OUT) NRF_USBD->SIZE.EPOUT[epnum] = 0; - - __ISB(); __DSB(); - } -} - -/*------------------------------------------------------------------*/ -/* Interrupt Handler - *------------------------------------------------------------------*/ -void bus_reset(void) -{ - // 6.35.6 USB controller automatically disabled all endpoints (except control) - // i.e EPOUTEN and EPINEN and reset USBADDR to 0 - for(int i=0; i<8; i++) - { - NRF_USBD->TASKS_STARTEPIN[i] = 0; - NRF_USBD->TASKS_STARTEPOUT[i] = 0; - } - - NRF_USBD->TASKS_STARTISOIN = 0; - NRF_USBD->TASKS_STARTISOOUT = 0; - - // Clear USB Event Interrupt - NRF_USBD->EVENTS_USBEVENT = 0; - NRF_USBD->EVENTCAUSE |= NRF_USBD->EVENTCAUSE; - - // Reset interrupt - NRF_USBD->INTENCLR = NRF_USBD->INTEN; - NRF_USBD->INTENSET = USBD_INTEN_USBRESET_Msk | USBD_INTEN_USBEVENT_Msk | USBD_INTEN_EPDATA_Msk | - USBD_INTEN_EP0SETUP_Msk | USBD_INTEN_EP0DATADONE_Msk | USBD_INTEN_ENDEPIN0_Msk | USBD_INTEN_ENDEPOUT0_Msk; - - tu_varclr(&_dcd); - _dcd.xfer[0][TUSB_DIR_IN].mps = MAX_PACKET_SIZE; - _dcd.xfer[0][TUSB_DIR_OUT].mps = MAX_PACKET_SIZE; -} - -void dcd_int_handler(uint8_t rhport) -{ - (void) rhport; - - uint32_t const inten = NRF_USBD->INTEN; - uint32_t int_status = 0; - - volatile uint32_t* regevt = &NRF_USBD->EVENTS_USBRESET; - - for(uint8_t i=0; i<USBD_INTEN_EPDATA_Pos+1; i++) - { - if ( tu_bit_test(inten, i) && regevt[i] ) - { - int_status |= TU_BIT(i); - - // event clear - regevt[i] = 0; - __ISB(); __DSB(); - } - } - - if ( int_status & USBD_INTEN_USBRESET_Msk ) - { - bus_reset(); - dcd_event_bus_reset(0, TUSB_SPEED_FULL, true); - } - - // ISOIN: Data was moved to endpoint buffer, client will be notified in SOF - if ( int_status & USBD_INTEN_ENDISOIN_Msk ) - { - xfer_td_t* xfer = get_td(EP_ISO_NUM, TUSB_DIR_IN); - - xfer->actual_len = NRF_USBD->ISOIN.AMOUNT; - // Data transferred from RAM to endpoint output buffer. - // Next transfer can be scheduled after SOF. - xfer->iso_in_transfer_ready = true; - } - - if ( int_status & USBD_INTEN_SOF_Msk ) - { - bool iso_enabled = false; - - // ISOOUT: Transfer data gathered in previous frame from buffer to RAM - if (NRF_USBD->EPOUTEN & USBD_EPOUTEN_ISOOUT_Msk) - { - iso_enabled = true; - xact_out_dma(EP_ISO_NUM); - } - - // ISOIN: Notify client that data was transferred - if (NRF_USBD->EPINEN & USBD_EPINEN_ISOIN_Msk) - { - iso_enabled = true; - - xfer_td_t* xfer = get_td(EP_ISO_NUM, TUSB_DIR_IN); - if ( xfer->iso_in_transfer_ready ) - { - xfer->iso_in_transfer_ready = false; - dcd_event_xfer_complete(0, EP_ISO_NUM | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true); - } - } - - if ( !iso_enabled ) - { - // ISO endpoint is not used, SOF is only enabled one-time for remote wakeup - // so we disable it now - NRF_USBD->INTENCLR = USBD_INTENSET_SOF_Msk; - } - - dcd_event_bus_signal(0, DCD_EVENT_SOF, true); - } - - if ( int_status & USBD_INTEN_USBEVENT_Msk ) - { - TU_LOG(2, "EVENTCAUSE = 0x%04lX\r\n", NRF_USBD->EVENTCAUSE); - - enum { EVT_CAUSE_MASK = USBD_EVENTCAUSE_SUSPEND_Msk | USBD_EVENTCAUSE_RESUME_Msk | USBD_EVENTCAUSE_USBWUALLOWED_Msk }; - uint32_t const evt_cause = NRF_USBD->EVENTCAUSE & EVT_CAUSE_MASK; - NRF_USBD->EVENTCAUSE = evt_cause; // clear interrupt - - if ( evt_cause & USBD_EVENTCAUSE_SUSPEND_Msk ) - { - // Put controller into low power mode - // Leave HFXO disable to application, since it may be used by other peripherals - NRF_USBD->LOWPOWER = 1; - - dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true); - } - - if ( evt_cause & USBD_EVENTCAUSE_USBWUALLOWED_Msk ) - { - // USB is out of low power mode, and wakeup is allowed - // Initiate RESUME signal - NRF_USBD->DPDMVALUE = USBD_DPDMVALUE_STATE_Resume; - NRF_USBD->TASKS_DPDMDRIVE = 1; - - // There is no Resume interrupt for remote wakeup, enable SOF for to report bus ready state - // Clear SOF event in case interrupt was not enabled yet. - if ((NRF_USBD->INTEN & USBD_INTEN_SOF_Msk) == 0) NRF_USBD->EVENTS_SOF = 0; - NRF_USBD->INTENSET = USBD_INTENSET_SOF_Msk; - } - - if ( evt_cause & USBD_EVENTCAUSE_RESUME_Msk ) - { - dcd_event_bus_signal(0, DCD_EVENT_RESUME, true); - } - } - - // Setup tokens are specific to the Control endpoint. - if ( int_status & USBD_INTEN_EP0SETUP_Msk ) - { - uint8_t const setup[8] = - { - NRF_USBD->BMREQUESTTYPE , NRF_USBD->BREQUEST, NRF_USBD->WVALUEL , NRF_USBD->WVALUEH, - NRF_USBD->WINDEXL , NRF_USBD->WINDEXH , NRF_USBD->WLENGTHL, NRF_USBD->WLENGTHH - }; - - // nrf5x hw auto handle set address, there is no need to inform usb stack - tusb_control_request_t const * request = (tusb_control_request_t const *) setup; - - if ( !(TUSB_REQ_RCPT_DEVICE == request->bmRequestType_bit.recipient && - TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type && - TUSB_REQ_SET_ADDRESS == request->bRequest) ) - { - dcd_event_setup_received(0, setup, true); - } - } - - if ( int_status & EDPT_END_ALL_MASK ) - { - // DMA complete move data from SRAM -> Endpoint - edpt_dma_end(); - } - - //--------------------------------------------------------------------+ - /* Control/Bulk/Interrupt (CBI) Transfer - * - * Data flow is: - * (bus) (dma) - * Host <-------> Endpoint <-------> RAM - * - * For CBI OUT: - * - Host -> Endpoint - * EPDATA (or EP0DATADONE) interrupted, check EPDATASTATUS.EPOUT[i] - * to start DMA. For Bulk/Interrupt, this step can occur automatically (without sw), - * which means data may or may not be ready (data_received flag). - * - Endpoint -> RAM - * ENDEPOUT[i] interrupted, transaction complete, sw prepare next transaction - * - * For CBI IN: - * - RAM -> Endpoint - * ENDEPIN[i] interrupted indicate DMA is complete. HW will start - * to move data to host - * - Endpoint -> Host - * EPDATA (or EP0DATADONE) interrupted, check EPDATASTATUS.EPIN[i]. - * Transaction is complete, sw prepare next transaction - * - * Note: in both Control In and Out of Data stage from Host <-> Endpoint - * EP0DATADONE will be set as interrupt source - */ - //--------------------------------------------------------------------+ - - /* CBI OUT: Endpoint -> SRAM (aka transaction complete) - * Note: Since nRF controller auto ACK next packet without SW awareness - * We must handle this stage before Host -> Endpoint just in case 2 event happens at once - * - * ISO OUT: Transaction must fit in single packet, it can be shorter then total - * len if Host decides to sent fewer bytes, it this case transaction is also - * complete and next transfer is not initiated here like for CBI. - */ - for(uint8_t epnum=0; epnum<EP_CBI_COUNT+1; epnum++) - { - if ( tu_bit_test(int_status, USBD_INTEN_ENDEPOUT0_Pos+epnum)) - { - xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT); - uint8_t const xact_len = NRF_USBD->EPOUT[epnum].AMOUNT; - - // Transfer complete if transaction len < Max Packet Size or total len is transferred - if ( (epnum != EP_ISO_NUM) && (xact_len == xfer->mps) && (xfer->actual_len < xfer->total_len) ) - { - if ( epnum == 0 ) - { - // Accept next Control Out packet. TASKS_EP0RCVOUT also require EasyDMA - if ( _dcd.dma_pending ) - { - // use usbd task to defer later - usbd_defer_func( (osal_task_func_t) start_ep0_task, (void*) &NRF_USBD->TASKS_EP0RCVOUT, true ); - }else - { - start_ep0_task(&NRF_USBD->TASKS_EP0RCVOUT); - } - }else - { - // nRF auto accept next Bulk/Interrupt OUT packet - // nothing to do - } - }else - { - xfer->total_len = xfer->actual_len; - - // CBI OUT complete - dcd_event_xfer_complete(0, epnum, xfer->actual_len, XFER_RESULT_SUCCESS, true); - } - } - - // Ended event for CBI IN : nothing to do - } - - // Endpoint <-> Host ( In & OUT ) - if ( int_status & (USBD_INTEN_EPDATA_Msk | USBD_INTEN_EP0DATADONE_Msk) ) - { - uint32_t data_status = NRF_USBD->EPDATASTATUS; - NRF_USBD->EPDATASTATUS = data_status; - __ISB(); __DSB(); - - // EP0DATADONE is set with either Control Out on IN Data - // Since EPDATASTATUS cannot be used to determine whether it is control OUT or IN. - // We will use BMREQUESTTYPE in setup packet to determine the direction - bool const is_control_in = (int_status & USBD_INTEN_EP0DATADONE_Msk) && (NRF_USBD->BMREQUESTTYPE & TUSB_DIR_IN_MASK); - bool const is_control_out = (int_status & USBD_INTEN_EP0DATADONE_Msk) && !(NRF_USBD->BMREQUESTTYPE & TUSB_DIR_IN_MASK); - - // CBI In: Endpoint -> Host (transaction complete) - for(uint8_t epnum=0; epnum<EP_CBI_COUNT; epnum++) - { - if ( tu_bit_test(data_status, epnum) || (epnum == 0 && is_control_in) ) - { - xfer_td_t* xfer = get_td(epnum, TUSB_DIR_IN); - - xfer->actual_len += NRF_USBD->EPIN[epnum].MAXCNT; - - if ( xfer->actual_len < xfer->total_len ) - { - // Start DMA to copy next data packet - xact_in_dma(epnum); - } else - { - // CBI IN complete - dcd_event_xfer_complete(0, epnum | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true); - } - } - } - - // CBI OUT: Host -> Endpoint - for(uint8_t epnum=0; epnum<EP_CBI_COUNT; epnum++) - { - if ( tu_bit_test(data_status, 16+epnum) || (epnum == 0 && is_control_out) ) - { - xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT); - - if (xfer->actual_len < xfer->total_len) - { - xact_out_dma(epnum); - }else - { - // Data overflow !!! Nah, nRF will auto accept next Bulk/Interrupt OUT packet - // Mark this endpoint with data received - xfer->data_received = true; - } - } - } - } -} - -//--------------------------------------------------------------------+ -// HFCLK helper -//--------------------------------------------------------------------+ -#ifdef SOFTDEVICE_PRESENT - -// For enable/disable hfclk with SoftDevice -#include "nrf_mbr.h" -#include "nrf_sdm.h" -#include "nrf_soc.h" - -#ifndef SD_MAGIC_NUMBER - #define SD_MAGIC_NUMBER 0x51B1E5DB -#endif - -static inline bool is_sd_existed(void) -{ - return *((uint32_t*)(SOFTDEVICE_INFO_STRUCT_ADDRESS+4)) == SD_MAGIC_NUMBER; -} - -// check if SD is existed and enabled -static inline bool is_sd_enabled(void) -{ - if ( !is_sd_existed() ) return false; - - uint8_t sd_en = false; - (void) sd_softdevice_is_enabled(&sd_en); - return sd_en; -} -#endif - -static bool hfclk_running(void) -{ -#ifdef SOFTDEVICE_PRESENT - if ( is_sd_enabled() ) - { - uint32_t is_running = 0; - (void) sd_clock_hfclk_is_running(&is_running); - return (is_running ? true : false); - } -#endif - - return nrf_clock_hf_is_running(NRF_CLOCK, NRF_CLOCK_HFCLK_HIGH_ACCURACY); -} - -static void hfclk_enable(void) -{ - // already running, nothing to do - if ( hfclk_running() ) return; - -#ifdef SOFTDEVICE_PRESENT - if ( is_sd_enabled() ) - { - (void)sd_clock_hfclk_request(); - return; - } -#endif - - nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_HFCLKSTARTED); - nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_HFCLKSTART); -} - -static void hfclk_disable(void) -{ -#ifdef SOFTDEVICE_PRESENT - if ( is_sd_enabled() ) - { - (void)sd_clock_hfclk_release(); - return; - } -#endif - - nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_HFCLKSTOP); -} - -// Power & Clock Peripheral on nRF5x to manage USB -// -// USB Bus power is managed by Power module, there are 3 VBUS power events: -// Detected, Ready, Removed. Upon these power events, This function will -// enable ( or disable ) usb & hfclk peripheral, set the usb pin pull up -// accordingly to the controller Startup/Standby Sequence in USBD 51.4 specs. -// -// Therefore this function must be called to handle USB power event by -// - nrfx_power_usbevt_init() : if Softdevice is not used or enabled -// - SoftDevice SOC event : if SD is used and enabled -void tusb_hal_nrf_power_event (uint32_t event) -{ - // Value is chosen to be as same as NRFX_POWER_USB_EVT_* in nrfx_power.h - enum { - USB_EVT_DETECTED = 0, - USB_EVT_REMOVED = 1, - USB_EVT_READY = 2 - }; - -#if CFG_TUSB_DEBUG >= 2 - const char* const power_evt_str[] = { "Detected", "Removed", "Ready" }; - TU_LOG(2, "Power USB event: %s\r\n", power_evt_str[event]); -#endif - - switch ( event ) - { - case USB_EVT_DETECTED: - if ( !NRF_USBD->ENABLE ) - { - // Prepare for receiving READY event: disable interrupt since we will blocking wait - NRF_USBD->INTENCLR = USBD_INTEN_USBEVENT_Msk; - NRF_USBD->EVENTCAUSE = USBD_EVENTCAUSE_READY_Msk; - __ISB(); __DSB(); // for sync - -#ifdef NRF52_SERIES // NRF53 does not need this errata - // ERRATA 171, 187, 166 - if ( nrfx_usbd_errata_187() ) - { - // CRITICAL_REGION_ENTER(); - if ( *((volatile uint32_t *) (0x4006EC00)) == 0x00000000 ) - { - *((volatile uint32_t *) (0x4006EC00)) = 0x00009375; - *((volatile uint32_t *) (0x4006ED14)) = 0x00000003; - *((volatile uint32_t *) (0x4006EC00)) = 0x00009375; - } - else - { - *((volatile uint32_t *) (0x4006ED14)) = 0x00000003; - } - // CRITICAL_REGION_EXIT(); - } - - if ( nrfx_usbd_errata_171() ) - { - // CRITICAL_REGION_ENTER(); - if ( *((volatile uint32_t *) (0x4006EC00)) == 0x00000000 ) - { - *((volatile uint32_t *) (0x4006EC00)) = 0x00009375; - *((volatile uint32_t *) (0x4006EC14)) = 0x000000C0; - *((volatile uint32_t *) (0x4006EC00)) = 0x00009375; - } - else - { - *((volatile uint32_t *) (0x4006EC14)) = 0x000000C0; - } - // CRITICAL_REGION_EXIT(); - } -#endif - - // Enable the peripheral (will cause Ready event) - NRF_USBD->ENABLE = 1; - __ISB(); __DSB(); // for sync - - // Enable HFCLK - hfclk_enable(); - } - break; - - case USB_EVT_READY: - // Skip if pull-up is enabled and HCLK is already running. - // Application probably call this more than necessary. - if ( NRF_USBD->USBPULLUP && hfclk_running() ) break; - - // Waiting for USBD peripheral enabled - while ( !(USBD_EVENTCAUSE_READY_Msk & NRF_USBD->EVENTCAUSE) ) { } - - NRF_USBD->EVENTCAUSE = USBD_EVENTCAUSE_READY_Msk; - __ISB(); __DSB(); // for sync - -#ifdef NRF52_SERIES - if ( nrfx_usbd_errata_171() ) - { - // CRITICAL_REGION_ENTER(); - if ( *((volatile uint32_t *) (0x4006EC00)) == 0x00000000 ) - { - *((volatile uint32_t *) (0x4006EC00)) = 0x00009375; - *((volatile uint32_t *) (0x4006EC14)) = 0x00000000; - *((volatile uint32_t *) (0x4006EC00)) = 0x00009375; - } - else - { - *((volatile uint32_t *) (0x4006EC14)) = 0x00000000; - } - - // CRITICAL_REGION_EXIT(); - } - - if ( nrfx_usbd_errata_187() ) - { - // CRITICAL_REGION_ENTER(); - if ( *((volatile uint32_t *) (0x4006EC00)) == 0x00000000 ) - { - *((volatile uint32_t *) (0x4006EC00)) = 0x00009375; - *((volatile uint32_t *) (0x4006ED14)) = 0x00000000; - *((volatile uint32_t *) (0x4006EC00)) = 0x00009375; - } - else - { - *((volatile uint32_t *) (0x4006ED14)) = 0x00000000; - } - // CRITICAL_REGION_EXIT(); - } - - if ( nrfx_usbd_errata_166() ) - { - *((volatile uint32_t *) (NRF_USBD_BASE + 0x800)) = 0x7E3; - *((volatile uint32_t *) (NRF_USBD_BASE + 0x804)) = 0x40; - - __ISB(); __DSB(); - } -#endif - - // ISO buffer Lower half for IN, upper half for OUT - NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN; - - // Enable bus-reset interrupt - NRF_USBD->INTENSET = USBD_INTEN_USBRESET_Msk; - - // Enable interrupt, priorities should be set by application - NVIC_ClearPendingIRQ(USBD_IRQn); - NVIC_EnableIRQ(USBD_IRQn); - - // Wait for HFCLK - while ( !hfclk_running() ) { } - - // Enable pull up - NRF_USBD->USBPULLUP = 1; - __ISB(); __DSB(); // for sync - break; - - case USB_EVT_REMOVED: - if ( NRF_USBD->ENABLE ) - { - // Abort all transfers - - // Disable pull up - NRF_USBD->USBPULLUP = 0; - __ISB(); __DSB(); // for sync - - // Disable Interrupt - NVIC_DisableIRQ(USBD_IRQn); - - // disable all interrupt - NRF_USBD->INTENCLR = NRF_USBD->INTEN; - - NRF_USBD->ENABLE = 0; - __ISB(); __DSB(); // for sync - - hfclk_disable(); - - dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, is_in_isr()); - } - break; - - default: break; - } -} - -#endif |