diff options
author | Gregory Nutt <gnutt@nuttx.org> | 2014-11-04 10:14:04 -0600 |
---|---|---|
committer | Gregory Nutt <gnutt@nuttx.org> | 2014-11-04 10:14:04 -0600 |
commit | b0669e950f3b76cb418f4b6645916ecedb606a13 (patch) | |
tree | e39a6c6937da0f2fded4f3283fd56cff06c0b8f9 | |
parent | 010c1b49310bec659c9a922e608776f0a53ff4b3 (diff) | |
download | nuttx-b0669e950f3b76cb418f4b6645916ecedb606a13.tar.gz nuttx-b0669e950f3b76cb418f4b6645916ecedb606a13.tar.bz2 nuttx-b0669e950f3b76cb418f4b6645916ecedb606a13.zip |
EFM32: Port USB device and host drivers from STM32. Still does not compile
-rw-r--r-- | nuttx/arch/arm/src/efm32/Kconfig | 43 | ||||
-rw-r--r-- | nuttx/arch/arm/src/efm32/Make.defs | 2 | ||||
-rw-r--r-- | nuttx/arch/arm/src/efm32/chip/efm32_usb.h | 1002 | ||||
-rw-r--r-- | nuttx/arch/arm/src/efm32/efm32_usb.h | 126 | ||||
-rw-r--r-- | nuttx/arch/arm/src/efm32/efm32_usbdev.c | 5701 | ||||
-rw-r--r-- | nuttx/arch/arm/src/efm32/efm32_usbhost.c | 4452 |
6 files changed, 10820 insertions, 506 deletions
diff --git a/nuttx/arch/arm/src/efm32/Kconfig b/nuttx/arch/arm/src/efm32/Kconfig index 2d4d22316..448cb9f20 100644 --- a/nuttx/arch/arm/src/efm32/Kconfig +++ b/nuttx/arch/arm/src/efm32/Kconfig @@ -73,7 +73,7 @@ config EFM32_EFM32GG select EFM32_HAVE_UART0 select EFM32_HAVE_UART1 select EFM32_HAVE_LEUART1 - select EFM32_HAVE_USBFSOTG + select EFM32_HAVE_OTGFS menu "EFM32 Peripheral Support" @@ -96,7 +96,7 @@ config EFM32_HAVE_LEUART1 bool default n -config EFM32_HAVE_USBFSOTG +config EFM32_HAVE_OTGFS bool default n @@ -169,10 +169,10 @@ config EFM32_LEUART1 select ARCH_HAVE_OTHER_UART select EFM32_LEUART -config EFM32_USBFSOTG +config EFM32_OTGFS bool "USB Full-Speed OTG" default n - depends on EFM32_HAVE_USBFSOTG && EXPERIMENTAL + depends on EFM32_HAVE_OTGFS && EXPERIMENTAL endmenu # EFM32 Peripheral Support @@ -392,4 +392,39 @@ endif # EFM32_SPI_DMA endmenu # SPI Configuration endif # EFM32_USART_ISSPI +menu "USB FS Host Configuration" + depends on USBHOST && EFM32_OTGFS + +config EFM32_OTGFS_RXFIFO_SIZE + int "Rx Packet Size" + default 128 + ---help--- + Size of the RX FIFO in 32-bit words. Default 128 (512 bytes) + +config EFM32_OTGFS_NPTXFIFO_SIZE + int "Non-periodic Tx FIFO Size" + default 96 + ---help--- + Size of the non-periodic Tx FIFO in 32-bit words. Default 96 (384 bytes) + +config EFM32_OTGFS_PTXFIFO_SIZE + int "Periodic Tx FIFO size" + default 128 + ---help--- + Size of the periodic Tx FIFO in 32-bit words. Default 96 (384 bytes) + +config EFM32_OTGFS_DESCSIZE + int "Descriptor Size" + default 128 + ---help--- + Maximum size to allocate for descriptor memory descriptor. Default: 128 + +config EFM32_OTGFS_SOFINTR + bool "Enable SOF interrupts" + default n + ---help--- + Enable SOF interrupts. Why would you ever want to do that? + +endmenu + endif # ARCH_CHIP_EFM32 diff --git a/nuttx/arch/arm/src/efm32/Make.defs b/nuttx/arch/arm/src/efm32/Make.defs index 1aec13974..3c19b698f 100644 --- a/nuttx/arch/arm/src/efm32/Make.defs +++ b/nuttx/arch/arm/src/efm32/Make.defs @@ -127,7 +127,7 @@ ifeq ($(CONFIG_EFM32_DMA),y) CHIP_CSRCS += efm32_dma.c endif -ifeq ($(CONFIG_EFM32_USBFSOTG),y) +ifeq ($(CONFIG_EFM32_OTGFS),y) ifeq ($(CONFIG_USBDEV),y) CHIP_CSRCS += efm32_usbdev.c endif diff --git a/nuttx/arch/arm/src/efm32/chip/efm32_usb.h b/nuttx/arch/arm/src/efm32/chip/efm32_usb.h index f6df7516e..8bac0a15c 100644 --- a/nuttx/arch/arm/src/efm32/chip/efm32_usb.h +++ b/nuttx/arch/arm/src/efm32/chip/efm32_usb.h @@ -158,53 +158,53 @@ #define EFM32_USB_DIEP5_OFFSET 0x3c9a0 /* Device IN Endpoint 5 */ #define EFM32_USB_DIEP6_OFFSET 0x3c9c0 /* Device IN Endpoint 6 */ -#define EFM32_USB_DIEPn_CTL_OFFSET 0x00000 /* Device IN Endpoint n Control Register */ -#define EFM32_USB_DIEPn_INT_OFFSET 0x00008 /* Device IN Endpoint n Interrupt Register */ -#define EFM32_USB_DIEPn_TSIZ_OFFSET 0x00010 /* Device IN Endpoint n Transfer Size Register */ -#define EFM32_USB_DIEPn_DMAADDR_OFFSET 0x00014 /* Device IN Endpoint n DMA Address Register */ -#define EFM32_USB_DIEPn_TXFSTS_OFFSET 0x00018 /* Device IN Endpoint n Transmit FIFO Status Register */ - -#define EFM32_USB_DIEP0_CTL_OFFSET 0x3c900 /* Device IN Endpoint 0 Control Register */ -#define EFM32_USB_DIEP0_INT_OFFSET 0x3c908 /* Device IN Endpoint 0 Interrupt Register */ -#define EFM32_USB_DIEP0_TSIZ_OFFSET 0x3c910 /* Device IN Endpoint 0 Transfer Size Register */ -#define EFM32_USB_DIEP0_DMAADDR_OFFSET 0x3c914 /* Device IN Endpoint 0 DMA Address Register */ -#define EFM32_USB_DIEP0_TXFSTS_OFFSET 0x3c918 /* Device IN Endpoint 0 Transmit FIFO Status Register */ - -#define EFM32_USB_DIEP1_CTL_OFFSET 0x3c920 /* Device IN Endpoint 1 Control Register */ -#define EFM32_USB_DIEP1_INT_OFFSET 0x3c928 /* Device IN Endpoint 1 Interrupt Register */ -#define EFM32_USB_DIEP1_TSIZ_OFFSET 0x3c930 /* Device IN Endpoint 1 Transfer Size Register */ -#define EFM32_USB_DIEP1_DMAADDR_OFFSET 0x3c934 /* Device IN Endpoint 1 DMA Address Register */ -#define EFM32_USB_DIEP1_TXFSTS_OFFSET 0x3c938 /* Device IN Endpoint 1 Transmit FIFO Status Register */ - -#define EFM32_USB_DIEP2_CTL_OFFSET 0x3c940 /* Device IN Endpoint 2 Control Register */ -#define EFM32_USB_DIEP2_INT_OFFSET 0x3c948 /* Device IN Endpoint 2 Interrupt Register */ -#define EFM32_USB_DIEP2_TSIZ_OFFSET 0x3c950 /* Device IN Endpoint 2 Transfer Size Register */ -#define EFM32_USB_DIEP2_DMAADDR_OFFSET 0x3c954 /* Device IN Endpoint 2 DMA Address Register */ -#define EFM32_USB_DIEP2_TXFSTS_OFFSET 0x3c958 /* Device IN Endpoint 2 Transmit FIFO Status Register */ - -#define EFM32_USB_DIEP3_CTL_OFFSET 0x3c960 /* Device IN Endpoint 3 Control Register */ -#define EFM32_USB_DIEP3_INT_OFFSET 0x3c968 /* Device IN Endpoint 3 Interrupt Register */ -#define EFM32_USB_DIEP3_TSIZ_OFFSET 0x3c970 /* Device IN Endpoint 3 Transfer Size Register */ -#define EFM32_USB_DIEP3_DMAADDR_OFFSET 0x3c974 /* Device IN Endpoint 3 DMA Address Register */ -#define EFM32_USB_DIEP3_TXFSTS_OFFSET 0x3c978 /* Device IN Endpoint 3 Transmit FIFO Status Register */ - -#define EFM32_USB_DIEP4_CTL_OFFSET 0x3c980 /* Device IN Endpoint 4 Control Register */ -#define EFM32_USB_DIEP4_INT_OFFSET 0x3c988 /* Device IN Endpoint 4 Interrupt Register */ -#define EFM32_USB_DIEP4_TSIZ_OFFSET 0x3c990 /* Device IN Endpoint 4 Transfer Size Register */ -#define EFM32_USB_DIEP4_DMAADDR_OFFSET 0x3c994 /* Device IN Endpoint 4 DMA Address Register */ -#define EFM32_USB_DIEP4_TXFSTS_OFFSET 0x3c998 /* Device IN Endpoint 4 Transmit FIFO Status Register */ - -#define EFM32_USB_DIEP5_CTL_OFFSET 0x3c9a0 /* Device IN Endpoint 5 Control Register */ -#define EFM32_USB_DIEP5_INT_OFFSET 0x3c9a8 /* Device IN Endpoint 5 Interrupt Register */ -#define EFM32_USB_DIEP5_TSIZ_OFFSET 0x3c9b0 /* Device IN Endpoint 5 Transfer Size Register */ -#define EFM32_USB_DIEP5_DMAADDR_OFFSET 0x3c9b4 /* Device IN Endpoint 5 DMA Address Register */ -#define EFM32_USB_DIEP5_TXFSTS_OFFSET 0x3c9b8 /* Device IN Endpoint 5 Transmit FIFO Status Register */ - -#define EFM32_USB_DIEP6_CTL_OFFSET 0x3c9c0 /* Device IN Endpoint 6 Control Register */ -#define EFM32_USB_DIEP6_INT_OFFSET 0x3c9c8 /* Device IN Endpoint 6 Interrupt Register */ -#define EFM32_USB_DIEP6_TSIZ_OFFSET 0x3c9d0 /* Device IN Endpoint 6 Transfer Size Register */ -#define EFM32_USB_DIEP6_DMAADDR_OFFSET 0x3c9d4 /* Device IN Endpoint 6 DMA Address Register */ -#define EFM32_USB_DIEP6_TXFSTS_OFFSET 0x3c9d8 /* Device IN Endpoint 6 Transmit FIFO Status Register */ +#define EFM32_USB_DIEPnCTL_OFFSET 0x00000 /* Device IN Endpoint n Control Register */ +#define EFM32_USB_DIEPnINT_OFFSET 0x00008 /* Device IN Endpoint n Interrupt Register */ +#define EFM32_USB_DIEPnTSIZ_OFFSET 0x00010 /* Device IN Endpoint n Transfer Size Register */ +#define EFM32_USB_DIEPnDMAADDR_OFFSET 0x00014 /* Device IN Endpoint n DMA Address Register */ +#define EFM32_USB_DIEPnTXFSTS_OFFSET 0x00018 /* Device IN Endpoint n Transmit FIFO Status Register */ + +#define EFM32_USB_DIEP0CTL_OFFSET 0x3c900 /* Device IN Endpoint 0 Control Register */ +#define EFM32_USB_DIEP0INT_OFFSET 0x3c908 /* Device IN Endpoint 0 Interrupt Register */ +#define EFM32_USB_DIEP0TSIZ_OFFSET 0x3c910 /* Device IN Endpoint 0 Transfer Size Register */ +#define EFM32_USB_DIEP0DMAADDR_OFFSET 0x3c914 /* Device IN Endpoint 0 DMA Address Register */ +#define EFM32_USB_DIEP0TXFSTS_OFFSET 0x3c918 /* Device IN Endpoint 0 Transmit FIFO Status Register */ + +#define EFM32_USB_DIEP1CTL_OFFSET 0x3c920 /* Device IN Endpoint 1 Control Register */ +#define EFM32_USB_DIEP1INT_OFFSET 0x3c928 /* Device IN Endpoint 1 Interrupt Register */ +#define EFM32_USB_DIEP1TSIZ_OFFSET 0x3c930 /* Device IN Endpoint 1 Transfer Size Register */ +#define EFM32_USB_DIEP1DMAADDR_OFFSET 0x3c934 /* Device IN Endpoint 1 DMA Address Register */ +#define EFM32_USB_DIEP1TXFSTS_OFFSET 0x3c938 /* Device IN Endpoint 1 Transmit FIFO Status Register */ + +#define EFM32_USB_DIEP2CTL_OFFSET 0x3c940 /* Device IN Endpoint 2 Control Register */ +#define EFM32_USB_DIEP2INT_OFFSET 0x3c948 /* Device IN Endpoint 2 Interrupt Register */ +#define EFM32_USB_DIEP2TSIZ_OFFSET 0x3c950 /* Device IN Endpoint 2 Transfer Size Register */ +#define EFM32_USB_DIEP2DMAADDR_OFFSET 0x3c954 /* Device IN Endpoint 2 DMA Address Register */ +#define EFM32_USB_DIEP2TXFSTS_OFFSET 0x3c958 /* Device IN Endpoint 2 Transmit FIFO Status Register */ + +#define EFM32_USB_DIEP3CTL_OFFSET 0x3c960 /* Device IN Endpoint 3 Control Register */ +#define EFM32_USB_DIEP3INT_OFFSET 0x3c968 /* Device IN Endpoint 3 Interrupt Register */ +#define EFM32_USB_DIEP3TSIZ_OFFSET 0x3c970 /* Device IN Endpoint 3 Transfer Size Register */ +#define EFM32_USB_DIEP3DMAADDR_OFFSET 0x3c974 /* Device IN Endpoint 3 DMA Address Register */ +#define EFM32_USB_DIEP3TXFSTS_OFFSET 0x3c978 /* Device IN Endpoint 3 Transmit FIFO Status Register */ + +#define EFM32_USB_DIEP4CTL_OFFSET 0x3c980 /* Device IN Endpoint 4 Control Register */ +#define EFM32_USB_DIEP4INT_OFFSET 0x3c988 /* Device IN Endpoint 4 Interrupt Register */ +#define EFM32_USB_DIEP4TSIZ_OFFSET 0x3c990 /* Device IN Endpoint 4 Transfer Size Register */ +#define EFM32_USB_DIEP4DMAADDR_OFFSET 0x3c994 /* Device IN Endpoint 4 DMA Address Register */ +#define EFM32_USB_DIEP4TXFSTS_OFFSET 0x3c998 /* Device IN Endpoint 4 Transmit FIFO Status Register */ + +#define EFM32_USB_DIEP5CTL_OFFSET 0x3c9a0 /* Device IN Endpoint 5 Control Register */ +#define EFM32_USB_DIEP5INT_OFFSET 0x3c9a8 /* Device IN Endpoint 5 Interrupt Register */ +#define EFM32_USB_DIEP5TSIZ_OFFSET 0x3c9b0 /* Device IN Endpoint 5 Transfer Size Register */ +#define EFM32_USB_DIEP5DMAADDR_OFFSET 0x3c9b4 /* Device IN Endpoint 5 DMA Address Register */ +#define EFM32_USB_DIEP5TXFSTS_OFFSET 0x3c9b8 /* Device IN Endpoint 5 Transmit FIFO Status Register */ + +#define EFM32_USB_DIEP6CTL_OFFSET 0x3c9c0 /* Device IN Endpoint 6 Control Register */ +#define EFM32_USB_DIEP6INT_OFFSET 0x3c9c8 /* Device IN Endpoint 6 Interrupt Register */ +#define EFM32_USB_DIEP6TSIZ_OFFSET 0x3c9d0 /* Device IN Endpoint 6 Transfer Size Register */ +#define EFM32_USB_DIEP6DMAADDR_OFFSET 0x3c9d4 /* Device IN Endpoint 6 DMA Address Register */ +#define EFM32_USB_DIEP6TXFSTS_OFFSET 0x3c9d8 /* Device IN Endpoint 6 Transmit FIFO Status Register */ #define EFM32_USB_DOEP_OFFSET(n) (0x3c900 + ((n) << 5)) #define EFM32_USB_DOEP0_OFFSET 0x3c900 /* Device OUT Endpoint 0 */ @@ -215,45 +215,45 @@ #define EFM32_USB_DOEP5_OFFSET 0x3c9a0 /* Device OUT Endpoint 5 */ #define EFM32_USB_DOEP6_OFFSET 0x3c9c0 /* Device OUT Endpoint 6 */ -#define EFM32_USB_DOEPn_CTL_OFFSET 0x00000 /* Device OUT Endpoint n Control Register */ -#define EFM32_USB_DOEPn_INT_OFFSET 0x00008 /* Device OUT Endpoint n Interrupt Register */ -#define EFM32_USB_DOEPn_TSIZ_OFFSET 0x00010 /* Device OUT Endpoint n Transfer Size Register */ -#define EFM32_USB_DOEPn_DMAADDR_OFFSET 0x00014 /* Device OUT Endpoint n DMA Address Register */ +#define EFM32_USB_DOEPnCTL_OFFSET 0x00000 /* Device OUT Endpoint n Control Register */ +#define EFM32_USB_DOEPnINT_OFFSET 0x00008 /* Device OUT Endpoint n Interrupt Register */ +#define EFM32_USB_DOEPnTSIZ_OFFSET 0x00010 /* Device OUT Endpoint n Transfer Size Register */ +#define EFM32_USB_DOEPnDMAADDR_OFFSET 0x00014 /* Device OUT Endpoint n DMA Address Register */ -#define EFM32_USB_DOEP0_CTL_OFFSET 0x3cb00 /* Device OUT Endpoint 0 Control Register */ -#define EFM32_USB_DOEP0_INT_OFFSET 0x3cb08 /* Device OUT Endpoint 0 Interrupt Register */ -#define EFM32_USB_DOEP0_TSIZ_OFFSET 0x3cb10 /* Device OUT Endpoint 0 Transfer Size Register */ -#define EFM32_USB_DOEP0_DMAADDR_OFFSET 0x3cb14 /* Device OUT Endpoint 0 DMA Address Register */ +#define EFM32_USB_DOEP0CTL_OFFSET 0x3cb00 /* Device OUT Endpoint 0 Control Register */ +#define EFM32_USB_DOEP0INT_OFFSET 0x3cb08 /* Device OUT Endpoint 0 Interrupt Register */ +#define EFM32_USB_DOEP0TSIZ_OFFSET 0x3cb10 /* Device OUT Endpoint 0 Transfer Size Register */ +#define EFM32_USB_DOEP0DMAADDR_OFFSET 0x3cb14 /* Device OUT Endpoint 0 DMA Address Register */ -#define EFM32_USB_DOEP1_CTL_OFFSET 0x3cb20 /* Device OUT Endpoint x Control Register */ -#define EFM32_USB_DOEP1_INT_OFFSET 0x3cb28 /* Device OUT Endpoint x Interrupt Register */ -#define EFM32_USB_DOEP1_TSIZ_OFFSET 0x3cb30 /* Device OUT Endpoint x Transfer Size Register */ -#define EFM32_USB_DOEP1_DMAADDR_OFFSET 0x3cb34 /* Device OUT Endpoint x DMA Address Register */ +#define EFM32_USB_DOEP1CTL_OFFSET 0x3cb20 /* Device OUT Endpoint x Control Register */ +#define EFM32_USB_DOEP1INT_OFFSET 0x3cb28 /* Device OUT Endpoint x Interrupt Register */ +#define EFM32_USB_DOEP1TSIZ_OFFSET 0x3cb30 /* Device OUT Endpoint x Transfer Size Register */ +#define EFM32_USB_DOEP1DMAADDR_OFFSET 0x3cb34 /* Device OUT Endpoint x DMA Address Register */ #define EFM32_USB_DOEP2_CTL_OFFSET 0x3cb40 /* Device OUT Endpoint x Control Register */ #define EFM32_USB_DOEP2_INT_OFFSET 0x3cb48 /* Device OUT Endpoint x Interrupt Register */ #define EFM32_USB_DOEP2_TSIZ_OFFSET 0x3cb50 /* Device OUT Endpoint x Transfer Size Register */ #define EFM32_USB_DOEP2_DMAADDR_OFFSET 0x3cb54 /* Device OUT Endpoint x DMA Address Register */ -#define EFM32_USB_DOEP3_CTL_OFFSET 0x3cb60 /* Device OUT Endpoint x Control Register */ -#define EFM32_USB_DOEP3_INT_OFFSET 0x3cb68 /* Device OUT Endpoint x Interrupt Register */ -#define EFM32_USB_DOEP3_TSIZ_OFFSET 0x3cb70 /* Device OUT Endpoint x Transfer Size Register */ -#define EFM32_USB_DOEP3_DMAADDR_OFFSET 0x3cb74 /* Device OUT Endpoint x DMA Address Register */ +#define EFM32_USB_DOEP3CTL_OFFSET 0x3cb60 /* Device OUT Endpoint x Control Register */ +#define EFM32_USB_DOEP3INT_OFFSET 0x3cb68 /* Device OUT Endpoint x Interrupt Register */ +#define EFM32_USB_DOEP3TSIZ_OFFSET 0x3cb70 /* Device OUT Endpoint x Transfer Size Register */ +#define EFM32_USB_DOEP3DMAADDR_OFFSET 0x3cb74 /* Device OUT Endpoint x DMA Address Register */ -#define EFM32_USB_DOEP4_CTL_OFFSET 0x3cb80 /* Device OUT Endpoint x Control Register */ -#define EFM32_USB_DOEP4_INT_OFFSET 0x3cb88 /* Device OUT Endpoint x Interrupt Register */ -#define EFM32_USB_DOEP4_TSIZ_OFFSET 0x3cb90 /* Device OUT Endpoint x Transfer Size Register */ -#define EFM32_USB_DOEP4_DMAADDR_OFFSET 0x3cb94 /* Device OUT Endpoint x DMA Address Register */ +#define EFM32_USB_DOEP4CTL_OFFSET 0x3cb80 /* Device OUT Endpoint x Control Register */ +#define EFM32_USB_DOEP4INT_OFFSET 0x3cb88 /* Device OUT Endpoint x Interrupt Register */ +#define EFM32_USB_DOEP4TSIZ_OFFSET 0x3cb90 /* Device OUT Endpoint x Transfer Size Register */ +#define EFM32_USB_DOEP4DMAADDR_OFFSET 0x3cb94 /* Device OUT Endpoint x DMA Address Register */ -#define EFM32_USB_DOEP5_CTL_OFFSET 0x3cba0 /* Device OUT Endpoint x Control Register */ -#define EFM32_USB_DOEP5_INT_OFFSET 0x3cba8 /* Device OUT Endpoint x Interrupt Register */ -#define EFM32_USB_DOEP5_TSIZ_OFFSET 0x3cbb0 /* Device OUT Endpoint x Transfer Size Register */ -#define EFM32_USB_DOEP5_DMAADDR_OFFSET 0x3cbb4 /* Device OUT Endpoint x DMA Address Register */ +#define EFM32_USB_DOEP5CTL_OFFSET 0x3cba0 /* Device OUT Endpoint x Control Register */ +#define EFM32_USB_DOEP5INT_OFFSET 0x3cba8 /* Device OUT Endpoint x Interrupt Register */ +#define EFM32_USB_DOEP5TSIZ_OFFSET 0x3cbb0 /* Device OUT Endpoint x Transfer Size Register */ +#define EFM32_USB_DOEP5DMAADDR_OFFSET 0x3cbb4 /* Device OUT Endpoint x DMA Address Register */ -#define EFM32_USB_DOEP6_CTL_OFFSET 0x3cbc0 /* Device OUT Endpoint x Control Register */ -#define EFM32_USB_DOEP6_INT_OFFSET 0x3cbc8 /* Device OUT Endpoint x Interrupt Register */ -#define EFM32_USB_DOEP6_TSIZ_OFFSET 0x3cbd0 /* Device OUT Endpoint x Transfer Size Register */ -#define EFM32_USB_DOEP6_DMAADDR_OFFSET 0x3cbd4 /* Device OUT Endpoint x DMA Address Register */ +#define EFM32_USB_DOEP6CTL_OFFSET 0x3cbc0 /* Device OUT Endpoint x Control Register */ +#define EFM32_USB_DOEP6INT_OFFSET 0x3cbc8 /* Device OUT Endpoint x Interrupt Register */ +#define EFM32_USB_DOEP6TSIZ_OFFSET 0x3cbd0 /* Device OUT Endpoint x Transfer Size Register */ +#define EFM32_USB_DOEP6DMAADDR_OFFSET 0x3cbd4 /* Device OUT Endpoint x DMA Address Register */ #define EFM32_USB_PCGCCTL_OFFSET 0x3ce00 /* Power and Clock Gating Control Register */ @@ -361,53 +361,53 @@ #define EFM32_USB_DIEP5_BASE (EFM32_USB_BASE+EFM32_USB_DIEP5_OFFSET) #define EFM32_USB_DIEP6_BASE (EFM32_USB_BASE+EFM32_USB_DIEP6_OFFSET) -#define EFM32_USB_DIEP_CTL(n) (EFM32_USB_DIEP_BASE(n)+EFM32_USB_DIEPn_CTL_OFFSET) -#define EFM32_USB_DIEP_INT(n) (EFM32_USB_DIEP_BASE(n)+EFM32_USB_DIEPn_INT_OFFSET) -#define EFM32_USB_DIEP_TSIZ(n) (EFM32_USB_DIEP_BASE(n)+EFM32_USB_DIEPn_TSIZ_OFFSET) -#define EFM32_USB_DIEP_DMAADDR(n) (EFM32_USB_DIEP_BASE(n)+EFM32_USB_DIEPn_DMAADDR_OFFSET) -#define EFM32_USB_DIEP_TXFSTS(n) (EFM32_USB_DIEP_BASE(n)+EFM32_USB_DIEPn_TXFSTS_OFFSET) - -#define EFM32_USB_DIEP0_CTL (EFM32_USB_BASE+EFM32_USB_DIEP0_CTL_OFFSET) -#define EFM32_USB_DIEP0_INT (EFM32_USB_BASE+EFM32_USB_DIEP0_INT_OFFSET) -#define EFM32_USB_DIEP0_TSIZ (EFM32_USB_BASE+EFM32_USB_DIEP0_TSIZ_OFFSET) -#define EFM32_USB_DIEP0_DMAADDR (EFM32_USB_BASE+EFM32_USB_DIEP0_DMAADDR_OFFSET) -#define EFM32_USB_DIEP0_TXFSTS (EFM32_USB_BASE+EFM32_USB_DIEP0_TXFSTS_OFFSET) - -#define EFM32_USB_DIEP1_CTL (EFM32_USB_BASE+EFM32_USB_DIEP1_CTL_OFFSET) -#define EFM32_USB_DIEP1_INT (EFM32_USB_BASE+EFM32_USB_DIEP1_INT_OFFSET) -#define EFM32_USB_DIEP1_TSIZ (EFM32_USB_BASE+EFM32_USB_DIEP1_TSIZ_OFFSET) -#define EFM32_USB_DIEP1_DMAADDR (EFM32_USB_BASE+EFM32_USB_DIEP1_DMAADDR_OFFSET) -#define EFM32_USB_DIEP1_TXFSTS (EFM32_USB_BASE+EFM32_USB_DIEP1_TXFSTS_OFFSET) - -#define EFM32_USB_DIEP2_CTL (EFM32_USB_BASE+EFM32_USB_DIEP2_CTL_OFFSET) -#define EFM32_USB_DIEP2_INT (EFM32_USB_BASE+EFM32_USB_DIEP2_INT_OFFSET) -#define EFM32_USB_DIEP2_TSIZ (EFM32_USB_BASE+EFM32_USB_DIEP2_TSIZ_OFFSET) -#define EFM32_USB_DIEP2_DMAADDR (EFM32_USB_BASE+EFM32_USB_DIEP2_DMAADDR_OFFSET) -#define EFM32_USB_DIEP2_TXFSTS (EFM32_USB_BASE+EFM32_USB_DIEP2_TXFSTS_OFFSET) - -#define EFM32_USB_DIEP3_CTL (EFM32_USB_BASE+EFM32_USB_DIEP3_CTL_OFFSET) -#define EFM32_USB_DIEP3_INT (EFM32_USB_BASE+EFM32_USB_DIEP3_INT_OFFSET) -#define EFM32_USB_DIEP3_TSIZ (EFM32_USB_BASE+EFM32_USB_DIEP3_TSIZ_OFFSET) -#define EFM32_USB_DIEP3_DMAADDR (EFM32_USB_BASE+EFM32_USB_DIEP3_DMAADDR_OFFSET) -#define EFM32_USB_DIEP3_TXFSTS (EFM32_USB_BASE+EFM32_USB_DIEP3_TXFSTS_OFFSET) - -#define EFM32_USB_DIEP4_CTL (EFM32_USB_BASE+EFM32_USB_DIEP4_CTL_OFFSET) -#define EFM32_USB_DIEP4_INT (EFM32_USB_BASE+EFM32_USB_DIEP4_INT_OFFSET) -#define EFM32_USB_DIEP4_TSIZ (EFM32_USB_BASE+EFM32_USB_DIEP4_TSIZ_OFFSET) -#define EFM32_USB_DIEP4_DMAADDR (EFM32_USB_BASE+EFM32_USB_DIEP4_DMAADDR_OFFSET) -#define EFM32_USB_DIEP4_TXFSTS (EFM32_USB_BASE+EFM32_USB_DIEP4_TXFSTS_OFFSET) - -#define EFM32_USB_DIEP5_CTL (EFM32_USB_BASE+EFM32_USB_DIEP5_CTL_OFFSET) -#define EFM32_USB_DIEP5_INT (EFM32_USB_BASE+EFM32_USB_DIEP5_INT_OFFSET) -#define EFM32_USB_DIEP5_TSIZ (EFM32_USB_BASE+EFM32_USB_DIEP5_TSIZ_OFFSET) -#define EFM32_USB_DIEP5_DMAADDR (EFM32_USB_BASE+EFM32_USB_DIEP5_DMAADDR_OFFSET) -#define EFM32_USB_DIEP5_TXFSTS (EFM32_USB_BASE+EFM32_USB_DIEP5_TXFSTS_OFFSET) - -#define EFM32_USB_DIEP6_CTL (EFM32_USB_BASE+EFM32_USB_DIEP6_CTL_OFFSET) -#define EFM32_USB_DIEP6_INT (EFM32_USB_BASE+EFM32_USB_DIEP6_INT_OFFSET) -#define EFM32_USB_DIEP6_TSIZ (EFM32_USB_BASE+EFM32_USB_DIEP6_TSIZ_OFFSET) -#define EFM32_USB_DIEP6_DMAADDR (EFM32_USB_BASE+EFM32_USB_DIEP6_DMAADDR_OFFSET) -#define EFM32_USB_DIEP6_TXFSTS (EFM32_USB_BASE+EFM32_USB_DIEP6_TXFSTS_OFFSET) +#define EFM32_USB_DIEPCTL(n) (EFM32_USB_DIEP_BASE(n)+EFM32_USB_DIEPnCTL_OFFSET) +#define EFM32_USB_DIEPINT(n) (EFM32_USB_DIEP_BASE(n)+EFM32_USB_DIEPnINT_OFFSET) +#define EFM32_USB_DIEPTSIZ(n) (EFM32_USB_DIEP_BASE(n)+EFM32_USB_DIEPnTSIZ_OFFSET) +#define EFM32_USB_DIEPDMAADDR(n) (EFM32_USB_DIEP_BASE(n)+EFM32_USB_DIEPnDMAADDR_OFFSET) +#define EFM32_USB_DIEPTXFSTS(n) (EFM32_USB_DIEP_BASE(n)+EFM32_USB_DIEPnTXFSTS_OFFSET) + +#define EFM32_USB_DIEP0CTL (EFM32_USB_BASE+EFM32_USB_DIEP0CTL_OFFSET) +#define EFM32_USB_DIEP0INT (EFM32_USB_BASE+EFM32_USB_DIEP0INT_OFFSET) +#define EFM32_USB_DIEP0TSIZ (EFM32_USB_BASE+EFM32_USB_DIEP0TSIZ_OFFSET) +#define EFM32_USB_DIEP0DMAADDR (EFM32_USB_BASE+EFM32_USB_DIEP0DMAADDR_OFFSET) +#define EFM32_USB_DIEP0TXFSTS (EFM32_USB_BASE+EFM32_USB_DIEP0TXFSTS_OFFSET) + +#define EFM32_USB_DIEP1CTL (EFM32_USB_BASE+EFM32_USB_DIEP1CTL_OFFSET) +#define EFM32_USB_DIEP1INT (EFM32_USB_BASE+EFM32_USB_DIEP1INT_OFFSET) +#define EFM32_USB_DIEP1TSIZ (EFM32_USB_BASE+EFM32_USB_DIEP1TSIZ_OFFSET) +#define EFM32_USB_DIEP1DMAADDR (EFM32_USB_BASE+EFM32_USB_DIEP1DMAADDR_OFFSET) +#define EFM32_USB_DIEP1TXFSTS (EFM32_USB_BASE+EFM32_USB_DIEP1TXFSTS_OFFSET) + +#define EFM32_USB_DIEP2CTL (EFM32_USB_BASE+EFM32_USB_DIEP2CTL_OFFSET) +#define EFM32_USB_DIEP2INT (EFM32_USB_BASE+EFM32_USB_DIEP2INT_OFFSET) +#define EFM32_USB_DIEP2TSIZ (EFM32_USB_BASE+EFM32_USB_DIEP2TSIZ_OFFSET) +#define EFM32_USB_DIEP2DMAADDR (EFM32_USB_BASE+EFM32_USB_DIEP2DMAADDR_OFFSET) +#define EFM32_USB_DIEP2TXFSTS (EFM32_USB_BASE+EFM32_USB_DIEP2TXFSTS_OFFSET) + +#define EFM32_USB_DIEP3CTL (EFM32_USB_BASE+EFM32_USB_DIEP3CTL_OFFSET) +#define EFM32_USB_DIEP3INT (EFM32_USB_BASE+EFM32_USB_DIEP3INT_OFFSET) +#define EFM32_USB_DIEP3TSIZ (EFM32_USB_BASE+EFM32_USB_DIEP3TSIZ_OFFSET) +#define EFM32_USB_DIEP3DMAADDR (EFM32_USB_BASE+EFM32_USB_DIEP3DMAADDR_OFFSET) +#define EFM32_USB_DIEP3_TXFSTS (EFM32_USB_BASE+EFM32_USB_DIEP3TXFSTS_OFFSET) + +#define EFM32_USB_DIEP4CTL (EFM32_USB_BASE+EFM32_USB_DIEP4CTL_OFFSET) +#define EFM32_USB_DIEP4INT (EFM32_USB_BASE+EFM32_USB_DIEP4INT_OFFSET) +#define EFM32_USB_DIEP4TSIZ (EFM32_USB_BASE+EFM32_USB_DIEP4TSIZ_OFFSET) +#define EFM32_USB_DIEP4DMAADDR (EFM32_USB_BASE+EFM32_USB_DIEP4DMAADDR_OFFSET) +#define EFM32_USB_DIEP4TXFSTS (EFM32_USB_BASE+EFM32_USB_DIEP4TXFSTS_OFFSET) + +#define EFM32_USB_DIEP5CTL (EFM32_USB_BASE+EFM32_USB_DIEP5CTL_OFFSET) +#define EFM32_USB_DIEP5INT (EFM32_USB_BASE+EFM32_USB_DIEP5INT_OFFSET) +#define EFM32_USB_DIEP5TSIZ (EFM32_USB_BASE+EFM32_USB_DIEP5TSIZ_OFFSET) +#define EFM32_USB_DIEP5DMAADDR (EFM32_USB_BASE+EFM32_USB_DIEP5DMAADDR_OFFSET) +#define EFM32_USB_DIEP5_TXFSTS (EFM32_USB_BASE+EFM32_USB_DIEP5TXFSTS_OFFSET) + +#define EFM32_USB_DIEP6CTL (EFM32_USB_BASE+EFM32_USB_DIEP6_CTL_OFFSET) +#define EFM32_USB_DIEP6INT (EFM32_USB_BASE+EFM32_USB_DIEP6_INT_OFFSET) +#define EFM32_USB_DIEP6TSIZ (EFM32_USB_BASE+EFM32_USB_DIEP6_TSIZ_OFFSET) +#define EFM32_USB_DIEP6DMAADDR (EFM32_USB_BASE+EFM32_USB_DIEP6_DMAADDR_OFFSET) +#define EFM32_USB_DIEP6TXFSTS (EFM32_USB_BASE+EFM32_USB_DIEP6_TXFSTS_OFFSET) #define EFM32_USB_DOEP_BASE(n) (EFM32_USB_BASE+EFM32_USB_DOEP_OFFSET(n)) #define EFM32_USB_DOEP0_BASE (EFM32_USB_BASE+EFM32_USB_DOEP0_OFFSET) @@ -418,45 +418,45 @@ #define EFM32_USB_DOEP5_BASE (EFM32_USB_BASE+EFM32_USB_DOEP5_OFFSET) #define EFM32_USB_DOEP6_BASE (EFM32_USB_BASE+EFM32_USB_DOEP6_OFFSET) -#define EFM32_USB_DOEP_CTL(n) (EFM32_USB_DOEP_BASE(n)+EFM32_USB_DOEPn_CTL_OFFSET) -#define EFM32_USB_DOEP_INT(n) (EFM32_USB_DOEP_BASE(n)+EFM32_USB_DOEPn_INT_OFFSET) -#define EFM32_USB_DOEP_TSIZ(n) (EFM32_USB_DOEP_BASE(n)+EFM32_USB_DOEPn_TSIZ_OFFSET) -#define EFM32_USB_DOEP_DMAADDR(n) (EFM32_USB_DOEP_BASE(n)+EFM32_USB_DOEPn_DMAADDR_OFFSET) - -#define EFM32_USB_DOEP0_CTL (EFM32_USB_BASE+EFM32_USB_DOEP0_CTL_OFFSET) -#define EFM32_USB_DOEP0_INT (EFM32_USB_BASE+EFM32_USB_DOEP0_INT_OFFSET) -#define EFM32_USB_DOEP0_TSIZ (EFM32_USB_BASE+EFM32_USB_DOEP0_TSIZ_OFFSET) -#define EFM32_USB_DOEP0_DMAADDR (EFM32_USB_BASE+EFM32_USB_DOEP0_DMAADDR_OFFSET) - -#define EFM32_USB_DOEP1_CTL (EFM32_USB_BASE+EFM32_USB_DOEP1_CTL_OFFSET) -#define EFM32_USB_DOEP1_INT (EFM32_USB_BASE+EFM32_USB_DOEP1_INT_OFFSET) -#define EFM32_USB_DOEP1_TSIZ (EFM32_USB_BASE+EFM32_USB_DOEP1_TSIZ_OFFSET) -#define EFM32_USB_DOEP1_DMAADDR (EFM32_USB_BASE+EFM32_USB_DOEP1_DMAADDR_OFFSET) - -#define EFM32_USB_DOEP2_CTL (EFM32_USB_BASE+EFM32_USB_DOEP2_CTL_OFFSET) -#define EFM32_USB_DOEP2_INT (EFM32_USB_BASE+EFM32_USB_DOEP2_INT_OFFSET) -#define EFM32_USB_DOEP2_TSIZ (EFM32_USB_BASE+EFM32_USB_DOEP2_TSIZ_OFFSET) -#define EFM32_USB_DOEP2_DMAADDR (EFM32_USB_BASE+EFM32_USB_DOEP2_DMAADDR_OFFSET) - -#define EFM32_USB_DOEP3_CTL (EFM32_USB_BASE+EFM32_USB_DOEP3_CTL_OFFSET) -#define EFM32_USB_DOEP3_INT (EFM32_USB_BASE+EFM32_USB_DOEP3_INT_OFFSET) -#define EFM32_USB_DOEP3_TSIZ (EFM32_USB_BASE+EFM32_USB_DOEP3_TSIZ_OFFSET) -#define EFM32_USB_DOEP3_DMAADDR (EFM32_USB_BASE+EFM32_USB_DOEP3_DMAADDR_OFFSET) - -#define EFM32_USB_DOEP4_CTL (EFM32_USB_BASE+EFM32_USB_DOEP4_CTL_OFFSET) -#define EFM32_USB_DOEP4_INT (EFM32_USB_BASE+EFM32_USB_DOEP4_INT_OFFSET) -#define EFM32_USB_DOEP4_TSIZ (EFM32_USB_BASE+EFM32_USB_DOEP4_TSIZ_OFFSET) -#define EFM32_USB_DOEP4_DMAADDR (EFM32_USB_BASE+EFM32_USB_DOEP4_DMAADDR_OFFSET) - -#define EFM32_USB_DOEP5_CTL (EFM32_USB_BASE+EFM32_USB_DOEP5_CTL_OFFSET) -#define EFM32_USB_DOEP5_INT (EFM32_USB_BASE+EFM32_USB_DOEP5_INT_OFFSET) -#define EFM32_USB_DOEP5_TSIZ (EFM32_USB_BASE+EFM32_USB_DOEP5_TSIZ_OFFSET) -#define EFM32_USB_DOEP5_DMAADDR (EFM32_USB_BASE+EFM32_USB_DOEP5_DMAADDR_OFFSET) - -#define EFM32_USB_DOEP6_CTL (EFM32_USB_BASE+EFM32_USB_DOEP6_CTL_OFFSET) -#define EFM32_USB_DOEP6_INT (EFM32_USB_BASE+EFM32_USB_DOEP6_INT_OFFSET) -#define EFM32_USB_DOEP6_TSIZ (EFM32_USB_BASE+EFM32_USB_DOEP6_TSIZ_OFFSET) -#define EFM32_USB_DOEP6_DMAADDR (EFM32_USB_BASE+EFM32_USB_DOEP6_DMAADDR_OFFSET) +#define EFM32_USB_DOEPCTL(n) (EFM32_USB_DOEP_BASE(n)+EFM32_USB_DOEPnCTL_OFFSET) +#define EFM32_USB_DOEPINT(n) (EFM32_USB_DOEP_BASE(n)+EFM32_USB_DOEPnINT_OFFSET) +#define EFM32_USB_DOEPTSIZ(n) (EFM32_USB_DOEP_BASE(n)+EFM32_USB_DOEPnTSIZ_OFFSET) +#define EFM32_USB_DOEPDMAADDR(n) (EFM32_USB_DOEP_BASE(n)+EFM32_USB_DOEPnDMAADDR_OFFSET) + +#define EFM32_USB_DOEP0CTL (EFM32_USB_BASE+EFM32_USB_DOEP0CTL_OFFSET) +#define EFM32_USB_DOEP0INT (EFM32_USB_BASE+EFM32_USB_DOEP0INT_OFFSET) +#define EFM32_USB_DOEP0TSIZ (EFM32_USB_BASE+EFM32_USB_DOEP0TSIZ_OFFSET) +#define EFM32_USB_DOEP0DMAADDR (EFM32_USB_BASE+EFM32_USB_DOEP0DMAADDR_OFFSET) + +#define EFM32_USB_DOEP1CTL (EFM32_USB_BASE+EFM32_USB_DOEP1CTL_OFFSET) +#define EFM32_USB_DOEP1INT (EFM32_USB_BASE+EFM32_USB_DOEP1INT_OFFSET) +#define EFM32_USB_DOEP1TSIZ (EFM32_USB_BASE+EFM32_USB_DOEP1TSIZ_OFFSET) +#define EFM32_USB_DOEP1DMAADDR (EFM32_USB_BASE+EFM32_USB_DOEP1DMAADDR_OFFSET) + +#define EFM32_USB_DOEP2CTL (EFM32_USB_BASE+EFM32_USB_DOEP2CTL_OFFSET) +#define EFM32_USB_DOEP2INT (EFM32_USB_BASE+EFM32_USB_DOEP2INT_OFFSET) +#define EFM32_USB_DOEP2TSIZ (EFM32_USB_BASE+EFM32_USB_DOEP2TSIZ_OFFSET) +#define EFM32_USB_DOEP2DMAADDR (EFM32_USB_BASE+EFM32_USB_DOEP2DMAADDR_OFFSET) + +#define EFM32_USB_DOEP3CTL (EFM32_USB_BASE+EFM32_USB_DOEP3CTL_OFFSET) +#define EFM32_USB_DOEP3INT (EFM32_USB_BASE+EFM32_USB_DOEP3INT_OFFSET) +#define EFM32_USB_DOEP3TSIZ (EFM32_USB_BASE+EFM32_USB_DOEP3TSIZ_OFFSET) +#define EFM32_USB_DOEP3DMAADDR (EFM32_USB_BASE+EFM32_USB_DOEP3DMAADDR_OFFSET) + +#define EFM32_USB_DOEP4CTL (EFM32_USB_BASE+EFM32_USB_DOEP4CTL_OFFSET) +#define EFM32_USB_DOEP4INT (EFM32_USB_BASE+EFM32_USB_DOEP4INT_OFFSET) +#define EFM32_USB_DOEP4TSIZ (EFM32_USB_BASE+EFM32_USB_DOEP4TSIZ_OFFSET) +#define EFM32_USB_DOEP4DMAADDR (EFM32_USB_BASE+EFM32_USB_DOEP4DMAADDR_OFFSET) + +#define EFM32_USB_DOEP5CTL (EFM32_USB_BASE+EFM32_USB_DOEP5CTL_OFFSET) +#define EFM32_USB_DOEP5INT (EFM32_USB_BASE+EFM32_USB_DOEP5INT_OFFSET) +#define EFM32_USB_DOEP5TSIZ (EFM32_USB_BASE+EFM32_USB_DOEP5TSIZ_OFFSET) +#define EFM32_USB_DOEP5DMAADDR (EFM32_USB_BASE+EFM32_USB_DOEP5DMAADDR_OFFSET) + +#define EFM32_USB_DOEP6CTL (EFM32_USB_BASE+EFM32_USB_DOEP6CTL_OFFSET) +#define EFM32_USB_DOEP6INT (EFM32_USB_BASE+EFM32_USB_DOEP6INT_OFFSET) +#define EFM32_USB_DOEP6TSIZ (EFM32_USB_BASE+EFM32_USB_DOEP6TSIZ_OFFSET) +#define EFM32_USB_DOEP6DMAADDR (EFM32_USB_BASE+EFM32_USB_DOEP6DMAADDR_OFFSET) #define EFM32_USB_PCGCCTL (EFM32_USB_BASE+EFM32_USB_PCGCCTL_OFFSET) @@ -2441,179 +2441,179 @@ #define _USB_DIEP0TXFSTS_SPCAVAIL_DEFAULT 0x00000200UL /* Mode DEFAULT for USB_DIEP0TXFSTS */ #define USB_DIEP0TXFSTS_SPCAVAIL_DEFAULT (_USB_DIEP0TXFSTS_SPCAVAIL_DEFAULT << 0) /* Shifted mode DEFAULT for USB_DIEP0TXFSTS */ -/* Bit fields for USB DIEP_CTL */ - -#define _USB_DIEP_CTL_RESETVALUE 0x00000000UL /* Default value for USB_DIEP_CTL */ -#define _USB_DIEP_CTL_MASK 0xFFEF87FFUL /* Mask for USB_DIEP_CTL */ - -#define _USB_DIEP_CTL_MPS_SHIFT 0 /* Shift value for USB_MPS */ -#define _USB_DIEP_CTL_MPS_MASK 0x7FFUL /* Bit mask for USB_MPS */ -#define _USB_DIEP_CTL_MPS_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEP_CTL */ -#define USB_DIEP_CTL_MPS_DEFAULT (_USB_DIEP_CTL_MPS_DEFAULT << 0) /* Shifted mode DEFAULT for USB_DIEP_CTL */ -#define USB_DIEP_CTL_USBACTEP (0x1UL << 15) /* USB Active Endpoint */ -#define _USB_DIEP_CTL_USBACTEP_SHIFT 15 /* Shift value for USB_USBACTEP */ -#define _USB_DIEP_CTL_USBACTEP_MASK 0x8000UL /* Bit mask for USB_USBACTEP */ -#define _USB_DIEP_CTL_USBACTEP_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEP_CTL */ -#define USB_DIEP_CTL_USBACTEP_DEFAULT (_USB_DIEP_CTL_USBACTEP_DEFAULT << 15) /* Shifted mode DEFAULT for USB_DIEP_CTL */ -#define USB_DIEP_CTL_DPIDEOF (0x1UL << 16) /* Endpoint Data PID / Even or Odd Frame */ -#define _USB_DIEP_CTL_DPIDEOF_SHIFT 16 /* Shift value for USB_DPIDEOF */ -#define _USB_DIEP_CTL_DPIDEOF_MASK 0x10000UL /* Bit mask for USB_DPIDEOF */ -#define _USB_DIEP_CTL_DPIDEOF_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEP_CTL */ -#define _USB_DIEP_CTL_DPIDEOF_DATA0EVEN 0x00000000UL /* Mode DATA0EVEN for USB_DIEP_CTL */ -#define _USB_DIEP_CTL_DPIDEOF_DATA1ODD 0x00000001UL /* Mode DATA1ODD for USB_DIEP_CTL */ -#define USB_DIEP_CTL_DPIDEOF_DEFAULT (_USB_DIEP_CTL_DPIDEOF_DEFAULT << 16) /* Shifted mode DEFAULT for USB_DIEP_CTL */ -#define USB_DIEP_CTL_DPIDEOF_DATA0EVEN (_USB_DIEP_CTL_DPIDEOF_DATA0EVEN << 16) /* Shifted mode DATA0EVEN for USB_DIEP_CTL */ -#define USB_DIEP_CTL_DPIDEOF_DATA1ODD (_USB_DIEP_CTL_DPIDEOF_DATA1ODD << 16) /* Shifted mode DATA1ODD for USB_DIEP_CTL */ -#define USB_DIEP_CTL_NAKSTS (0x1UL << 17) /* NAK Status */ -#define _USB_DIEP_CTL_NAKSTS_SHIFT 17 /* Shift value for USB_NAKSTS */ -#define _USB_DIEP_CTL_NAKSTS_MASK 0x20000UL /* Bit mask for USB_NAKSTS */ -#define _USB_DIEP_CTL_NAKSTS_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEP_CTL */ -#define USB_DIEP_CTL_NAKSTS_DEFAULT (_USB_DIEP_CTL_NAKSTS_DEFAULT << 17) /* Shifted mode DEFAULT for USB_DIEP_CTL */ -#define _USB_DIEP_CTL_EPTYPE_SHIFT 18 /* Shift value for USB_EPTYPE */ -#define _USB_DIEP_CTL_EPTYPE_MASK 0xC0000UL /* Bit mask for USB_EPTYPE */ -#define _USB_DIEP_CTL_EPTYPE_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEP_CTL */ -#define _USB_DIEP_CTL_EPTYPE_CONTROL 0x00000000UL /* Mode CONTROL for USB_DIEP_CTL */ -#define _USB_DIEP_CTL_EPTYPE_ISO 0x00000001UL /* Mode ISO for USB_DIEP_CTL */ -#define _USB_DIEP_CTL_EPTYPE_BULK 0x00000002UL /* Mode BULK for USB_DIEP_CTL */ -#define _USB_DIEP_CTL_EPTYPE_INT 0x00000003UL /* Mode INT for USB_DIEP_CTL */ -#define USB_DIEP_CTL_EPTYPE_DEFAULT (_USB_DIEP_CTL_EPTYPE_DEFAULT << 18) /* Shifted mode DEFAULT for USB_DIEP_CTL */ -#define USB_DIEP_CTL_EPTYPE_CONTROL (_USB_DIEP_CTL_EPTYPE_CONTROL << 18) /* Shifted mode CONTROL for USB_DIEP_CTL */ -#define USB_DIEP_CTL_EPTYPE_ISO (_USB_DIEP_CTL_EPTYPE_ISO << 18) /* Shifted mode ISO for USB_DIEP_CTL */ -#define USB_DIEP_CTL_EPTYPE_BULK (_USB_DIEP_CTL_EPTYPE_BULK << 18) /* Shifted mode BULK for USB_DIEP_CTL */ -#define USB_DIEP_CTL_EPTYPE_INT (_USB_DIEP_CTL_EPTYPE_INT << 18) /* Shifted mode INT for USB_DIEP_CTL */ -#define USB_DIEP_CTL_STALL (0x1UL << 21) /* Handshake */ -#define _USB_DIEP_CTL_STALL_SHIFT 21 /* Shift value for USB_STALL */ -#define _USB_DIEP_CTL_STALL_MASK 0x200000UL /* Bit mask for USB_STALL */ -#define _USB_DIEP_CTL_STALL_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEP_CTL */ -#define USB_DIEP_CTL_STALL_DEFAULT (_USB_DIEP_CTL_STALL_DEFAULT << 21) /* Shifted mode DEFAULT for USB_DIEP_CTL */ -#define _USB_DIEP_CTL_TXFNUM_SHIFT 22 /* Shift value for USB_TXFNUM */ -#define _USB_DIEP_CTL_TXFNUM_MASK 0x3c00000UL /* Bit mask for USB_TXFNUM */ -#define _USB_DIEP_CTL_TXFNUM_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEP_CTL */ -#define USB_DIEP_CTL_TXFNUM_DEFAULT (_USB_DIEP_CTL_TXFNUM_DEFAULT << 22) /* Shifted mode DEFAULT for USB_DIEP_CTL */ -#define USB_DIEP_CTL_CNAK (0x1UL << 26) /* Clear NAK */ -#define _USB_DIEP_CTL_CNAK_SHIFT 26 /* Shift value for USB_CNAK */ -#define _USB_DIEP_CTL_CNAK_MASK 0x4000000UL /* Bit mask for USB_CNAK */ -#define _USB_DIEP_CTL_CNAK_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEP_CTL */ -#define USB_DIEP_CTL_CNAK_DEFAULT (_USB_DIEP_CTL_CNAK_DEFAULT << 26) /* Shifted mode DEFAULT for USB_DIEP_CTL */ -#define USB_DIEP_CTL_SNAK (0x1UL << 27) /* Set NAK */ -#define _USB_DIEP_CTL_SNAK_SHIFT 27 /* Shift value for USB_SNAK */ -#define _USB_DIEP_CTL_SNAK_MASK 0x8000000UL /* Bit mask for USB_SNAK */ -#define _USB_DIEP_CTL_SNAK_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEP_CTL */ -#define USB_DIEP_CTL_SNAK_DEFAULT (_USB_DIEP_CTL_SNAK_DEFAULT << 27) /* Shifted mode DEFAULT for USB_DIEP_CTL */ -#define USB_DIEP_CTL_SETD0PIDEF (0x1UL << 28) /* Set DATA0 PID / Even Frame */ -#define _USB_DIEP_CTL_SETD0PIDEF_SHIFT 28 /* Shift value for USB_SETD0PIDEF */ -#define _USB_DIEP_CTL_SETD0PIDEF_MASK 0x10000000UL /* Bit mask for USB_SETD0PIDEF */ -#define _USB_DIEP_CTL_SETD0PIDEF_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEP_CTL */ -#define USB_DIEP_CTL_SETD0PIDEF_DEFAULT (_USB_DIEP_CTL_SETD0PIDEF_DEFAULT << 28) /* Shifted mode DEFAULT for USB_DIEP_CTL */ -#define USB_DIEP_CTL_SETD1PIDOF (0x1UL << 29) /* Set DATA1 PID / Odd Frame */ -#define _USB_DIEP_CTL_SETD1PIDOF_SHIFT 29 /* Shift value for USB_SETD1PIDOF */ -#define _USB_DIEP_CTL_SETD1PIDOF_MASK 0x20000000UL /* Bit mask for USB_SETD1PIDOF */ -#define _USB_DIEP_CTL_SETD1PIDOF_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEP_CTL */ -#define USB_DIEP_CTL_SETD1PIDOF_DEFAULT (_USB_DIEP_CTL_SETD1PIDOF_DEFAULT << 29) /* Shifted mode DEFAULT for USB_DIEP_CTL */ -#define USB_DIEP_CTL_EPDIS (0x1UL << 30) /* Endpoint Disable */ -#define _USB_DIEP_CTL_EPDIS_SHIFT 30 /* Shift value for USB_EPDIS */ -#define _USB_DIEP_CTL_EPDIS_MASK 0x40000000UL /* Bit mask for USB_EPDIS */ -#define _USB_DIEP_CTL_EPDIS_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEP_CTL */ -#define USB_DIEP_CTL_EPDIS_DEFAULT (_USB_DIEP_CTL_EPDIS_DEFAULT << 30) /* Shifted mode DEFAULT for USB_DIEP_CTL */ -#define USB_DIEP_CTL_EPENA (0x1UL << 31) /* Endpoint Enable */ -#define _USB_DIEP_CTL_EPENA_SHIFT 31 /* Shift value for USB_EPENA */ -#define _USB_DIEP_CTL_EPENA_MASK 0x80000000UL /* Bit mask for USB_EPENA */ -#define _USB_DIEP_CTL_EPENA_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEP_CTL */ -#define USB_DIEP_CTL_EPENA_DEFAULT (_USB_DIEP_CTL_EPENA_DEFAULT << 31) /* Shifted mode DEFAULT for USB_DIEP_CTL */ - -/* Bit fields for USB DIEP_INT */ - -#define _USB_DIEP_INT_RESETVALUE 0x00000080UL /* Default value for USB_DIEP_INT */ -#define _USB_DIEP_INT_MASK 0x000038DFUL /* Mask for USB_DIEP_INT */ - -#define USB_DIEP_INT_XFERCOMPL (0x1UL << 0) /* Transfer Completed Interrupt */ -#define _USB_DIEP_INT_XFERCOMPL_SHIFT 0 /* Shift value for USB_XFERCOMPL */ -#define _USB_DIEP_INT_XFERCOMPL_MASK 0x1UL /* Bit mask for USB_XFERCOMPL */ -#define _USB_DIEP_INT_XFERCOMPL_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEP_INT */ -#define USB_DIEP_INT_XFERCOMPL_DEFAULT (_USB_DIEP_INT_XFERCOMPL_DEFAULT << 0) /* Shifted mode DEFAULT for USB_DIEP_INT */ -#define USB_DIEP_INT_EPDISBLD (0x1UL << 1) /* Endpoint Disabled Interrupt */ -#define _USB_DIEP_INT_EPDISBLD_SHIFT 1 /* Shift value for USB_EPDISBLD */ -#define _USB_DIEP_INT_EPDISBLD_MASK 0x2UL /* Bit mask for USB_EPDISBLD */ -#define _USB_DIEP_INT_EPDISBLD_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEP_INT */ -#define USB_DIEP_INT_EPDISBLD_DEFAULT (_USB_DIEP_INT_EPDISBLD_DEFAULT << 1) /* Shifted mode DEFAULT for USB_DIEP_INT */ -#define USB_DIEP_INT_AHBERR (0x1UL << 2) /* AHB Error */ -#define _USB_DIEP_INT_AHBERR_SHIFT 2 /* Shift value for USB_AHBERR */ -#define _USB_DIEP_INT_AHBERR_MASK 0x4UL /* Bit mask for USB_AHBERR */ -#define _USB_DIEP_INT_AHBERR_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEP_INT */ -#define USB_DIEP_INT_AHBERR_DEFAULT (_USB_DIEP_INT_AHBERR_DEFAULT << 2) /* Shifted mode DEFAULT for USB_DIEP_INT */ -#define USB_DIEP_INT_TIMEOUT (0x1UL << 3) /* Timeout Condition */ -#define _USB_DIEP_INT_TIMEOUT_SHIFT 3 /* Shift value for USB_TIMEOUT */ -#define _USB_DIEP_INT_TIMEOUT_MASK 0x8UL /* Bit mask for USB_TIMEOUT */ -#define _USB_DIEP_INT_TIMEOUT_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEP_INT */ -#define USB_DIEP_INT_TIMEOUT_DEFAULT (_USB_DIEP_INT_TIMEOUT_DEFAULT << 3) /* Shifted mode DEFAULT for USB_DIEP_INT */ -#define USB_DIEP_INT_INTKNTXFEMP (0x1UL << 4) /* IN Token Received When TxFIFO is Empty */ -#define _USB_DIEP_INT_INTKNTXFEMP_SHIFT 4 /* Shift value for USB_INTKNTXFEMP */ -#define _USB_DIEP_INT_INTKNTXFEMP_MASK 0x10UL /* Bit mask for USB_INTKNTXFEMP */ -#define _USB_DIEP_INT_INTKNTXFEMP_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEP_INT */ -#define USB_DIEP_INT_INTKNTXFEMP_DEFAULT (_USB_DIEP_INT_INTKNTXFEMP_DEFAULT << 4) /* Shifted mode DEFAULT for USB_DIEP_INT */ -#define USB_DIEP_INT_INEPNAKEFF (0x1UL << 6) /* IN Endpoint NAK Effective */ -#define _USB_DIEP_INT_INEPNAKEFF_SHIFT 6 /* Shift value for USB_INEPNAKEFF */ -#define _USB_DIEP_INT_INEPNAKEFF_MASK 0x40UL /* Bit mask for USB_INEPNAKEFF */ -#define _USB_DIEP_INT_INEPNAKEFF_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEP_INT */ -#define USB_DIEP_INT_INEPNAKEFF_DEFAULT (_USB_DIEP_INT_INEPNAKEFF_DEFAULT << 6) /* Shifted mode DEFAULT for USB_DIEP_INT */ -#define USB_DIEP_INT_TXFEMP (0x1UL << 7) /* Transmit FIFO Empty */ -#define _USB_DIEP_INT_TXFEMP_SHIFT 7 /* Shift value for USB_TXFEMP */ -#define _USB_DIEP_INT_TXFEMP_MASK 0x80UL /* Bit mask for USB_TXFEMP */ -#define _USB_DIEP_INT_TXFEMP_DEFAULT 0x00000001UL /* Mode DEFAULT for USB_DIEP_INT */ -#define USB_DIEP_INT_TXFEMP_DEFAULT (_USB_DIEP_INT_TXFEMP_DEFAULT << 7) /* Shifted mode DEFAULT for USB_DIEP_INT */ -#define USB_DIEP_INT_PKTDRPSTS (0x1UL << 11) /* Packet Drop Status */ -#define _USB_DIEP_INT_PKTDRPSTS_SHIFT 11 /* Shift value for USB_PKTDRPSTS */ -#define _USB_DIEP_INT_PKTDRPSTS_MASK 0x800UL /* Bit mask for USB_PKTDRPSTS */ -#define _USB_DIEP_INT_PKTDRPSTS_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEP_INT */ -#define USB_DIEP_INT_PKTDRPSTS_DEFAULT (_USB_DIEP_INT_PKTDRPSTS_DEFAULT << 11) /* Shifted mode DEFAULT for USB_DIEP_INT */ -#define USB_DIEP_INT_BBLEERR (0x1UL << 12) /* NAK Interrupt */ -#define _USB_DIEP_INT_BBLEERR_SHIFT 12 /* Shift value for USB_BBLEERR */ -#define _USB_DIEP_INT_BBLEERR_MASK 0x1000UL /* Bit mask for USB_BBLEERR */ -#define _USB_DIEP_INT_BBLEERR_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEP_INT */ -#define USB_DIEP_INT_BBLEERR_DEFAULT (_USB_DIEP_INT_BBLEERR_DEFAULT << 12) /* Shifted mode DEFAULT for USB_DIEP_INT */ -#define USB_DIEP_INT_NAKINTRPT (0x1UL << 13) /* NAK Interrupt */ -#define _USB_DIEP_INT_NAKINTRPT_SHIFT 13 /* Shift value for USB_NAKINTRPT */ -#define _USB_DIEP_INT_NAKINTRPT_MASK 0x2000UL /* Bit mask for USB_NAKINTRPT */ -#define _USB_DIEP_INT_NAKINTRPT_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEP_INT */ -#define USB_DIEP_INT_NAKINTRPT_DEFAULT (_USB_DIEP_INT_NAKINTRPT_DEFAULT << 13) /* Shifted mode DEFAULT for USB_DIEP_INT */ - -/* Bit fields for USB DIEP_TSIZ */ - -#define _USB_DIEP_TSIZ_RESETVALUE 0x00000000UL /* Default value for USB_DIEP_TSIZ */ -#define _USB_DIEP_TSIZ_MASK 0x7FFFFFFFUL /* Mask for USB_DIEP_TSIZ */ - -#define _USB_DIEP_TSIZ_XFERSIZE_SHIFT 0 /* Shift value for USB_XFERSIZE */ -#define _USB_DIEP_TSIZ_XFERSIZE_MASK 0x7FFFFUL /* Bit mask for USB_XFERSIZE */ -#define _USB_DIEP_TSIZ_XFERSIZE_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEP_TSIZ */ -#define USB_DIEP_TSIZ_XFERSIZE_DEFAULT (_USB_DIEP_TSIZ_XFERSIZE_DEFAULT << 0) /* Shifted mode DEFAULT for USB_DIEP_TSIZ */ -#define _USB_DIEP_TSIZ_PKTCNT_SHIFT 19 /* Shift value for USB_PKTCNT */ -#define _USB_DIEP_TSIZ_PKTCNT_MASK 0x1FF80000UL /* Bit mask for USB_PKTCNT */ -#define _USB_DIEP_TSIZ_PKTCNT_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEP_TSIZ */ -#define USB_DIEP_TSIZ_PKTCNT_DEFAULT (_USB_DIEP_TSIZ_PKTCNT_DEFAULT << 19) /* Shifted mode DEFAULT for USB_DIEP_TSIZ */ -#define _USB_DIEP_TSIZ_MC_SHIFT 29 /* Shift value for USB_MC */ -#define _USB_DIEP_TSIZ_MC_MASK 0x60000000UL /* Bit mask for USB_MC */ -#define _USB_DIEP_TSIZ_MC_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEP_TSIZ */ -#define USB_DIEP_TSIZ_MC_DEFAULT (_USB_DIEP_TSIZ_MC_DEFAULT << 29) /* Shifted mode DEFAULT for USB_DIEP_TSIZ */ - -/* Bit fields for USB DIEP_DMAADDR */ - -#define _USB_DIEP_DMAADDR_RESETVALUE 0x00000000UL /* Default value for USB_DIEP_DMAADDR */ -#define _USB_DIEP_DMAADDR_MASK 0xFFFFFFFFUL /* Mask for USB_DIEP_DMAADDR */ - -#define _USB_DIEP_DMAADDR_DMAADDR_SHIFT 0 /* Shift value for USB_DMAADDR */ -#define _USB_DIEP_DMAADDR_DMAADDR_MASK 0xFFFFFFFFUL /* Bit mask for USB_DMAADDR */ -#define _USB_DIEP_DMAADDR_DMAADDR_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEP_DMAADDR */ -#define USB_DIEP_DMAADDR_DMAADDR_DEFAULT (_USB_DIEP_DMAADDR_DMAADDR_DEFAULT << 0) /* Shifted mode DEFAULT for USB_DIEP_DMAADDR */ - -/* Bit fields for USB DIEP_TXFSTS */ - -#define _USB_DIEP_TXFSTS_RESETVALUE 0x00000200UL /* Default value for USB_DIEP_TXFSTS */ -#define _USB_DIEP_TXFSTS_MASK 0x0000FFFFUL /* Mask for USB_DIEP_TXFSTS */ - -#define _USB_DIEP_TXFSTS_SPCAVAIL_SHIFT 0 /* Shift value for USB_SPCAVAIL */ -#define _USB_DIEP_TXFSTS_SPCAVAIL_MASK 0xFFFFUL /* Bit mask for USB_SPCAVAIL */ -#define _USB_DIEP_TXFSTS_SPCAVAIL_DEFAULT 0x00000200UL /* Mode DEFAULT for USB_DIEP_TXFSTS */ -#define USB_DIEP_TXFSTS_SPCAVAIL_DEFAULT (_USB_DIEP_TXFSTS_SPCAVAIL_DEFAULT << 0) /* Shifted mode DEFAULT for USB_DIEP_TXFSTS */ +/* Bit fields for USB DIEPCTL */ + +#define _USB_DIEPCTL_RESETVALUE 0x00000000UL /* Default value for USB_DIEPCTL */ +#define _USB_DIEPCTL_MASK 0xFFEF87FFUL /* Mask for USB_DIEPCTL */ + +#define _USB_DIEPCTL_MPS_SHIFT 0 /* Shift value for USB_MPS */ +#define _USB_DIEPCTL_MPS_MASK 0x7FFUL /* Bit mask for USB_MPS */ +#define _USB_DIEPCTL_MPS_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEPCTL */ +#define USB_DIEPCTL_MPS_DEFAULT (_USB_DIEPCTL_MPS_DEFAULT << 0) /* Shifted mode DEFAULT for USB_DIEPCTL */ +#define USB_DIEPCTL_USBACTEP (0x1UL << 15) /* USB Active Endpoint */ +#define _USB_DIEPCTL_USBACTEP_SHIFT 15 /* Shift value for USB_USBACTEP */ +#define _USB_DIEPCTL_USBACTEP_MASK 0x8000UL /* Bit mask for USB_USBACTEP */ +#define _USB_DIEPCTL_USBACTEP_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEPCTL */ +#define USB_DIEPCTL_USBACTEP_DEFAULT (_USB_DIEPCTL_USBACTEP_DEFAULT << 15) /* Shifted mode DEFAULT for USB_DIEPCTL */ +#define USB_DIEPCTL_DPIDEOF (0x1UL << 16) /* Endpoint Data PID / Even or Odd Frame */ +#define _USB_DIEPCTL_DPIDEOF_SHIFT 16 /* Shift value for USB_DPIDEOF */ +#define _USB_DIEPCTL_DPIDEOF_MASK 0x10000UL /* Bit mask for USB_DPIDEOF */ +#define _USB_DIEPCTL_DPIDEOF_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEPCTL */ +#define _USB_DIEPCTL_DPIDEOF_DATA0EVEN 0x00000000UL /* Mode DATA0EVEN for USB_DIEPCTL */ +#define _USB_DIEPCTL_DPIDEOF_DATA1ODD 0x00000001UL /* Mode DATA1ODD for USB_DIEPCTL */ +#define USB_DIEPCTL_DPIDEOF_DEFAULT (_USB_DIEPCTL_DPIDEOF_DEFAULT << 16) /* Shifted mode DEFAULT for USB_DIEPCTL */ +#define USB_DIEPCTL_DPIDEOF_DATA0EVEN (_USB_DIEPCTL_DPIDEOF_DATA0EVEN << 16) /* Shifted mode DATA0EVEN for USB_DIEPCTL */ +#define USB_DIEPCTL_DPIDEOF_DATA1ODD (_USB_DIEPCTL_DPIDEOF_DATA1ODD << 16) /* Shifted mode DATA1ODD for USB_DIEPCTL */ +#define USB_DIEPCTL_NAKSTS (0x1UL << 17) /* NAK Status */ +#define _USB_DIEPCTL_NAKSTS_SHIFT 17 /* Shift value for USB_NAKSTS */ +#define _USB_DIEPCTL_NAKSTS_MASK 0x20000UL /* Bit mask for USB_NAKSTS */ +#define _USB_DIEPCTL_NAKSTS_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEPCTL */ +#define USB_DIEPCTL_NAKSTS_DEFAULT (_USB_DIEPCTL_NAKSTS_DEFAULT << 17) /* Shifted mode DEFAULT for USB_DIEPCTL */ +#define _USB_DIEPCTL_EPTYPE_SHIFT 18 /* Shift value for USB_EPTYPE */ +#define _USB_DIEPCTL_EPTYPE_MASK 0xC0000UL /* Bit mask for USB_EPTYPE */ +#define _USB_DIEPCTL_EPTYPE_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEPCTL */ +#define _USB_DIEPCTL_EPTYPE_CONTROL 0x00000000UL /* Mode CONTROL for USB_DIEPCTL */ +#define _USB_DIEPCTL_EPTYPE_ISO 0x00000001UL /* Mode ISO for USB_DIEPCTL */ +#define _USB_DIEPCTL_EPTYPE_BULK 0x00000002UL /* Mode BULK for USB_DIEPCTL */ +#define _USB_DIEPCTL_EPTYPE_INT 0x00000003UL /* Mode INT for USB_DIEPCTL */ +#define USB_DIEPCTL_EPTYPE_DEFAULT (_USB_DIEPCTL_EPTYPE_DEFAULT << 18) /* Shifted mode DEFAULT for USB_DIEPCTL */ +#define USB_DIEPCTL_EPTYPE_CONTROL (_USB_DIEPCTL_EPTYPE_CONTROL << 18) /* Shifted mode CONTROL for USB_DIEPCTL */ +#define USB_DIEPCTL_EPTYPE_ISO (_USB_DIEPCTL_EPTYPE_ISO << 18) /* Shifted mode ISO for USB_DIEPCTL */ +#define USB_DIEPCTL_EPTYPE_BULK (_USB_DIEPCTL_EPTYPE_BULK << 18) /* Shifted mode BULK for USB_DIEPCTL */ +#define USB_DIEPCTL_EPTYPE_INT (_USB_DIEPCTL_EPTYPE_INT << 18) /* Shifted mode INT for USB_DIEPCTL */ +#define USB_DIEPCTL_STALL (0x1UL << 21) /* Handshake */ +#define _USB_DIEPCTL_STALL_SHIFT 21 /* Shift value for USB_STALL */ +#define _USB_DIEPCTL_STALL_MASK 0x200000UL /* Bit mask for USB_STALL */ +#define _USB_DIEPCTL_STALL_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEPCTL */ +#define USB_DIEPCTL_STALL_DEFAULT (_USB_DIEPCTL_STALL_DEFAULT << 21) /* Shifted mode DEFAULT for USB_DIEPCTL */ +#define _USB_DIEPCTL_TXFNUM_SHIFT 22 /* Shift value for USB_TXFNUM */ +#define _USB_DIEPCTL_TXFNUM_MASK 0x3c00000UL /* Bit mask for USB_TXFNUM */ +#define _USB_DIEPCTL_TXFNUM_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEPCTL */ +#define USB_DIEPCTL_TXFNUM_DEFAULT (_USB_DIEPCTL_TXFNUM_DEFAULT << 22) /* Shifted mode DEFAULT for USB_DIEPCTL */ +#define USB_DIEPCTL_CNAK (0x1UL << 26) /* Clear NAK */ +#define _USB_DIEPCTL_CNAK_SHIFT 26 /* Shift value for USB_CNAK */ +#define _USB_DIEPCTL_CNAK_MASK 0x4000000UL /* Bit mask for USB_CNAK */ +#define _USB_DIEPCTL_CNAK_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEPCTL */ +#define USB_DIEPCTL_CNAK_DEFAULT (_USB_DIEPCTL_CNAK_DEFAULT << 26) /* Shifted mode DEFAULT for USB_DIEPCTL */ +#define USB_DIEPCTL_SNAK (0x1UL << 27) /* Set NAK */ +#define _USB_DIEPCTL_SNAK_SHIFT 27 /* Shift value for USB_SNAK */ +#define _USB_DIEPCTL_SNAK_MASK 0x8000000UL /* Bit mask for USB_SNAK */ +#define _USB_DIEPCTL_SNAK_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEPCTL */ +#define USB_DIEPCTL_SNAK_DEFAULT (_USB_DIEPCTL_SNAK_DEFAULT << 27) /* Shifted mode DEFAULT for USB_DIEPCTL */ +#define USB_DIEPCTL_SETD0PIDEF (0x1UL << 28) /* Set DATA0 PID / Even Frame */ +#define _USB_DIEPCTL_SETD0PIDEF_SHIFT 28 /* Shift value for USB_SETD0PIDEF */ +#define _USB_DIEPCTL_SETD0PIDEF_MASK 0x10000000UL /* Bit mask for USB_SETD0PIDEF */ +#define _USB_DIEPCTL_SETD0PIDEF_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEPCTL */ +#define USB_DIEPCTL_SETD0PIDEF_DEFAULT (_USB_DIEPCTL_SETD0PIDEF_DEFAULT << 28) /* Shifted mode DEFAULT for USB_DIEPCTL */ +#define USB_DIEPCTL_SETD1PIDOF (0x1UL << 29) /* Set DATA1 PID / Odd Frame */ +#define _USB_DIEPCTL_SETD1PIDOF_SHIFT 29 /* Shift value for USB_SETD1PIDOF */ +#define _USB_DIEPCTL_SETD1PIDOF_MASK 0x20000000UL /* Bit mask for USB_SETD1PIDOF */ +#define _USB_DIEPCTL_SETD1PIDOF_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEPCTL */ +#define USB_DIEPCTL_SETD1PIDOF_DEFAULT (_USB_DIEPCTL_SETD1PIDOF_DEFAULT << 29) /* Shifted mode DEFAULT for USB_DIEPCTL */ +#define USB_DIEPCTL_EPDIS (0x1UL << 30) /* Endpoint Disable */ +#define _USB_DIEPCTL_EPDIS_SHIFT 30 /* Shift value for USB_EPDIS */ +#define _USB_DIEPCTL_EPDIS_MASK 0x40000000UL /* Bit mask for USB_EPDIS */ +#define _USB_DIEPCTL_EPDIS_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEPCTL */ +#define USB_DIEPCTL_EPDIS_DEFAULT (_USB_DIEPCTL_EPDIS_DEFAULT << 30) /* Shifted mode DEFAULT for USB_DIEPCTL */ +#define USB_DIEPCTL_EPENA (0x1UL << 31) /* Endpoint Enable */ +#define _USB_DIEPCTL_EPENA_SHIFT 31 /* Shift value for USB_EPENA */ +#define _USB_DIEPCTL_EPENA_MASK 0x80000000UL /* Bit mask for USB_EPENA */ +#define _USB_DIEPCTL_EPENA_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEPCTL */ +#define USB_DIEPCTL_EPENA_DEFAULT (_USB_DIEPCTL_EPENA_DEFAULT << 31) /* Shifted mode DEFAULT for USB_DIEPCTL */ + +/* Bit fields for USB DIEPINT */ + +#define _USB_DIEPINT_RESETVALUE 0x00000080UL /* Default value for USB_DIEPINT */ +#define _USB_DIEPINT_MASK 0x000038DFUL /* Mask for USB_DIEPINT */ + +#define USB_DIEPINT_XFERCOMPL (0x1UL << 0) /* Transfer Completed Interrupt */ +#define _USB_DIEPINT_XFERCOMPL_SHIFT 0 /* Shift value for USB_XFERCOMPL */ +#define _USB_DIEPINT_XFERCOMPL_MASK 0x1UL /* Bit mask for USB_XFERCOMPL */ +#define _USB_DIEPINT_XFERCOMPL_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEPINT */ +#define USB_DIEPINT_XFERCOMPL_DEFAULT (_USB_DIEPINT_XFERCOMPL_DEFAULT << 0) /* Shifted mode DEFAULT for USB_DIEPINT */ +#define USB_DIEPINT_EPDISBLD (0x1UL << 1) /* Endpoint Disabled Interrupt */ +#define _USB_DIEPINT_EPDISBLD_SHIFT 1 /* Shift value for USB_EPDISBLD */ +#define _USB_DIEPINT_EPDISBLD_MASK 0x2UL /* Bit mask for USB_EPDISBLD */ +#define _USB_DIEPINT_EPDISBLD_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEPINT */ +#define USB_DIEPINT_EPDISBLD_DEFAULT (_USB_DIEPINT_EPDISBLD_DEFAULT << 1) /* Shifted mode DEFAULT for USB_DIEPINT */ +#define USB_DIEPINT_AHBERR (0x1UL << 2) /* AHB Error */ +#define _USB_DIEPINT_AHBERR_SHIFT 2 /* Shift value for USB_AHBERR */ +#define _USB_DIEPINT_AHBERR_MASK 0x4UL /* Bit mask for USB_AHBERR */ +#define _USB_DIEPINT_AHBERR_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEPINT */ +#define USB_DIEPINT_AHBERR_DEFAULT (_USB_DIEPINT_AHBERR_DEFAULT << 2) /* Shifted mode DEFAULT for USB_DIEPINT */ +#define USB_DIEPINT_TIMEOUT (0x1UL << 3) /* Timeout Condition */ +#define _USB_DIEPINT_TIMEOUT_SHIFT 3 /* Shift value for USB_TIMEOUT */ +#define _USB_DIEPINT_TIMEOUT_MASK 0x8UL /* Bit mask for USB_TIMEOUT */ +#define _USB_DIEPINT_TIMEOUT_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEPINT */ +#define USB_DIEPINT_TIMEOUT_DEFAULT (_USB_DIEPINT_TIMEOUT_DEFAULT << 3) /* Shifted mode DEFAULT for USB_DIEPINT */ +#define USB_DIEPINT_INTKNTXFEMP (0x1UL << 4) /* IN Token Received When TxFIFO is Empty */ +#define _USB_DIEPINT_INTKNTXFEMP_SHIFT 4 /* Shift value for USB_INTKNTXFEMP */ +#define _USB_DIEPINT_INTKNTXFEMP_MASK 0x10UL /* Bit mask for USB_INTKNTXFEMP */ +#define _USB_DIEPINT_INTKNTXFEMP_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEPINT */ +#define USB_DIEPINT_INTKNTXFEMP_DEFAULT (_USB_DIEPINT_INTKNTXFEMP_DEFAULT << 4) /* Shifted mode DEFAULT for USB_DIEPINT */ +#define USB_DIEPINT_INEPNAKEFF (0x1UL << 6) /* IN Endpoint NAK Effective */ +#define _USB_DIEPINT_INEPNAKEFF_SHIFT 6 /* Shift value for USB_INEPNAKEFF */ +#define _USB_DIEPINT_INEPNAKEFF_MASK 0x40UL /* Bit mask for USB_INEPNAKEFF */ +#define _USB_DIEPINT_INEPNAKEFF_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEPINT */ +#define USB_DIEPINT_INEPNAKEFF_DEFAULT (_USB_DIEPINT_INEPNAKEFF_DEFAULT << 6) /* Shifted mode DEFAULT for USB_DIEPINT */ +#define USB_DIEPINT_TXFEMP (0x1UL << 7) /* Transmit FIFO Empty */ +#define _USB_DIEPINT_TXFEMP_SHIFT 7 /* Shift value for USB_TXFEMP */ +#define _USB_DIEPINT_TXFEMP_MASK 0x80UL /* Bit mask for USB_TXFEMP */ +#define _USB_DIEPINT_TXFEMP_DEFAULT 0x00000001UL /* Mode DEFAULT for USB_DIEPINT */ +#define USB_DIEPINT_TXFEMP_DEFAULT (_USB_DIEPINT_TXFEMP_DEFAULT << 7) /* Shifted mode DEFAULT for USB_DIEPINT */ +#define USB_DIEPINT_PKTDRPSTS (0x1UL << 11) /* Packet Drop Status */ +#define _USB_DIEPINT_PKTDRPSTS_SHIFT 11 /* Shift value for USB_PKTDRPSTS */ +#define _USB_DIEPINT_PKTDRPSTS_MASK 0x800UL /* Bit mask for USB_PKTDRPSTS */ +#define _USB_DIEPINT_PKTDRPSTS_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEPINT */ +#define USB_DIEPINT_PKTDRPSTS_DEFAULT (_USB_DIEPINT_PKTDRPSTS_DEFAULT << 11) /* Shifted mode DEFAULT for USB_DIEPINT */ +#define USB_DIEPINT_BBLEERR (0x1UL << 12) /* NAK Interrupt */ +#define _USB_DIEPINT_BBLEERR_SHIFT 12 /* Shift value for USB_BBLEERR */ +#define _USB_DIEPINT_BBLEERR_MASK 0x1000UL /* Bit mask for USB_BBLEERR */ +#define _USB_DIEPINT_BBLEERR_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEPINT */ +#define USB_DIEPINT_BBLEERR_DEFAULT (_USB_DIEPINT_BBLEERR_DEFAULT << 12) /* Shifted mode DEFAULT for USB_DIEPINT */ +#define USB_DIEPINT_NAKINTRPT (0x1UL << 13) /* NAK Interrupt */ +#define _USB_DIEPINT_NAKINTRPT_SHIFT 13 /* Shift value for USB_NAKINTRPT */ +#define _USB_DIEPINT_NAKINTRPT_MASK 0x2000UL /* Bit mask for USB_NAKINTRPT */ +#define _USB_DIEPINT_NAKINTRPT_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEPINT */ +#define USB_DIEPINT_NAKINTRPT_DEFAULT (_USB_DIEPINT_NAKINTRPT_DEFAULT << 13) /* Shifted mode DEFAULT for USB_DIEPINT */ + +/* Bit fields for USB DIEPTSIZ */ + +#define _USB_DIEPTSIZ_RESETVALUE 0x00000000UL /* Default value for USB_DIEPTSIZ */ +#define _USB_DIEPTSIZ_MASK 0x7FFFFFFFUL /* Mask for USB_DIEPTSIZ */ + +#define _USB_DIEPTSIZ_XFERSIZE_SHIFT 0 /* Shift value for USB_XFERSIZE */ +#define _USB_DIEPTSIZ_XFERSIZE_MASK 0x7FFFFUL /* Bit mask for USB_XFERSIZE */ +#define _USB_DIEPTSIZ_XFERSIZE_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEPTSIZ */ +#define USB_DIEPTSIZ_XFERSIZE_DEFAULT (_USB_DIEPTSIZ_XFERSIZE_DEFAULT << 0) /* Shifted mode DEFAULT for USB_DIEPTSIZ */ +#define _USB_DIEPTSIZ_PKTCNT_SHIFT 19 /* Shift value for USB_PKTCNT */ +#define _USB_DIEPTSIZ_PKTCNT_MASK 0x1FF80000UL /* Bit mask for USB_PKTCNT */ +#define _USB_DIEPTSIZ_PKTCNT_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEPTSIZ */ +#define USB_DIEPTSIZ_PKTCNT_DEFAULT (_USB_DIEPTSIZ_PKTCNT_DEFAULT << 19) /* Shifted mode DEFAULT for USB_DIEPTSIZ */ +#define _USB_DIEPTSIZ_MC_SHIFT 29 /* Shift value for USB_MC */ +#define _USB_DIEPTSIZ_MC_MASK 0x60000000UL /* Bit mask for USB_MC */ +#define _USB_DIEPTSIZ_MC_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEPTSIZ */ +#define USB_DIEPTSIZ_MC_DEFAULT (_USB_DIEPTSIZ_MC_DEFAULT << 29) /* Shifted mode DEFAULT for USB_DIEPTSIZ */ + +/* Bit fields for USB DIEPDMAADDR */ + +#define _USB_DIEPDMAADDR_RESETVALUE 0x00000000UL /* Default value for USB_DIEPDMAADDR */ +#define _USB_DIEPDMAADDR_MASK 0xFFFFFFFFUL /* Mask for USB_DIEPDMAADDR */ + +#define _USB_DIEPDMAADDR_DMAADDR_SHIFT 0 /* Shift value for USB_DMAADDR */ +#define _USB_DIEPDMAADDR_DMAADDR_MASK 0xFFFFFFFFUL /* Bit mask for USB_DMAADDR */ +#define _USB_DIEPDMAADDR_DMAADDR_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DIEPDMAADDR */ +#define USB_DIEPDMAADDR_DMAADDR_DEFAULT (_USB_DIEPDMAADDR_DMAADDR_DEFAULT << 0) /* Shifted mode DEFAULT for USB_DIEPDMAADDR */ + +/* Bit fields for USB DIEPTXFSTS */ + +#define _USB_DIEPTXFSTS_RESETVALUE 0x00000200UL /* Default value for USB_DIEPTXFSTS */ +#define _USB_DIEPTXFSTS_MASK 0x0000FFFFUL /* Mask for USB_DIEPTXFSTS */ + +#define _USB_DIEPTXFSTS_SPCAVAIL_SHIFT 0 /* Shift value for USB_SPCAVAIL */ +#define _USB_DIEPTXFSTS_SPCAVAIL_MASK 0xFFFFUL /* Bit mask for USB_SPCAVAIL */ +#define _USB_DIEPTXFSTS_SPCAVAIL_DEFAULT 0x00000200UL /* Mode DEFAULT for USB_DIEPTXFSTS */ +#define USB_DIEPTXFSTS_SPCAVAIL_DEFAULT (_USB_DIEPTXFSTS_SPCAVAIL_DEFAULT << 0) /* Shifted mode DEFAULT for USB_DIEPTXFSTS */ /* Bit fields for USB DOEP0CTL */ @@ -2757,173 +2757,173 @@ #define _USB_DOEP0DMAADDR_DOEP0DMAADDR_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEP0DMAADDR */ #define USB_DOEP0DMAADDR_DOEP0DMAADDR_DEFAULT (_USB_DOEP0DMAADDR_DOEP0DMAADDR_DEFAULT << 0) /* Shifted mode DEFAULT for USB_DOEP0DMAADDR */ -/* Bit fields for USB DOEP_CTL */ - -#define _USB_DOEP_CTL_RESETVALUE 0x00000000UL /* Default value for USB_DOEP_CTL */ -#define _USB_DOEP_CTL_MASK 0xFC3F87FFUL /* Mask for USB_DOEP_CTL */ - -#define _USB_DOEP_CTL_MPS_SHIFT 0 /* Shift value for USB_MPS */ -#define _USB_DOEP_CTL_MPS_MASK 0x7FFUL /* Bit mask for USB_MPS */ -#define _USB_DOEP_CTL_MPS_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEP_CTL */ -#define USB_DOEP_CTL_MPS_DEFAULT (_USB_DOEP_CTL_MPS_DEFAULT << 0) /* Shifted mode DEFAULT for USB_DOEP_CTL */ -#define USB_DOEP_CTL_USBACTEP (0x1UL << 15) /* USB Active Endpoint */ -#define _USB_DOEP_CTL_USBACTEP_SHIFT 15 /* Shift value for USB_USBACTEP */ -#define _USB_DOEP_CTL_USBACTEP_MASK 0x8000UL /* Bit mask for USB_USBACTEP */ -#define _USB_DOEP_CTL_USBACTEP_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEP_CTL */ -#define USB_DOEP_CTL_USBACTEP_DEFAULT (_USB_DOEP_CTL_USBACTEP_DEFAULT << 15) /* Shifted mode DEFAULT for USB_DOEP_CTL */ -#define USB_DOEP_CTL_DPIDEOF (0x1UL << 16) /* Endpoint Data PID / Even-odd Frame */ -#define _USB_DOEP_CTL_DPIDEOF_SHIFT 16 /* Shift value for USB_DPIDEOF */ -#define _USB_DOEP_CTL_DPIDEOF_MASK 0x10000UL /* Bit mask for USB_DPIDEOF */ -#define _USB_DOEP_CTL_DPIDEOF_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEP_CTL */ -#define _USB_DOEP_CTL_DPIDEOF_DATA0EVEN 0x00000000UL /* Mode DATA0EVEN for USB_DOEP_CTL */ -#define _USB_DOEP_CTL_DPIDEOF_DATA1ODD 0x00000001UL /* Mode DATA1ODD for USB_DOEP_CTL */ -#define USB_DOEP_CTL_DPIDEOF_DEFAULT (_USB_DOEP_CTL_DPIDEOF_DEFAULT << 16) /* Shifted mode DEFAULT for USB_DOEP_CTL */ -#define USB_DOEP_CTL_DPIDEOF_DATA0EVEN (_USB_DOEP_CTL_DPIDEOF_DATA0EVEN << 16) /* Shifted mode DATA0EVEN for USB_DOEP_CTL */ -#define USB_DOEP_CTL_DPIDEOF_DATA1ODD (_USB_DOEP_CTL_DPIDEOF_DATA1ODD << 16) /* Shifted mode DATA1ODD for USB_DOEP_CTL */ -#define USB_DOEP_CTL_NAKSTS (0x1UL << 17) /* NAK Status */ -#define _USB_DOEP_CTL_NAKSTS_SHIFT 17 /* Shift value for USB_NAKSTS */ -#define _USB_DOEP_CTL_NAKSTS_MASK 0x20000UL /* Bit mask for USB_NAKSTS */ -#define _USB_DOEP_CTL_NAKSTS_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEP_CTL */ -#define USB_DOEP_CTL_NAKSTS_DEFAULT (_USB_DOEP_CTL_NAKSTS_DEFAULT << 17) /* Shifted mode DEFAULT for USB_DOEP_CTL */ -#define _USB_DOEP_CTL_EPTYPE_SHIFT 18 /* Shift value for USB_EPTYPE */ -#define _USB_DOEP_CTL_EPTYPE_MASK 0xC0000UL /* Bit mask for USB_EPTYPE */ -#define _USB_DOEP_CTL_EPTYPE_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEP_CTL */ -#define _USB_DOEP_CTL_EPTYPE_CONTROL 0x00000000UL /* Mode CONTROL for USB_DOEP_CTL */ -#define _USB_DOEP_CTL_EPTYPE_ISO 0x00000001UL /* Mode ISO for USB_DOEP_CTL */ -#define _USB_DOEP_CTL_EPTYPE_BULK 0x00000002UL /* Mode BULK for USB_DOEP_CTL */ -#define _USB_DOEP_CTL_EPTYPE_INT 0x00000003UL /* Mode INT for USB_DOEP_CTL */ -#define USB_DOEP_CTL_EPTYPE_DEFAULT (_USB_DOEP_CTL_EPTYPE_DEFAULT << 18) /* Shifted mode DEFAULT for USB_DOEP_CTL */ -#define USB_DOEP_CTL_EPTYPE_CONTROL (_USB_DOEP_CTL_EPTYPE_CONTROL << 18) /* Shifted mode CONTROL for USB_DOEP_CTL */ -#define USB_DOEP_CTL_EPTYPE_ISO (_USB_DOEP_CTL_EPTYPE_ISO << 18) /* Shifted mode ISO for USB_DOEP_CTL */ -#define USB_DOEP_CTL_EPTYPE_BULK (_USB_DOEP_CTL_EPTYPE_BULK << 18) /* Shifted mode BULK for USB_DOEP_CTL */ -#define USB_DOEP_CTL_EPTYPE_INT (_USB_DOEP_CTL_EPTYPE_INT << 18) /* Shifted mode INT for USB_DOEP_CTL */ -#define USB_DOEP_CTL_SNP (0x1UL << 20) /* Snoop Mode */ -#define _USB_DOEP_CTL_SNP_SHIFT 20 /* Shift value for USB_SNP */ -#define _USB_DOEP_CTL_SNP_MASK 0x100000UL /* Bit mask for USB_SNP */ -#define _USB_DOEP_CTL_SNP_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEP_CTL */ -#define USB_DOEP_CTL_SNP_DEFAULT (_USB_DOEP_CTL_SNP_DEFAULT << 20) /* Shifted mode DEFAULT for USB_DOEP_CTL */ -#define USB_DOEP_CTL_STALL (0x1UL << 21) /* STALL Handshake */ -#define _USB_DOEP_CTL_STALL_SHIFT 21 /* Shift value for USB_STALL */ -#define _USB_DOEP_CTL_STALL_MASK 0x200000UL /* Bit mask for USB_STALL */ -#define _USB_DOEP_CTL_STALL_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEP_CTL */ -#define USB_DOEP_CTL_STALL_DEFAULT (_USB_DOEP_CTL_STALL_DEFAULT << 21) /* Shifted mode DEFAULT for USB_DOEP_CTL */ -#define USB_DOEP_CTL_CNAK (0x1UL << 26) /* Clear NAK */ -#define _USB_DOEP_CTL_CNAK_SHIFT 26 /* Shift value for USB_CNAK */ -#define _USB_DOEP_CTL_CNAK_MASK 0x4000000UL /* Bit mask for USB_CNAK */ -#define _USB_DOEP_CTL_CNAK_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEP_CTL */ -#define USB_DOEP_CTL_CNAK_DEFAULT (_USB_DOEP_CTL_CNAK_DEFAULT << 26) /* Shifted mode DEFAULT for USB_DOEP_CTL */ -#define USB_DOEP_CTL_SNAK (0x1UL << 27) /* Set NAK */ -#define _USB_DOEP_CTL_SNAK_SHIFT 27 /* Shift value for USB_SNAK */ -#define _USB_DOEP_CTL_SNAK_MASK 0x8000000UL /* Bit mask for USB_SNAK */ -#define _USB_DOEP_CTL_SNAK_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEP_CTL */ -#define USB_DOEP_CTL_SNAK_DEFAULT (_USB_DOEP_CTL_SNAK_DEFAULT << 27) /* Shifted mode DEFAULT for USB_DOEP_CTL */ -#define USB_DOEP_CTL_SETD0PIDEF (0x1UL << 28) /* Set DATA0 PID / Even Frame */ -#define _USB_DOEP_CTL_SETD0PIDEF_SHIFT 28 /* Shift value for USB_SETD0PIDEF */ -#define _USB_DOEP_CTL_SETD0PIDEF_MASK 0x10000000UL /* Bit mask for USB_SETD0PIDEF */ -#define _USB_DOEP_CTL_SETD0PIDEF_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEP_CTL */ -#define USB_DOEP_CTL_SETD0PIDEF_DEFAULT (_USB_DOEP_CTL_SETD0PIDEF_DEFAULT << 28) /* Shifted mode DEFAULT for USB_DOEP_CTL */ -#define USB_DOEP_CTL_SETD1PIDOF (0x1UL << 29) /* Set DATA1 PID / Odd Frame */ -#define _USB_DOEP_CTL_SETD1PIDOF_SHIFT 29 /* Shift value for USB_SETD1PIDOF */ -#define _USB_DOEP_CTL_SETD1PIDOF_MASK 0x20000000UL /* Bit mask for USB_SETD1PIDOF */ -#define _USB_DOEP_CTL_SETD1PIDOF_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEP_CTL */ -#define USB_DOEP_CTL_SETD1PIDOF_DEFAULT (_USB_DOEP_CTL_SETD1PIDOF_DEFAULT << 29) /* Shifted mode DEFAULT for USB_DOEP_CTL */ -#define USB_DOEP_CTL_EPDIS (0x1UL << 30) /* Endpoint Disable */ -#define _USB_DOEP_CTL_EPDIS_SHIFT 30 /* Shift value for USB_EPDIS */ -#define _USB_DOEP_CTL_EPDIS_MASK 0x40000000UL /* Bit mask for USB_EPDIS */ -#define _USB_DOEP_CTL_EPDIS_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEP_CTL */ -#define USB_DOEP_CTL_EPDIS_DEFAULT (_USB_DOEP_CTL_EPDIS_DEFAULT << 30) /* Shifted mode DEFAULT for USB_DOEP_CTL */ -#define USB_DOEP_CTL_EPENA (0x1UL << 31) /* Endpoint Enable */ -#define _USB_DOEP_CTL_EPENA_SHIFT 31 /* Shift value for USB_EPENA */ -#define _USB_DOEP_CTL_EPENA_MASK 0x80000000UL /* Bit mask for USB_EPENA */ -#define _USB_DOEP_CTL_EPENA_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEP_CTL */ -#define USB_DOEP_CTL_EPENA_DEFAULT (_USB_DOEP_CTL_EPENA_DEFAULT << 31) /* Shifted mode DEFAULT for USB_DOEP_CTL */ - -/* Bit fields for USB DOEP_INT */ - -#define _USB_DOEP_INT_RESETVALUE 0x00000000UL /* Default value for USB_DOEP_INT */ -#define _USB_DOEP_INT_MASK 0x0000385FUL /* Mask for USB_DOEP_INT */ - -#define USB_DOEP_INT_XFERCOMPL (0x1UL << 0) /* Transfer Completed Interrupt */ -#define _USB_DOEP_INT_XFERCOMPL_SHIFT 0 /* Shift value for USB_XFERCOMPL */ -#define _USB_DOEP_INT_XFERCOMPL_MASK 0x1UL /* Bit mask for USB_XFERCOMPL */ -#define _USB_DOEP_INT_XFERCOMPL_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEP_INT */ -#define USB_DOEP_INT_XFERCOMPL_DEFAULT (_USB_DOEP_INT_XFERCOMPL_DEFAULT << 0) /* Shifted mode DEFAULT for USB_DOEP_INT */ -#define USB_DOEP_INT_EPDISBLD (0x1UL << 1) /* Endpoint Disabled Interrupt */ -#define _USB_DOEP_INT_EPDISBLD_SHIFT 1 /* Shift value for USB_EPDISBLD */ -#define _USB_DOEP_INT_EPDISBLD_MASK 0x2UL /* Bit mask for USB_EPDISBLD */ -#define _USB_DOEP_INT_EPDISBLD_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEP_INT */ -#define USB_DOEP_INT_EPDISBLD_DEFAULT (_USB_DOEP_INT_EPDISBLD_DEFAULT << 1) /* Shifted mode DEFAULT for USB_DOEP_INT */ -#define USB_DOEP_INT_AHBERR (0x1UL << 2) /* AHB Error */ -#define _USB_DOEP_INT_AHBERR_SHIFT 2 /* Shift value for USB_AHBERR */ -#define _USB_DOEP_INT_AHBERR_MASK 0x4UL /* Bit mask for USB_AHBERR */ -#define _USB_DOEP_INT_AHBERR_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEP_INT */ -#define USB_DOEP_INT_AHBERR_DEFAULT (_USB_DOEP_INT_AHBERR_DEFAULT << 2) /* Shifted mode DEFAULT for USB_DOEP_INT */ -#define USB_DOEP_INT_SETUP (0x1UL << 3) /* Setup Phase Done */ -#define _USB_DOEP_INT_SETUP_SHIFT 3 /* Shift value for USB_SETUP */ -#define _USB_DOEP_INT_SETUP_MASK 0x8UL /* Bit mask for USB_SETUP */ -#define _USB_DOEP_INT_SETUP_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEP_INT */ -#define USB_DOEP_INT_SETUP_DEFAULT (_USB_DOEP_INT_SETUP_DEFAULT << 3) /* Shifted mode DEFAULT for USB_DOEP_INT */ -#define USB_DOEP_INT_OUTTKNEPDIS (0x1UL << 4) /* OUT Token Received When Endpoint Disabled */ -#define _USB_DOEP_INT_OUTTKNEPDIS_SHIFT 4 /* Shift value for USB_OUTTKNEPDIS */ -#define _USB_DOEP_INT_OUTTKNEPDIS_MASK 0x10UL /* Bit mask for USB_OUTTKNEPDIS */ -#define _USB_DOEP_INT_OUTTKNEPDIS_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEP_INT */ -#define USB_DOEP_INT_OUTTKNEPDIS_DEFAULT (_USB_DOEP_INT_OUTTKNEPDIS_DEFAULT << 4) /* Shifted mode DEFAULT for USB_DOEP_INT */ -#define USB_DOEP_INT_BACK2BACKSETUP (0x1UL << 6) /* Back-to-Back SETUP Packets Received */ -#define _USB_DOEP_INT_BACK2BACKSETUP_SHIFT 6 /* Shift value for USB_BACK2BACKSETUP */ -#define _USB_DOEP_INT_BACK2BACKSETUP_MASK 0x40UL /* Bit mask for USB_BACK2BACKSETUP */ -#define _USB_DOEP_INT_BACK2BACKSETUP_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEP_INT */ -#define USB_DOEP_INT_BACK2BACKSETUP_DEFAULT (_USB_DOEP_INT_BACK2BACKSETUP_DEFAULT << 6) /* Shifted mode DEFAULT for USB_DOEP_INT */ -#define USB_DOEP_INT_PKTDRPSTS (0x1UL << 11) /* Packet Drop Status */ -#define _USB_DOEP_INT_PKTDRPSTS_SHIFT 11 /* Shift value for USB_PKTDRPSTS */ -#define _USB_DOEP_INT_PKTDRPSTS_MASK 0x800UL /* Bit mask for USB_PKTDRPSTS */ -#define _USB_DOEP_INT_PKTDRPSTS_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEP_INT */ -#define USB_DOEP_INT_PKTDRPSTS_DEFAULT (_USB_DOEP_INT_PKTDRPSTS_DEFAULT << 11) /* Shifted mode DEFAULT for USB_DOEP_INT */ -#define USB_DOEP_INT_BBLEERR (0x1UL << 12) /* Babble Error */ -#define _USB_DOEP_INT_BBLEERR_SHIFT 12 /* Shift value for USB_BBLEERR */ -#define _USB_DOEP_INT_BBLEERR_MASK 0x1000UL /* Bit mask for USB_BBLEERR */ -#define _USB_DOEP_INT_BBLEERR_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEP_INT */ -#define USB_DOEP_INT_BBLEERR_DEFAULT (_USB_DOEP_INT_BBLEERR_DEFAULT << 12) /* Shifted mode DEFAULT for USB_DOEP_INT */ -#define USB_DOEP_INT_NAKINTRPT (0x1UL << 13) /* NAK Interrupt */ -#define _USB_DOEP_INT_NAKINTRPT_SHIFT 13 /* Shift value for USB_NAKINTRPT */ -#define _USB_DOEP_INT_NAKINTRPT_MASK 0x2000UL /* Bit mask for USB_NAKINTRPT */ -#define _USB_DOEP_INT_NAKINTRPT_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEP_INT */ -#define USB_DOEP_INT_NAKINTRPT_DEFAULT (_USB_DOEP_INT_NAKINTRPT_DEFAULT << 13) /* Shifted mode DEFAULT for USB_DOEP_INT */ - -/* Bit fields for USB DOEP_TSIZ */ - -#define _USB_DOEP_TSIZ_RESETVALUE 0x00000000UL /* Default value for USB_DOEP_TSIZ */ -#define _USB_DOEP_TSIZ_MASK 0x7FFFFFFFUL /* Mask for USB_DOEP_TSIZ */ - -#define _USB_DOEP_TSIZ_XFERSIZE_SHIFT 0 /* Shift value for USB_XFERSIZE */ -#define _USB_DOEP_TSIZ_XFERSIZE_MASK 0x7FFFFUL /* Bit mask for USB_XFERSIZE */ -#define _USB_DOEP_TSIZ_XFERSIZE_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEP_TSIZ */ -#define USB_DOEP_TSIZ_XFERSIZE_DEFAULT (_USB_DOEP_TSIZ_XFERSIZE_DEFAULT << 0) /* Shifted mode DEFAULT for USB_DOEP_TSIZ */ -#define _USB_DOEP_TSIZ_PKTCNT_SHIFT 19 /* Shift value for USB_PKTCNT */ -#define _USB_DOEP_TSIZ_PKTCNT_MASK 0x1FF80000UL /* Bit mask for USB_PKTCNT */ -#define _USB_DOEP_TSIZ_PKTCNT_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEP_TSIZ */ -#define USB_DOEP_TSIZ_PKTCNT_DEFAULT (_USB_DOEP_TSIZ_PKTCNT_DEFAULT << 19) /* Shifted mode DEFAULT for USB_DOEP_TSIZ */ -#define _USB_DOEP_TSIZ_RXDPIDSUPCNT_SHIFT 29 /* Shift value for USB_RXDPIDSUPCNT */ -#define _USB_DOEP_TSIZ_RXDPIDSUPCNT_MASK 0x60000000UL /* Bit mask for USB_RXDPIDSUPCNT */ -#define _USB_DOEP_TSIZ_RXDPIDSUPCNT_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEP_TSIZ */ -#define _USB_DOEP_TSIZ_RXDPIDSUPCNT_DATA0 0x00000000UL /* Mode DATA0 for USB_DOEP_TSIZ */ -#define _USB_DOEP_TSIZ_RXDPIDSUPCNT_DATA2 0x00000001UL /* Mode DATA2 for USB_DOEP_TSIZ */ -#define _USB_DOEP_TSIZ_RXDPIDSUPCNT_DATA1 0x00000002UL /* Mode DATA1 for USB_DOEP_TSIZ */ -#define _USB_DOEP_TSIZ_RXDPIDSUPCNT_MDATA 0x00000003UL /* Mode MDATA for USB_DOEP_TSIZ */ -#define USB_DOEP_TSIZ_RXDPIDSUPCNT_DEFAULT (_USB_DOEP_TSIZ_RXDPIDSUPCNT_DEFAULT << 29) /* Shifted mode DEFAULT for USB_DOEP_TSIZ */ -#define USB_DOEP_TSIZ_RXDPIDSUPCNT_DATA0 (_USB_DOEP_TSIZ_RXDPIDSUPCNT_DATA0 << 29) /* Shifted mode DATA0 for USB_DOEP_TSIZ */ -#define USB_DOEP_TSIZ_RXDPIDSUPCNT_DATA2 (_USB_DOEP_TSIZ_RXDPIDSUPCNT_DATA2 << 29) /* Shifted mode DATA2 for USB_DOEP_TSIZ */ -#define USB_DOEP_TSIZ_RXDPIDSUPCNT_DATA1 (_USB_DOEP_TSIZ_RXDPIDSUPCNT_DATA1 << 29) /* Shifted mode DATA1 for USB_DOEP_TSIZ */ -#define USB_DOEP_TSIZ_RXDPIDSUPCNT_MDATA (_USB_DOEP_TSIZ_RXDPIDSUPCNT_MDATA << 29) /* Shifted mode MDATA for USB_DOEP_TSIZ */ - -/* Bit fields for USB DOEP_DMAADDR */ - -#define _USB_DOEP_DMAADDR_RESETVALUE 0x00000000UL /* Default value for USB_DOEP_DMAADDR */ -#define _USB_DOEP_DMAADDR_MASK 0xFFFFFFFFUL /* Mask for USB_DOEP_DMAADDR */ - -#define _USB_DOEP_DMAADDR_DMAADDR_SHIFT 0 /* Shift value for USB_DMAADDR */ -#define _USB_DOEP_DMAADDR_DMAADDR_MASK 0xFFFFFFFFUL /* Bit mask for USB_DMAADDR */ -#define _USB_DOEP_DMAADDR_DMAADDR_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEP_DMAADDR */ -#define USB_DOEP_DMAADDR_DMAADDR_DEFAULT (_USB_DOEP_DMAADDR_DMAADDR_DEFAULT << 0) /* Shifted mode DEFAULT for USB_DOEP_DMAADDR */ +/* Bit fields for USB DOEPCTL */ + +#define _USB_DOEPCTL_RESETVALUE 0x00000000UL /* Default value for USB_DOEPCTL */ +#define _USB_DOEPCTL_MASK 0xFC3F87FFUL /* Mask for USB_DOEPCTL */ + +#define _USB_DOEPCTL_MPS_SHIFT 0 /* Shift value for USB_MPS */ +#define _USB_DOEPCTL_MPS_MASK 0x7FFUL /* Bit mask for USB_MPS */ +#define _USB_DOEPCTL_MPS_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEPCTL */ +#define USB_DOEPCTL_MPS_DEFAULT (_USB_DOEPCTL_MPS_DEFAULT << 0) /* Shifted mode DEFAULT for USB_DOEPCTL */ +#define USB_DOEPCTL_USBACTEP (0x1UL << 15) /* USB Active Endpoint */ +#define _USB_DOEPCTL_USBACTEP_SHIFT 15 /* Shift value for USB_USBACTEP */ +#define _USB_DOEPCTL_USBACTEP_MASK 0x8000UL /* Bit mask for USB_USBACTEP */ +#define _USB_DOEPCTL_USBACTEP_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEPCTL */ +#define USB_DOEPCTL_USBACTEP_DEFAULT (_USB_DOEPCTL_USBACTEP_DEFAULT << 15) /* Shifted mode DEFAULT for USB_DOEPCTL */ +#define USB_DOEPCTL_DPIDEOF (0x1UL << 16) /* Endpoint Data PID / Even-odd Frame */ +#define _USB_DOEPCTL_DPIDEOF_SHIFT 16 /* Shift value for USB_DPIDEOF */ +#define _USB_DOEPCTL_DPIDEOF_MASK 0x10000UL /* Bit mask for USB_DPIDEOF */ +#define _USB_DOEPCTL_DPIDEOF_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEPCTL */ +#define _USB_DOEPCTL_DPIDEOF_DATA0EVEN 0x00000000UL /* Mode DATA0EVEN for USB_DOEPCTL */ +#define _USB_DOEPCTL_DPIDEOF_DATA1ODD 0x00000001UL /* Mode DATA1ODD for USB_DOEPCTL */ +#define USB_DOEPCTL_DPIDEOF_DEFAULT (_USB_DOEPCTL_DPIDEOF_DEFAULT << 16) /* Shifted mode DEFAULT for USB_DOEPCTL */ +#define USB_DOEPCTL_DPIDEOF_DATA0EVEN (_USB_DOEPCTL_DPIDEOF_DATA0EVEN << 16) /* Shifted mode DATA0EVEN for USB_DOEPCTL */ +#define USB_DOEPCTL_DPIDEOF_DATA1ODD (_USB_DOEPCTL_DPIDEOF_DATA1ODD << 16) /* Shifted mode DATA1ODD for USB_DOEPCTL */ +#define USB_DOEPCTL_NAKSTS (0x1UL << 17) /* NAK Status */ +#define _USB_DOEPCTL_NAKSTS_SHIFT 17 /* Shift value for USB_NAKSTS */ +#define _USB_DOEPCTL_NAKSTS_MASK 0x20000UL /* Bit mask for USB_NAKSTS */ +#define _USB_DOEPCTL_NAKSTS_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEPCTL */ +#define USB_DOEPCTL_NAKSTS_DEFAULT (_USB_DOEPCTL_NAKSTS_DEFAULT << 17) /* Shifted mode DEFAULT for USB_DOEPCTL */ +#define _USB_DOEPCTL_EPTYPE_SHIFT 18 /* Shift value for USB_EPTYPE */ +#define _USB_DOEPCTL_EPTYPE_MASK 0xC0000UL /* Bit mask for USB_EPTYPE */ +#define _USB_DOEPCTL_EPTYPE_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEPCTL */ +#define _USB_DOEPCTL_EPTYPE_CONTROL 0x00000000UL /* Mode CONTROL for USB_DOEPCTL */ +#define _USB_DOEPCTL_EPTYPE_ISO 0x00000001UL /* Mode ISO for USB_DOEPCTL */ +#define _USB_DOEPCTL_EPTYPE_BULK 0x00000002UL /* Mode BULK for USB_DOEPCTL */ +#define _USB_DOEPCTL_EPTYPE_INT 0x00000003UL /* Mode INT for USB_DOEPCTL */ +#define USB_DOEPCTL_EPTYPE_DEFAULT (_USB_DOEPCTL_EPTYPE_DEFAULT << 18) /* Shifted mode DEFAULT for USB_DOEPCTL */ +#define USB_DOEPCTL_EPTYPE_CONTROL (_USB_DOEPCTL_EPTYPE_CONTROL << 18) /* Shifted mode CONTROL for USB_DOEPCTL */ +#define USB_DOEPCTL_EPTYPE_ISO (_USB_DOEPCTL_EPTYPE_ISO << 18) /* Shifted mode ISO for USB_DOEPCTL */ +#define USB_DOEPCTL_EPTYPE_BULK (_USB_DOEPCTL_EPTYPE_BULK << 18) /* Shifted mode BULK for USB_DOEPCTL */ +#define USB_DOEPCTL_EPTYPE_INT (_USB_DOEPCTL_EPTYPE_INT << 18) /* Shifted mode INT for USB_DOEPCTL */ +#define USB_DOEPCTL_SNP (0x1UL << 20) /* Snoop Mode */ +#define _USB_DOEPCTL_SNP_SHIFT 20 /* Shift value for USB_SNP */ +#define _USB_DOEPCTL_SNP_MASK 0x100000UL /* Bit mask for USB_SNP */ +#define _USB_DOEPCTL_SNP_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEPCTL */ +#define USB_DOEPCTL_SNP_DEFAULT (_USB_DOEPCTL_SNP_DEFAULT << 20) /* Shifted mode DEFAULT for USB_DOEPCTL */ +#define USB_DOEPCTL_STALL (0x1UL << 21) /* STALL Handshake */ +#define _USB_DOEPCTL_STALL_SHIFT 21 /* Shift value for USB_STALL */ +#define _USB_DOEPCTL_STALL_MASK 0x200000UL /* Bit mask for USB_STALL */ +#define _USB_DOEPCTL_STALL_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEPCTL */ +#define USB_DOEPCTL_STALL_DEFAULT (_USB_DOEPCTL_STALL_DEFAULT << 21) /* Shifted mode DEFAULT for USB_DOEPCTL */ +#define USB_DOEPCTL_CNAK (0x1UL << 26) /* Clear NAK */ +#define _USB_DOEPCTL_CNAK_SHIFT 26 /* Shift value for USB_CNAK */ +#define _USB_DOEPCTL_CNAK_MASK 0x4000000UL /* Bit mask for USB_CNAK */ +#define _USB_DOEPCTL_CNAK_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEPCTL */ +#define USB_DOEPCTL_CNAK_DEFAULT (_USB_DOEPCTL_CNAK_DEFAULT << 26) /* Shifted mode DEFAULT for USB_DOEPCTL */ +#define USB_DOEPCTL_SNAK (0x1UL << 27) /* Set NAK */ +#define _USB_DOEPCTL_SNAK_SHIFT 27 /* Shift value for USB_SNAK */ +#define _USB_DOEPCTL_SNAK_MASK 0x8000000UL /* Bit mask for USB_SNAK */ +#define _USB_DOEPCTL_SNAK_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEPCTL */ +#define USB_DOEPCTL_SNAK_DEFAULT (_USB_DOEPCTL_SNAK_DEFAULT << 27) /* Shifted mode DEFAULT for USB_DOEPCTL */ +#define USB_DOEPCTL_SETD0PIDEF (0x1UL << 28) /* Set DATA0 PID / Even Frame */ +#define _USB_DOEPCTL_SETD0PIDEF_SHIFT 28 /* Shift value for USB_SETD0PIDEF */ +#define _USB_DOEPCTL_SETD0PIDEF_MASK 0x10000000UL /* Bit mask for USB_SETD0PIDEF */ +#define _USB_DOEPCTL_SETD0PIDEF_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEPCTL */ +#define USB_DOEPCTL_SETD0PIDEF_DEFAULT (_USB_DOEPCTL_SETD0PIDEF_DEFAULT << 28) /* Shifted mode DEFAULT for USB_DOEPCTL */ +#define USB_DOEPCTL_SETD1PIDOF (0x1UL << 29) /* Set DATA1 PID / Odd Frame */ +#define _USB_DOEPCTL_SETD1PIDOF_SHIFT 29 /* Shift value for USB_SETD1PIDOF */ +#define _USB_DOEPCTL_SETD1PIDOF_MASK 0x20000000UL /* Bit mask for USB_SETD1PIDOF */ +#define _USB_DOEPCTL_SETD1PIDOF_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEPCTL */ +#define USB_DOEPCTL_SETD1PIDOF_DEFAULT (_USB_DOEPCTL_SETD1PIDOF_DEFAULT << 29) /* Shifted mode DEFAULT for USB_DOEPCTL */ +#define USB_DOEPCTL_EPDIS (0x1UL << 30) /* Endpoint Disable */ +#define _USB_DOEPCTL_EPDIS_SHIFT 30 /* Shift value for USB_EPDIS */ +#define _USB_DOEPCTL_EPDIS_MASK 0x40000000UL /* Bit mask for USB_EPDIS */ +#define _USB_DOEPCTL_EPDIS_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEPCTL */ +#define USB_DOEPCTL_EPDIS_DEFAULT (_USB_DOEPCTL_EPDIS_DEFAULT << 30) /* Shifted mode DEFAULT for USB_DOEPCTL */ +#define USB_DOEPCTL_EPENA (0x1UL << 31) /* Endpoint Enable */ +#define _USB_DOEPCTL_EPENA_SHIFT 31 /* Shift value for USB_EPENA */ +#define _USB_DOEPCTL_EPENA_MASK 0x80000000UL /* Bit mask for USB_EPENA */ +#define _USB_DOEPCTL_EPENA_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEPCTL */ +#define USB_DOEPCTL_EPENA_DEFAULT (_USB_DOEPCTL_EPENA_DEFAULT << 31) /* Shifted mode DEFAULT for USB_DOEPCTL */ + +/* Bit fields for USB DOEPINT */ + +#define _USB_DOEPINT_RESETVALUE 0x00000000UL /* Default value for USB_DOEPINT */ +#define _USB_DOEPINT_MASK 0x0000385FUL /* Mask for USB_DOEPINT */ + +#define USB_DOEPINT_XFERCOMPL (0x1UL << 0) /* Transfer Completed Interrupt */ +#define _USB_DOEPINT_XFERCOMPL_SHIFT 0 /* Shift value for USB_XFERCOMPL */ +#define _USB_DOEPINT_XFERCOMPL_MASK 0x1UL /* Bit mask for USB_XFERCOMPL */ +#define _USB_DOEPINT_XFERCOMPL_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEPINT */ +#define USB_DOEPINT_XFERCOMPL_DEFAULT (_USB_DOEPINT_XFERCOMPL_DEFAULT << 0) /* Shifted mode DEFAULT for USB_DOEPINT */ +#define USB_DOEPINT_EPDISBLD (0x1UL << 1) /* Endpoint Disabled Interrupt */ +#define _USB_DOEPINT_EPDISBLD_SHIFT 1 /* Shift value for USB_EPDISBLD */ +#define _USB_DOEPINT_EPDISBLD_MASK 0x2UL /* Bit mask for USB_EPDISBLD */ +#define _USB_DOEPINT_EPDISBLD_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEPINT */ +#define USB_DOEPINT_EPDISBLD_DEFAULT (_USB_DOEPINT_EPDISBLD_DEFAULT << 1) /* Shifted mode DEFAULT for USB_DOEPINT */ +#define USB_DOEPINT_AHBERR (0x1UL << 2) /* AHB Error */ +#define _USB_DOEPINT_AHBERR_SHIFT 2 /* Shift value for USB_AHBERR */ +#define _USB_DOEPINT_AHBERR_MASK 0x4UL /* Bit mask for USB_AHBERR */ +#define _USB_DOEPINT_AHBERR_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEPINT */ +#define USB_DOEPINT_AHBERR_DEFAULT (_USB_DOEPINT_AHBERR_DEFAULT << 2) /* Shifted mode DEFAULT for USB_DOEPINT */ +#define USB_DOEPINT_SETUP (0x1UL << 3) /* Setup Phase Done */ +#define _USB_DOEPINT_SETUP_SHIFT 3 /* Shift value for USB_SETUP */ +#define _USB_DOEPINT_SETUP_MASK 0x8UL /* Bit mask for USB_SETUP */ +#define _USB_DOEPINT_SETUP_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEPINT */ +#define USB_DOEPINT_SETUP_DEFAULT (_USB_DOEPINT_SETUP_DEFAULT << 3) /* Shifted mode DEFAULT for USB_DOEPINT */ +#define USB_DOEPINT_OUTTKNEPDIS (0x1UL << 4) /* OUT Token Received When Endpoint Disabled */ +#define _USB_DOEPINT_OUTTKNEPDIS_SHIFT 4 /* Shift value for USB_OUTTKNEPDIS */ +#define _USB_DOEPINT_OUTTKNEPDIS_MASK 0x10UL /* Bit mask for USB_OUTTKNEPDIS */ +#define _USB_DOEPINT_OUTTKNEPDIS_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEPINT */ +#define USB_DOEPINT_OUTTKNEPDIS_DEFAULT (_USB_DOEPINT_OUTTKNEPDIS_DEFAULT << 4) /* Shifted mode DEFAULT for USB_DOEPINT */ +#define USB_DOEPINT_BACK2BACKSETUP (0x1UL << 6) /* Back-to-Back SETUP Packets Received */ +#define _USB_DOEPINT_BACK2BACKSETUP_SHIFT 6 /* Shift value for USB_BACK2BACKSETUP */ +#define _USB_DOEPINT_BACK2BACKSETUP_MASK 0x40UL /* Bit mask for USB_BACK2BACKSETUP */ +#define _USB_DOEPINT_BACK2BACKSETUP_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEPINT */ +#define USB_DOEPINT_BACK2BACKSETUP_DEFAULT (_USB_DOEPINT_BACK2BACKSETUP_DEFAULT << 6) /* Shifted mode DEFAULT for USB_DOEPINT */ +#define USB_DOEPINT_PKTDRPSTS (0x1UL << 11) /* Packet Drop Status */ +#define _USB_DOEPINT_PKTDRPSTS_SHIFT 11 /* Shift value for USB_PKTDRPSTS */ +#define _USB_DOEPINT_PKTDRPSTS_MASK 0x800UL /* Bit mask for USB_PKTDRPSTS */ +#define _USB_DOEPINT_PKTDRPSTS_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEPINT */ +#define USB_DOEPINT_PKTDRPSTS_DEFAULT (_USB_DOEPINT_PKTDRPSTS_DEFAULT << 11) /* Shifted mode DEFAULT for USB_DOEPINT */ +#define USB_DOEPINT_BBLEERR (0x1UL << 12) /* Babble Error */ +#define _USB_DOEPINT_BBLEERR_SHIFT 12 /* Shift value for USB_BBLEERR */ +#define _USB_DOEPINT_BBLEERR_MASK 0x1000UL /* Bit mask for USB_BBLEERR */ +#define _USB_DOEPINT_BBLEERR_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEPINT */ +#define USB_DOEPINT_BBLEERR_DEFAULT (_USB_DOEPINT_BBLEERR_DEFAULT << 12) /* Shifted mode DEFAULT for USB_DOEPINT */ +#define USB_DOEPINT_NAKINTRPT (0x1UL << 13) /* NAK Interrupt */ +#define _USB_DOEPINT_NAKINTRPT_SHIFT 13 /* Shift value for USB_NAKINTRPT */ +#define _USB_DOEPINT_NAKINTRPT_MASK 0x2000UL /* Bit mask for USB_NAKINTRPT */ +#define _USB_DOEPINT_NAKINTRPT_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEPINT */ +#define USB_DOEPINT_NAKINTRPT_DEFAULT (_USB_DOEPINT_NAKINTRPT_DEFAULT << 13) /* Shifted mode DEFAULT for USB_DOEPINT */ + +/* Bit fields for USB DOEPTSIZ */ + +#define _USB_DOEPTSIZ_RESETVALUE 0x00000000UL /* Default value for USB_DOEPTSIZ */ +#define _USB_DOEPTSIZ_MASK 0x7FFFFFFFUL /* Mask for USB_DOEPTSIZ */ + +#define _USB_DOEPTSIZ_XFERSIZE_SHIFT 0 /* Shift value for USB_XFERSIZE */ +#define _USB_DOEPTSIZ_XFERSIZE_MASK 0x7FFFFUL /* Bit mask for USB_XFERSIZE */ +#define _USB_DOEPTSIZ_XFERSIZE_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEPTSIZ */ +#define USB_DOEPTSIZ_XFERSIZE_DEFAULT (_USB_DOEPTSIZ_XFERSIZE_DEFAULT << 0) /* Shifted mode DEFAULT for USB_DOEPTSIZ */ +#define _USB_DOEPTSIZ_PKTCNT_SHIFT 19 /* Shift value for USB_PKTCNT */ +#define _USB_DOEPTSIZ_PKTCNT_MASK 0x1FF80000UL /* Bit mask for USB_PKTCNT */ +#define _USB_DOEPTSIZ_PKTCNT_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEPTSIZ */ +#define USB_DOEPTSIZ_PKTCNT_DEFAULT (_USB_DOEPTSIZ_PKTCNT_DEFAULT << 19) /* Shifted mode DEFAULT for USB_DOEPTSIZ */ +#define _USB_DOEPTSIZ_RXDPIDSUPCNT_SHIFT 29 /* Shift value for USB_RXDPIDSUPCNT */ +#define _USB_DOEPTSIZ_RXDPIDSUPCNT_MASK 0x60000000UL /* Bit mask for USB_RXDPIDSUPCNT */ +#define _USB_DOEPTSIZ_RXDPIDSUPCNT_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEPTSIZ */ +#define _USB_DOEPTSIZ_RXDPIDSUPCNT_DATA0 0x00000000UL /* Mode DATA0 for USB_DOEPTSIZ */ +#define _USB_DOEPTSIZ_RXDPIDSUPCNT_DATA2 0x00000001UL /* Mode DATA2 for USB_DOEPTSIZ */ +#define _USB_DOEPTSIZ_RXDPIDSUPCNT_DATA1 0x00000002UL /* Mode DATA1 for USB_DOEPTSIZ */ +#define _USB_DOEPTSIZ_RXDPIDSUPCNT_MDATA 0x00000003UL /* Mode MDATA for USB_DOEPTSIZ */ +#define USB_DOEPTSIZ_RXDPIDSUPCNT_DEFAULT (_USB_DOEPTSIZ_RXDPIDSUPCNT_DEFAULT << 29) /* Shifted mode DEFAULT for USB_DOEPTSIZ */ +#define USB_DOEPTSIZ_RXDPIDSUPCNT_DATA0 (_USB_DOEPTSIZ_RXDPIDSUPCNT_DATA0 << 29) /* Shifted mode DATA0 for USB_DOEPTSIZ */ +#define USB_DOEPTSIZ_RXDPIDSUPCNT_DATA2 (_USB_DOEPTSIZ_RXDPIDSUPCNT_DATA2 << 29) /* Shifted mode DATA2 for USB_DOEPTSIZ */ +#define USB_DOEPTSIZ_RXDPIDSUPCNT_DATA1 (_USB_DOEPTSIZ_RXDPIDSUPCNT_DATA1 << 29) /* Shifted mode DATA1 for USB_DOEPTSIZ */ +#define USB_DOEPTSIZ_RXDPIDSUPCNT_MDATA (_USB_DOEPTSIZ_RXDPIDSUPCNT_MDATA << 29) /* Shifted mode MDATA for USB_DOEPTSIZ */ + +/* Bit fields for USB DOEPDMAADDR */ + +#define _USB_DOEPDMAADDR_RESETVALUE 0x00000000UL /* Default value for USB_DOEPDMAADDR */ +#define _USB_DOEPDMAADDR_MASK 0xFFFFFFFFUL /* Mask for USB_DOEPDMAADDR */ + +#define _USB_DOEPDMAADDR_DMAADDR_SHIFT 0 /* Shift value for USB_DMAADDR */ +#define _USB_DOEPDMAADDR_DMAADDR_MASK 0xFFFFFFFFUL /* Bit mask for USB_DMAADDR */ +#define _USB_DOEPDMAADDR_DMAADDR_DEFAULT 0x00000000UL /* Mode DEFAULT for USB_DOEPDMAADDR */ +#define USB_DOEPDMAADDR_DMAADDR_DEFAULT (_USB_DOEPDMAADDR_DMAADDR_DEFAULT << 0) /* Shifted mode DEFAULT for USB_DOEPDMAADDR */ /* Bit fields for USB PCGCCTL */ diff --git a/nuttx/arch/arm/src/efm32/efm32_usb.h b/nuttx/arch/arm/src/efm32/efm32_usb.h new file mode 100644 index 000000000..f08fc0ecb --- /dev/null +++ b/nuttx/arch/arm/src/efm32/efm32_usb.h @@ -0,0 +1,126 @@ +/************************************************************************************ + * arch/arm/src/efm32/efm32_usb.h + * + * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <gnutt@nuttx.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_EFM32_EFM32_OTGFS_H +#define __ARCH_ARM_SRC_EFM32_EFM32_OTGFS_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include <nuttx/config.h> + +#include <stdint.h> + +#include "chip/efm32_usb.h" + +#if defined(CONFIG_EFM32_OTGFS) + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ +/* Configuration ********************************************************************/ + +#ifndef CONFIG_OTGFS_PRI +# define CONFIG_OTGFS_PRI NVIC_SYSH_PRIORITY_DEFAULT +#endif + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/******************************************************************************* + * Name: efm32_otgfshost_initialize + * + * Description: + * Initialize USB host device controller hardware. + * + * Input Parameters: + * controller -- If the device supports more than USB host controller, then + * this identifies which controller is being initializeed. Normally, this + * is just zero. + * + * Returned Value: + * And instance of the USB host interface. The controlling task should + * use this interface to (1) call the wait() method to wait for a device + * to be connected, and (2) call the enumerate() method to bind the device + * to a class driver. + * + * Assumptions: + * - This function should called in the initialization sequence in order + * to initialize the USB device functionality. + * - Class drivers should be initialized prior to calling this function. + * Otherwise, there is a race condition if the device is already connected. + * + *******************************************************************************/ + +#ifdef CONFIG_USBHOST +struct usbhost_connection_s; +FAR struct usbhost_connection_s *efm32_otgfshost_initialize(int controller); +#endif + +/************************************************************************************ + * Name: efm32_usbsuspend + * + * Description: + * Board logic must provide the efm32_usbsuspend logic if the OTG FS device driver + * is used. This function is called whenever the USB enters or leaves suspend + * mode. This is an opportunity for the board logic to shutdown clocks, power, + * etc. while the USB is suspended. + * + ************************************************************************************/ + +void efm32_usbsuspend(FAR struct usbdev_s *dev, bool resume); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_EFM32_OTGFS */ +#endif /* __ARCH_ARM_SRC_EFM32_EFM32_OTGFS_H */ + diff --git a/nuttx/arch/arm/src/efm32/efm32_usbdev.c b/nuttx/arch/arm/src/efm32/efm32_usbdev.c new file mode 100644 index 000000000..c1af01ad2 --- /dev/null +++ b/nuttx/arch/arm/src/efm32/efm32_usbdev.c @@ -0,0 +1,5701 @@ +/******************************************************************************* + * arch/arm/src/efm32/efm32_otgfsdev.c + * + * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <gnutt@nuttx.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + *******************************************************************************/ + +/******************************************************************************* + * Included Files + *******************************************************************************/ + +#include <nuttx/config.h> + +#include <sys/types.h> +#include <stdint.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <debug.h> + +#include <nuttx/arch.h> +#include <nuttx/kmalloc.h> +#include <nuttx/usb/usb.h> +#include <nuttx/usb/usbdev.h> +#include <nuttx/usb/usbdev_trace.h> + +#include <arch/irq.h> +#include <arch/board/board.h> + +#include "chip.h" +#include "up_arch.h" +#include "up_internal.h" + +#include "efm32_usb.h" + +#if defined(CONFIG_USBDEV) && (defined(CONFIG_EFM32_OTGFS)) + +/******************************************************************************* + * Preprocessor Definitions + *******************************************************************************/ +/* Configuration ***************************************************************/ + +#ifndef CONFIG_USBDEV_EP0_MAXSIZE +# define CONFIG_USBDEV_EP0_MAXSIZE 64 +#endif + +#ifndef CONFIG_USBDEV_SETUP_MAXDATASIZE +# define CONFIG_USBDEV_SETUP_MAXDATASIZE CONFIG_USBDEV_EP0_MAXSIZE +#endif + +#ifndef CONFIG_USBDEV_MAXPOWER +# define CONFIG_USBDEV_MAXPOWER 100 /* mA */ +#endif + +/* There is 1.25Kb of FIFO memory. The default partitions this memory + * so that there is a TxFIFO allocated for each endpoint and with more + * memory provided for the common RxFIFO. A more knowledge-able + * configuration would not allocate any TxFIFO space to OUT endpoints. + */ + +#ifndef CONFIG_USBDEV_RXFIFO_SIZE +# define CONFIG_USBDEV_RXFIFO_SIZE 512 +#endif + +#ifndef CONFIG_USBDEV_EP0_TXFIFO_SIZE +# define CONFIG_USBDEV_EP0_TXFIFO_SIZE 192 +#endif + +#ifndef CONFIG_USBDEV_EP1_TXFIFO_SIZE +# define CONFIG_USBDEV_EP1_TXFIFO_SIZE 192 +#endif + +#ifndef CONFIG_USBDEV_EP2_TXFIFO_SIZE +# define CONFIG_USBDEV_EP2_TXFIFO_SIZE 192 +#endif + +#ifndef CONFIG_USBDEV_EP3_TXFIFO_SIZE +# define CONFIG_USBDEV_EP3_TXFIFO_SIZE 192 +#endif + +#if (CONFIG_USBDEV_RXFIFO_SIZE + CONFIG_USBDEV_EP0_TXFIFO_SIZE + \ + CONFIG_USBDEV_EP2_TXFIFO_SIZE + CONFIG_USBDEV_EP3_TXFIFO_SIZE) > 1280 +# error "FIFO allocations exceed FIFO memory size" +#endif + +/* The actual FIFO addresses that we use must be aligned to 4-byte boundaries; + * FIFO sizes must be provided in units of 32-bit words. + */ + +#define EFM32_RXFIFO_BYTES ((CONFIG_USBDEV_RXFIFO_SIZE + 3) & ~3) +#define EFM32_RXFIFO_WORDS ((CONFIG_USBDEV_RXFIFO_SIZE + 3) >> 2) + +#define EFM32_EP0_TXFIFO_BYTES ((CONFIG_USBDEV_EP0_TXFIFO_SIZE + 3) & ~3) +#define EFM32_EP0_TXFIFO_WORDS ((CONFIG_USBDEV_EP0_TXFIFO_SIZE + 3) >> 2) + +#if EFM32_EP0_TXFIFO_WORDS < 16 || EFM32_EP0_TXFIFO_WORDS > 256 +# error "CONFIG_USBDEV_EP0_TXFIFO_SIZE is out of range" +#endif + +#define EFM32_EP1_TXFIFO_BYTES ((CONFIG_USBDEV_EP1_TXFIFO_SIZE + 3) & ~3) +#define EFM32_EP1_TXFIFO_WORDS ((CONFIG_USBDEV_EP1_TXFIFO_SIZE + 3) >> 2) + +#if EFM32_EP1_TXFIFO_WORDS < 16 +# error "CONFIG_USBDEV_EP1_TXFIFO_SIZE is out of range" +#endif + +#define EFM32_EP2_TXFIFO_BYTES ((CONFIG_USBDEV_EP2_TXFIFO_SIZE + 3) & ~3) +#define EFM32_EP2_TXFIFO_WORDS ((CONFIG_USBDEV_EP2_TXFIFO_SIZE + 3) >> 2) + +#if EFM32_EP2_TXFIFO_WORDS < 16 +# error "CONFIG_USBDEV_EP2_TXFIFO_SIZE is out of range" +#endif + +#define EFM32_EP3_TXFIFO_BYTES ((CONFIG_USBDEV_EP3_TXFIFO_SIZE + 3) & ~3) +#define EFM32_EP3_TXFIFO_WORDS ((CONFIG_USBDEV_EP3_TXFIFO_SIZE + 3) >> 2) + +#if EFM32_EP3_TXFIFO_WORDS < 16 +# error "CONFIG_USBDEV_EP3_TXFIFO_SIZE is out of range" +#endif + +/* Debug ***********************************************************************/ +/* Trace error codes */ + +#define EFM32_TRACEERR_ALLOCFAIL 0x01 +#define EFM32_TRACEERR_BADCLEARFEATURE 0x02 +#define EFM32_TRACEERR_BADDEVGETSTATUS 0x03 +#define EFM32_TRACEERR_BADEPNO 0x04 +#define EFM32_TRACEERR_BADEPGETSTATUS 0x05 +#define EFM32_TRACEERR_BADGETCONFIG 0x06 +#define EFM32_TRACEERR_BADGETSETDESC 0x07 +#define EFM32_TRACEERR_BADGETSTATUS 0x08 +#define EFM32_TRACEERR_BADSETADDRESS 0x09 +#define EFM32_TRACEERR_BADSETCONFIG 0x0a +#define EFM32_TRACEERR_BADSETFEATURE 0x0b +#define EFM32_TRACEERR_BADTESTMODE 0x0c +#define EFM32_TRACEERR_BINDFAILED 0x0d +#define EFM32_TRACEERR_DISPATCHSTALL 0x0e +#define EFM32_TRACEERR_DRIVER 0x0f +#define EFM32_TRACEERR_DRIVERREGISTERED 0x10 +#define EFM32_TRACEERR_EP0NOSETUP 0x11 +#define EFM32_TRACEERR_EP0SETUPSTALLED 0x12 +#define EFM32_TRACEERR_EPINNULLPACKET 0x13 +#define EFM32_TRACEERR_EPINUNEXPECTED 0x14 +#define EFM32_TRACEERR_EPOUTNULLPACKET 0x15 +#define EFM32_TRACEERR_EPOUTUNEXPECTED 0x16 +#define EFM32_TRACEERR_INVALIDCTRLREQ 0x17 +#define EFM32_TRACEERR_INVALIDPARMS 0x18 +#define EFM32_TRACEERR_IRQREGISTRATION 0x19 +#define EFM32_TRACEERR_NOEP 0x1a +#define EFM32_TRACEERR_NOTCONFIGURED 0x1b +#define EFM32_TRACEERR_EPOUTQEMPTY 0x1c +#define EFM32_TRACEERR_EPINREQEMPTY 0x1d +#define EFM32_TRACEERR_NOOUTSETUP 0x1e +#define EFM32_TRACEERR_POLLTIMEOUT 0x1f + +/* Trace interrupt codes */ + +#define EFM32_TRACEINTID_USB 1 /* USB Interrupt entry/exit */ +#define EFM32_TRACEINTID_INTPENDING 2 /* On each pass through the loop */ + +#define EFM32_TRACEINTID_EPOUT (10 + 0) /* First level interrupt decode */ +#define EFM32_TRACEINTID_EPIN (10 + 1) +#define EFM32_TRACEINTID_MISMATCH (10 + 2) +#define EFM32_TRACEINTID_WAKEUP (10 + 3) +#define EFM32_TRACEINTID_SUSPEND (10 + 4) +#define EFM32_TRACEINTID_SOF (10 + 5) +#define EFM32_TRACEINTID_RXFIFO (10 + 6) +#define EFM32_TRACEINTID_DEVRESET (10 + 7) +#define EFM32_TRACEINTID_ENUMDNE (10 + 8) +#define EFM32_TRACEINTID_IISOIXFR (10 + 9) +#define EFM32_TRACEINTID_IISOOXFR (10 + 10) +#define EFM32_TRACEINTID_SRQ (10 + 11) +#define EFM32_TRACEINTID_OTG (10 + 12) + +#define EFM32_TRACEINTID_EPOUT_XFRC (40 + 0) /* EPOUT second level decode */ +#define EFM32_TRACEINTID_EPOUT_EPDISD (40 + 1) +#define EFM32_TRACEINTID_EPOUT_SETUP (40 + 2) +#define EFM32_TRACEINTID_DISPATCH (40 + 3) + +#define EFM32_TRACEINTID_GETSTATUS (50 + 0) /* EPOUT third level decode */ +#define EFM32_TRACEINTID_EPGETSTATUS (50 + 1) +#define EFM32_TRACEINTID_DEVGETSTATUS (50 + 2) +#define EFM32_TRACEINTID_IFGETSTATUS (50 + 3) +#define EFM32_TRACEINTID_CLEARFEATURE (50 + 4) +#define EFM32_TRACEINTID_SETFEATURE (50 + 5) +#define EFM32_TRACEINTID_SETADDRESS (50 + 6) +#define EFM32_TRACEINTID_GETSETDESC (50 + 7) +#define EFM32_TRACEINTID_GETCONFIG (50 + 8) +#define EFM32_TRACEINTID_SETCONFIG (50 + 9) +#define EFM32_TRACEINTID_GETSETIF (50 + 10) +#define EFM32_TRACEINTID_SYNCHFRAME (50 + 11) + +#define EFM32_TRACEINTID_EPIN_XFRC (70 + 0) /* EPIN second level decode */ +#define EFM32_TRACEINTID_EPIN_TOC (70 + 1) +#define EFM32_TRACEINTID_EPIN_ITTXFE (70 + 2) +#define EFM32_TRACEINTID_EPIN_EPDISD (70 + 3) +#define EFM32_TRACEINTID_EPIN_TXFE (70 + 4) + +#define EFM32_TRACEINTID_EPIN_EMPWAIT (80 + 0) /* EPIN second level decode */ + +#define EFM32_TRACEINTID_OUTNAK (90 + 0) /* RXFLVL second level decode */ +#define EFM32_TRACEINTID_OUTRECVD (90 + 1) +#define EFM32_TRACEINTID_OUTDONE (90 + 2) +#define EFM32_TRACEINTID_SETUPDONE (90 + 3) +#define EFM32_TRACEINTID_SETUPRECVD (90 + 4) + +/* Endpoints ******************************************************************/ + +/* Number of endpoints */ + +#define EFM32_NENDPOINTS (4) /* ep0-3 x 2 for IN and OUT */ + +/* Odd physical endpoint numbers are IN; even are OUT */ + +#define EFM32_EPPHYIN2LOG(epphy) ((uint8_t)(epphy)|USB_DIR_IN) +#define EFM32_EPPHYOUT2LOG(epphy) ((uint8_t)(epphy)|USB_DIR_OUT) + +/* Endpoint 0 */ + +#define EP0 (0) + +/* The set of all enpoints available to the class implementation (1-3) */ + +#define EFM32_EP_AVAILABLE (0x0e) /* All available endpoints */ + +/* Maximum packet sizes for full speed endpoints */ + +#define EFM32_MAXPACKET (64) /* Max packet size (1-64) */ + +/* Delays **********************************************************************/ + +#define EFM32_READY_DELAY 200000 +#define EFM32_FLUSH_DELAY 200000 + +/* Request queue operations ****************************************************/ + +#define efm32_rqempty(ep) ((ep)->head == NULL) +#define efm32_rqpeek(ep) ((ep)->head) + +/* Standard stuff **************************************************************/ + +#ifndef MIN +# define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef MAX +# define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +/******************************************************************************* + * Private Types + *******************************************************************************/ + +/* Overall device state */ + +enum efm32_devstate_e +{ + DEVSTATE_DEFAULT = 0, /* Power-up, unconfigured state. This state simply + * means that the device is not yet been given an + * address. + * SET: At initialization, uninitialization, + * reset, and whenever the device address + * is set to zero + * TESTED: Never + */ + DEVSTATE_ADDRESSED, /* Device address has been assigned, not no + * configuration has yet been selected. + * SET: When either a non-zero device address + * is first assigned or when the device + * is unconfigured (with configuration == 0) + * TESTED: never + */ + DEVSTATE_CONFIGURED, /* Address assigned and configured: + * SET: When the device has been addressed and + * an non-zero configuration has been selected. + * TESTED: In many places to assure that the USB device + * has been properly configured by the host. + */ +}; + +/* Endpoint 0 states */ + +enum efm32_ep0state_e +{ + EP0STATE_IDLE = 0, /* Idle State, leave on receiving a SETUP packet or + * epsubmit: + * SET: In efm32_epin() and efm32_epout() when + * we revert from request processing to + * SETUP processing. + * TESTED: Never + */ + EP0STATE_SETUP_OUT, /* OUT SETUP packet received. Waiting for the DATA + * OUT phase of SETUP Packet to complete before + * processing a SETUP command (without a USB request): + * SET: Set in efm32_rxinterrupt() when SETUP OUT + * packet is received. + * TESTED: In efm32_ep0out_receive() + */ + EP0STATE_SETUP_READY, /* IN SETUP packet received -OR- OUT SETUP packet and + * accompanying data have been received. Processing + * of SETUP command will happen soon. + * SET: (1) efm32_ep0out_receive() when the OUT + * SETUP data phase completes, or (2) + * efm32_rxinterrupt() when an IN SETUP is + * packet received. + * TESTED: Tested in efm32_epout_interrupt() when + * SETUP phase is done to see if the SETUP + * command is ready to be processed. Also + * tested in efm32_ep0out_setup() just to + * double-check that we have a SETUP request + * and any accompanying data. + */ + EP0STATE_SETUP_PROCESS, /* SETUP Packet is being processed by efm32_ep0out_setup(): + * SET: When SETUP packet received in EP0 OUT + * TESTED: Never + */ + EP0STATE_SETUPRESPONSE, /* Short SETUP response write (without a USB request): + * SET: When SETUP response is sent by + * efm32_ep0in_setupresponse() + * TESTED: Never + */ + EP0STATE_DATA_IN, /* Waiting for data out stage (with a USB request): + * SET: In efm32_epin_request() when a write + * request is processed on EP0. + * TESTED: In efm32_epin() to see if we should + * revert to SETUP processing. + */ + EP0STATE_DATA_OUT /* Waiting for data in phase to complete ( with a + * USB request) + * SET: In efm32_epout_request() when a read + * request is processed on EP0. + * TESTED: In efm32_epout() to see if we should + * revert to SETUP processing + */ +}; + +/* Parsed control request */ + +struct efm32_ctrlreq_s +{ + uint8_t type; + uint8_t req; + uint16_t value; + uint16_t index; + uint16_t len; +}; + +/* A container for a request so that the request may be retained in a list */ + +struct efm32_req_s +{ + struct usbdev_req_s req; /* Standard USB request */ + struct efm32_req_s *flink; /* Supports a singly linked list */ +}; + +/* This is the internal representation of an endpoint */ + +struct efm32_ep_s +{ + /* Common endpoint fields. This must be the first thing defined in the + * structure so that it is possible to simply cast from struct usbdev_ep_s + * to struct efm32_ep_s. + */ + + struct usbdev_ep_s ep; /* Standard endpoint structure */ + + /* EFM32-specific fields */ + + struct efm32_usbdev_s *dev; /* Reference to private driver data */ + struct efm32_req_s *head; /* Request list for this endpoint */ + struct efm32_req_s *tail; + uint8_t epphy; /* Physical EP address */ + uint8_t eptype:2; /* Endpoint type */ + uint8_t active:1; /* 1: A request is being processed */ + uint8_t stalled:1; /* 1: Endpoint is stalled */ + uint8_t isin:1; /* 1: IN Endpoint */ + uint8_t odd:1; /* 1: Odd frame */ + uint8_t zlp:1; /* 1: Transmit a zero-length-packet (IN EPs only) */ +}; + +/* This structure retains the state of the USB device controller */ + +struct efm32_usbdev_s +{ + /* Common device fields. This must be the first thing defined in the + * structure so that it is possible to simply cast from struct usbdev_s + * to struct efm32_usbdev_s. + */ + + struct usbdev_s usbdev; + + /* The bound device class driver */ + + struct usbdevclass_driver_s *driver; + + /* EFM32-specific fields */ + + uint8_t stalled:1; /* 1: Protocol stalled */ + uint8_t selfpowered:1; /* 1: Device is self powered */ + uint8_t addressed:1; /* 1: Peripheral address has been set */ + uint8_t configured:1; /* 1: Class driver has been configured */ + uint8_t wakeup:1; /* 1: Device remote wake-up */ + uint8_t dotest:1; /* 1: Test mode selected */ + + uint8_t devstate:4; /* See enum efm32_devstate_e */ + uint8_t ep0state:4; /* See enum efm32_ep0state_e */ + uint8_t testmode:4; /* Selected test mode */ + uint8_t epavail[2]; /* Bitset of available OUT/IN endpoints */ + + /* E0 SETUP data buffering. + * + * ctrlreq: + * The 8-byte SETUP request is received on the EP0 OUT endpoint and is + * saved. + * + * ep0data + * For OUT SETUP requests, the SETUP data phase must also complete before + * the SETUP command can be processed. The pack receipt logic will save + * the accompanying EP0 IN data in ep0data[] before the SETUP command is + * processed. + * + * For IN SETUP requests, the DATA phase will occurr AFTER the SETUP + * control request is processed. In that case, ep0data[] may be used as + * the response buffer. + * + * ep0datlen + * Lenght of OUT DATA received in ep0data[] (Not used with OUT data) + */ + + struct usb_ctrlreq_s ctrlreq; + uint8_t ep0data[CONFIG_USBDEV_SETUP_MAXDATASIZE]; + uint16_t ep0datlen; + + /* The endpoint lists */ + + struct efm32_ep_s epin[EFM32_NENDPOINTS]; + struct efm32_ep_s epout[EFM32_NENDPOINTS]; +}; + +/******************************************************************************* + * Private Function Prototypes + *******************************************************************************/ + +/* Register operations ********************************************************/ + +#if defined(CONFIG_EFM32_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG) +static uint32_t efm32_getreg(uint32_t addr); +static void efm32_putreg(uint32_t val, uint32_t addr); +#else +# define efm32_getreg(addr) getreg32(addr) +# define efm32_putreg(val,addr) putreg32(val,addr) +#endif + +/* Request queue operations ****************************************************/ + +static FAR struct efm32_req_s *efm32_req_remfirst(FAR struct efm32_ep_s *privep); +static bool efm32_req_addlast(FAR struct efm32_ep_s *privep, + FAR struct efm32_req_s *req); + +/* Low level data transfers and request operations *****************************/ +/* Special endpoint 0 data transfer logic */ + +static void efm32_ep0in_setupresponse(FAR struct efm32_usbdev_s *priv, + FAR uint8_t *data, uint32_t nbytes); +static inline void efm32_ep0in_transmitzlp(FAR struct efm32_usbdev_s *priv); +static void efm32_ep0in_activate(void); + +static void efm32_ep0out_ctrlsetup(FAR struct efm32_usbdev_s *priv); + +/* IN request and TxFIFO handling */ + +static void efm32_txfifo_write(FAR struct efm32_ep_s *privep, + FAR uint8_t *buf, int nbytes); +static void efm32_epin_transfer(FAR struct efm32_ep_s *privep, + FAR uint8_t *buf, int nbytes); +static void efm32_epin_request(FAR struct efm32_usbdev_s *priv, + FAR struct efm32_ep_s *privep); + +/* OUT request and RxFIFO handling */ + +static void efm32_rxfifo_read(FAR struct efm32_ep_s *privep, + FAR uint8_t *dest, uint16_t len); +static void efm32_rxfifo_discard(FAR struct efm32_ep_s *privep, int len); +static void efm32_epout_complete(FAR struct efm32_usbdev_s *priv, + FAR struct efm32_ep_s *privep); +static inline void efm32_ep0out_receive(FAR struct efm32_ep_s *privep, int bcnt); +static inline void efm32_epout_receive(FAR struct efm32_ep_s *privep, int bcnt); +static void efm32_epout_request(FAR struct efm32_usbdev_s *priv, + FAR struct efm32_ep_s *privep); + +/* General request handling */ + +static void efm32_ep_flush(FAR struct efm32_ep_s *privep); +static void efm32_req_complete(FAR struct efm32_ep_s *privep, + int16_t result); +static void efm32_req_cancel(FAR struct efm32_ep_s *privep, + int16_t status); + +/* Interrupt handling **********************************************************/ + +static struct efm32_ep_s *efm32_ep_findbyaddr(struct efm32_usbdev_s *priv, + uint16_t eplog); +static int efm32_req_dispatch(FAR struct efm32_usbdev_s *priv, + FAR const struct usb_ctrlreq_s *ctrl); +static void efm32_usbreset(FAR struct efm32_usbdev_s *priv); + +/* Second level OUT endpoint interrupt processing */ + +static inline void efm32_ep0out_testmode(FAR struct efm32_usbdev_s *priv, + uint16_t index); +static inline void efm32_ep0out_stdrequest(struct efm32_usbdev_s *priv, + FAR struct efm32_ctrlreq_s *ctrlreq); +static inline void efm32_ep0out_setup(struct efm32_usbdev_s *priv); +static inline void efm32_epout(FAR struct efm32_usbdev_s *priv, + uint8_t epno); +static inline void efm32_epout_interrupt(FAR struct efm32_usbdev_s *priv); + +/* Second level IN endpoint interrupt processing */ + +static inline void efm32_epin_runtestmode(FAR struct efm32_usbdev_s *priv); +static inline void efm32_epin(FAR struct efm32_usbdev_s *priv, uint8_t epno); +static inline void efm32_epin_txfifoempty(FAR struct efm32_usbdev_s *priv, int epno); +static inline void efm32_epin_interrupt(FAR struct efm32_usbdev_s *priv); + +/* Other second level interrupt processing */ + +static inline void efm32_resumeinterrupt(FAR struct efm32_usbdev_s *priv); +static inline void efm32_suspendinterrupt(FAR struct efm32_usbdev_s *priv); +static inline void efm32_rxinterrupt(FAR struct efm32_usbdev_s *priv); +static inline void efm32_enuminterrupt(FAR struct efm32_usbdev_s *priv); +#ifdef CONFIG_USBDEV_ISOCHRONOUS +static inline void efm32_isocininterrupt(FAR struct efm32_usbdev_s *priv); +static inline void efm32_isocoutinterrupt(FAR struct efm32_usbdev_s *priv); +#endif +#ifdef CONFIG_USBDEV_VBUSSENSING +static inline void efm32_sessioninterrupt(FAR struct efm32_usbdev_s *priv); +static inline void efm32_otginterrupt(FAR struct efm32_usbdev_s *priv); +#endif + +/* First level interrupt processing */ + +static int efm32_usbinterrupt(int irq, FAR void *context); + +/* Endpoint operations *********************************************************/ +/* Global OUT NAK controls */ + +static void efm32_enablegonak(FAR struct efm32_ep_s *privep); +static void efm32_disablegonak(FAR struct efm32_ep_s *privep); + +/* Endpoint configuration */ + +static int efm32_epout_configure(FAR struct efm32_ep_s *privep, + uint8_t eptype, uint16_t maxpacket); +static int efm32_epin_configure(FAR struct efm32_ep_s *privep, + uint8_t eptype, uint16_t maxpacket); +static int efm32_ep_configure(FAR struct usbdev_ep_s *ep, + FAR const struct usb_epdesc_s *desc, bool last); +static void efm32_ep0_configure(FAR struct efm32_usbdev_s *priv); + +/* Endpoint disable */ + +static void efm32_epout_disable(FAR struct efm32_ep_s *privep); +static void efm32_epin_disable(FAR struct efm32_ep_s *privep); +static int efm32_ep_disable(FAR struct usbdev_ep_s *ep); + +/* Endpoint request management */ + +static FAR struct usbdev_req_s *efm32_ep_allocreq(FAR struct usbdev_ep_s *ep); +static void efm32_ep_freereq(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *); + +/* Endpoint buffer management */ + +#ifdef CONFIG_USBDEV_DMA +static void *efm32_ep_allocbuffer(FAR struct usbdev_ep_s *ep, unsigned bytes); +static void efm32_ep_freebuffer(FAR struct usbdev_ep_s *ep, FAR void *buf); +#endif + +/* Endpoint request submission */ + +static int efm32_ep_submit(FAR struct usbdev_ep_s *ep, + struct usbdev_req_s *req); + +/* Endpoint request cancellation */ + +static int efm32_ep_cancel(FAR struct usbdev_ep_s *ep, + struct usbdev_req_s *req); + +/* Stall handling */ + +static int efm32_epout_setstall(FAR struct efm32_ep_s *privep); +static int efm32_epin_setstall(FAR struct efm32_ep_s *privep); +static int efm32_ep_setstall(FAR struct efm32_ep_s *privep); +static int efm32_ep_clrstall(FAR struct efm32_ep_s *privep); +static int efm32_ep_stall(FAR struct usbdev_ep_s *ep, bool resume); +static void efm32_ep0_stall(FAR struct efm32_usbdev_s *priv); + +/* Endpoint allocation */ + +static FAR struct usbdev_ep_s *efm32_ep_alloc(FAR struct usbdev_s *dev, + uint8_t epno, bool in, uint8_t eptype); +static void efm32_ep_free(FAR struct usbdev_s *dev, + FAR struct usbdev_ep_s *ep); + +/* USB device controller operations ********************************************/ + +static int efm32_getframe(struct usbdev_s *dev); +static int efm32_wakeup(struct usbdev_s *dev); +static int efm32_selfpowered(struct usbdev_s *dev, bool selfpowered); +static int efm32_pullup(struct usbdev_s *dev, bool enable); +static void efm32_setaddress(struct efm32_usbdev_s *priv, + uint16_t address); +static int efm32_txfifo_flush(uint32_t txfnum); +static int efm32_rxfifo_flush(void); + +/* Initialization **************************************************************/ + +static void efm32_swinitialize(FAR struct efm32_usbdev_s *priv); +static void efm32_hwinitialize(FAR struct efm32_usbdev_s *priv); + +/******************************************************************************* + * Private Data + *******************************************************************************/ +/* Since there is only a single USB interface, all status information can be + * be simply retained in a single global instance. + */ + +static struct efm32_usbdev_s g_otgfsdev; + +static const struct usbdev_epops_s g_epops = +{ + .configure = efm32_ep_configure, + .disable = efm32_ep_disable, + .allocreq = efm32_ep_allocreq, + .freereq = efm32_ep_freereq, +#ifdef CONFIG_USBDEV_DMA + .allocbuffer = efm32_ep_allocbuffer, + .freebuffer = efm32_ep_freebuffer, +#endif + .submit = efm32_ep_submit, + .cancel = efm32_ep_cancel, + .stall = efm32_ep_stall, +}; + +static const struct usbdev_ops_s g_devops = +{ + .allocep = efm32_ep_alloc, + .freeep = efm32_ep_free, + .getframe = efm32_getframe, + .wakeup = efm32_wakeup, + .selfpowered = efm32_selfpowered, + .pullup = efm32_pullup, +}; + +/* Device error strings that may be enabled for more desciptive USB trace + * output. + */ + +#ifdef CONFIG_USBDEV_TRACE_STRINGS +const struct trace_msg_t g_usb_trace_strings_deverror[] = +{ + TRACE_STR(EFM32_TRACEERR_ALLOCFAIL ), + TRACE_STR(EFM32_TRACEERR_BADCLEARFEATURE ), + TRACE_STR(EFM32_TRACEERR_BADDEVGETSTATUS ), + TRACE_STR(EFM32_TRACEERR_BADEPNO ), + TRACE_STR(EFM32_TRACEERR_BADEPGETSTATUS ), + TRACE_STR(EFM32_TRACEERR_BADGETCONFIG ), + TRACE_STR(EFM32_TRACEERR_BADGETSETDESC ), + TRACE_STR(EFM32_TRACEERR_BADGETSTATUS ), + TRACE_STR(EFM32_TRACEERR_BADSETADDRESS ), + TRACE_STR(EFM32_TRACEERR_BADSETCONFIG ), + TRACE_STR(EFM32_TRACEERR_BADSETFEATURE ), + TRACE_STR(EFM32_TRACEERR_BADTESTMODE ), + TRACE_STR(EFM32_TRACEERR_BINDFAILED ), + TRACE_STR(EFM32_TRACEERR_DISPATCHSTALL ), + TRACE_STR(EFM32_TRACEERR_DRIVER ), + TRACE_STR(EFM32_TRACEERR_DRIVERREGISTERED), + TRACE_STR(EFM32_TRACEERR_EP0NOSETUP ), + TRACE_STR(EFM32_TRACEERR_EP0SETUPSTALLED ), + TRACE_STR(EFM32_TRACEERR_EPINNULLPACKET ), + TRACE_STR(EFM32_TRACEERR_EPINUNEXPECTED ), + TRACE_STR(EFM32_TRACEERR_EPOUTNULLPACKET ), + TRACE_STR(EFM32_TRACEERR_EPOUTUNEXPECTED ), + TRACE_STR(EFM32_TRACEERR_INVALIDCTRLREQ ), + TRACE_STR(EFM32_TRACEERR_INVALIDPARMS ), + TRACE_STR(EFM32_TRACEERR_IRQREGISTRATION ), + TRACE_STR(EFM32_TRACEERR_NOEP ), + TRACE_STR(EFM32_TRACEERR_NOTCONFIGURED ), + TRACE_STR(EFM32_TRACEERR_EPOUTQEMPTY ), + TRACE_STR(EFM32_TRACEERR_EPINREQEMPTY ), + TRACE_STR(EFM32_TRACEERR_NOOUTSETUP ), + TRACE_STR(EFM32_TRACEERR_POLLTIMEOUT ), + TRACE_STR_END +}; +#endif + +/* Interrupt event strings that may be enabled for more desciptive USB trace + * output. + */ + +#ifdef CONFIG_USBDEV_TRACE_STRINGS +const struct trace_msg_t g_usb_trace_strings_intdecode[] = +{ + TRACE_STR(EFM32_TRACEINTID_USB ), + TRACE_STR(EFM32_TRACEINTID_INTPENDING ), + TRACE_STR(EFM32_TRACEINTID_EPOUT ), + TRACE_STR(EFM32_TRACEINTID_EPIN ), + TRACE_STR(EFM32_TRACEINTID_MISMATCH ), + TRACE_STR(EFM32_TRACEINTID_WAKEUP ), + TRACE_STR(EFM32_TRACEINTID_SUSPEND ), + TRACE_STR(EFM32_TRACEINTID_SOF ), + TRACE_STR(EFM32_TRACEINTID_RXFIFO ), + TRACE_STR(EFM32_TRACEINTID_DEVRESET ), + TRACE_STR(EFM32_TRACEINTID_ENUMDNE ), + TRACE_STR(EFM32_TRACEINTID_IISOIXFR ), + TRACE_STR(EFM32_TRACEINTID_IISOOXFR ), + TRACE_STR(EFM32_TRACEINTID_SRQ ), + TRACE_STR(EFM32_TRACEINTID_OTG ), + TRACE_STR(EFM32_TRACEINTID_EPOUT_XFRC ), + TRACE_STR(EFM32_TRACEINTID_EPOUT_EPDISD), + TRACE_STR(EFM32_TRACEINTID_EPOUT_SETUP ), + TRACE_STR(EFM32_TRACEINTID_DISPATCH ), + TRACE_STR(EFM32_TRACEINTID_GETSTATUS ), + TRACE_STR(EFM32_TRACEINTID_EPGETSTATUS ), + TRACE_STR(EFM32_TRACEINTID_DEVGETSTATUS), + TRACE_STR(EFM32_TRACEINTID_IFGETSTATUS ), + TRACE_STR(EFM32_TRACEINTID_CLEARFEATURE), + TRACE_STR(EFM32_TRACEINTID_SETFEATURE ), + TRACE_STR(EFM32_TRACEINTID_SETADDRESS ), + TRACE_STR(EFM32_TRACEINTID_GETSETDESC ), + TRACE_STR(EFM32_TRACEINTID_GETCONFIG ), + TRACE_STR(EFM32_TRACEINTID_SETCONFIG ), + TRACE_STR(EFM32_TRACEINTID_GETSETIF ), + TRACE_STR(EFM32_TRACEINTID_SYNCHFRAME ), + TRACE_STR(EFM32_TRACEINTID_EPIN_XFRC ), + TRACE_STR(EFM32_TRACEINTID_EPIN_TOC ), + TRACE_STR(EFM32_TRACEINTID_EPIN_ITTXFE ), + TRACE_STR(EFM32_TRACEINTID_EPIN_EPDISD ), + TRACE_STR(EFM32_TRACEINTID_EPIN_TXFE ), + TRACE_STR(EFM32_TRACEINTID_EPIN_EMPWAIT), + TRACE_STR(EFM32_TRACEINTID_OUTNAK ), + TRACE_STR(EFM32_TRACEINTID_OUTRECVD ), + TRACE_STR(EFM32_TRACEINTID_OUTDONE ), + TRACE_STR(EFM32_TRACEINTID_SETUPDONE ), + TRACE_STR(EFM32_TRACEINTID_SETUPRECVD ), + TRACE_STR_END +}; +#endif + +/******************************************************************************* + * Public Data + *******************************************************************************/ + +/******************************************************************************* + * Private Functions + *******************************************************************************/ + +/******************************************************************************* + * Name: efm32_getreg + * + * Description: + * Get the contents of an EFM32 register + * + *******************************************************************************/ + +#if defined(CONFIG_EFM32_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG) +static uint32_t efm32_getreg(uint32_t addr) +{ + static uint32_t prevaddr = 0; + static uint32_t preval = 0; + static uint32_t count = 0; + + /* Read the value from the register */ + + uint32_t val = getreg32(addr); + + /* Is this the same value that we read from the same registe last time? Are + * we polling the register? If so, suppress some of the output. + */ + + if (addr == prevaddr && val == preval) + { + if (count == 0xffffffff || ++count > 3) + { + if (count == 4) + { + lldbg("...\n"); + } + return val; + } + } + + /* No this is a new address or value */ + + else + { + /* Did we print "..." for the previous value? */ + + if (count > 3) + { + /* Yes.. then show how many times the value repeated */ + + lldbg("[repeats %d more times]\n", count-3); + } + + /* Save the new address, value, and count */ + + prevaddr = addr; + preval = val; + count = 1; + } + + /* Show the register value read */ + + lldbg("%08x->%08x\n", addr, val); + return val; +} +#endif + +/******************************************************************************* + * Name: efm32_putreg + * + * Description: + * Set the contents of an EFM32 register to a value + * + *******************************************************************************/ + +#if defined(CONFIG_EFM32_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG) +static void efm32_putreg(uint32_t val, uint32_t addr) +{ + /* Show the register value being written */ + + lldbg("%08x<-%08x\n", addr, val); + + /* Write the value */ + + putreg32(val, addr); +} +#endif + +/******************************************************************************* + * Name: efm32_req_remfirst + * + * Description: + * Remove a request from the head of an endpoint request queue + * + *******************************************************************************/ + +static FAR struct efm32_req_s *efm32_req_remfirst(FAR struct efm32_ep_s *privep) +{ + FAR struct efm32_req_s *ret = privep->head; + + if (ret) + { + privep->head = ret->flink; + if (!privep->head) + { + privep->tail = NULL; + } + + ret->flink = NULL; + } + + return ret; +} + +/******************************************************************************* + * Name: efm32_req_addlast + * + * Description: + * Add a request to the end of an endpoint request queue + * + *******************************************************************************/ + +static bool efm32_req_addlast(FAR struct efm32_ep_s *privep, + FAR struct efm32_req_s *req) +{ + bool is_empty = !privep->head; + + req->flink = NULL; + if (is_empty) + { + privep->head = req; + privep->tail = req; + } + else + { + privep->tail->flink = req; + privep->tail = req; + } + return is_empty; +} + +/******************************************************************************* + * Name: efm32_ep0in_setupresponse + * + * Description: + * Schedule a short transfer on Endpoint 0 (IN or OUT) + * + *******************************************************************************/ + +static void efm32_ep0in_setupresponse(FAR struct efm32_usbdev_s *priv, + FAR uint8_t *buf, uint32_t nbytes) +{ + efm32_epin_transfer(&priv->epin[EP0], buf, nbytes); + priv->ep0state = EP0STATE_SETUPRESPONSE; + efm32_ep0out_ctrlsetup(priv); +} + +/**************************************************************************** + * Name: efm32_ep0in_transmitzlp + * + * Description: + * Send a zero length packet (ZLP) on endpoint 0 IN + * + ****************************************************************************/ + +static inline void efm32_ep0in_transmitzlp(FAR struct efm32_usbdev_s *priv) +{ + efm32_ep0in_setupresponse(priv, NULL, 0); +} + +/******************************************************************************* + * Name: efm32_ep0in_activate + * + * Description: + * Activate the endpoint 0 IN endpoint. + * + *******************************************************************************/ + +static void efm32_ep0in_activate(void) +{ + uint32_t regval; + + /* Set the max packet size of the IN EP. */ + + regval = efm32_getreg(EFM32_USB_DIEP0CTL); + regval &= ~_USB_DIEP0CTL_MPS_MASK; + +#if CONFIG_USBDEV_EP0_MAXSIZE == 8 + regval |= _USB_DIEP0CTL_MPS_8B; +#elif CONFIG_USBDEV_EP0_MAXSIZE == 16 + regval |= _USB_DIEP0CTL_MPS_16B; +#elif CONFIG_USBDEV_EP0_MAXSIZE == 32 + regval |= _USB_DIEP0CTL_MPS_32B; +#elif CONFIG_USBDEV_EP0_MAXSIZE == 64 + regval |= _USB_DIEP0CTL_MPS_64B; +#else +# error "Unsupported value of CONFIG_USBDEV_EP0_MAXSIZE" +#endif + + efm32_putreg(regval, EFM32_USB_DIEP0CTL); + + /* Clear global IN NAK */ + + regval = efm32_getreg(EFM32_USB_DCTL); + regval |= USB_DCTL_CGINAK; + efm32_putreg(regval, EFM32_USB_DCTL); +} + +/******************************************************************************* + * Name: efm32_ep0out_ctrlsetup + * + * Description: + * Setup to receive a SETUP packet. + * + *******************************************************************************/ + +static void efm32_ep0out_ctrlsetup(FAR struct efm32_usbdev_s *priv) +{ + uint32_t regval; + + /* Setup the hardware to perform the SETUP transfer */ + + regval = (USB_SIZEOF_CTRLREQ * 3 << _USB_DOEP0TSIZ_XFERSIZE_SHIFT) | + (USB_DOEP0TSIZ_PKTCNT) | + (3 << _USB_DOEP0TSIZ_SUPCNT_SHIFT); + efm32_putreg(regval,EFM32_USB_DOEP0TSIZ); + + /* Then clear NAKing and enable the transfer */ + + regval = efm32_getreg(EFM32_USB_DOEP0CTL); + regval |= (USB_DOEP0CTL_CNAK | USB_DOEP0CTL_EPENA); + efm32_putreg(regval, EFM32_USB_DOEP0CTL); +} + +/**************************************************************************** + * Name: efm32_txfifo_write + * + * Description: + * Send data to the endpoint's TxFIFO. + * + ****************************************************************************/ + +static void efm32_txfifo_write(FAR struct efm32_ep_s *privep, + FAR uint8_t *buf, int nbytes) +{ + uint32_t regaddr; + uint32_t regval; + int nwords; + int i; + + /* Convert the number of bytes to words */ + + nwords = (nbytes + 3) >> 2; + + /* Get the TxFIFO for this endpoint (same as the endpoint number) */ + + regaddr = EFM32_USB_DFIFO_DEP(privep->epphy); + + /* Then transfer each word to the TxFIFO */ + + for (i = 0; i < nwords; i++) + { + /* Read four bytes from the source buffer (to avoid unaligned accesses) + * and pack these into one 32-bit word (little endian). + */ + + regval = (uint32_t)*buf++; + regval |= ((uint32_t)*buf++) << 8; + regval |= ((uint32_t)*buf++) << 16; + regval |= ((uint32_t)*buf++) << 24; + + /* Then write the packet data to the TxFIFO */ + + efm32_putreg(regval, regaddr); + } +} + +/**************************************************************************** + * Name: efm32_epin_transfer + * + * Description: + * Start the Tx data transfer + * + ****************************************************************************/ + +static void efm32_epin_transfer(FAR struct efm32_ep_s *privep, + FAR uint8_t *buf, int nbytes) +{ + uint32_t pktcnt; + uint32_t regval; + + /* Read the DIEPSIZx register */ + + regval = efm32_getreg(EFM32_USB_DIEPTSIZ(privep->epphy)); + + /* Clear the XFERSIZE, PKTCNT, and MCNT field of the DIEPSIZx register */ + + regval &= ~(_USB_DIEPTSIZ_XFERSIZE_MASK | _USB_DIEPTSIZ_PKTCNT_MASK | + _USB_DIEPTSIZ_MCNT_MASK); + + /* Are we sending a zero length packet (ZLP) */ + + if (nbytes == 0) + { + /* Yes.. leave the transfer size at zero and set the packet count to 1 */ + + pktcnt = 1; + } + else + { + /* No.. Program the transfer size and packet count . First calculate: + * + * xfrsize = The total number of bytes to be sent. + * pktcnt = the number of packets (of maxpacket bytes) required to + * perform the transfer. + */ + + pktcnt = ((uint32_t)nbytes + (privep->ep.maxpacket - 1)) / privep->ep.maxpacket; + } + + /* Set the XFERSIZE and PKTCNT */ + + regval |= (pktcnt << _USB_DIEPTSIZ_PKTCNT_SHIFT); + regval |= ((uint32_t)nbytes << _USB_DIEPTSIZ_XFERSIZE_SHIFT); + + /* If this is an isconchronous endpoint, then set the multi-count field to + * the PKTCNT as well. + */ + + if (privep->eptype == USB_EP_ATTR_XFER_ISOC) + { + regval |= (pktcnt << _USB_DIEPTSIZ_MCNT_SHIFT); + } + + /* Save DIEPSIZx register value */ + + efm32_putreg(regval, EFM32_USB_DIEPTSIZ(privep->epphy)); + + /* Read the DIEPCTLx register */ + + regval = efm32_getreg(EFM32_USB_DIEPCTL(privep->epphy)); + + /* If this is an isochronous endpoint, then set the even/odd frame bit + * the DIEPCTLx register. + */ + + if (privep->eptype == USB_EP_ATTR_XFER_ISOC) + { + /* Check bit 0 of the frame number of the received SOF and set the + * even/odd frame to match. + */ + + uint32_t status = efm32_getreg(EFM32_USB_DSTS); + if ((status & USB_DSTS_SOFFN0) == USB_DSTS_SOFFN_EVEN) + { + regval |= USB_DIEPCTL_SEVNFRM; + } + else + { + regval |= USB_DIEPCTL_SODDFRM; + } + } + + /* EP enable, IN data in FIFO */ + + regval &= ~USB_DIEPCTL_EPDIS; + regval |= (USB_DIEPCTL_CNAK | USB_DIEPCTL_EPENA); + efm32_putreg(regval, EFM32_USB_DIEPCTL(privep->epphy)); + + /* Transfer the data to the TxFIFO. At this point, the caller has already + * assured that there is sufficient space in the TxFIFO to hold the transfer + * we can just blindly continue. + */ + + efm32_txfifo_write(privep, buf, nbytes); +} + +/**************************************************************************** + * Name: efm32_epin_request + * + * Description: + * Begin or continue write request processing. + * + ****************************************************************************/ + +static void efm32_epin_request(FAR struct efm32_usbdev_s *priv, + FAR struct efm32_ep_s *privep) +{ + struct efm32_req_s *privreq; + uint32_t regaddr; + uint32_t regval; + uint8_t *buf; + int nbytes; + int nwords; + int bytesleft; + + /* We get here in one of four possible ways. From three interrupting + * events: + * + * 1. From efm32_epin as part of the transfer complete interrupt processing + * This interrupt indicates that the last transfer has completed. + * 2. As part of the ITTXFE interrupt processing. That interrupt indicates + * that an IN token was received when the associated TxFIFO was empty. + * 3. From efm32_epin_txfifoempty as part of the TXFE interrupt processing. + * The TXFE interrupt is only enabled when the TxFIFO is full and the + * software must wait for space to become available in the TxFIFO. + * + * And this function may be called immediately when the write request is + * queue to start up the next transaction. + * + * 4. From efm32_ep_submit when a new write request is received WHILE the + * endpoint is not active (privep->active == false). + */ + + /* Check the request from the head of the endpoint request queue */ + + privreq = efm32_rqpeek(privep); + if (!privreq) + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_EPINREQEMPTY), privep->epphy); + + /* There is no TX transfer in progress and no new pending TX + * requests to send. To stop transmitting any data on a particular + * IN endpoint, the application must set the IN NAK bit. To set this + * bit, the following field must be programmed. + */ + + regaddr = EFM32_USB_DIEPCTL(privep->epphy); + regval = efm32_getreg(regaddr); + regval |= USB_DIEPCTL_SNAK; + efm32_putreg(regval, regaddr); + + /* The endpoint is no longer active */ + + privep->active = false; + return; + } + + ullvdbg("EP%d req=%p: len=%d xfrd=%d zlp=%d\n", + privep->epphy, privreq, privreq->req.len, + privreq->req.xfrd, privep->zlp); + + /* Check for a special case: If we are just starting a request (xfrd==0) and + * the class driver is trying to send a zero-length packet (len==0). Then set + * the ZLP flag so that the packet will be sent. + */ + + if (privreq->req.len == 0) + { + /* The ZLP flag is set TRUE whenever we want to force the driver to + * send a zero-length-packet on the next pass through the loop (below). + * The flag is cleared whenever a packet is sent in the loop below. + */ + + privep->zlp = true; + } + + /* Add one more packet to the TxFIFO. We will wait for the transfer + * complete event before we add the next packet (or part of a packet + * to the TxFIFO). + * + * The documentation says that we can can multiple packets to the TxFIFO, + * but it seems that we need to get the transfer complete event before + * we can add the next (or maybe I have got something wrong?) + */ + +#if 0 + while (privreq->req.xfrd < privreq->req.len || privep->zlp) +#else + if (privreq->req.xfrd < privreq->req.len || privep->zlp) +#endif + { + /* Get the number of bytes left to be sent in the request */ + + bytesleft = privreq->req.len - privreq->req.xfrd; + nbytes = bytesleft; + + /* Assume no zero-length-packet on the next pass through this loop */ + + privep->zlp = false; + + /* Limit the size of the transfer to one full packet and handle + * zero-length packets (ZLPs). + */ + + if (nbytes > 0) + { + /* Either send the maxpacketsize or all of the remaining data in + * the request. + */ + + if (nbytes >= privep->ep.maxpacket) + { + nbytes = privep->ep.maxpacket; + + /* Handle the case where this packet is exactly the + * maxpacketsize. Do we need to send a zero-length packet + * in this case? + */ + + if (bytesleft == privep->ep.maxpacket && + (privreq->req.flags & USBDEV_REQFLAGS_NULLPKT) != 0) + { + /* The ZLP flag is set TRUE whenever we want to force + * the driver to send a zero-length-packet on the next + * pass through this loop. The flag is cleared (above) + * whenever we are committed to sending any packet and + * set here when we want to force one more pass through + * the loop. + */ + + privep->zlp = true; + } + } + } + + /* Get the transfer size in 32-bit words */ + + nwords = (nbytes + 3) >> 2; + + /* Get the number of 32-bit words available in the TxFIFO. The + * DXTFSTS indicates the amount of free space available in the + * endpoint TxFIFO. Values are in terms of 32-bit words: + * + * 0: Endpoint TxFIFO is full + * 1: 1 word available + * 2: 2 words available + * n: n words available + */ + + regaddr = EFM32_USB_DTXFSTS(privep->epphy); + + /* Check for space in the TxFIFO. If space in the TxFIFO is not + * available, then set up an interrupt to resume the transfer when + * the TxFIFO is empty. + */ + + regval = efm32_getreg(regaddr); + if ((int)(regval & _USB_DTXFSTS_MASK) < nwords) + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_EPIN_EMPWAIT), (uint16_t)regval); + + /* There is insufficient space in the TxFIFO. Wait for a TxFIFO + * empty interrupt and try again. + */ + + uint32_t empmsk = efm32_getreg(EFM32_USB_DIEPEMPMSK); + empmsk |= USB_DIEPEMPMSK(privep->epphy); + efm32_putreg(empmsk, EFM32_USB_DIEPEMPMSK); + + /* Terminate the transfer. We will try again when the TxFIFO empty + * interrupt is received. + */ + + return; + } + + /* Transfer data to the TxFIFO */ + + buf = privreq->req.buf + privreq->req.xfrd; + efm32_epin_transfer(privep, buf, nbytes); + + /* If it was not before, the OUT endpoint is now actively transferring + * data. + */ + + privep->active = true; + + /* EP0 is a special case */ + + if (privep->epphy == EP0) + { + priv->ep0state = EP0STATE_DATA_IN; + } + + /* Update for the next time through the loop */ + + privreq->req.xfrd += nbytes; + } + + /* Note that the ZLP, if any, must be sent as a separate transfer. The need + * for a ZLP is indicated by privep->zlp. If all of the bytes were sent + * (including any final null packet) then we are finished with the transfer + */ + + if (privreq->req.xfrd >= privreq->req.len && !privep->zlp) + { + usbtrace(TRACE_COMPLETE(privep->epphy), privreq->req.xfrd); + + /* We are finished with the request (although the transfer has not + * yet completed). + */ + + efm32_req_complete(privep, OK); + } +} + +/******************************************************************************* + * Name: efm32_rxfifo_read + * + * Description: + * Read packet from the RxFIFO into a read request. + * + *******************************************************************************/ + +static void efm32_rxfifo_read(FAR struct efm32_ep_s *privep, + FAR uint8_t *dest, uint16_t len) +{ + uint32_t regaddr; + int i; + + /* Get the address of the RxFIFO. Note: there is only one RxFIFO so + * we might as well use the addess associated with EP0. + */ + + regaddr = EFM32_USB_DFIFO_DEP(EP0); + + /* Read 32-bits and write 4 x 8-bits at time (to avoid unaligned accesses) */ + + for (i = 0; i < len; i += 4) + { + union + { + uint32_t w; + uint8_t b[4]; + } data; + + /* Read 1 x 32-bits of EP0 packet data */ + + data.w = efm32_getreg(regaddr); + + /* Write 4 x 8-bits of EP0 packet data */ + + *dest++ = data.b[0]; + *dest++ = data.b[1]; + *dest++ = data.b[2]; + *dest++ = data.b[3]; + } +} + +/******************************************************************************* + * Name: efm32_rxfifo_discard + * + * Description: + * Discard packet data from the RxFIFO. + * + *******************************************************************************/ + +static void efm32_rxfifo_discard(FAR struct efm32_ep_s *privep, int len) +{ + if (len > 0) + { + uint32_t regaddr; + int i; + + /* Get the address of the RxFIFO Note: there is only one RxFIFO so + * we might as well use the addess associated with EP0. + */ + + regaddr = EFM32_USB_DFIFO_DEP(EP0); + + /* Read 32-bits at time */ + + for (i = 0; i < len; i += 4) + { + volatile uint32_t data = efm32_getreg(regaddr); + (void)data; + } + } +} + +/******************************************************************************* + * Name: efm32_epout_complete + * + * Description: + * This function is called when an OUT transfer complete interrupt is + * received. It completes the read request at the head of the endpoint's + * request queue. + * + *******************************************************************************/ + +static void efm32_epout_complete(FAR struct efm32_usbdev_s *priv, + FAR struct efm32_ep_s *privep) +{ + struct efm32_req_s *privreq; + + /* Since a transfer just completed, there must be a read request at the head of + * the endpoint request queue. + */ + + privreq = efm32_rqpeek(privep); + DEBUGASSERT(privreq); + + if (!privreq) + { + /* An OUT transfer completed, but no packet to receive the data. This + * should not happen. + */ + + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_EPOUTQEMPTY), privep->epphy); + privep->active = false; + return; + } + + ullvdbg("EP%d: len=%d xfrd=%d\n", + privep->epphy, privreq->req.len, privreq->req.xfrd); + + /* Return the completed read request to the class driver and mark the state + * IDLE. + */ + + usbtrace(TRACE_COMPLETE(privep->epphy), privreq->req.xfrd); + efm32_req_complete(privep, OK); + privep->active = false; + + /* Now set up the next read request (if any) */ + + efm32_epout_request(priv, privep); +} + +/******************************************************************************* + * Name: efm32_ep0out_receive + * + * Description: + * This function is called from the RXFLVL interrupt handler when new incoming + * data is available in the endpoint's RxFIFO. This function will simply + * copy the incoming data into pending request's data buffer. + * + *******************************************************************************/ + +static inline void efm32_ep0out_receive(FAR struct efm32_ep_s *privep, int bcnt) +{ + FAR struct efm32_usbdev_s *priv; + + /* Sanity Checking */ + + DEBUGASSERT(privep && privep->ep.priv); + priv = (FAR struct efm32_usbdev_s *)privep->ep.priv; + + ullvdbg("EP0: bcnt=%d\n", bcnt); + usbtrace(TRACE_READ(EP0), bcnt); + + /* Verify that an OUT SETUP request as received before this data was + * received in the RxFIFO. + */ + + if (priv->ep0state == EP0STATE_SETUP_OUT) + { + /* Read the data into our special buffer for SETUP data */ + + int readlen = MIN(CONFIG_USBDEV_SETUP_MAXDATASIZE, bcnt); + efm32_rxfifo_read(privep, priv->ep0data, readlen); + + /* Do we have to discard any excess bytes? */ + + efm32_rxfifo_discard(privep, bcnt - readlen); + + /* Now we can process the setup command */ + + privep->active = false; + priv->ep0state = EP0STATE_SETUP_READY; + priv->ep0datlen = readlen; + + efm32_ep0out_setup(priv); + } + else + { + /* This is an error. We don't have any idea what to do with the EP0 + * data in this case. Just read and discard it so that the RxFIFO + * does not become constipated. + */ + + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_NOOUTSETUP), priv->ep0state); + efm32_rxfifo_discard(privep, bcnt); + privep->active = false; + } +} + +/******************************************************************************* + * Name: efm32_epout_receive + * + * Description: + * This function is called from the RXFLVL interrupt handler when new incoming + * data is available in the endpoint's RxFIFO. This function will simply + * copy the incoming data into pending request's data buffer. + * + *******************************************************************************/ + +static inline void efm32_epout_receive(FAR struct efm32_ep_s *privep, int bcnt) +{ + struct efm32_req_s *privreq; + uint8_t *dest; + int buflen; + int readlen; + + /* Get a reference to the request at the head of the endpoint's request + * queue. + */ + + privreq = efm32_rqpeek(privep); + if (!privreq) + { + /* Incoming data is available in the RxFIFO, but there is no read setup + * to receive the receive the data. This should not happen for data + * endpoints; those endpoints should have been NAKing any OUT data tokens. + * + * We should get here normally on OUT data phase following an OUT + * SETUP command. EP0 data will still receive data in this case and it + * should not be NAKing. + */ + + if (privep->epphy == 0) + { + efm32_ep0out_receive(privep, bcnt); + } + else + { + /* Otherwise, the data is lost. This really should not happen if + * NAKing is working as expected. + */ + + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_EPOUTQEMPTY), privep->epphy); + + /* Discard the data in the RxFIFO */ + + efm32_rxfifo_discard(privep, bcnt); + } + + privep->active = false; + return; + } + + ullvdbg("EP%d: len=%d xfrd=%d\n", privep->epphy, privreq->req.len, privreq->req.xfrd); + usbtrace(TRACE_READ(privep->epphy), bcnt); + + /* Get the number of bytes to transfer from the RxFIFO */ + + buflen = privreq->req.len - privreq->req.xfrd; + DEBUGASSERT(buflen > 0 && buflen >= bcnt); + readlen = MIN(buflen, bcnt); + + /* Get the destination of the data transfer */ + + dest = privreq->req.buf + privreq->req.xfrd; + + /* Transfer the data from the RxFIFO to the request's data buffer */ + + efm32_rxfifo_read(privep, dest, readlen); + + /* If there were more bytes in the RxFIFO than could be held in the read + * request, then we will have to discard those. + */ + + efm32_rxfifo_discard(privep, bcnt - readlen); + + /* Update the number of bytes transferred */ + + privreq->req.xfrd += readlen; +} + +/******************************************************************************* + * Name: efm32_epout_request + * + * Description: + * This function is called when either (1) new read request is received, or + * (2) a pending receive request completes. If there is no read in pending, + * then this function will initiate the next OUT (read) operation. + * + *******************************************************************************/ + +static void efm32_epout_request(FAR struct efm32_usbdev_s *priv, + FAR struct efm32_ep_s *privep) +{ + struct efm32_req_s *privreq; + uint32_t regaddr; + uint32_t regval; + uint32_t xfrsize; + uint32_t pktcnt; + + /* Make sure that there is not already a pending request request. If there is, + * just return, leaving the newly received request in the request queue. + */ + + if (!privep->active) + { + /* Loop until a valid request is found (or the request queue is empty). + * The loop is only need to look at the request queue again is an invalid + * read request is encountered. + */ + + for (;;) + { + /* Get a reference to the request at the head of the endpoint's request queue */ + + privreq = efm32_rqpeek(privep); + if (!privreq) + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_EPOUTQEMPTY), privep->epphy); + + /* There are no read requests to be setup. Configure the hardware to + * NAK any incoming packets. (This should already be the case. I + * think that the hardware will automatically NAK after a transfer is + * completed until SNAK is cleared). + */ + + regaddr = EFM32_USB_DOEPCTL(privep->epphy); + regval = efm32_getreg(regaddr); + regval |= USB_DOEPCTL_SNAK; + efm32_putreg(regval, regaddr); + + /* This endpoint is no longer actively transferring */ + + privep->active = false; + return; + } + + ullvdbg("EP%d: len=%d\n", privep->epphy, privreq->req.len); + + /* Ignore any attempt to receive a zero length packet (this really + * should not happen. + */ + + if (privreq->req.len <= 0) + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_EPOUTNULLPACKET), 0); + efm32_req_complete(privep, OK); + } + + /* Otherwise, we have a usable read request... break out of the loop */ + + else + { + break; + } + } + + /* Setup the pending read into the request buffer. First calculate: + * + * pktcnt = the number of packets (of maxpacket bytes) required to + * perform the transfer. + * xfrsize = The total number of bytes required (in units of + * maxpacket bytes). + */ + + pktcnt = (privreq->req.len + (privep->ep.maxpacket - 1)) / privep->ep.maxpacket; + xfrsize = pktcnt * privep->ep.maxpacket; + + /* Then setup the hardware to perform this transfer */ + + regaddr = EFM32_USB_DOEPTSIZ(privep->epphy); + regval = efm32_getreg(regaddr); + regval &= ~(_USB_DOEPTSIZ_XFERSIZE_MASK | _USB_DOEPTSIZ_PKTCNT_MASK); + regval |= (xfrsize << _USB_DOEPTSIZ_XFERSIZE_SHIFT); + regval |= (pktcnt << _USB_DOEPTSIZ_PKTCNT_SHIFT); + efm32_putreg(regval, regaddr); + + /* Then enable the transfer */ + + regaddr = EFM32_USB_DOEPCTL(privep->epphy); + regval = efm32_getreg(regaddr); + + /* When an isochronous transfer is enabled the Even/Odd frame bit must + * also be set appropriately. + */ + +#ifdef CONFIG_USBDEV_ISOCHRONOUS + if (privep->eptype == USB_EP_ATTR_XFER_ISOC) + { + if (privep->odd) + { + regval |= USB_DOEPCTL_SODDFRM; + } + else + { + regval |= USB_DOEPCTL_SEVNFRM; + } + } +#endif + + /* Clearing NAKing and enable the transfer. */ + + regval |= (USB_DOEPCTL_CNAK | USB_DOEPCTL_EPENA); + efm32_putreg(regval, regaddr); + + /* A transfer is now active on this endpoint */ + + privep->active = true; + + /* EP0 is a special case. We need to know when to switch back to + * normal SETUP processing. + */ + + if (privep->epphy == EP0) + { + priv->ep0state = EP0STATE_DATA_OUT; + } + } +} + +/******************************************************************************* + * Name: efm32_ep_flush + * + * Description: + * Flush any primed descriptors from this ep + * + *******************************************************************************/ + +static void efm32_ep_flush(struct efm32_ep_s *privep) +{ + if (privep->isin) + { + efm32_txfifo_flush(USB_GRSTCTL_TXFNUM_D(privep->epphy)); + } + else + { + efm32_rxfifo_flush(); + } +} + +/******************************************************************************* + * Name: efm32_req_complete + * + * Description: + * Handle termination of the request at the head of the endpoint request queue. + * + *******************************************************************************/ + +static void efm32_req_complete(struct efm32_ep_s *privep, int16_t result) +{ + FAR struct efm32_req_s *privreq; + + /* Remove the request at the head of the request list */ + + privreq = efm32_req_remfirst(privep); + DEBUGASSERT(privreq != NULL); + + /* If endpoint 0, temporarily reflect the state of protocol stalled + * in the callback. + */ + + bool stalled = privep->stalled; + if (privep->epphy == EP0) + { + privep->stalled = privep->dev->stalled; + } + + /* Save the result in the request structure */ + + privreq->req.result = result; + + /* Callback to the request completion handler */ + + privreq->req.callback(&privep->ep, &privreq->req); + + /* Restore the stalled indication */ + + privep->stalled = stalled; +} + +/******************************************************************************* + * Name: efm32_req_cancel + * + * Description: + * Cancel all pending requests for an endpoint + * + *******************************************************************************/ + +static void efm32_req_cancel(struct efm32_ep_s *privep, int16_t status) +{ + if (!efm32_rqempty(privep)) + { + efm32_ep_flush(privep); + } + + while (!efm32_rqempty(privep)) + { + usbtrace(TRACE_COMPLETE(privep->epphy), + (efm32_rqpeek(privep))->req.xfrd); + efm32_req_complete(privep, status); + } +} + +/******************************************************************************* + * Name: efm32_ep_findbyaddr + * + * Description: + * Find the physical endpoint structure corresponding to a logic endpoint + * address + * + *******************************************************************************/ + +static struct efm32_ep_s *efm32_ep_findbyaddr(struct efm32_usbdev_s *priv, + uint16_t eplog) +{ + struct efm32_ep_s *privep; + uint8_t epphy = USB_EPNO(eplog); + + if (epphy >= EFM32_NENDPOINTS) + { + return NULL; + } + + /* Is this an IN or an OUT endpoint? */ + + if (USB_ISEPIN(eplog)) + { + privep = &priv->epin[epphy]; + } + else + { + privep = &priv->epout[epphy]; + } + + /* Return endpoint reference */ + + DEBUGASSERT(privep->epphy == epphy); + return privep; +} + +/******************************************************************************* + * Name: efm32_req_dispatch + * + * Description: + * Provide unhandled setup actions to the class driver. This is logically part + * of the USB interrupt handler. + * + *******************************************************************************/ + +static int efm32_req_dispatch(struct efm32_usbdev_s *priv, + const struct usb_ctrlreq_s *ctrl) +{ + int ret = -EIO; + + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_DISPATCH), 0); + if (priv->driver) + { + /* Forward to the control request to the class driver implementation */ + + ret = CLASS_SETUP(priv->driver, &priv->usbdev, ctrl, + priv->ep0data, priv->ep0datlen); + } + + if (ret < 0) + { + /* Stall on failure */ + + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_DISPATCHSTALL), 0); + priv->stalled = true; + } + + return ret; +} + +/******************************************************************************* + * Name: efm32_usbreset + * + * Description: + * Reset Usb engine + * + *******************************************************************************/ + +static void efm32_usbreset(struct efm32_usbdev_s *priv) +{ + FAR struct efm32_ep_s *privep; + uint32_t regval; + int i; + + /* Clear the Remote Wake-up Signaling */ + + regval = efm32_getreg(EFM32_USB_DCTL); + regval &= ~USB_DCTL_RWUSIG; + efm32_putreg(regval, EFM32_USB_DCTL); + + /* Flush the EP0 Tx FIFO */ + + efm32_txfifo_flush(USB_GRSTCTL_TXFNUM_D(EP0)); + + /* Tell the class driver that we are disconnected. The class + * driver should then accept any new configurations. + */ + + if (priv->driver) + { + CLASS_DISCONNECT(priv->driver, &priv->usbdev); + } + + /* Mark all endpoints as available */ + + priv->epavail[0] = EFM32_EP_AVAILABLE; + priv->epavail[1] = EFM32_EP_AVAILABLE; + + /* Disable all end point interrupts */ + + for (i = 0; i < EFM32_NENDPOINTS ; i++) + { + /* Disable endpoint interrupts */ + + efm32_putreg(0xff, EFM32_USB_DIEPINT(i)); + efm32_putreg(0xff, EFM32_USB_DOEPINT(i)); + + /* Return write requests to the class implementation */ + + privep = &priv->epin[i]; + efm32_req_cancel(privep, -ESHUTDOWN); + + /* Reset IN endpoint status */ + + privep->stalled = false; + + /* Return read requests to the class implementation */ + + privep = &priv->epout[i]; + efm32_req_cancel(privep, -ESHUTDOWN); + + /* Reset endpoint status */ + + privep->stalled = false; + } + + efm32_putreg(0xffffffff, EFM32_USB_DAINT); + + /* Mask all device endpoint interrupts except EP0 */ + + regval = (USB_DAINT_IEP(EP0) | USB_DAINT_OEP(EP0)); + efm32_putreg(regval, EFM32_USB_DAINTMSK); + + /* Unmask OUT interrupts */ + + regval = (USB_DOEPMSK_XFERCOMPLMSK | USB_DOEPMSK_SETUPMSK | + USB_DOEPMSK_EPDISBLDMSK); + efm32_putreg(regval, EFM32_USB_DOEPMSK); + + /* Unmask IN interrupts */ + + regval = (USB_DIEPMSK_XFERCOMPLMSK | USB_DIEPMSK_EPDISBLDMSK | + USB_DIEPMSK_TIMEOUTMSK); + efm32_putreg(regval, EFM32_USB_DIEPMSK); + + /* Reset device address to 0 */ + + efm32_setaddress(priv, 0); + priv->devstate = DEVSTATE_DEFAULT; + priv->usbdev.speed = USB_SPEED_FULL; + + /* Re-configure EP0 */ + + efm32_ep0_configure(priv); + + /* Setup EP0 to receive SETUP packets */ + + efm32_ep0out_ctrlsetup(priv); +} + +/******************************************************************************* + * Name: efm32_ep0out_testmode + * + * Description: + * Select test mode + * + *******************************************************************************/ + +static inline void efm32_ep0out_testmode(FAR struct efm32_usbdev_s *priv, + uint16_t index) +{ + uint8_t testmode; + + testmode = index >> 8; + switch (testmode) + { + case 1: + priv->testmode = USB_TESTMODE_J; + break; + + case 2: + priv->testmode = USB_TESTMODE_K; + break; + + case 3: + priv->testmode = USB_TESTMODE_SE0_NAK; + break; + + case 4: + priv->testmode = USB_TESTMODE_PACKET; + break; + + case 5: + priv->testmode = USB_TESTMODE_FORCE; + break; + + default: + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_BADTESTMODE), testmode); + priv->dotest = false; + priv->testmode = USB_TESTMODE_DISABLED; + priv->stalled = true; + } + + priv->dotest = true; + efm32_ep0in_transmitzlp(priv); +} + +/******************************************************************************* + * Name: efm32_ep0out_stdrequest + * + * Description: + * Handle a stanard request on EP0. Pick off the things of interest to the + * USB device controller driver; pass what is left to the class driver. + * + *******************************************************************************/ + +static inline void efm32_ep0out_stdrequest(struct efm32_usbdev_s *priv, + FAR struct efm32_ctrlreq_s *ctrlreq) +{ + FAR struct efm32_ep_s *privep; + + /* Handle standard request */ + + switch (ctrlreq->req) + { + case USB_REQ_GETSTATUS: + { + /* type: device-to-host; recipient = device, interface, endpoint + * value: 0 + * index: zero interface endpoint + * len: 2; data = status + */ + + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_GETSTATUS), 0); + if (!priv->addressed || + ctrlreq->len != 2 || + USB_REQ_ISOUT(ctrlreq->type) || + ctrlreq->value != 0) + { + priv->stalled = true; + } + else + { + switch (ctrlreq->type & USB_REQ_RECIPIENT_MASK) + { + case USB_REQ_RECIPIENT_ENDPOINT: + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_EPGETSTATUS), 0); + privep = efm32_ep_findbyaddr(priv, ctrlreq->index); + if (!privep) + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_BADEPGETSTATUS), 0); + priv->stalled = true; + } + else + { + if (privep->stalled) + { + priv->ep0data[0] = (1 << USB_FEATURE_ENDPOINTHALT); + } + else + { + priv->ep0data[0] = 0; /* Not stalled */ + } + + priv->ep0data[1] = 0; + efm32_ep0in_setupresponse(priv, priv->ep0data, 2); + } + } + break; + + case USB_REQ_RECIPIENT_DEVICE: + { + if (ctrlreq->index == 0) + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_DEVGETSTATUS), 0); + + /* Features: Remote Wakeup and selfpowered */ + + priv->ep0data[0] = (priv->selfpowered << USB_FEATURE_SELFPOWERED); + priv->ep0data[0] |= (priv->wakeup << USB_FEATURE_REMOTEWAKEUP); + priv->ep0data[1] = 0; + + efm32_ep0in_setupresponse(priv, priv->ep0data, 2); + } + else + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_BADDEVGETSTATUS), 0); + priv->stalled = true; + } + } + break; + + case USB_REQ_RECIPIENT_INTERFACE: + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_IFGETSTATUS), 0); + priv->ep0data[0] = 0; + priv->ep0data[1] = 0; + + efm32_ep0in_setupresponse(priv, priv->ep0data, 2); + } + break; + + default: + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_BADGETSTATUS), 0); + priv->stalled = true; + } + break; + } + } + } + break; + + case USB_REQ_CLEARFEATURE: + { + /* type: host-to-device; recipient = device, interface or endpoint + * value: feature selector + * index: zero interface endpoint; + * len: zero, data = none + */ + + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_CLEARFEATURE), 0); + if (priv->addressed != 0 && ctrlreq->len == 0) + { + uint8_t recipient = ctrlreq->type & USB_REQ_RECIPIENT_MASK; + if (recipient == USB_REQ_RECIPIENT_ENDPOINT && + ctrlreq->value == USB_FEATURE_ENDPOINTHALT && + (privep = efm32_ep_findbyaddr(priv, ctrlreq->index)) != NULL) + { + efm32_ep_clrstall(privep); + efm32_ep0in_transmitzlp(priv); + } + else if (recipient == USB_REQ_RECIPIENT_DEVICE && + ctrlreq->value == USB_FEATURE_REMOTEWAKEUP) + { + priv->wakeup = 0; + efm32_ep0in_transmitzlp(priv); + } + else + { + /* Actually, I think we could just stall here. */ + + (void)efm32_req_dispatch(priv, &priv->ctrlreq); + } + } + else + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_BADCLEARFEATURE), 0); + priv->stalled = true; + } + } + break; + + case USB_REQ_SETFEATURE: + { + /* type: host-to-device; recipient = device, interface, endpoint + * value: feature selector + * index: zero interface endpoint; + * len: 0; data = none + */ + + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_SETFEATURE), 0); + if (priv->addressed != 0 && ctrlreq->len == 0) + { + uint8_t recipient = ctrlreq->type & USB_REQ_RECIPIENT_MASK; + if (recipient == USB_REQ_RECIPIENT_ENDPOINT && + ctrlreq->value == USB_FEATURE_ENDPOINTHALT && + (privep = efm32_ep_findbyaddr(priv, ctrlreq->index)) != NULL) + { + efm32_ep_setstall(privep); + efm32_ep0in_transmitzlp(priv); + } + else if (recipient == USB_REQ_RECIPIENT_DEVICE && + ctrlreq->value == USB_FEATURE_REMOTEWAKEUP) + { + priv->wakeup = 1; + efm32_ep0in_transmitzlp(priv); + } + else if (recipient == USB_REQ_RECIPIENT_DEVICE && + ctrlreq->value == USB_FEATURE_TESTMODE && + ((ctrlreq->index & 0xff) == 0)) + { + efm32_ep0out_testmode(priv, ctrlreq->index); + } + else if (priv->configured) + { + /* Actually, I think we could just stall here. */ + + (void)efm32_req_dispatch(priv, &priv->ctrlreq); + } + else + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_BADSETFEATURE), 0); + priv->stalled = true; + } + } + else + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_BADSETFEATURE), 0); + priv->stalled = true; + } + } + break; + + case USB_REQ_SETADDRESS: + { + /* type: host-to-device; recipient = device + * value: device address + * index: 0 + * len: 0; data = none + */ + + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_SETADDRESS), ctrlreq->value); + if ((ctrlreq->type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE && + ctrlreq->index == 0 && + ctrlreq->len == 0 && + ctrlreq->value < 128 && + priv->devstate != DEVSTATE_CONFIGURED) + { + /* Save the address. We cannot actually change to the next address until + * the completion of the status phase. + */ + + efm32_setaddress(priv, (uint16_t)priv->ctrlreq.value[0]); + efm32_ep0in_transmitzlp(priv); + } + else + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_BADSETADDRESS), 0); + priv->stalled = true; + } + } + break; + + case USB_REQ_GETDESCRIPTOR: + /* type: device-to-host; recipient = device + * value: descriptor type and index + * index: 0 or language ID; + * len: descriptor len; data = descriptor + */ + + case USB_REQ_SETDESCRIPTOR: + /* type: host-to-device; recipient = device + * value: descriptor type and index + * index: 0 or language ID; + * len: descriptor len; data = descriptor + */ + + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_GETSETDESC), 0); + if ((ctrlreq->type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE) + { + (void)efm32_req_dispatch(priv, &priv->ctrlreq); + } + else + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_BADGETSETDESC), 0); + priv->stalled = true; + } + } + break; + + case USB_REQ_GETCONFIGURATION: + /* type: device-to-host; recipient = device + * value: 0; + * index: 0; + * len: 1; data = configuration value + */ + + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_GETCONFIG), 0); + if (priv->addressed && + (ctrlreq->type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE && + ctrlreq->value == 0 && + ctrlreq->index == 0 && + ctrlreq->len == 1) + { + (void)efm32_req_dispatch(priv, &priv->ctrlreq); + } + else + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_BADGETCONFIG), 0); + priv->stalled = true; + } + } + break; + + case USB_REQ_SETCONFIGURATION: + /* type: host-to-device; recipient = device + * value: configuration value + * index: 0; + * len: 0; data = none + */ + + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_SETCONFIG), 0); + if (priv->addressed && + (ctrlreq->type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE && + ctrlreq->index == 0 && + ctrlreq->len == 0) + { + /* Give the configuration to the class driver */ + + int ret = efm32_req_dispatch(priv, &priv->ctrlreq); + + /* If the class driver accepted the configuration, then mark the + * device state as configured (or not, depending on the + * configuration). + */ + + if (ret == OK) + { + uint8_t cfg = (uint8_t)ctrlreq->value; + if (cfg != 0) + { + priv->devstate = DEVSTATE_CONFIGURED; + priv->configured = true; + } + else + { + priv->devstate = DEVSTATE_ADDRESSED; + priv->configured = false; + } + } + } + else + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_BADSETCONFIG), 0); + priv->stalled = true; + } + } + break; + + case USB_REQ_GETINTERFACE: + /* type: device-to-host; recipient = interface + * value: 0 + * index: interface; + * len: 1; data = alt interface + */ + + case USB_REQ_SETINTERFACE: + /* type: host-to-device; recipient = interface + * value: alternate setting + * index: interface; + * len: 0; data = none + */ + + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_GETSETIF), 0); + (void)efm32_req_dispatch(priv, &priv->ctrlreq); + } + break; + + case USB_REQ_SYNCHFRAME: + /* type: device-to-host; recipient = endpoint + * value: 0 + * index: endpoint; + * len: 2; data = frame number + */ + + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_SYNCHFRAME), 0); + } + break; + + default: + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_INVALIDCTRLREQ), 0); + priv->stalled = true; + } + break; + } +} + +/******************************************************************************* + * Name: efm32_ep0out_setup + * + * Description: + * USB Ctrl EP Setup Event. This is logically part of the USB interrupt + * handler. This event occurs when a setup packet is receive on EP0 OUT. + * + *******************************************************************************/ + +static inline void efm32_ep0out_setup(struct efm32_usbdev_s *priv) +{ + struct efm32_ctrlreq_s ctrlreq; + + /* Verify that a SETUP was received */ + + if (priv->ep0state != EP0STATE_SETUP_READY) + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_EP0NOSETUP), priv->ep0state); + return; + } + + /* Terminate any pending requests */ + + efm32_req_cancel(&priv->epout[EP0], -EPROTO); + efm32_req_cancel(&priv->epin[EP0], -EPROTO); + + /* Assume NOT stalled */ + + priv->epout[EP0].stalled = false; + priv->epin[EP0].stalled = false; + priv->stalled = false; + + /* Starting to process a control request - update state */ + + priv->ep0state = EP0STATE_SETUP_PROCESS; + + /* And extract the little-endian 16-bit values to host order */ + + ctrlreq.type = priv->ctrlreq.type; + ctrlreq.req = priv->ctrlreq.req; + ctrlreq.value = GETUINT16(priv->ctrlreq.value); + ctrlreq.index = GETUINT16(priv->ctrlreq.index); + ctrlreq.len = GETUINT16(priv->ctrlreq.len); + + ullvdbg("type=%02x req=%02x value=%04x index=%04x len=%04x\n", + ctrlreq.type, ctrlreq.req, ctrlreq.value, ctrlreq.index, ctrlreq.len); + + /* Check for a standard request */ + + if ((ctrlreq.type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD) + { + /* Dispatch any non-standard requests */ + + (void)efm32_req_dispatch(priv, &priv->ctrlreq); + } + else + { + /* Handle standard requests. */ + + efm32_ep0out_stdrequest(priv, &ctrlreq); + } + + /* Check if the setup processing resulted in a STALL */ + + if (priv->stalled) + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_EP0SETUPSTALLED), priv->ep0state); + efm32_ep0_stall(priv); + } + + /* Reset state/data associated with thie SETUP request */ + + priv->ep0datlen = 0; +} + +/******************************************************************************* + * Name: efm32_epout + * + * Description: + * This is part of the OUT endpoint interrupt processing. This function + * handles the OUT event for a single endpoint. + * + *******************************************************************************/ + +static inline void efm32_epout(FAR struct efm32_usbdev_s *priv, uint8_t epno) +{ + FAR struct efm32_ep_s *privep; + + /* Endpoint 0 is a special case. */ + + if (epno == 0) + { + privep = &priv->epout[EP0]; + + /* In the EP0STATE_DATA_OUT state, we are receiving data into the + * request buffer. In that case, we must continue the request + * processing. + */ + + if (priv->ep0state == EP0STATE_DATA_OUT) + { + /* Continue processing data from the EP0 OUT request queue */ + + efm32_epout_complete(priv, privep); + + /* If we are not actively processing an OUT request, then we + * need to setup to receive the next control request. + */ + + if (!privep->active) + { + efm32_ep0out_ctrlsetup(priv); + priv->ep0state = EP0STATE_IDLE; + } + } + } + + /* For other endpoints, the only possibility is that we are continuing + * or finishing an OUT request. + */ + + else if (priv->devstate == DEVSTATE_CONFIGURED) + { + efm32_epout_complete(priv, &priv->epout[epno]); + } +} + +/******************************************************************************* + * Name: efm32_epout_interrupt + * + * Description: + * USB OUT endpoint interrupt handler. The core generates this interrupt when + * there is an interrupt is pending on one of the OUT endpoints of the core. + * The driver must read the OTGFS DAINT register to determine the exact number + * of the OUT endpoint on which the interrupt occurred, and then read the + * corresponding OTGFS DOEPINTx register to determine the exact cause of the + * interrupt. + * + *******************************************************************************/ + +static inline void efm32_epout_interrupt(FAR struct efm32_usbdev_s *priv) +{ + uint32_t daint; + uint32_t regval; + uint32_t doepint; + int epno; + + /* Get the pending, enabled interrupts for the OUT endpoint from the endpoint + * interrupt status register. + */ + + regval = efm32_getreg(EFM32_USB_DAINT); + regval &= efm32_getreg(EFM32_USB_DAINTMSK); + daint = (regval & _USB_DAINT_OEP_MASK) >> _USB_DAINT_OEP_SHIFT; + + if (daint == 0) + { + /* We got an interrupt, but there is no unmasked endpoint that caused + * it ?! When this happens, the interrupt flag never gets cleared and + * we are stuck in infinite interrupt loop. + * + * This shouldn't happen if we are diligent about handling timing + * issues when masking endpoint interrupts. However, this workaround + * avoids infinite loop and allows operation to continue normally. It + * works by clearing each endpoint flags, masked or not. + */ + + regval = efm32_getreg(EFM32_USB_DAINT); + daint = (regval & _USB_DAINT_OEP_MASK) >> _USB_DAINT_OEP_SHIFT; + + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_EPOUTUNEXPECTED), + (uint16_t)regval); + + epno = 0; + while (daint) + { + if ((daint & 1) != 0) + { + regval = efm32_getreg(EFM32_USB_DOEPINT(epno)); + ulldbg("DOEPINT(%d) = %08x\n", epno, regval); + efm32_putreg(0xFF, EFM32_USB_DOEPINT(epno)); + } + + epno++; + daint >>= 1; + } + + return; + } + + /* Process each pending IN endpoint interrupt */ + + epno = 0; + while (daint) + { + /* Is an OUT interrupt pending for this endpoint? */ + + if ((daint & 1) != 0) + { + /* Yes.. get the OUT endpoint interrupt status */ + + doepint = efm32_getreg(EFM32_USB_DOEPINT(epno)); + doepint &= efm32_getreg(EFM32_USB_DOEPMSK); + + /* Transfer completed interrupt. This interrupt is trigged when + * efm32_rxinterrupt() removes the last packet data from the RxFIFO. + * In this case, core internally sets the NAK bit for this endpoint to + * prevent it from receiving any more packets. + */ + + if ((doepint & USB_DOEPINT_XFRC) != 0) + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_EPOUT_XFRC), (uint16_t)doepint); + + /* Clear the bit in DOEPINTn for this interrupt */ + + efm32_putreg(USB_DOEPINT_XFRC, EFM32_USB_DOEPINT(epno)); + + /* Handle the RX transfer data ready event */ + + efm32_epout(priv, epno); + } + + /* Endpoint disabled interrupt (ignored because this interrrupt is + * used in polled mode by the endpoint disable logic). + */ +#if 1 + /* REVISIT: */ + if ((doepint & USB_DOEPINT_EPDISD) != 0) + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_EPOUT_EPDISD), (uint16_t)doepint); + + /* Clear the bit in DOEPINTn for this interrupt */ + + efm32_putreg(USB_DOEPINT_EPDISD, EFM32_USB_DOEPINT(epno)); + } +#endif + /* Setup Phase Done (control EPs) */ + + if ((doepint & USB_DOEPINT_SETUP) != 0) + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_EPOUT_SETUP), priv->ep0state); + + /* Handle the receipt of the IN SETUP packets now (OUT setup + * packet processing may be delayed until the accompanying + * OUT DATA is received) + */ + + if (priv->ep0state == EP0STATE_SETUP_READY) + { + efm32_ep0out_setup(priv); + } + efm32_putreg(USB_DOEPINT_SETUP, EFM32_USB_DOEPINT(epno)); + } + } + + epno++; + daint >>= 1; + } +} + +/******************************************************************************* + * Name: efm32_epin_runtestmode + * + * Description: + * Execute the test mode setup by the SET FEATURE request + * + *******************************************************************************/ + +static inline void efm32_epin_runtestmode(FAR struct efm32_usbdev_s *priv) +{ + uint32_t regval = efm32_getreg(EFM32_USB_DCTL); + regval &= _USB_DCTL_TCTL_MASK; + regval |= (uint32_t)priv->testmode << _USB_DCTL_TCTL_SHIFT; + efm32_putreg(regval , EFM32_USB_DCTL); + + priv->dotest = 0; + priv->testmode = USB_TESTMODE_DISABLED; +} + +/******************************************************************************* + * Name: efm32_epin + * + * Description: + * This is part of the IN endpoint interrupt processing. This function + * handles the IN event for a single endpoint. + * + *******************************************************************************/ + +static inline void efm32_epin(FAR struct efm32_usbdev_s *priv, uint8_t epno) +{ + FAR struct efm32_ep_s *privep = &priv->epin[epno]; + + /* Endpoint 0 is a special case. */ + + if (epno == 0) + { + /* In the EP0STATE_DATA_IN state, we are sending data from request + * buffer. In that case, we must continue the request processing. + */ + + if (priv->ep0state == EP0STATE_DATA_IN) + { + /* Continue processing data from the EP0 OUT request queue */ + + efm32_epin_request(priv, privep); + + /* If we are not actively processing an OUT request, then we + * need to setup to receive the next control request. + */ + + if (!privep->active) + { + efm32_ep0out_ctrlsetup(priv); + priv->ep0state = EP0STATE_IDLE; + } + } + + /* Test mode is another special case */ + + if (priv->dotest) + { + efm32_epin_runtestmode(priv); + } + } + + /* For other endpoints, the only possibility is that we are continuing + * or finishing an IN request. + */ + + else if (priv->devstate == DEVSTATE_CONFIGURED) + { + /* Continue processing data from the endpoint write request queue */ + + efm32_epin_request(priv, privep); + } +} + +/**************************************************************************** + * Name: efm32_epin_txfifoempty + * + * Description: + * TxFIFO empty interrupt handling + * + ****************************************************************************/ + +static inline void efm32_epin_txfifoempty(FAR struct efm32_usbdev_s *priv, int epno) +{ + FAR struct efm32_ep_s *privep = &priv->epin[epno]; + + /* Continue processing the write request queue. This may mean sending + * more data from the exisiting request or terminating the current requests + * and (perhaps) starting the IN transfer from the next write request. + */ + + efm32_epin_request(priv, privep); +} + +/******************************************************************************* + * Name: efm32_epin_interrupt + * + * Description: + * USB IN endpoint interrupt handler. The core generates this interrupt when + * an interrupt is pending on one of the IN endpoints of the core. The driver + * must read the OTGFS DAINT register to determine the exact number of the IN + * endpoint on which the interrupt occurred, and then read the corresponding + * OTGFS DIEPINTx register to determine the exact cause of the interrupt. + * + *******************************************************************************/ + +static inline void efm32_epin_interrupt(FAR struct efm32_usbdev_s *priv) +{ + uint32_t diepint; + uint32_t daint; + uint32_t mask; + uint32_t empty; + int epno; + + /* Get the pending, enabled interrupts for the IN endpoint from the endpoint + * interrupt status register. + */ + + daint = efm32_getreg(EFM32_USB_DAINT); + daint &= efm32_getreg(EFM32_USB_DAINTMSK); + daint &= _USB_DAINT_IEP_MASK; + + if (daint == 0) + { + /* We got an interrupt, but there is no unmasked endpoint that caused + * it ?! When this happens, the interrupt flag never gets cleared and + * we are stuck in infinite interrupt loop. + * + * This shouldn't happen if we are diligent about handling timing + * issues when masking endpoint interrupts. However, this workaround + * avoids infinite loop and allows operation to continue normally. It + * works by clearing each endpoint flags, masked or not. + */ + + daint = efm32_getreg(EFM32_USB_DAINT); + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_EPINUNEXPECTED), + (uint16_t)daint); + + daint &= _USB_DAINT_IEP_MASK; + epno = 0; + + while (daint) + { + if ((daint & 1) != 0) + { + ulldbg("DIEPINT(%d) = %08x\n", + epno, efm32_getreg(EFM32_USB_DIEPINT(epno))); + efm32_putreg(0xFF, EFM32_USB_DIEPINT(epno)); + } + + epno++; + daint >>= 1; + } + + return; + } + + /* Process each pending IN endpoint interrupt */ + + epno = 0; + while (daint) + { + /* Is an IN interrupt pending for this endpoint? */ + + if ((daint & 1) != 0) + { + /* Get IN interrupt mask register. Bits 0-6 correspond to enabled + * interrupts as will be found in the DIEPINT interrupt status + * register. + */ + + mask = efm32_getreg(EFM32_USB_DIEPMSK); + + /* Check if the TxFIFO not empty interrupt is enabled for this + * endpoint in the DIEPMSK register. Bits n corresponds to + * endpoint n in the register. That condition corresponds to + * bit 7 of the DIEPINT interrupt status register. There is + * no TXFE bit in the mask register, so we fake one here. + */ + + empty = efm32_getreg(EFM32_USB_DIEPEMPMSK); + if ((empty & USB_DIEPEMPMSK(epno)) != 0) + { + mask |= USB_DIEPINT_TXFE; + } + + /* Now, read the interrupt status and mask out all disabled + * interrupts. + */ + + diepint = efm32_getreg(EFM32_USB_DIEPINT(epno)) & mask; + + /* Decode and process the enabled, pending interrupts */ + /* Transfer completed interrupt */ + + if ((diepint & USB_DIEPINT_XFRC) != 0) + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_EPIN_XFRC), + (uint16_t)diepint); + + /* It is possible that logic may be waiting for a the + * TxFIFO to become empty. We disable the TxFIFO empty + * interrupt here; it will be re-enabled if there is still + * insufficient space in the TxFIFO. + */ + + empty &= ~USB_DIEPEMPMSK(epno); + efm32_putreg(empty, EFM32_USB_DIEPEMPMSK); + efm32_putreg(USB_DIEPINT_XFRC, EFM32_USB_DIEPINT(epno)); + + /* IN transfer complete */ + + efm32_epin(priv, epno); + } + + /* Timeout condition */ + + if ((diepint & USB_DIEPINT_TOC) != 0) + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_EPIN_TOC), (uint16_t)diepint); + efm32_putreg(USB_DIEPINT_TOC, EFM32_USB_DIEPINT(epno)); + } + + /* IN token received when TxFIFO is empty. Applies to non-periodic IN + * endpoints only. This interrupt indicates that an IN token was received + * when the associated TxFIFO (periodic/non-periodic) was empty. This + * interrupt is asserted on the endpoint for which the IN token was + * received. + */ + + if ((diepint & USB_DIEPINT_ITTXFE) != 0) + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_EPIN_ITTXFE), (uint16_t)diepint); + efm32_epin_request(priv, &priv->epin[epno]); + efm32_putreg(USB_DIEPINT_ITTXFE, EFM32_USB_DIEPINT(epno)); + } + + /* IN endpoint NAK effective (ignored as this used only in polled + * mode) + */ +#if 0 + if ((diepint & USB_DIEPINT_INEPNE) != 0) + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_EPIN_INEPNE), (uint16_t)diepint); + efm32_putreg(USB_DIEPINT_INEPNE, EFM32_USB_DIEPINT(epno)); + } +#endif + /* Endpoint disabled interrupt (ignored as this used only in polled + * mode) + */ +#if 0 + if ((diepint & USB_DIEPINT_EPDISD) != 0) + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_EPIN_EPDISD), (uint16_t)diepint); + efm32_putreg(USB_DIEPINT_EPDISD, EFM32_USB_DIEPINT(epno)); + } +#endif + /* Transmit FIFO empty */ + + if ((diepint & USB_DIEPINT_TXFE) != 0) + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_EPIN_TXFE), (uint16_t)diepint); + + /* If we were waiting for TxFIFO to become empty, the we might have both + * XFRC and TXFE interrupts pending. Since we do the same thing for both + * cases, ignore the TXFE if we have already processed the XFRC. + */ + + if ((diepint & USB_DIEPINT_XFRC) == 0) + { + /* Mask further FIFO empty interrupts. This will be re-enabled + * whenever we need to wait for a FIFO event. + */ + + empty &= ~USB_DIEPEMPMSK(epno); + efm32_putreg(empty, EFM32_USB_DIEPEMPMSK); + + /* Handle TxFIFO empty */ + + efm32_epin_txfifoempty(priv, epno); + } + + /* Clear the pending TxFIFO empty interrupt */ + + efm32_putreg(USB_DIEPINT_TXFE, EFM32_USB_DIEPINT(epno)); + } + } + + epno++; + daint >>= 1; + } +} + +/******************************************************************************* + * Name: efm32_resumeinterrupt + * + * Description: + * Resume/remote wakeup detected interrupt + * + *******************************************************************************/ + +static inline void efm32_resumeinterrupt(FAR struct efm32_usbdev_s *priv) +{ + uint32_t regval; + + /* Restart the PHY clock and un-gate USB core clock (HCLK) */ + +#ifdef CONFIG_USBDEV_LOWPOWER + regval = efm32_getreg(EFM32_USB_PCGCCTL); + regval &= ~(OTGFS_PCGCCTL_STPPCLK | OTGFS_PCGCCTL_GATEHCLK); + efm32_putreg(regval, EFM32_USB_PCGCCTL); +#endif + + /* Clear remote wake-up signaling */ + + regval = efm32_getreg(EFM32_USB_DCTL); + regval &= ~USB_DCTL_RWUSIG; + efm32_putreg(regval, EFM32_USB_DCTL); + + /* Restore full power -- whatever that means for this particular board */ + + efm32_usbsuspend((struct usbdev_s *)priv, true); + + /* Notify the class driver of the resume event */ + + if (priv->driver) + { + CLASS_RESUME(priv->driver, &priv->usbdev); + } +} + +/******************************************************************************* + * Name: efm32_suspendinterrupt + * + * Description: + * USB suspend interrupt + * + *******************************************************************************/ + +static inline void efm32_suspendinterrupt(FAR struct efm32_usbdev_s *priv) +{ +#ifdef CONFIG_USBDEV_LOWPOWER + uint32_t regval; +#endif + + /* Notify the class driver of the suspend event */ + + if (priv->driver) + { + CLASS_SUSPEND(priv->driver, &priv->usbdev); + } + +#ifdef CONFIG_USBDEV_LOWPOWER + /* USB_DSTS_SUSPSTS is set as long as the suspend condition is detected + * on USB. Check if we are still have the suspend condition, that we are + * connected to the host, and that we have been configured. + */ + + regval = efm32_getreg(EFM32_USB_DSTS); + + if ((regval & USB_DSTS_SUSPSTS) != 0 && devstate == DEVSTATE_CONFIGURED) + { + /* Switch off OTG FS clocking. Setting OTGFS_PCGCCTL_STPPCLK stops the + * PHY clock. + */ + + regval = efm32_getreg(EFM32_USB_PCGCCTL); + regval |= OTGFS_PCGCCTL_STPPCLK; + efm32_putreg(regval, EFM32_USB_PCGCCTL); + + /* Setting OTGFS_PCGCCTL_GATEHCLK gate HCLK to modules other than + * the AHB Slave and Master and wakeup logic. + */ + + regval |= OTGFS_PCGCCTL_GATEHCLK; + efm32_putreg(regval, EFM32_USB_PCGCCTL); + } +#endif + + /* Let the board-specific logic know that we have entered the suspend + * state + */ + + efm32_usbsuspend((FAR struct usbdev_s *)priv, false); +} + +/******************************************************************************* + * Name: efm32_rxinterrupt + * + * Description: + * RxFIFO non-empty interrupt. This interrupt indicates that there is at + * least one packet pending to be read from the RxFIFO. + * + *******************************************************************************/ + +static inline void efm32_rxinterrupt(FAR struct efm32_usbdev_s *priv) +{ + FAR struct efm32_ep_s *privep; + uint32_t regval; + int bcnt; + int epphy; + + /* Disable the Rx status queue level interrupt */ + + regval = efm32_getreg(EFM32_USB_GINTMSK); + regval &= ~USB_GINT_RXFLVL; + efm32_putreg(regval, EFM32_USB_GINTMSK); + + /* Get the status from the top of the FIFO */ + + regval = efm32_getreg(EFM32_USB_GRXSTSP); + + /* Decode status fields */ + + epphy = (regval & _USB_GRXSTSD_EPNUM_MASK) >> _USB_GRXSTSD_EPNUM_SHIFT; + privep = &priv->epout[epphy]; + + /* Handle the RX event according to the packet status field */ + + switch (regval & _USB_GRXSTSD_PKTSTS_MASK) + { + /* Global OUT NAK. This indicate that the global OUT NAK bit has taken + * effect. + * + * PKTSTS = Global OUT NAK, BCNT = 0, EPNUM = Don't Care, DPID = Don't + * Care. + */ + + case USB_GRXSTSD_PKTSTS_OUTNAK: + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_OUTNAK), 0); + } + break; + + /* OUT data packet received. + * + * PKTSTS = DataOUT, BCNT = size of the received data OUT packet, + * EPNUM = EPNUM on which the packet was received, DPID = Actual Data PID. + */ + + case USB_GRXSTSD_PKTSTS_OUTRECVD: + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_OUTRECVD), epphy); + bcnt = (regval & _USB_GRXSTSD_BCNT_MASK) >> _USB_GRXSTSD_BCNT_SHIFT; + if (bcnt > 0) + { + efm32_epout_receive(privep, bcnt); + } + } + break; + + /* OUT transfer completed. This indicates that an OUT data transfer for + * the specified OUT endpoint has completed. After this entry is popped + * from the receive FIFO, the core asserts a Transfer Completed interrupt + * on the specified OUT endpoint. + * + * PKTSTS = Data OUT Transfer Done, BCNT = 0, EPNUM = OUT EP Num on + * which the data transfer is complete, DPID = Don't Care. + */ + + case USB_GRXSTSD_PKTSTS_OUTDONE: + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_OUTDONE), epphy); + } + break; + + /* SETUP transaction completed. This indicates that the Setup stage for + * the specified endpoint has completed and the Data stage has started. + * After this entry is popped from the receive FIFO, the core asserts a + * Setup interrupt on the specified control OUT endpoint (triggers an + * interrupt). + * + * PKTSTS = Setup Stage Done, BCNT = 0, EPNUM = Control EP Num, + * DPID = Don't Care. + */ + + case USB_GRXSTSD_PKTSTS_SETUPDONE: + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_SETUPDONE), epphy); + } + break; + + /* SETUP data packet received. This indicates that a SETUP packet for the + * specified endpoint is now available for reading from the receive FIFO. + * + * PKTSTS = SETUP, BCNT = 8, EPNUM = Control EP Num, DPID = D0. + */ + + case USB_GRXSTSD_PKTSTS_SETUPRECVD: + { + uint16_t datlen; + + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_SETUPRECVD), epphy); + + /* Read EP0 setup data. NOTE: If multiple SETUP packets are received, + * the last one overwrites the previous setup packets and only that + * last SETUP packet will be processed. + */ + + efm32_rxfifo_read(&priv->epout[EP0], (FAR uint8_t*)&priv->ctrlreq, + USB_SIZEOF_CTRLREQ); + + /* Was this an IN or an OUT SETUP packet. If it is an OUT SETUP, + * then we need to wait for the completion of the data phase to + * process the setup command. If it is an IN SETUP packet, then + * we must processing the command BEFORE we enter the DATA phase. + * + * If the data associated with the OUT SETUP packet is zero length, + * then, of course, we don't need to wait. + */ + + datlen = GETUINT16(priv->ctrlreq.len); + if (USB_REQ_ISOUT(priv->ctrlreq.type) && datlen > 0) + { + /* Clear NAKSTS so that we can receive the data */ + + regval = efm32_getreg(EFM32_USB_DOEP0CTL); + regval |= USB_DOEP0CTL_CNAK; + efm32_putreg(regval, EFM32_USB_DOEP0CTL); + + /* Wait for the data phase. */ + + priv->ep0state = EP0STATE_SETUP_OUT; + } + else + { + /* We can process the setup data as soon as SETUP done word is + * popped of the RxFIFO. + */ + + priv->ep0state = EP0STATE_SETUP_READY; + } + } + break; + + default: + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_INVALIDPARMS), + (regval & _USB_GRXSTSD_PKTSTS_MASK) >> _USB_GRXSTSD_PKTSTS_SHIFT); + } + break; + } + + /* Enable the Rx Status Queue Level interrupt */ + + regval = efm32_getreg(EFM32_USB_GINTMSK); + regval |= USB_GINT_RXFLVL; + efm32_putreg(regval, EFM32_USB_GINTMSK); +} + +/******************************************************************************* + * Name: efm32_enuminterrupt + * + * Description: + * Enumeration done interrupt + * + *******************************************************************************/ + +static inline void efm32_enuminterrupt(FAR struct efm32_usbdev_s *priv) +{ + uint32_t regval; + + /* Activate EP0 */ + + efm32_ep0in_activate(); + + /* Set USB turn-around time for the full speed device with internal PHY interface. */ + + regval = efm32_getreg(EFM32_USB_GUSBCFG); + regval &= ~_USB_GUSBCFG_TRDT_MASK; + regval |= USB_GUSBCFG_TRDT(5); + efm32_putreg(regval, EFM32_USB_GUSBCFG); +} + +/******************************************************************************* + * Name: efm32_isocininterrupt + * + * Description: + * Incomplete isochronous IN transfer interrupt. Assertion of the incomplete + * isochronous IN transfer interrupt indicates an incomplete isochronous IN + * transfer on at least one of the isochronous IN endpoints. + * + *******************************************************************************/ + +#ifdef CONFIG_USBDEV_ISOCHRONOUS +static inline void efm32_isocininterrupt(FAR struct efm32_usbdev_s *priv) +{ + int i; + + /* The application must read the endpoint control register for all isochronous + * IN endpoints to detect endpoints with incomplete IN data transfers. + */ + + for (i = 0; i < EFM32_NENDPOINTS; i++) + { + /* Is this an isochronous IN endpoint? */ + + privep = &priv->epin[i]; + if (privep->eptype != USB_EP_ATTR_XFER_ISOC) + { + /* No... keep looking */ + + continue; + } + + /* Is there an active read request on the isochronous OUT endpoint? */ + + if (!privep->active) + { + /* No.. the endpoint is not actively transmitting data */ + + continue; + } + + /* Check if this is the endpoint that had the incomplete transfer */ + + regaddr = EFM32_USB_DIEPCTL(privep->epphy); + doepctl = efm32_getreg(regaddr); + dsts = efm32_getreg(EFM32_USB_DSTS); + + /* EONUM = 0:even frame, 1:odd frame + * SOFFN = Frame number of the received SOF + */ + + eonum = ((doepctl & USB_DIEPCTL_EONUM) != 0); + soffn = ((dsts & USB_DSTS_SOFFN0) != 0); + + if (eonum != soffn) + { + /* Not this endpoint */ + + continue; + } + + /* For isochronous IN endpoints with incomplete transfers, + * the application must discard the data in the memory and + * disable the endpoint. + */ + + efm32_req_complete(privep, -EIO); +#warning "Will clear USB_DIEPCTL_USBAEP too" + efm32_epin_disable(privep); + break; + } +} +#endif + +/******************************************************************************* + * Name: efm32_isocoutinterrupt + * + * Description: + * Incomplete periodic transfer interrupt + * + *******************************************************************************/ + +#ifdef CONFIG_USBDEV_ISOCHRONOUS +static inline void efm32_isocoutinterrupt(FAR struct efm32_usbdev_s *priv) +{ + FAR struct efm32_ep_s *privep; + FAR struct efm32_req_s *privreq; + uint32_t regaddr; + uint32_t doepctl; + uint32_t dsts; + bool eonum; + bool soffn; + + /* When it receives an IISOOXFR interrupt, the application must read the + * control registers of all isochronous OUT endpoints to determine which + * endpoints had an incomplete transfer in the current microframe. An + * endpoint transfer is incomplete if both the following conditions are true: + * + * DOEPCTLx:EONUM = DSTS:SOFFN[0], and + * DOEPCTLx:EPENA = 1 + */ + + for (i = 0; i < EFM32_NENDPOINTS; i++) + { + /* Is this an isochronous OUT endpoint? */ + + privep = &priv->epout[i]; + if (privep->eptype != USB_EP_ATTR_XFER_ISOC) + { + /* No... keep looking */ + + continue; + } + + /* Is there an active read request on the isochronous OUT endpoint? */ + + if (!privep->active) + { + /* No.. the endpoint is not actively transmitting data */ + + continue; + } + + /* Check if this is the endpoint that had the incomplete transfer */ + + regaddr = EFM32_USB_DOEPCTL(privep->epphy); + doepctl = efm32_getreg(regaddr); + dsts = efm32_getreg(EFM32_USB_DSTS); + + /* EONUM = 0:even frame, 1:odd frame + * SOFFN = Frame number of the received SOF + */ + + eonum = ((doepctl & USB_DOEPCTL_EONUM) != 0); + soffn = ((dsts & USB_DSTS_SOFFN0) != 0); + + if (eonum != soffn) + { + /* Not this endpoint */ + + continue; + } + + /* For isochronous OUT endpoints with incomplete transfers, + * the application must discard the data in the memory and + * disable the endpoint. + */ + + efm32_req_complete(privep, -EIO); +#warning "Will clear USB_DOEPCTL_USBAEP too" + efm32_epout_disable(privep); + break; + } +} +#endif + +/******************************************************************************* + * Name: efm32_sessioninterrupt + * + * Description: + * Session request/new session detected interrupt + * + *******************************************************************************/ + +#ifdef CONFIG_USBDEV_VBUSSENSING +static inline void efm32_sessioninterrupt(FAR struct efm32_usbdev_s *priv) +{ +#warning "Missing logic" +} +#endif + +/******************************************************************************* + * Name: efm32_otginterrupt + * + * Description: + * OTG interrupt + * + *******************************************************************************/ + +#ifdef CONFIG_USBDEV_VBUSSENSING +static inline void efm32_otginterrupt(FAR struct efm32_usbdev_s *priv) +{ + uint32_t regval; + + /* Check for session end detected */ + + regval = efm32_getreg(EFM32_USB_GOTGINT); + if ((regval & OTGFS_GOTGINT_SEDET) != 0) + { +#warning "Missing logic" + } + + /* Clear OTG interrupt */ + + efm32_putreg(retval, EFM32_USB_GOTGINT); +} +#endif + +/******************************************************************************* + * Name: efm32_usbinterrupt + * + * Description: + * USB interrupt handler + * + *******************************************************************************/ + +static int efm32_usbinterrupt(int irq, FAR void *context) +{ + /* At present, there is only a single OTG FS device support. Hence it is + * pre-allocated as g_otgfsdev. However, in most code, the private data + * structure will be referenced using the 'priv' pointer (rather than the + * global data) in order to simplify any future support for multiple devices. + */ + + FAR struct efm32_usbdev_s *priv = &g_otgfsdev; + uint32_t regval; + + usbtrace(TRACE_INTENTRY(EFM32_TRACEINTID_USB), 0); + + /* Assure that we are in device mode */ + + DEBUGASSERT((efm32_getreg(EFM32_USB_GINTSTS) & USB_GINTSTS_CMOD) == USB_GINTSTS_DEVMODE); + + /* Get the state of all enabled interrupts. We will do this repeatedly + * some interrupts (like RXFLVL) will generate additional interrupting + * events. + */ + + for (;;) + { + /* Get the set of pending, un-masked interrupts */ + + regval = efm32_getreg(EFM32_USB_GINTSTS); + regval &= efm32_getreg(EFM32_USB_GINTMSK); + + /* Break out of the loop when there are no further pending (and + * unmasked) interrupts to be processes. + */ + + if (regval == 0) + { + break; + } + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_INTPENDING), (uint16_t)regval); + + /* OUT endpoint interrupt. The core sets this bit to indicate that an + * interrupt is pending on one of the OUT endpoints of the core. + */ + + if ((regval & USB_GINT_OEP) != 0) + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_EPOUT), (uint16_t)regval); + efm32_epout_interrupt(priv); + efm32_putreg(USB_GINT_OEP, EFM32_USB_GINTSTS); + } + + /* IN endpoint interrupt. The core sets this bit to indicate that + * an interrupt is pending on one of the IN endpoints of the core. + */ + + if ((regval & USB_GINT_IEP) != 0) + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_EPIN), (uint16_t)regval); + efm32_epin_interrupt(priv); + efm32_putreg(USB_GINT_IEP, EFM32_USB_GINTSTS); + } + + /* Host/device mode mismatch error interrupt */ + +#ifdef CONFIG_DEBUG_USB + if ((regval & USB_GINT_MMIS) != 0) + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_MISMATCH), (uint16_t)regval); + efm32_putreg(USB_GINT_MMIS, EFM32_USB_GINTSTS); + } +#endif + + /* Resume/remote wakeup detected interrupt */ + + if ((regval & USB_GINT_WKUP) != 0) + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_WAKEUP), (uint16_t)regval); + efm32_resumeinterrupt(priv); + efm32_putreg(USB_GINT_WKUP, EFM32_USB_GINTSTS); + } + + /* USB suspend interrupt */ + + if ((regval & USB_GINT_USBSUSP) != 0) + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_SUSPEND), (uint16_t)regval); + efm32_suspendinterrupt(priv); + efm32_putreg(USB_GINT_USBSUSP, EFM32_USB_GINTSTS); + } + + /* Start of frame interrupt */ + +#ifdef CONFIG_USBDEV_SOFINTERRUPT + if ((regval & USB_GINT_SOF) != 0) + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_SOF), (uint16_t)regval); + efm32_putreg(USB_GINT_SOF, EFM32_USB_GINTSTS); + } +#endif + + /* RxFIFO non-empty interrupt. Indicates that there is at least one + * packet pending to be read from the RxFIFO. + */ + + if ((regval & USB_GINT_RXFLVL) != 0) + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_RXFIFO), (uint16_t)regval); + efm32_rxinterrupt(priv); + efm32_putreg(USB_GINT_RXFLVL, EFM32_USB_GINTSTS); + } + + /* USB reset interrupt */ + + if ((regval & USB_GINT_USBRST) != 0) + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_DEVRESET), (uint16_t)regval); + + /* Perform the device reset */ + + efm32_usbreset(priv); + usbtrace(TRACE_INTEXIT(EFM32_TRACEINTID_USB), 0); + efm32_putreg(USB_GINT_USBRST, EFM32_USB_GINTSTS); + return OK; + } + + /* Enumeration done interrupt */ + + if ((regval & USB_GINT_ENUMDNE) != 0) + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_ENUMDNE), (uint16_t)regval); + efm32_enuminterrupt(priv); + efm32_putreg(USB_GINT_ENUMDNE, EFM32_USB_GINTSTS); + } + + /* Incomplete isochronous IN transfer interrupt. When the core finds + * non-empty any of the isochronous IN endpoint FIFOs scheduled for + * the current frame non-empty, the core generates an IISOIXFR + * interrupt. + */ + +#ifdef CONFIG_USBDEV_ISOCHRONOUS + if ((regval & USB_GINT_IISOIXFR) != 0) + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_IISOIXFR), (uint16_t)regval); + efm32_isocininterrupt(priv); + efm32_putreg(USB_GINT_IISOIXFR, EFM32_USB_GINTSTS); + } + + /* Incomplete isochronous OUT transfer. For isochronous OUT + * endpoints, the XFRC interrupt may not always be asserted. If the + * core drops isochronous OUT data packets, the application could fail + * to detect the XFRC interrupt. The incomplete Isochronous OUT data + * interrupt indicates that an XFRC interrupt was not asserted on at + * least one of the isochronous OUT endpoints. At this point, the + * endpoint with the incomplete transfer remains enabled, but no active + * transfers remain in progress on this endpoint on the USB. + */ + + if ((regval & USB_GINT_IISOOXFR) != 0) + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_IISOOXFR), (uint16_t)regval); + efm32_isocoutinterrupt(priv); + efm32_putreg(USB_GINT_IISOOXFR, EFM32_USB_GINTSTS); + } +#endif + + /* Session request/new session detected interrupt */ + +#ifdef CONFIG_USBDEV_VBUSSENSING + if ((regval & USB_GINT_SRQ) != 0) + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_SRQ), (uint16_t)regval); + efm32_sessioninterrupt(priv); + efm32_putreg(USB_GINT_SRQ, EFM32_USB_GINTSTS); + } + + /* OTG interrupt */ + + if ((regval & USB_GINT_OTG) != 0) + { + usbtrace(TRACE_INTDECODE(EFM32_TRACEINTID_OTG), (uint16_t)regval); + efm32_otginterrupt(priv); + efm32_putreg(USB_GINT_OTG, EFM32_USB_GINTSTS); + } +#endif + } + + usbtrace(TRACE_INTEXIT(EFM32_TRACEINTID_USB), 0); + return OK; +} + +/******************************************************************************* + * Endpoint operations + *******************************************************************************/ + +/******************************************************************************* + * Name: efm32_enablegonak + * + * Description: + * Enable global OUT NAK mode + * + *******************************************************************************/ + +static void efm32_enablegonak(FAR struct efm32_ep_s *privep) +{ + uint32_t regval; + + /* First, make sure that there is no GNOAKEFF interrupt pending. */ + +#if 0 + efm32_putreg(USB_GINT_GONAKEFF, EFM32_USB_GINTSTS); +#endif + + /* Enable Global OUT NAK mode in the core. */ + + regval = efm32_getreg(EFM32_USB_DCTL); + regval |= USB_DCTL_SGONAK; + efm32_putreg(regval, EFM32_USB_DCTL); + +#if 0 + /* Wait for the GONAKEFF interrupt that indicates that the OUT NAK + * mode is in effect. When the interrupt handler pops the OUTNAK word + * from the RxFIFO, the core sets the GONAKEFF interrupt. + */ + + while ((efm32_getreg(EFM32_USB_GINTSTS) & USB_GINT_GONAKEFF) == 0); + efm32_putreg(USB_GINT_GONAKEFF, EFM32_USB_GINTSTS); + +#else + /* Since we are in the interrupt handler, we cannot wait inline for the + * GONAKEFF because it cannot occur until service th RXFLVL global interrupt + * and pop the OUTNAK word from the RxFIFO. + * + * Perhaps it is sufficient to wait for Global OUT NAK status to be reported + * in OTGFS DCTL register? + */ + + while ((efm32_getreg(EFM32_USB_DCTL) & USB_DCTL_GONSTS) == 0); +#endif +} + +/******************************************************************************* + * Name: efm32_disablegonak + * + * Description: + * Disable global OUT NAK mode + * + *******************************************************************************/ + +static void efm32_disablegonak(FAR struct efm32_ep_s *privep) +{ + uint32_t regval; + + /* Set the "Clear the Global OUT NAK bit" to disable global OUT NAK mode */ + + regval = efm32_getreg(EFM32_USB_DCTL); + regval |= USB_DCTL_CGONAK; + efm32_putreg(regval, EFM32_USB_DCTL); +} + +/******************************************************************************* + * Name: efm32_epout_configure + * + * Description: + * Configure an OUT endpoint, making it usable + * + * Input Parameters: + * privep - a pointer to an internal endpoint structure + * eptype - The type of the endpoint + * maxpacket - The max packet size of the endpoint + * + *******************************************************************************/ + +static int efm32_epout_configure(FAR struct efm32_ep_s *privep, uint8_t eptype, + uint16_t maxpacket) +{ + uint32_t mpsiz; + uint32_t regaddr; + uint32_t regval; + + usbtrace(TRACE_EPCONFIGURE, privep->epphy); + + /* For EP0, the packet size is encoded */ + + if (privep->epphy == EP0) + { + DEBUGASSERT(eptype == USB_EP_ATTR_XFER_CONTROL); + + /* Map the size in bytes to the encoded value in the register */ + + switch (maxpacket) + { + case 8: + mpsiz = USB_DOEP0CTL_MPS_8; + break; + + case 16: + mpsiz = USB_DOEP0CTL_MPS_16; + break; + + case 32: + mpsiz = USB_DOEP0CTL_MPS_32; + break; + + case 64: + mpsiz = USB_DOEP0CTL_MPS_64; + break; + + default: + udbg("Unsupported maxpacket: %d\n", maxpacket); + return -EINVAL; + } + } + + /* For other endpoints, the packet size is in bytes */ + + else + { + mpsiz = (maxpacket << _USB_DOEPCTL_MPS_SHIFT); + } + + /* If the endpoint is already active don't change the endpoint control + * register. + */ + + regaddr = EFM32_USB_DOEPCTL(privep->epphy); + regval = efm32_getreg(regaddr); + if ((regval & USB_DOEPCTL_USBAEP) == 0) + { + if (regval & USB_DOEPCTL_NAKSTS) + { + regval |= USB_DOEPCTL_CNAK; + } + + regval &= ~(_USB_DOEPCTL_MPS_MASK | _USB_DOEPCTL_EPTYP_MASK); + regval |= mpsiz; + regval |= (eptype << _USB_DOEPCTL_EPTYP_SHIFT); + regval |= (USB_DOEPCTL_SD0PID | USB_DOEPCTL_USBAEP); + efm32_putreg(regval, regaddr); + + /* Save the endpoint configuration */ + + privep->ep.maxpacket = maxpacket; + privep->eptype = eptype; + privep->stalled = false; + } + + /* Enable the interrupt for this endpoint */ + + regval = efm32_getreg(EFM32_USB_DAINTMSK); + regval |= USB_DAINT_OEP(privep->epphy); + efm32_putreg(regval, EFM32_USB_DAINTMSK); + return OK; +} + +/******************************************************************************* + * Name: efm32_epin_configure + * + * Description: + * Configure an IN endpoint, making it usable + * + * Input Parameters: + * privep - a pointer to an internal endpoint structure + * eptype - The type of the endpoint + * maxpacket - The max packet size of the endpoint + * + *******************************************************************************/ + +static int efm32_epin_configure(FAR struct efm32_ep_s *privep, uint8_t eptype, + uint16_t maxpacket) +{ + uint32_t mpsiz; + uint32_t regaddr; + uint32_t regval; + + usbtrace(TRACE_EPCONFIGURE, privep->epphy); + + /* For EP0, the packet size is encoded */ + + if (privep->epphy == EP0) + { + DEBUGASSERT(eptype == USB_EP_ATTR_XFER_CONTROL); + + /* Map the size in bytes to the encoded value in the register */ + + switch (maxpacket) + { + case 8: + mpsiz = _USB_DIEP0CTL_MPS_8; + break; + + case 16: + mpsiz = _USB_DIEP0CTL_MPS_16; + break; + + case 32: + mpsiz = _USB_DIEP0CTL_MPS_32; + break; + + case 64: + mpsiz = _USB_DIEP0CTL_MPS_64; + break; + + default: + udbg("Unsupported maxpacket: %d\n", maxpacket); + return -EINVAL; + } + } + + /* For other endpoints, the packet size is in bytes */ + + else + { + mpsiz = (maxpacket << _USB_DIEPCTL_MPS_SHIFT); + } + + + /* If the endpoint is already active don't change the endpoint control + * register. + */ + + regaddr = EFM32_USB_DIEPCTL(privep->epphy); + regval = efm32_getreg(regaddr); + if ((regval & USB_DIEPCTL_USBAEP) == 0) + { + if (regval & USB_DIEPCTL_NAKSTS) + { + regval |= USB_DIEPCTL_CNAK; + } + + regval &= ~(_USB_DIEPCTL_MPS_MASK | _USB_DIEPCTL_EPTYP_MASK | _USB_DIEPCTL_TXFNUM_MASK); + regval |= mpsiz; + regval |= (eptype << _USB_DIEPCTL_EPTYP_SHIFT); + regval |= (eptype << _USB_DIEPCTL_TXFNUM_SHIFT); + regval |= (USB_DIEPCTL_SD0PID | USB_DIEPCTL_USBAEP); + efm32_putreg(regval, regaddr); + + /* Save the endpoint configuration */ + + privep->ep.maxpacket = maxpacket; + privep->eptype = eptype; + privep->stalled = false; + } + + /* Enable the interrupt for this endpoint */ + + regval = efm32_getreg(EFM32_USB_DAINTMSK); + regval |= USB_DAINT_IEP(privep->epphy); + efm32_putreg(regval, EFM32_USB_DAINTMSK); + + return OK; +} + +/******************************************************************************* + * Name: efm32_ep_configure + * + * Description: + * Configure endpoint, making it usable + * + * Input Parameters: + * ep - the struct usbdev_ep_s instance obtained from allocep() + * desc - A struct usb_epdesc_s instance describing the endpoint + * last - true if this this last endpoint to be configured. Some hardware + * needs to take special action when all of the endpoints have been + * configured. + * + *******************************************************************************/ + +static int efm32_ep_configure(FAR struct usbdev_ep_s *ep, + FAR const struct usb_epdesc_s *desc, + bool last) +{ + FAR struct efm32_ep_s *privep = (FAR struct efm32_ep_s *)ep; + uint16_t maxpacket; + uint8_t eptype; + int ret; + + usbtrace(TRACE_EPCONFIGURE, privep->epphy); + DEBUGASSERT(desc->addr == ep->eplog); + + /* Initialize EP capabilities */ + + maxpacket = GETUINT16(desc->mxpacketsize); + eptype = desc->attr & _USB_EP_ATTR_XFERTYPE_MASK; + + /* Setup Endpoint Control Register */ + + if (privep->isin) + { + ret = efm32_epin_configure(privep, eptype, maxpacket); + } + else + { + ret = efm32_epout_configure(privep, eptype, maxpacket); + } + + return ret; +} + +/******************************************************************************* + * Name: efm32_ep0_configure + * + * Description: + * Reset Usb engine + * + *******************************************************************************/ + +static void efm32_ep0_configure(FAR struct efm32_usbdev_s *priv) +{ + /* Enable EP0 IN and OUT */ + + (void)efm32_epin_configure(&priv->epin[EP0], USB_EP_ATTR_XFER_CONTROL, + CONFIG_USBDEV_EP0_MAXSIZE); + (void)efm32_epout_configure(&priv->epout[EP0], USB_EP_ATTR_XFER_CONTROL, + CONFIG_USBDEV_EP0_MAXSIZE); +} + +/******************************************************************************* + * Name: efm32_epout_disable + * + * Description: + * Diable an OUT endpoint will no longer be used + * + *******************************************************************************/ + +static void efm32_epout_disable(FAR struct efm32_ep_s *privep) +{ + uint32_t regaddr; + uint32_t regval; + irqstate_t flags; + + usbtrace(TRACE_EPDISABLE, privep->epphy); + + /* Is this an IN or an OUT endpoint */ + + /* Before disabling any OUT endpoint, the application must enable + * Global OUT NAK mode in the core. + */ + + flags = irqsave(); + efm32_enablegonak(privep); + + /* Disable the required OUT endpoint by setting the EPDIS and SNAK bits + * int DOECPTL register. + */ + + regaddr = EFM32_USB_DOEPCTL(privep->epphy); + regval = efm32_getreg(regaddr); + regval &= ~USB_DOEPCTL_USBAEP; + regval |= (USB_DOEPCTL_EPDIS | USB_DOEPCTL_SNAK); + efm32_putreg(regval, regaddr); + + /* Wait for the EPDISD interrupt which indicates that the OUT + * endpoint is completely disabled. + */ + +#if 0 /* Doesn't happen */ + regaddr = EFM32_USB_DOEPINT(privep->epphy); + while ((efm32_getreg(regaddr) & USB_DOEPINT_EPDISD) == 0); +#else + /* REVISIT: */ + up_udelay(10); +#endif + + /* Clear the EPDISD interrupt indication */ + + efm32_putreg(USB_DOEPINT_EPDISD, EFM32_USB_DOEPINT(privep->epphy)); + + /* Then disble the Global OUT NAK mode to continue receiving data + * from other non-disabled OUT endpoints. + */ + + efm32_disablegonak(privep); + + /* Disable endpoint interrupts */ + + regval = efm32_getreg(EFM32_USB_DAINTMSK); + regval &= ~USB_DAINT_OEP(privep->epphy); + efm32_putreg(regval, EFM32_USB_DAINTMSK); + + /* Cancel any queued read requests */ + + efm32_req_cancel(privep, -ESHUTDOWN); + + irqrestore(flags); +} + +/******************************************************************************* + * Name: efm32_epin_disable + * + * Description: + * Disable an IN endpoint when it will no longer be used + * + *******************************************************************************/ + +static void efm32_epin_disable(FAR struct efm32_ep_s *privep) +{ + uint32_t regaddr; + uint32_t regval; + irqstate_t flags; + + usbtrace(TRACE_EPDISABLE, privep->epphy); + + /* After USB reset, the endpoint will already be deactivated by the + * hardware. Trying to disable again will just hang in the wait. + */ + + regaddr = EFM32_USB_DIEPCTL(privep->epphy); + regval = efm32_getreg(regaddr); + if ((regval & USB_DIEPCTL_USBAEP) == 0) + { + return; + } + + /* This INEPNE wait logic is suggested by reference manual, but seems + * to get stuck to infinite loop. + */ + +#if 0 + /* Make sure that there is no pending IPEPNE interrupt (because we are + * to poll this bit below). + */ + + efm32_putreg(USB_DIEPINT_INEPNE, EFM32_USB_DIEPINT(privep->epphy)); + + /* Set the endpoint in NAK mode */ + + regaddr = EFM32_USB_DIEPCTL(privep->epphy); + regval = efm32_getreg(regaddr); + regval &= ~USB_DIEPCTL_USBAEP; + regval |= (USB_DIEPCTL_EPDIS | USB_DIEPCTL_SNAK); + efm32_putreg(regval, regaddr); + + /* Wait for the INEPNE interrupt that indicates that we are now in NAK mode */ + + regaddr = EFM32_USB_DIEPINT(privep->epphy); + while ((efm32_getreg(regaddr) & USB_DIEPINT_INEPNE) == 0); + + /* Clear the INEPNE interrupt indication */ + + efm32_putreg(USB_DIEPINT_INEPNE, regaddr); +#endif + + /* Deactivate and disable the endpoint by setting the EPDIS and SNAK bits + * the DIEPCTLx register. + */ + + flags = irqsave(); + regaddr = EFM32_USB_DIEPCTL(privep->epphy); + regval = efm32_getreg(regaddr); + regval &= ~USB_DIEPCTL_USBAEP; + regval |= (USB_DIEPCTL_EPDIS | USB_DIEPCTL_SNAK); + efm32_putreg(regval, regaddr); + + /* Wait for the EPDISD interrupt which indicates that the IN + * endpoint is completely disabled. + */ + + regaddr = EFM32_USB_DIEPINT(privep->epphy); + while ((efm32_getreg(regaddr) & USB_DIEPINT_EPDISD) == 0); + + /* Clear the EPDISD interrupt indication */ + + efm32_putreg(USB_DIEPINT_EPDISD, efm32_getreg(regaddr)); + + /* Flush any data remaining in the TxFIFO */ + + efm32_txfifo_flush(USB_GRSTCTL_TXFNUM_D(privep->epphy)); + + /* Disable endpoint interrupts */ + + regval = efm32_getreg(EFM32_USB_DAINTMSK); + regval &= ~USB_DAINT_IEP(privep->epphy); + efm32_putreg(regval, EFM32_USB_DAINTMSK); + + /* Cancel any queued write requests */ + + efm32_req_cancel(privep, -ESHUTDOWN); + irqrestore(flags); +} + +/******************************************************************************* + * Name: efm32_ep_disable + * + * Description: + * The endpoint will no longer be used + * + *******************************************************************************/ + +static int efm32_ep_disable(FAR struct usbdev_ep_s *ep) +{ + FAR struct efm32_ep_s *privep = (FAR struct efm32_ep_s *)ep; + +#ifdef CONFIG_DEBUG + if (!ep) + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + + usbtrace(TRACE_EPDISABLE, privep->epphy); + + /* Is this an IN or an OUT endpoint */ + + if (privep->isin) + { + /* Disable the IN endpoint */ + + efm32_epin_disable(privep); + } + else + { + /* Disable the OUT endpoint */ + + efm32_epout_disable(privep); + } + + return OK; +} + +/******************************************************************************* + * Name: efm32_ep_allocreq + * + * Description: + * Allocate an I/O request + * + *******************************************************************************/ + +static FAR struct usbdev_req_s *efm32_ep_allocreq(FAR struct usbdev_ep_s *ep) +{ + FAR struct efm32_req_s *privreq; + +#ifdef CONFIG_DEBUG + if (!ep) + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_INVALIDPARMS), 0); + return NULL; + } +#endif + + usbtrace(TRACE_EPALLOCREQ, ((FAR struct efm32_ep_s *)ep)->epphy); + + privreq = (FAR struct efm32_req_s *)kmm_malloc(sizeof(struct efm32_req_s)); + if (!privreq) + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_ALLOCFAIL), 0); + return NULL; + } + + memset(privreq, 0, sizeof(struct efm32_req_s)); + return &privreq->req; +} + +/******************************************************************************* + * Name: efm32_ep_freereq + * + * Description: + * Free an I/O request + * + *******************************************************************************/ + +static void efm32_ep_freereq(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req) +{ + FAR struct efm32_req_s *privreq = (FAR struct efm32_req_s *)req; + +#ifdef CONFIG_DEBUG + if (!ep || !req) + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_INVALIDPARMS), 0); + return; + } +#endif + + usbtrace(TRACE_EPFREEREQ, ((FAR struct efm32_ep_s *)ep)->epphy); + kmm_free(privreq); +} + +/******************************************************************************* + * Name: efm32_ep_allocbuffer + * + * Description: + * Allocate an I/O buffer + * + *******************************************************************************/ + +#ifdef CONFIG_USBDEV_DMA +static void *efm32_ep_allocbuffer(FAR struct usbdev_ep_s *ep, unsigned bytes) +{ + usbtrace(TRACE_EPALLOCBUFFER, privep->epphy); + +#ifdef CONFIG_USBDEV_DMAMEMORY + return usbdev_dma_alloc(bytes); +#else + return kmm_malloc(bytes); +#endif +} +#endif + +/******************************************************************************* + * Name: efm32_ep_freebuffer + * + * Description: + * Free an I/O buffer + * + *******************************************************************************/ + +#ifdef CONFIG_USBDEV_DMA +static void efm32_ep_freebuffer(FAR struct usbdev_ep_s *ep, FAR void *buf) +{ + usbtrace(TRACE_EPFREEBUFFER, privep->epphy); + +#ifdef CONFIG_USBDEV_DMAMEMORY + usbdev_dma_free(buf); +#else + kmm_free(buf); +#endif +} +#endif + +/******************************************************************************* + * Name: efm32_ep_submit + * + * Description: + * Submit an I/O request to the endpoint + * + *******************************************************************************/ + +static int efm32_ep_submit(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req) +{ + FAR struct efm32_req_s *privreq = (FAR struct efm32_req_s *)req; + FAR struct efm32_ep_s *privep = (FAR struct efm32_ep_s *)ep; + FAR struct efm32_usbdev_s *priv; + irqstate_t flags; + int ret = OK; + + /* Some sanity checking */ + +#ifdef CONFIG_DEBUG + if (!req || !req->callback || !req->buf || !ep) + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_INVALIDPARMS), 0); + ullvdbg("req=%p callback=%p buf=%p ep=%p\n", req, req->callback, req->buf, ep); + return -EINVAL; + } +#endif + + usbtrace(TRACE_EPSUBMIT, privep->epphy); + priv = privep->dev; + +#ifdef CONFIG_DEBUG + if (!priv->driver) + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_NOTCONFIGURED), priv->usbdev.speed); + return -ESHUTDOWN; + } +#endif + + /* Handle the request from the class driver */ + + req->result = -EINPROGRESS; + req->xfrd = 0; + + /* Disable Interrupts */ + + flags = irqsave(); + + /* If we are stalled, then drop all requests on the floor */ + + if (privep->stalled) + { + ret = -EBUSY; + } + else + { + /* Add the new request to the request queue for the endpoint. */ + + if (efm32_req_addlast(privep, privreq) && !privep->active) + { + /* If a request was added to an IN endpoint, then attempt to send + * the request data buffer now. + */ + + if (privep->isin) + { + usbtrace(TRACE_INREQQUEUED(privep->epphy), privreq->req.len); + + /* If the endpoint is not busy with another write request, + * then process the newly received write request now. + */ + + if (!privep->active) + { + efm32_epin_request(priv, privep); + } + } + + /* If the request was added to an OUT endoutput, then attempt to + * setup a read into the request data buffer now (this will, of + * course, fail if there is already a read in place). + */ + + else + { + usbtrace(TRACE_OUTREQQUEUED(privep->epphy), privreq->req.len); + efm32_epout_request(priv, privep); + } + } + } + + irqrestore(flags); + return ret; +} + +/******************************************************************************* + * Name: efm32_ep_cancel + * + * Description: + * Cancel an I/O request previously sent to an endpoint + * + *******************************************************************************/ + +static int efm32_ep_cancel(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req) +{ + FAR struct efm32_ep_s *privep = (FAR struct efm32_ep_s *)ep; + irqstate_t flags; + +#ifdef CONFIG_DEBUG + if (!ep || !req) + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + + usbtrace(TRACE_EPCANCEL, privep->epphy); + + flags = irqsave(); + + /* FIXME: if the request is the first, then we need to flush the EP + * otherwise just remove it from the list + * + * but ... all other implementations cancel all requests ... + */ + + efm32_req_cancel(privep, -ESHUTDOWN); + irqrestore(flags); + return OK; +} + +/******************************************************************************* + * Name: efm32_epout_setstall + * + * Description: + * Stall an OUT endpoint + * + *******************************************************************************/ + +static int efm32_epout_setstall(FAR struct efm32_ep_s *privep) +{ +#if 1 + /* This implementation follows the requirements from the EFM32 F4 reference + * manual. + */ + + uint32_t regaddr; + uint32_t regval; + + /* Put the core in the Global OUT NAK mode */ + + efm32_enablegonak(privep); + + /* Disable and STALL the OUT endpoint by setting the EPDIS and STALL bits + * in the DOECPTL register. + */ + + regaddr = EFM32_USB_DOEPCTL(privep->epphy); + regval = efm32_getreg(regaddr); + regval |= (USB_DOEPCTL_EPDIS | USB_DOEPCTL_STALL); + efm32_putreg(regval, regaddr); + + /* Wait for the EPDISD interrupt which indicates that the OUT + * endpoint is completely disabled. + */ + +#if 0 /* Doesn't happen */ + regaddr = EFM32_USB_DOEPINT(privep->epphy); + while ((efm32_getreg(regaddr) & USB_DOEPINT_EPDISD) == 0); +#else + /* REVISIT: */ + up_udelay(10); +#endif + + /* Disable Global OUT NAK mode */ + + efm32_disablegonak(privep); + + /* The endpoint is now stalled */ + + privep->stalled = true; + return OK; +#else + /* This implementation follows the STMicro code example. */ + /* REVISIT: */ + + uint32_t regaddr; + uint32_t regval; + + /* Stall the OUT endpoint by setting the STALL bit in the DOECPTL register. */ + + regaddr = EFM32_USB_DOEPCTL(privep->epphy); + regval = efm32_getreg(regaddr); + regval |= USB_DOEPCTL_STALL; + efm32_putreg(regval, regaddr); + + /* The endpoint is now stalled */ + + privep->stalled = true; + return OK; +#endif +} + +/******************************************************************************* + * Name: efm32_epin_setstall + * + * Description: + * Stall an IN endpoint + * + *******************************************************************************/ + +static int efm32_epin_setstall(FAR struct efm32_ep_s *privep) +{ + uint32_t regaddr; + uint32_t regval; + + /* Get the IN endpoint device control register */ + + regaddr = EFM32_USB_DIEPCTL(privep->epphy); + regval = efm32_getreg(regaddr); + + /* Then stall the endpoint */ + + regval |= USB_DIEPCTL_STALL; + efm32_putreg(regval, regaddr); + + /* The endpoint is now stalled */ + + privep->stalled = true; + return OK; +} + +/******************************************************************************* + * Name: efm32_ep_setstall + * + * Description: + * Stall an endpoint + * + *******************************************************************************/ + +static int efm32_ep_setstall(FAR struct efm32_ep_s *privep) +{ + usbtrace(TRACE_EPSTALL, privep->epphy); + + /* Is this an IN endpoint? */ + + if (privep->isin == 1) + { + return efm32_epin_setstall(privep); + } + else + { + return efm32_epout_setstall(privep); + } +} + +/******************************************************************************* + * Name: efm32_ep_clrstall + * + * Description: + * Resume a stalled endpoint + * + *******************************************************************************/ + +static int efm32_ep_clrstall(FAR struct efm32_ep_s *privep) +{ + uint32_t regaddr; + uint32_t regval; + uint32_t stallbit; + uint32_t data0bit; + + usbtrace(TRACE_EPRESUME, privep->epphy); + + /* Is this an IN endpoint? */ + + if (privep->isin == 1) + { + /* Clear the stall bit in the IN endpoint device control register */ + + regaddr = EFM32_USB_DIEPCTL(privep->epphy); + stallbit = USB_DIEPCTL_STALL; + data0bit = USB_DIEPCTL_SD0PID; + } + else + { + /* Clear the stall bit in the IN endpoint device control register */ + + regaddr = EFM32_USB_DOEPCTL(privep->epphy); + stallbit = USB_DOEPCTL_STALL; + data0bit = USB_DOEPCTL_SD0PID; + } + + /* Clear the stall bit */ + + regval = efm32_getreg(regaddr); + regval &= ~stallbit; + + /* Set the DATA0 pid for interrupt and bulk endpoints */ + + if (privep->eptype == USB_EP_ATTR_XFER_INT || + privep->eptype == USB_EP_ATTR_XFER_BULK) + { + /* Writing this bit sets the DATA0 PID */ + + regval |= data0bit; + } + + efm32_putreg(regval, regaddr); + + /* The endpoint is no longer stalled */ + + privep->stalled = false; + return OK; +} + +/******************************************************************************* + * Name: efm32_ep_stall + * + * Description: + * Stall or resume an endpoint + * + *******************************************************************************/ + +static int efm32_ep_stall(FAR struct usbdev_ep_s *ep, bool resume) +{ + FAR struct efm32_ep_s *privep = (FAR struct efm32_ep_s *)ep; + irqstate_t flags; + int ret; + + /* Set or clear the stall condition as requested */ + + flags = irqsave(); + if (resume) + { + ret = efm32_ep_clrstall(privep); + } + else + { + ret = efm32_ep_setstall(privep); + } + irqrestore(flags); + + return ret; +} + +/******************************************************************************* + * Name: efm32_ep0_stall + * + * Description: + * Stall endpoint 0 + * + *******************************************************************************/ + +static void efm32_ep0_stall(FAR struct efm32_usbdev_s *priv) +{ + efm32_epin_setstall(&priv->epin[EP0]); + efm32_epout_setstall(&priv->epout[EP0]); + priv->stalled = true; + efm32_ep0out_ctrlsetup(priv); +} + +/******************************************************************************* + * Device operations + *******************************************************************************/ + +/******************************************************************************* + * Name: efm32_ep_alloc + * + * Description: + * Allocate an endpoint matching the parameters. + * + * Input Parameters: + * eplog - 7-bit logical endpoint number (direction bit ignored). Zero means + * that any endpoint matching the other requirements will suffice. The + * assigned endpoint can be found in the eplog field. + * in - true: IN (device-to-host) endpoint requested + * eptype - Endpoint type. One of {USB_EP_ATTR_XFER_ISOC, USB_EP_ATTR_XFER_BULK, + * USB_EP_ATTR_XFER_INT} + * + *******************************************************************************/ + +static FAR struct usbdev_ep_s *efm32_ep_alloc(FAR struct usbdev_s *dev, + uint8_t eplog, bool in, + uint8_t eptype) +{ + FAR struct efm32_usbdev_s *priv = (FAR struct efm32_usbdev_s *)dev; + uint8_t epavail; + irqstate_t flags; + int epphy; + int epno = 0; + + usbtrace(TRACE_DEVALLOCEP, (uint16_t)eplog); + + /* Ignore any direction bits in the logical address */ + + epphy = USB_EPNO(eplog); + + /* Get the set of available endpoints depending on the direction */ + + flags = irqsave(); + epavail = priv->epavail[in]; + + /* A physical address of 0 means that any endpoint will do */ + + if (epphy > 0) + { + /* Otherwise, we will return the endpoint structure only for the requested + * 'logical' endpoint. All of the other checks will still be performed. + * + * First, verify that the logical endpoint is in the range supported by + * by the hardware. + */ + + if (epphy >= EFM32_NENDPOINTS) + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_BADEPNO), (uint16_t)epphy); + return NULL; + } + + /* Remove all of the candidate endpoints from the bitset except for the + * this physical endpoint number. + */ + + epavail &= (1 << epphy); + } + + /* Is there an available endpoint? */ + + if (epavail) + { + /* Yes.. Select the lowest numbered endpoint in the set of available + * endpoints. + */ + + for (epno = 1; epno < EFM32_NENDPOINTS; epno++) + { + uint8_t bit = 1 << epno; + if ((epavail & bit) != 0) + { + /* Mark the endpoint no longer available */ + + priv->epavail[in] &= ~(1 << epno); + + /* And return the pointer to the standard endpoint structure */ + + irqrestore(flags); + return in ? &priv->epin[epno].ep : &priv->epout[epno].ep; + } + } + + /* We should not get here */ + } + + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_NOEP), (uint16_t)eplog); + irqrestore(flags); + return NULL; +} + +/******************************************************************************* + * Name: efm32_ep_free + * + * Description: + * Free the previously allocated endpoint + * + *******************************************************************************/ + +static void efm32_ep_free(FAR struct usbdev_s *dev, FAR struct usbdev_ep_s *ep) +{ + FAR struct efm32_usbdev_s *priv = (FAR struct efm32_usbdev_s *)dev; + FAR struct efm32_ep_s *privep = (FAR struct efm32_ep_s *)ep; + irqstate_t flags; + + usbtrace(TRACE_DEVFREEEP, (uint16_t)privep->epphy); + + if (priv && privep) + { + /* Mark the endpoint as available */ + + flags = irqsave(); + priv->epavail[privep->isin] |= (1 << privep->epphy); + irqrestore(flags); + } +} + +/******************************************************************************* + * Name: efm32_getframe + * + * Description: + * Returns the current frame number + * + *******************************************************************************/ + +static int efm32_getframe(struct usbdev_s *dev) +{ + uint32_t regval; + + usbtrace(TRACE_DEVGETFRAME, 0); + + /* Return the last frame number of the last SOF detected by the hardware */ + + regval = efm32_getreg(EFM32_USB_DSTS); + return (int)((regval & _USB_DSTS_SOFFN_MASK) >> _USB_DSTS_SOFFN_SHIFT); +} + +/******************************************************************************* + * Name: efm32_wakeup + * + * Description: + * Exit suspend mode. + * + *******************************************************************************/ + +static int efm32_wakeup(struct usbdev_s *dev) +{ + FAR struct efm32_usbdev_s *priv = (FAR struct efm32_usbdev_s *)dev; + uint32_t regval; + irqstate_t flags; + + usbtrace(TRACE_DEVWAKEUP, 0); + + /* Is wakeup enabled? */ + + flags = irqsave(); + if (priv->wakeup) + { + /* Yes... is the core suspended? */ + + regval = efm32_getreg(EFM32_USB_DSTS); + if ((regval & USB_DSTS_SUSPSTS) != 0) + { + /* Re-start the PHY clock and un-gate USB core clock (HCLK) */ + +#ifdef CONFIG_USBDEV_LOWPOWER + regval = efm32_getreg(EFM32_USB_PCGCCTL); + regval &= ~(OTGFS_PCGCCTL_STPPCLK | OTGFS_PCGCCTL_GATEHCLK); + efm32_putreg(regval, EFM32_USB_PCGCCTL); +#endif + /* Activate Remote wakeup signaling */ + + regval = efm32_getreg(EFM32_USB_DCTL); + regval |= USB_DCTL_RWUSIG; + efm32_putreg(regval, EFM32_USB_DCTL); + up_mdelay(5); + regval &= ~USB_DCTL_RWUSIG; + efm32_putreg(regval, EFM32_USB_DCTL); + } + } + + irqrestore(flags); + return OK; +} + +/******************************************************************************* + * Name: efm32_selfpowered + * + * Description: + * Sets/clears the device self-powered feature + * + *******************************************************************************/ + +static int efm32_selfpowered(struct usbdev_s *dev, bool selfpowered) +{ + FAR struct efm32_usbdev_s *priv = (FAR struct efm32_usbdev_s *)dev; + + usbtrace(TRACE_DEVSELFPOWERED, (uint16_t)selfpowered); + +#ifdef CONFIG_DEBUG + if (!dev) + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_INVALIDPARMS), 0); + return -ENODEV; + } +#endif + + priv->selfpowered = selfpowered; + return OK; +} + +/******************************************************************************* + * Name: efm32_pullup + * + * Description: + * Software-controlled connect to/disconnect from USB host + * + *******************************************************************************/ + +static int efm32_pullup(struct usbdev_s *dev, bool enable) +{ + uint32_t regval; + + usbtrace(TRACE_DEVPULLUP, (uint16_t)enable); + + irqstate_t flags = irqsave(); + regval = efm32_getreg(EFM32_USB_DCTL); + if (enable) + { + /* Connect the device by clearing the soft disconnect bit in the DCTL + * register + */ + + regval &= ~USB_DCTL_SDIS; + } + else + { + /* Connect the device by setting the soft disconnect bit in the DCTL + * register + */ + + regval |= USB_DCTL_SDIS; + } + + efm32_putreg(regval, EFM32_USB_DCTL); + irqrestore(flags); + return OK; +} + +/******************************************************************************* + * Name: efm32_setaddress + * + * Description: + * Set the devices USB address + * + *******************************************************************************/ + +static void efm32_setaddress(struct efm32_usbdev_s *priv, uint16_t address) +{ + uint32_t regval; + + /* Set the device address in the DCFG register */ + + regval = efm32_getreg(EFM32_USB_DCFG); + regval &= ~_USB_DCFG_DAD_MASK; + regval |= ((uint32_t)address << _USB_DCFG_DAD_SHIFT); + efm32_putreg(regval, EFM32_USB_DCFG); + + /* Are we now addressed? (i.e., do we have a non-NULL device + * address?) + */ + + if (address != 0) + { + priv->devstate = DEVSTATE_ADDRESSED; + priv->addressed = true; + } + else + { + priv->devstate = DEVSTATE_DEFAULT; + priv->addressed = false; + } +} + +/******************************************************************************* + * Name: efm32_txfifo_flush + * + * Description: + * Flush the specific TX fifo. + * + *******************************************************************************/ + +static int efm32_txfifo_flush(uint32_t txfnum) +{ + uint32_t regval; + uint32_t timeout; + + /* Initiate the TX FIFO flush operation */ + + regval = USB_GRSTCTL_TXFFLSH | txfnum; + efm32_putreg(regval, EFM32_USB_GRSTCTL); + + /* Wait for the FLUSH to complete */ + + for (timeout = 0; timeout < EFM32_FLUSH_DELAY; timeout++) + { + regval = efm32_getreg(EFM32_USB_GRSTCTL); + if ((regval & USB_GRSTCTL_TXFFLSH) == 0) + { + break; + } + } + + /* Wait for 3 PHY Clocks */ + + up_udelay(3); + return OK; +} + +/******************************************************************************* + * Name: efm32_rxfifo_flush + * + * Description: + * Flush the RX fifo. + * + *******************************************************************************/ + +static int efm32_rxfifo_flush(void) +{ + uint32_t regval; + uint32_t timeout; + + /* Initiate the RX FIFO flush operation */ + + efm32_putreg(USB_GRSTCTL_RXFFLSH, EFM32_USB_GRSTCTL); + + /* Wait for the FLUSH to complete */ + + for (timeout = 0; timeout < EFM32_FLUSH_DELAY; timeout++) + { + regval = efm32_getreg(EFM32_USB_GRSTCTL); + if ((regval & USB_GRSTCTL_RXFFLSH) == 0) + { + break; + } + } + + /* Wait for 3 PHY Clocks */ + + up_udelay(3); + return OK; +} + +/******************************************************************************* + * Name: efm32_swinitialize + * + * Description: + * Initialize all driver data structures. + * + *******************************************************************************/ + +static void efm32_swinitialize(FAR struct efm32_usbdev_s *priv) +{ + FAR struct efm32_ep_s *privep; + int i; + + /* Initialize the device state structure */ + + memset(priv, 0, sizeof(struct efm32_usbdev_s)); + + priv->usbdev.ops = &g_devops; + priv->usbdev.ep0 = &priv->epin[EP0].ep; + + priv->epavail[0] = EFM32_EP_AVAILABLE; + priv->epavail[1] = EFM32_EP_AVAILABLE; + + priv->epin[EP0].ep.priv = priv; + priv->epout[EP0].ep.priv = priv; + + /* Initialize the endpoint lists */ + + for (i = 0; i < EFM32_NENDPOINTS; i++) + { + /* Set endpoint operations, reference to driver structure (not + * really necessary because there is only one controller), and + * the physical endpoint number (which is just the index to the + * endpoint). + */ + + privep = &priv->epin[i]; + privep->ep.ops = &g_epops; + privep->dev = priv; + privep->isin = 1; + + /* The index, i, is the physical endpoint address; Map this + * to a logical endpoint address usable by the class driver. + */ + + privep->epphy = i; + privep->ep.eplog = EFM32_EPPHYIN2LOG(i); + + /* Control until endpoint is activated */ + + privep->eptype = USB_EP_ATTR_XFER_CONTROL; + privep->ep.maxpacket = CONFIG_USBDEV_EP0_MAXSIZE; + } + + /* Initialize the endpoint lists */ + + for (i = 0; i < EFM32_NENDPOINTS; i++) + { + /* Set endpoint operations, reference to driver structure (not + * really necessary because there is only one controller), and + * the physical endpoint number (which is just the index to the + * endpoint). + */ + + privep = &priv->epout[i]; + privep->ep.ops = &g_epops; + privep->dev = priv; + + /* The index, i, is the physical endpoint address; Map this + * to a logical endpoint address usable by the class driver. + */ + + privep->epphy = i; + privep->ep.eplog = EFM32_EPPHYOUT2LOG(i); + + /* Control until endpoint is activated */ + + privep->eptype = USB_EP_ATTR_XFER_CONTROL; + privep->ep.maxpacket = CONFIG_USBDEV_EP0_MAXSIZE; + } +} + +/******************************************************************************* + * Name: efm32_hwinitialize + * + * Description: + * Configure the OTG FS core for operation. + * + *******************************************************************************/ + +static void efm32_hwinitialize(FAR struct efm32_usbdev_s *priv) +{ + uint32_t regval; + uint32_t timeout; + uint32_t address; + int i; + + /* "The application must perform the following steps to initialize the core + * at device on, power on, or after a mode change from Host to Device. + * 1. Program the following fields in USB_DCFG register. + * - Device Speed + * - NonZero Length Status OUT Handshake + * - Periodic Frame Interval + * 2. Program the USB_GINTMSK register to unmask the following interrupts. + * - USB Reset + * - Enumeration Done + * - Early Suspend + * - USB Suspend + * 3. Wait for the USB_GINTSTS.USBRST interrupt, which indicates a reset + * has been detected on the USB and lasts for about 10 ms. On receiving + * this interrupt, the application must perform the steps listed in + * Initialization on USB Reset ... + * 4. Wait for the USB_GINTSTS.ENUMDONE interrupt. This interrupt indicates + * the end of reset on the USB. On receiving this interrupt, the + * application must read the USB_DSTS register to determine the + * enumeration speed and perform the steps listed in Initialization on + * Enumeration Completion ..." + * + * "Initialization on USB Reset + * 1. Set the NAK bit for all OUT endpoints + * - USB_DOEPx_CTL.SNAK = 1 (for all OUT endpoints) + * 2. Unmask the following interrupt bits: + * - USB_USB_DAINTMSK.INEP0 = 1 (control 0 IN endpoint) + * - USB_USB_DAINTMSK.OUTEP0 = 1 (control 0 OUT endpoint) + * - USB_DOEPMSK.SETUP = 1 + * - USB_DOEPMSK.XFERCOMPL = 1 + * - USB_DIEPMSK.XFERCOMPL = 1 + * - USB_DIEPMSK.TIMEOUTMSK = 1 + * 3. To transmit or receive data, the device must initialize more + * registers as specified in Device DMA/Slave Mode Initialization ... + * 4. Set up the Data FIFO RAM for each of the FIFOs + * - Program the USB_GRXFSIZ Register, to be able to receive control + * OUT data and setup data. At a minimum, this must be equal to 1 max + * packet size of control endpoint 0 + 2 DWORDs (for the status of the + * control OUT data packet) + 10 DWORDs (for setup packets). + * - Program the Device IN Endpoint Transmit FIFO size register + * (depending on the FIFO number chosen), to be able to transmit + * control IN data. At a minimum, this must be equal to 1 max packet + * size of control endpoint 0. + * 5. Program the following fields in the endpoint-specific registers for + * control OUT endpoint 0 to receive a SETUP packet + * - USB_DOEP0TSIZ.SUPCNT = 3 (to receive up to 3 back-to-back SETUP + * packets) + * - In DMA mode, USB_DOEP0DMAADDR register with a memory address to + * store any SETUP packets received" + * + * "Initialization on Enumeration Completion + * 1. On the Enumeration Done interrupt (USB_GINTSTS.ENUMDONE, read the + * USB_DSTS register to determine the enumeration speed. + * 2. Program the USB_DIEP0CTL.MPS field to set the maximum packet size. + * This step configures control endpoint 0. The maximum packet size + * for a control endpoint depends on the enumeration speed. + * 3. In DMA mode, program the USB_DOEP0CTL register to enable control + * OUT endpoint 0, to receive a SETUP packet. + * - USB_DOEP0CTL.EPENA = 1" + */ +#warning Review for missing logic + + /* At start-up the core is in FS mode. */ + + /* Disable global interrupts by clearing the GINTMASK bit in the GAHBCFG + * register; Set the TXFELVL bit in the GAHBCFG register so that TxFIFO + * interrupts will occur when the TxFIFO is truly empty (not just half full). + */ + + efm32_putreg(USB_GAHBCFG_TXFELVL, EFM32_USB_GAHBCFG); + + /* Common USB OTG core initialization */ + /* Reset after a PHY select and set Host mode. First, wait for AHB master + * IDLE state. + */ + + for (timeout = 0; timeout < EFM32_READY_DELAY; timeout++) + { + up_udelay(3); + regval = efm32_getreg(EFM32_USB_GRSTCTL); + if ((regval & USB_GRSTCTL_AHBIDL) != 0) + { + break; + } + } + + /* Then perform the core soft reset. */ + + efm32_putreg(USB_GRSTCTL_CSRST, EFM32_USB_GRSTCTL); + for (timeout = 0; timeout < EFM32_READY_DELAY; timeout++) + { + regval = efm32_getreg(EFM32_USB_GRSTCTL); + if ((regval & USB_GRSTCTL_CSRST) == 0) + { + break; + } + } + + /* Wait for 3 PHY Clocks */ + + up_udelay(3); + + /* Deactivate the power down */ + + regval = (USB_GCCFG_PWRDWN | USB_GCCFG_VBUSASEN | USB_GCCFG_VBUSBSEN); +#ifndef CONFIG_USBDEV_VBUSSENSING + regval |= USB_GCCFG_NOVBUSSENS; +#endif +#ifdef CONFIG_EFM32_OTGFS_SOFOUTPUT + regval |= USB_GCCFG_SOFOUTEN; +#endif + efm32_putreg(regval, EFM32_USB_GCCFG); + up_mdelay(20); + + /* Force Device Mode */ + + regval = efm32_getreg(EFM32_USB_GUSBCFG); + regval &= ~USB_GUSBCFG_FHMOD; + regval |= USB_GUSBCFG_FDMOD; + efm32_putreg(regval, EFM32_USB_GUSBCFG); + up_mdelay(50); + + /* Initialize device mode */ + /* Restart the PHY Clock */ + + efm32_putreg(0, EFM32_USB_PCGCCTL); + + /* Device configuration register */ + + regval = efm32_getreg(EFM32_USB_DCFG); + regval &= ~_USB_DCFG_PFIVL_MASK; + regval |= USB_DCFG_PFIVL_80PCT; + efm32_putreg(regval, EFM32_USB_DCFG); + + /* Set full speed PHY */ + + regval = efm32_getreg(EFM32_USB_DCFG); + regval &= ~_USB_DCFG_DSPD_MASK; + regval |= USB_DCFG_DSPD_FS; + efm32_putreg(regval, EFM32_USB_DCFG); + + /* Set Rx FIFO size */ + + efm32_putreg(EFM32_RXFIFO_WORDS, EFM32_USB_GRXFSIZ); + + /* EP0 TX */ + + address = EFM32_RXFIFO_WORDS; + regval = (address << _USB_DIEPTXF0_TX0FD_SHIFT) | + (EFM32_EP0_TXFIFO_WORDS << _USB_DIEPTXF0_TX0FSA_SHIFT); + efm32_putreg(regval, EFM32_USB_DIEPTXF0); + + /* EP1 TX */ + + address += EFM32_EP0_TXFIFO_WORDS; + regval = (address << _USB_DIEPTXF_INEPTXSA_SHIFT) | + (EFM32_EP1_TXFIFO_WORDS << _USB_DIEPTXF_INEPTXFD_SHIFT); + efm32_putreg(regval, EFM32_USB_DIEPTXF1); + + /* EP2 TX */ + + address += EFM32_EP1_TXFIFO_WORDS; + regval = (address << _USB_DIEPTXF_INEPTXSA_SHIFT) | + (EFM32_EP2_TXFIFO_WORDS << _USB_DIEPTXF_INEPTXFD_SHIFT); + efm32_putreg(regval, EFM32_USB_DIEPTXF2); + + /* EP3 TX */ + + address += EFM32_EP2_TXFIFO_WORDS; + regval = (address << _USB_DIEPTXF_INEPTXSA_SHIFT) | + (EFM32_EP3_TXFIFO_WORDS << _USB_DIEPTXF_INEPTXFD_SHIFT); + efm32_putreg(regval, EFM32_USB_DIEPTXF3); + + /* Flush the FIFOs */ + + efm32_txfifo_flush(USB_GRSTCTL_TXFNUM_DALL); + efm32_rxfifo_flush(); + + /* Clear all pending Device Interrupts */ + + efm32_putreg(0, EFM32_USB_DIEPMSK); + efm32_putreg(0, EFM32_USB_DOEPMSK); + efm32_putreg(0, EFM32_USB_DIEPEMPMSK); + efm32_putreg(0xffffffff, EFM32_USB_DAINT); + efm32_putreg(0, EFM32_USB_DAINTMSK); + + /* Configure all IN endpoints */ + + for (i = 0; i < EFM32_NENDPOINTS; i++) + { + regval = efm32_getreg(EFM32_USB_DIEPCTL(i)); + if ((regval & USB_DIEPCTL_EPENA) != 0) + { + /* The endpoint is already enabled */ + + regval = USB_DIEPCTL_EPENA | USB_DIEPCTL_SNAK; + } + else + { + regval = 0; + } + + efm32_putreg(regval, EFM32_USB_DIEPCTL(i)); + efm32_putreg(0, EFM32_USB_DIEPTSIZ(i)); + efm32_putreg(0xff, EFM32_USB_DIEPINT(i)); + } + + /* Configure all OUT endpoints */ + + for (i = 0; i < EFM32_NENDPOINTS; i++) + { + regval = efm32_getreg(EFM32_USB_DOEPCTL(i)); + if ((regval & USB_DOEPCTL_EPENA) != 0) + { + /* The endpoint is already enabled */ + + regval = USB_DOEPCTL_EPENA | USB_DOEPCTL_SNAK; + } + else + { + regval = 0; + } + + efm32_putreg(regval, EFM32_USB_DOEPCTL(i)); + efm32_putreg(0, EFM32_USB_DOEPTSIZ(i)); + efm32_putreg(0xff, EFM32_USB_DOEPINT(i)); + } + + /* Disable all interrupts. */ + + efm32_putreg(0, EFM32_USB_GINTMSK); + + /* Clear any pending USB_OTG Interrupts */ + + efm32_putreg(0xffffffff, EFM32_USB_GOTGINT); + + /* Clear any pending interrupts */ + + efm32_putreg(0xbfffffff, EFM32_USB_GINTSTS); + + /* Enable the interrupts in the INTMSK */ + + regval = (USB_GINT_RXFLVL | USB_GINT_USBSUSP | USB_GINT_ENUMDNE | + USB_GINT_IEP | USB_GINT_OEP | USB_GINT_USBRST); + +#ifdef CONFIG_USBDEV_ISOCHRONOUS + regval |= (USB_GINT_IISOIXFR | USB_GINT_IISOOXFR); +#endif + +#ifdef CONFIG_USBDEV_SOFINTERRUPT + regval |= USB_GINT_SOF; +#endif + +#ifdef CONFIG_USBDEV_VBUSSENSING + regval |= (USB_GINT_OTG | USB_GINT_SRQ); +#endif + +#ifdef CONFIG_DEBUG_USB + regval |= USB_GINT_MMIS; +#endif + + efm32_putreg(regval, EFM32_USB_GINTMSK); + + /* Enable the USB global interrupt by setting GINTMSK in the global OTG + * FS AHB configuration register; Set the TXFELVL bit in the GAHBCFG + * register so that TxFIFO interrupts will occur when the TxFIFO is truly + * empty (not just half full). + */ + + efm32_putreg(USB_GAHBCFG_GINTMSK | USB_GAHBCFG_TXFELVL, + EFM32_USB_GAHBCFG); +} + +/******************************************************************************* + * Public Functions + *******************************************************************************/ + +/******************************************************************************* + * Name: up_usbinitialize + * + * Description: + * Initialize USB hardware. + * + * Assumptions: + * - This function is called very early in the initialization sequence + * - PLL and GIO pin initialization is not performed here but should been in + * the low-level boot logic: PLL1 must be configured for operation at 48MHz + * and P0.23 and PO.31 in PINSEL1 must be configured for Vbus and USB connect + * LED. + * + *******************************************************************************/ + +void up_usbinitialize(void) +{ + /* At present, there is only a single OTG FS device support. Hence it is + * pre-allocated as g_otgfsdev. However, in most code, the private data + * structure will be referenced using the 'priv' pointer (rather than the + * global data) in order to simplify any future support for multiple devices. + */ + + FAR struct efm32_usbdev_s *priv = &g_otgfsdev; + int ret; + + usbtrace(TRACE_DEVINIT, 0); + + /* "The USB requires the device to run from a 48 MHz crystal (2500 ppm or + * better). The core part of the USB will always run from HFCORECLKUSBC + * which is HFCLK undivided (48 MHz). The current consumption for the + * rest of the device can be reduced by dividing down HFCORECLK using + * the CMU_HFCORECLKDIV register. ..." + * + * "Follow these steps to enable the USB: + * 1. Enable the clock to the system part by setting USB in + * CMU_HFCORECLKEN0. + * 2. If the internal USB regulator is bypassed (by applying 3.3V on + * USB_VREGI and USB_VREGO externally), disable the regulator by + * setting VREGDIS in USB_CTRL. + * 3. If the PHY is powered from VBUS using the internal regulator, the + * VREGO sense circuit should be enabled by setting VREGOSEN in + * USB_CTRL. + * 4. Enable the USB PHY pins by setting PHYPEN in USB_ROUTE. + * 5. If host or OTG dual-role device, set VBUSENAP in USB_CTRL to the + * desired value and then enable the USB_VBUSEN pin in USB_ROUTE. Set + * the MODE for the pin to PUSHPULL. + * 6. If low-speed device, set DMPUAP in USB_CTRL to the desired value + * and then enable the USB_DMPU pin in USB_ROUTE. Set the MODE for the + * pin to PUSHPULL. + * 7. Make sure HFXO is ready and selected. The core part requires the + * undivided HFCLK to be 48 MHz when USB is active (during + * suspend/session-off a 32 kHz clock is used).. + * 8. Enable the clock to the core part by setting USBC in + * CMU_HFCORECLKEN0. + * 9. Wait for the core to come out of reset. This is easiest done by + * polling a core register with non-zero reset value until it reads a + * non-zero value. This takes approximately 20 48-MHz cycles. + * 10. Start initializing the USB core ... + */ +#warning Missing Logic + + /* Uninitialize the hardware so that we know that we are starting from a + * known state. */ + + up_usbuninitialize(); + + /* Initialie the driver data structure */ + + efm32_swinitialize(priv); + + /* Attach the OTG FS interrupt handler */ + + ret = irq_attach(EFM32_IRQ_USB, efm32_usbinterrupt); + if (ret < 0) + { + udbg("irq_attach failed\n", ret); + goto errout; + } + + /* Initialize the USB OTG core */ + + efm32_hwinitialize(priv); + + /* Disconnect device */ + + efm32_pullup(&priv->usbdev, false); + + /* Reset/Re-initialize the USB hardware */ + + efm32_usbreset(priv); + + /* Enable USB controller interrupts at the NVIC */ + + up_enable_irq(EFM32_IRQ_USB); + +#ifdef CONFIG_ARCH_IRQPRIO + /* Set the interrupt priority */ + + up_prioritize_irq(EFM32_IRQ_USB, CONFIG_OTGFS_PRI); +#endif + return; + +errout: + up_usbuninitialize(); +} + +/******************************************************************************* + * Name: up_usbuninitialize + *******************************************************************************/ + +void up_usbuninitialize(void) +{ + /* At present, there is only a single OTG FS device support. Hence it is + * pre-allocated as g_otgfsdev. However, in most code, the private data + * structure will be referenced using the 'priv' pointer (rather than the + * global data) in order to simplify any future support for multiple devices. + */ + + FAR struct efm32_usbdev_s *priv = &g_otgfsdev; + irqstate_t flags; + int i; + + usbtrace(TRACE_DEVUNINIT, 0); + + if (priv->driver) + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_DRIVERREGISTERED), 0); + usbdev_unregister(priv->driver); + } + + /* Disconnect device */ + + flags = irqsave(); + efm32_pullup(&priv->usbdev, false); + priv->usbdev.speed = USB_SPEED_UNKNOWN; + + /* Disable and detach IRQs */ + + up_disable_irq(EFM32_IRQ_USB); + irq_detach(EFM32_IRQ_USB); + + /* Disable all endpoint interrupts */ + + for (i = 0; i < EFM32_NENDPOINTS; i++) + { + efm32_putreg(0xff, EFM32_USB_DIEPINT(i)); + efm32_putreg(0xff, EFM32_USB_DOEPINT(i)); + } + + efm32_putreg(0, EFM32_USB_DIEPMSK); + efm32_putreg(0, EFM32_USB_DOEPMSK); + efm32_putreg(0, EFM32_USB_DIEPEMPMSK); + efm32_putreg(0, EFM32_USB_DAINTMSK); + efm32_putreg(0xffffffff, EFM32_USB_DAINT); + + /* Flush the FIFOs */ + + efm32_txfifo_flush(USB_GRSTCTL_TXFNUM_DALL); + efm32_rxfifo_flush(); + + /* TODO: Turn off USB power and clocking */ + + priv->devstate = DEVSTATE_DEFAULT; + irqrestore(flags); +} + +/******************************************************************************* + * Name: usbdev_register + * + * Description: + * Register a USB device class driver. The class driver's bind() method will be + * called to bind it to a USB device driver. + * + *******************************************************************************/ + +int usbdev_register(struct usbdevclass_driver_s *driver) +{ + /* At present, there is only a single OTG FS device support. Hence it is + * pre-allocated as g_otgfsdev. However, in most code, the private data + * structure will be referenced using the 'priv' pointer (rather than the + * global data) in order to simplify any future support for multiple devices. + */ + + FAR struct efm32_usbdev_s *priv = &g_otgfsdev; + int ret; + + usbtrace(TRACE_DEVREGISTER, 0); + +#ifdef CONFIG_DEBUG + if (!driver || !driver->ops->bind || !driver->ops->unbind || + !driver->ops->disconnect || !driver->ops->setup) + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } + + if (priv->driver) + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_DRIVER), 0); + return -EBUSY; + } +#endif + + /* First hook up the driver */ + + priv->driver = driver; + + /* Then bind the class driver */ + + ret = CLASS_BIND(driver, &priv->usbdev); + if (ret) + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_BINDFAILED), (uint16_t)-ret); + priv->driver = NULL; + } + else + { + /* Enable USB controller interrupts */ + + up_enable_irq(EFM32_IRQ_USB); + + /* FIXME: nothing seems to call DEV_CONNECT(), but we need to set + * the RS bit to enable the controller. It kind of makes sense + * to do this after the class has bound to us... + * GEN: This bug is really in the class driver. It should make the + * soft connect when it is ready to be enumerated. I have added + * that logic to the class drivers but left this logic here. + */ + + efm32_pullup(&priv->usbdev, true); + priv->usbdev.speed = USB_SPEED_FULL; + } + + return ret; +} + +/******************************************************************************* + * Name: usbdev_unregister + * + * Description: + * Un-register usbdev class driver.If the USB device is connected to a USB host, + * it will first disconnect(). The driver is also requested to unbind() and clean + * up any device state, before this procedure finally returns. + * + *******************************************************************************/ + +int usbdev_unregister(struct usbdevclass_driver_s *driver) +{ + /* At present, there is only a single OTG FS device support. Hence it is + * pre-allocated as g_otgfsdev. However, in most code, the private data + * structure will be referenced using the 'priv' pointer (rather than the + * global data) in order to simplify any future support for multiple devices. + */ + + FAR struct efm32_usbdev_s *priv = &g_otgfsdev; + irqstate_t flags; + + usbtrace(TRACE_DEVUNREGISTER, 0); + +#ifdef CONFIG_DEBUG + if (driver != priv->driver) + { + usbtrace(TRACE_DEVERROR(EFM32_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + + /* Reset the hardware and cancel all requests. All requests must be + * canceled while the class driver is still bound. + */ + + flags = irqsave(); + efm32_usbreset(priv); + irqrestore(flags); + + /* Unbind the class driver */ + + CLASS_UNBIND(driver, &priv->usbdev); + + /* Disable USB controller interrupts */ + + flags = irqsave(); + up_disable_irq(EFM32_IRQ_USB); + + /* Disconnect device */ + + efm32_pullup(&priv->usbdev, false); + + /* Unhook the driver */ + + priv->driver = NULL; + irqrestore(flags); + + return OK; +} + +#endif /* CONFIG_USBDEV && CONFIG_EFM32_OTGFSDEV */ diff --git a/nuttx/arch/arm/src/efm32/efm32_usbhost.c b/nuttx/arch/arm/src/efm32/efm32_usbhost.c new file mode 100644 index 000000000..ca920d2a9 --- /dev/null +++ b/nuttx/arch/arm/src/efm32/efm32_usbhost.c @@ -0,0 +1,4452 @@ +/******************************************************************************* + * arch/arm/src/efm32/efm32_otgfshost.c + * + * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Authors: Gregory Nutt <gnutt@nuttx.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + *******************************************************************************/ + +/******************************************************************************* + * Included Files + *******************************************************************************/ + +#include <nuttx/config.h> + +#include <sys/types.h> +#include <stdint.h> +#include <stdbool.h> +#include <stdlib.h> +#include <unistd.h> +#include <semaphore.h> +#include <string.h> +#include <errno.h> +#include <debug.h> + +#include <nuttx/arch.h> +#include <nuttx/kmalloc.h> +#include <nuttx/clock.h> +#include <nuttx/usb/usb.h> +#include <nuttx/usb/usbhost.h> +#include <nuttx/usb/usbhost_trace.h> + +#include <arch/irq.h> + +#include "chip.h" /* Includes default GPIO settings */ +#include <arch/board/board.h> /* May redefine GPIO settings */ + +#include "up_arch.h" +#include "up_internal.h" + +#include "efm32_usb.h" + +#if defined(CONFIG_USBHOST) && defined(CONFIG_EFM32_OTGFS) + +/******************************************************************************* + * Pre-processor Definitions + *******************************************************************************/ +/* Configuration ***************************************************************/ +/* + * EFM32 USB OTG FS Host Driver Support + * + * Pre-requisites + * + * CONFIG_USBHOST - Enable general USB host support + * CONFIG_EFM32_OTGFS - Enable the EFM32 USB OTG FS block + * + * Options: + * + * CONFIG_EFM32_OTGFS_RXFIFO_SIZE - Size of the RX FIFO in 32-bit words. + * Default 128 (512 bytes) + * CONFIG_EFM32_OTGFS_NPTXFIFO_SIZE - Size of the non-periodic Tx FIFO + * in 32-bit words. Default 96 (384 bytes) + * CONFIG_EFM32_OTGFS_PTXFIFO_SIZE - Size of the periodic Tx FIFO in 32-bit + * words. Default 96 (384 bytes) + * CONFIG_EFM32_OTGFS_DESCSIZE - Maximum size of a descriptor. Default: 128 + * CONFIG_EFM32_OTGFS_SOFINTR - Enable SOF interrupts. Why would you ever + * want to do that? + * CONFIG_EFM32_USBHOST_REGDEBUG - Enable very low-level register access + * debug. Depends on CONFIG_DEBUG. + * CONFIG_EFM32_USBHOST_PKTDUMP - Dump all incoming and outgoing USB + * packets. Depends on CONFIG_DEBUG. + */ + +/* Pre-requisites (partial) */ + +#ifndef CONFIG_EFM32_SYSCFG +# error "CONFIG_EFM32_SYSCFG is required" +#endif + +/* Default RxFIFO size */ + +#ifndef CONFIG_EFM32_OTGFS_RXFIFO_SIZE +# define CONFIG_EFM32_OTGFS_RXFIFO_SIZE 128 +#endif + +/* Default host non-periodic Tx FIFO size */ + +#ifndef CONFIG_EFM32_OTGFS_NPTXFIFO_SIZE +# define CONFIG_EFM32_OTGFS_NPTXFIFO_SIZE 96 +#endif + +/* Default host periodic Tx fifo size register */ + +#ifndef CONFIG_EFM32_OTGFS_PTXFIFO_SIZE +# define CONFIG_EFM32_OTGFS_PTXFIFO_SIZE 96 +#endif + +/* Maximum size of a descriptor */ + +#ifndef CONFIG_EFM32_OTGFS_DESCSIZE +# define CONFIG_EFM32_OTGFS_DESCSIZE 128 +#endif + +/* Register/packet debug depends on CONFIG_DEBUG */ + +#ifndef CONFIG_DEBUG +# undef CONFIG_EFM32_USBHOST_REGDEBUG +# undef CONFIG_EFM32_USBHOST_PKTDUMP +#endif + +/* HCD Setup *******************************************************************/ +/* Hardware capabilities */ + +#define EFM32_NHOST_CHANNELS 8 /* Number of host channels */ +#define EFM32_MAX_PACKET_SIZE 64 /* Full speed max packet size */ +#define EFM32_EP0_DEF_PACKET_SIZE 8 /* EP0 default packet size */ +#define EFM32_EP0_MAX_PACKET_SIZE 64 /* EP0 FS max packet size */ +#define EFM32_MAX_TX_FIFOS 15 /* Max number of TX FIFOs */ +#define EFM32_MAX_PKTCOUNT 256 /* Max packet count */ +#define EFM32_RETRY_COUNT 3 /* Number of ctrl transfer retries */ +#define EFM32_DEF_DEVADDR 0 /* Default device address */ + +/* Delays **********************************************************************/ + +#define EFM32_READY_DELAY 200000 /* In loop counts */ +#define EFM32_FLUSH_DELAY 200000 /* In loop counts */ +#define EFM32_SETUP_DELAY SEC2TICK(5) /* 5 seconds in system ticks */ +#define EFM32_DATANAK_DELAY SEC2TICK(5) /* 5 seconds in system ticks */ + +/* Ever-present MIN/MAX macros */ + +#ifndef MIN +# define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef MAX +# define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif + +/******************************************************************************* + * Private Types + *******************************************************************************/ + +/* The following enumeration represents the various states of the USB host + * state machine (for debug purposes only) + */ + +enum efm32_smstate_e +{ + SMSTATE_DETACHED = 0, /* Not attached to a device */ + SMSTATE_ATTACHED, /* Attached to a device */ + SMSTATE_ENUM, /* Attached, enumerating */ + SMSTATE_CLASS_BOUND, /* Enumeration complete, class bound */ +}; + +/* This enumeration provides the reason for the channel halt. */ + +enum efm32_chreason_e +{ + CHREASON_IDLE = 0, /* Inactive (initial state) */ + CHREASON_FREED, /* Channel is no longer in use */ + CHREASON_XFRC, /* Transfer complete */ + CHREASON_NAK, /* NAK received */ + CHREASON_NYET, /* NotYet received */ + CHREASON_STALL, /* Endpoint stalled */ + CHREASON_TXERR, /* Transfer error received */ + CHREASON_DTERR, /* Data toggle error received */ + CHREASON_FRMOR /* Frame overrun */ +}; + +/* This structure retains the state of one host channel. NOTE: Since there + * is only one channel operation active at a time, some of the fields in + * in the structure could be moved in struct efm32_ubhost_s to achieve + * some memory savings. + */ + +struct efm32_chan_s +{ + sem_t waitsem; /* Channel wait semaphore */ + volatile uint8_t result; /* The result of the transfer */ + volatile uint8_t chreason; /* Channel halt reason. See enum efm32_chreason_e */ + uint8_t epno; /* Device endpoint number (0-127) */ + uint8_t eptype; /* See OTGFS_EPTYPE_* definitions */ + uint8_t pid; /* Data PID */ + uint8_t npackets; /* Number of packets (for data toggle) */ + bool inuse; /* True: This channel is "in use" */ + volatile bool indata1; /* IN data toggle. True: DATA01 (Bulk and INTR only) */ + volatile bool outdata1; /* OUT data toggle. True: DATA01 */ + bool in; /* True: IN endpoint */ + volatile bool waiter; /* True: Thread is waiting for a channel event */ + uint16_t maxpacket; /* Max packet size */ + volatile uint16_t buflen; /* Buffer length (remaining) */ + volatile uint16_t inflight; /* Number of Tx bytes "in-flight" */ + FAR uint8_t *buffer; /* Transfer buffer pointer */ +}; + +/* This structure retains the state of the USB host controller */ + +struct efm32_usbhost_s +{ + /* Common device fields. This must be the first thing defined in the + * structure so that it is possible to simply cast from struct usbhost_s + * to structefm32_usbhost_s. + */ + + struct usbhost_driver_s drvr; + + /* The bound device class driver */ + + struct usbhost_class_s *class; + + /* Overall driver status */ + + volatile uint8_t smstate; /* The state of the USB host state machine */ + uint8_t devaddr; /* Device address */ + uint8_t ep0in; /* EP0 IN control channel index */ + uint8_t ep0out; /* EP0 OUT control channel index */ + uint8_t ep0size; /* EP0 max packet size */ + uint8_t chidx; /* ID of channel waiting for space in Tx FIFO */ + bool lowspeed; /* True: low speed device */ + volatile bool connected; /* Connected to device */ + volatile bool eventwait; /* True: Thread is waiting for a port event */ + sem_t exclsem; /* Support mutually exclusive access */ + sem_t eventsem; /* Semaphore to wait for a port event */ + + /* The state of each host channel */ + + struct efm32_chan_s chan[EFM32_MAX_TX_FIFOS]; +}; + +/******************************************************************************* + * Private Function Prototypes + *******************************************************************************/ + +/* Register operations ********************************************************/ + +#ifdef CONFIG_EFM32_USBHOST_REGDEBUG +static void efm32_printreg(uint32_t addr, uint32_t val, bool iswrite); +static void efm32_checkreg(uint32_t addr, uint32_t val, bool iswrite); +static uint32_t efm32_getreg(uint32_t addr); +static void efm32_putreg(uint32_t addr, uint32_t value); +#else +# define efm32_getreg(addr) getreg32(addr) +# define efm32_putreg(addr,val) putreg32(val,addr) +#endif + +static inline void efm32_modifyreg(uint32_t addr, uint32_t clrbits, + uint32_t setbits); + +#ifdef CONFIG_EFM32_USBHOST_PKTDUMP +# define efm32_pktdump(m,b,n) lib_dumpbuffer(m,b,n) +#else +# define efm32_pktdump(m,b,n) +#endif + +/* Semaphores ******************************************************************/ + +static void efm32_takesem(sem_t *sem); +#define efm32_givesem(s) sem_post(s); + +/* Byte stream access helper functions *****************************************/ + +static inline uint16_t efm32_getle16(const uint8_t *val); + +/* Channel management **********************************************************/ + +static int efm32_chan_alloc(FAR struct efm32_usbhost_s *priv); +static inline void efm32_chan_free(FAR struct efm32_usbhost_s *priv, int chidx); +static inline void efm32_chan_freeall(FAR struct efm32_usbhost_s *priv); +static void efm32_chan_configure(FAR struct efm32_usbhost_s *priv, int chidx); +static void efm32_chan_halt(FAR struct efm32_usbhost_s *priv, int chidx, + enum efm32_chreason_e chreason); +static int efm32_chan_waitsetup(FAR struct efm32_usbhost_s *priv, + FAR struct efm32_chan_s *chan); +static int efm32_chan_wait(FAR struct efm32_usbhost_s *priv, + FAR struct efm32_chan_s *chan); +static void efm32_chan_wakeup(FAR struct efm32_usbhost_s *priv, + FAR struct efm32_chan_s *chan); + +/* Control/data transfer logic *************************************************/ + +static void efm32_transfer_start(FAR struct efm32_usbhost_s *priv, int chidx); +#if 0 /* Not used */ +static inline uint16_t efm32_getframe(void); +#endif +static int efm32_ctrl_sendsetup(FAR struct efm32_usbhost_s *priv, + FAR const struct usb_ctrlreq_s *req); +static int efm32_ctrl_senddata(FAR struct efm32_usbhost_s *priv, + FAR uint8_t *buffer, unsigned int buflen); +static int efm32_ctrl_recvdata(FAR struct efm32_usbhost_s *priv, + FAR uint8_t *buffer, unsigned int buflen); +static int efm32_in_transfer(FAR struct efm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen); +static int efm32_out_transfer(FAR struct efm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen); + +/* Interrupt handling **********************************************************/ +/* Lower level interrupt handlers */ + +static void efm32_gint_wrpacket(FAR struct efm32_usbhost_s *priv, + FAR uint8_t *buffer, int chidx, int buflen); +static inline void efm32_gint_hcinisr(FAR struct efm32_usbhost_s *priv, + int chidx); +static inline void efm32_gint_hcoutisr(FAR struct efm32_usbhost_s *priv, + int chidx); +static void efm32_gint_connected(FAR struct efm32_usbhost_s *priv); +static void efm32_gint_disconnected(FAR struct efm32_usbhost_s *priv); + +/* Second level interrupt handlers */ + +#ifdef CONFIG_EFM32_OTGFS_SOFINTR +static inline void efm32_gint_sofisr(FAR struct efm32_usbhost_s *priv); +#endif +static inline void efm32_gint_rxflvlisr(FAR struct efm32_usbhost_s *priv); +static inline void efm32_gint_nptxfeisr(FAR struct efm32_usbhost_s *priv); +static inline void efm32_gint_ptxfeisr(FAR struct efm32_usbhost_s *priv); +static inline void efm32_gint_hcisr(FAR struct efm32_usbhost_s *priv); +static inline void efm32_gint_hprtisr(FAR struct efm32_usbhost_s *priv); +static inline void efm32_gint_discisr(FAR struct efm32_usbhost_s *priv); +static inline void efm32_gint_ipxfrisr(FAR struct efm32_usbhost_s *priv); + +/* First level, global interrupt handler */ + +static int efm32_gint_isr(int irq, FAR void *context); + +/* Interrupt controls */ + +static void efm32_gint_enable(void); +static void efm32_gint_disable(void); +static inline void efm32_hostinit_enable(void); +static void efm32_txfe_enable(FAR struct efm32_usbhost_s *priv, int chidx); + +/* USB host controller operations **********************************************/ + +static int efm32_wait(FAR struct usbhost_connection_s *conn, + FAR const bool *connected); +static int efm32_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx); + +static int efm32_ep0configure(FAR struct usbhost_driver_s *drvr, uint8_t funcaddr, + uint16_t maxpacketsize); +static int efm32_getdevinfo(FAR struct usbhost_driver_s *drvr, + FAR struct usbhost_devinfo_s *devinfo); +static int efm32_epalloc(FAR struct usbhost_driver_s *drvr, + FAR const FAR struct usbhost_epdesc_s *epdesc, + FAR usbhost_ep_t *ep); +static int efm32_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep); +static int efm32_alloc(FAR struct usbhost_driver_s *drvr, + FAR uint8_t **buffer, FAR size_t *maxlen); +static int efm32_free(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer); +static int efm32_ioalloc(FAR struct usbhost_driver_s *drvr, + FAR uint8_t **buffer, size_t buflen); +static int efm32_iofree(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer); +static int efm32_ctrlin(FAR struct usbhost_driver_s *drvr, + FAR const struct usb_ctrlreq_s *req, + FAR uint8_t *buffer); +static int efm32_ctrlout(FAR struct usbhost_driver_s *drvr, + FAR const struct usb_ctrlreq_s *req, + FAR const uint8_t *buffer); +static int efm32_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, + FAR uint8_t *buffer, size_t buflen); +static void efm32_disconnect(FAR struct usbhost_driver_s *drvr); + +/* Initialization **************************************************************/ + +static void efm32_portreset(FAR struct efm32_usbhost_s *priv); +static void efm32_flush_txfifos(uint32_t txfnum); +static void efm32_flush_rxfifo(void); +static void efm32_vbusdrive(FAR struct efm32_usbhost_s *priv, bool state); +static void efm32_host_initialize(FAR struct efm32_usbhost_s *priv); + +static inline void efm32_sw_initialize(FAR struct efm32_usbhost_s *priv); +static inline int efm32_hw_initialize(FAR struct efm32_usbhost_s *priv); + +/******************************************************************************* + * Private Data + *******************************************************************************/ + +/* In this driver implementation, support is provided for only a single a single + * USB device. All status information can be simply retained in a single global + * instance. + */ + +static struct efm32_usbhost_s g_usbhost = +{ + .drvr = + { + .ep0configure = efm32_ep0configure, + .getdevinfo = efm32_getdevinfo, + .epalloc = efm32_epalloc, + .epfree = efm32_epfree, + .alloc = efm32_alloc, + .free = efm32_free, + .ioalloc = efm32_ioalloc, + .iofree = efm32_iofree, + .ctrlin = efm32_ctrlin, + .ctrlout = efm32_ctrlout, + .transfer = efm32_transfer, + .disconnect = efm32_disconnect, + }, + .class = NULL, +}; + +/* This is the connection/enumeration interface */ + +static struct usbhost_connection_s g_usbconn = +{ + .wait = efm32_wait, + .enumerate = efm32_enumerate, +}; + +/******************************************************************************* + * Public Data + *******************************************************************************/ + +/******************************************************************************* + * Private Functions + *******************************************************************************/ + +/******************************************************************************* + * Name: efm32_printreg + * + * Description: + * Print the contents of an EFM32xx register operation + * + *******************************************************************************/ + +#ifdef CONFIG_EFM32_USBHOST_REGDEBUG +static void efm32_printreg(uint32_t addr, uint32_t val, bool iswrite) +{ + lldbg("%08x%s%08x\n", addr, iswrite ? "<-" : "->", val); +} +#endif + +/******************************************************************************* + * Name: efm32_checkreg + * + * Description: + * Get the contents of an EFM32 register + * + *******************************************************************************/ + +#ifdef CONFIG_EFM32_USBHOST_REGDEBUG +static void efm32_checkreg(uint32_t addr, uint32_t val, bool iswrite) +{ + static uint32_t prevaddr = 0; + static uint32_t preval = 0; + static uint32_t count = 0; + static bool prevwrite = false; + + /* Is this the same value that we read from/wrote to the same register last time? + * Are we polling the register? If so, suppress the output. + */ + + if (addr == prevaddr && val == preval && prevwrite == iswrite) + { + /* Yes.. Just increment the count */ + + count++; + } + else + { + /* No this is a new address or value or operation. Were there any + * duplicate accesses before this one? + */ + + if (count > 0) + { + /* Yes.. Just one? */ + + if (count == 1) + { + /* Yes.. Just one */ + + efm32_printreg(prevaddr, preval, prevwrite); + } + else + { + /* No.. More than one. */ + + lldbg("[repeats %d more times]\n", count); + } + } + + /* Save the new address, value, count, and operation for next time */ + + prevaddr = addr; + preval = val; + count = 0; + prevwrite = iswrite; + + /* Show the new regisgter access */ + + efm32_printreg(addr, val, iswrite); + } +} +#endif + +/******************************************************************************* + * Name: efm32_getreg + * + * Description: + * Get the contents of an EFM32 register + * + *******************************************************************************/ + +#ifdef CONFIG_EFM32_USBHOST_REGDEBUG +static uint32_t efm32_getreg(uint32_t addr) +{ + /* Read the value from the register */ + + uint32_t val = getreg32(addr); + + /* Check if we need to print this value */ + + efm32_checkreg(addr, val, false); + return val; +} +#endif + +/******************************************************************************* + * Name: efm32_putreg + * + * Description: + * Set the contents of an EFM32 register to a value + * + *******************************************************************************/ + +#ifdef CONFIG_EFM32_USBHOST_REGDEBUG +static void efm32_putreg(uint32_t addr, uint32_t val) +{ + /* Check if we need to print this value */ + + efm32_checkreg(addr, val, true); + + /* Write the value */ + + putreg32(val, addr); +} +#endif + +/******************************************************************************* + * Name: efm32_modifyreg + * + * Description: + * Modify selected bits of an EFM32 register. + * + *******************************************************************************/ + +static inline void efm32_modifyreg(uint32_t addr, uint32_t clrbits, uint32_t setbits) +{ + efm32_putreg(addr, (((efm32_getreg(addr)) & ~clrbits) | setbits)); +} + +/**************************************************************************** + * Name: efm32_takesem + * + * Description: + * This is just a wrapper to handle the annoying behavior of semaphore + * waits that return due to the receipt of a signal. + * + *******************************************************************************/ + +static void efm32_takesem(sem_t *sem) +{ + /* Take the semaphore (perhaps waiting) */ + + while (sem_wait(sem) != 0) + { + /* The only case that an error should occr here is if the wait was + * awakened by a signal. + */ + + ASSERT(errno == EINTR); + } +} + +/**************************************************************************** + * Name: efm32_getle16 + * + * Description: + * Get a (possibly unaligned) 16-bit little endian value. + * + *******************************************************************************/ + +static inline uint16_t efm32_getle16(const uint8_t *val) +{ + return (uint16_t)val[1] << 8 | (uint16_t)val[0]; +} + +/******************************************************************************* + * Name: efm32_chan_alloc + * + * Description: + * Allocate a channel. + * + *******************************************************************************/ + +static int efm32_chan_alloc(FAR struct efm32_usbhost_s *priv) +{ + int chidx; + + /* Search the table of channels */ + + for (chidx = 0; chidx < EFM32_NHOST_CHANNELS; chidx++) + { + /* Is this channel available? */ + + if (!priv->chan[chidx].inuse) + { + /* Yes... make it "in use" and return the index */ + + priv->chan[chidx].inuse = true; + return chidx; + } + } + + /* All of the channels are "in-use" */ + + return -EBUSY; +} + +/******************************************************************************* + * Name: efm32_chan_free + * + * Description: + * Free a previoiusly allocated channel. + * + *******************************************************************************/ + +static void efm32_chan_free(FAR struct efm32_usbhost_s *priv, int chidx) +{ + DEBUGASSERT((unsigned)chidx < EFM32_NHOST_CHANNELS); + + /* Halt the channel */ + + efm32_chan_halt(priv, chidx, CHREASON_FREED); + + /* Mark the channel available */ + + priv->chan[chidx].inuse = false; +} + +/******************************************************************************* + * Name: efm32_chan_freeall + * + * Description: + * Free all channels. + * + *******************************************************************************/ + +static inline void efm32_chan_freeall(FAR struct efm32_usbhost_s *priv) +{ + uint8_t chidx; + + /* Free all host channels */ + + for (chidx = 2; chidx < EFM32_NHOST_CHANNELS; chidx ++) + { + efm32_chan_free(priv, chidx); + } +} + +/******************************************************************************* + * Name: efm32_chan_configure + * + * Description: + * Configure or re-configure a host channel. Host channels are configured + * when endpoint is allocated and EP0 (only) is re-configured with the + * max packet size or device address changes. + * + *******************************************************************************/ + +static void efm32_chan_configure(FAR struct efm32_usbhost_s *priv, int chidx) +{ + uint32_t regval; + + /* Clear any old pending interrupts for this host channel. */ + + efm32_putreg(EFM32_USB_HCINT(chidx), 0xffffffff); + + /* Enable channel interrupts required for transfers on this channel. */ + + regval = 0; + + switch (priv->chan[chidx].eptype) + { + case OTGFS_EPTYPE_CTRL: + case OTGFS_EPTYPE_BULK: + { +#ifdef HAVE_USBHOST_TRACE_VERBOSE + uint16_t intrace; + uint16_t outtrace; + + /* Determine the definitive trace ID to use below */ + + if (priv->chan[chidx].eptype == OTGFS_EPTYPE_CTRL) + { + intrace = OTGFS_VTRACE2_CHANCONF_CTRL_IN; + outtrace = OTGFS_VTRACE2_CHANCONF_CTRL_OUT; + } + else + { + intrace = OTGFS_VTRACE2_CHANCONF_BULK_IN; + outtrace = OTGFS_VTRACE2_CHANCONF_BULK_OUT; + } +#endif + + /* Interrupts required for CTRL and BULK endpoints */ + + regval |= (OTGFS_HCINT_XFRC | OTGFS_HCINT_STALL | OTGFS_HCINT_NAK | + OTGFS_HCINT_TXERR | OTGFS_HCINT_DTERR); + + /* Additional setting for IN/OUT endpoints */ + + if (priv->chan[chidx].in) + { + usbhost_vtrace2(intrace, chidx, priv->chan[chidx].epno); + regval |= OTGFS_HCINT_BBERR; + } + else + { + usbhost_vtrace2(outtrace, chidx, priv->chan[chidx].epno); + regval |= OTGFS_HCINT_NYET; + } + } + break; + + case OTGFS_EPTYPE_INTR: + { + /* Interrupts required for INTR endpoints */ + + regval |= (OTGFS_HCINT_XFRC | OTGFS_HCINT_STALL | OTGFS_HCINT_NAK | + OTGFS_HCINT_TXERR | OTGFS_HCINT_FRMOR | OTGFS_HCINT_DTERR); + + /* Additional setting for IN endpoints */ + + if (priv->chan[chidx].in) + { + usbhost_vtrace2(OTGFS_VTRACE2_CHANCONF_INTR_IN, chidx, + priv->chan[chidx].epno); + regval |= OTGFS_HCINT_BBERR; + } +#ifdef HAVE_USBHOST_TRACE_VERBOSE + else + { + usbhost_vtrace2(OTGFS_VTRACE2_CHANCONF_INTR_OUT, chidx, + priv->chan[chidx].epno); + } +#endif + } + break; + + case OTGFS_EPTYPE_ISOC: + { + /* Interrupts required for ISOC endpoints */ + + regval |= (OTGFS_HCINT_XFRC | OTGFS_HCINT_ACK | OTGFS_HCINT_FRMOR); + + /* Additional setting for IN endpoints */ + + if (priv->chan[chidx].in) + { + usbhost_vtrace2(OTGFS_VTRACE2_CHANCONF_ISOC_IN, chidx, + priv->chan[chidx].epno); + regval |= (OTGFS_HCINT_TXERR | OTGFS_HCINT_BBERR); + } +#ifdef HAVE_USBHOST_TRACE_VERBOSE + else + { + usbhost_vtrace2(OTGFS_VTRACE2_CHANCONF_ISOC_OUT, chidx, + priv->chan[chidx].epno); + } +#endif + } + break; + } + + efm32_putreg(EFM32_USB_HCINTMSK(chidx), regval); + + /* Enable the top level host channel interrupt. */ + + efm32_modifyreg(EFM32_USB_HAINTMSK, 0, OTGFS_HAINT(chidx)); + + /* Make sure host channel interrupts are enabled. */ + + efm32_modifyreg(EFM32_USB_GINTMSK, 0, USB_GINT_HC); + + /* Program the HCCHAR register */ + + regval = ((uint32_t)priv->chan[chidx].maxpacket << _OTGFS_HCCHAR_MPSIZ_SHIFT) | + ((uint32_t)priv->chan[chidx].epno << _OTGFS_HCCHAR_EPNUM_SHIFT) | + ((uint32_t)priv->chan[chidx].eptype << _OTGFS_HCCHAR_EPTYP_SHIFT) | + ((uint32_t)priv->devaddr << _OTGFS_HCCHAR_DAD_SHIFT); + + /* Special case settings for low speed devices */ + + if (priv->lowspeed) + { + regval |= OTGFS_HCCHAR_LSDEV; + } + + /* Special case settings for IN endpoints */ + + if (priv->chan[chidx].in) + { + regval |= OTGFS_HCCHAR_EPDIR_IN; + } + + /* Special case settings for INTR endpoints */ + + if (priv->chan[chidx].eptype == OTGFS_EPTYPE_INTR) + { + regval |= OTGFS_HCCHAR_ODDFRM; + } + + /* Write the channel configuration */ + + efm32_putreg(EFM32_USB_HCCHAR(chidx), regval); +} + +/******************************************************************************* + * Name: efm32_chan_halt + * + * Description: + * Halt the channel associated with 'chidx' by setting the CHannel DISable + * (CHDIS) bit in in the HCCHAR register. + * + *******************************************************************************/ + +static void efm32_chan_halt(FAR struct efm32_usbhost_s *priv, int chidx, + enum efm32_chreason_e chreason) +{ + uint32_t hcchar; + uint32_t intmsk; + uint32_t eptype; + unsigned int avail; + + /* Save the reason for the halt. We need this in the channel halt interrrupt + * handling logic to know what to do next. + */ + + usbhost_vtrace2(OTGFS_VTRACE2_CHANHALT, chidx, chreason); + + priv->chan[chidx].chreason = (uint8_t)chreason; + + /* "The application can disable any channel by programming the OTG_FS_HCCHARx + * register with the CHDIS and CHENA bits set to 1. This enables the OTG_FS + * host to flush the posted requests (if any) and generates a channel halted + * interrupt. The application must wait for the CHH interrupt in OTG_FS_HCINTx + * before reallocating the channel for other transactions. The OTG_FS host + * does not interrupt the transaction that has already been started on the + * USB." + */ + + hcchar = efm32_getreg(EFM32_USB_HCCHAR(chidx)); + hcchar |= (OTGFS_HCCHAR_CHDIS | OTGFS_HCCHAR_CHENA); + + /* Get the endpoint type from the HCCHAR register */ + + eptype = hcchar & _OTGFS_HCCHAR_EPTYP_MASK; + + /* Check for space in the Tx FIFO to issue the halt. + * + * "Before disabling a channel, the application must ensure that there is at + * least one free space available in the non-periodic request queue (when + * disabling a non-periodic channel) or the periodic request queue (when + * disabling a periodic channel). The application can simply flush the + * posted requests when the Request queue is full (before disabling the + * channel), by programming the OTG_FS_HCCHARx register with the CHDIS bit + * set to 1, and the CHENA bit cleared to 0. + */ + + if (eptype == OTGFS_HCCHAR_EPTYP_CTRL || eptype == OTGFS_HCCHAR_EPTYP_BULK) + { + /* Get the number of words available in the non-periodic Tx FIFO. */ + + avail = efm32_getreg(EFM32_USB_HNPTXSTS) & _OTGFS_HNPTXSTS_NPTXFSAV_MASK; + } + else /* if (eptype == OTGFS_HCCHAR_EPTYP_ISOC || eptype == OTGFS_HCCHAR_EPTYP_INTR) */ + { + /* Get the number of words available in the non-periodic Tx FIFO. */ + + avail = efm32_getreg(EFM32_USB_HPTXSTS) & _OTGFS_HPTXSTS_PTXFSAVL_MASK; + } + + /* Check if there is any space available in the Tx FIFO. */ + + if (avail == 0) + { + /* The Tx FIFO is full... disable the channel to flush the requests */ + + hcchar &= ~OTGFS_HCCHAR_CHENA; + } + + /* Unmask the CHannel Halted (CHH) interrupt */ + + intmsk = efm32_getreg(EFM32_USB_HCINTMSK(chidx)); + intmsk |= OTGFS_HCINT_CHH; + efm32_putreg(EFM32_USB_HCINTMSK(chidx), intmsk); + + /* Halt the channel by setting CHDIS (and maybe CHENA) in the HCCHAR */ + + efm32_putreg(EFM32_USB_HCCHAR(chidx), hcchar); +} + +/******************************************************************************* + * Name: efm32_chan_waitsetup + * + * Description: + * Set the request for the transfer complete event well BEFORE enabling the + * transfer (as soon as we are absolutely committed to the to avoid transfer). + * We do this to minimize race conditions. This logic would have to be expanded + * if we want to have more than one packet in flight at a time! + * + * Assumptions: + * Called from a normal thread context BEFORE the transfer has been started. + * + *******************************************************************************/ + +static int efm32_chan_waitsetup(FAR struct efm32_usbhost_s *priv, + FAR struct efm32_chan_s *chan) +{ + irqstate_t flags = irqsave(); + int ret = -ENODEV; + + /* Is the device still connected? */ + + if (priv->connected) + { + /* Yes.. then set waiter to indicate that we expect to be informed when + * either (1) the device is disconnected, or (2) the transfer completed. + */ + + chan->waiter = true; + ret = OK; + } + + irqrestore(flags); + return ret; +} + +/******************************************************************************* + * Name: efm32_chan_wait + * + * Description: + * Wait for a transfer on a channel to complete. + * + * Assumptions: + * Called from a normal thread context + * + *******************************************************************************/ + +static int efm32_chan_wait(FAR struct efm32_usbhost_s *priv, + FAR struct efm32_chan_s *chan) +{ + irqstate_t flags; + int ret; + + /* Disable interrupts so that the following operations will be atomic. On + * the OTG FS global interrupt needs to be disabled. However, here we disable + * all interrupts to exploit that fact that interrupts will be re-enabled + * while we wait. + */ + + flags = irqsave(); + + /* Loop, testing for an end of transfer conditino. The channel 'result' + * was set to EBUSY and 'waiter' was set to true before the transfer; 'waiter' + * will be set to false and 'result' will be set appropriately when the + * tranfer is completed. + */ + + do + { + /* Wait for the transfer to complete. NOTE the transfer may already + * completed before we get here or the transfer may complete while we + * wait here. + */ + + ret = sem_wait(&chan->waitsem); + + /* sem_wait should succeeed. But it is possible that we could be + * awakened by a signal too. + */ + + DEBUGASSERT(ret == OK || get_errno() == EINTR); + } + while (chan->waiter); + + /* The transfer is complete re-enable interrupts and return the result */ + + ret = -(int)chan->result; + irqrestore(flags); + return ret; +} + +/******************************************************************************* + * Name: efm32_chan_wakeup + * + * Description: + * A channel transfer has completed... wakeup any threads waiting for the + * transfer to complete. + * + * Assumptions: + * This function is called from the transfer complete interrupt handler for + * the channel. Interrupts are disabled. + * + *******************************************************************************/ + +static void efm32_chan_wakeup(FAR struct efm32_usbhost_s *priv, + FAR struct efm32_chan_s *chan) +{ + /* Is the transfer complete? Is there a thread waiting for this transfer + * to complete? + */ + + if (chan->result != EBUSY && chan->waiter) + { + usbhost_vtrace2(chan->in ? OTGFS_VTRACE2_CHANWAKEUP_IN : + OTGFS_VTRACE2_CHANWAKEUP_OUT, + chan->epno, chan->result); + + efm32_givesem(&chan->waitsem); + chan->waiter = false; + } +} + +/******************************************************************************* + * Name: efm32_transfer_start + * + * Description: + * Start at transfer on the select IN or OUT channel. + * + *******************************************************************************/ + +static void efm32_transfer_start(FAR struct efm32_usbhost_s *priv, int chidx) +{ + FAR struct efm32_chan_s *chan; + uint32_t regval; + unsigned int npackets; + unsigned int maxpacket; + unsigned int avail; + unsigned int wrsize; + unsigned int minsize; + + /* Set up the initial state of the transfer */ + + chan = &priv->chan[chidx]; + + usbhost_vtrace2(OTGFS_VTRACE2_STARTTRANSFER, chidx, chan->buflen); + + chan->result = EBUSY; + chan->inflight = 0; + priv->chidx = chidx; + + /* Compute the expected number of packets associated to the transfer. + * If the transfer length is zero (or less than the size of one maximum + * size packet), then one packet is expected. + */ + + /* If the transfer size is greater than one packet, then calculate the + * number of packets that will be received/sent, including any partial + * final packet. + */ + + maxpacket = chan->maxpacket; + + if (chan->buflen > maxpacket) + { + npackets = (chan->buflen + maxpacket - 1) / maxpacket; + + /* Clip if the buffer length if it exceeds the maximum number of + * packets that can be transferred (this should not happen). + */ + + if (npackets > EFM32_MAX_PKTCOUNT) + { + npackets = EFM32_MAX_PKTCOUNT; + chan->buflen = EFM32_MAX_PKTCOUNT * maxpacket; + usbhost_trace2(OTGFS_TRACE2_CLIP, chidx, chan->buflen); + } + } + else + { + /* One packet will be sent/received (might be a zero length packet) */ + + npackets = 1; + } + + /* If it is an IN transfer, then adjust the size of the buffer UP to + * a full number of packets. Hmmm... couldn't this cause an overrun + * into unallocated memory? + */ + +#if 0 /* Think about this */ + if (chan->in) + { + /* Force the buffer length to an even multiple of maxpacket */ + + chan->buflen = npackets * maxpacket; + } +#endif + + /* Save the number of packets in the transfer. We will need this in + * order to set the next data toggle correctly when the transfer + * completes. + */ + + chan->npackets = (uint8_t)npackets; + + /* Setup the HCTSIZn register */ + + regval = ((uint32_t)chan->buflen << _OTGFS_HCTSIZ_XFRSIZ_SHIFT) | + ((uint32_t)npackets << _OTGFS_HCTSIZ_PKTCNT_SHIFT) | + ((uint32_t)chan->pid << _OTGFS_HCTSIZ_DPID_SHIFT); + efm32_putreg(EFM32_USB_HCTSIZ(chidx), regval); + + /* Setup the HCCHAR register: Frame oddness and host channel enable */ + + regval = efm32_getreg(EFM32_USB_HCCHAR(chidx)); + + /* Set/clear the Odd Frame bit. Check for an even frame; if so set Odd + * Frame. This field is applicable for only periodic (isochronous and + * interrupt) channels. + */ + + if ((efm32_getreg(EFM32_USB_HFNUM) & 1) == 0) + { + regval |= OTGFS_HCCHAR_ODDFRM; + } + else + { + regval &= ~OTGFS_HCCHAR_ODDFRM; + } + + regval &= ~OTGFS_HCCHAR_CHDIS; + regval |= OTGFS_HCCHAR_CHENA; + efm32_putreg(EFM32_USB_HCCHAR(chidx), regval); + + /* If this is an out transfer, then we need to do more.. we need to copy + * the outgoing data into the correct TxFIFO. + */ + + if (!chan->in && chan->buflen > 0) + { + /* Handle non-periodic (CTRL and BULK) OUT transfers differently than + * periodic (INTR and ISOC) OUT transfers. + */ + + minsize = MIN(chan->buflen, chan->maxpacket); + + switch (chan->eptype) + { + case OTGFS_EPTYPE_CTRL: /* Non periodic transfer */ + case OTGFS_EPTYPE_BULK: + { + /* Read the Non-periodic Tx FIFO status register */ + + regval = efm32_getreg(EFM32_USB_HNPTXSTS); + avail = ((regval & _OTGFS_HNPTXSTS_NPTXFSAV_MASK) >> _OTGFS_HNPTXSTS_NPTXFSAV_SHIFT) << 2; + } + break; + + /* Periodic transfer */ + + case OTGFS_EPTYPE_INTR: + case OTGFS_EPTYPE_ISOC: + { + /* Read the Non-periodic Tx FIFO status register */ + + regval = efm32_getreg(EFM32_USB_HPTXSTS); + avail = ((regval & _OTGFS_HPTXSTS_PTXFSAVL_MASK) >> _OTGFS_HPTXSTS_PTXFSAVL_SHIFT) << 2; + } + break; + + default: + DEBUGASSERT(false); + return; + } + + /* Is there space in the TxFIFO to hold the minimum size packet? */ + + if (minsize <= avail) + { + /* Yes.. Get the size of the biggest thing that we can put in the Tx FIFO now */ + + wrsize = chan->buflen; + if (wrsize > avail) + { + /* Clip the write size to the number of full, max sized packets + * that will fit in the Tx FIFO. + */ + + unsigned int wrpackets = avail / chan->maxpacket; + wrsize = wrpackets * chan->maxpacket; + } + + /* Write packet into the Tx FIFO. */ + + efm32_gint_wrpacket(priv, chan->buffer, chidx, wrsize); + } + + /* Did we put the entire buffer into the Tx FIFO? */ + + if (chan->buflen > avail) + { + /* No, there was insufficient space to hold the entire transfer ... + * Enable the Tx FIFO interrupt to handle the transfer when the Tx + * FIFO becomes empty. + */ + + efm32_txfe_enable(priv, chidx); + } + } +} + +/******************************************************************************* + * Name: efm32_getframe + * + * Description: + * Get the current frame number. The frame number (FRNUM) field increments + * when a new SOF is transmitted on the USB, and is cleared to 0 when it + * reaches 0x3fff. + * + *******************************************************************************/ + +#if 0 /* Not used */ +static inline uint16_t efm32_getframe(void) +{ + return (uint16_t)(efm32_getreg(EFM32_USB_HFNUM) & _OTGFS_HFNUM_FRNUM_MASK); +} +#endif + +/******************************************************************************* + * Name: efm32_ctrl_sendsetup + * + * Description: + * Send an IN/OUT SETUP packet. + * + *******************************************************************************/ + +static int efm32_ctrl_sendsetup(FAR struct efm32_usbhost_s *priv, + FAR const struct usb_ctrlreq_s *req) +{ + FAR struct efm32_chan_s *chan; + uint32_t start; + uint32_t elapsed; + int ret; + + /* Loop while the device reports NAK (and a timeout is not exceeded */ + + chan = &priv->chan[priv->ep0out]; + start = clock_systimer(); + + do + { + /* Send the SETUP packet */ + + chan->pid = OTGFS_PID_SETUP; + chan->buffer = (FAR uint8_t *)req; + chan->buflen = USB_SIZEOF_CTRLREQ; + + /* Set up for the wait BEFORE starting the transfer */ + + ret = efm32_chan_waitsetup(priv, chan); + if (ret != OK) + { + usbhost_trace1(OTGFS_TRACE1_DEVDISCONN, 0); + return ret; + } + + /* Start the transfer */ + + efm32_transfer_start(priv, priv->ep0out); + + /* Wait for the transfer to complete */ + + ret = efm32_chan_wait(priv, chan); + + /* Return on success and for all failures other than EAGAIN. EAGAIN + * means that the device NAKed the SETUP command and that we should + * try a few more times. + */ + + if (ret != -EAGAIN) + { + /* Output some debug information if the transfer failed */ + + if (ret < 0) + { + usbhost_trace1(OTGFS_TRACE1_TRNSFRFAILED, ret); + } + + /* Return the result in any event */ + + return ret; + } + + /* Get the elapsed time (in frames) */ + + elapsed = clock_systimer() - start; + } + while (elapsed < EFM32_SETUP_DELAY); + + return -ETIMEDOUT; +} + +/******************************************************************************* + * Name: efm32_ctrl_senddata + * + * Description: + * Send data in the data phase of an OUT control transfer. Or send status + * in the status phase of an IN control transfer + * + *******************************************************************************/ + +static int efm32_ctrl_senddata(FAR struct efm32_usbhost_s *priv, + FAR uint8_t *buffer, unsigned int buflen) +{ + FAR struct efm32_chan_s *chan = &priv->chan[priv->ep0out]; + int ret; + + /* Save buffer information */ + + chan->buffer = buffer; + chan->buflen = buflen; + + /* Set the DATA PID */ + + if (buflen == 0) + { + /* For status OUT stage with buflen == 0, set PID DATA1 */ + + chan->outdata1 = true; + } + + /* Set the Data PID as per the outdata1 boolean */ + + chan->pid = chan->outdata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0; + + /* Set up for the wait BEFORE starting the transfer */ + + ret = efm32_chan_waitsetup(priv, chan); + if (ret != OK) + { + usbhost_trace1(OTGFS_TRACE1_DEVDISCONN, 0); + return ret; + } + + /* Start the transfer */ + + efm32_transfer_start(priv, priv->ep0out); + + /* Wait for the transfer to complete and return the result */ + + return efm32_chan_wait(priv, chan); +} + +/******************************************************************************* + * Name: efm32_ctrl_recvdata + * + * Description: + * Receive data in the data phase of an IN control transfer. Or receive status + * in the status phase of an OUT control transfer + * + *******************************************************************************/ + +static int efm32_ctrl_recvdata(FAR struct efm32_usbhost_s *priv, + FAR uint8_t *buffer, unsigned int buflen) +{ + FAR struct efm32_chan_s *chan = &priv->chan[priv->ep0in]; + int ret; + + /* Save buffer information */ + + chan->pid = OTGFS_PID_DATA1; + chan->buffer = buffer; + chan->buflen = buflen; + + /* Set up for the wait BEFORE starting the transfer */ + + ret = efm32_chan_waitsetup(priv, chan); + if (ret != OK) + { + usbhost_trace1(OTGFS_TRACE1_DEVDISCONN, 0); + return ret; + } + + /* Start the transfer */ + + efm32_transfer_start(priv, priv->ep0in); + + /* Wait for the transfer to complete and return the result */ + + return efm32_chan_wait(priv, chan); +} + +/******************************************************************************* + * Name: efm32_in_transfer + * + * Description: + * Transfer 'buflen' bytes into 'buffer' from an IN channel. + * + *******************************************************************************/ + +static int efm32_in_transfer(FAR struct efm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen) +{ + FAR struct efm32_chan_s *chan; + uint32_t start; + uint32_t elapsed; + int ret = OK; + + /* Loop until the transfer completes (i.e., buflen is decremented to zero) + * or a fatal error occurs (any error other than a simple NAK) + */ + + chan = &priv->chan[chidx]; + chan->buffer = buffer; + chan->buflen = buflen; + + start = clock_systimer(); + while (chan->buflen > 0) + { + /* Set up for the wait BEFORE starting the transfer */ + + ret = efm32_chan_waitsetup(priv, chan); + if (ret != OK) + { + usbhost_trace1(OTGFS_TRACE1_DEVDISCONN, 0); + return ret; + } + + /* Set up for the transfer based on the direction and the endpoint type */ + + switch (chan->eptype) + { + default: + case OTGFS_EPTYPE_CTRL: /* Control */ + { + /* This kind of transfer on control endpoints other than EP0 are not + * currently supported + */ + + return -ENOSYS; + } + + case OTGFS_EPTYPE_ISOC: /* Isochronous */ + { + /* Set up the IN data PID */ + + usbhost_vtrace2(OTGFS_VTRACE2_ISOCIN, chidx, buflen); + chan->pid = OTGFS_PID_DATA0; + } + break; + + case OTGFS_EPTYPE_BULK: /* Bulk */ + { + /* Setup the IN data PID */ + + usbhost_vtrace2(OTGFS_VTRACE2_BULKIN, chidx, buflen); + chan->pid = chan->indata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0; + } + break; + + case OTGFS_EPTYPE_INTR: /* Interrupt */ + { + /* Setup the IN data PID */ + + usbhost_vtrace2(OTGFS_VTRACE2_INTRIN, chidx, buflen); + chan->pid = chan->indata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0; + } + break; + } + + /* Start the transfer */ + + efm32_transfer_start(priv, chidx); + + /* Wait for the transfer to complete and get the result */ + + ret = efm32_chan_wait(priv, chan); + + /* EAGAIN indicates that the device NAKed the transfer and we need + * do try again. Anything else (success or other errors) will + * cause use to return + */ + + if (ret != OK) + { + usbhost_trace1(OTGFS_TRACE1_TRNSFRFAILED,ret); + + /* Check for a special case: If (1) the transfer was NAKed and (2) + * no Tx FIFO empty or Rx FIFO not-empty event occurred, then we + * should be able to just flush the Rx and Tx FIFOs and try again. + * We can detect this latter case becasue the then the transfer + * buffer pointer and buffer size will be unaltered. + */ + + elapsed = clock_systimer() - start; + if (ret != -EAGAIN || /* Not a NAK condition OR */ + elapsed >= EFM32_DATANAK_DELAY || /* Timeout has elapsed OR */ + chan->buflen != buflen) /* Data has been partially transferred */ + { + /* Break out and return the error */ + + break; + } + } + } + + return ret; +} + +/******************************************************************************* + * Name: efm32_out_transfer + * + * Description: + * Transfer the 'buflen' bytes in 'buffer' through an OUT channel. + * + *******************************************************************************/ + +static int efm32_out_transfer(FAR struct efm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen) +{ + FAR struct efm32_chan_s *chan; + uint32_t start; + uint32_t elapsed; + size_t xfrlen; + int ret = OK; + + /* Loop until the transfer completes (i.e., buflen is decremented to zero) + * or a fatal error occurs (any error other than a simple NAK) + */ + + chan = &priv->chan[chidx]; + start = clock_systimer(); + + while (buflen > 0) + { + /* Transfer one packet at a time. The hardware is capable of queueing + * multiple OUT packets, but I just haven't figured out how to handle + * the case where a single OUT packet in the group is NAKed. + */ + + xfrlen = MIN(chan->maxpacket, buflen); + chan->buffer = buffer; + chan->buflen = xfrlen; + + /* Set up for the wait BEFORE starting the transfer */ + + ret = efm32_chan_waitsetup(priv, chan); + if (ret != OK) + { + usbhost_trace1(OTGFS_TRACE1_DEVDISCONN,0); + return ret; + } + + /* Set up for the transfer based on the direction and the endpoint type */ + + switch (chan->eptype) + { + default: + case OTGFS_EPTYPE_CTRL: /* Control */ + { + /* This kind of transfer on control endpoints other than EP0 are not + * currently supported + */ + + return -ENOSYS; + } + + case OTGFS_EPTYPE_ISOC: /* Isochronous */ + { + /* Set up the OUT data PID */ + + usbhost_vtrace2(OTGFS_VTRACE2_ISOCOUT, chidx, buflen); + chan->pid = OTGFS_PID_DATA0; + } + break; + + case OTGFS_EPTYPE_BULK: /* Bulk */ + { + /* Setup the OUT data PID */ + + usbhost_vtrace2(OTGFS_VTRACE2_BULKOUT, chidx, buflen); + chan->pid = chan->outdata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0; + } + break; + + case OTGFS_EPTYPE_INTR: /* Interrupt */ + { + /* Setup the OUT data PID */ + + usbhost_vtrace2(OTGFS_VTRACE2_INTROUT, chidx, buflen); + chan->pid = chan->outdata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0; + + /* Toggle the OUT data PID for the next transfer */ + + chan->outdata1 ^= true; + } + break; + } + + /* Start the transfer */ + + efm32_transfer_start(priv, chidx); + + /* Wait for the transfer to complete and get the result */ + + ret = efm32_chan_wait(priv, chan); + + /* Handle transfer failures */ + + if (ret != OK) + { + usbhost_trace1(OTGFS_TRACE1_TRNSFRFAILED,ret); + + /* Check for a special case: If (1) the transfer was NAKed and (2) + * no Tx FIFO empty or Rx FIFO not-empty event occurred, then we + * should be able to just flush the Rx and Tx FIFOs and try again. + * We can detect this latter case becasue the then the transfer + * buffer pointer and buffer size will be unaltered. + */ + + elapsed = clock_systimer() - start; + if (ret != -EAGAIN || /* Not a NAK condition OR */ + elapsed >= EFM32_DATANAK_DELAY || /* Timeout has elapsed OR */ + chan->buflen != xfrlen) /* Data has been partially transferred */ + { + /* Break out and return the error */ + + break; + } + + /* Is this flush really necessary? What does the hardware do with the + * data in the FIFO when the NAK occurs? Does it discard it? + */ + + efm32_flush_txfifos(USB_GRSTCTL_TXFNUM_HALL); + + /* Get the device a little time to catch up. Then retry the transfer + * using the same buffer pointer and length. + */ + + usleep(20*1000); + } + else + { + /* Successfully transferred. Update the buffer pointer and length */ + + buffer += xfrlen; + buflen -= xfrlen; + } + } + + return ret; +} + +/******************************************************************************* + * Name: efm32_gint_wrpacket + * + * Description: + * Transfer the 'buflen' bytes in 'buffer' to the Tx FIFO associated with + * 'chidx' (non-DMA). + * + *******************************************************************************/ + +static void efm32_gint_wrpacket(FAR struct efm32_usbhost_s *priv, + FAR uint8_t *buffer, int chidx, int buflen) +{ + FAR uint32_t *src; + uint32_t fifo; + int buflen32; + + efm32_pktdump("Sending", buffer, buflen); + + /* Get the number of 32-byte words associated with this byte size */ + + buflen32 = (buflen + 3) >> 2; + + /* Get the address of the Tx FIFO associated with this channel */ + + fifo = EFM32_USB_DFIFO_HCH(chidx); + + /* Transfer all of the data into the Tx FIFO */ + + src = (FAR uint32_t *)buffer; + for (; buflen32 > 0; buflen32--) + { + uint32_t data = *src++; + efm32_putreg(fifo, data); + } + + /* Increment the count of bytes "in-flight" in the Tx FIFO */ + + priv->chan[chidx].inflight += buflen; +} + +/******************************************************************************* + * Name: efm32_gint_hcinisr + * + * Description: + * USB OTG FS host IN channels interrupt handler + * + * One the completion of the transfer, the channel result byte may be set as + * follows: + * + * OK - Transfer completed successfully + * EAGAIN - If devices NAKs the transfer or NYET occurs + * EPERM - If the endpoint stalls + * EIO - On a TX or data toggle error + * EPIPE - Frame overrun + * + * EBUSY in the result field indicates that the transfer has not completed. + * + *******************************************************************************/ + +static inline void efm32_gint_hcinisr(FAR struct efm32_usbhost_s *priv, + int chidx) +{ + FAR struct efm32_chan_s *chan = &priv->chan[chidx]; + uint32_t regval; + uint32_t pending; + + /* Read the HCINT register to get the pending HC interrupts. Read the + * HCINTMSK register to get the set of enabled HC interrupts. + */ + + pending = efm32_getreg(EFM32_USB_HCINT(chidx)); + regval = efm32_getreg(EFM32_USB_HCINTMSK(chidx)); + + /* AND the two to get the set of enabled, pending HC interrupts */ + + pending &= regval; + ullvdbg("HCINTMSK%d: %08x pending: %08x\n", chidx, regval, pending); + + /* Check for a pending ACK response received/transmitted (ACK) interrupt */ + + if ((pending & OTGFS_HCINT_ACK) != 0) + { + /* Clear the pending the ACK response received/transmitted (ACK) interrupt */ + + efm32_putreg(EFM32_USB_HCINT(chidx), OTGFS_HCINT_ACK); + } + + /* Check for a pending STALL response receive (STALL) interrupt */ + + else if ((pending & OTGFS_HCINT_STALL) != 0) + { + /* Clear the NAK and STALL Conditions. */ + + efm32_putreg(EFM32_USB_HCINT(chidx), (OTGFS_HCINT_NAK | OTGFS_HCINT_STALL)); + + /* Halt the channel when a STALL, TXERR, BBERR or DTERR interrupt is + * received on the channel. + */ + + efm32_chan_halt(priv, chidx, CHREASON_STALL); + + /* When there is a STALL, clear any pending NAK so that it is not + * processed below. + */ + + pending &= ~OTGFS_HCINT_NAK; + } + + /* Check for a pending Data Toggle ERRor (DTERR) interrupt */ + + else if ((pending & OTGFS_HCINT_DTERR) != 0) + { + /* Halt the channel when a STALL, TXERR, BBERR or DTERR interrupt is + * received on the channel. + */ + + efm32_chan_halt(priv, chidx, CHREASON_DTERR); + + /* Clear the NAK and data toggle error conditions */ + + efm32_putreg(EFM32_USB_HCINT(chidx), (OTGFS_HCINT_NAK | OTGFS_HCINT_DTERR)); + } + + /* Check for a pending FRaMe OverRun (FRMOR) interrupt */ + + if ((pending & OTGFS_HCINT_FRMOR) != 0) + { + /* Halt the channel -- the CHH interrrupt is expected next */ + + efm32_chan_halt(priv, chidx, CHREASON_FRMOR); + + /* Clear the FRaMe OverRun (FRMOR) condition */ + + efm32_putreg(EFM32_USB_HCINT(chidx), OTGFS_HCINT_FRMOR); + } + + /* Check for a pending TransFeR Completed (XFRC) interrupt */ + + else if ((pending & OTGFS_HCINT_XFRC) != 0) + { + /* Clear the TransFeR Completed (XFRC) condition */ + + efm32_putreg(EFM32_USB_HCINT(chidx), OTGFS_HCINT_XFRC); + + /* Then handle the transfer completion event based on the endpoint type */ + + if (chan->eptype == OTGFS_EPTYPE_CTRL || chan->eptype == OTGFS_EPTYPE_BULK) + { + /* Halt the channel -- the CHH interrrupt is expected next */ + + efm32_chan_halt(priv, chidx, CHREASON_XFRC); + + /* Clear any pending NAK condition. The 'indata1' data toggle + * should have been appropriately updated by the RxFIFO + * logic as each packet was received. + */ + + efm32_putreg(EFM32_USB_HCINT(chidx), OTGFS_HCINT_NAK); + } + else if (chan->eptype == OTGFS_EPTYPE_INTR) + { + /* Force the next transfer on an ODD frame */ + + regval = efm32_getreg(EFM32_USB_HCCHAR(chidx)); + regval |= OTGFS_HCCHAR_ODDFRM; + efm32_putreg(EFM32_USB_HCCHAR(chidx), regval); + + /* Set the request done state */ + + chan->result = OK; + } + } + + /* Check for a pending CHannel Halted (CHH) interrupt */ + + else if ((pending & OTGFS_HCINT_CHH) != 0) + { + /* Mask the CHannel Halted (CHH) interrupt */ + + regval = efm32_getreg(EFM32_USB_HCINTMSK(chidx)); + regval &= ~OTGFS_HCINT_CHH; + efm32_putreg(EFM32_USB_HCINTMSK(chidx), regval); + + /* Update the request state based on the host state machine state */ + + if (chan->chreason == CHREASON_XFRC) + { + /* Set the request done reult */ + + chan->result = OK; + } + else if (chan->chreason == CHREASON_STALL) + { + /* Set the request stall result */ + + chan->result = EPERM; + } + else if ((chan->chreason == CHREASON_TXERR) || + (chan->chreason == CHREASON_DTERR)) + { + /* Set the request I/O error result */ + + chan->result = EIO; + } + else if (chan->chreason == CHREASON_NAK) + { + /* Halt on NAK only happens on an INTR channel. Fetch the HCCHAR register + * and check for an interrupt endpoint. + */ + + regval = efm32_getreg(EFM32_USB_HCCHAR(chidx)); + if ((regval & _OTGFS_HCCHAR_EPTYP_MASK) == OTGFS_HCCHAR_EPTYP_INTR) + { + /* Toggle the IN data toggle (Used by Bulk and INTR only) */ + + chan->indata1 ^= true; + } + + /* Set the NAK error result */ + + chan->result = EAGAIN; + } + else /* if (chan->chreason == CHREASON_FRMOR) */ + { + /* Set the frame overrun error result */ + + chan->result = EPIPE; + } + + /* Clear the CHannel Halted (CHH) condition */ + + efm32_putreg(EFM32_USB_HCINT(chidx), OTGFS_HCINT_CHH); + } + + /* Check for a pending Transaction ERror (TXERR) interrupt */ + + else if ((pending & OTGFS_HCINT_TXERR) != 0) + { + /* Halt the channel when a STALL, TXERR, BBERR or DTERR interrupt is + * received on the channel. + */ + + efm32_chan_halt(priv, chidx, CHREASON_TXERR); + + /* Clear the Transaction ERror (TXERR) condition */ + + efm32_putreg(EFM32_USB_HCINT(chidx), OTGFS_HCINT_TXERR); + } + + /* Check for a pending NAK response received (NAK) interrupt */ + + else if ((pending & OTGFS_HCINT_NAK) != 0) + { + /* For a BULK transfer, the hardware is capable of retrying + * automatically on a NAK. However, this is not always + * what we need to do. So we always halt the transfer and + * return control to high level logic in the even of a NAK. + */ + +#if 1 + /* Halt the interrupt channel */ + + if (chan->eptype == OTGFS_EPTYPE_INTR) + { + /* Halt the channel -- the CHH interrupt is expected next */ + + efm32_chan_halt(priv, chidx, CHREASON_NAK); + } + + /* Re-activate CTRL and BULK channels */ + + else if (chan->eptype == OTGFS_EPTYPE_CTRL || + chan->eptype == OTGFS_EPTYPE_BULK) + { + /* Re-activate the channel by clearing CHDIS and assuring that + * CHENA is set + */ + + regval = efm32_getreg(EFM32_USB_HCCHAR(chidx)); + regval |= OTGFS_HCCHAR_CHENA; + regval &= ~OTGFS_HCCHAR_CHDIS; + efm32_putreg(EFM32_USB_HCCHAR(chidx), regval); + } +#else + /* Halt all transfers on the NAK -- the CHH interrupt is expected next */ + + efm32_chan_halt(priv, chidx, CHREASON_NAK); +#endif + + /* Clear the NAK condition */ + + efm32_putreg(EFM32_USB_HCINT(chidx), OTGFS_HCINT_NAK); + } + + /* Check for a transfer complete event */ + + efm32_chan_wakeup(priv, chan); +} + +/******************************************************************************* + * Name: efm32_gint_hcoutisr + * + * Description: + * USB OTG FS host OUT channels interrupt handler + * + * One the completion of the transfer, the channel result byte may be set as + * follows: + * + * OK - Transfer completed successfully + * EAGAIN - If devices NAKs the transfer or NYET occurs + * EPERM - If the endpoint stalls + * EIO - On a TX or data toggle error + * EPIPE - Frame overrun + * + * EBUSY in the result field indicates that the transfer has not completed. + * + *******************************************************************************/ + +static inline void efm32_gint_hcoutisr(FAR struct efm32_usbhost_s *priv, + int chidx) +{ + FAR struct efm32_chan_s *chan = &priv->chan[chidx]; + uint32_t regval; + uint32_t pending; + + /* Read the HCINT register to get the pending HC interrupts. Read the + * HCINTMSK register to get the set of enabled HC interrupts. + */ + + pending = efm32_getreg(EFM32_USB_HCINT(chidx)); + regval = efm32_getreg(EFM32_USB_HCINTMSK(chidx)); + + /* AND the two to get the set of enabled, pending HC interrupts */ + + pending &= regval; + ullvdbg("HCINTMSK%d: %08x pending: %08x\n", chidx, regval, pending); + + /* Check for a pending ACK response received/transmitted (ACK) interrupt */ + + if ((pending & OTGFS_HCINT_ACK) != 0) + { + /* Clear the pending the ACK response received/transmitted (ACK) interrupt */ + + efm32_putreg(EFM32_USB_HCINT(chidx), OTGFS_HCINT_ACK); + } + + /* Check for a pending FRaMe OverRun (FRMOR) interrupt */ + + else if ((pending & OTGFS_HCINT_FRMOR) != 0) + { + /* Halt the channel (probably not necessary for FRMOR) */ + + efm32_chan_halt(priv, chidx, CHREASON_FRMOR); + + /* Clear the pending the FRaMe OverRun (FRMOR) interrupt */ + + efm32_putreg(EFM32_USB_HCINT(chidx), OTGFS_HCINT_FRMOR); + } + + /* Check for a pending TransFeR Completed (XFRC) interrupt */ + + else if ((pending & OTGFS_HCINT_XFRC) != 0) + { + /* Decrement the number of bytes remaining by the number of + * bytes that were "in-flight". + */ + + priv->chan[chidx].buffer += priv->chan[chidx].inflight; + priv->chan[chidx].buflen -= priv->chan[chidx].inflight; + priv->chan[chidx].inflight = 0; + + /* Halt the channel -- the CHH interrrupt is expected next */ + + efm32_chan_halt(priv, chidx, CHREASON_XFRC); + + /* Clear the pending the TransFeR Completed (XFRC) interrupt */ + + efm32_putreg(EFM32_USB_HCINT(chidx), OTGFS_HCINT_XFRC); + } + + /* Check for a pending STALL response receive (STALL) interrupt */ + + else if ((pending & OTGFS_HCINT_STALL) != 0) + { + /* Clear the pending the STALL response receiv (STALL) interrupt */ + + efm32_putreg(EFM32_USB_HCINT(chidx), OTGFS_HCINT_STALL); + + /* Halt the channel when a STALL, TXERR, BBERR or DTERR interrupt is + * received on the channel. + */ + + efm32_chan_halt(priv, chidx, CHREASON_STALL); + } + + /* Check for a pending NAK response received (NAK) interrupt */ + + else if ((pending & OTGFS_HCINT_NAK) != 0) + { + /* Halt the channel -- the CHH interrrupt is expected next */ + + efm32_chan_halt(priv, chidx, CHREASON_NAK); + + /* Clear the pending the NAK response received (NAK) interrupt */ + + efm32_putreg(EFM32_USB_HCINT(chidx), OTGFS_HCINT_NAK); + } + + /* Check for a pending Transaction ERror (TXERR) interrupt */ + + else if ((pending & OTGFS_HCINT_TXERR) != 0) + { + /* Halt the channel when a STALL, TXERR, BBERR or DTERR interrupt is + * received on the channel. + */ + + efm32_chan_halt(priv, chidx, CHREASON_TXERR); + + /* Clear the pending the Transaction ERror (TXERR) interrupt */ + + efm32_putreg(EFM32_USB_HCINT(chidx), OTGFS_HCINT_TXERR); + } + + /* Check for a NYET interrupt */ + +#if 0 /* NYET is a reserved bit in the HCINT register */ + else if ((pending & OTGFS_HCINT_NYET) != 0) + { + /* Halt the channel */ + + efm32_chan_halt(priv, chidx, CHREASON_NYET); + + /* Clear the pending the NYET interrupt */ + + efm32_putreg(EFM32_USB_HCINT(chidx), OTGFS_HCINT_NYET); + } +#endif + + /* Check for a pending Data Toggle ERRor (DTERR) interrupt */ + + else if (pending & OTGFS_HCINT_DTERR) + { + /* Halt the channel when a STALL, TXERR, BBERR or DTERR interrupt is + * received on the channel. + */ + + efm32_chan_halt(priv, chidx, CHREASON_DTERR); + + /* Clear the pending the Data Toggle ERRor (DTERR) and NAK interrupts */ + + efm32_putreg(EFM32_USB_HCINT(chidx), (OTGFS_HCINT_DTERR | OTGFS_HCINT_NAK)); + } + + /* Check for a pending CHannel Halted (CHH) interrupt */ + + else if ((pending & OTGFS_HCINT_CHH) != 0) + { + /* Mask the CHannel Halted (CHH) interrupt */ + + regval = efm32_getreg(EFM32_USB_HCINTMSK(chidx)); + regval &= ~OTGFS_HCINT_CHH; + efm32_putreg(EFM32_USB_HCINTMSK(chidx), regval); + + if (chan->chreason == CHREASON_XFRC) + { + /* Set the request done result */ + + chan->result = OK; + + /* Read the HCCHAR register to get the HCCHAR register to get + * the endpoint type. + */ + + regval = efm32_getreg(EFM32_USB_HCCHAR(chidx)); + + /* Is it a bulk endpoint? Were an odd number of packets + * transferred? + */ + + if ((regval & _OTGFS_HCCHAR_EPTYP_MASK) == OTGFS_HCCHAR_EPTYP_BULK && + (chan->npackets & 1) != 0) + { + /* Yes to both... toggle the data out PID */ + + chan->outdata1 ^= true; + } + } + else if (chan->chreason == CHREASON_NAK || + chan->chreason == CHREASON_NYET) + { + /* Set the try again later result */ + + chan->result = EAGAIN; + } + else if (chan->chreason == CHREASON_STALL) + { + /* Set the request stall result */ + + chan->result = EPERM; + } + else if ((chan->chreason == CHREASON_TXERR) || + (chan->chreason == CHREASON_DTERR)) + { + /* Set the I/O failure result */ + + chan->result = EIO; + } + else /* if (chan->chreason == CHREASON_FRMOR) */ + { + /* Set the frame error result */ + + chan->result = EPIPE; + } + + /* Clear the pending the CHannel Halted (CHH) interrupt */ + + efm32_putreg(EFM32_USB_HCINT(chidx), OTGFS_HCINT_CHH); + } + + /* Check for a transfer complete event */ + + efm32_chan_wakeup(priv, chan); +} + +/******************************************************************************* + * Name: efm32_gint_connected + * + * Description: + * Handle a connection event. + * + *******************************************************************************/ + +static void efm32_gint_connected(FAR struct efm32_usbhost_s *priv) +{ + /* We we previously disconnected? */ + + if (!priv->connected) + { + /* Yes.. then now we are connected */ + + usbhost_vtrace1(OTGFS_VTRACE1_CONNECTED,0); + priv->connected = true; + DEBUGASSERT(priv->smstate == SMSTATE_DETACHED); + + /* Notify any waiters */ + + priv->smstate = SMSTATE_ATTACHED; + if (priv->eventwait) + { + efm32_givesem(&priv->eventsem); + priv->eventwait = false; + } + } +} + +/******************************************************************************* + * Name: efm32_gint_disconnected + * + * Description: + * Handle a disconnection event. + * + *******************************************************************************/ + +static void efm32_gint_disconnected(FAR struct efm32_usbhost_s *priv) +{ + /* Were we previously connected? */ + + if (priv->connected) + { + /* Yes.. then we no longer connected */ + + usbhost_vtrace1(OTGFS_VTRACE1_DISCONNECTED,0); + + /* Are we bound to a class driver? */ + + if (priv->class) + { + /* Yes.. Disconnect the class driver */ + + CLASS_DISCONNECTED(priv->class); + priv->class = NULL; + } + + /* Re-Initilaize Host for new Enumeration */ + + priv->smstate = SMSTATE_DETACHED; + priv->ep0size = EFM32_EP0_MAX_PACKET_SIZE; + priv->devaddr = EFM32_DEF_DEVADDR; + priv->connected = false; + priv->lowspeed = false; + efm32_chan_freeall(priv); + + /* Notify any waiters that there is a change in the connection state */ + + if (priv->eventwait) + { + efm32_givesem(&priv->eventsem); + priv->eventwait = false; + } + } +} + +/******************************************************************************* + * Name: efm32_gint_sofisr + * + * Description: + * USB OTG FS start-of-frame interrupt handler + * + *******************************************************************************/ + +#ifdef CONFIG_EFM32_OTGFS_SOFINTR +static inline void efm32_gint_sofisr(FAR struct efm32_usbhost_s *priv) +{ + /* Handle SOF interrupt */ +#warning "Do what?" + + /* Clear pending SOF interrupt */ + + efm32_putreg(EFM32_USB_GINTSTS, USB_GINT_SOF); +} +#endif + +/******************************************************************************* + * Name: efm32_gint_rxflvlisr + * + * Description: + * USB OTG FS RxFIFO non-empty interrupt handler + * + *******************************************************************************/ + +static inline void efm32_gint_rxflvlisr(FAR struct efm32_usbhost_s *priv) +{ + FAR uint32_t *dest; + uint32_t grxsts; + uint32_t intmsk; + uint32_t hcchar; + uint32_t hctsiz; + uint32_t fifo; + int bcnt; + int bcnt32; + int chidx; + int i; + + /* Disable the RxFIFO non-empty interrupt */ + + intmsk = efm32_getreg(EFM32_USB_GINTMSK); + intmsk &= ~USB_GINT_RXFLVL; + efm32_putreg(EFM32_USB_GINTMSK, intmsk); + + /* Read and pop the next status from the Rx FIFO */ + + grxsts = efm32_getreg(EFM32_USB_GRXSTSP); + ullvdbg("GRXSTS: %08x\n", grxsts); + + /* Isolate the channel number/index in the status word */ + + chidx = (grxsts & _OTGFS_GRXSTSH_CHNUM_MASK) >> _OTGFS_GRXSTSH_CHNUM_SHIFT; + + /* Get the host channel characteristics register (HCCHAR) for this channel */ + + hcchar = efm32_getreg(EFM32_USB_HCCHAR(chidx)); + + /* Then process the interrupt according to the packet status */ + + switch (grxsts & _OTGFS_GRXSTSH_PKTSTS_MASK) + { + case OTGFS_GRXSTSH_PKTSTS_INRECVD: /* IN data packet received */ + { + /* Read the data into the host buffer. */ + + bcnt = (grxsts & _OTGFS_GRXSTSH_BCNT_MASK) >> _OTGFS_GRXSTSH_BCNT_SHIFT; + if (bcnt > 0 && priv->chan[chidx].buffer != NULL) + { + /* Transfer the packet from the Rx FIFO into the user buffer */ + + dest = (FAR uint32_t *)priv->chan[chidx].buffer; + fifo = EFM32_USB_DFIFO_HCH(0); + bcnt32 = (bcnt + 3) >> 2; + + for (i = 0; i < bcnt32; i++) + { + *dest++ = efm32_getreg(fifo); + } + + efm32_pktdump("Received", priv->chan[chidx].buffer, bcnt); + + /* Toggle the IN data pid (Used by Bulk and INTR only) */ + + priv->chan[chidx].indata1 ^= true; + + /* Manage multiple packet transfers */ + + priv->chan[chidx].buffer += bcnt; + priv->chan[chidx].buflen -= bcnt; + + /* Check if more packets are expected */ + + hctsiz = efm32_getreg(EFM32_USB_HCTSIZ(chidx)); + if ((hctsiz & _OTGFS_HCTSIZ_PKTCNT_MASK) != 0) + { + /* Re-activate the channel when more packets are expected */ + + hcchar |= OTGFS_HCCHAR_CHENA; + hcchar &= ~OTGFS_HCCHAR_CHDIS; + efm32_putreg(EFM32_USB_HCCHAR(chidx), hcchar); + } + } + } + break; + + case OTGFS_GRXSTSH_PKTSTS_INDONE: /* IN transfer completed */ + case OTGFS_GRXSTSH_PKTSTS_DTOGERR: /* Data toggle error */ + case OTGFS_GRXSTSH_PKTSTS_HALTED: /* Channel halted */ + default: + break; + } + + /* Re-enable the RxFIFO non-empty interrupt */ + + intmsk |= USB_GINT_RXFLVL; + efm32_putreg(EFM32_USB_GINTMSK, intmsk); +} + +/******************************************************************************* + * Name: efm32_gint_nptxfeisr + * + * Description: + * USB OTG FS non-periodic TxFIFO empty interrupt handler + * + *******************************************************************************/ + +static inline void efm32_gint_nptxfeisr(FAR struct efm32_usbhost_s *priv) +{ + FAR struct efm32_chan_s *chan; + uint32_t regval; + unsigned int wrsize; + unsigned int avail; + unsigned int chidx; + + /* Recover the index of the channel that is waiting for space in the Tx + * FIFO. + */ + + chidx = priv->chidx; + chan = &priv->chan[chidx]; + + /* Reduce the buffer size by the number of bytes that were previously placed + * in the Tx FIFO. + */ + + chan->buffer += chan->inflight; + chan->buflen -= chan->inflight; + chan->inflight = 0; + + /* If we have now transfered the entire buffer, then this transfer is + * complete (this case really should never happen because we disable + * the NPTXFE interrupt on the final packet). + */ + + if (chan->buflen <= 0) + { + /* Disable further Tx FIFO empty interrupts and bail. */ + + efm32_modifyreg(EFM32_USB_GINTMSK, USB_GINT_NPTXFE, 0); + return; + } + + /* Read the status from the top of the non-periodic TxFIFO */ + + regval = efm32_getreg(EFM32_USB_HNPTXSTS); + + /* Extract the number of bytes available in the non-periodic Tx FIFO. */ + + avail = ((regval & _OTGFS_HNPTXSTS_NPTXFSAV_MASK) >> _OTGFS_HNPTXSTS_NPTXFSAV_SHIFT) << 2; + + /* Get minimal size packet that can be sent. Something is seriously + * configured wrong if one packet will not fit into the empty Tx FIFO. + */ + + DEBUGASSERT(chan->buflen > 0 && + avail >= MIN(chan->buflen, chan->maxpacket)); + + /* Get the size to put in the Tx FIFO now */ + + wrsize = chan->buflen; + if (wrsize > avail) + { + /* Clip the write size to the number of full, max sized packets + * that will fit in the Tx FIFO. + */ + + unsigned int wrpackets = avail / chan->maxpacket; + wrsize = wrpackets * chan->maxpacket; + } + + /* Otherwise, this will be the last packet to be sent in this transaction. + * We now need to disable further NPTXFE interrupts. + */ + + else + { + efm32_modifyreg(EFM32_USB_GINTMSK, USB_GINT_NPTXFE, 0); + } + + /* Write the next group of packets into the Tx FIFO */ + + ullvdbg("HNPTXSTS: %08x chidx: %d avail: %d buflen: %d wrsize: %d\n", + regval, chidx, avail, chan->buflen, wrsize); + + efm32_gint_wrpacket(priv, chan->buffer, chidx, wrsize); +} + +/******************************************************************************* + * Name: efm32_gint_ptxfeisr + * + * Description: + * USB OTG FS periodic TxFIFO empty interrupt handler + * + *******************************************************************************/ + +static inline void efm32_gint_ptxfeisr(FAR struct efm32_usbhost_s *priv) +{ + FAR struct efm32_chan_s *chan; + uint32_t regval; + unsigned int wrsize; + unsigned int avail; + unsigned int chidx; + + /* Recover the index of the channel that is waiting for space in the Tx + * FIFO. + */ + + chidx = priv->chidx; + chan = &priv->chan[chidx]; + + /* Reduce the buffer size by the number of bytes that were previously placed + * in the Tx FIFO. + */ + + chan->buffer += chan->inflight; + chan->buflen -= chan->inflight; + chan->inflight = 0; + + /* If we have now transfered the entire buffer, then this transfer is + * complete (this case really should never happen because we disable + * the PTXFE interrupt on the final packet). + */ + + if (chan->buflen <= 0) + { + /* Disable further Tx FIFO empty interrupts and bail. */ + + efm32_modifyreg(EFM32_USB_GINTMSK, USB_GINT_PTXFE, 0); + return; + } + + /* Read the status from the top of the periodic TxFIFO */ + + regval = efm32_getreg(EFM32_USB_HPTXSTS); + + /* Extract the number of bytes available in the periodic Tx FIFO. */ + + avail = ((regval & _OTGFS_HPTXSTS_PTXFSAVL_MASK) >> _OTGFS_HPTXSTS_PTXFSAVL_SHIFT) << 2; + + /* Get minimal size packet that can be sent. Something is seriously + * configured wrong if one packet will not fit into the empty Tx FIFO. + */ + + DEBUGASSERT(chan->buflen > 0 && + avail >= MIN(chan->buflen, chan->maxpacket)); + + /* Get the size to put in the Tx FIFO now */ + + wrsize = chan->buflen; + if (wrsize > avail) + { + /* Clip the write size to the number of full, max sized packets + * that will fit in the Tx FIFO. + */ + + unsigned int wrpackets = avail / chan->maxpacket; + wrsize = wrpackets * chan->maxpacket; + } + + /* Otherwise, this will be the last packet to be sent in this transaction. + * We now need to disable further PTXFE interrupts. + */ + + else + { + efm32_modifyreg(EFM32_USB_GINTMSK, USB_GINT_PTXFE, 0); + } + + /* Write the next group of packets into the Tx FIFO */ + + ullvdbg("HPTXSTS: %08x chidx: %d avail: %d buflen: %d wrsize: %d\n", + regval, chidx, avail, chan->buflen, wrsize); + + efm32_gint_wrpacket(priv, chan->buffer, chidx, wrsize); +} + +/******************************************************************************* + * Name: efm32_gint_hcisr + * + * Description: + * USB OTG FS host channels interrupt handler + * + *******************************************************************************/ + +static inline void efm32_gint_hcisr(FAR struct efm32_usbhost_s *priv) +{ + uint32_t haint; + uint32_t hcchar; + int i = 0; + + /* Read the Host all channels interrupt register and test each bit in the + * register. Each bit i, i=0...(EFM32_NHOST_CHANNELS-1), corresponds to + * a pending interrupt on channel i. + */ + + haint = efm32_getreg(EFM32_USB_HAINT); + for (i = 0; i < EFM32_NHOST_CHANNELS; i++) + { + /* Is an interrupt pending on this channel? */ + + if ((haint & OTGFS_HAINT(i)) != 0) + { + /* Yes... read the HCCHAR register to get the direction bit */ + + hcchar = efm32_getreg(EFM32_USB_HCCHAR(i)); + + /* Was this an interrupt on an IN or an OUT channel? */ + + if ((hcchar & OTGFS_HCCHAR_EPDIR) != 0) + { + /* Handle the HC IN channel interrupt */ + + efm32_gint_hcinisr(priv, i); + } + else + { + /* Handle the HC OUT channel interrupt */ + + efm32_gint_hcoutisr(priv, i); + } + } + } +} + +/******************************************************************************* + * Name: efm32_gint_hprtisr + * + * Description: + * USB OTG FS host port interrupt handler + * + *******************************************************************************/ + +static inline void efm32_gint_hprtisr(FAR struct efm32_usbhost_s *priv) +{ + uint32_t hprt; + uint32_t newhprt; + uint32_t hcfg; + + usbhost_vtrace1(OTGFS_VTRACE1_GINT_HPRT, 0); + /* Read the port status and control register (HPRT) */ + + hprt = efm32_getreg(EFM32_USB_HPRT); + + /* Setup to clear the interrupt bits in GINTSTS by setting the corresponding + * bits in the HPRT. The HCINT interrupt bit is cleared when the appropriate + * status bits in the HPRT register are cleared. + */ + + newhprt = hprt & ~(OTGFS_HPRT_PENA | OTGFS_HPRT_PCDET | + OTGFS_HPRT_PENCHNG | OTGFS_HPRT_POCCHNG); + + /* Check for Port Overcurrent CHaNGe (POCCHNG) */ + + if ((hprt & OTGFS_HPRT_POCCHNG) != 0) + { + /* Set up to clear the POCCHNG status in the new HPRT contents. */ + + usbhost_vtrace1(OTGFS_VTRACE1_GINT_HPRT_POCCHNG, 0); + newhprt |= OTGFS_HPRT_POCCHNG; + } + + /* Check for Port Connect DETected (PCDET). The core sets this bit when a + * device connection is detected. + */ + + if ((hprt & OTGFS_HPRT_PCDET) != 0) + { + /* Set up to clear the PCDET status in the new HPRT contents. Then + * process the new connection event. + */ + + usbhost_vtrace1(OTGFS_VTRACE1_GINT_HPRT_PCDET, 0); + newhprt |= OTGFS_HPRT_PCDET; + efm32_portreset(priv); + efm32_gint_connected(priv); + } + + /* Check for Port Enable CHaNGed (PENCHNG) */ + + if ((hprt & OTGFS_HPRT_PENCHNG) != 0) + { + /* Set up to clear the PENCHNG status in the new HPRT contents. */ + + usbhost_vtrace1(OTGFS_VTRACE1_GINT_HPRT_PENCHNG, 0); + newhprt |= OTGFS_HPRT_PENCHNG; + + /* Was the port enabled? */ + + if ((hprt & OTGFS_HPRT_PENA) != 0) + { + /* Yes.. handle the new connection event */ + + efm32_gint_connected(priv); + + /* Check the Host ConFiGuration register (HCFG) */ + + hcfg = efm32_getreg(EFM32_USB_HCFG); + + /* Is this a low speed or full speed connection (OTG FS does not + * support high speed) + */ + + if ((hprt & _OTGFS_HPRT_PSPD_MASK) == OTGFS_HPRT_PSPD_LS) + { + /* Set the Host Frame Interval Register for the 6KHz speed */ + + usbhost_vtrace1(OTGFS_VTRACE1_GINT_HPRT_LSDEV, 0); + efm32_putreg(EFM32_USB_HFIR, 6000); + + /* Are we switching from FS to LS? */ + + if ((hcfg & _OTGFS_HCFG_FSLSPCS_MASK) != OTGFS_HCFG_FSLSPCS_LS6MHz) + { + + usbhost_vtrace1(OTGFS_VTRACE1_GINT_HPRT_FSLSSW, 0); + /* Yes... configure for LS */ + + hcfg &= ~_OTGFS_HCFG_FSLSPCS_MASK; + hcfg |= OTGFS_HCFG_FSLSPCS_LS6MHz; + efm32_putreg(EFM32_USB_HCFG, hcfg); + + /* And reset the port */ + + efm32_portreset(priv); + } + } + else /* if ((hprt & _OTGFS_HPRT_PSPD_MASK) == OTGFS_HPRT_PSPD_FS) */ + { + + usbhost_vtrace1(OTGFS_VTRACE1_GINT_HPRT_FSDEV, 0); + efm32_putreg(EFM32_USB_HFIR, 48000); + + /* Are we switching from LS to FS? */ + + if ((hcfg & _OTGFS_HCFG_FSLSPCS_MASK) != OTGFS_HCFG_FSLSPCS_FS48MHz) + { + + usbhost_vtrace1(OTGFS_VTRACE1_GINT_HPRT_LSFSSW, 0); + /* Yes... configure for FS */ + + hcfg &= ~_OTGFS_HCFG_FSLSPCS_MASK; + hcfg |= OTGFS_HCFG_FSLSPCS_FS48MHz; + efm32_putreg(EFM32_USB_HCFG, hcfg); + + /* And reset the port */ + + efm32_portreset(priv); + } + } + } + } + + /* Clear port interrupts by setting bits in the HPRT */ + + efm32_putreg(EFM32_USB_HPRT, newhprt); +} + +/******************************************************************************* + * Name: efm32_gint_discisr + * + * Description: + * USB OTG FS disconnect detected interrupt handler + * + *******************************************************************************/ + +static inline void efm32_gint_discisr(FAR struct efm32_usbhost_s *priv) +{ + /* Handle the disconnection event */ + + efm32_gint_disconnected(priv); + + /* Clear the dicsonnect interrupt */ + + efm32_putreg(EFM32_USB_GINTSTS, USB_GINT_DISC); +} + +/******************************************************************************* + * Name: efm32_gint_ipxfrisr + * + * Description: + * USB OTG FS incomplete periodic interrupt handler + * + *******************************************************************************/ + +static inline void efm32_gint_ipxfrisr(FAR struct efm32_usbhost_s *priv) +{ + uint32_t regval; + + /* CHENA : Set to enable the channel + * CHDIS : Set to stop transmitting/receiving data on a channel + */ + + regval = efm32_getreg(EFM32_USB_HCCHAR(0)); + regval |= (OTGFS_HCCHAR_CHDIS | OTGFS_HCCHAR_CHENA); + efm32_putreg(EFM32_USB_HCCHAR(0), regval); + + /* Clear the incomplete isochronous OUT interrupt */ + + efm32_putreg(EFM32_USB_GINTSTS, USB_GINT_IPXFR); +} + +/******************************************************************************* + * Name: efm32_gint_isr + * + * Description: + * USB OTG FS global interrupt handler + * + *******************************************************************************/ + +static int efm32_gint_isr(int irq, FAR void *context) +{ + /* At present, there is only support for a single OTG FS host. Hence it is + * pre-allocated as g_usbhost. However, in most code, the private data + * structure will be referenced using the 'priv' pointer (rather than the + * global data) in order to simplify any future support for multiple devices. + */ + + FAR struct efm32_usbhost_s *priv = &g_usbhost; + uint32_t pending; + + /* If OTG were supported, we would need to check if we are in host or + * device mode when the global interrupt occurs. Here we support only + * host mode + */ + + /* Loop while there are pending interrupts to process. This loop may save a + * little interrupt handling overhead. + */ + + for (;;) + { + /* Get the unmasked bits in the GINT status */ + + pending = efm32_getreg(EFM32_USB_GINTSTS); + pending &= efm32_getreg(EFM32_USB_GINTMSK); + + /* Return from the interrupt when there are no furhter pending + * interrupts. + */ + + if (pending == 0) + { + return OK; + } + + /* Otherwise, process each pending, unmasked GINT interrupts */ + + + /* Handle the start of frame interrupt */ + +#ifdef CONFIG_EFM32_OTGFS_SOFINTR + if ((pending & USB_GINT_SOF) != 0) + { + usbhost_vtrace1(OTGFS_VTRACE1_GINT_SOF, 0); + efm32_gint_sofisr(priv); + } +#endif + + /* Handle the RxFIFO non-empty interrupt */ + + if ((pending & USB_GINT_RXFLVL) != 0) + { + usbhost_vtrace1(OTGFS_VTRACE1_GINT_RXFLVL, 0); + efm32_gint_rxflvlisr(priv); + } + + /* Handle the non-periodic TxFIFO empty interrupt */ + + if ((pending & USB_GINT_NPTXFE) != 0) + { + usbhost_vtrace1(OTGFS_VTRACE1_GINT_NPTXFE, 0); + efm32_gint_nptxfeisr(priv); + } + + /* Handle the periodic TxFIFO empty interrupt */ + + if ((pending & USB_GINT_PTXFE) != 0) + { + usbhost_vtrace1(OTGFS_VTRACE1_GINT_PTXFE, 0); + efm32_gint_ptxfeisr(priv); + } + + /* Handle the host channels interrupt */ + + if ((pending & USB_GINT_HC) != 0) + { + usbhost_vtrace1(OTGFS_VTRACE1_GINT_HC, 0); + efm32_gint_hcisr(priv); + } + + /* Handle the host port interrupt */ + + if ((pending & USB_GINT_HPRT) != 0) + { + efm32_gint_hprtisr(priv); + } + + /* Handle the disconnect detected interrupt */ + + if ((pending & USB_GINT_DISC) != 0) + { + usbhost_vtrace1(OTGFS_VTRACE1_GINT_DISC, 0); + efm32_gint_discisr(priv); + } + + /* Handle the incomplete periodic transfer */ + + if ((pending & USB_GINT_IPXFR) != 0) + { + usbhost_vtrace1(OTGFS_VTRACE1_GINT_IPXFR, 0); + efm32_gint_ipxfrisr(priv); + } + } + + /* We won't get here */ + + return OK; +} + +/******************************************************************************* + * Name: efm32_gint_enable and efm32_gint_disable + * + * Description: + * Respectively enable or disable the global OTG FS interrupt. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + *******************************************************************************/ + +static void efm32_gint_enable(void) +{ + uint32_t regval; + + /* Set the GINTMSK bit to unmask the interrupt */ + + regval = efm32_getreg(EFM32_USB_GAHBCFG); + regval |= USB_GAHBCFG_GINTMSK; + efm32_putreg(EFM32_USB_GAHBCFG, regval); +} + +static void efm32_gint_disable(void) +{ + uint32_t regval; + + /* Clear the GINTMSK bit to mask the interrupt */ + + regval = efm32_getreg(EFM32_USB_GAHBCFG); + regval &= ~USB_GAHBCFG_GINTMSK; + efm32_putreg(EFM32_USB_GAHBCFG, regval); +} + +/******************************************************************************* + * Name: efm32_hostinit_enable + * + * Description: + * Enable host interrupts. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + *******************************************************************************/ + +static inline void efm32_hostinit_enable(void) +{ + uint32_t regval; + + /* Disable all interrupts. */ + + efm32_putreg(EFM32_USB_GINTMSK, 0); + + /* Clear any pending interrupts. */ + + efm32_putreg(EFM32_USB_GINTSTS, 0xffffffff); + + /* Clear any pending USB OTG Interrupts (should be done elsewhere if OTG is supported) */ + + efm32_putreg(EFM32_USB_GOTGINT, 0xffffffff); + + /* Clear any pending USB OTG interrupts */ + + efm32_putreg(EFM32_USB_GINTSTS, 0xbfffffff); + + /* Enable the host interrupts */ + /* Common interrupts: + * + * USB_GINT_WKUP : Resume/remote wakeup detected interrupt + * USB_GINT_USBSUSP : USB suspend + */ + + regval = (USB_GINT_WKUP | USB_GINT_USBSUSP); + + /* If OTG were supported, we would need to enable the following as well: + * + * USB_GINT_OTG : OTG interrupt + * USB_GINT_SRQ : Session request/new session detected interrupt + * USB_GINT_CIDSCHG : Connector ID status change + */ + + /* Host-specific interrupts + * + * USB_GINT_SOF : Start of frame + * USB_GINT_RXFLVL : RxFIFO non-empty + * USB_GINT_IISOOXFR : Incomplete isochronous OUT transfer + * USB_GINT_HPRT : Host port interrupt + * USB_GINT_HC : Host channels interrupt + * USB_GINT_DISC : Disconnect detected interrupt + */ + +#ifdef CONFIG_EFM32_OTGFS_SOFINTR + regval |= (USB_GINT_SOF | USB_GINT_RXFLVL | USB_GINT_IISOOXFR | + USB_GINT_HPRT | USB_GINT_HC | USB_GINT_DISC); +#else + regval |= (USB_GINT_RXFLVL | USB_GINT_IPXFR | USB_GINT_HPRT | + USB_GINT_HC | USB_GINT_DISC); +#endif + efm32_putreg(EFM32_USB_GINTMSK, regval); +} + +/******************************************************************************* + * Name: efm32_txfe_enable + * + * Description: + * Enable Tx FIFO empty interrupts. This is necessary when the entire + * transfer will not fit into Tx FIFO. The transfer will then be completed + * when the Tx FIFO is empty. NOTE: The Tx FIFO interrupt is disabled + * the fifo empty interrupt handler when the transfer is complete. + * + * Input Parameters: + * priv - Driver state structure reference + * chidx - The channel that requires the Tx FIFO empty interrupt + * + * Returned Value: + * None + * + * Assumptions: + * Called from user task context. Interrupts must be disabled to assure + * exclusive access to the GINTMSK register. + * + *******************************************************************************/ + +static void efm32_txfe_enable(FAR struct efm32_usbhost_s *priv, int chidx) +{ + FAR struct efm32_chan_s *chan = &priv->chan[chidx]; + irqstate_t flags; + uint32_t regval; + + /* Disable all interrupts so that we have exclusive access to the GINTMSK + * (it would be sufficent just to disable the GINT interrupt). + */ + + flags = irqsave(); + + /* Should we enable the periodic or non-peridic Tx FIFO empty interrupts */ + + regval = efm32_getreg(EFM32_USB_GINTMSK); + switch (chan->eptype) + { + default: + case OTGFS_EPTYPE_CTRL: /* Non periodic transfer */ + case OTGFS_EPTYPE_BULK: + regval |= USB_GINT_NPTXFE; + break; + + case OTGFS_EPTYPE_INTR: /* Periodic transfer */ + case OTGFS_EPTYPE_ISOC: + regval |= USB_GINT_PTXFE; + break; + } + + /* Enable interrupts */ + + efm32_putreg(EFM32_USB_GINTMSK, regval); + irqrestore(flags); +} + +/******************************************************************************* + * USB Host Controller Operations + *******************************************************************************/ + +/******************************************************************************* + * Name: efm32_wait + * + * Description: + * Wait for a device to be connected or disconneced. + * + * Input Parameters: + * conn - The USB host connection instance obtained as a parameter from the call to + * the USB driver initialization logic. + * connected - A pointer to a boolean value. TRUE: Wait for device to be + * connected; FALSE: wait for device to be disconnected + * + * Returned Values: + * Zero (OK) is returned when a device in connected. This function will not + * return until either (1) a device is connected or (2) some failure occurs. + * On a failure, a negated errno value is returned indicating the nature of + * the failure + * + * Assumptions: + * - Called from a single thread so no mutual exclusion is required. + * - Never called from an interrupt handler. + * + *******************************************************************************/ + +static int efm32_wait(FAR struct usbhost_connection_s *conn, + FAR const bool *connected) +{ + FAR struct efm32_usbhost_s *priv = &g_usbhost; + irqstate_t flags; + + /* Are we already connected? */ + + flags = irqsave(); + while (priv->connected == *connected) + { + /* No... wait for the connection/disconnection */ + + priv->eventwait = true; + efm32_takesem(&priv->eventsem); + } + + irqrestore(flags); + + udbg("Connected:%s\n", priv->connected ? "YES" : "NO"); + return OK; +} + +/******************************************************************************* + * Name: efm32_enumerate + * + * Description: + * Enumerate the connected device. As part of this enumeration process, + * the driver will (1) get the device's configuration descriptor, (2) + * extract the class ID info from the configuration descriptor, (3) call + * usbhost_findclass() to find the class that supports this device, (4) + * call the create() method on the struct usbhost_registry_s interface + * to get a class instance, and finally (5) call the configdesc() method + * of the struct usbhost_class_s interface. After that, the class is in + * charge of the sequence of operations. + * + * Input Parameters: + * conn - The USB host connection instance obtained as a parameter from the call to + * the USB driver initialization logic. + * rphndx - Root hub port index. 0-(n-1) corresponds to root hub port 1-n. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * - Only a single class bound to a single device is supported. + * - Called from a single thread so no mutual exclusion is required. + * - Never called from an interrupt handler. + * + *******************************************************************************/ + +static int efm32_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx) +{ + FAR struct efm32_usbhost_s *priv = &g_usbhost; + uint32_t regval; + int chidx; + int ret; + + DEBUGASSERT(priv && rhpndx == 0); + + /* Are we connected to a device? The caller should have called the wait() + * method first to be assured that a device is connected. + */ + + while (!priv->connected) + { + /* No, return an error */ + + usbhost_trace1(OTGFS_TRACE1_DEVDISCONN,0); + return -ENODEV; + } + + DEBUGASSERT(priv->smstate == SMSTATE_ATTACHED); + + /* Allocate and initialize the control OUT channel */ + + chidx = efm32_chan_alloc(priv); + DEBUGASSERT(chidx >= 0); + + priv->ep0out = chidx; + priv->chan[chidx].epno = 0; + priv->chan[chidx].in = false; + priv->chan[chidx].eptype = OTGFS_EPTYPE_CTRL; + priv->chan[chidx].maxpacket = EFM32_EP0_DEF_PACKET_SIZE; + priv->chan[chidx].indata1 = false; + priv->chan[chidx].outdata1 = false; + + /* Allocate and initialize the control IN channel */ + + chidx = efm32_chan_alloc(priv); + DEBUGASSERT(chidx >= 0); + + priv->ep0in = chidx; + priv->chan[chidx].epno = 0; + priv->chan[chidx].in = true; + priv->chan[chidx].eptype = OTGFS_EPTYPE_CTRL; + priv->chan[chidx].maxpacket = EFM32_EP0_DEF_PACKET_SIZE; + priv->chan[chidx].indata1 = false; + priv->chan[chidx].outdata1 = false; + + /* USB 2.0 spec says at least 50ms delay before port reset. We wait 100ms. */ + + usleep(100*1000); + + /* Reset the host port */ + + efm32_portreset(priv); + + /* Get the current device speed */ + + regval = efm32_getreg(EFM32_USB_HPRT); + priv->lowspeed = ((regval & _OTGFS_HPRT_PSPD_MASK) == OTGFS_HPRT_PSPD_LS); + + /* Configure control channels */ + + efm32_chan_configure(priv, priv->ep0out); + efm32_chan_configure(priv, priv->ep0in); + + /* Let the common usbhost_enumerate do all of the real work. Note that the + * FunctionAddress (USB address) is hardcoded to one. + */ + + uvdbg("Enumerate the device\n"); + priv->smstate = SMSTATE_ENUM; + ret = usbhost_enumerate(&g_usbhost.drvr, 1, &priv->class); + + /* The enumeration may fail either because of some HCD interfaces failure + * or because the device class is not supported. In either case, we just + * need to perform the disconnection operation and make ready for a new + * enumeration. + */ + + if (ret < 0) + { + /* Return to the disconnected state */ + + efm32_gint_disconnected(priv); + } + + return ret; +} + +/************************************************************************************ + * Name: efm32_ep0configure + * + * Description: + * Configure endpoint 0. This method is normally used internally by the + * enumerate() method but is made available at the interface to support an + * external implementation of the enumeration logic. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * funcaddr - The USB address of the function containing the endpoint that EP0 + * controls + * maxpacketsize - The maximum number of bytes that can be sent to or + * received from the endpoint in a single data packet + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ************************************************************************************/ + +static int efm32_ep0configure(FAR struct usbhost_driver_s *drvr, uint8_t funcaddr, + uint16_t maxpacketsize) +{ + FAR struct efm32_usbhost_s *priv = (FAR struct efm32_usbhost_s *)drvr; + + DEBUGASSERT(drvr && funcaddr < 128 && maxpacketsize < 2048); + + /* We must have exclusive access to the USB host hardware and state structures */ + + efm32_takesem(&priv->exclsem); + + /* Save the device address and EP0 max packet size */ + + priv->devaddr = funcaddr; + priv->ep0size = maxpacketsize; + + /* Configure the EP0 OUT channel */ + + priv->chan[priv->ep0out].maxpacket = maxpacketsize; + efm32_chan_configure(priv, priv->ep0out); + + /* Configure the EP0 IN channel */ + + priv->chan[priv->ep0in].maxpacket = maxpacketsize; + efm32_chan_configure(priv, priv->ep0in); + + efm32_givesem(&priv->exclsem); + return OK; +} + +/************************************************************************************ + * Name: efm32_getdevinfo + * + * Description: + * Get information about the connected device. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * devinfo - A pointer to memory provided by the caller in which to return the + * device information. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ************************************************************************************/ + +static int efm32_getdevinfo(FAR struct usbhost_driver_s *drvr, + FAR struct usbhost_devinfo_s *devinfo) +{ + FAR struct efm32_usbhost_s *priv = (FAR struct efm32_usbhost_s *)drvr; + + DEBUGASSERT(drvr && devinfo); + devinfo->speed = priv->lowspeed ? DEVINFO_SPEED_LOW : DEVINFO_SPEED_FULL; + return OK; +} + +/************************************************************************************ + * Name: efm32_epalloc + * + * Description: + * Allocate and configure one endpoint. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * epdesc - Describes the endpoint to be allocated. + * ep - A memory location provided by the caller in which to receive the + * allocated endpoint descriptor. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ************************************************************************************/ + +static int efm32_epalloc(FAR struct usbhost_driver_s *drvr, + FAR const struct usbhost_epdesc_s *epdesc, + FAR usbhost_ep_t *ep) +{ + FAR struct efm32_usbhost_s *priv = (FAR struct efm32_usbhost_s *)drvr; + FAR struct efm32_chan_s *chan; + int chidx; + int ret; + + /* Sanity check. NOTE that this method should only be called if a device is + * connected (because we need a valid low speed indication). + */ + + DEBUGASSERT(priv && epdesc && ep && priv->connected); + + /* We must have exclusive access to the USB host hardware and state structures */ + + efm32_takesem(&priv->exclsem); + + /* Allocate a host channel for the endpoint */ + + chidx = efm32_chan_alloc(priv); + if (chidx < 0) + { + udbg("Failed to allocate a host channel\n"); + ret = -ENOMEM; + goto errout; + } + + /* Decode the endpoint descriptor to initialize the channel data structures. + * Note: Here we depend on the fact that the endpoint point type is + * encoded in the same way in the endpoint descriptor as it is in the OTG + * FS hardware. + */ + + chan = &priv->chan[chidx]; + chan->epno = epdesc->addr & USB_EPNO_MASK; + chan->in = epdesc->in; + chan->eptype = epdesc->xfrtype; + chan->maxpacket = epdesc->mxpacketsize; + chan->indata1 = false; + chan->outdata1 = false; + + /* Then configure the endpoint */ + + efm32_chan_configure(priv, chidx); + + /* Return the index to the allocated channel as the endpoint "handle" */ + + *ep = (usbhost_ep_t)chidx; + ret = OK; + +errout: + efm32_givesem(&priv->exclsem); + return ret; +} + +/************************************************************************************ + * Name: efm32_epfree + * + * Description: + * Free and endpoint previously allocated by DRVR_EPALLOC. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * ep - The endpoint to be freed. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ************************************************************************************/ + +static int efm32_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep) +{ + struct efm32_usbhost_s *priv = (struct efm32_usbhost_s *)drvr; + int chidx = (int)ep; + + DEBUGASSERT(priv && chidx < EFM32_MAX_TX_FIFOS); + + /* We must have exclusive access to the USB host hardware and state structures */ + + efm32_takesem(&priv->exclsem); + + /* Halt the channel and mark the channel avaiable */ + + efm32_chan_free(priv, chidx); + + efm32_givesem(&priv->exclsem); + return OK; +} + +/******************************************************************************* + * Name: efm32_alloc + * + * Description: + * Some hardware supports special memory in which request and descriptor data can + * be accessed more efficiently. This method provides a mechanism to allocate + * the request/descriptor memory. If the underlying hardware does not support + * such "special" memory, this functions may simply map to kmm_malloc. + * + * This interface was optimized under a particular assumption. It was assumed + * that the driver maintains a pool of small, pre-allocated buffers for descriptor + * traffic. NOTE that size is not an input, but an output: The size of the + * pre-allocated buffer is returned. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * buffer - The address of a memory location provided by the caller in which to + * return the allocated buffer memory address. + * maxlen - The address of a memory location provided by the caller in which to + * return the maximum size of the allocated buffer memory. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * - Called from a single thread so no mutual exclusion is required. + * - Never called from an interrupt handler. + * + *******************************************************************************/ + +static int efm32_alloc(FAR struct usbhost_driver_s *drvr, + FAR uint8_t **buffer, FAR size_t *maxlen) +{ + FAR uint8_t *alloc; + + DEBUGASSERT(drvr && buffer && maxlen); + + /* There is no special memory requirement for the EFM32. */ + + alloc = (FAR uint8_t *)kmm_malloc(CONFIG_EFM32_OTGFS_DESCSIZE); + if (!alloc) + { + return -ENOMEM; + } + + /* Return the allocated address and size of the descriptor buffer */ + + *buffer = alloc; + *maxlen = CONFIG_EFM32_OTGFS_DESCSIZE; + return OK; +} + +/******************************************************************************* + * Name: efm32_free + * + * Description: + * Some hardware supports special memory in which request and descriptor data can + * be accessed more efficiently. This method provides a mechanism to free that + * request/descriptor memory. If the underlying hardware does not support + * such "special" memory, this functions may simply map to kmm_free(). + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * buffer - The address of the allocated buffer memory to be freed. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * - Never called from an interrupt handler. + * + *******************************************************************************/ + +static int efm32_free(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer) +{ + /* There is no special memory requirement */ + + DEBUGASSERT(drvr && buffer); + kmm_free(buffer); + return OK; +} + +/************************************************************************************ + * Name: efm32_ioalloc + * + * Description: + * Some hardware supports special memory in which larger IO buffers can + * be accessed more efficiently. This method provides a mechanism to allocate + * the request/descriptor memory. If the underlying hardware does not support + * such "special" memory, this functions may simply map to kmm_malloc. + * + * This interface differs from DRVR_ALLOC in that the buffers are variable-sized. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * buffer - The address of a memory location provided by the caller in which to + * return the allocated buffer memory address. + * buflen - The size of the buffer required. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ************************************************************************************/ + +static int efm32_ioalloc(FAR struct usbhost_driver_s *drvr, + FAR uint8_t **buffer, size_t buflen) +{ + FAR uint8_t *alloc; + + DEBUGASSERT(drvr && buffer && buflen > 0); + + /* There is no special memory requirement */ + + alloc = (FAR uint8_t *)kmm_malloc(buflen); + if (!alloc) + { + return -ENOMEM; + } + + /* Return the allocated buffer */ + + *buffer = alloc; + return OK; +} + +/************************************************************************************ + * Name: efm32_iofree + * + * Description: + * Some hardware supports special memory in which IO data can be accessed more + * efficiently. This method provides a mechanism to free that IO buffer + * memory. If the underlying hardware does not support such "special" memory, + * this functions may simply map to kmm_free(). + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * buffer - The address of the allocated buffer memory to be freed. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ************************************************************************************/ + +static int efm32_iofree(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer) +{ + /* There is no special memory requirement */ + + DEBUGASSERT(drvr && buffer); + kmm_free(buffer); + return OK; +} + +/******************************************************************************* + * Name: efm32_ctrlin and efm32_ctrlout + * + * Description: + * Process a IN or OUT request on the control endpoint. These methods + * will enqueue the request and wait for it to complete. Only one transfer may be + * queued; Neither these methods nor the transfer() method can be called again + * until the control transfer functions returns. + * + * These are blocking methods; these functions will not return until the + * control transfer has completed. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * req - Describes the request to be sent. This request must lie in memory + * created by DRVR_ALLOC. + * buffer - A buffer used for sending the request and for returning any + * responses. This buffer must be large enough to hold the length value + * in the request description. buffer must have been allocated using DRVR_ALLOC + * + * NOTE: On an IN transaction, req and buffer may refer to the same allocated + * memory. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * - Only a single class bound to a single device is supported. + * - Called from a single thread so no mutual exclusion is required. + * - Never called from an interrupt handler. + * + *******************************************************************************/ + +static int efm32_ctrlin(FAR struct usbhost_driver_s *drvr, + FAR const struct usb_ctrlreq_s *req, + FAR uint8_t *buffer) +{ + struct efm32_usbhost_s *priv = (struct efm32_usbhost_s *)drvr; + uint16_t buflen; + uint32_t start; + uint32_t elapsed; + int retries; + int ret; + + DEBUGASSERT(drvr && req); + usbhost_vtrace2(OTGFS_VTRACE2_CTRLIN, req->type, req->req); + uvdbg("type:%02x req:%02x value:%02x%02x index:%02x%02x len:%02x%02x\n", + req->type, req->req, req->value[1], req->value[0], + req->index[1], req->index[0], req->len[1], req->len[0]); + + /* Extract values from the request */ + + buflen = efm32_getle16(req->len); + + /* We must have exclusive access to the USB host hardware and state structures */ + + efm32_takesem(&priv->exclsem); + + /* Loop, retrying until the retry time expires */ + + for (retries = 0; retries < EFM32_RETRY_COUNT; retries++) + { + /* Send the SETUP request */ + + ret = efm32_ctrl_sendsetup(priv, req); + if (ret < 0) + { + usbhost_trace1(OTGFS_TRACE1_SENDSETUP, -ret); + continue; + } + + /* Get the start time. Loop again until the timeout expires */ + + start = clock_systimer(); + do + { + /* Handle the IN data phase (if any) */ + + if (buflen > 0) + { + ret = efm32_ctrl_recvdata(priv, buffer, buflen); + if (ret < 0) + { + usbhost_trace1(OTGFS_TRACE1_RECVDATA, -ret); + } + } + + /* Handle the status OUT phase */ + + if (ret == OK) + { + priv->chan[priv->ep0out].outdata1 ^= true; + ret = efm32_ctrl_senddata(priv, NULL, 0); + if (ret == OK) + { + /* All success transactions exit here */ + + efm32_givesem(&priv->exclsem); + return OK; + } + + usbhost_trace1(OTGFS_TRACE1_SENDDATA, ret < 0 ? -ret : ret); + } + + /* Get the elapsed time (in frames) */ + + elapsed = clock_systimer() - start; + } + while (elapsed < EFM32_DATANAK_DELAY); + } + + /* All failures exit here after all retries and timeouts have been exhausted */ + + efm32_givesem(&priv->exclsem); + return -ETIMEDOUT; +} + +static int efm32_ctrlout(FAR struct usbhost_driver_s *drvr, + FAR const struct usb_ctrlreq_s *req, + FAR const uint8_t *buffer) +{ + struct efm32_usbhost_s *priv = (struct efm32_usbhost_s *)drvr; + uint16_t buflen; + uint32_t start; + uint32_t elapsed; + int retries; + int ret; + + DEBUGASSERT(drvr && req); + usbhost_vtrace2(OTGFS_VTRACE2_CTRLOUT, req->type, req->req); + uvdbg("type:%02x req:%02x value:%02x%02x index:%02x%02x len:%02x%02x\n", + req->type, req->req, req->value[1], req->value[0], + req->index[1], req->index[0], req->len[1], req->len[0]); + + /* Extract values from the request */ + + buflen = efm32_getle16(req->len); + + /* We must have exclusive access to the USB host hardware and state structures */ + + efm32_takesem(&priv->exclsem); + + /* Loop, retrying until the retry time expires */ + + for (retries = 0; retries < EFM32_RETRY_COUNT; retries++) + { + /* Send the SETUP request */ + + /* Send the SETUP request */ + + ret = efm32_ctrl_sendsetup(priv, req); + if (ret < 0) + { + usbhost_trace1(OTGFS_TRACE1_SENDSETUP, -ret); + continue; + } + + /* Get the start time. Loop again until the timeout expires */ + + start = clock_systimer(); + do + { + /* Handle the data OUT phase (if any) */ + + if (buflen > 0) + { + /* Start DATA out transfer (only one DATA packet) */ + + priv->chan[priv->ep0out].outdata1 = true; + ret = efm32_ctrl_senddata(priv, NULL, 0); + if (ret < 0) + { + usbhost_trace1(OTGFS_TRACE1_SENDDATA, -ret); + } + } + + /* Handle the status IN phase */ + + if (ret == OK) + { + ret = efm32_ctrl_recvdata(priv, NULL, 0); + if (ret == OK) + { + /* All success transactins exit here */ + + efm32_givesem(&priv->exclsem); + return OK; + } + + usbhost_trace1(OTGFS_TRACE1_RECVDATA, ret < 0 ? -ret : ret); + } + + /* Get the elapsed time (in frames) */ + + elapsed = clock_systimer() - start; + } + while (elapsed < EFM32_DATANAK_DELAY); + } + + /* All failures exit here after all retries and timeouts have been exhausted */ + + efm32_givesem(&priv->exclsem); + return -ETIMEDOUT; +} + +/******************************************************************************* + * Name: efm32_transfer + * + * Description: + * Process a request to handle a transfer descriptor. This method will + * enqueue the transfer request and return immediately. Only one transfer may be + * queued; Neither this method nor the ctrlin or ctrlout methods can be called + * again until this function returns. + * + * This is a blocking method; this functions will not return until the + * transfer has completed. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * ep - The IN or OUT endpoint descriptor for the device endpoint on which to + * perform the transfer. + * buffer - A buffer containing the data to be sent (OUT endpoint) or received + * (IN endpoint). buffer must have been allocated using DRVR_ALLOC + * buflen - The length of the data to be sent or received. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure: + * + * EAGAIN - If devices NAKs the transfer (or NYET or other error where + * it may be appropriate to restart the entire transaction). + * EPERM - If the endpoint stalls + * EIO - On a TX or data toggle error + * EPIPE - Overrun errors + * + * Assumptions: + * - Only a single class bound to a single device is supported. + * - Called from a single thread so no mutual exclusion is required. + * - Never called from an interrupt handler. + * + *******************************************************************************/ + +static int efm32_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, + FAR uint8_t *buffer, size_t buflen) +{ + FAR struct efm32_usbhost_s *priv = (FAR struct efm32_usbhost_s *)drvr; + unsigned int chidx = (unsigned int)ep; + int ret; + + uvdbg("chidx: %d buflen: %d\n", (unsigned int)ep, buflen); + + DEBUGASSERT(priv && buffer && chidx < EFM32_MAX_TX_FIFOS && buflen > 0); + + /* We must have exclusive access to the USB host hardware and state structures */ + + efm32_takesem(&priv->exclsem); + + /* Handle IN and OUT transfer slightly differently */ + + if (priv->chan[chidx].in) + { + ret = efm32_in_transfer(priv, chidx, buffer, buflen); + } + else + { + ret = efm32_out_transfer(priv, chidx, buffer, buflen); + } + + efm32_givesem(&priv->exclsem); + return ret; +} + +/******************************************************************************* + * Name: efm32_disconnect + * + * Description: + * Called by the class when an error occurs and driver has been disconnected. + * The USB host driver should discard the handle to the class instance (it is + * stale) and not attempt any further interaction with the class driver instance + * (until a new instance is received from the create() method). The driver + * should not called the class' disconnected() method. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * + * Returned Values: + * None + * + * Assumptions: + * - Only a single class bound to a single device is supported. + * - Never called from an interrupt handler. + * + *******************************************************************************/ + +static void efm32_disconnect(FAR struct usbhost_driver_s *drvr) +{ + struct efm32_usbhost_s *priv = (struct efm32_usbhost_s *)drvr; + DEBUGASSERT(priv); + + priv->class = NULL; +} + +/******************************************************************************* + * Initialization + *******************************************************************************/ +/******************************************************************************* + * Name: efm32_portreset + * + * Description: + * Reset the USB host port. + * + * NOTE: "Before starting to drive a USB reset, the application waits for the + * OTG interrupt triggered by the debounce done bit (DBCDNE bit in + * OTG_FS_GOTGINT), which indicates that the bus is stable again after the + * electrical debounce caused by the attachment of a pull-up resistor on DP + * (FS) or DM (LS). + * + * Input Parameters: + * priv -- USB host driver private data structure. + * + * Returned Value: + * None + * + *******************************************************************************/ + +static void efm32_portreset(FAR struct efm32_usbhost_s *priv) +{ + uint32_t regval; + + regval = efm32_getreg(EFM32_USB_HPRT); + regval &= ~(OTGFS_HPRT_PENA|OTGFS_HPRT_PCDET|OTGFS_HPRT_PENCHNG|OTGFS_HPRT_POCCHNG); + regval |= OTGFS_HPRT_PRST; + efm32_putreg(EFM32_USB_HPRT, regval); + + up_mdelay(20); + + regval &= ~OTGFS_HPRT_PRST; + efm32_putreg(EFM32_USB_HPRT, regval); + + up_mdelay(20); +} + +/******************************************************************************* + * Name: efm32_flush_txfifos + * + * Description: + * Flush the selected Tx FIFO. + * + * Input Parameters: + * txfnum -- USB host driver private data structure. + * + * Returned Value: + * None. + * + *******************************************************************************/ + +static void efm32_flush_txfifos(uint32_t txfnum) +{ + uint32_t regval; + uint32_t timeout; + + /* Initiate the TX FIFO flush operation */ + + regval = USB_GRSTCTL_TXFFLSH | txfnum; + efm32_putreg(regval, EFM32_USB_GRSTCTL); + + /* Wait for the FLUSH to complete */ + + for (timeout = 0; timeout < EFM32_FLUSH_DELAY; timeout++) + { + regval = efm32_getreg(EFM32_USB_GRSTCTL); + if ((regval & USB_GRSTCTL_TXFFLSH) == 0) + { + break; + } + } + + /* Wait for 3 PHY Clocks */ + + up_udelay(3); +} + +/******************************************************************************* + * Name: efm32_flush_rxfifo + * + * Description: + * Flush the Rx FIFO. + * + * Input Parameters: + * priv -- USB host driver private data structure. + * + * Returned Value: + * None. + * + *******************************************************************************/ + +static void efm32_flush_rxfifo(void) +{ + uint32_t regval; + uint32_t timeout; + + /* Initiate the RX FIFO flush operation */ + + efm32_putreg(USB_GRSTCTL_RXFFLSH, EFM32_USB_GRSTCTL); + + /* Wait for the FLUSH to complete */ + + for (timeout = 0; timeout < EFM32_FLUSH_DELAY; timeout++) + { + regval = efm32_getreg(EFM32_USB_GRSTCTL); + if ((regval & USB_GRSTCTL_RXFFLSH) == 0) + { + break; + } + } + + /* Wait for 3 PHY Clocks */ + + up_udelay(3); +} + +/******************************************************************************* + * Name: efm32_vbusdrive + * + * Description: + * Drive the Vbus +5V. + * + * Input Parameters: + * priv - USB host driver private data structure. + * state - True: Drive, False: Don't drive + * + * Returned Value: + * None. + * + *******************************************************************************/ + +static void efm32_vbusdrive(FAR struct efm32_usbhost_s *priv, bool state) +{ + uint32_t regval; + + /* Enable/disable the external charge pump */ + + efm32_usbhost_vbusdrive(0, state); + + /* Turn on the Host port power. */ + + regval = efm32_getreg(EFM32_USB_HPRT); + regval &= ~(OTGFS_HPRT_PENA|OTGFS_HPRT_PCDET|OTGFS_HPRT_PENCHNG|OTGFS_HPRT_POCCHNG); + + if (((regval & OTGFS_HPRT_PPWR) == 0) && state) + { + regval |= OTGFS_HPRT_PPWR; + efm32_putreg(EFM32_USB_HPRT, regval); + } + + if (((regval & OTGFS_HPRT_PPWR) != 0) && !state) + { + regval &= ~OTGFS_HPRT_PPWR; + efm32_putreg(EFM32_USB_HPRT, regval); + } + + up_mdelay(200); +} + +/******************************************************************************* + * Name: efm32_host_initialize + * + * Description: + * Initialize/re-initialize hardware for host mode operation. At present, + * this function is called only from efm32_hw_initialize(). But if OTG mode + * were supported, this function would also be called to swtich between + * host and device modes on a connector ID change interrupt. + * + * Input Parameters: + * priv -- USB host driver private data structure. + * + * Returned Value: + * None. + * + *******************************************************************************/ + +static void efm32_host_initialize(FAR struct efm32_usbhost_s *priv) +{ + uint32_t regval; + uint32_t offset; + int i; + + /* Restart the PHY Clock */ + + efm32_putreg(EFM32_USB_PCGCCTL, 0); + + /* Initialize Host Configuration (HCFG) register */ + + regval = efm32_getreg(EFM32_USB_HCFG); + regval &= ~_OTGFS_HCFG_FSLSPCS_MASK; + regval |= OTGFS_HCFG_FSLSPCS_FS48MHz; + efm32_putreg(EFM32_USB_HCFG, regval); + + /* Reset the host port */ + + efm32_portreset(priv); + + /* Clear the FS-/LS-only support bit in the HCFG register */ + + regval = efm32_getreg(EFM32_USB_HCFG); + regval &= ~OTGFS_HCFG_FSLSS; + efm32_putreg(EFM32_USB_HCFG, regval); + + /* Carve up FIFO memory for the Rx FIFO and the periodic and non-periodic Tx FIFOs */ + /* Configure Rx FIFO size (GRXFSIZ) */ + + efm32_putreg(EFM32_USB_GRXFSIZ, CONFIG_EFM32_OTGFS_RXFIFO_SIZE); + offset = CONFIG_EFM32_OTGFS_RXFIFO_SIZE; + + /* Setup the host non-periodic Tx FIFO size (HNPTXFSIZ) */ + + regval = (offset | (CONFIG_EFM32_OTGFS_NPTXFIFO_SIZE << _OTGFS_HNPTXFSIZ_NPTXFD_SHIFT)); + efm32_putreg(EFM32_USB_HNPTXFSIZ, regval); + offset += CONFIG_EFM32_OTGFS_NPTXFIFO_SIZE; + + /* Set up the host periodic Tx fifo size register (HPTXFSIZ) */ + + regval = (offset | (CONFIG_EFM32_OTGFS_PTXFIFO_SIZE << _OTGFS_HPTXFSIZ_PTXFD_SHIFT)); + efm32_putreg(EFM32_USB_HPTXFSIZ, regval); + + /* If OTG were supported, we sould need to clear HNP enable bit in the + * USB_OTG control register about here. + */ + + /* Flush all FIFOs */ + + efm32_flush_txfifos(USB_GRSTCTL_TXFNUM_HALL); + efm32_flush_rxfifo(); + + /* Clear all pending HC Interrupts */ + + for (i = 0; i < EFM32_NHOST_CHANNELS; i++) + { + efm32_putreg(EFM32_USB_HCINT(i), 0xffffffff); + efm32_putreg(EFM32_USB_HCINTMSK(i), 0); + } + + /* Driver Vbus +5V (the smoke test). Should be done elsewhere in OTG + * mode. + */ + + efm32_vbusdrive(priv, true); + + /* Enable host interrupts */ + + efm32_hostinit_enable(); +} + +/******************************************************************************* + * Name: efm32_sw_initialize + * + * Description: + * One-time setup of the host driver state structure. + * + * Input Parameters: + * priv -- USB host driver private data structure. + * + * Returned Value: + * None. + * + *******************************************************************************/ + +static inline void efm32_sw_initialize(FAR struct efm32_usbhost_s *priv) +{ + int i; + + /* Initialize the state data structure */ + + sem_init(&priv->eventsem, 0, 0); + sem_init(&priv->exclsem, 0, 1); + + priv->smstate = SMSTATE_DETACHED; + priv->ep0size = EFM32_EP0_MAX_PACKET_SIZE; + priv->devaddr = EFM32_DEF_DEVADDR; + priv->connected = false; + priv->lowspeed = false; + + /* Put all of the channels back in their initial, allocated state */ + + memset(priv->chan, 0, EFM32_MAX_TX_FIFOS * sizeof(struct efm32_chan_s)); + + /* Initialize each channel */ + + for (i = 0; i < EFM32_MAX_TX_FIFOS; i++) + { + FAR struct efm32_chan_s *chan = &priv->chan[i]; + sem_init(&chan->waitsem, 0, 0); + } +} + +/******************************************************************************* + * Name: efm32_hw_initialize + * + * Description: + * One-time setup of the host controller harware for normal operations. + * + * Input Parameters: + * priv -- USB host driver private data structure. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + *******************************************************************************/ + +static inline int efm32_hw_initialize(FAR struct efm32_usbhost_s *priv) +{ + uint32_t regval; + unsigned long timeout; + + /* "To initialize the core as host, the application must perform the + * following steps. + * 1. Program USB_GINTMSK.PRTINT to unmask. + * 2. Program the USB_HCFG register to select full-speed host. + * 3. Program the USB_HPRT.PRTPWR bit to 1. This drives VBUS on the USB. + * 4. Wait for the USB_HPRT.PRTCONNDET interrupt. This indicates that a + * device is connect to the port. + * 5. Program the USB_HPRT.PRTRST bit to 1. This starts the reset process. + * 6. Wait at least 10 ms for the reset process to complete. + * 7. Program the USB_HPRT.PRTRST bit to 0. + * 8. Wait for the USB_HPRT.PRTENCHNG interrupt. + * 9. Read the USB_HPRT.PRTSPD field to get the enumerated speed. + * 10. Program the USB_HFIR register with a value corresponding to the + * selected PHY clock. At this point, the host is up and running and + * the port register begins to report device disconnects, etc. The + * port is active with SOFs occurring down the enabled port. + * 11. Program the RXFSIZE register to select the size of the receive FIFO. + * 12. Program the NPTXFSIZE register to select the size and the start + * address of the Non-periodic Transmit FIFO for non-periodic + * transactions. + * 13. Program the USB_HPTXFSIZ register to select the size and start + * address of the Periodic Transmit FIFO for periodic transactions." + */ +#warning Review for missing logic + + /* Set the PHYSEL bit in the GUSBCFG register to select the OTG FS serial + * transceiver: "This bit is always 1 with write-only access" + */ + + regval = efm32_getreg(EFM32_USB_GUSBCFG);; + regval |= USB_GUSBCFG_PHYSEL; + efm32_putreg(EFM32_USB_GUSBCFG, regval); + + /* Reset after a PHY select and set Host mode. First, wait for AHB master + * IDLE state. + */ + + for (timeout = 0; timeout < EFM32_READY_DELAY; timeout++) + { + up_udelay(3); + regval = efm32_getreg(EFM32_USB_GRSTCTL); + if ((regval & USB_GRSTCTL_AHBIDL) != 0) + { + break; + } + } + + /* Then perform the core soft reset. */ + + efm32_putreg(EFM32_USB_GRSTCTL, USB_GRSTCTL_CSRST); + for (timeout = 0; timeout < EFM32_READY_DELAY; timeout++) + { + regval = efm32_getreg(EFM32_USB_GRSTCTL); + if ((regval & USB_GRSTCTL_CSRST) == 0) + { + break; + } + } + + /* Wait for 3 PHY Clocks */ + + up_udelay(3); + + /* Deactivate the power down */ + + regval = (USB_GCCFG_PWRDWN | USB_GCCFG_VBUSASEN | USB_GCCFG_VBUSBSEN); +#ifndef CONFIG_USBDEV_VBUSSENSING + regval |= USB_GCCFG_NOVBUSSENS; +#endif +#ifdef CONFIG_EFM32_OTGFS_SOFOUTPUT + regval |= USB_GCCFG_SOFOUTEN; +#endif + efm32_putreg(EFM32_USB_GCCFG, regval); + up_mdelay(20); + + /* Initialize OTG features: In order to support OTP, the HNPCAP and SRPCAP + * bits would need to be set in the GUSBCFG register about here. + */ + + /* Force Host Mode */ + + regval = efm32_getreg(EFM32_USB_GUSBCFG); + regval &= ~USB_GUSBCFG_FDMOD; + regval |= USB_GUSBCFG_FHMOD; + efm32_putreg(EFM32_USB_GUSBCFG, regval); + up_mdelay(50); + + /* Initialize host mode and return success */ + + efm32_host_initialize(priv); + return OK; +} + +/******************************************************************************* + * Public Functions + *******************************************************************************/ + +/******************************************************************************* + * Name: efm32_otgfshost_initialize + * + * Description: + * Initialize USB host device controller hardware. + * + * Input Parameters: + * controller -- If the device supports more than USB host controller, then + * this identifies which controller is being initialized. Normally, this + * is just zero. + * + * Returned Value: + * And instance of the USB host interface. The controlling task should + * use this interface to (1) call the wait() method to wait for a device + * to be connected, and (2) call the enumerate() method to bind the device + * to a class driver. + * + * Assumptions: + * - This function should called in the initialization sequence in order + * to initialize the USB device functionality. + * - Class drivers should be initialized prior to calling this function. + * Otherwise, there is a race condition if the device is already connected. + * + *******************************************************************************/ + +FAR struct usbhost_connection_s *efm32_otgfshost_initialize(int controller) +{ + /* At present, there is only support for a single OTG FS host. Hence it is + * pre-allocated as g_usbhost. However, in most code, the private data + * structure will be referenced using the 'priv' pointer (rather than the + * global data) in order to simplify any future support for multiple devices. + */ + + FAR struct efm32_usbhost_s *priv = &g_usbhost; + + /* Sanity checks */ + + DEBUGASSERT(controller == 0); + + /* "The USB requires the device to run from a 48 MHz crystal (2500 ppm or + * better). The core part of the USB will always run from HFCORECLKUSBC + * which is HFCLK undivided (48 MHz). The current consumption for the + * rest of the device can be reduced by dividing down HFCORECLK using + * the CMU_HFCORECLKDIV register. ..." + * + * "Follow these steps to enable the USB: + * 1. Enable the clock to the system part by setting USB in + * CMU_HFCORECLKEN0. + * 2. If the internal USB regulator is bypassed (by applying 3.3V on + * USB_VREGI and USB_VREGO externally), disable the regulator by + * setting VREGDIS in USB_CTRL. + * 3. If the PHY is powered from VBUS using the internal regulator, the + * VREGO sense circuit should be enabled by setting VREGOSEN in + * USB_CTRL. + * 4. Enable the USB PHY pins by setting PHYPEN in USB_ROUTE. + * 5. If host or OTG dual-role device, set VBUSENAP in USB_CTRL to the + * desired value and then enable the USB_VBUSEN pin in USB_ROUTE. Set + * the MODE for the pin to PUSHPULL. + * 6. If low-speed device, set DMPUAP in USB_CTRL to the desired value + * and then enable the USB_DMPU pin in USB_ROUTE. Set the MODE for the + * pin to PUSHPULL. + * 7. Make sure HFXO is ready and selected. The core part requires the + * undivided HFCLK to be 48 MHz when USB is active (during + * suspend/session-off a 32 kHz clock is used).. + * 8. Enable the clock to the core part by setting USBC in + * CMU_HFCORECLKEN0. + * 9. Wait for the core to come out of reset. This is easiest done by + * polling a core register with non-zero reset value until it reads a + * non-zero value. This takes approximately 20 48-MHz cycles. + * 10. Start initializing the USB core ..." + */ +#warning Missing Logic + + /* Make sure that interrupts from the OTG FS core are disabled */ + + efm32_gint_disable(); + + /* Reset the state of the host driver */ + + efm32_sw_initialize(priv); + + /* Initialize the USB OTG FS core */ + + efm32_hw_initialize(priv); + + /* Attach USB host controller interrupt handler */ + + if (irq_attach(EFM32_IRQ_USB, efm32_gint_isr) != 0) + { + usbhost_trace1(OTGFS_TRACE1_IRQATTACH, 0); + return NULL; + } + + /* Enable USB OTG FS global interrupts */ + + efm32_gint_enable(); + + /* Enable interrupts at the interrupt controller */ + + up_enable_irq(EFM32_IRQ_USB); + return &g_usbconn; +} + +#endif /* CONFIG_USBHOST && CONFIG_EFM32_OTGFS */ |