Merge pull request #1101 from kasjer/kasjer/da146xx-bus-reset-sleep

da146xx bus reset and sleep reworked
This commit is contained in:
Ha Thach 2021-10-13 16:33:54 +07:00 committed by GitHub
commit 9da234cd7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 100 additions and 38 deletions

View File

@ -60,10 +60,17 @@
#define EP_MAX 4
// Node functional states
#define NFSR_NODE_RESET 0
#define NFSR_NODE_RESUME 1
#define NFSR_NODE_OPERATIONAL 2
#define NFSR_NODE_SUSPEND 3
// Those two following states are added to allow going out of sleep mode
// using frame interrupt. On remove wakeup RESUME state must be kept for
// at least 1ms. It is accomplished by using FRAME interrupt that goes
// through those two fake states before entering OPERATIONAL state.
#define NFSR_NODE_WAKING (0x10 | (NFSR_NODE_RESUME))
#define NFSR_NODE_WAKING2 (0x20 | (NFSR_NODE_RESUME))
static TU_ATTR_ALIGNED(4) uint8_t _setup_packet[8];
@ -224,7 +231,7 @@ static struct
{
bool vbus_present;
bool init_called;
bool in_reset;
uint8_t nfsr;
xfer_ctl_t xfer_status[EP_MAX][2];
// Endpoints that use DMA, one for each direction
uint8_t dma_ep[2];
@ -266,6 +273,15 @@ static const tusb_desc_endpoint_t ep0IN_desc =
#define XFER_CTL_BASE(_ep, _dir) &_dcd.xfer_status[_ep][_dir]
static void set_nfsr(uint8_t val)
{
_dcd.nfsr = val;
// Write only lower 2 bits to register, higher bits are used
// to count down till OPERATIONAL state can be entered when
// remote wakeup activated.
USB->USB_NFSR_REG = val & 3;
}
static void fill_tx_fifo(xfer_ctl_t * xfer)
{
int left_to_send;
@ -635,60 +651,81 @@ static void handle_tx_ev(void)
handle_epx_tx_ev(XFER_CTL_BASE(3, TUSB_DIR_IN));
}
static uint32_t check_reset_end(uint32_t alt_ev)
{
if (_dcd.nfsr == NFSR_NODE_RESET)
{
if (GET_BIT(alt_ev, USB_USB_ALTEV_REG_USB_RESET))
{
// Could be still in reset, but since USB_M_RESET is disabled it can
// be also old reset state that was not cleared yet.
// If (after reading USB_ALTEV_REG register again) bit is cleared
// reset state just ended.
// Keep non-reset bits combined from two previous ALTEV read and
// one from the next line.
alt_ev = (alt_ev & ~USB_USB_ALTEV_REG_USB_RESET_Msk) | USB->USB_ALTEV_REG;
}
if (GET_BIT(alt_ev, USB_USB_ALTEV_REG_USB_RESET) == 0)
{
USB->USB_ALTMSK_REG = USB_USB_ALTMSK_REG_USB_M_RESET_Msk |
USB_USB_ALTEV_REG_USB_SD3_Msk;
set_nfsr(NFSR_NODE_OPERATIONAL);
dcd_edpt_open(0, &ep0OUT_desc);
dcd_edpt_open(0, &ep0IN_desc);
}
}
return alt_ev;
}
static void handle_bus_reset(void)
{
uint32_t alt_ev;
USB->USB_NFSR_REG = 0;
USB->USB_FAR_REG = 0x80;
USB->USB_ALTMSK_REG = 0;
USB->USB_NFSR_REG = NFSR_NODE_RESET;
USB->USB_TXMSK_REG = 0;
USB->USB_RXMSK_REG = 0;
(void)USB->USB_ALTEV_REG;
_dcd.in_reset = true;
set_nfsr(NFSR_NODE_RESET);
dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
USB->USB_DMA_CTRL_REG = 0;
USB->USB_MAMSK_REG = USB_USB_MAMSK_REG_USB_M_INTR_Msk |
#if USE_SOF
USB_USB_MAMSK_REG_USB_M_FRAME_Msk |
#endif
USB_USB_MAMSK_REG_USB_M_WARN_Msk |
USB_USB_MAMSK_REG_USB_M_ALT_Msk;
USB->USB_NFSR_REG = NFSR_NODE_OPERATIONAL;
USB->USB_ALTMSK_REG = USB_USB_ALTMSK_REG_USB_M_SD3_Msk |
USB_USB_ALTMSK_REG_USB_M_RESUME_Msk;
// There is no information about end of reset state
// USB_FRAME event will be used to enable reset detection again
REG_SET_BIT(USB_MAEV_REG, USB_FRAME);
dcd_edpt_open (0, &ep0OUT_desc);
dcd_edpt_open (0, &ep0IN_desc);
USB->USB_ALTMSK_REG = USB_USB_ALTMSK_REG_USB_M_RESUME_Msk;
alt_ev = USB->USB_ALTEV_REG;
check_reset_end(alt_ev);
}
static void handle_alt_ev(void)
{
uint32_t alt_ev = USB->USB_ALTEV_REG;
if (GET_BIT(alt_ev, USB_USB_ALTEV_REG_USB_RESET))
alt_ev = check_reset_end(alt_ev);
if (GET_BIT(alt_ev, USB_USB_ALTEV_REG_USB_RESET) && _dcd.nfsr != NFSR_NODE_RESET)
{
handle_bus_reset();
}
else
else if (GET_BIT(alt_ev, USB_USB_ALTEV_REG_USB_RESUME))
{
if (GET_BIT(alt_ev, USB_USB_ALTEV_REG_USB_RESUME))
if (USB->USB_NFSR_REG == NFSR_NODE_SUSPEND)
{
USB->USB_NFSR_REG = NFSR_NODE_OPERATIONAL;
USB->USB_ALTMSK_REG &= ~USB_USB_ALTMSK_REG_USB_M_RESUME_Msk;
USB->USB_ALTMSK_REG |= USB_USB_ALTMSK_REG_USB_M_SD3_Msk;
set_nfsr(NFSR_NODE_OPERATIONAL);
USB->USB_ALTMSK_REG = USB_USB_ALTMSK_REG_USB_M_RESET_Msk |
USB_USB_ALTMSK_REG_USB_M_SD3_Msk;
dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
}
if (GET_BIT(alt_ev, USB_USB_ALTEV_REG_USB_SD3))
{
USB->USB_NFSR_REG = NFSR_NODE_SUSPEND;
USB->USB_ALTMSK_REG |= USB_USB_ALTMSK_REG_USB_M_RESUME_Msk;
USB->USB_ALTMSK_REG &= ~USB_USB_ALTMSK_REG_USB_M_SD3_Msk | USB_USB_ALTMSK_REG_USB_M_SD5_Msk;
dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
}
}
else if (GET_BIT(alt_ev, USB_USB_ALTEV_REG_USB_SD3))
{
set_nfsr(NFSR_NODE_SUSPEND);
USB->USB_ALTMSK_REG = USB_USB_ALTMSK_REG_USB_M_RESET_Msk |
USB_USB_ALTMSK_REG_USB_M_RESUME_Msk;
dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
}
}
@ -780,6 +817,12 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
void dcd_remote_wakeup(uint8_t rhport)
{
(void)rhport;
if (_dcd.nfsr == NFSR_NODE_SUSPEND)
{
// Enter fake state that will use FRAME interrupt to wait before going operational.
set_nfsr(NFSR_NODE_WAKING);
USB->USB_MAMSK_REG |= USB_USB_MAMSK_REG_USB_M_FRAME_Msk;
}
}
void dcd_connect(uint8_t rhport)
@ -791,16 +834,16 @@ void dcd_connect(uint8_t rhport)
USB->USB_MCTRL_REG = USB_USB_MCTRL_REG_USBEN_Msk;
USB->USB_NFSR_REG = 0;
USB->USB_FAR_REG = 0x80;
USB->USB_NFSR_REG = NFSR_NODE_RESET;
USB->USB_TXMSK_REG = 0;
USB->USB_RXMSK_REG = 0;
USB->USB_MAMSK_REG = USB_USB_MAMSK_REG_USB_M_INTR_Msk |
USB_USB_MAMSK_REG_USB_M_ALT_Msk |
USB_USB_MAMSK_REG_USB_M_WARN_Msk;
USB->USB_ALTMSK_REG = USB_USB_ALTMSK_REG_USB_M_RESET_Msk;
USB->USB_ALTMSK_REG = USB_USB_ALTMSK_REG_USB_M_RESET_Msk |
USB_USB_ALTEV_REG_USB_SD3_Msk;
REG_SET_BIT(USB_MCTRL_REG, USB_NAT);
USB->USB_MCTRL_REG = USB_USB_MCTRL_REG_USBEN_Msk | USB_USB_MCTRL_REG_USB_NAT_Msk;
// Select chosen DMA to be triggered by USB.
DMA->DMA_REQ_MUX_REG = (DMA->DMA_REQ_MUX_REG & ~DA146XX_DMA_USB_MUX_MASK) | DA146XX_DMA_USB_MUX;
@ -1054,7 +1097,7 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
void dcd_int_handler(uint8_t rhport)
{
uint32_t int_status = USB->USB_MAEV_REG;
uint32_t int_status = USB->USB_MAEV_REG & USB->USB_MAMSK_REG;
(void)rhport;
@ -1096,19 +1139,38 @@ void dcd_int_handler(uint8_t rhport)
if (GET_BIT(int_status, USB_USB_MAEV_REG_USB_FRAME))
{
if (_dcd.in_reset)
if (_dcd.nfsr == NFSR_NODE_RESET)
{
// Enable reset detection
_dcd.in_reset = false;
(void)USB->USB_ALTEV_REG;
// During reset FRAME interrupt is enabled to periodically
// check when reset state ends.
// FRAME interrupt is generated every 1ms without host sending
// actual SOF.
check_reset_end(USB_USB_ALTEV_REG_USB_RESET_Msk);
}
else if (_dcd.nfsr == NFSR_NODE_WAKING)
{
// No need to call set_nfsr, just set state
_dcd.nfsr = NFSR_NODE_WAKING2;
}
else if (_dcd.nfsr == NFSR_NODE_WAKING2)
{
// No need to call set_nfsr, just set state
_dcd.nfsr = NFSR_NODE_RESUME;
}
else if (_dcd.nfsr == NFSR_NODE_RESUME)
{
set_nfsr(NFSR_NODE_OPERATIONAL);
}
else
{
#if USE_SOF
dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
#else
// SOF was used to re-enable reset detection
// No need to keep it enabled
USB->USB_MAMSK_REG &= ~USB_USB_MAMSK_REG_USB_M_FRAME_Msk;
// FRAME interrupt was used to re-enable reset detection or remote
// wakeup no need to keep it enabled when USE_SOF is off.
USB->USB_MAMSK_REG &= ~USB_USB_MAMSK_REG_USB_M_FRAME_Msk;
#endif
}
}
if (GET_BIT(int_status, USB_USB_MAEV_REG_USB_TX_EV))