diff --git a/src/portable/st/stm32h7/dcd_stm32h7.c b/src/portable/st/stm32h7/dcd_stm32h7.c index 3fde611c2..bdfaea783 100644 --- a/src/portable/st/stm32h7/dcd_stm32h7.c +++ b/src/portable/st/stm32h7/dcd_stm32h7.c @@ -1,7 +1,8 @@ /* * The MIT License (MIT) * - * Copyright (c) 2019 William D. Jones for Adafruit Industries + * Copyright (c) 2018 Scott Shawcroft, 2019 William D. Jones for Adafruit Industries + * 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 @@ -34,10 +35,15 @@ /*------------------------------------------------------------------*/ /* MACRO TYPEDEF CONSTANT ENUM *------------------------------------------------------------------*/ -#define DEVICE_BASE (USB_OTG_DeviceTypeDef *) (USB2_OTG_FS_PERIPH_BASE + USB_OTG_DEVICE_BASE) -#define OUT_EP_BASE (USB_OTG_OUTEndpointTypeDef *) (USB2_OTG_FS_PERIPH_BASE + USB_OTG_OUT_ENDPOINT_BASE) -#define IN_EP_BASE (USB_OTG_INEndpointTypeDef *) (USB2_OTG_FS_PERIPH_BASE + USB_OTG_IN_ENDPOINT_BASE) -#define FIFO_BASE(_x) (volatile uint32_t *) (USB2_OTG_FS_PERIPH_BASE + USB_OTG_FIFO_BASE + (_x) * USB_OTG_FIFO_SIZE) +#define DEVICE_BASE (USB_OTG_DeviceTypeDef *) (USB_OTG_FS_PERIPH_BASE + USB_OTG_DEVICE_BASE) +#define OUT_EP_BASE (USB_OTG_OUTEndpointTypeDef *) (USB_OTG_FS_PERIPH_BASE + USB_OTG_OUT_ENDPOINT_BASE) +#define IN_EP_BASE (USB_OTG_INEndpointTypeDef *) (USB_OTG_FS_PERIPH_BASE + USB_OTG_IN_ENDPOINT_BASE) +#define FIFO_BASE(_x) (volatile uint32_t *) (USB_OTG_FS_PERIPH_BASE + USB_OTG_FIFO_BASE + (_x) * USB_OTG_FIFO_SIZE) + +// TODO Merge with OTG_HS +// Max endpoints for each direction +#define EP_MAX 8 +#define EP_FIFO_SIZE 4096 static TU_ATTR_ALIGNED(4) uint32_t _setup_packet[6]; static uint8_t _setup_offs; // We store up to 3 setup packets. @@ -52,18 +58,16 @@ typedef struct { typedef volatile uint32_t * usb_fifo_t; -xfer_ctl_t xfer_status[4][2]; +xfer_ctl_t xfer_status[EP_MAX][2]; #define XFER_CTL_BASE(_ep, _dir) &xfer_status[_ep][_dir] // Setup the control endpoint 0. -static void bus_reset(void) -{ +static void bus_reset(void) { USB_OTG_DeviceTypeDef * dev = DEVICE_BASE; USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE; - for(int n = 0; n < 4; n++) - { + for(uint8_t n = 0; n < EP_MAX; n++) { out_ep[n].DOEPCTL |= USB_OTG_DOEPCTL_SNAK; } @@ -71,45 +75,45 @@ static void bus_reset(void) dev->DOEPMSK |= USB_OTG_DOEPMSK_STUPM | USB_OTG_DOEPMSK_XFRCM; dev->DIEPMSK |= USB_OTG_DIEPMSK_TOM | USB_OTG_DIEPMSK_XFRCM; - // Peripheral FIFO architecture (Rev6 RM 56.11.1) + // "USB Data FIFOs" section in reference manual + // Peripheral FIFO architecture // - // --------------- 1024 ( 4096 bytes ) - // | IN FIFO 7 | + // --------------- 320 or 1024 ( 1280 or 4096 bytes ) + // | IN FIFO MAX | // --------------- - // | ... | + // | ... | // --------------- y + x + 16 + GRXFSIZ - // | IN FIFO 2 | + // | IN FIFO 2 | // --------------- x + 16 + GRXFSIZ - // | IN FIFO 1 | + // | IN FIFO 1 | // --------------- 16 + GRXFSIZ - // | IN FIFO 0 | + // | IN FIFO 0 | // --------------- GRXFSIZ - // | OUT FIFO | - // | ( Shared ) | + // | OUT FIFO | + // | ( Shared ) | // --------------- 0 // - // FIFO sizes are set up by the following rules (each word 32-bits): - // All EP OUT shared a unique OUT FIFO which uses (based on page 2747 of Rev 6 of reference manual): - // * 10 locations in hardware for setup packets + setup control words - // (up to 3 setup packets). - // * 2 locations for OUT endpoint control words. - // * 16 for largest packet size of 64 bytes. ( TODO Highspeed is 512 bytes) - // * 1 location for global NAK (not required/used here). + // According to "FIFO RAM allocation" section in RM, FIFO RAM are allocated as follows (each word 32-bits): + // - Each EP IN needs at least max packet size, 16 words is sufficient for EP0 IN // - // It is recommended to allocate 2 times the largest packet size, therefore - // Recommended value = 10 + 1 + 2 x (16+2) = 47 --> Let's make it 50 - USB2_OTG_FS->GRXFSIZ = 50; + // - All EP OUT shared a unique OUT FIFO which uses + // * 10 locations in hardware for setup packets + setup control words (up to 3 setup packets). + // * 2 locations for OUT endpoint control words. + // * 16 for largest packet size of 64 bytes. ( TODO Highspeed is 512 bytes) + // * 1 location for global NAK (not required/used here). + // * It is recommended to allocate 2 times the largest packet size, therefore + // Recommended value = 10 + 1 + 2 x (16+2) = 47 --> Let's make it 52 + USB_OTG_FS->GRXFSIZ = 52; // Control IN uses FIFO 0 with 64 bytes ( 16 32-bit word ) - USB2_OTG_FS->DIEPTXF0_HNPTXFSIZ = (16 << USB_OTG_TX0FD_Pos) | (USB2_OTG_FS->GRXFSIZ & 0x0000ffffUL); + USB_OTG_FS->DIEPTXF0_HNPTXFSIZ = (16 << USB_OTG_TX0FD_Pos) | (USB_OTG_FS->GRXFSIZ & 0x0000ffffUL); out_ep[0].DOEPTSIZ |= (3 << USB_OTG_DOEPTSIZ_STUPCNT_Pos); - USB2_OTG_FS->GINTMSK |= USB_OTG_GINTMSK_OEPINT | USB_OTG_GINTMSK_IEPINT; + USB_OTG_FS->GINTMSK |= USB_OTG_GINTMSK_OEPINT | USB_OTG_GINTMSK_IEPINT; } -static void end_of_reset(void) -{ +static void end_of_reset(void) { USB_OTG_DeviceTypeDef * dev = DEVICE_BASE; USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE; // On current silicon on the Full Speed core, speed is fixed to Full Speed. @@ -139,22 +143,23 @@ void dcd_init (uint8_t rhport) { (void) rhport; - // Programming model begins on page 2634 of Rev 6 of reference manual. - USB2_OTG_FS->GAHBCFG |= USB_OTG_GAHBCFG_TXFELVL | USB_OTG_GAHBCFG_GINT; + // Programming model begins in the last section of the chapter on the USB + // peripheral in each Reference Manual. + USB_OTG_FS->GAHBCFG |= USB_OTG_GAHBCFG_TXFELVL | USB_OTG_GAHBCFG_GINT; // No HNP/SRP (no OTG support), program timeout later, turnaround // programmed for 32+ MHz. - USB2_OTG_FS->GUSBCFG |= (0x06 << USB_OTG_GUSBCFG_TRDT_Pos) | USB_OTG_GUSBCFG_PHYSEL; + USB_OTG_FS->GUSBCFG |= (0x06 << USB_OTG_GUSBCFG_TRDT_Pos) | USB_OTG_GUSBCFG_PHYSEL; // Clear all used interrupts - USB2_OTG_FS->GINTSTS |= USB_OTG_GINTSTS_OTGINT | USB_OTG_GINTSTS_MMIS | \ + USB_OTG_FS->GINTSTS |= USB_OTG_GINTSTS_OTGINT | USB_OTG_GINTSTS_MMIS | \ USB_OTG_GINTSTS_USBRST | USB_OTG_GINTSTS_ENUMDNE | \ USB_OTG_GINTSTS_ESUSP | USB_OTG_GINTSTS_USBSUSP | USB_OTG_GINTSTS_SOF; // Required as part of core initialization. Disable OTGINT as we don't use // it right now. TODO: How should mode mismatch be handled? It will cause // the core to stop working/require reset. - USB2_OTG_FS->GINTMSK |= /* USB_OTG_GINTMSK_OTGINT | */ USB_OTG_GINTMSK_MMISM; + USB_OTG_FS->GINTMSK |= /* USB_OTG_GINTMSK_OTGINT | */ USB_OTG_GINTMSK_MMISM; USB_OTG_DeviceTypeDef * dev = DEVICE_BASE; @@ -162,17 +167,18 @@ void dcd_init (uint8_t rhport) // (non zero-length packet), send STALL back and discard. Full speed. dev->DCFG |= USB_OTG_DCFG_NZLSOHSK | (3 << USB_OTG_DCFG_DSPD_Pos); - USB2_OTG_FS->GINTMSK |= USB_OTG_GINTMSK_USBRST | USB_OTG_GINTMSK_ENUMDNEM | \ + USB_OTG_FS->GINTMSK |= USB_OTG_GINTMSK_USBRST | USB_OTG_GINTMSK_ENUMDNEM | \ USB_OTG_GINTMSK_SOFM | USB_OTG_GINTMSK_RXFLVLM /* SB_OTG_GINTMSK_ESUSPM | \ USB_OTG_GINTMSK_USBSUSPM */; - // Enable pullup, enable peripheral. + // Enable VBUS hardware sensing, enable pullup, enable peripheral. #ifdef USB_OTG_GCCFG_VBDEN - USB2_OTG_FS->GCCFG |= USB_OTG_GCCFG_VBDEN | USB_OTG_GCCFG_PWRDWN; + USB_OTG_FS->GCCFG |= USB_OTG_GCCFG_VBDEN | USB_OTG_GCCFG_PWRDWN; #else - USB2_OTG_FS->GCCFG |= USB_OTG_GCCFG_VBUSBSEN | USB_OTG_GCCFG_PWRDWN; + USB_OTG_FS->GCCFG |= USB_OTG_GCCFG_VBUSBSEN | USB_OTG_GCCFG_PWRDWN; #endif + // Soft Connect -> Enable pullup on D+/D-. // This step does not appear to be specified in the programmer's model. dev->DCTL &= ~USB_OTG_DCTL_SDIS; } @@ -227,7 +233,7 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress); // Unsupported endpoint numbers/size. - if((desc_edpt->wMaxPacketSize.size > 64) || (epnum > 7)) { + if((desc_edpt->wMaxPacketSize.size > 64) || (epnum > EP_MAX)) { return false; } @@ -240,24 +246,27 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) desc_edpt->wMaxPacketSize.size << USB_OTG_DOEPCTL_MPSIZ_Pos; dev->DAINTMSK |= (1 << (USB_OTG_DAINTMSK_OEPM_Pos + epnum)); } else { - // Peripheral FIFO architecture (Rev6 RM 56.11.1) + // "USB Data FIFOs" section in reference manual + // Peripheral FIFO architecture // - // --------------- 1024 ( 4096 bytes ) - // | IN FIFO 7 | + // --------------- 320 or 1024 ( 1280 or 4096 bytes ) + // | IN FIFO MAX | // --------------- - // | ... | + // | ... | // --------------- y + x + 16 + GRXFSIZ - // | IN FIFO 2 | + // | IN FIFO 2 | // --------------- x + 16 + GRXFSIZ - // | IN FIFO 1 | + // | IN FIFO 1 | // --------------- 16 + GRXFSIZ - // | IN FIFO 0 | + // | IN FIFO 0 | // --------------- GRXFSIZ - // | OUT FIFO | - // | ( Shared ) | + // | OUT FIFO | + // | ( Shared ) | // --------------- 0 // - // Since OUT FIFO = 50, FIFO 0 = 16, average of FIFOx = (1024-50-16) / 7 = 136 ~ 130 + // Since OUT FIFO = GRXFSIZ, FIFO 0 = 16, for simplicity, we equally allocated for the rest of endpoints + // - Size : (FIFO_SIZE/4 - GRXFSIZ - 16) / (EP_MAX-1) + // - Offset: GRXFSIZ + 16 + Size*(epnum-1) in_ep[epnum].DIEPCTL |= (1 << USB_OTG_DIEPCTL_USBAEP_Pos) | \ (epnum - 1) << USB_OTG_DIEPCTL_TXFNUM_Pos | \ @@ -267,9 +276,10 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) dev->DAINTMSK |= (1 << (USB_OTG_DAINTMSK_IEPM_Pos + epnum)); // Both TXFD and TXSA are in unit of 32-bit words - uint16_t const fifo_size = 130; - uint32_t const fifo_offset = (USB2_OTG_FS->GRXFSIZ & 0x0000ffff) + 16 + fifo_size*(epnum-1); - USB2_OTG_FS->DIEPTXF[epnum - 1] = (130 << USB_OTG_DIEPTXF_INEPTXFD_Pos) | fifo_offset; + uint16_t const allocated_size = (USB_OTG_FS->GRXFSIZ & 0x0000ffff) + 16; + uint16_t const fifo_size = (EP_FIFO_SIZE/4 - allocated_size) / (EP_MAX-1); + uint32_t const fifo_offset = allocated_size + fifo_size*(epnum-1); + USB_OTG_FS->DIEPTXF[epnum - 1] = (fifo_size << USB_OTG_DIEPTXF_INEPTXFD_Pos) | fifo_offset; } return true; @@ -345,9 +355,9 @@ void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr) } // Flush the FIFO, and wait until we have confirmed it cleared. - USB2_OTG_FS->GRSTCTL |= ((epnum - 1) << USB_OTG_GRSTCTL_TXFNUM_Pos); - USB2_OTG_FS->GRSTCTL |= USB_OTG_GRSTCTL_TXFFLSH; - while((USB2_OTG_FS->GRSTCTL & USB_OTG_GRSTCTL_TXFFLSH_Msk) != 0); + USB_OTG_FS->GRSTCTL |= ((epnum - 1) << USB_OTG_GRSTCTL_TXFNUM_Pos); + USB_OTG_FS->GRSTCTL |= USB_OTG_GRSTCTL_TXFFLSH; + while((USB_OTG_FS->GRSTCTL & USB_OTG_GRSTCTL_TXFFLSH_Msk) != 0); } else { // Only disable currently enabled non-control endpoint if ( (epnum == 0) || !(out_ep[epnum].DOEPCTL & USB_OTG_DOEPCTL_EPENA) ){ @@ -358,7 +368,7 @@ void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr) // anyway, and it can't be cleared by user code. If this while loop never // finishes, we have bigger problems than just the stack. dev->DCTL |= USB_OTG_DCTL_SGONAK; - while((USB2_OTG_FS->GINTSTS & USB_OTG_GINTSTS_BOUTNAKEFF_Msk) == 0); + while((USB_OTG_FS->GINTSTS & USB_OTG_GINTSTS_BOUTNAKEFF_Msk) == 0); // Ditto here- disable the endpoint. out_ep[epnum].DOEPCTL |= (USB_OTG_DOEPCTL_STALL | USB_OTG_DOEPCTL_EPDIS); @@ -512,7 +522,7 @@ static void read_rx_fifo(USB_OTG_OUTEndpointTypeDef * out_ep) { // Pop control word off FIFO (completed xfers will have 2 control words, // we only pop one ctl word each interrupt). - uint32_t ctl_word = USB2_OTG_FS->GRXSTSP; + uint32_t ctl_word = USB_OTG_FS->GRXSTSP; uint8_t pktsts = (ctl_word & USB_OTG_GRXSTSP_PKTSTS_Msk) >> USB_OTG_GRXSTSP_PKTSTS_Pos; uint8_t epnum = (ctl_word & USB_OTG_GRXSTSP_EPNUM_Msk) >> USB_OTG_GRXSTSP_EPNUM_Pos; uint16_t bcnt = (ctl_word & USB_OTG_GRXSTSP_BCNT_Msk) >> USB_OTG_GRXSTSP_BCNT_Pos; @@ -551,7 +561,7 @@ static void read_rx_fifo(USB_OTG_OUTEndpointTypeDef * out_ep) { static void handle_epout_ints(USB_OTG_DeviceTypeDef * dev, USB_OTG_OUTEndpointTypeDef * out_ep) { // DAINT for a given EP clears when DOEPINTx is cleared. // OEPINT will be cleared when DAINT's out bits are cleared. - for(int n = 0; n < 8; n++) { + for(uint8_t n = 0; n < EP_MAX; n++) { xfer_ctl_t * xfer = XFER_CTL_BASE(n, TUSB_DIR_OUT); if(dev->DAINT & (1 << (USB_OTG_DAINT_OEPINT_Pos + n))) { @@ -589,7 +599,7 @@ static void handle_epout_ints(USB_OTG_DeviceTypeDef * dev, USB_OTG_OUTEndpointTy static void handle_epin_ints(USB_OTG_DeviceTypeDef * dev, USB_OTG_INEndpointTypeDef * in_ep) { // DAINT for a given EP clears when DIEPINTx is cleared. // IEPINT will be cleared when DAINT's out bits are cleared. - for(uint8_t n = 0; n < 8; n++) { + for(uint8_t n = 0; n < EP_MAX; n++) { xfer_ctl_t * xfer = XFER_CTL_BASE(n, TUSB_DIR_IN); if(dev->DAINT & (1 << (USB_OTG_DAINT_IEPINT_Pos + n))) { @@ -609,29 +619,30 @@ static void handle_epin_ints(USB_OTG_DeviceTypeDef * dev, USB_OTG_INEndpointType } } -void OTG_FS_IRQHandler (void) -{ +void OTG_FS_IRQHandler(void) { USB_OTG_DeviceTypeDef * dev = DEVICE_BASE; USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE; USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE; - uint32_t int_status = USB2_OTG_FS->GINTSTS; + uint32_t int_status = USB_OTG_FS->GINTSTS; - if(int_status & USB_OTG_GINTMSK_USBRST) - { - USB2_OTG_FS->GINTSTS = USB_OTG_GINTSTS_USBRST; + if(int_status & USB_OTG_GINTSTS_USBRST) { + // USBRST is start of reset. + USB_OTG_FS->GINTSTS = USB_OTG_GINTSTS_USBRST; bus_reset(); } - if(int_status & USB_OTG_GINTMSK_ENUMDNEM) - { - USB2_OTG_FS->GINTSTS = USB_OTG_GINTMSK_ENUMDNEM; + if(int_status & USB_OTG_GINTSTS_ENUMDNE) { + // ENUMDNE detects speed of the link. For full-speed, we + // always expect the same value. This interrupt is considered + // the end of reset. + USB_OTG_FS->GINTSTS = USB_OTG_GINTSTS_ENUMDNE; end_of_reset(); dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true); } if(int_status & USB_OTG_GINTSTS_SOF) { - USB2_OTG_FS->GINTSTS = USB_OTG_GINTSTS_SOF; + USB_OTG_FS->GINTSTS = USB_OTG_GINTSTS_SOF; dcd_event_bus_signal(0, DCD_EVENT_SOF, true); }