#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"
#include "device/usbd.h"
#include "device/usbd_pvt.h"
#if CFG_TUSB_OS == OPT_OS_MYNEWT
#include "mcu/mcu.h"
#endif
enum
{
MAX_PACKET_SIZE = 64,
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, EP_CBI_COUNT = 8 };
typedef struct
{
uint8_t* buffer;
uint16_t total_len;
volatile uint16_t actual_len;
uint16_t mps;
volatile bool data_received;
bool iso_in_transfer_ready;
} xfer_td_t;
static struct
{
xfer_td_t xfer[EP_CBI_COUNT + 1][2];
volatile bool dma_running;
}_dcd;
#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
TU_ATTR_ALWAYS_INLINE static inline bool is_in_isr(void)
{
return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) ? true : false;
}
static void start_dma(volatile uint32_t* reg_startep)
{
_dcd.dma_running = true;
(*reg_startep) = 1;
__ISB(); __DSB();
if ( (reg_startep == &NRF_USBD->TASKS_EP0STATUS) || (reg_startep == &NRF_USBD->TASKS_EP0RCVOUT) )
{
_dcd.dma_running = false;
}
}
static void edpt_dma_start(volatile uint32_t* reg_startep)
{
if ( is_in_isr() || __get_PRIMASK() || !NVIC_GetEnableIRQ(USBD_IRQn) )
{
if (_dcd.dma_running)
{
usbd_defer_func((osal_task_func_t) edpt_dma_start, (void*) (uintptr_t) reg_startep, true);
}else
{
start_dma(reg_startep);
}
}else
{
uint8_t const rhport = 0;
bool started = false;
while(!started)
{
dcd_int_disable(rhport);
if ( !_dcd.dma_running )
{
start_dma(reg_startep);
started = true;
}
dcd_int_enable(rhport);
}
}
}
static void edpt_dma_end(void)
{
TU_ASSERT(_dcd.dma_running, );
_dcd.dma_running = false;
}
static inline xfer_td_t* get_td(uint8_t epnum, uint8_t dir)
{
return &_dcd.xfer[epnum][dir];
}
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 (xact_len & USBD_SIZE_ISOOUT_ZERO_Msk)
{
xact_len = 0;
}
else
{
NRF_USBD->ISOOUT.PTR = (uint32_t) xfer->buffer;
NRF_USBD->ISOOUT.MAXCNT = xact_len;
edpt_dma_start(&NRF_USBD->TASKS_STARTISOOUT);
}
}
else
{
xact_len = tu_min16((uint16_t) NRF_USBD->SIZE.EPOUT[epnum], xfer->total_len - xfer->actual_len);
NRF_USBD->EPOUT[epnum].PTR = (uint32_t) xfer->buffer;
NRF_USBD->EPOUT[epnum].MAXCNT = xact_len;
edpt_dma_start(&NRF_USBD->TASKS_STARTEPOUT[epnum]);
}
}
static void xact_in_dma(uint8_t epnum)
{
xfer_td_t* xfer = get_td(epnum, TUSB_DIR_IN);
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;
edpt_dma_start(&NRF_USBD->TASKS_STARTEPIN[epnum]);
}
void dcd_init (uint8_t rhport)
{
TU_LOG1("dcd init\r\n");
(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;
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;
NRF_USBD->LOWPOWER = 0;
}
void dcd_disconnect(uint8_t rhport)
{
(void) rhport;
NRF_USBD->USBPULLUP = 0;
dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, false);
}
void dcd_connect(uint8_t rhport)
{
(void) rhport;
NRF_USBD->USBPULLUP = 1;
}
bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
{
(void) rhport;
uint8_t const ep_addr = desc_edpt->bEndpointAddress;
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
_dcd.xfer[epnum][dir].mps = tu_edpt_packet_size(desc_edpt);
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);
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)
{
if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_IN].mps) NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN;
NRF_USBD->EVENTS_ENDISOOUT = 0;
if ((NRF_USBD->INTEN & USBD_INTEN_SOF_Msk) == 0) NRF_USBD->EVENTS_SOF = 0;
NRF_USBD->INTENSET = USBD_INTENSET_ENDISOOUT_Msk | USBD_INTENSET_SOF_Msk;
NRF_USBD->EPOUTEN |= USBD_EPOUTEN_ISOOUT_Msk;
}
else
{
NRF_USBD->EVENTS_ENDISOIN = 0;
if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_OUT].mps) NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN;
if ((NRF_USBD->INTEN & USBD_INTEN_SOF_Msk) == 0) NRF_USBD->EVENTS_SOF = 0;
NRF_USBD->INTENSET = USBD_INTENSET_ENDISOIN_Msk | USBD_INTENSET_SOF_Msk;
NRF_USBD->EPINEN |= USBD_EPINEN_ISOIN_Msk;
}
}
NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_UnStall << USBD_EPSTALL_STALL_Pos) | ep_addr;
NRF_USBD->DTOGGLE = (USBD_DTOGGLE_VALUE_Data0 << USBD_DTOGGLE_VALUE_Pos) | ep_addr;
__ISB(); __DSB();
return true;
}
void dcd_edpt_close_all (uint8_t rhport)
{
dcd_int_disable(rhport);
for ( uint8_t ep = 1; ep < EP_CBI_COUNT; ep++ )
{
NRF_USBD->INTENCLR = TU_BIT(USBD_INTEN_ENDEPOUT0_Pos + ep) | TU_BIT(USBD_INTEN_ENDEPIN0_Pos + ep);
NRF_USBD->TASKS_STARTEPIN[ep] = 0;
NRF_USBD->TASKS_STARTEPOUT[ep] = 0;
tu_memclr(_dcd.xfer[ep], 2*sizeof(xfer_td_t));
}
NRF_USBD->INTENCLR = USBD_INTENCLR_SOF_Msk | USBD_INTENCLR_ENDISOOUT_Msk | USBD_INTENCLR_ENDISOIN_Msk;
NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_OneDir;
NRF_USBD->TASKS_STARTISOIN = 0;
NRF_USBD->TASKS_STARTISOOUT = 0;
tu_memclr(_dcd.xfer[EP_ISO_NUM], 2*sizeof(xfer_td_t));
NRF_USBD->EPOUTEN = 1UL;
NRF_USBD->EPINEN = 1UL;
dcd_int_enable(rhport);
}
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)
{
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;
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;
}
NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_OneDir;
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);
dcd_int_disable(rhport);
xfer->buffer = buffer;
xfer->total_len = total_bytes;
xfer->actual_len = 0;
dcd_int_enable(rhport);
bool const control_status = (epnum == 0 && total_bytes == 0 && dir != tu_edpt_dir(NRF_USBD->BMREQUESTTYPE));
if ( control_status )
{
edpt_dma_start(&NRF_USBD->TASKS_EP0STATUS);
dcd_event_xfer_complete(0, ep_addr, 0, XFER_RESULT_SUCCESS, is_in_isr());
}
else if ( dir == TUSB_DIR_OUT )
{
if ( epnum == 0 )
{
edpt_dma_start(&NRF_USBD->TASKS_EP0RCVOUT);
}else
{
if ( xfer->data_received && xfer->total_len > xfer->actual_len)
{
xfer->data_received = false;
xact_out_dma(epnum);
}
else
{
}
}
}
else
{
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);
uint8_t const dir = tu_edpt_dir(ep_addr);
xfer_td_t* xfer = get_td(epnum, dir);
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;
if ( (dir == TUSB_DIR_OUT) && xfer->data_received )
{
xfer->data_received = false;
xact_out_dma(epnum);
}
}
__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 )
{
NRF_USBD->DTOGGLE = ep_addr;
NRF_USBD->DTOGGLE = (USBD_DTOGGLE_VALUE_Data0 << USBD_DTOGGLE_VALUE_Pos) | ep_addr;
NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_UnStall << USBD_EPSTALL_STALL_Pos) | ep_addr;
if (dir == TUSB_DIR_OUT) NRF_USBD->SIZE.EPOUT[epnum] = 0;
__ISB(); __DSB();
}
}
void bus_reset(void)
{
NRF_USBD->EPOUTEN = 1UL;
NRF_USBD->EPINEN = 1UL;
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;
NRF_USBD->EVENTS_USBEVENT = 0;
NRF_USBD->EVENTCAUSE |= NRF_USBD->EVENTCAUSE;
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);
regevt[i] = 0;
__ISB(); __DSB();
}
}
if ( int_status & USBD_INTEN_USBRESET_Msk )
{
bus_reset();
dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
}
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;
xfer->iso_in_transfer_ready = true;
}
if ( int_status & USBD_INTEN_SOF_Msk )
{
bool iso_enabled = false;
if (NRF_USBD->EPOUTEN & USBD_EPOUTEN_ISOOUT_Msk)
{
iso_enabled = true;
xact_out_dma(EP_ISO_NUM);
}
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 )
{
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;
if ( evt_cause & USBD_EVENTCAUSE_SUSPEND_Msk )
{
NRF_USBD->LOWPOWER = 1;
dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
}
if ( evt_cause & USBD_EVENTCAUSE_USBWUALLOWED_Msk )
{
NRF_USBD->DPDMVALUE = USBD_DPDMVALUE_STATE_Resume;
NRF_USBD->TASKS_DPDMDRIVE = 1;
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);
}
}
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
};
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 )
{
edpt_dma_end();
}
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;
xfer->buffer += xact_len;
xfer->actual_len += xact_len;
if ( (epnum != EP_ISO_NUM) && (xact_len == xfer->mps) && (xfer->actual_len < xfer->total_len) )
{
if ( epnum == 0 )
{
edpt_dma_start(&NRF_USBD->TASKS_EP0RCVOUT);
}else
{
}
}else
{
xfer->total_len = xfer->actual_len;
dcd_event_xfer_complete(0, epnum, xfer->actual_len, XFER_RESULT_SUCCESS, true);
}
}
}
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();
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);
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);
uint8_t const xact_len = NRF_USBD->EPIN[epnum].AMOUNT;
xfer->buffer += xact_len;
xfer->actual_len += xact_len;
if ( xfer->actual_len < xfer->total_len )
{
xact_in_dma(epnum);
} else
{
dcd_event_xfer_complete(0, epnum | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true);
}
}
}
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
{
xfer->data_received = true;
}
}
}
}
}
#ifdef SOFTDEVICE_PRESENT
#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;
}
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)
{
#if CFG_TUSB_OS == OPT_OS_MYNEWT
usb_clock_request();
return;
#else
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);
#endif
}
static void hfclk_disable(void)
{
#if CFG_TUSB_OS == OPT_OS_MYNEWT
usb_clock_release();
return;
#else
#ifdef SOFTDEVICE_PRESENT
if ( is_sd_enabled() )
{
(void)sd_clock_hfclk_release();
return;
}
#endif
nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_HFCLKSTOP);
#endif
}
void tusb_hal_nrf_power_event (uint32_t event)
{
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 )
{
NRF_USBD->INTENCLR = USBD_INTEN_USBEVENT_Msk;
NRF_USBD->EVENTCAUSE = USBD_EVENTCAUSE_READY_Msk;
__ISB(); __DSB();
#ifdef NRF52_SERIES
if ( nrfx_usbd_errata_187() )
{
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;
}
}
if ( nrfx_usbd_errata_171() )
{
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;
}
}
#endif
NRF_USBD->ENABLE = 1;
__ISB(); __DSB();
hfclk_enable();
}
break;
case USB_EVT_READY:
if ( NRF_USBD->USBPULLUP && hfclk_running() ) break;
while ( !(USBD_EVENTCAUSE_READY_Msk & NRF_USBD->EVENTCAUSE) ) { }
NRF_USBD->EVENTCAUSE = USBD_EVENTCAUSE_READY_Msk;
__ISB(); __DSB();
#ifdef NRF52_SERIES
if ( nrfx_usbd_errata_171() )
{
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;
}
}
if ( nrfx_usbd_errata_187() )
{
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;
}
}
if ( nrfx_usbd_errata_166() )
{
*((volatile uint32_t *) (NRF_USBD_BASE + 0x800)) = 0x7E3;
*((volatile uint32_t *) (NRF_USBD_BASE + 0x804)) = 0x40;
__ISB(); __DSB();
}
#endif
NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN;
NRF_USBD->INTENSET = USBD_INTEN_USBRESET_Msk;
NVIC_ClearPendingIRQ(USBD_IRQn);
if (tud_inited())
{
NVIC_EnableIRQ(USBD_IRQn);
}
while ( !hfclk_running() ) { }
NRF_USBD->USBPULLUP = 1;
__ISB(); __DSB(); break;
case USB_EVT_REMOVED:
if ( NRF_USBD->ENABLE )
{
NRF_USBD->USBPULLUP = 0;
__ISB(); __DSB();
NVIC_DisableIRQ(USBD_IRQn);
NRF_USBD->INTENCLR = NRF_USBD->INTEN;
NRF_USBD->ENABLE = 0;
__ISB(); __DSB();
hfclk_disable();
dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, is_in_isr());
}
break;
default: break;
}
}
#endif