diff options
Diffstat (limited to 'src/portable/broadcom/synopsys/dcd_synopsys.c')
| -rw-r--r-- | src/portable/broadcom/synopsys/dcd_synopsys.c | 1267 |
1 files changed, 0 insertions, 1267 deletions
diff --git a/src/portable/broadcom/synopsys/dcd_synopsys.c b/src/portable/broadcom/synopsys/dcd_synopsys.c deleted file mode 100644 index c4881ee50..000000000 --- a/src/portable/broadcom/synopsys/dcd_synopsys.c +++ /dev/null @@ -1,1267 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Scott Shawcroft, 2019 William D. Jones for Adafruit Industries - * Copyright (c) 2019 Ha Thach (tinyusb.org) - * Copyright (c) 2020 Jan Duempelmann - * Copyright (c) 2020 Reinhard Panhuber - * - * 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 CFG_TUD_ENABLED && \ - (CFG_TUSB_MCU == OPT_MCU_BCM2711 ) \ - - -#include "synopsys_common.h" - -#include "broadcom/interrupts.h" - -// Since TinyUSB doesn't use SOF for now, and this interrupt too often (1ms interval) -// We disable SOF for now until needed later on -#define USE_SOF 0 - -// EP_MAX : Max number of bi-directional endpoints including EP0 -// EP_FIFO_SIZE : Size of dedicated USB SRAM -#if CFG_TUSB_MCU == OPT_MCU_BCM2711 -// #include "bcm2711.h" -#define EP_MAX_FS 8 -#define EP_FIFO_SIZE_FS 4096 -#define EP_MAX_HS 8 -#define EP_FIFO_SIZE_HS 4096 -#else -#error "Unsupported MCUs" -#endif - -#define EP_MAX 8 -#define EP_FIFO_SIZE 4096 - -// Info on values here: https://github.com/torvalds/linux/blob/79160a603bdb51916226caf4a6616cc4e1c58a58/Documentation/devicetree/bindings/usb/dwc2.yaml - -// From: https://github.com/raspberrypi/linux/blob/rpi-5.10.y/arch/arm/boot/dts/bcm283x.dtsi -// usb: usb@7e980000 { -// compatible = "brcm,bcm2835-usb"; -// reg = <0x7e980000 0x10000>; -// interrupts = <1 9>; -// #address-cells = <1>; -// #size-cells = <0>; -// clocks = <&clk_usb>; -// clock-names = "otg"; -// phys = <&usbphy>; -// phy-names = "usb2-phy"; -// }; - -// From: https://github.com/raspberrypi/linux/blob/rpi-5.10.y/arch/arm/boot/dts/bcm283x-rpi-usb-otg.dtsi -// SPDX-License-Identifier: GPL-2.0 -// &usb { -// dr_mode = "otg"; -// g-rx-fifo-size = <256>; -// g-np-tx-fifo-size = <32>; - -// * According to dwc2 the sum of all device EP -// * fifo sizes shouldn't exceed 3776 bytes. - -// g-tx-fifo-size = <256 256 512 512 512 768 768>; -// }; - -// From: https://github.com/raspberrypi/linux/blob/rpi-5.10.y/arch/arm/boot/dts/bcm2711-rpi.dtsi -// &usb { -// /* Enable the FIQ support */ -// reg = <0x7e980000 0x10000>, -// <0x7e00b200 0x200>; -// interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>, -// <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>; -// status = "disabled"; -// }; - -// From: https://github.com/raspberrypi/linux/blob/rpi-5.10.y/arch/arm/boot/dts/bcm2711.dtsi -// &usb { -// interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>; -// }; - -// From: https://github.com/torvalds/linux/blob/1d597682d3e669ec7021aa33d088ed3d136a5149/drivers/usb/dwc2/params.c -// static void dwc2_set_bcm_params(struct dwc2_hsotg *hsotg) -// { -// struct dwc2_core_params *p = &hsotg->params; - -// p->host_rx_fifo_size = 774; -// p->max_transfer_size = 65535; -// p->max_packet_count = 511; -// p->ahbcfg = 0x10; -// } - -#include "device/dcd.h" - -TU_VERIFY_STATIC(sizeof(USB_OTG_GlobalTypeDef) == 0x140, "size is incorrect"); - -//--------------------------------------------------------------------+ -// MACRO TYPEDEF CONSTANT ENUM -//--------------------------------------------------------------------+ - -#define RHPORT_REGS_BASE USB_OTG_GLOBAL_BASE - -#define GLOBAL_BASE(_port) ((USB_OTG_GlobalTypeDef*) RHPORT_REGS_BASE) -#define DEVICE_BASE(_port) (USB_OTG_DeviceTypeDef *) (USB_OTG_DEVICE_BASE) -#define OUT_EP_BASE(_port) (USB_OTG_OUTEndpointTypeDef *) (RHPORT_REGS_BASE + USB_OTG_OUT_ENDPOINT_BASE) -#define IN_EP_BASE(_port) (USB_OTG_INEndpointTypeDef *) (RHPORT_REGS_BASE + USB_OTG_IN_ENDPOINT_BASE) -#define FIFO_BASE(_port, _x) ((volatile uint32_t *) (RHPORT_REGS_BASE + USB_OTG_FIFO_BASE + (_x) * USB_OTG_FIFO_SIZE)) - -enum -{ - DCD_HIGH_SPEED = 0, // Highspeed mode - DCD_FULL_SPEED_USE_HS = 1, // Full speed in Highspeed port (probably with internal PHY) - DCD_FULL_SPEED = 3, // Full speed with internal PHY -}; - -static TU_ATTR_ALIGNED(4) uint32_t _setup_packet[2]; - -typedef struct { - uint8_t * buffer; - tu_fifo_t * ff; - uint16_t total_len; - uint16_t max_size; - uint8_t interval; -} xfer_ctl_t; - -typedef volatile uint32_t * usb_fifo_t; - -xfer_ctl_t xfer_status[EP_MAX][2]; -#define XFER_CTL_BASE(_ep, _dir) &xfer_status[_ep][_dir] - -// EP0 transfers are limited to 1 packet - larger sizes has to be split -static uint16_t ep0_pending[2]; // Index determines direction as tusb_dir_t type - -// TX FIFO RAM allocation so far in words - RX FIFO size is readily available from usb_otg->GRXFSIZ -static uint16_t _allocated_fifo_words_tx; // TX FIFO size in words (IN EPs) -static bool _out_ep_closed; // Flag to check if RX FIFO size needs an update (reduce its size) - -// Calculate the RX FIFO size according to recommendations from reference manual -static inline uint16_t calc_rx_ff_size(uint16_t ep_size) -{ - return 15 + 2*(ep_size/4) + 2*EP_MAX; -} - -static void update_grxfsiz(uint8_t rhport) -{ - (void) rhport; - - USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport); - - // Determine largest EP size for RX FIFO - uint16_t max_epsize = 0; - for (uint8_t epnum = 0; epnum < EP_MAX; epnum++) - { - max_epsize = tu_max16(max_epsize, xfer_status[epnum][TUSB_DIR_OUT].max_size); - } - - // Update size of RX FIFO - usb_otg->GRXFSIZ = calc_rx_ff_size(max_epsize); -} - -// Setup the control endpoint 0. -static void bus_reset(uint8_t rhport) -{ - (void) rhport; - - USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport); - USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport); - USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE(rhport); - USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE(rhport); - - tu_memclr(xfer_status, sizeof(xfer_status)); - _out_ep_closed = false; - - // clear device address - dev->DCFG &= ~USB_OTG_DCFG_DAD_Msk; - - // 1. NAK for all OUT endpoints - for(uint8_t n = 0; n < EP_MAX; n++) { - out_ep[n].DOEPCTL |= USB_OTG_DOEPCTL_SNAK; - } - - // 2. Un-mask interrupt bits - dev->DAINTMSK = (1 << USB_OTG_DAINTMSK_OEPM_Pos) | (1 << USB_OTG_DAINTMSK_IEPM_Pos); - dev->DOEPMSK = USB_OTG_DOEPMSK_STUPM | USB_OTG_DOEPMSK_XFRCM; - dev->DIEPMSK = USB_OTG_DIEPMSK_TOM | USB_OTG_DIEPMSK_XFRCM; - - // "USB Data FIFOs" section in reference manual - // Peripheral FIFO architecture - // - // The FIFO is split up in a lower part where the RX FIFO is located and an upper part where the TX FIFOs start. - // We do this to allow the RX FIFO to grow dynamically which is possible since the free space is located - // between the RX and TX FIFOs. This is required by ISO OUT EPs which need a bigger FIFO than the standard - // configuration done below. - // - // Dynamically FIFO sizes are of interest only for ISO EPs since all others are usually not opened and closed. - // All EPs other than ISO are opened as soon as the driver starts up i.e. when the host sends a - // configure interface command. Hence, all IN EPs other the ISO will be located at the top. IN ISO EPs are usually - // opened when the host sends an additional command: setInterface. At this point in time - // the ISO EP will be located next to the free space and can change its size. In case more IN EPs change its size - // an additional memory - // - // --------------- 320 or 1024 ( 1280 or 4096 bytes ) - // | IN FIFO 0 | - // --------------- (320 or 1024) - 16 - // | IN FIFO 1 | - // --------------- (320 or 1024) - 16 - x - // | . . . . | - // --------------- (320 or 1024) - 16 - x - y - ... - z - // | IN FIFO MAX | - // --------------- - // | FREE | - // --------------- GRXFSIZ - // | OUT FIFO | - // | ( Shared ) | - // --------------- 0 - // - // 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 - // - // - All EP OUT shared a unique OUT FIFO which uses - // - 13 for setup packets + control words (up to 3 setup packets). - // - 1 for global NAK (not required/used here). - // - Largest-EPsize / 4 + 1. ( FS: 64 bytes, HS: 512 bytes). Recommended is "2 x (Largest-EPsize/4) + 1" - // - 2 for each used OUT endpoint - // - // Therefore GRXFSIZ = 13 + 1 + 1 + 2 x (Largest-EPsize/4) + 2 x EPOUTnum - // - FullSpeed (64 Bytes ): GRXFSIZ = 15 + 2 x 16 + 2 x EP_MAX = 47 + 2 x EP_MAX - // - Highspeed (512 bytes): GRXFSIZ = 15 + 2 x 128 + 2 x EP_MAX = 271 + 2 x EP_MAX - // - // NOTE: Largest-EPsize & EPOUTnum is actual used endpoints in configuration. Since DCD has no knowledge - // of the overall picture yet. We will use the worst scenario: largest possible + EP_MAX - // - // For Isochronous, largest EP size can be 1023/1024 for FS/HS respectively. In addition if multiple ISO - // are enabled at least "2 x (Largest-EPsize/4) + 1" are recommended. Maybe provide a macro for application to - // overwrite this. - - #if TUD_OPT_HIGH_SPEED - usb_otg->GRXFSIZ = calc_rx_ff_size(512); - #else - usb_otg->GRXFSIZ = calc_rx_ff_size(64); - #endif - - _allocated_fifo_words_tx = 16; - - // Control IN uses FIFO 0 with 64 bytes ( 16 32-bit word ) - usb_otg->DIEPTXF0_HNPTXFSIZ = (16 << USB_OTG_TX0FD_Pos) | (EP_FIFO_SIZE/4 - _allocated_fifo_words_tx); - - // Fixed control EP0 size to 64 bytes - in_ep[0].DIEPCTL &= ~(0x03 << USB_OTG_DIEPCTL_MPSIZ_Pos); - xfer_status[0][TUSB_DIR_OUT].max_size = xfer_status[0][TUSB_DIR_IN].max_size = 64; - - out_ep[0].DOEPTSIZ |= (3 << USB_OTG_DOEPTSIZ_STUPCNT_Pos); - - usb_otg->GINTMSK |= USB_OTG_GINTMSK_OEPINT | USB_OTG_GINTMSK_IEPINT; -} - -// Set turn-around timeout according to link speed -extern uint32_t SystemCoreClock; -static void set_turnaround(USB_OTG_GlobalTypeDef * usb_otg, tusb_speed_t speed) -{ - usb_otg->GUSBCFG &= ~USB_OTG_GUSBCFG_TRDT; - - if ( speed == TUSB_SPEED_HIGH ) - { - // Use fixed 0x09 for Highspeed - usb_otg->GUSBCFG |= (0x09 << USB_OTG_GUSBCFG_TRDT_Pos); - } - else - { - // Turnaround timeout depends on the MCU clock - uint32_t turnaround; - - if ( SystemCoreClock >= 32000000U ) - turnaround = 0x6U; - else if ( SystemCoreClock >= 27500000U ) - turnaround = 0x7U; - else if ( SystemCoreClock >= 24000000U ) - turnaround = 0x8U; - else if ( SystemCoreClock >= 21800000U ) - turnaround = 0x9U; - else if ( SystemCoreClock >= 20000000U ) - turnaround = 0xAU; - else if ( SystemCoreClock >= 18500000U ) - turnaround = 0xBU; - else if ( SystemCoreClock >= 17200000U ) - turnaround = 0xCU; - else if ( SystemCoreClock >= 16000000U ) - turnaround = 0xDU; - else if ( SystemCoreClock >= 15000000U ) - turnaround = 0xEU; - else - turnaround = 0xFU; - - // Fullspeed depends on MCU clocks, but we will use 0x06 for 32+ Mhz - usb_otg->GUSBCFG |= (turnaround << USB_OTG_GUSBCFG_TRDT_Pos); - } -} - -static tusb_speed_t get_speed(uint8_t rhport) -{ - (void) rhport; - USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport); - uint32_t const enum_spd = (dev->DSTS & USB_OTG_DSTS_ENUMSPD_Msk) >> USB_OTG_DSTS_ENUMSPD_Pos; - return (enum_spd == DCD_HIGH_SPEED) ? TUSB_SPEED_HIGH : TUSB_SPEED_FULL; -} - -static void set_speed(uint8_t rhport, tusb_speed_t speed) -{ - uint32_t bitvalue; - - if ( rhport == 1 ) - { - bitvalue = ((TUSB_SPEED_HIGH == speed) ? DCD_HIGH_SPEED : DCD_FULL_SPEED_USE_HS); - } - else - { - bitvalue = DCD_FULL_SPEED; - } - - USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport); - - // Clear and set speed bits - dev->DCFG &= ~(3 << USB_OTG_DCFG_DSPD_Pos); - dev->DCFG |= (bitvalue << USB_OTG_DCFG_DSPD_Pos); -} - -#if 0 -// From CM4IO xtal to usb hub, may not be correct -#define HSE_VALUE 24000000 - -static bool USB_HS_PHYCInit(void) -{ - USB_HS_PHYC_GlobalTypeDef *usb_hs_phyc = (USB_HS_PHYC_GlobalTypeDef*) USB_HS_PHYC_CONTROLLER_BASE; - - // Enable LDO - usb_hs_phyc->USB_HS_PHYC_LDO |= USB_HS_PHYC_LDO_ENABLE; - - // Wait until LDO ready - while ( 0 == (usb_hs_phyc->USB_HS_PHYC_LDO & USB_HS_PHYC_LDO_STATUS) ) {} - - uint32_t phyc_pll = 0; - - // TODO Try to get HSE_VALUE from registers instead of depending CFLAGS - switch ( HSE_VALUE ) - { - case 12000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_12MHZ ; break; - case 12500000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_12_5MHZ ; break; - case 16000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_16MHZ ; break; - case 24000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_24MHZ ; break; - case 25000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_25MHZ ; break; - case 32000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_Msk ; break; // Value not defined in header - default: - TU_ASSERT(0); - } - usb_hs_phyc->USB_HS_PHYC_PLL = phyc_pll; - - // Control the tuning interface of the High Speed PHY - // Use magic value (USB_HS_PHYC_TUNE_VALUE) from ST driver - usb_hs_phyc->USB_HS_PHYC_TUNE |= 0x00000F13U; - - // Enable PLL internal PHY - usb_hs_phyc->USB_HS_PHYC_PLL |= USB_HS_PHYC_PLL_PLLEN; - - // Original ST code has 2 ms delay for PLL stabilization. - // Primitive test shows that more than 10 USB un/replug cycle showed no error with enumeration - - return true; -} -#endif - -static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t const dir, uint16_t const num_packets, uint16_t total_bytes) -{ - (void) rhport; - - USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport); - USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE(rhport); - USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE(rhport); - - // EP0 is limited to one packet each xfer - // We use multiple transaction of xfer->max_size length to get a whole transfer done - if(epnum == 0) { - xfer_ctl_t * const xfer = XFER_CTL_BASE(epnum, dir); - total_bytes = tu_min16(ep0_pending[dir], xfer->max_size); - ep0_pending[dir] -= total_bytes; - } - - // IN and OUT endpoint xfers are interrupt-driven, we just schedule them here. - if(dir == TUSB_DIR_IN) { - // A full IN transfer (multiple packets, possibly) triggers XFRC. - in_ep[epnum].DIEPTSIZ = (num_packets << USB_OTG_DIEPTSIZ_PKTCNT_Pos) | - ((total_bytes << USB_OTG_DIEPTSIZ_XFRSIZ_Pos) & USB_OTG_DIEPTSIZ_XFRSIZ_Msk); - - in_ep[epnum].DIEPCTL |= USB_OTG_DIEPCTL_EPENA | USB_OTG_DIEPCTL_CNAK; - // For ISO endpoint set correct odd/even bit for next frame. - if ((in_ep[epnum].DIEPCTL & USB_OTG_DIEPCTL_EPTYP) == USB_OTG_DIEPCTL_EPTYP_0 && (XFER_CTL_BASE(epnum, dir))->interval == 1) - { - // Take odd/even bit from frame counter. - uint32_t const odd_frame_now = (dev->DSTS & (1u << USB_OTG_DSTS_FNSOF_Pos)); - in_ep[epnum].DIEPCTL |= (odd_frame_now ? USB_OTG_DIEPCTL_SD0PID_SEVNFRM_Msk : USB_OTG_DIEPCTL_SODDFRM_Msk); - } - // Enable fifo empty interrupt only if there are something to put in the fifo. - if(total_bytes != 0) { - dev->DIEPEMPMSK |= (1 << epnum); - } - } else { - // A full OUT transfer (multiple packets, possibly) triggers XFRC. - out_ep[epnum].DOEPTSIZ &= ~(USB_OTG_DOEPTSIZ_PKTCNT_Msk | USB_OTG_DOEPTSIZ_XFRSIZ); - out_ep[epnum].DOEPTSIZ |= (num_packets << USB_OTG_DOEPTSIZ_PKTCNT_Pos) | - ((total_bytes << USB_OTG_DOEPTSIZ_XFRSIZ_Pos) & USB_OTG_DOEPTSIZ_XFRSIZ_Msk); - - out_ep[epnum].DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK; - if ((out_ep[epnum].DOEPCTL & USB_OTG_DOEPCTL_EPTYP) == USB_OTG_DOEPCTL_EPTYP_0 && (XFER_CTL_BASE(epnum, dir))->interval == 1) - { - // Take odd/even bit from frame counter. - uint32_t const odd_frame_now = (dev->DSTS & (1u << USB_OTG_DSTS_FNSOF_Pos)); - out_ep[epnum].DOEPCTL |= (odd_frame_now ? USB_OTG_DOEPCTL_SD0PID_SEVNFRM_Msk : USB_OTG_DOEPCTL_SODDFRM_Msk); - } - } -} - -/*------------------------------------------------------------------*/ -/* Controller API - *------------------------------------------------------------------*/ - -TU_ATTR_UNUSED -static void reset_core(USB_OTG_GlobalTypeDef * usb_otg) { - while ((usb_otg->GRSTCTL & USB_OTG_GRSTCTL_AHBIDL) == 0) {} - - TU_LOG(2, " resetting\r\n"); - usb_otg->GRSTCTL |= USB_OTG_GRSTCTL_CSRST; - TU_LOG(2, " waiting\r\n"); - while ((usb_otg->GRSTCTL & (USB_OTG_GRSTCTL_AHBIDL | USB_OTG_GRSTCTL_CSRST)) != USB_OTG_GRSTCTL_AHBIDL) {} - TU_LOG(2, " reset done\r\n"); -} - -void dcd_init (uint8_t rhport) -{ - printf("test done\r\n"); - // Programming model begins in the last section of the chapter on the USB - // peripheral in each Reference Manual. - TU_LOG(2, " dcd_init\r\n"); - - TU_LOG2("Test 123\r\n"); - - USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport); - -#if 1 - // No VBUS sense - usb_otg->GCCFG &= ~(1UL << 21); // USB_OTG_GCCFG_VBDEN - - // B-peripheral session valid override enable - usb_otg->GOTGCTL |= (1UL << 6); // USB_OTG_GOTGCTL_BVALOEN - usb_otg->GOTGCTL |= (1UL << 7); // USB_OTG_GOTGCTL_BVALOVAL - - // Force device mode - usb_otg->GUSBCFG &= ~USB_OTG_GUSBCFG_FHMOD; - usb_otg->GUSBCFG |= USB_OTG_GUSBCFG_FDMOD; - - // deactivate internal PHY - usb_otg->GCCFG &= ~USB_OTG_GCCFG_PWRDWN; - - // Init The UTMI Interface - usb_otg->GUSBCFG &= ~(USB_OTG_GUSBCFG_TSDPS | USB_OTG_GUSBCFG_ULPIFSLS | USB_OTG_GUSBCFG_PHYSEL); - - // Select default internal VBUS Indicator and Drive for ULPI - usb_otg->GUSBCFG &= ~(USB_OTG_GUSBCFG_ULPIEVBUSD | USB_OTG_GUSBCFG_ULPIEVBUSI); - - // Select UTMI Interface - usb_otg->GUSBCFG &= ~(1UL << 4); // USB_OTG_GUSBCFG_ULPI_UTMI_SEL - usb_otg->GCCFG |= (1UL << 32); // USB_OTG_GCCFG_PHYHSEN - - // Enables control of a High Speed USB PHY - //USB_HS_PHYCInit(); - - // Reset core after selecting PHY - // Wait AHB IDLE, reset then wait until it is cleared -// while ((usb_otg->GRSTCTL & USB_OTG_GRSTCTL_AHBIDL) == 0U) {} -// usb_otg->GRSTCTL |= USB_OTG_GRSTCTL_CSRST; -// while ((usb_otg->GRSTCTL & USB_OTG_GRSTCTL_CSRST) == USB_OTG_GRSTCTL_CSRST) {} - - reset_core(usb_otg); - - // Restart PHY clock - *((volatile uint32_t *)(RHPORT_REGS_BASE + USB_OTG_PCGCCTL_BASE)) = 0; - -#else - - // ReadBackReg(&Core->Usb); - // Core->Usb.UlpiDriveExternalVbus = 0; - // Core->Usb.TsDlinePulseEnable = 0; - // WriteThroughReg(&Core->Usb); - - // This sequence is modeled after: https://github.com/Chadderz121/csud/blob/e13b9355d043a9cdd384b335060f1bc0416df61e/source/hcd/dwc/designware20.c#L689 - usb_otg->GUSBCFG &= ~(USB_OTG_GUSBCFG_TSDPS | USB_OTG_GUSBCFG_ULPIEVBUSD); - reset_core(usb_otg); - - // Core->Usb.ModeSelect = UTMI; - // LOG_DEBUG("HCD: Interface: UTMI+.\n"); - // Core->Usb.PhyInterface = false; - - // HcdReset(); - TU_LOG2("init phy\r\n"); - usb_otg->GUSBCFG |= (1 << 4); // bit four sets UTMI+ mode - usb_otg->GUSBCFG &= ~(1 << 3); // bit three disables phy interface - reset_core(usb_otg); - - // LOG_DEBUG("HCD: ULPI FSLS configuration: disabled.\n"); - // Core->Usb.UlpiFsls = false; - // Core->Usb.ulpi_clk_sus_m = false; - usb_otg->GUSBCFG &= ~(USB_OTG_GUSBCFG_ULPIFSLS | USB_OTG_GUSBCFG_ULPICSM); - - // LOG_DEBUG("HCD: DMA configuration: enabled.\n"); - // Core->Ahb.DmaEnable = true; - // Core->Ahb.DmaRemainderMode = Incremental; - usb_otg->GAHBCFG &= ~(1 << 23); // Remainder mode - usb_otg->GAHBCFG |= USB_OTG_GAHBCFG_DMAEN; - - // LOG_DEBUG("HCD: HNP/SRP configuration: HNP, SRP.\n"); - // Core->Usb.HnpCapable = true; - // Core->Usb.SrpCapable = true; - usb_otg->GUSBCFG |= USB_OTG_GUSBCFG_SRPCAP | USB_OTG_GUSBCFG_HNPCAP; - -#endif - - // Clear all interrupts - usb_otg->GINTSTS |= usb_otg->GINTSTS; - - // Required as part of core initialization. - // TODO: How should mode mismatch be handled? It will cause - // the core to stop working/require reset. - usb_otg->GINTMSK |= USB_OTG_GINTMSK_OTGINT | USB_OTG_GINTMSK_MMISM; - - USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport); - - // If USB host misbehaves during status portion of control xfer - // (non zero-length packet), send STALL back and discard. - dev->DCFG |= USB_OTG_DCFG_NZLSOHSK; - - set_speed(rhport, TUSB_SPEED_HIGH); - - // TODO internal phy (full speed) - usb_otg->GCCFG |= USB_OTG_GCCFG_PWRDWN; - - usb_otg->GINTMSK |= USB_OTG_GINTMSK_USBRST | USB_OTG_GINTMSK_ENUMDNEM | - USB_OTG_GINTMSK_USBSUSPM | USB_OTG_GINTMSK_WUIM | - USB_OTG_GINTMSK_RXFLVLM | (USE_SOF ? USB_OTG_GINTMSK_SOFM : 0); - - // Enable global interrupt - usb_otg->GAHBCFG |= USB_OTG_GAHBCFG_GINT; - - dcd_connect(rhport); -} - -void dcd_int_enable (uint8_t rhport) -{ - (void) rhport; - BP_EnableIRQ(USB_IRQn); -} - -void dcd_int_disable (uint8_t rhport) -{ - (void) rhport; - BP_DisableIRQ(USB_IRQn); -} - -void dcd_set_address (uint8_t rhport, uint8_t dev_addr) -{ - USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport); - dev->DCFG = (dev->DCFG & ~USB_OTG_DCFG_DAD_Msk) | (dev_addr << USB_OTG_DCFG_DAD_Pos); - - // Response with status after changing device address - dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); -} - -static void remote_wakeup_delay(void) -{ - // try to delay for 1 ms - uint32_t count = SystemCoreClock / 1000; - while ( count-- ) - { - // __NOP(); - } -} - -void dcd_remote_wakeup(uint8_t rhport) -{ - (void) rhport; - - USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport); - USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport); - - // set remote wakeup - dev->DCTL |= USB_OTG_DCTL_RWUSIG; - - // enable SOF to detect bus resume - usb_otg->GINTSTS = USB_OTG_GINTSTS_SOF; - usb_otg->GINTMSK |= USB_OTG_GINTMSK_SOFM; - - // Per specs: remote wakeup signal bit must be clear within 1-15ms - remote_wakeup_delay(); - - dev->DCTL &= ~USB_OTG_DCTL_RWUSIG; -} - -void dcd_connect(uint8_t rhport) -{ - (void) rhport; - USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport); - dev->DCTL &= ~USB_OTG_DCTL_SDIS; -} - -void dcd_disconnect(uint8_t rhport) -{ - (void) rhport; - USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport); - dev->DCTL |= USB_OTG_DCTL_SDIS; -} - - -/*------------------------------------------------------------------*/ -/* DCD Endpoint port - *------------------------------------------------------------------*/ - -bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) -{ - (void) rhport; - - USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport); - USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport); - USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE(rhport); - USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE(rhport); - - uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress); - uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress); - - TU_ASSERT(epnum < EP_MAX); - - xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir); - xfer->max_size = tu_edpt_packet_size(desc_edpt); - xfer->interval = desc_edpt->bInterval; - - uint16_t const fifo_size = (xfer->max_size + 3) / 4; // Round up to next full word - - if(dir == TUSB_DIR_OUT) - { - // Calculate required size of RX FIFO - uint16_t const sz = calc_rx_ff_size(4*fifo_size); - - // If size_rx needs to be extended check if possible and if so enlarge it - if (usb_otg->GRXFSIZ < sz) - { - TU_ASSERT(sz + _allocated_fifo_words_tx <= EP_FIFO_SIZE/4); - - // Enlarge RX FIFO - usb_otg->GRXFSIZ = sz; - } - - out_ep[epnum].DOEPCTL |= (1 << USB_OTG_DOEPCTL_USBAEP_Pos) | - (desc_edpt->bmAttributes.xfer << USB_OTG_DOEPCTL_EPTYP_Pos) | - (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? USB_OTG_DOEPCTL_SD0PID_SEVNFRM : 0) | - (xfer->max_size << USB_OTG_DOEPCTL_MPSIZ_Pos); - - dev->DAINTMSK |= (1 << (USB_OTG_DAINTMSK_OEPM_Pos + epnum)); - } - else - { - // "USB Data FIFOs" section in reference manual - // Peripheral FIFO architecture - // - // --------------- 320 or 1024 ( 1280 or 4096 bytes ) - // | IN FIFO 0 | - // --------------- (320 or 1024) - 16 - // | IN FIFO 1 | - // --------------- (320 or 1024) - 16 - x - // | . . . . | - // --------------- (320 or 1024) - 16 - x - y - ... - z - // | IN FIFO MAX | - // --------------- - // | FREE | - // --------------- GRXFSIZ - // | OUT FIFO | - // | ( Shared ) | - // --------------- 0 - // - // In FIFO is allocated by following rules: - // - IN EP 1 gets FIFO 1, IN EP "n" gets FIFO "n". - - // Check if free space is available - TU_ASSERT(_allocated_fifo_words_tx + fifo_size + usb_otg->GRXFSIZ <= EP_FIFO_SIZE/4); - - _allocated_fifo_words_tx += fifo_size; - - TU_LOG(2, " Allocated %u bytes at offset %u", fifo_size*4, EP_FIFO_SIZE-_allocated_fifo_words_tx*4); - - // DIEPTXF starts at FIFO #1. - // Both TXFD and TXSA are in unit of 32-bit words. - usb_otg->DIEPTXF[epnum - 1] = (fifo_size << USB_OTG_DIEPTXF_INEPTXFD_Pos) | (EP_FIFO_SIZE/4 - _allocated_fifo_words_tx); - - in_ep[epnum].DIEPCTL |= (1 << USB_OTG_DIEPCTL_USBAEP_Pos) | - (epnum << USB_OTG_DIEPCTL_TXFNUM_Pos) | - (desc_edpt->bmAttributes.xfer << USB_OTG_DIEPCTL_EPTYP_Pos) | - (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? USB_OTG_DIEPCTL_SD0PID_SEVNFRM : 0) | - (xfer->max_size << USB_OTG_DIEPCTL_MPSIZ_Pos); - - dev->DAINTMSK |= (1 << (USB_OTG_DAINTMSK_IEPM_Pos + epnum)); - } - - return true; -} - -// Close all non-control endpoints, cancel all pending transfers if any. -void dcd_edpt_close_all (uint8_t rhport) -{ - (void) rhport; - -// USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport); - USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport); - USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE(rhport); - USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE(rhport); - - // Disable non-control interrupt - dev->DAINTMSK = (1 << USB_OTG_DAINTMSK_OEPM_Pos) | (1 << USB_OTG_DAINTMSK_IEPM_Pos); - - for(uint8_t n = 1; n < EP_MAX; n++) - { - // disable OUT endpoint - out_ep[n].DOEPCTL = 0; - xfer_status[n][TUSB_DIR_OUT].max_size = 0; - - // disable IN endpoint - in_ep[n].DIEPCTL = 0; - xfer_status[n][TUSB_DIR_IN].max_size = 0; - } - - // reset allocated fifo IN - _allocated_fifo_words_tx = 16; -} - -bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) -{ - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir); - xfer->buffer = buffer; - xfer->ff = NULL; - xfer->total_len = total_bytes; - - // EP0 can only handle one packet - if(epnum == 0) { - ep0_pending[dir] = total_bytes; - // Schedule the first transaction for EP0 transfer - edpt_schedule_packets(rhport, epnum, dir, 1, ep0_pending[dir]); - return true; - } - - uint16_t num_packets = (total_bytes / xfer->max_size); - uint16_t const short_packet_size = total_bytes % xfer->max_size; - - // Zero-size packet is special case. - if(short_packet_size > 0 || (total_bytes == 0)) { - num_packets++; - } - - // Schedule packets to be sent within interrupt - edpt_schedule_packets(rhport, epnum, dir, num_packets, total_bytes); - - return true; -} - -// The number of bytes has to be given explicitly to allow more flexible control of how many -// bytes should be written and second to keep the return value free to give back a boolean -// success message. If total_bytes is too big, the FIFO will copy only what is available -// into the USB buffer! -bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes) -{ - // USB buffers always work in bytes so to avoid unnecessary divisions we demand item_size = 1 - TU_ASSERT(ff->item_size == 1); - - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir); - xfer->buffer = NULL; - xfer->ff = ff; - xfer->total_len = total_bytes; - - uint16_t num_packets = (total_bytes / xfer->max_size); - uint16_t const short_packet_size = total_bytes % xfer->max_size; - - // Zero-size packet is special case. - if(short_packet_size > 0 || (total_bytes == 0)) num_packets++; - - // Schedule packets to be sent within interrupt - edpt_schedule_packets(rhport, epnum, dir, num_packets, total_bytes); - - return true; -} - -static void dcd_edpt_disable (uint8_t rhport, uint8_t ep_addr, bool stall) -{ - (void) rhport; - - USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport); - USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport); - USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE(rhport); - USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE(rhport); - - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - if(dir == TUSB_DIR_IN) { - // Only disable currently enabled non-control endpoint - if ( (epnum == 0) || !(in_ep[epnum].DIEPCTL & USB_OTG_DIEPCTL_EPENA) ){ - in_ep[epnum].DIEPCTL |= USB_OTG_DIEPCTL_SNAK | (stall ? USB_OTG_DIEPCTL_STALL : 0); - } else { - // Stop transmitting packets and NAK IN xfers. - in_ep[epnum].DIEPCTL |= USB_OTG_DIEPCTL_SNAK; - while((in_ep[epnum].DIEPINT & USB_OTG_DIEPINT_INEPNE) == 0); - - // Disable the endpoint. - in_ep[epnum].DIEPCTL |= USB_OTG_DIEPCTL_EPDIS | (stall ? USB_OTG_DIEPCTL_STALL : 0); - while((in_ep[epnum].DIEPINT & USB_OTG_DIEPINT_EPDISD_Msk) == 0); - in_ep[epnum].DIEPINT = USB_OTG_DIEPINT_EPDISD; - } - - // Flush the FIFO, and wait until we have confirmed it cleared. - usb_otg->GRSTCTL |= (epnum << USB_OTG_GRSTCTL_TXFNUM_Pos); - usb_otg->GRSTCTL |= USB_OTG_GRSTCTL_TXFFLSH; - while((usb_otg->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) ){ - out_ep[epnum].DOEPCTL |= stall ? USB_OTG_DOEPCTL_STALL : 0; - } else { - // Asserting GONAK is required to STALL an OUT endpoint. - // Simpler to use polling here, we don't use the "B"OUTNAKEFF interrupt - // 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((usb_otg->GINTSTS & USB_OTG_GINTSTS_BOUTNAKEFF_Msk) == 0); - - // Ditto here- disable the endpoint. - out_ep[epnum].DOEPCTL |= USB_OTG_DOEPCTL_EPDIS | (stall ? USB_OTG_DOEPCTL_STALL : 0); - while((out_ep[epnum].DOEPINT & USB_OTG_DOEPINT_EPDISD_Msk) == 0); - out_ep[epnum].DOEPINT = USB_OTG_DOEPINT_EPDISD; - - // Allow other OUT endpoints to keep receiving. - dev->DCTL |= USB_OTG_DCTL_CGONAK; - } - } -} - -/** - * Close an endpoint. - */ -void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr) -{ - USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport); - - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - dcd_edpt_disable(rhport, ep_addr, false); - - // Update max_size - xfer_status[epnum][dir].max_size = 0; // max_size = 0 marks a disabled EP - required for changing FIFO allocation - - if (dir == TUSB_DIR_IN) - { - uint16_t const fifo_size = (usb_otg->DIEPTXF[epnum - 1] & USB_OTG_DIEPTXF_INEPTXFD_Msk) >> USB_OTG_DIEPTXF_INEPTXFD_Pos; - uint16_t const fifo_start = (usb_otg->DIEPTXF[epnum - 1] & USB_OTG_DIEPTXF_INEPTXSA_Msk) >> USB_OTG_DIEPTXF_INEPTXSA_Pos; - // For now only the last opened endpoint can be closed without fuss. - TU_ASSERT(fifo_start == EP_FIFO_SIZE/4 - _allocated_fifo_words_tx,); - _allocated_fifo_words_tx -= fifo_size; - } - else - { - _out_ep_closed = true; // Set flag such that RX FIFO gets reduced in size once RX FIFO is empty - } -} - -void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr) -{ - dcd_edpt_disable(rhport, ep_addr, true); -} - -void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr) -{ - (void) rhport; - - USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE(rhport); - USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE(rhport); - - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - // Clear stall and reset data toggle - if(dir == TUSB_DIR_IN) { - in_ep[epnum].DIEPCTL &= ~USB_OTG_DIEPCTL_STALL; - in_ep[epnum].DIEPCTL |= USB_OTG_DIEPCTL_SD0PID_SEVNFRM; - } else { - out_ep[epnum].DOEPCTL &= ~USB_OTG_DOEPCTL_STALL; - out_ep[epnum].DOEPCTL |= USB_OTG_DOEPCTL_SD0PID_SEVNFRM; - } -} - -/*------------------------------------------------------------------*/ - -// Read a single data packet from receive FIFO -static void read_fifo_packet(uint8_t rhport, uint8_t * dst, uint16_t len) -{ - (void) rhport; - - usb_fifo_t rx_fifo = FIFO_BASE(rhport, 0); - - // Reading full available 32 bit words from fifo - uint16_t full_words = len >> 2; - for(uint16_t i = 0; i < full_words; i++) { - uint32_t tmp = *rx_fifo; - dst[0] = tmp & 0x000000FF; - dst[1] = (tmp & 0x0000FF00) >> 8; - dst[2] = (tmp & 0x00FF0000) >> 16; - dst[3] = (tmp & 0xFF000000) >> 24; - dst += 4; - } - - // Read the remaining 1-3 bytes from fifo - uint8_t bytes_rem = len & 0x03; - if(bytes_rem != 0) { - uint32_t tmp = *rx_fifo; - dst[0] = tmp & 0x000000FF; - if(bytes_rem > 1) { - dst[1] = (tmp & 0x0000FF00) >> 8; - } - if(bytes_rem > 2) { - dst[2] = (tmp & 0x00FF0000) >> 16; - } - } -} - -// Write a single data packet to EPIN FIFO -static void write_fifo_packet(uint8_t rhport, uint8_t fifo_num, uint8_t * src, uint16_t len) -{ - (void) rhport; - - usb_fifo_t tx_fifo = FIFO_BASE(rhport, fifo_num); - - // Pushing full available 32 bit words to fifo - uint16_t full_words = len >> 2; - for(uint16_t i = 0; i < full_words; i++){ - *tx_fifo = (src[3] << 24) | (src[2] << 16) | (src[1] << 8) | src[0]; - src += 4; - } - - // Write the remaining 1-3 bytes into fifo - uint8_t bytes_rem = len & 0x03; - if(bytes_rem){ - uint32_t tmp_word = 0; - tmp_word |= src[0]; - if(bytes_rem > 1){ - tmp_word |= src[1] << 8; - } - if(bytes_rem > 2){ - tmp_word |= src[2] << 16; - } - *tx_fifo = tmp_word; - } -} - -static void handle_rxflvl_ints(uint8_t rhport, USB_OTG_OUTEndpointTypeDef * out_ep) { - USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport); - usb_fifo_t rx_fifo = FIFO_BASE(rhport, 0); - - // Pop control word off FIFO - uint32_t ctl_word = usb_otg->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; - - switch(pktsts) { - case 0x01: // Global OUT NAK (Interrupt) - break; - - case 0x02: // Out packet recvd - { - xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT); - - // Read packet off RxFIFO - if (xfer->ff) - { - // Ring buffer - tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void *)(uintptr_t) rx_fifo, bcnt); - } - else - { - // Linear buffer - read_fifo_packet(rhport, xfer->buffer, bcnt); - - // Increment pointer to xfer data - xfer->buffer += bcnt; - } - - // Truncate transfer length in case of short packet - if(bcnt < xfer->max_size) { - xfer->total_len -= (out_ep[epnum].DOEPTSIZ & USB_OTG_DOEPTSIZ_XFRSIZ_Msk) >> USB_OTG_DOEPTSIZ_XFRSIZ_Pos; - if(epnum == 0) { - xfer->total_len -= ep0_pending[TUSB_DIR_OUT]; - ep0_pending[TUSB_DIR_OUT] = 0; - } - } - } - break; - - case 0x03: // Out packet done (Interrupt) - break; - - case 0x04: // Setup packet done (Interrupt) - out_ep[epnum].DOEPTSIZ |= (3 << USB_OTG_DOEPTSIZ_STUPCNT_Pos); - break; - - case 0x06: // Setup packet recvd - // We can receive up to three setup packets in succession, but - // only the last one is valid. - _setup_packet[0] = (* rx_fifo); - _setup_packet[1] = (* rx_fifo); - break; - - default: // Invalid - TU_BREAKPOINT(); - break; - } -} - -static void handle_epout_ints(uint8_t rhport, 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(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))) { - // SETUP packet Setup Phase done. - if(out_ep[n].DOEPINT & USB_OTG_DOEPINT_STUP) { - out_ep[n].DOEPINT = USB_OTG_DOEPINT_STUP; - dcd_event_setup_received(rhport, (uint8_t*) &_setup_packet[0], true); - } - - // OUT XFER complete - if(out_ep[n].DOEPINT & USB_OTG_DOEPINT_XFRC) { - out_ep[n].DOEPINT = USB_OTG_DOEPINT_XFRC; - - // EP0 can only handle one packet - if((n == 0) && ep0_pending[TUSB_DIR_OUT]) { - // Schedule another packet to be received. - edpt_schedule_packets(rhport, n, TUSB_DIR_OUT, 1, ep0_pending[TUSB_DIR_OUT]); - } else { - dcd_event_xfer_complete(rhport, n, xfer->total_len, XFER_RESULT_SUCCESS, true); - } - } - } - } -} - -static void handle_epin_ints(uint8_t rhport, 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 < EP_MAX; n++ ) - { - xfer_ctl_t *xfer = XFER_CTL_BASE(n, TUSB_DIR_IN); - - if ( dev->DAINT & (1 << (USB_OTG_DAINT_IEPINT_Pos + n)) ) - { - // IN XFER complete (entire xfer). - if ( in_ep[n].DIEPINT & USB_OTG_DIEPINT_XFRC ) - { - in_ep[n].DIEPINT = USB_OTG_DIEPINT_XFRC; - - // EP0 can only handle one packet - if((n == 0) && ep0_pending[TUSB_DIR_IN]) { - // Schedule another packet to be transmitted. - edpt_schedule_packets(rhport, n, TUSB_DIR_IN, 1, ep0_pending[TUSB_DIR_IN]); - } else { - dcd_event_xfer_complete(rhport, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true); - } - } - - // XFER FIFO empty - if ( (in_ep[n].DIEPINT & USB_OTG_DIEPINT_TXFE) && (dev->DIEPEMPMSK & (1 << n)) ) - { - // DIEPINT's TXFE bit is read-only, software cannot clear it. - // It will only be cleared by hardware when written bytes is more than - // - 64 bytes or - // - Half of TX FIFO size (configured by DIEPTXF) - - uint16_t remaining_packets = (in_ep[n].DIEPTSIZ & USB_OTG_DIEPTSIZ_PKTCNT_Msk) >> USB_OTG_DIEPTSIZ_PKTCNT_Pos; - - // Process every single packet (only whole packets can be written to fifo) - for(uint16_t i = 0; i < remaining_packets; i++) - { - uint16_t const remaining_bytes = (in_ep[n].DIEPTSIZ & USB_OTG_DIEPTSIZ_XFRSIZ_Msk) >> USB_OTG_DIEPTSIZ_XFRSIZ_Pos; - - // Packet can not be larger than ep max size - uint16_t const packet_size = tu_min16(remaining_bytes, xfer->max_size); - - // It's only possible to write full packets into FIFO. Therefore DTXFSTS register of current - // EP has to be checked if the buffer can take another WHOLE packet - if(packet_size > ((in_ep[n].DTXFSTS & USB_OTG_DTXFSTS_INEPTFSAV_Msk) << 2)) break; - - // Push packet to Tx-FIFO - if (xfer->ff) - { - usb_fifo_t tx_fifo = FIFO_BASE(rhport, n); - tu_fifo_read_n_const_addr_full_words(xfer->ff, (void *)(uintptr_t) tx_fifo, packet_size); - } - else - { - write_fifo_packet(rhport, n, xfer->buffer, packet_size); - - // Increment pointer to xfer data - xfer->buffer += packet_size; - } - } - - // Turn off TXFE if all bytes are written. - if (((in_ep[n].DIEPTSIZ & USB_OTG_DIEPTSIZ_XFRSIZ_Msk) >> USB_OTG_DIEPTSIZ_XFRSIZ_Pos) == 0) - { - dev->DIEPEMPMSK &= ~(1 << n); - } - } - } - } -} - -void dcd_int_handler(uint8_t rhport) -{ - USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport); - USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport); - USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE(rhport); - USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE(rhport); - - uint32_t const int_status = usb_otg->GINTSTS & usb_otg->GINTMSK; - - if(int_status & USB_OTG_GINTSTS_USBRST) - { - // USBRST is start of reset. - usb_otg->GINTSTS = USB_OTG_GINTSTS_USBRST; - bus_reset(rhport); - } - - if(int_status & USB_OTG_GINTSTS_ENUMDNE) - { - // ENUMDNE is the end of reset where speed of the link is detected - - usb_otg->GINTSTS = USB_OTG_GINTSTS_ENUMDNE; - - tusb_speed_t const speed = get_speed(rhport); - - set_turnaround(usb_otg, speed); - dcd_event_bus_reset(rhport, speed, true); - } - - if(int_status & USB_OTG_GINTSTS_USBSUSP) - { - usb_otg->GINTSTS = USB_OTG_GINTSTS_USBSUSP; - dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true); - } - - if(int_status & USB_OTG_GINTSTS_WKUINT) - { - usb_otg->GINTSTS = USB_OTG_GINTSTS_WKUINT; - dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true); - } - - // TODO check USB_OTG_GINTSTS_DISCINT for disconnect detection - // if(int_status & USB_OTG_GINTSTS_DISCINT) - - if(int_status & USB_OTG_GINTSTS_OTGINT) - { - // OTG INT bit is read-only - uint32_t const otg_int = usb_otg->GOTGINT; - - if (otg_int & USB_OTG_GOTGINT_SEDET) - { - dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true); - } - - usb_otg->GOTGINT = otg_int; - } - - if(int_status & USB_OTG_GINTSTS_SOF) - { - usb_otg->GINTSTS = USB_OTG_GINTSTS_SOF; - - // Disable SOF interrupt since currently only used for remote wakeup detection - usb_otg->GINTMSK &= ~USB_OTG_GINTMSK_SOFM; - - dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true); - } - - // RxFIFO non-empty interrupt handling. - if(int_status & USB_OTG_GINTSTS_RXFLVL) - { - // RXFLVL bit is read-only - - // Mask out RXFLVL while reading data from FIFO - usb_otg->GINTMSK &= ~USB_OTG_GINTMSK_RXFLVLM; - - // Loop until all available packets were handled - do - { - handle_rxflvl_ints(rhport, out_ep); - } while(usb_otg->GINTSTS & USB_OTG_GINTSTS_RXFLVL); - - // Manage RX FIFO size - if (_out_ep_closed) - { - update_grxfsiz(rhport); - - // Disable flag - _out_ep_closed = false; - } - - usb_otg->GINTMSK |= USB_OTG_GINTMSK_RXFLVLM; - } - - // OUT endpoint interrupt handling. - if(int_status & USB_OTG_GINTSTS_OEPINT) - { - // OEPINT is read-only - handle_epout_ints(rhport, dev, out_ep); - } - - // IN endpoint interrupt handling. - if(int_status & USB_OTG_GINTSTS_IEPINT) - { - // IEPINT bit read-only - handle_epin_ints(rhport, dev, in_ep); - } - - // // Check for Incomplete isochronous IN transfer - // if(int_status & USB_OTG_GINTSTS_IISOIXFR) { - // printf(" IISOIXFR!\r\n"); - //// TU_LOG2(" IISOIXFR!\r\n"); - // } -} - -#endif |
