summaryrefslogtreecommitdiff
path: root/src/portable/broadcom/synopsys/dcd_synopsys.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/portable/broadcom/synopsys/dcd_synopsys.c')
-rw-r--r--src/portable/broadcom/synopsys/dcd_synopsys.c1267
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