diff --git a/Documentation/platforms/arm/nrf54l/index.rst b/Documentation/platforms/arm/nrf54l/index.rst index b685cc74e74..ed31e427554 100644 --- a/Documentation/platforms/arm/nrf54l/index.rst +++ b/Documentation/platforms/arm/nrf54l/index.rst @@ -26,7 +26,7 @@ SPIM Yes TIMER Yes TWIM Yes UARTE Yes No hardware flow control -USBHS No +USBHS Yes LM20A/B device controller WDT No ========== ======= ===================================== @@ -149,6 +149,18 @@ pin assignment table. stop bit configuration. A format change returns ``-EBUSY`` while a byte is being transmitted or DMA error recovery is in progress. +USBHS +----- + +``CONFIG_NRF54L_USBDEV`` enables the LM20A/B USB device controller. +``CONFIG_USBDEV_DUALSPEED`` enables high-speed operation; otherwise the +controller uses full speed. The driver provides sixteen endpoints in each +direction, including EP0. IN endpoints support packets up to 512 bytes. + +USBHS is a Synopsys DWC2 core. ``nrf54l_usbd.c`` is a port of the STM32H7 +OTG device driver (``stm32h7/stm32_otgdev.c``); ``nrf54l_usbhs.c`` handles +the Nordic wrapper: clock, PHY, core reset and VBUS detection. + Power Management ================ diff --git a/arch/arm/src/nrf54l/CMakeLists.txt b/arch/arm/src/nrf54l/CMakeLists.txt index cb33809d1a5..fb36dc21186 100644 --- a/arch/arm/src/nrf54l/CMakeLists.txt +++ b/arch/arm/src/nrf54l/CMakeLists.txt @@ -74,6 +74,10 @@ if(CONFIG_PM) list(APPEND SRCS nrf54l_pminitialize.c) endif() +if(CONFIG_USBDEV) + list(APPEND SRCS nrf54l_usbhs.c nrf54l_usbd.c) +endif() + if(CONFIG_NRF54L_SOFTDEVICE_CONTROLLER) set(NRFXLIB_VER "3.4.1") set(NRFXLIB_DIR "${NUTTX_CHIP_ABS_DIR}/sdk-nrfxlib") diff --git a/arch/arm/src/nrf54l/Kconfig b/arch/arm/src/nrf54l/Kconfig index f562280d332..1dfb8d22130 100644 --- a/arch/arm/src/nrf54l/Kconfig +++ b/arch/arm/src/nrf54l/Kconfig @@ -260,6 +260,13 @@ config NRF54L_GRTC bool "GRTC" default n +config NRF54L_USBDEV + bool "USB Device" + default n + depends on ARCH_CHIP_NRF54LM20A || ARCH_CHIP_NRF54LM20B + select USBDEV + select SCHED_LPWORK + endmenu # nRF54L Peripheral Selection config NRF54L_USE_LFCLK diff --git a/arch/arm/src/nrf54l/Make.defs b/arch/arm/src/nrf54l/Make.defs index ba6fb4d5c8e..0d40a186667 100644 --- a/arch/arm/src/nrf54l/Make.defs +++ b/arch/arm/src/nrf54l/Make.defs @@ -81,6 +81,10 @@ ifeq ($(CONFIG_PM),y) CHIP_CSRCS += nrf54l_pminitialize.c endif +ifeq ($(CONFIG_USBDEV),y) +CHIP_CSRCS += nrf54l_usbhs.c nrf54l_usbd.c +endif + ifeq ($(CONFIG_NRF54L_SOFTDEVICE_CONTROLLER),y) NRFXLIB_UNPACK := sdk-nrfxlib diff --git a/arch/arm/src/nrf54l/hardware/nrf54l_usbd.h b/arch/arm/src/nrf54l/hardware/nrf54l_usbd.h new file mode 100644 index 00000000000..72e16837a1f --- /dev/null +++ b/arch/arm/src/nrf54l/hardware/nrf54l_usbd.h @@ -0,0 +1,947 @@ +/**************************************************************************** + * arch/arm/src/nrf54l/hardware/nrf54l_usbd.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_NRF54L_HARDWARE_NRF54L_USBD_H +#define __ARCH_ARM_SRC_NRF54L_HARDWARE_NRF54L_USBD_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "nrf54l_memorymap.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* General definitions */ + +#define OTG_EPTYPE_CTRL (0) /* Control */ +#define OTG_EPTYPE_ISOC (1) /* Isochronous */ +#define OTG_EPTYPE_BULK (2) /* Bulk */ +#define OTG_EPTYPE_INTR (3) /* Interrupt */ + +#define OTG_PID_DATA0 (0) +#define OTG_PID_DATA2 (1) +#define OTG_PID_DATA1 (2) +#define OTG_PID_MDATA (3) /* Non-control */ +#define OTG_PID_SETUP (3) /* Control */ + +/* Register Offsets *********************************************************/ + +/* Core global control and status registers */ + +#define NRF54L_USBD_GOTGCTL_OFFSET 0x0000 /* Control and status register */ +#define NRF54L_USBD_GOTGINT_OFFSET 0x0004 /* Interrupt register */ +#define NRF54L_USBD_GAHBCFG_OFFSET 0x0008 /* AHB configuration register */ +#define NRF54L_USBD_GUSBCFG_OFFSET 0x000c /* USB configuration register */ +#define NRF54L_USBD_GRSTCTL_OFFSET 0x0010 /* Reset register */ +#define NRF54L_USBD_GINTSTS_OFFSET 0x0014 /* Core interrupt register */ +#define NRF54L_USBD_GINTMSK_OFFSET 0x0018 /* Interrupt mask register */ +#define NRF54L_USBD_GRXSTSR_OFFSET 0x001c /* Receive status debug read/OTG status read register */ +#define NRF54L_USBD_GRXSTSP_OFFSET 0x0020 /* Receive status debug read/OTG status pop register */ +#define NRF54L_USBD_GRXFSIZ_OFFSET 0x0024 /* Receive FIFO size register */ +#define NRF54L_USBD_HNPTXFSIZ_OFFSET 0x0028 /* Host non-periodic transmit FIFO size register */ +#define NRF54L_USBD_DIEPTXF0_OFFSET 0x0028 /* Endpoint 0 Transmit FIFO size */ +#define NRF54L_USBD_HNPTXSTS_OFFSET 0x002c /* Non-periodic transmit FIFO/queue status register */ +#define NRF54L_USBD_GGPIO_OFFSET 0x0038 /* General purpose input/output register */ +#define NRF54L_USBD_GUID_OFFSET 0x003c /* User ID register */ +#define NRF54L_USBD_GSNPSID_OFFSET 0x0040 /* Synopsys core revision */ +#define NRF54L_USBD_GHWCFG1_OFFSET 0x0044 /* Hardware configuration 1 */ +#define NRF54L_USBD_GHWCFG2_OFFSET 0x0048 /* Hardware configuration 2 */ +#define NRF54L_USBD_GHWCFG3_OFFSET 0x004c /* Hardware configuration 3 */ +#define NRF54L_USBD_GHWCFG4_OFFSET 0x0050 /* Hardware configuration 4 */ +#define NRF54L_USBD_HPTXFSIZ_OFFSET 0x0100 /* Host periodic transmit FIFO size register */ + +#define NRF54L_USBD_DIEPTXF_OFFSET(n) (0x0104+(((n)-1) << 2)) /* Device IN endpoint n transmit FIFO size */ + +/* Host-mode control and status registers */ + +#define NRF54L_USBD_HCFG_OFFSET 0x0400 /* Host configuration register */ +#define NRF54L_USBD_HFIR_OFFSET 0x0404 /* Host frame interval register */ +#define NRF54L_USBD_HFNUM_OFFSET 0x0408 /* Host frame number/frame time remaining register */ +#define NRF54L_USBD_HPTXSTS_OFFSET 0x0410 /* Host periodic transmit FIFO/queue status register */ +#define NRF54L_USBD_HAINT_OFFSET 0x0414 /* Host all channels interrupt register */ +#define NRF54L_USBD_HAINTMSK_OFFSET 0x0418 /* Host all channels interrupt mask register */ +#define NRF54L_USBD_HPRT_OFFSET 0x0440 /* Host port control and status register */ + +#define NRF54L_USBD_CHAN_OFFSET(n) (0x500 + ((n) << 5)) /* Host channel n register block */ +#define NRF54L_USBD_HCCHAR_CHOFFSET 0x0000 /* Host channel characteristics register */ +#define NRF54L_USBD_HCINT_CHOFFSET 0x0008 /* Host channel interrupt register */ +#define NRF54L_USBD_HCINTMSK_CHOFFSET 0x000c /* Host channel interrupt mask register */ +#define NRF54L_USBD_HCTSIZ_CHOFFSET 0x0010 /* Host channel transfer size register */ + +#define NRF54L_USBD_HCCHAR_OFFSET(n) (0x500 + ((n) << 5)) /* Host channel n characteristics */ + +#define NRF54L_USBD_HCINT_OFFSET(n) (0x508 + ((n) << 5)) /* Host channel n interrupt status */ + +#define NRF54L_USBD_HCINTMSK_OFFSET(n) (0x50c + ((n) << 5)) /* Host channel n interrupt mask */ + +#define NRF54L_USBD_HCTSIZ_OFFSET(n) (0x510 + ((n) << 5)) /* Host channel n transfer size */ + +/* Device-mode control and status registers */ + +#define NRF54L_USBD_DCFG_OFFSET 0x0800 /* Device configuration register */ +#define NRF54L_USBD_DCTL_OFFSET 0x0804 /* Device control register */ +#define NRF54L_USBD_DSTS_OFFSET 0x0808 /* Device status register */ +#define NRF54L_USBD_DIEPMSK_OFFSET 0x0810 /* Device IN endpoint common interrupt mask register */ +#define NRF54L_USBD_DOEPMSK_OFFSET 0x0814 /* Device OUT endpoint common interrupt mask register */ +#define NRF54L_USBD_DAINT_OFFSET 0x0818 /* Device all endpoints interrupt register */ +#define NRF54L_USBD_DAINTMSK_OFFSET 0x081c /* All endpoints interrupt mask register */ +#define NRF54L_USBD_DVBUSDIS_OFFSET 0x0828 /* Device VBUS discharge time register */ +#define NRF54L_USBD_DVBUSPULSE_OFFSET 0x082c /* Device VBUS pulsing time register */ +#define NRF54L_USBD_DIEPEMPMSK_OFFSET 0x0834 /* Device IN endpoint FIFO empty interrupt mask register */ + +#define NRF54L_USBD_DIEP_OFFSET(n) (0x0900 + ((n) << 5)) /* Device IN endpoint n register block */ +#define NRF54L_USBD_DIEPCTL_EPOFFSET 0x0000 /* Device endpoint control register */ +#define NRF54L_USBD_DIEPINT_EPOFFSET 0x0008 /* Device endpoint interrupt register */ +#define NRF54L_USBD_DIEPTSIZ_EPOFFSET 0x0010 /* Device IN endpoint transfer size register */ +#define NRF54L_USBD_DTXFSTS_EPOFFSET 0x0018 /* Device IN endpoint transmit FIFO status register */ + +#define NRF54L_USBD_DIEPCTL_OFFSET(n) (0x0900 + ((n) << 5)) /* Device IN endpoint n control */ + +#define NRF54L_USBD_DIEPINT_OFFSET(n) (0x0908 + ((n) << 5)) /* Device IN endpoint n interrupt status */ + +#define NRF54L_USBD_DIEPTSIZ_OFFSET(n) (0x910 + ((n) << 5)) /* Device IN endpoint n transfer size */ + +#define NRF54L_USBD_DTXFSTS_OFFSET(n) (0x0918 + ((n) << 5)) /* Device IN endpoint n transmit FIFO status */ + +#define NRF54L_USBD_DOEP_OFFSET(n) (0x0b00 + ((n) << 5)) /* Device OUT endpoint n register block */ +#define NRF54L_USBD_DOEPCTL_EPOFFSET 0x0000 /* Device control OUT endpoint 0 control register */ +#define NRF54L_USBD_DOEPINT_EPOFFSET 0x0008 /* Device endpoint-x interrupt register */ + +#define NRF54L_USBD_DOEPCTL_OFFSET(n) (0x0b00 + ((n) << 5)) /* Device OUT endpoint n control */ + +#define NRF54L_USBD_DOEPINT_OFFSET(n) (0x0b08 + ((n) << 5)) /* Device OUT endpoint n interrupt status */ + +#define NRF54L_USBD_DOEPTSIZ_OFFSET(n) (0x0b10 + ((n) << 5)) /* Device OUT endpoint n transfer size */ + +/* Power and clock gating registers */ + +#define NRF54L_USBD_PCGCCTL_OFFSET 0x0e00 /* Power and clock gating control register */ + +/* Data FIFO (DFIFO) access registers */ + +#define NRF54L_USBD_DFIFO_DEP_OFFSET(n) (0x1000 + ((n) << 12)) /* Device endpoint n FIFO access */ +#define NRF54L_USBD_DFIFO_HCH_OFFSET(n) (0x1000 + ((n) << 12)) /* Host channel n FIFO access */ + +/* Register Addresses *******************************************************/ + +#define NRF54L_USBD_GOTGCTL (NRF54L_USBHSCORE_BASE+NRF54L_USBD_GOTGCTL_OFFSET) +#define NRF54L_USBD_GOTGINT (NRF54L_USBHSCORE_BASE+NRF54L_USBD_GOTGINT_OFFSET) +#define NRF54L_USBD_GAHBCFG (NRF54L_USBHSCORE_BASE+NRF54L_USBD_GAHBCFG_OFFSET) +#define NRF54L_USBD_GUSBCFG (NRF54L_USBHSCORE_BASE+NRF54L_USBD_GUSBCFG_OFFSET) +#define NRF54L_USBD_GRSTCTL (NRF54L_USBHSCORE_BASE+NRF54L_USBD_GRSTCTL_OFFSET) +#define NRF54L_USBD_GINTSTS (NRF54L_USBHSCORE_BASE+NRF54L_USBD_GINTSTS_OFFSET) +#define NRF54L_USBD_GINTMSK (NRF54L_USBHSCORE_BASE+NRF54L_USBD_GINTMSK_OFFSET) +#define NRF54L_USBD_GRXSTSR (NRF54L_USBHSCORE_BASE+NRF54L_USBD_GRXSTSR_OFFSET) +#define NRF54L_USBD_GRXSTSP (NRF54L_USBHSCORE_BASE+NRF54L_USBD_GRXSTSP_OFFSET) +#define NRF54L_USBD_GRXFSIZ (NRF54L_USBHSCORE_BASE+NRF54L_USBD_GRXFSIZ_OFFSET) +#define NRF54L_USBD_HNPTXFSIZ (NRF54L_USBHSCORE_BASE+NRF54L_USBD_HNPTXFSIZ_OFFSET) +#define NRF54L_USBD_DIEPTXF0 (NRF54L_USBHSCORE_BASE+NRF54L_USBD_DIEPTXF0_OFFSET) +#define NRF54L_USBD_HNPTXSTS (NRF54L_USBHSCORE_BASE+NRF54L_USBD_HNPTXSTS_OFFSET) +#define NRF54L_USBD_GGPIO (NRF54L_USBHSCORE_BASE+NRF54L_USBD_GGPIO_OFFSET) +#define NRF54L_USBD_GUID (NRF54L_USBHSCORE_BASE+NRF54L_USBD_GUID_OFFSET) +#define NRF54L_USBD_GSNPSID (NRF54L_USBHSCORE_BASE+NRF54L_USBD_GSNPSID_OFFSET) +#define NRF54L_USBD_GHWCFG1 (NRF54L_USBHSCORE_BASE+NRF54L_USBD_GHWCFG1_OFFSET) +#define NRF54L_USBD_GHWCFG2 (NRF54L_USBHSCORE_BASE+NRF54L_USBD_GHWCFG2_OFFSET) +#define NRF54L_USBD_GHWCFG3 (NRF54L_USBHSCORE_BASE+NRF54L_USBD_GHWCFG3_OFFSET) +#define NRF54L_USBD_GHWCFG4 (NRF54L_USBHSCORE_BASE+NRF54L_USBD_GHWCFG4_OFFSET) +#define NRF54L_USBD_HPTXFSIZ (NRF54L_USBHSCORE_BASE+NRF54L_USBD_HPTXFSIZ_OFFSET) + +#define NRF54L_USBD_DIEPTXF(n) (NRF54L_USBHSCORE_BASE+NRF54L_USBD_DIEPTXF_OFFSET(n)) + +/* Host-mode control and status registers */ + +#define NRF54L_USBD_HCFG (NRF54L_USBHSCORE_BASE+NRF54L_USBD_HCFG_OFFSET) +#define NRF54L_USBD_HFIR (NRF54L_USBHSCORE_BASE+NRF54L_USBD_HFIR_OFFSET) +#define NRF54L_USBD_HFNUM (NRF54L_USBHSCORE_BASE+NRF54L_USBD_HFNUM_OFFSET) +#define NRF54L_USBD_HPTXSTS (NRF54L_USBHSCORE_BASE+NRF54L_USBD_HPTXSTS_OFFSET) +#define NRF54L_USBD_HAINT (NRF54L_USBHSCORE_BASE+NRF54L_USBD_HAINT_OFFSET) +#define NRF54L_USBD_HAINTMSK (NRF54L_USBHSCORE_BASE+NRF54L_USBD_HAINTMSK_OFFSET) +#define NRF54L_USBD_HPRT (NRF54L_USBHSCORE_BASE+NRF54L_USBD_HPRT_OFFSET) + +#define NRF54L_USBD_CHAN(n) (NRF54L_USBHSCORE_BASE+NRF54L_USBD_CHAN_OFFSET(n)) + +#define NRF54L_USBD_HCCHAR(n) (NRF54L_USBHSCORE_BASE+NRF54L_USBD_HCCHAR_OFFSET(n)) + +#define NRF54L_USBD_HCINT(n) (NRF54L_USBHSCORE_BASE+NRF54L_USBD_HCINT_OFFSET(n)) + +#define NRF54L_USBD_HCINTMSK(n) (NRF54L_USBHSCORE_BASE+NRF54L_USBD_HCINTMSK_OFFSET(n)) + +#define NRF54L_USBD_HCTSIZ(n) (NRF54L_USBHSCORE_BASE+NRF54L_USBD_HCTSIZ_OFFSET(n)) + +/* Device-mode control and status registers */ + +#define NRF54L_USBD_DCFG (NRF54L_USBHSCORE_BASE+NRF54L_USBD_DCFG_OFFSET) +#define NRF54L_USBD_DCTL (NRF54L_USBHSCORE_BASE+NRF54L_USBD_DCTL_OFFSET) +#define NRF54L_USBD_DSTS (NRF54L_USBHSCORE_BASE+NRF54L_USBD_DSTS_OFFSET) +#define NRF54L_USBD_DIEPMSK (NRF54L_USBHSCORE_BASE+NRF54L_USBD_DIEPMSK_OFFSET) +#define NRF54L_USBD_DOEPMSK (NRF54L_USBHSCORE_BASE+NRF54L_USBD_DOEPMSK_OFFSET) +#define NRF54L_USBD_DAINT (NRF54L_USBHSCORE_BASE+NRF54L_USBD_DAINT_OFFSET) +#define NRF54L_USBD_DAINTMSK (NRF54L_USBHSCORE_BASE+NRF54L_USBD_DAINTMSK_OFFSET) +#define NRF54L_USBD_DVBUSDIS (NRF54L_USBHSCORE_BASE+NRF54L_USBD_DVBUSDIS_OFFSET) +#define NRF54L_USBD_DVBUSPULSE (NRF54L_USBHSCORE_BASE+NRF54L_USBD_DVBUSPULSE_OFFSET) +#define NRF54L_USBD_DIEPEMPMSK (NRF54L_USBHSCORE_BASE+NRF54L_USBD_DIEPEMPMSK_OFFSET) + +#define NRF54L_USBD_DIEP(n) (NRF54L_USBHSCORE_BASE+NRF54L_USBD_DIEP_OFFSET(n)) + +#define NRF54L_USBD_DIEPCTL(n) (NRF54L_USBHSCORE_BASE+NRF54L_USBD_DIEPCTL_OFFSET(n)) + +#define NRF54L_USBD_DIEPINT(n) (NRF54L_USBHSCORE_BASE+NRF54L_USBD_DIEPINT_OFFSET(n)) + +#define NRF54L_USBD_DIEPTSIZ(n) (NRF54L_USBHSCORE_BASE+NRF54L_USBD_DIEPTSIZ_OFFSET(n)) + +#define NRF54L_USBD_DTXFSTS(n) (NRF54L_USBHSCORE_BASE+NRF54L_USBD_DTXFSTS_OFFSET(n)) + +#define NRF54L_USBD_DOEP(n) (NRF54L_USBHSCORE_BASE+NRF54L_USBD_DOEP_OFFSET(n)) + +#define NRF54L_USBD_DOEPCTL(n) (NRF54L_USBHSCORE_BASE+NRF54L_USBD_DOEPCTL_OFFSET(n)) + +#define NRF54L_USBD_DOEPINT(n) (NRF54L_USBHSCORE_BASE+NRF54L_USBD_DOEPINT_OFFSET(n)) + +#define NRF54L_USBD_DOEPTSIZ(n) (NRF54L_USBHSCORE_BASE+NRF54L_USBD_DOEPTSIZ_OFFSET(n)) + +/* Power and clock gating registers */ + +#define NRF54L_USBD_PCGCCTL (NRF54L_USBHSCORE_BASE+NRF54L_USBD_PCGCCTL_OFFSET) + +/* Data FIFO (DFIFO) access registers */ + +#define NRF54L_USBD_DFIFO_DEP(n) (NRF54L_USBHSCORE_BASE+NRF54L_USBD_DFIFO_DEP_OFFSET(n)) +#define NRF54L_USBD_DFIFO_HCH(n) (NRF54L_USBHSCORE_BASE+NRF54L_USBD_DFIFO_HCH_OFFSET(n)) + +/* Register Bitfield Definitions ********************************************/ + +/* Core global control and status registers */ + +/* Control and status register */ + +#define OTG_GOTGCTL_SRQSCS (1 << 0) /* Bit 0: Session request success */ +#define OTG_GOTGCTL_SRQ (1 << 1) /* Bit 1: Session request */ +#define OTG_GOTGCTL_VBVALOEN (1 << 2) /* Bit 2: VBUS valid override enable */ +#define OTG_GOTGCTL_VBVALOVAL (1 << 3) /* Bit 3: VBUS valid override value */ +#define OTG_GOTGCTL_AVALOEN (1 << 4) /* Bit 4: A-peripheral session valid override enable */ +#define OTG_GOTGCTL_AVALOVAL (1 << 5) /* Bit 5: A-peripheral session valid override value */ +#define OTG_GOTGCTL_BVALOEN (1 << 6) /* Bit 6: B-peripheral session valid override enable */ +#define OTG_GOTGCTL_BVALOVAL (1 << 7) /* Bit 7: B-peripheral session valid override value */ +#define OTG_GOTGCTL_HNGSCS (1 << 8) /* Bit 8: Host negotiation success */ +#define OTG_GOTGCTL_HNPRQ (1 << 9) /* Bit 9: HNP request */ +#define OTG_GOTGCTL_HSHNPEN (1 << 10) /* Bit 10: host set HNP enable */ +#define OTG_GOTGCTL_DHNPEN (1 << 11) /* Bit 11: Device HNP enabled */ +#define OTG_GOTGCTL_EHEN (1 << 12) /* Bit 12: Embedded host enable */ + /* Bits 13-15: Reserved */ +#define OTG_GOTGCTL_CIDSTS (1 << 16) /* Bit 16: Connector ID status */ +#define OTG_GOTGCTL_DBCT (1 << 17) /* Bit 17: Long/short debounce time */ +#define OTG_GOTGCTL_ASVLD (1 << 18) /* Bit 18: A-session valid */ +#define OTG_GOTGCTL_BSVLD (1 << 19) /* Bit 19: B-session valid */ +#define OTG_GOTGCTL_OTGVER (1 << 20) /* Bit 20: OTG version */ + /* Bits 21-31: Reserved */ + +/* Interrupt register */ + + /* Bits 1-0 Reserved */ +#define OTG_GOTGINT_SEDET (1 << 2) /* Bit 2: Session end detected */ + /* Bits 3-7: Reserved */ +#define OTG_GOTGINT_SRSSCHG (1 << 8) /* Bit 8: Session request success status change */ +#define OTG_GOTGINT_HNSSCHG (1 << 9) /* Bit 9: Host negotiation success status change */ + /* Bits 16-10 Reserved */ +#define OTG_GOTGINT_HNGDET (1 << 17) /* Bit 17: Host negotiation detected */ +#define OTG_GOTGINT_ADTOCHG (1 << 18) /* Bit 18: A-device timeout change */ +#define OTG_GOTGINT_DBCDNE (1 << 19) /* Bit 19: Debounce done */ +#define OTG_GOTGINT_IDCHNG (1 << 20) /* Bit 20: Change in ID pin input value */ + /* Bits 21-31: Reserved */ + +/* AHB configuration register */ + +#define OTG_GAHBCFG_GINTMSK (1 << 0) /* Bit 0: Global interrupt mask */ +#define OTG_GAHBCFG_HBSTLEN_SHIFT (1) /* Bits 1-4: Bust length/type */ +# define OTG_GAHBCFG_HBSTLEN_MASK (0xf << OTG_GAHBCFG_HBSTLEN_SHIFT) +#define OTG_GAHBCFG_DMAEN (1 << 5) /* Bit 5: DMA enable */ + /* Bit 6: Reserved */ +#define OTG_GAHBCFG_TXFELVL (1 << 7) /* Bit 7: TxFIFO empty level */ +#define OTG_GAHBCFG_PTXFELVL (1 << 8) /* Bit 8: Periodic TxFIFO empty level */ + /* Bits 20-31: Reserved */ + +/* USB configuration register */ + +#define OTG_GUSBCFG_TOCAL_SHIFT (0) /* Bits 0-2: FS timeout calibration */ +#define OTG_GUSBCFG_TOCAL_MASK (7 << OTG_GUSBCFG_TOCAL_SHIFT) +#define OTG_GUSBCFG_PHYIF (1 << 3) /* Bit 3: 16-bit UTMI+ interface */ +#define OTG_GUSBCFG_ULPIUTMISEL (1 << 4) /* Bit 4: Select ULPI instead of UTMI+ */ +#define OTG_GUSBCFG_PHYSEL (1 << 6) /* Bit 6: Full Speed serial transceiver select */ + /* Bit 7: Reserved */ +#define OTG_GUSBCFG_SRPCAP (1 << 8) /* Bit 8: SRP-capable */ +#define OTG_GUSBCFG_HNPCAP (1 << 9) /* Bit 9: HNP-capable */ +#define OTG_GUSBCFG_TRDT_SHIFT (10) /* Bits 10-13: USB turnaround time */ +#define OTG_GUSBCFG_TRDT_MASK (15 << OTG_GUSBCFG_TRDT_SHIFT) +# define OTG_GUSBCFG_TRDT(n) ((n) << OTG_GUSBCFG_TRDT_SHIFT) + /* Bit 14: Reserved */ +#define OTG_GUSBCFG_PHYLPC (1 << 15) /* Bit 15: PHY low-power clock select */ + /* Bit 16: Reserved */ +#define OTG_GUSBCFG_ULPIFSL (1 << 17) /* Bit 17: ULPI FS/LS select */ +#define OTG_GUSBCFG_ULPIAR (1 << 18) /* Bit 18: ULPI Auto-resume */ +#define OTG_GUSBCFG_ULPICSM (1 << 19) /* Bit 19: ULPI clock SuspendM */ +#define OTG_GUSBCFG_ULPIEVBUSD (1 << 20) /* Bit 20: ULPI external VBUS drive */ +#define OTG_GUSBCFG_ULPIEVBUSI (1 << 21) /* Bit 21: ULPI external VBUS indicator */ +#define OTG_GUSBCFG_TSPDS (1 << 22) /* Bit 22: TermSel DLine pulsing selection */ +#define OTG_GUSBCFG_PCCI (1 << 23) /* Bit 23: Indicator complement */ +#define OTG_GUSBCFG_PTCI (1 << 24) /* Bit 24: Indicator pass through */ +#define OTG_GUSBCFG_ULPIIPD (1 << 25) /* Bit 25: ULPI interface protect disable */ +#define OTG_GUSBCFG_FHMOD (1 << 29) /* Bit 29: Force host mode */ +#define OTG_GUSBCFG_FDMOD (1 << 30) /* Bit 30: Force device mode */ + /* Bit 31: Reserved */ + +/* Reset register */ + +#define OTG_GRSTCTL_CSRST (1 << 0) /* Bit 0: Core soft reset */ +#define OTG_GRSTCTL_CSRSTDONE (1 << 29) /* Bit 29: Core soft reset done */ +#define OTG_GRSTCTL_PSRST (1 << 1) /* Bit 1: Partial soft reset */ + /* Bits 2-3 Reserved */ +#define OTG_GRSTCTL_RXFFLSH (1 << 4) /* Bit 4: RxFIFO flush */ +#define OTG_GRSTCTL_TXFFLSH (1 << 5) /* Bit 5: TxFIFO flush */ +#define OTG_GRSTCTL_TXFNUM_SHIFT (6) /* Bits 6-10: TxFIFO number */ +#define OTG_GRSTCTL_TXFNUM_MASK (31 << OTG_GRSTCTL_TXFNUM_SHIFT) +# define OTG_GRSTCTL_TXFNUM_HNONPER (0 << OTG_GRSTCTL_TXFNUM_SHIFT) /* Non-periodic TxFIFO flush in host mode */ +# define OTG_GRSTCTL_TXFNUM_HPER (1 << OTG_GRSTCTL_TXFNUM_SHIFT) /* Periodic TxFIFO flush in host mode */ +# define OTG_GRSTCTL_TXFNUM_HALL (16 << OTG_GRSTCTL_TXFNUM_SHIFT) /* Flush all the transmit FIFOs in host mode.*/ +# define OTG_GRSTCTL_TXFNUM_D(n) ((n) << OTG_GRSTCTL_TXFNUM_SHIFT) /* TXFIFO n flush in device mode, n=0-15 */ +# define OTG_GRSTCTL_TXFNUM_DALL (16 << OTG_GRSTCTL_TXFNUM_SHIFT) /* Flush all the transmit FIFOs in device mode.*/ +#define OTG_GRSTCTL_DMAREQ (1 << 30) /* Bit 30: DMA request signal enabled */ +#define OTG_GRSTCTL_AHBIDL (1 << 31) /* Bit 31: AHB master idle */ + +/* Core interrupt and Interrupt mask registers */ + +#define OTG_GINTSTS_CMOD (1 << 0) /* Bit 0: Current mode of operation */ +# define OTG_GINTSTS_DEVMODE (0) +# define OTG_GINTSTS_HOSTMODE (OTG_GINTSTS_CMOD) +#define OTG_GINT_MMIS (1 << 1) /* Bit 1: Mode mismatch interrupt */ +#define OTG_GINT_OTG (1 << 2) /* Bit 2: OTG interrupt */ +#define OTG_GINT_SOF (1 << 3) /* Bit 3: Start of frame */ +#define OTG_GINT_RXFLVL (1 << 4) /* Bit 4: RxFIFO non-empty */ +#define OTG_GINT_NPTXFE (1 << 5) /* Bit 5: Non-periodic TxFIFO empty */ +#define OTG_GINT_GINAKEFF (1 << 6) /* Bit 6: Global IN non-periodic NAK effective */ +#define OTG_GINT_GONAKEFF (1 << 7) /* Bit 7: Global OUT NAK effective */ + /* Bits 8-9: Reserved */ +#define OTG_GINT_ESUSP (1 << 10) /* Bit 10: Early suspend */ +#define OTG_GINT_USBSUSP (1 << 11) /* Bit 11: USB suspend */ +#define OTG_GINT_USBRST (1 << 12) /* Bit 12: USB reset */ +#define OTG_GINT_ENUMDNE (1 << 13) /* Bit 13: Enumeration done */ +#define OTG_GINT_ISOODRP (1 << 14) /* Bit 14: Isochronous OUT packet dropped interrupt */ +#define OTG_GINT_EOPF (1 << 15) /* Bit 15: End of periodic frame interrupt */ + /* Bits 16-17 Reserved */ +#define OTG_GINT_IEP (1 << 18) /* Bit 18: IN endpoint interrupt */ +#define OTG_GINT_OEP (1 << 19) /* Bit 19: OUT endpoint interrupt */ +#define OTG_GINT_IISOIXFR (1 << 20) /* Bit 20: Incomplete isochronous IN transfer */ +#define OTG_GINT_IISOOXFR (1 << 21) /* Bit 21: Incomplete isochronous OUT transfer (device) */ +#define OTG_GINT_IPXFR (1 << 21) /* Bit 21: Incomplete periodic transfer (host) */ +#define OTG_GINT_DATAFSUSP (1 << 22) /* Bit 22: Data fetch sispended */ +#define OTG_GINT_RSTDET (1 << 23) /* Bit 23: Reset detected interrupt */ +#define OTG_GINT_HPRT (1 << 24) /* Bit 24: Host port interrupt */ +#define OTG_GINT_HC (1 << 25) /* Bit 25: Host channels interrupt */ +#define OTG_GINT_PTXFE (1 << 26) /* Bit 26: Periodic TxFIFO empty */ +#define OTG_GINT_LPMINT (1 << 27) /* Bit 27: LPM interrupt */ +#define OTG_GINT_CIDSCHG (1 << 28) /* Bit 28: Connector ID status change */ +#define OTG_GINT_DISC (1 << 29) /* Bit 29: Disconnect detected interrupt */ +#define OTG_GINT_SRQ (1 << 30) /* Bit 30: Session request/new session detected interrupt */ +#define OTG_GINT_WKUP (1 << 31) /* Bit 31: Resume/remote wakeup detected interrupt */ +#define OTG_GINT_RESERVED ((1 << 8) | (1 << 9) | (1 << 16) | (1 << 17)) + +/* Receive status debug read/OTG status read and pop registers (host mode) */ + +#define OTG_GRXSTSH_CHNUM_SHIFT (0) /* Bits 0-3: Channel number */ +#define OTG_GRXSTSH_CHNUM_MASK (15 << OTG_GRXSTSH_CHNUM_SHIFT) +#define OTG_GRXSTSH_BCNT_SHIFT (4) /* Bits 4-14: Byte count */ +#define OTG_GRXSTSH_BCNT_MASK (0x7ff << OTG_GRXSTSH_BCNT_SHIFT) +#define OTG_GRXSTSH_DPID_SHIFT (15) /* Bits 15-16: Data PID */ +#define OTG_GRXSTSH_DPID_MASK (3 << OTG_GRXSTSH_DPID_SHIFT) +# define OTG_GRXSTSH_DPID_DATA0 (0 << OTG_GRXSTSH_DPID_SHIFT) +# define OTG_GRXSTSH_DPID_DATA2 (1 << OTG_GRXSTSH_DPID_SHIFT) +# define OTG_GRXSTSH_DPID_DATA1 (2 << OTG_GRXSTSH_DPID_SHIFT) +# define OTG_GRXSTSH_DPID_MDATA (3 << OTG_GRXSTSH_DPID_SHIFT) +#define OTG_GRXSTSH_PKTSTS_SHIFT (17) /* Bits 17-20: Packet status */ +#define OTG_GRXSTSH_PKTSTS_MASK (15 << OTG_GRXSTSH_PKTSTS_SHIFT) +# define OTG_GRXSTSH_PKTSTS_INRECVD (2 << OTG_GRXSTSH_PKTSTS_SHIFT) /* IN data packet received */ +# define OTG_GRXSTSH_PKTSTS_INDONE (3 << OTG_GRXSTSH_PKTSTS_SHIFT) /* IN transfer completed */ +# define OTG_GRXSTSH_PKTSTS_DTOGERR (5 << OTG_GRXSTSH_PKTSTS_SHIFT) /* Data toggle error */ +# define OTG_GRXSTSH_PKTSTS_HALTED (7 << OTG_GRXSTSH_PKTSTS_SHIFT) /* Channel halted */ + /* Bits 21-31: Reserved */ + +/* Receive status debug read/OTG status read and pop registers + * (device mode) + */ + +#define OTG_GRXSTSD_EPNUM_SHIFT (0) /* Bits 0-3: Endpoint number */ +#define OTG_GRXSTSD_EPNUM_MASK (15 << OTG_GRXSTSD_EPNUM_SHIFT) +#define OTG_GRXSTSD_BCNT_SHIFT (4) /* Bits 4-14: Byte count */ +#define OTG_GRXSTSD_BCNT_MASK (0x7ff << OTG_GRXSTSD_BCNT_SHIFT) +#define OTG_GRXSTSD_DPID_SHIFT (15) /* Bits 15-16: Data PID */ +#define OTG_GRXSTSD_DPID_MASK (3 << OTG_GRXSTSD_DPID_SHIFT) +# define OTG_GRXSTSD_DPID_DATA0 (0 << OTG_GRXSTSD_DPID_SHIFT) +# define OTG_GRXSTSD_DPID_DATA2 (1 << OTG_GRXSTSD_DPID_SHIFT) +# define OTG_GRXSTSD_DPID_DATA1 (2 << OTG_GRXSTSD_DPID_SHIFT) +# define OTG_GRXSTSD_DPID_MDATA (3 << OTG_GRXSTSD_DPID_SHIFT) +#define OTG_GRXSTSD_PKTSTS_SHIFT (17) /* Bits 17-20: Packet status */ +#define OTG_GRXSTSD_PKTSTS_MASK (15 << OTG_GRXSTSD_PKTSTS_SHIFT) +# define OTG_GRXSTSD_PKTSTS_OUTNAK (1 << OTG_GRXSTSD_PKTSTS_SHIFT) /* Global OUT NAK */ +# define OTG_GRXSTSD_PKTSTS_OUTRECVD (2 << OTG_GRXSTSD_PKTSTS_SHIFT) /* OUT data packet received */ +# define OTG_GRXSTSD_PKTSTS_OUTDONE (3 << OTG_GRXSTSD_PKTSTS_SHIFT) /* OUT transfer completed */ +# define OTG_GRXSTSD_PKTSTS_SETUPDONE (4 << OTG_GRXSTSD_PKTSTS_SHIFT) /* SETUP transaction completed */ +# define OTG_GRXSTSD_PKTSTS_SETUPRECVD (6 << OTG_GRXSTSD_PKTSTS_SHIFT) /* SETUP data packet received */ +#define OTG_GRXSTSD_FRMNUM_SHIFT (21) /* Bits 21-24: Frame number */ +#define OTG_GRXSTSD_FRMNUM_MASK (15 << OTG_GRXSTSD_FRMNUM_SHIFT) + /* Bits 25-26: Reserved */ +#define OTG_GRXSTSH_STSPHST (17) /* Bit 27: Status phase start */ + /* Bits 28-31: Reserved */ + +/* Receive FIFO size register */ + +#define OTG_GRXFSIZ_MASK (0x0fff) + +/* Host non-periodic transmit FIFO size register */ + +#define OTG_HNPTXFSIZ_NPTXFSA_SHIFT (0) /* Bits 0-11: Non-periodic transmit RAM start address */ +#define OTG_HNPTXFSIZ_NPTXFSA_MASK (0x0fff << OTG_HNPTXFSIZ_NPTXFSA_SHIFT) +#define OTG_HNPTXFSIZ_NPTXFD_SHIFT (16) /* Bits 16-27: Non-periodic TxFIFO depth */ +#define OTG_HNPTXFSIZ_NPTXFD_MASK (0x0fff << OTG_HNPTXFSIZ_NPTXFD_SHIFT) +# define OTG_HNPTXFSIZ_NPTXFD_MIN (16 << OTG_HNPTXFSIZ_NPTXFD_SHIFT) +# define OTG_HNPTXFSIZ_NPTXFD_MAX (256 << OTG_HNPTXFSIZ_NPTXFD_SHIFT) + +/* Endpoint 0 Transmit FIFO size */ + +#define OTG_DIEPTXF0_TX0FD_SHIFT (0) /* Bits 0-11: Endpoint 0 transmit RAM start address */ +#define OTG_DIEPTXF0_TX0FD_MASK (0x0fff << OTG_DIEPTXF0_TX0FD_SHIFT) +#define OTG_DIEPTXF0_TX0FSA_SHIFT (16) /* Bits 16-27: Endpoint 0 TxFIFO depth */ +#define OTG_DIEPTXF0_TX0FSA_MASK (0x0fff << OTG_DIEPTXF0_TX0FSA_SHIFT) +# define OTG_DIEPTXF0_TX0FSA_MIN (16 << OTG_DIEPTXF0_TX0FSA_SHIFT) +# define OTG_DIEPTXF0_TX0FSA_MAX (256 << OTG_DIEPTXF0_TX0FSA_SHIFT) + +/* Non-periodic transmit FIFO/queue status register */ + +#define OTG_HNPTXSTS_NPTXFSAV_SHIFT (0) /* Bits 0-15: Non-periodic TxFIFO space available */ +#define OTG_HNPTXSTS_NPTXFSAV_MASK (0xffff << OTG_HNPTXSTS_NPTXFSAV_SHIFT) +# define OTG_HNPTXSTS_NPTXFSAV_FULL (0 << OTG_HNPTXSTS_NPTXFSAV_SHIFT) +#define OTG_HNPTXSTS_NPTQXSAV_SHIFT (16) /* Bits 16-23: Non-periodic transmit request queue space available */ +#define OTG_HNPTXSTS_NPTQXSAV_MASK (0xff << OTG_HNPTXSTS_NPTQXSAV_SHIFT) +# define OTG_HNPTXSTS_NPTQXSAV_FULL (0 << OTG_HNPTXSTS_NPTQXSAV_SHIFT) +#define OTG_HNPTXSTS_NPTXQTOP_SHIFT (24) /* Bits 24-30: Top of the non-periodic transmit request queue */ +#define OTG_HNPTXSTS_NPTXQTOP_MASK (0x7f << OTG_HNPTXSTS_NPTXQTOP_SHIFT) +# define OTG_HNPTXSTS_TERMINATE (1 << 24) /* Bit 24: Terminate (last entry for selected channel/endpoint) */ +# define OTG_HNPTXSTS_TYPE_SHIFT (25) /* Bits 25-26: Status */ +# define OTG_HNPTXSTS_TYPE_MASK (3 << OTG_HNPTXSTS_TYPE_SHIFT) +# define OTG_HNPTXSTS_TYPE_INOUT (0 << OTG_HNPTXSTS_TYPE_SHIFT) /* IN/OUT token */ +# define OTG_HNPTXSTS_TYPE_ZLP (1 << OTG_HNPTXSTS_TYPE_SHIFT) /* Zero-length transmit packet (device IN/host OUT) */ +# define OTG_HNPTXSTS_TYPE_HALT (3 << OTG_HNPTXSTS_TYPE_SHIFT) /* Channel halt command */ +# define OTG_HNPTXSTS_CHNUM_SHIFT (27) /* Bits 27-30: Channel number */ +# define OTG_HNPTXSTS_CHNUM_MASK (15 << OTG_HNPTXSTS_CHNUM_SHIFT) +# define OTG_HNPTXSTS_EPNUM_SHIFT (27) /* Bits 27-30: Endpoint number */ +# define OTG_HNPTXSTS_EPNUM_MASK (15 << OTG_HNPTXSTS_EPNUM_SHIFT) + /* Bit 31 Reserved */ + +/* Hardware configuration 3 register */ + +#define OTG_GHWCFG3_DFIFODEPTH_SHIFT (16) /* Bits 16-31: FIFO depth in words */ +#define OTG_GHWCFG3_DFIFODEPTH_MASK (0xffff << OTG_GHWCFG3_DFIFODEPTH_SHIFT) + +/* Host periodic transmit FIFO size register */ + +#define OTG_HPTXFSIZ_PTXSA_SHIFT (0) /* Bits 0-15: Host periodic TxFIFO start address */ +#define OTG_HPTXFSIZ_PTXSA_MASK (0xffff << OTG_HPTXFSIZ_PTXSA_SHIFT) +#define OTG_HPTXFSIZ_PTXFD_SHIFT (16) /* Bits 16-31: Host periodic TxFIFO depth */ +#define OTG_HPTXFSIZ_PTXFD_MASK (0xffff << OTG_HPTXFSIZ_PTXFD_SHIFT) + +/* Device IN endpoint transmit FIFOn size register */ + +#define OTG_DIEPTXF_INEPTXSA_SHIFT (0) /* Bits 0-12: IN endpoint FIFOx transmit RAM start address */ +#define OTG_DIEPTXF_INEPTXSA_MASK (0x1fff << OTG_DIEPTXF_INEPTXSA_SHIFT) +#define OTG_DIEPTXF_INEPTXFD_SHIFT (16) /* Bits 16-27: IN endpoint TxFIFO depth */ +#define OTG_DIEPTXF_INEPTXFD_MASK (0x0fff << OTG_DIEPTXF_INEPTXFD_SHIFT) +# define OTG_DIEPTXF_INEPTXFD_MIN (16 << OTG_DIEPTXF_INEPTXFD_SHIFT) + +/* Host-mode control and status registers */ + +/* Host configuration register */ + +#define OTG_HCFG_FSLSPCS_SHIFT (0) /* Bits 0-1: FS/LS PHY clock select */ +#define OTG_HCFG_FSLSPCS_MASK (3 << OTG_HCFG_FSLSPCS_SHIFT) +# define OTG_HCFG_FSLSPCS_FS48MHz (1 << OTG_HCFG_FSLSPCS_SHIFT) /* FS host mode, PHY clock is running at 48 MHz */ +# define OTG_HCFG_FSLSPCS_LS48MHz (1 << OTG_HCFG_FSLSPCS_SHIFT) /* LS host mode, Select 48 MHz PHY clock frequency */ +# define OTG_HCFG_FSLSPCS_LS6MHz (2 << OTG_HCFG_FSLSPCS_SHIFT) /* LS host mode, Select 6 MHz PHY clock frequency */ +#define OTG_HCFG_FSLSS (1 << 2) /* Bit 2: FS- and LS-only support */ + /* Bits 31:3 Reserved */ + +/* Host frame interval register */ + +#define OTG_HFIR_SHIFT (0) /* Bits 0-16: Frame interval */ +#define OTG_HFIR_MASK (0xffff << OTG_HFIR_SHIFT) +#define OTG_HFIR_RLDCTRL (1 << 16) /* Bit 16: Reload control */ + /* Bits 17-31: Reserved */ + +/* Host frame number/frame time remaining register */ + +#define OTG_HFNUM_FRNUM_SHIFT (0) /* Bits 0-15: Frame number */ +#define OTG_HFNUM_FRNUM_MASK (0xffff << OTG_HFNUM_FRNUM_SHIFT) +#define OTG_HFNUM_FTREM_SHIFT (16) /* Bits 16-31: Frame time remaining */ +#define OTG_HFNUM_FTREM_MASK (0xffff << OTG_HFNUM_FTREM_SHIFT) + +/* Host periodic transmit FIFO/queue status register */ + +#define OTG_HPTXSTS_PTXFSAVL_SHIFT (0) /* Bits 0-15: Periodic transmit data FIFO space available */ +#define OTG_HPTXSTS_PTXFSAVL_MASK (0xffff << OTG_HPTXSTS_PTXFSAVL_SHIFT) +# define OTG_HPTXSTS_PTXFSAVL_FULL (0 << OTG_HPTXSTS_PTXFSAVL_SHIFT) +#define OTG_HPTXSTS_PTXQSAV_SHIFT (16) /* Bits 16-23: Periodic transmit request queue space available */ +#define OTG_HPTXSTS_PTXQSAV_MASK (0xff << OTG_HPTXSTS_PTXQSAV_SHIFT) +# define OTG_HPTXSTS_PTXQSAV_FULL (0 << OTG_HPTXSTS_PTXQSAV_SHIFT) +#define OTG_HPTXSTS_PTXQTOP_SHIFT (24) /* Bits 24-31: Top of the periodic transmit request queue */ +#define OTG_HPTXSTS_PTXQTOP_MASK (0xff << OTG_HPTXSTS_PTXQTOP_SHIFT) +# define OTG_HPTXSTS_TERMINATE (1 << 24) /* Bit 24: Terminate (last entry for selected channel/endpoint) */ +# define OTG_HPTXSTS_TYPE_SHIFT (25) /* Bits 25-26: Type */ +# define OTG_HPTXSTS_TYPE_MASK (3 << OTG_HPTXSTS_TYPE_SHIFT) +# define OTG_HPTXSTS_TYPE_INOUT (0 << OTG_HPTXSTS_TYPE_SHIFT) /* IN/OUT token */ +# define OTG_HPTXSTS_TYPE_ZLP (1 << OTG_HPTXSTS_TYPE_SHIFT) /* Zero-length transmit packet */ +# define OTG_HPTXSTS_TYPE_HALT (3 << OTG_HPTXSTS_TYPE_SHIFT) /* Disable channel command */ +# define OTG_HPTXSTS_EPNUM_SHIFT (27) /* Bits 27-30: Endpoint number */ +# define OTG_HPTXSTS_EPNUM_MASK (15 << OTG_HPTXSTS_EPNUM_SHIFT) +# define OTG_HPTXSTS_CHNUM_SHIFT (27) /* Bits 27-30: Channel number */ +# define OTG_HPTXSTS_CHNUM_MASK (15 << OTG_HPTXSTS_CHNUM_SHIFT) +# define OTG_HPTXSTS_ODD (1 << 31) /* Bit 31: Send in odd (vs even) frame */ + +/* Host all channels interrupt and all channels interrupt mask registers */ + +#define OTG_HAINT(n) (1 << (n)) /* Bits 15:0 HAINTM: Channel interrupt */ + +/* Host port control and status register */ + +#define OTG_HPRT_PCSTS (1 << 0) /* Bit 0: Port connect status */ +#define OTG_HPRT_PCDET (1 << 1) /* Bit 1: Port connect detected */ +#define OTG_HPRT_PENA (1 << 2) /* Bit 2: Port enable */ +#define OTG_HPRT_PENCHNG (1 << 3) /* Bit 3: Port enable/disable change */ +#define OTG_HPRT_POCA (1 << 4) /* Bit 4: Port overcurrent active */ +#define OTG_HPRT_POCCHNG (1 << 5) /* Bit 5: Port overcurrent change */ +#define OTG_HPRT_PRES (1 << 6) /* Bit 6: Port resume */ +#define OTG_HPRT_PSUSP (1 << 7) /* Bit 7: Port suspend */ +#define OTG_HPRT_PRST (1 << 8) /* Bit 8: Port reset */ + /* Bit 9: Reserved */ +#define OTG_HPRT_PLSTS_SHIFT (10) /* Bits 10-11: Port line status */ +#define OTG_HPRT_PLSTS_MASK (3 << OTG_HPRT_PLSTS_SHIFT) +# define OTG_HPRT_PLSTS_DP (1 << 10) /* Bit 10: Logic level of OTG_FS_FS_DP */ +# define OTG_HPRT_PLSTS_DM (1 << 11) /* Bit 11: Logic level of OTG_FS_FS_DM */ +#define OTG_HPRT_PPWR (1 << 12) /* Bit 12: Port power */ +#define OTG_HPRT_PTCTL_SHIFT (13) /* Bits 13-16: Port test control */ +#define OTG_HPRT_PTCTL_MASK (15 << OTG_HPRT_PTCTL_SHIFT) +# define OTG_HPRT_PTCTL_DISABLED (0 << OTG_HPRT_PTCTL_SHIFT) /* Test mode disabled */ +# define OTG_HPRT_PTCTL_J (1 << OTG_HPRT_PTCTL_SHIFT) /* Test_J mode */ +# define OTG_HPRT_PTCTL_L (2 << OTG_HPRT_PTCTL_SHIFT) /* Test_K mode */ +# define OTG_HPRT_PTCTL_SE0_NAK (3 << OTG_HPRT_PTCTL_SHIFT) /* Test_SE0_NAK mode */ +# define OTG_HPRT_PTCTL_PACKET (4 << OTG_HPRT_PTCTL_SHIFT) /* Test_Packet mode */ +# define OTG_HPRT_PTCTL_FORCE (5 << OTG_HPRT_PTCTL_SHIFT) /* Test_Force_Enable */ +#define OTG_HPRT_PSPD_SHIFT (17) /* Bits 17-18: Port speed */ +#define OTG_HPRT_PSPD_MASK (3 << OTG_HPRT_PSPD_SHIFT) +# define OTG_HPRT_PSPD_HS (0 << OTG_HPRT_PSPD_SHIFT) /* High speed */ +# define OTG_HPRT_PSPD_FS (1 << OTG_HPRT_PSPD_SHIFT) /* Full speed */ +# define OTG_HPRT_PSPD_LS (2 << OTG_HPRT_PSPD_SHIFT) /* Low speed */ + /* Bits 19-31: Reserved */ + +/* Host channel-n characteristics register */ + +#define OTG_HCCHAR_MPSIZ_SHIFT (0) /* Bits 0-10: Maximum packet size */ +#define OTG_HCCHAR_MPSIZ_MASK (0x7ff << OTG_HCCHAR_MPSIZ_SHIFT) +#define OTG_HCCHAR_EPNUM_SHIFT (11) /* Bits 11-14: Endpoint number */ +#define OTG_HCCHAR_EPNUM_MASK (15 << OTG_HCCHAR_EPNUM_SHIFT) +#define OTG_HCCHAR_EPDIR (1 << 15) /* Bit 15: Endpoint direction */ +# define OTG_HCCHAR_EPDIR_OUT (0) +# define OTG_HCCHAR_EPDIR_IN OTG_HCCHAR_EPDIR + /* Bit 16 Reserved */ +#define OTG_HCCHAR_LSDEV (1 << 17) /* Bit 17: Low-speed device */ +#define OTG_HCCHAR_EPTYP_SHIFT (18) /* Bits 18-19: Endpoint type */ +#define OTG_HCCHAR_EPTYP_MASK (3 << OTG_HCCHAR_EPTYP_SHIFT) +# define OTG_HCCHAR_EPTYP_CTRL (0 << OTG_HCCHAR_EPTYP_SHIFT) /* Control */ +# define OTG_HCCHAR_EPTYP_ISOC (1 << OTG_HCCHAR_EPTYP_SHIFT) /* Isochronous */ +# define OTG_HCCHAR_EPTYP_BULK (2 << OTG_HCCHAR_EPTYP_SHIFT) /* Bulk */ +# define OTG_HCCHAR_EPTYP_INTR (3 << OTG_HCCHAR_EPTYP_SHIFT) /* Interrupt */ +#define OTG_HCCHAR_MCNT_SHIFT (20) /* Bits 20-21: Multicount */ +#define OTG_HCCHAR_MCNT_MASK (3 << OTG_HCCHAR_MCNT_SHIFT) +#define OTG_HCCHAR_DAD_SHIFT (22) /* Bits 22-28: Device address */ +#define OTG_HCCHAR_DAD_MASK (0x7f << OTG_HCCHAR_DAD_SHIFT) +#define OTG_HCCHAR_ODDFRM (1 << 29) /* Bit 29: Odd frame */ +#define OTG_HCCHAR_CHDIS (1 << 30) /* Bit 30: Channel disable */ +#define OTG_HCCHAR_CHENA (1 << 31) /* Bit 31: Channel enable */ + +/* TODO: OTG host channel-n split control register */ + +/* Host channel-n interrupt and Host channel-0 interrupt mask registers */ + +#define OTG_HCINT_XFRC (1 << 0) /* Bit 0: Transfer completed */ +#define OTG_HCINT_CHH (1 << 1) /* Bit 1: Channel halted */ +#define OTG_HCINT_AHBERR (1 << 2) /* Bit 2: AHB error */ +#define OTG_HCINT_STALL (1 << 3) /* Bit 3: STALL response received interrupt */ +#define OTG_HCINT_NAK (1 << 4) /* Bit 4: NAK response received interrupt */ +#define OTG_HCINT_ACK (1 << 5) /* Bit 5: ACK response received/transmitted interrupt */ +#define OTG_HCINT_NYET (1 << 6) /* Bit 6: Response received interrupt */ +#define OTG_HCINT_TXERR (1 << 7) /* Bit 7: Transaction error */ +#define OTG_HCINT_BBERR (1 << 8) /* Bit 8: Babble error */ +#define OTG_HCINT_FRMOR (1 << 9) /* Bit 9: Frame overrun */ +#define OTG_HCINT_DTERR (1 << 10) /* Bit 10: Data toggle error */ + /* Bits 11-31: Reserved */ + +/* Host channel-n interrupt register */ + +#define OTG_HCTSIZ_XFRSIZ_SHIFT (0) /* Bits 0-18: Transfer size */ +#define OTG_HCTSIZ_XFRSIZ_MASK (0x7ffff << OTG_HCTSIZ_XFRSIZ_SHIFT) +#define OTG_HCTSIZ_PKTCNT_SHIFT (19) /* Bits 19-28: Packet count */ +#define OTG_HCTSIZ_PKTCNT_MASK (0x3ff << OTG_HCTSIZ_PKTCNT_SHIFT) +#define OTG_HCTSIZ_DPID_SHIFT (29) /* Bits 29-30: Data PID */ +#define OTG_HCTSIZ_DPID_MASK (3 << OTG_HCTSIZ_DPID_SHIFT) +# define OTG_HCTSIZ_DPID_DATA0 (0 << OTG_HCTSIZ_DPID_SHIFT) +# define OTG_HCTSIZ_DPID_DATA2 (1 << OTG_HCTSIZ_DPID_SHIFT) +# define OTG_HCTSIZ_DPID_DATA1 (2 << OTG_HCTSIZ_DPID_SHIFT) +# define OTG_HCTSIZ_DPID_MDATA (3 << OTG_HCTSIZ_DPID_SHIFT) /* Non-control */ +# define OTG_HCTSIZ_PID_SETUP (3 << OTG_HCTSIZ_DPID_SHIFT) /* Control */ + /* Bit 31: Reserved */ + +/* Device configuration register */ + +#define OTG_DCFG_DSPD_SHIFT (0) /* Bits 0-1: Device speed */ +#define OTG_DCFG_DSPD_MASK (3 << OTG_DCFG_DSPD_SHIFT) +# define OTG_DCFG_DSPD_HS (0 << OTG_DCFG_DSPD_SHIFT) /* High speed */ +# define OTG_DCFG_DSPD_FSHS (1 << OTG_DCFG_DSPD_SHIFT) /* Full speed using HS */ +# define OTG_DCFG_DSPD_FS (3 << OTG_DCFG_DSPD_SHIFT) /* Full speed using internal FS PHY */ +#define OTG_DCFG_NZLSOHSK (1 << 2) /* Bit 2: Non-zero-length status OUT handshake */ + /* Bit 3: Reserved */ +#define OTG_DCFG_DAD_SHIFT (4) /* Bits 4-10: Device address */ +#define OTG_DCFG_DAD_MASK (0x7f << OTG_DCFG_DAD_SHIFT) +#define OTG_DCFG_PFIVL_SHIFT (11) /* Bits 11-12: Periodic frame interval */ +#define OTG_DCFG_PFIVL_MASK (3 << OTG_DCFG_PFIVL_SHIFT) +# define OTG_DCFG_PFIVL_80PCT (0 << OTG_DCFG_PFIVL_SHIFT) /* 80% of the frame interval */ +# define OTG_DCFG_PFIVL_85PCT (1 << OTG_DCFG_PFIVL_SHIFT) /* 85% of the frame interval */ +# define OTG_DCFG_PFIVL_90PCT (2 << OTG_DCFG_PFIVL_SHIFT) /* 90% of the frame interval */ +# define OTG_DCFG_PFIVL_95PCT (3 << OTG_DCFG_PFIVL_SHIFT) /* 95% of the frame interval */ + /* Bit 13: Reserved */ +#define OTG_DCFG_XCVRDLY (1 << 14) /* Bit 14: Transceiver delay */ +#define OTG_DCFG_ERRATIMY (1 << 15) /* Bit 15: Erratic error interrupt mask */ + /* Bits 16-23: Reserved */ +#define OTG_DCFG_PERSCHIVL_SHIFT (24) /* Bits 24-25: Periodic schedule interval */ +#define OTG_DCFG_PERSCHIVL_MASK (3 << OTG_DCFG_PERSCHIVL_SHIFT) + /* Bits 26-31: Reserved */ + +/* Device control register */ + +#define OTG_TESTMODE_DISABLED (0) /* Test mode disabled */ +#define OTG_TESTMODE_J (1) /* Test_J mode */ +#define OTG_TESTMODE_K (2) /* Test_K mode */ +#define OTG_TESTMODE_SE0_NAK (3) /* Test_SE0_NAK mode */ +#define OTG_TESTMODE_PACKET (4) /* Test_Packet mode */ +#define OTG_TESTMODE_FORCE (5) /* Test_Force_Enable */ + +#define OTG_DCTL_RWUSIG (1 << 0) /* Bit 0: Remote wakeup signaling */ +#define OTG_DCTL_SDIS (1 << 1) /* Bit 1: Soft disconnect */ +#define OTG_DCTL_GINSTS (1 << 2) /* Bit 2: Global IN NAK status */ +#define OTG_DCTL_GONSTS (1 << 3) /* Bit 3: Global OUT NAK status */ +#define OTG_DCTL_TCTL_SHIFT (4) /* Bits 4-6: Test control */ +#define OTG_DCTL_TCTL_MASK (7 << OTG_DCTL_TCTL_SHIFT) +# define OTG_DCTL_TCTL_DISABLED (0 << OTG_DCTL_TCTL_SHIFT) /* Test mode disabled */ +# define OTG_DCTL_TCTL_J (1 << OTG_DCTL_TCTL_SHIFT) /* Test_J mode */ +# define OTG_DCTL_TCTL_K (2 << OTG_DCTL_TCTL_SHIFT) /* Test_K mode */ +# define OTG_DCTL_TCTL_SE0_NAK (3 << OTG_DCTL_TCTL_SHIFT) /* Test_SE0_NAK mode */ +# define OTG_DCTL_TCTL_PACKET (4 << OTG_DCTL_TCTL_SHIFT) /* Test_Packet mode */ +# define OTG_DCTL_TCTL_FORCE (5 << OTG_DCTL_TCTL_SHIFT) /* Test_Force_Enable */ +#define OTG_DCTL_SGINAK (1 << 7) /* Bit 7: Set global IN NAK */ +#define OTG_DCTL_CGINAK (1 << 8) /* Bit 8: Clear global IN NAK */ +#define OTG_DCTL_SGONAK (1 << 9) /* Bit 9: Set global OUT NAK */ +#define OTG_DCTL_CGONAK (1 << 10) /* Bit 10: Clear global OUT NAK */ +#define OTG_DCTL_POPRGDNE (1 << 11) /* Bit 11: Power-on programming done */ + /* Bits 12-17: Reserved */ +#define OTG_DCTL_DSBESLRJCT (1 << 18) /* Bit 18: Deep sleep BESL reject */ + /* Bits 19-31: Reserved */ + +/* Device status register */ + +#define OTG_DSTS_SUSPSTS (1 << 0) /* Bit 0: Suspend status */ +#define OTG_DSTS_ENUMSPD_SHIFT (1) /* Bits 1-2: Enumerated speed */ +#define OTG_DSTS_ENUMSPD_MASK (3 << OTG_DSTS_ENUMSPD_SHIFT) +# define OTG_DSTS_ENUMSPD_HS (0 << OTG_DSTS_ENUMSPD_SHIFT) +# define OTG_DSTS_ENUMSPD_FSHS (1 << OTG_DSTS_ENUMSPD_SHIFT) +# define OTG_DSTS_ENUMSPD_FS (3 << OTG_DSTS_ENUMSPD_SHIFT) +#define OTG_DSTS_EERR (1 << 3) /* Bit 3: Erratic error */ +#define OTG_DSTS_SOFFN_SHIFT (8) /* Bits 8-21: Frame number of the received SOF */ +#define OTG_DSTS_SOFFN_MASK (0x3fff << OTG_DSTS_SOFFN_SHIFT) +#define OTG_DSTS_SOFFN0 (1 << 8) /* Bits 8: Frame number even/odd bit */ +#define OTG_DSTS_SOFFN_EVEN 0 +#define OTG_DSTS_SOFFN_ODD OTG_DSTS_SOFFN0 + /* Bits 23-31: Reserved */ +#define OTG_DSTS_DEVLNSTS_SHIFT (22) /* Bits 22-23: Device line status */ +#define OTG_DSTS_DEVLNSTS_MASK (3 << OTG_DSTS_DEVLNSTS_SHIFT) +# define OTG_DSTS_DEVLNSTS_DP (1 << OTG_DSTS_DEVLNSTS_SHIFT) +# define OTG_DSTS_DEVLNSTS_DM (2 << OTG_DSTS_DEVLNSTS_SHIFT) + /* Bits 24-31: Reserved */ + +/* Device IN endpoint common interrupt mask register */ + +#define OTG_DIEPMSK_XFRCM (1 << 0) /* Bit 0: Transfer completed interrupt mask */ +#define OTG_DIEPMSK_EPDM (1 << 1) /* Bit 1: Endpoint disabled interrupt mask */ +#define OTG_DIEPMSK_AHBERR (1 << 2) /* Bit 2: AHB error */ +#define OTG_DIEPMSK_TOM (1 << 3) /* Bit 3: Timeout condition mask (Non-isochronous endpoints) */ +#define OTG_DIEPMSK_ITTXFEMSK (1 << 4) /* Bit 4: IN token received when TxFIFO empty mask */ +#define OTG_DIEPMSK_INEPNMM (1 << 5) /* Bit 5: IN token received with EP mismatch mask */ +#define OTG_DIEPMSK_INEPNEM (1 << 6) /* Bit 6: IN endpoint NAK effective mask */ + /* Bit 7: Reserved */ +#define OTG_DIEPMSK_TXFURM (1 << 8) /* Bit 8: FIFO underrun mask */ + /* Bits 9-12: Reserved */ +#define OTG_DIEPMSK_NAKM (1 << 13) /* Bit 13: NAK interrupt mask */ + /* Bits 14-31: Reserved */ + +/* Device OUT endpoint common interrupt mask register */ + +#define OTG_DOEPMSK_XFRCM (1 << 0) /* Bit 0: Transfer completed interrupt mask */ +#define OTG_DOEPMSK_EPDM (1 << 1) /* Bit 1: Endpoint disabled interrupt mask */ +#define OTG_DOEPMSK_AHBERR (1 << 2) /* Bit 2: AHB error */ +#define OTG_DOEPMSK_STUPM (1 << 3) /* Bit 3: SETUP phase done mask */ +#define OTG_DOEPMSK_OTEPDM (1 << 4) /* Bit 4: OUT token received when endpoint disabled mask */ +#define OTG_DOEPMSK_STSPHSRXM (1 << 5) /* Bit 5: Status phase received for control write mask */ +#define OTG_DOEPMSK_B2BSTUPM (1 << 6) /* Bit 6: Back-to-back SETUP packets received mask */ + /* Bit 7: Reserved */ +#define OTG_DOEPMSK_OUTPKTERRM (1 << 8) /* Bit 8: Out packet error mask */ + /* Bits 9-11: Reserved */ +#define OTG_DOEPMSK_BERR (1 << 12) /* Bit 12: Babble error interrupt mask */ +#define OTG_DOEPMSK_NAK (1 << 13) /* Bit 13: NAK interrupt mask */ +#define OTG_DOEPMSK_NYET (1 << 14) /* Bit 14: NYET interrupt mask */ + /* Bits 15-31: Reserved */ + +/* Device all endpoints interrupt and All endpoints interrupt mask + * registers + */ + +#define OTG_DAINT_IEP_SHIFT (0) /* Bits 0-15: IN endpoint interrupt bits */ +#define OTG_DAINT_IEP_MASK (0xffff << OTG_DAINT_IEP_SHIFT) +# define OTG_DAINT_IEP(n) (1 << (n)) +#define OTG_DAINT_OEP_SHIFT (16) /* Bits 16-31: OUT endpoint interrupt bits */ +#define OTG_DAINT_OEP_MASK (0xffff << OTG_DAINT_OEP_SHIFT) +# define OTG_DAINT_OEP(n) (1 << ((n)+16)) + +/* Device VBUS discharge time register */ + +#define OTG_DVBUSDIS_MASK (0xffff) + +/* Device VBUS pulsing time register */ + +#define OTG_DVBUSPULSE_MASK (0xfff) + +/* TODO: OTG device threshold control register */ + +/* Device IN endpoint FIFO empty interrupt mask register */ + +#define OTG_DIEPEMPMSK(n) (1 << (n)) + +/* TODO: OTG device each endpoint interrupt register */ + +/* TODO: OTG device each endpoint-1 interrupt */ + +/* Device control IN endpoint 0 maximum packet size */ + +#define OTG_DIEPCTL0_MPSIZ_SHIFT (0) /* Bits 0-1: Maximum packet size */ +#define OTG_DIEPCTL0_MPSIZ_MASK (3 << OTG_DIEPCTL0_MPSIZ_SHIFT) +# define OTG_DIEPCTL0_MPSIZ_64 (0 << OTG_DIEPCTL0_MPSIZ_SHIFT) /* 64 bytes */ +# define OTG_DIEPCTL0_MPSIZ_32 (1 << OTG_DIEPCTL0_MPSIZ_SHIFT) /* 32 bytes */ +# define OTG_DIEPCTL0_MPSIZ_16 (2 << OTG_DIEPCTL0_MPSIZ_SHIFT) /* 16 bytes */ +# define OTG_DIEPCTL0_MPSIZ_8 (3 << OTG_DIEPCTL0_MPSIZ_SHIFT) /* 8 bytes */ + +/* Device control IN endpoint n control register */ + +#define OTG_DIEPCTL_MPSIZ_SHIFT (0) /* Bits 0-10: Maximum packet size */ +#define OTG_DIEPCTL_MPSIZ_MASK (0x7ff << OTG_DIEPCTL_MPSIZ_SHIFT) + /* Bits 11-14: Reserved */ +#define OTG_DIEPCTL_USBAEP (1 << 15) /* Bit 15: USB active endpoint */ +#define OTG_DIEPCTL_EONUM (1 << 16) /* Bit 16: Even/odd frame */ +# define OTG_DIEPCTL_EVEN (0) +# define OTG_DIEPCTL_ODD OTG_DIEPCTL_EONUM +# define OTG_DIEPCTL_DATA0 (0) +# define OTG_DIEPCTL_DATA1 OTG_DIEPCTL_EONUM +#define OTG_DIEPCTL_NAKSTS (1 << 17) /* Bit 17: NAK status */ +#define OTG_DIEPCTL_EPTYP_SHIFT (18) /* Bits 18-19: Endpoint type */ +#define OTG_DIEPCTL_EPTYP_MASK (3 << OTG_DIEPCTL_EPTYP_SHIFT) +# define OTG_DIEPCTL_EPTYP_CTRL (0 << OTG_DIEPCTL_EPTYP_SHIFT) /* Control */ +# define OTG_DIEPCTL_EPTYP_ISOC (1 << OTG_DIEPCTL_EPTYP_SHIFT) /* Isochronous */ +# define OTG_DIEPCTL_EPTYP_BULK (2 << OTG_DIEPCTL_EPTYP_SHIFT) /* Bulk */ +# define OTG_DIEPCTL_EPTYP_INTR (3 << OTG_DIEPCTL_EPTYP_SHIFT) /* Interrupt */ + /* Bit 20: Reserved */ +#define OTG_DIEPCTL_STALL (1 << 21) /* Bit 21: STALL handshake */ +#define OTG_DIEPCTL_TXFNUM_SHIFT (22) /* Bits 22-25: TxFIFO number */ +#define OTG_DIEPCTL_TXFNUM_MASK (15 << OTG_DIEPCTL_TXFNUM_SHIFT) +#define OTG_DIEPCTL_CNAK (1 << 26) /* Bit 26: Clear NAK */ +#define OTG_DIEPCTL_SNAK (1 << 27) /* Bit 27: Set NAK */ +#define OTG_DIEPCTL_SD0PID (1 << 28) /* Bit 28: Set DATA0 PID (interrupt/bulk) */ +#define OTG_DIEPCTL_SEVNFRM (1 << 28) /* Bit 28: Set even frame (isochronous)) */ +#define OTG_DIEPCTL_SODDFRM (1 << 29) /* Bit 29: Set odd frame (isochronous) */ +#define OTG_DIEPCTL_EPDIS (1 << 30) /* Bit 30: Endpoint disable */ +#define OTG_DIEPCTL_EPENA (1 << 31) /* Bit 31: Endpoint enable */ + +/* Device endpoint-n interrupt register */ + +#define OTG_DIEPINT_XFRC (1 << 0) /* Bit 0: Transfer completed interrupt */ +#define OTG_DIEPINT_EPDISD (1 << 1) /* Bit 1: Endpoint disabled interrupt */ +#define OTG_DIEPINT_AHBERR (1 << 2) /* Bit 2: AHB error */ +#define OTG_DIEPINT_TOC (1 << 3) /* Bit 3: Timeout condition */ +#define OTG_DIEPINT_ITTXFE (1 << 4) /* Bit 4: IN token received when TxFIFO is empty */ +#define OTG_DIEPINT_INEPNM (1 << 5) /* Bit 5: */ +#define OTG_DIEPINT_INEPNE (1 << 6) /* Bit 6: IN endpoint NAK effective */ +#define OTG_DIEPINT_TXFE (1 << 7) /* Bit 7: Transmit FIFO empty */ +#define OTG_DIEPINT_TXFIFOUDRN (1 << 8) /* Bit 8: Transmit FIFO underrun */ + /* Bits 9-10: Reserved */ +#define OTG_DIEPINT_PKTDRPSTS (1 << 11) /* Bit 11: Packet dropped status */ + /* Bit 12: Reserved */ +#define OTG_DIEPINT_NAK (1 << 13) /* Bit 13: NAK input */ + /* Bits 14-31: Reserved */ + +/* Device IN endpoint 0 transfer size register */ + +#define OTG_DIEPTSIZ0_XFRSIZ_SHIFT (0) /* Bits 0-6: Transfer size */ +#define OTG_DIEPTSIZ0_XFRSIZ_MASK (0x7f << OTG_DIEPTSIZ0_XFRSIZ_SHIFT) + /* Bits 7-18: Reserved, must be kept at reset value */ +#define OTG_DIEPTSIZ0_PKTCNT_SHIFT (19) /* Bits 19-20: Packet count */ +#define OTG_DIEPTSIZ0_PKTCNT_MASK (3 << OTG_DIEPTSIZ0_PKTCNT_SHIFT) + /* Bits 21-31: Reserved, must be kept at reset value */ + +/* Device IN endpoint n transfer size register */ + +#define OTG_DIEPTSIZ_XFRSIZ_SHIFT (0) /* Bits 0-18: Transfer size */ +#define OTG_DIEPTSIZ_XFRSIZ_MASK (0x7ffff << OTG_DIEPTSIZ_XFRSIZ_SHIFT) +#define OTG_DIEPTSIZ_PKTCNT_SHIFT (19) /* Bit 19-28: Packet count */ +#define OTG_DIEPTSIZ_PKTCNT_MASK (0x3ff << OTG_DIEPTSIZ_PKTCNT_SHIFT) +#define OTG_DIEPTSIZ_MCNT_SHIFT (29) /* Bits 29-30: Multi count */ +#define OTG_DIEPTSIZ_MCNT_MASK (3 << OTG_DIEPTSIZ_MCNT_SHIFT) + /* Bit 31: Reserved */ + +/* Device OUT endpoint TxFIFO status register */ + +#define OTG_DTXFSTS_MASK (0xffff) + +/* Device OUT endpoint 0 control register */ + +#define OTG_DOEPCTL0_MPSIZ_SHIFT (0) /* Bits 0-1: Maximum packet size */ +#define OTG_DOEPCTL0_MPSIZ_MASK (3 << OTG_DOEPCTL0_MPSIZ_SHIFT) +# define OTG_DOEPCTL0_MPSIZ_64 (0 << OTG_DOEPCTL0_MPSIZ_SHIFT) /* 64 bytes */ +# define OTG_DOEPCTL0_MPSIZ_32 (1 << OTG_DOEPCTL0_MPSIZ_SHIFT) /* 32 bytes */ +# define OTG_DOEPCTL0_MPSIZ_16 (2 << OTG_DOEPCTL0_MPSIZ_SHIFT) /* 16 bytes */ +# define OTG_DOEPCTL0_MPSIZ_8 (3 << OTG_DOEPCTL0_MPSIZ_SHIFT) /* 8 bytes */ + /* Bits 2-14: Reserved */ +#define OTG_DOEPCTL0_USBAEP (1 << 15) /* Bit 15: USB active endpoint */ + /* Bit 16: Reserved */ +#define OTG_DOEPCTL0_NAKSTS (1 << 17) /* Bit 17: NAK status */ +#define OTG_DOEPCTL0_EPTYP_SHIFT (18) /* Bits 18-19: Endpoint type */ +#define OTG_DOEPCTL0_EPTYP_MASK (3 << OTG_DOEPCTL0_EPTYP_SHIFT) +# define OTG_DOEPCTL0_EPTYP_CTRL (0 << OTG_DOEPCTL0_EPTYP_SHIFT) /* Control (hard-coded) */ +#define OTG_DOEPCTL0_SNPM (1 << 20) /* Bit 20: Snoop mode */ +#define OTG_DOEPCTL0_STALL (1 << 21) /* Bit 21: STALL handshake */ + /* Bits 22-25: Reserved */ +#define OTG_DOEPCTL0_CNAK (1 << 26) /* Bit 26: Clear NAK */ +#define OTG_DOEPCTL0_SNAK (1 << 27) /* Bit 27: Set NAK */ + /* Bits 28-29: Reserved */ +#define OTG_DOEPCTL0_EPDIS (1 << 30) /* Bit 30: Endpoint disable */ +#define OTG_DOEPCTL0_EPENA (1 << 31) /* Bit 31: Endpoint enable */ + +/* Device OUT endpoint n control register */ + +#define OTG_DOEPCTL_MPSIZ_SHIFT (0) /* Bits 0-10: Maximum packet size */ +#define OTG_DOEPCTL_MPSIZ_MASK (0x7ff << OTG_DOEPCTL_MPSIZ_SHIFT) + /* Bits 11-14: Reserved */ +#define OTG_DOEPCTL_USBAEP (1 << 15) /* Bit 15: USB active endpoint */ +#define OTG_DOEPCTL_DPID (1 << 16) /* Bit 16: Endpoint data PID (interrupt/bulk) */ +# define OTG_DOEPCTL_DATA0 (0) +# define OTG_DOEPCTL_DATA1 OTG_DOEPCTL_DPID +#define OTG_DOEPCTL_EONUM (1 << 16) /* Bit 16: Even/odd frame (isochronous) */ +# define OTG_DOEPCTL_EVEN (0) +# define OTG_DOEPCTL_ODD OTG_DOEPCTL_EONUM +#define OTG_DOEPCTL_NAKSTS (1 << 17) /* Bit 17: NAK status */ +#define OTG_DOEPCTL_EPTYP_SHIFT (18) /* Bits 18-19: Endpoint type */ +#define OTG_DOEPCTL_EPTYP_MASK (3 << OTG_DOEPCTL_EPTYP_SHIFT) +# define OTG_DOEPCTL_EPTYP_CTRL (0 << OTG_DOEPCTL_EPTYP_SHIFT) /* Control */ +# define OTG_DOEPCTL_EPTYP_ISOC (1 << OTG_DOEPCTL_EPTYP_SHIFT) /* Isochronous */ +# define OTG_DOEPCTL_EPTYP_BULK (2 << OTG_DOEPCTL_EPTYP_SHIFT) /* Bulk */ +# define OTG_DOEPCTL_EPTYP_INTR (3 << OTG_DOEPCTL_EPTYP_SHIFT) /* Interrupt */ +#define OTG_DOEPCTL_SNPM (1 << 20) /* Bit 20: Snoop mode */ +#define OTG_DOEPCTL_STALL (1 << 21) /* Bit 21: STALL handshake */ + /* Bits 22-25: Reserved */ +#define OTG_DOEPCTL_CNAK (1 << 26) /* Bit 26: Clear NAK */ +#define OTG_DOEPCTL_SNAK (1 << 27) /* Bit 27: Set NAK */ +#define OTG_DOEPCTL_SD0PID (1 << 28) /* Bit 28: Set DATA0 PID (interrupt/bulk) */ +#define OTG_DOEPCTL_SEVNFRM (1 << 28) /* Bit 28: Set even frame (isochronous) */ +#define OTG_DOEPCTL_SD1PID (1 << 29) /* Bit 29: Set DATA1 PID (interrupt/bulk) */ +#define OTG_DOEPCTL_SODDFRM (1 << 29) /* Bit 29: Set odd frame (isochronous */ +#define OTG_DOEPCTL_EPDIS (1 << 30) /* Bit 30: Endpoint disable */ +#define OTG_DOEPCTL_EPENA (1 << 31) /* Bit 31: Endpoint enable */ + +/* Device endpoint-n interrupt register */ + +#define OTG_DOEPINT_XFRC (1 << 0) /* Bit 0: Transfer completed interrupt */ +#define OTG_DOEPINT_EPDISD (1 << 1) /* Bit 1: Endpoint disabled interrupt */ +#define OTG_DOEPINT_AHBERR (1 << 2) /* Bit 2: AHB error */ +#define OTG_DOEPINT_SETUP (1 << 3) /* Bit 3: SETUP phase done */ +#define OTG_DOEPINT_OTEPDIS (1 << 4) /* Bit 4: OUT token received when endpoint disabled */ +#define OTG_DOEPINT_STSPHSRXM (1 << 5) /* Bit 5: Status phase received for control write mask */ +#define OTG_DOEPINT_B2BSTUP (1 << 6) /* Bit 6: Back-to-back SETUP packets received */ + /* Bit 7: Reserved */ +#define OTG_DOEPINT_OUTPKTERRM (1 << 8) /* Bit 8: Out packet error mask */ + /* Bits 9-11: Reserved */ +#define OTG_DOEPINT_BERR (1 << 12) /* Bit 12: Babble error interrupt mask */ +#define OTG_DOEPINT_NAK (1 << 13) /* Bit 13: NAK interrupt mask */ +#define OTG_DOEPINT_NYET (1 << 14) /* Bit 14: NYET interrupt mask */ + /* Bits 15-31: Reserved */ + +/* Device OUT endpoint-0 transfer size register */ + +#define OTG_DOEPTSIZ0_XFRSIZ_SHIFT (0) /* Bits 0-6: Transfer size */ +#define OTG_DOEPTSIZ0_XFRSIZ_MASK (0x7f << OTG_DOEPTSIZ0_XFRSIZ_SHIFT) + /* Bits 7-18: Reserved */ +#define OTG_DOEPTSIZ0_PKTCNT (1 << 19) /* Bit 19 PKTCNT: Packet count */ + /* Bits 20-28: Reserved */ +#define OTG_DOEPTSIZ0_STUPCNT_SHIFT (29) /* Bits 29-30: SETUP packet count */ +#define OTG_DOEPTSIZ0_STUPCNT_MASK (3 << OTG_DOEPTSIZ0_STUPCNT_SHIFT) + /* Bit 31: Reserved */ + +/* Device OUT endpoint-n transfer size register */ + +#define OTG_DOEPTSIZ_XFRSIZ_SHIFT (0) /* Bits 0-18: Transfer size */ +#define OTG_DOEPTSIZ_XFRSIZ_MASK (0x7ffff << OTG_DOEPTSIZ_XFRSIZ_SHIFT) +#define OTG_DOEPTSIZ_PKTCNT_SHIFT (19) /* Bit 19-28: Packet count */ +#define OTG_DOEPTSIZ_PKTCNT_MASK (0x3ff << OTG_DOEPTSIZ_PKTCNT_SHIFT) +#define OTG_DOEPTSIZ_STUPCNT_SHIFT (29) /* Bits 29-30: SETUP packet count */ +#define OTG_DOEPTSIZ_STUPCNT_MASK (3 << OTG_DOEPTSIZ_STUPCNT_SHIFT) +#define OTG_DOEPTSIZ_RXDPID_SHIFT (29) /* Bits 29-30: Received data PID */ +#define OTG_DOEPTSIZ_RXDPID_MASK (3 << OTG_DOEPTSIZ_RXDPID_SHIFT) +# define OTG_DOEPTSIZ_RXDPID_DATA0 (0 << OTG_DOEPTSIZ_RXDPID_SHIFT) +# define OTG_DOEPTSIZ_RXDPID_DATA2 (1 << OTG_DOEPTSIZ_RXDPID_SHIFT) +# define OTG_DOEPTSIZ_RXDPID_DATA1 (2 << OTG_DOEPTSIZ_RXDPID_SHIFT) +# define OTG_DOEPTSIZ_RXDPID_MDATA (3 << OTG_DOEPTSIZ_RXDPID_SHIFT) + /* Bit 31: Reserved */ + +/* Power and clock gating control register */ + +#define OTG_PCGCCTL_STPPCLK (1 << 0) /* Bit 0: Stop PHY clock */ +#define OTG_PCGCCTL_GATEHCLK (1 << 1) /* Bit 1: Gate HCLK */ + /* Bits 2-3: Reserved */ +#define OTG_PCGCCTL_PHYSUSP (1 << 4) /* Bit 4: PHY Suspended */ +#define OTG_PCGCCTL_ENL1GTG (1 << 5) /* Bit 5: PHY Suspended */ +#define OTG_PCGCCTL_PHYSLEEP (1 << 6) /* Bit 6: PHY Suspended */ +#define OTG_PCGCCTL_SUSP (1 << 7) /* Bit 7: PHY Suspended */ + /* Bits 7-31: Reserved */ + +#endif /* __ARCH_ARM_SRC_NRF54L_HARDWARE_NRF54L_USBD_H */ diff --git a/arch/arm/src/nrf54l/hardware/nrf54l_usbhs.h b/arch/arm/src/nrf54l/hardware/nrf54l_usbhs.h new file mode 100644 index 00000000000..a5d6c8ed4c8 --- /dev/null +++ b/arch/arm/src/nrf54l/hardware/nrf54l_usbhs.h @@ -0,0 +1,146 @@ +/**************************************************************************** + * arch/arm/src/nrf54l/hardware/nrf54l_usbhs.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_NRF54L_HARDWARE_NRF54L_USBHS_H +#define __ARCH_ARM_SRC_NRF54L_HARDWARE_NRF54L_USBHS_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "hardware/nrf54l_memorymap.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* USBHS wrapper register offsets *******************************************/ + +#define NRF54L_USBHS_TASKS_START_OFFSET 0x0000 /* Start USB core */ +#define NRF54L_USBHS_TASKS_STOP_OFFSET 0x0004 /* Stop USB core */ +#define NRF54L_USBHS_PUBLISH_SOF_OFFSET 0x0180 /* Publish start-of-frame event */ +#define NRF54L_USBHS_ENABLE_OFFSET 0x0400 /* Enable USB core and PHY */ +#define NRF54L_USBHS_PHY_CONFIG_OFFSET 0x0440 /* PHY tuning configuration */ +#define NRF54L_USBHS_PHY_CLOCK_OFFSET 0x0444 /* PHY clock configuration */ +#define NRF54L_USBHS_PHY_BATTCHRG_OFFSET 0x0448 /* Battery charger detection control */ +#define NRF54L_USBHS_PHY_BATTCHRGSTATUS_OFFSET 0x044c /* Battery charger detection status */ +#define NRF54L_USBHS_PHY_INPUTOVERRIDE_OFFSET 0x0458 /* PHY input override enable */ +#define NRF54L_USBHS_PHY_OVERRIDEVALUES_OFFSET 0x045c /* PHY input override values */ +#define NRF54L_USBHS_PHY_RTUNE_OFFSET 0x0464 /* PHY resistance tuning */ + +/* USBHS wrapper register addresses *****************************************/ + +#define NRF54L_USBHS_TASKS_START (NRF54L_USBHS_BASE + NRF54L_USBHS_TASKS_START_OFFSET) +#define NRF54L_USBHS_TASKS_STOP (NRF54L_USBHS_BASE + NRF54L_USBHS_TASKS_STOP_OFFSET) +#define NRF54L_USBHS_PUBLISH_SOF (NRF54L_USBHS_BASE + NRF54L_USBHS_PUBLISH_SOF_OFFSET) +#define NRF54L_USBHS_ENABLE (NRF54L_USBHS_BASE + NRF54L_USBHS_ENABLE_OFFSET) +#define NRF54L_USBHS_PHY_CONFIG (NRF54L_USBHS_BASE + NRF54L_USBHS_PHY_CONFIG_OFFSET) +#define NRF54L_USBHS_PHY_CLOCK (NRF54L_USBHS_BASE + NRF54L_USBHS_PHY_CLOCK_OFFSET) +#define NRF54L_USBHS_PHY_BATTCHRG (NRF54L_USBHS_BASE + NRF54L_USBHS_PHY_BATTCHRG_OFFSET) +#define NRF54L_USBHS_PHY_BATTCHRGSTATUS (NRF54L_USBHS_BASE + NRF54L_USBHS_PHY_BATTCHRGSTATUS_OFFSET) +#define NRF54L_USBHS_PHY_INPUTOVERRIDE (NRF54L_USBHS_BASE + NRF54L_USBHS_PHY_INPUTOVERRIDE_OFFSET) +#define NRF54L_USBHS_PHY_OVERRIDEVALUES (NRF54L_USBHS_BASE + NRF54L_USBHS_PHY_OVERRIDEVALUES_OFFSET) +#define NRF54L_USBHS_PHY_RTUNE (NRF54L_USBHS_BASE + NRF54L_USBHS_PHY_RTUNE_OFFSET) + +/* USBHS wrapper register bit definitions ***********************************/ + +#define USBHS_TASKS_START (1 << 0) +#define USBHS_TASKS_STOP (1 << 0) +#define USBHS_PUBLISH_SOF_CHIDX_SHIFT 0 +#define USBHS_PUBLISH_SOF_CHIDX_MASK (0xff << USBHS_PUBLISH_SOF_CHIDX_SHIFT) +#define USBHS_PUBLISH_SOF_EN (1 << 31) +#define USBHS_ENABLE_CORE (1 << 0) +#define USBHS_ENABLE_PHY (1 << 1) + +/* PHY.CONFIG */ + +#define USBHS_PHY_CONFIG_PLLITUNE_SHIFT 0 +#define USBHS_PHY_CONFIG_PLLITUNE_MASK (3 << USBHS_PHY_CONFIG_PLLITUNE_SHIFT) +#define USBHS_PHY_CONFIG_PLLPTUNE_SHIFT 2 +#define USBHS_PHY_CONFIG_PLLPTUNE_MASK (15 << USBHS_PHY_CONFIG_PLLPTUNE_SHIFT) +#define USBHS_PHY_CONFIG_COMPDISTUNE_SHIFT 6 +#define USBHS_PHY_CONFIG_COMPDISTUNE_MASK (7 << USBHS_PHY_CONFIG_COMPDISTUNE_SHIFT) +#define USBHS_PHY_CONFIG_SQRXTUNE_SHIFT 9 +#define USBHS_PHY_CONFIG_SQRXTUNE_MASK (7 << USBHS_PHY_CONFIG_SQRXTUNE_SHIFT) +#define USBHS_PHY_CONFIG_VDATREFTUNE_SHIFT 12 +#define USBHS_PHY_CONFIG_VDATREFTUNE_MASK (3 << USBHS_PHY_CONFIG_VDATREFTUNE_SHIFT) +#define USBHS_PHY_CONFIG_TXHSXVTUNE_SHIFT 14 +#define USBHS_PHY_CONFIG_TXHSXVTUNE_MASK (3 << USBHS_PHY_CONFIG_TXHSXVTUNE_SHIFT) +#define USBHS_PHY_CONFIG_TXFSLSTUNE_SHIFT 16 +#define USBHS_PHY_CONFIG_TXFSLSTUNE_MASK (15 << USBHS_PHY_CONFIG_TXFSLSTUNE_SHIFT) +#define USBHS_PHY_CONFIG_TXVREFTUNE_SHIFT 20 +#define USBHS_PHY_CONFIG_TXVREFTUNE_MASK (15 << USBHS_PHY_CONFIG_TXVREFTUNE_SHIFT) +#define USBHS_PHY_CONFIG_TXRISETUNE_SHIFT 24 +#define USBHS_PHY_CONFIG_TXRISETUNE_MASK (3 << USBHS_PHY_CONFIG_TXRISETUNE_SHIFT) +#define USBHS_PHY_CONFIG_TXRESTUNE_SHIFT 26 +#define USBHS_PHY_CONFIG_TXRESTUNE_MASK (3 << USBHS_PHY_CONFIG_TXRESTUNE_SHIFT) +#define USBHS_PHY_CONFIG_TXPREEMPAMPTUNE_SHIFT 28 +#define USBHS_PHY_CONFIG_TXPREEMPAMPTUNE_MASK (3 << USBHS_PHY_CONFIG_TXPREEMPAMPTUNE_SHIFT) +#define USBHS_PHY_CONFIG_TXPREEMPPULSETUNE (1 << 30) + +/* PHY.CLOCK */ + +#define USBHS_PHY_CLOCK_FSEL_SHIFT 0 +#define USBHS_PHY_CLOCK_FSEL_MASK (7 << USBHS_PHY_CLOCK_FSEL_SHIFT) +#define USBHS_PHY_CLOCK_PLLBTUNE (1 << 3) +#define USBHS_PHY_CLOCK_COMMONONN (1 << 4) + +/* PHY.BATTCHRG and PHY.BATTCHRGSTATUS */ + +#define USBHS_PHY_BATTCHRG_CHRGSEL (1 << 0) +#define USBHS_PHY_BATTCHRG_VDATENB (1 << 1) +#define USBHS_PHY_BATTCHRG_VDATSRCENB (1 << 2) +#define USBHS_PHY_BATTCHRGSTATUS_CHGDET (1 << 1) +#define USBHS_PHY_BATTCHRGSTATUS_FSVPLUS (1 << 2) +#define USBHS_PHY_BATTCHRGSTATUS_FSVMINUS (1 << 3) + +/* PHY.INPUTOVERRIDE */ + +#define USBHS_PHY_INPUTOVERRIDE_OPMODE_SHIFT 18 +#define USBHS_PHY_INPUTOVERRIDE_OPMODE_MASK (3 << USBHS_PHY_INPUTOVERRIDE_OPMODE_SHIFT) +#define USBHS_PHY_INPUTOVERRIDE_XCVRSEL_SHIFT 20 +#define USBHS_PHY_INPUTOVERRIDE_XCVRSEL_MASK (3 << USBHS_PHY_INPUTOVERRIDE_XCVRSEL_SHIFT) +#define USBHS_PHY_INPUTOVERRIDE_DPPULLDOWN (1 << 23) +#define USBHS_PHY_INPUTOVERRIDE_DMPULLDOWN (1 << 24) +#define USBHS_PHY_INPUTOVERRIDE_SUSPENDM (1 << 25) +#define USBHS_PHY_INPUTOVERRIDE_VBUSVALID (1 << 30) +#define USBHS_PHY_INPUTOVERRIDE_ID (1 << 31) + +/* PHY.OVERRIDEVALUES */ + +#define USBHS_PHY_OVERRIDEVALUES_OPMODE_SHIFT 18 +#define USBHS_PHY_OVERRIDEVALUES_OPMODE_MASK (3 << USBHS_PHY_OVERRIDEVALUES_OPMODE_SHIFT) +#define USBHS_PHY_OVERRIDEVALUES_XCVRSEL_SHIFT 20 +#define USBHS_PHY_OVERRIDEVALUES_XCVRSEL_MASK (3 << USBHS_PHY_OVERRIDEVALUES_XCVRSEL_SHIFT) +#define USBHS_PHY_OVERRIDEVALUES_DPPULLDOWN (1 << 23) +#define USBHS_PHY_OVERRIDEVALUES_DMPULLDOWN (1 << 24) +#define USBHS_PHY_OVERRIDEVALUES_SUSPENDM (1 << 25) +#define USBHS_PHY_OVERRIDEVALUES_VBUSVALID (1 << 30) +#define USBHS_PHY_OVERRIDEVALUES_ID (1 << 31) + +/* PHY.RTUNE */ + +#define USBHS_PHY_RTUNE_RTUNESEL (1 << 0) +#define USBHS_PHY_RTUNE_RCALCODE_SHIFT 1 +#define USBHS_PHY_RTUNE_RCALCODE_MASK (15 << USBHS_PHY_RTUNE_RCALCODE_SHIFT) + +#endif /* __ARCH_ARM_SRC_NRF54L_HARDWARE_NRF54L_USBHS_H */ diff --git a/arch/arm/src/nrf54l/hardware/nrf54l_vregusb.h b/arch/arm/src/nrf54l/hardware/nrf54l_vregusb.h new file mode 100644 index 00000000000..f5e6d7f4e98 --- /dev/null +++ b/arch/arm/src/nrf54l/hardware/nrf54l_vregusb.h @@ -0,0 +1,75 @@ +/**************************************************************************** + * arch/arm/src/nrf54l/hardware/nrf54l_vregusb.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_NRF54L_HARDWARE_NRF54L_VREGUSB_H +#define __ARCH_ARM_SRC_NRF54L_HARDWARE_NRF54L_VREGUSB_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "hardware/nrf54l_memorymap.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Register offsets *********************************************************/ + +#define NRF54L_VREGUSB_TASKS_START_OFFSET 0x0000 /* Start USB regulator */ +#define NRF54L_VREGUSB_TASKS_STOP_OFFSET 0x0004 /* Stop USB regulator */ +#define NRF54L_VREGUSB_EVENTS_VBUSDETECTED_OFFSET 0x0104 /* VBUS detected */ +#define NRF54L_VREGUSB_EVENTS_VBUSREMOVED_OFFSET 0x0110 /* VBUS removed */ +#define NRF54L_VREGUSB_INTEN_OFFSET 0x0300 /* Enable or disable interrupts */ +#define NRF54L_VREGUSB_INTENSET_OFFSET 0x0304 /* Enable interrupts */ +#define NRF54L_VREGUSB_INTENCLR_OFFSET 0x0308 /* Disable interrupts */ +#define NRF54L_VREGUSB_INTPEND_OFFSET 0x030c /* Pending interrupts */ +#define NRF54L_VREGUSB_STATUS_OFFSET 0x0400 /* VBUS detection status */ + +/* Register addresses *******************************************************/ + +#define NRF54L_VREGUSB_TASKS_START (NRF54L_VREGUSB_BASE + NRF54L_VREGUSB_TASKS_START_OFFSET) +#define NRF54L_VREGUSB_TASKS_STOP (NRF54L_VREGUSB_BASE + NRF54L_VREGUSB_TASKS_STOP_OFFSET) +#define NRF54L_VREGUSB_EVENTS_VBUSDETECTED (NRF54L_VREGUSB_BASE + NRF54L_VREGUSB_EVENTS_VBUSDETECTED_OFFSET) +#define NRF54L_VREGUSB_EVENTS_VBUSREMOVED (NRF54L_VREGUSB_BASE + NRF54L_VREGUSB_EVENTS_VBUSREMOVED_OFFSET) +#define NRF54L_VREGUSB_INTEN (NRF54L_VREGUSB_BASE + NRF54L_VREGUSB_INTEN_OFFSET) +#define NRF54L_VREGUSB_INTENSET (NRF54L_VREGUSB_BASE + NRF54L_VREGUSB_INTENSET_OFFSET) +#define NRF54L_VREGUSB_INTENCLR (NRF54L_VREGUSB_BASE + NRF54L_VREGUSB_INTENCLR_OFFSET) +#define NRF54L_VREGUSB_INTPEND (NRF54L_VREGUSB_BASE + NRF54L_VREGUSB_INTPEND_OFFSET) +#define NRF54L_VREGUSB_STATUS (NRF54L_VREGUSB_BASE + NRF54L_VREGUSB_STATUS_OFFSET) + +/* Register bit definitions *************************************************/ + +#define VREGUSB_TASKS_START (1 << 0) +#define VREGUSB_TASKS_STOP (1 << 0) +#define VREGUSB_EVENTS_VBUSDETECTED (1 << 0) +#define VREGUSB_EVENTS_VBUSREMOVED (1 << 0) +#define VREGUSB_INT_VBUSDETECTED (1 << 1) +#define VREGUSB_INT_VBUSREMOVED (1 << 4) + +/* STATUS is specified by Nordic's VREGUSB driver, but absent from the MDK + * register structure. + */ + +#define VREGUSB_STATUS_VBUSDET (1 << 2) + +#endif /* __ARCH_ARM_SRC_NRF54L_HARDWARE_NRF54L_VREGUSB_H */ diff --git a/arch/arm/src/nrf54l/nrf54l_usbd.c b/arch/arm/src/nrf54l/nrf54l_usbd.c new file mode 100644 index 00000000000..296eb4d1388 --- /dev/null +++ b/arch/arm/src/nrf54l/nrf54l_usbd.c @@ -0,0 +1,5762 @@ +/**************************************************************************** + * arch/arm/src/nrf54l/nrf54l_usbd.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "chip.h" +#include "nrf54l_usbhs.h" +#include "hardware/nrf54l_usbd.h" +#include "arm_internal.h" + +#ifdef CONFIG_USBDEV + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +#ifndef CONFIG_USBDEV_EP0_MAXSIZE +# define CONFIG_USBDEV_EP0_MAXSIZE 64 +#endif + +#if CONFIG_USBDEV_EP0_MAXSIZE != 8 && CONFIG_USBDEV_EP0_MAXSIZE != 16 && \ + CONFIG_USBDEV_EP0_MAXSIZE != 32 && CONFIG_USBDEV_EP0_MAXSIZE != 64 +# error Unsupported endpoint 0 packet size +#endif + +#if defined(CONFIG_USBDEV_DUALSPEED) && CONFIG_USBDEV_EP0_MAXSIZE != 64 +# error High-speed endpoint 0 requires 64-byte packets +#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 + +/* FIFO sizes are in 32-bit words. Reserve one high-speed bulk packet per + * IN endpoint and a shared receive FIFO for all sixteen OUT endpoints. + */ + +#define NRF54L_NENDPOINTS 16 +#define NRF54L_RXFIFO_WORDS 768 +#define NRF54L_EP0_TXFIFO_WORDS 16 +#define NRF54L_TXFIFO_WORDS 128 + +#define OTG_GINT_RC_W1 (OTG_GINT_MMIS | \ + OTG_GINT_SOF | \ + OTG_GINT_ESUSP | \ + OTG_GINT_USBSUSP | \ + OTG_GINT_USBRST | \ + OTG_GINT_ENUMDNE | \ + OTG_GINT_ISOODRP | \ + OTG_GINT_EOPF | \ + OTG_GINT_IISOIXFR | \ + OTG_GINT_IISOOXFR | \ + OTG_GINT_RSTDET | \ + OTG_GINT_LPMINT | \ + OTG_GINT_CIDSCHG | \ + OTG_GINT_DISC | \ + OTG_GINT_SRQ | \ + OTG_GINT_WKUP) + +/* Debug ********************************************************************/ + +/* Trace error codes */ + +#define NRF54L_TRACEERR_ALLOCFAIL 0x01 +#define NRF54L_TRACEERR_BADCLEARFEATURE 0x02 +#define NRF54L_TRACEERR_BADDEVGETSTATUS 0x03 +#define NRF54L_TRACEERR_BADEPNO 0x04 +#define NRF54L_TRACEERR_BADEPGETSTATUS 0x05 +#define NRF54L_TRACEERR_BADGETCONFIG 0x06 +#define NRF54L_TRACEERR_BADGETSETDESC 0x07 +#define NRF54L_TRACEERR_BADGETSTATUS 0x08 +#define NRF54L_TRACEERR_BADSETADDRESS 0x09 +#define NRF54L_TRACEERR_BADSETCONFIG 0x0a +#define NRF54L_TRACEERR_BADSETFEATURE 0x0b +#define NRF54L_TRACEERR_BADTESTMODE 0x0c +#define NRF54L_TRACEERR_BINDFAILED 0x0d +#define NRF54L_TRACEERR_DISPATCHSTALL 0x0e +#define NRF54L_TRACEERR_DRIVER 0x0f +#define NRF54L_TRACEERR_DRIVERREGISTERED 0x10 +#define NRF54L_TRACEERR_EP0NOSETUP 0x11 +#define NRF54L_TRACEERR_EP0SETUPSTALLED 0x12 +#define NRF54L_TRACEERR_EPINNULLPACKET 0x13 +#define NRF54L_TRACEERR_EPINUNEXPECTED 0x14 +#define NRF54L_TRACEERR_EPOUTNULLPACKET 0x15 +#define NRF54L_TRACEERR_EPOUTUNEXPECTED 0x16 +#define NRF54L_TRACEERR_INVALIDCTRLREQ 0x17 +#define NRF54L_TRACEERR_INVALIDPARMS 0x18 +#define NRF54L_TRACEERR_IRQREGISTRATION 0x19 +#define NRF54L_TRACEERR_NOEP 0x1a +#define NRF54L_TRACEERR_NOTCONFIGURED 0x1b +#define NRF54L_TRACEERR_EPOUTQEMPTY 0x1c +#define NRF54L_TRACEERR_EPINREQEMPTY 0x1d +#define NRF54L_TRACEERR_NOOUTSETUP 0x1e +#define NRF54L_TRACEERR_POLLTIMEOUT 0x1f + +/* Trace interrupt codes */ + +#define NRF54L_TRACEINTID_USB 1 /* USB Interrupt entry/exit */ +#define NRF54L_TRACEINTID_INTPENDING 2 /* On each pass through the loop */ + +#define NRF54L_TRACEINTID_EPOUT (10 + 0) /* First level interrupt decode */ +#define NRF54L_TRACEINTID_EPIN (10 + 1) +#define NRF54L_TRACEINTID_MISMATCH (10 + 2) +#define NRF54L_TRACEINTID_WAKEUP (10 + 3) +#define NRF54L_TRACEINTID_SUSPEND (10 + 4) +#define NRF54L_TRACEINTID_SOF (10 + 5) +#define NRF54L_TRACEINTID_RXFIFO (10 + 6) +#define NRF54L_TRACEINTID_DEVRESET (10 + 7) +#define NRF54L_TRACEINTID_ENUMDNE (10 + 8) +#define NRF54L_TRACEINTID_IISOIXFR (10 + 9) +#define NRF54L_TRACEINTID_IISOOXFR (10 + 10) +#define NRF54L_TRACEINTID_SRQ (10 + 11) +#define NRF54L_TRACEINTID_OTG (10 + 12) + +#define NRF54L_TRACEINTID_EPOUT_XFRC (40 + 0) /* EPOUT second level decode */ +#define NRF54L_TRACEINTID_EPOUT_EPDISD (40 + 1) +#define NRF54L_TRACEINTID_EPOUT_SETUP (40 + 2) +#define NRF54L_TRACEINTID_DISPATCH (40 + 3) + +#define NRF54L_TRACEINTID_GETSTATUS (50 + 0) /* EPOUT third level decode */ +#define NRF54L_TRACEINTID_EPGETSTATUS (50 + 1) +#define NRF54L_TRACEINTID_DEVGETSTATUS (50 + 2) +#define NRF54L_TRACEINTID_IFGETSTATUS (50 + 3) +#define NRF54L_TRACEINTID_CLEARFEATURE (50 + 4) +#define NRF54L_TRACEINTID_SETFEATURE (50 + 5) +#define NRF54L_TRACEINTID_SETADDRESS (50 + 6) +#define NRF54L_TRACEINTID_GETSETDESC (50 + 7) +#define NRF54L_TRACEINTID_GETCONFIG (50 + 8) +#define NRF54L_TRACEINTID_SETCONFIG (50 + 9) +#define NRF54L_TRACEINTID_GETSETIF (50 + 10) +#define NRF54L_TRACEINTID_SYNCHFRAME (50 + 11) + +#define NRF54L_TRACEINTID_EPIN_XFRC (70 + 0) /* EPIN second level decode */ +#define NRF54L_TRACEINTID_EPIN_TOC (70 + 1) +#define NRF54L_TRACEINTID_EPIN_ITTXFE (70 + 2) +#define NRF54L_TRACEINTID_EPIN_EPDISD (70 + 3) +#define NRF54L_TRACEINTID_EPIN_TXFE (70 + 4) + +#define NRF54L_TRACEINTID_EPIN_EMPWAIT (80 + 0) /* EPIN second level decode */ + +#define NRF54L_TRACEINTID_OUTNAK (90 + 0) /* RXFLVL second level decode */ +#define NRF54L_TRACEINTID_OUTRECVD (90 + 1) +#define NRF54L_TRACEINTID_OUTDONE (90 + 2) +#define NRF54L_TRACEINTID_SETUPDONE (90 + 3) +#define NRF54L_TRACEINTID_SETUPRECVD (90 + 4) + +/* Endpoints ****************************************************************/ + +/* Odd physical endpoint numbers are IN; even are OUT */ + +#define NRF54L_EPPHYIN2LOG(epphy) ((uint8_t)(epphy)|USB_DIR_IN) +#define NRF54L_EPPHYOUT2LOG(epphy) ((uint8_t)(epphy)|USB_DIR_OUT) + +/* Endpoint 0 */ + +#define EP0 (0) + +/* The set of all endpoints available to the class implementation (1-7) */ + +#define NRF54L_EP_AVAILABLE (0xfffe) /* All available endpoints */ + +/* Maximum packet sizes for full speed endpoints */ + +#define NRF54L_MAXPACKET (64) /* Max packet size (1-64) */ + +/* Delays *******************************************************************/ + +#define NRF54L_READY_DELAY 200000 +#define NRF54L_FLUSH_DELAY 200000 + +/* Request queue operations *************************************************/ + +#define nrf54l_rqempty(ep) ((ep)->head == NULL) +#define nrf54l_rqpeek(ep) ((ep)->head) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Overall device state */ + +enum nrf54l_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 nrf54l_ep0state_e +{ + EP0STATE_IDLE = 0, /* Idle State, leave on receiving a SETUP packet or + * epsubmit: + * SET: In nrf54l_epin() and nrf54l_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 nrf54l_rxinterrupt() when SETUP OUT + * packet is received. + * TESTED: In nrf54l_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) nrf54l_ep0out_receive() when the OUT + * SETUP data phase completes, or (2) + * nrf54l_rxinterrupt() when an IN SETUP is + * packet received. + * TESTED: Tested in nrf54l_epout_interrupt() when + * SETUP phase is done to see if the SETUP + * command is ready to be processed. Also + * tested in nrf54l_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 nrf54l_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 + * nrf54l_ep0in_setupresponse() + * TESTED: Never + */ + EP0STATE_DATA_IN, /* Waiting for data out stage (with a USB request): + * SET: In nrf54l_epin_request() when a write + * request is processed on EP0. + * TESTED: In nrf54l_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 nrf54l_epout_request() when a read + * request is processed on EP0. + * TESTED: In nrf54l_epout() to see if we should + * revert to SETUP processing + */ +}; + +/* Parsed control request */ + +struct nrf54l_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 nrf54l_req_s +{ + struct usbdev_req_s req; /* Standard USB request */ + struct nrf54l_req_s *flink; /* Supports a singly linked list */ +}; + +/* This is the internal representation of an endpoint */ + +struct nrf54l_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 nrf54l_ep_s. + */ + + struct usbdev_ep_s ep; /* Standard endpoint structure */ + + /* NRF54L-specific fields */ + + struct nrf54l_usbdev_s *dev; /* Reference to private driver data */ + struct nrf54l_req_s *head; /* Request list for this endpoint */ + struct nrf54l_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 nrf54l_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 nrf54l_usbdev_s. + */ + + struct usbdev_s usbdev; + + /* The bound device class driver */ + + struct usbdevclass_driver_s *driver; + + struct work_s vbuswork; + bool initialized; + bool active; + bool connected; + volatile bool vbus_changed; + + /* NRF54L-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 nrf54l_devstate_e */ + uint8_t ep0state:4; /* See enum nrf54l_ep0state_e */ + uint8_t testmode:4; /* Selected test mode */ + uint16_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 occur AFTER the SETUP + * control request is processed. In that case, ep0data[] may be used as + * the response buffer. + * + * ep0datlen + * Length 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 nrf54l_ep_s epin[NRF54L_NENDPOINTS]; + struct nrf54l_ep_s epout[NRF54L_NENDPOINTS]; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Register operations ******************************************************/ + +#if defined(CONFIG_NRF54L_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG_FEATURES) +static uint32_t nrf54l_getreg(uint32_t addr); +static void nrf54l_putreg(uint32_t val, uint32_t addr); +#else +# define nrf54l_getreg(addr) getreg32(addr) +# define nrf54l_putreg(val,addr) putreg32(val,addr) +#endif + +/* Request queue operations *************************************************/ + +static struct nrf54l_req_s *nrf54l_req_remfirst(struct nrf54l_ep_s *privep); +static bool nrf54l_req_addlast(struct nrf54l_ep_s *privep, + struct nrf54l_req_s *req); + +/* Low level data transfers and request operations **************************/ + +/* Special endpoint 0 data transfer logic */ + +static int nrf54l_ep0_mpsiz(uint16_t maxpacket); +static void nrf54l_ep0in_setupresponse(struct nrf54l_usbdev_s *priv, + uint8_t *data, + uint32_t nbytes); +static inline void nrf54l_ep0in_transmitzlp(struct nrf54l_usbdev_s *priv); +static void nrf54l_ep0in_activate(void); + +static void nrf54l_ep0out_ctrlsetup(struct nrf54l_usbdev_s *priv); + +/* IN request and TxFIFO handling */ + +static void nrf54l_txfifo_write(struct nrf54l_ep_s *privep, + uint8_t *buf, int nbytes); +static void nrf54l_epin_transfer(struct nrf54l_ep_s *privep, + uint8_t *buf, int nbytes); +static void nrf54l_epin_request(struct nrf54l_usbdev_s *priv, + struct nrf54l_ep_s *privep); + +/* OUT request and RxFIFO handling */ + +static void nrf54l_rxfifo_read(struct nrf54l_ep_s *privep, + uint8_t *dest, uint16_t len); +static void nrf54l_rxfifo_discard(struct nrf54l_ep_s *privep, + int len); +static void nrf54l_epout_complete(struct nrf54l_usbdev_s *priv, + struct nrf54l_ep_s *privep); +static inline void nrf54l_ep0out_receive(struct nrf54l_ep_s *privep, + int bcnt); +static inline void nrf54l_epout_receive(struct nrf54l_ep_s *privep, + int bcnt); +static void nrf54l_epout_request(struct nrf54l_usbdev_s *priv, + struct nrf54l_ep_s *privep); + +/* General request handling */ + +static void nrf54l_ep_flush(struct nrf54l_ep_s *privep); +static void nrf54l_req_complete(struct nrf54l_ep_s *privep, + int16_t result); +static void nrf54l_req_cancel(struct nrf54l_ep_s *privep, + int16_t status); + +/* Interrupt handling *******************************************************/ + +static struct +nrf54l_ep_s *nrf54l_ep_findbyaddr(struct nrf54l_usbdev_s *priv, + uint16_t eplog); +static int nrf54l_req_dispatch(struct nrf54l_usbdev_s *priv, + const struct usb_ctrlreq_s *ctrl); +static void nrf54l_usbreset(struct nrf54l_usbdev_s *priv); + +/* Second level OUT endpoint interrupt processing */ + +static inline void nrf54l_ep0out_testmode(struct nrf54l_usbdev_s *priv, + uint16_t index); +static inline void nrf54l_ep0out_stdrequest(struct nrf54l_usbdev_s *priv, + struct nrf54l_ctrlreq_s *ctrlreq); +static inline void nrf54l_ep0out_setup(struct nrf54l_usbdev_s *priv); +static inline void nrf54l_epout(struct nrf54l_usbdev_s *priv, + uint8_t epno); +static inline void nrf54l_epout_interrupt(struct nrf54l_usbdev_s *priv); + +/* Second level IN endpoint interrupt processing */ + +static inline void nrf54l_epin_runtestmode(struct nrf54l_usbdev_s *priv); +static inline void nrf54l_epin(struct nrf54l_usbdev_s *priv, uint8_t epno); +static inline void nrf54l_epin_txfifoempty(struct nrf54l_usbdev_s *priv, + int epno); +static inline void nrf54l_epin_interrupt(struct nrf54l_usbdev_s *priv); + +/* Other second level interrupt processing */ + +static inline void nrf54l_resumeinterrupt(struct nrf54l_usbdev_s *priv); +static inline void nrf54l_suspendinterrupt(struct nrf54l_usbdev_s *priv); +static inline void nrf54l_rxinterrupt(struct nrf54l_usbdev_s *priv); +static inline void nrf54l_enuminterrupt(struct nrf54l_usbdev_s *priv); +#ifdef CONFIG_USBDEV_ISOCHRONOUS +static inline void nrf54l_isocininterrupt(struct nrf54l_usbdev_s *priv); +static inline void nrf54l_isocoutinterrupt(struct nrf54l_usbdev_s *priv); +#endif + +/* First level interrupt processing */ + +static int nrf54l_usbinterrupt(int irq, void *context, + void *arg); + +/* Endpoint operations ******************************************************/ + +/* Global OUT NAK controls */ + +static void nrf54l_enablegonak(struct nrf54l_ep_s *privep); +static void nrf54l_disablegonak(struct nrf54l_ep_s *privep); + +/* Endpoint configuration */ + +static int nrf54l_epout_configure(struct nrf54l_ep_s *privep, + uint8_t eptype, + uint16_t maxpacket); +static int nrf54l_epin_configure(struct nrf54l_ep_s *privep, + uint8_t eptype, uint16_t maxpacket); +static int nrf54l_ep_configure(struct usbdev_ep_s *ep, + const struct usb_epdesc_s *desc, + bool last); +static void nrf54l_ep0_configure(struct nrf54l_usbdev_s *priv); + +/* Endpoint disable */ + +static void nrf54l_epout_disable(struct nrf54l_ep_s *privep); +static void nrf54l_epin_disable(struct nrf54l_ep_s *privep); +static int nrf54l_ep_disable(struct usbdev_ep_s *ep); + +/* Endpoint request management */ + +static struct usbdev_req_s *nrf54l_ep_allocreq(struct usbdev_ep_s *ep); +static void nrf54l_ep_freereq(struct usbdev_ep_s *ep, + struct usbdev_req_s *); + +/* Endpoint buffer management */ + +#ifdef CONFIG_USBDEV_DMA +static void *nrf54l_ep_allocbuffer(struct usbdev_ep_s *ep, + unsigned bytes); +static void nrf54l_ep_freebuffer(struct usbdev_ep_s *ep, + void *buf); +#endif + +/* Endpoint request submission */ + +static int nrf54l_ep_submit(struct usbdev_ep_s *ep, + struct usbdev_req_s *req); + +/* Endpoint request cancellation */ + +static int nrf54l_ep_cancel(struct usbdev_ep_s *ep, + struct usbdev_req_s *req); + +/* Stall handling */ + +static int nrf54l_epout_setstall(struct nrf54l_ep_s *privep); +static int nrf54l_epin_setstall(struct nrf54l_ep_s *privep); +static int nrf54l_ep_setstall(struct nrf54l_ep_s *privep); +static int nrf54l_ep_clrstall(struct nrf54l_ep_s *privep); +static int nrf54l_ep_stall(struct usbdev_ep_s *ep, bool resume); +static void nrf54l_ep0_stall(struct nrf54l_usbdev_s *priv); + +/* Endpoint allocation */ + +static struct usbdev_ep_s *nrf54l_ep_alloc(struct usbdev_s *dev, + uint8_t epno, bool in, + uint8_t eptype); +static void nrf54l_ep_free(struct usbdev_s *dev, + struct usbdev_ep_s *ep); + +/* USB device controller operations *****************************************/ + +static int nrf54l_getframe(struct usbdev_s *dev); +static int nrf54l_wakeup(struct usbdev_s *dev); +static int nrf54l_selfpowered(struct usbdev_s *dev, + bool selfpowered); +static int nrf54l_pullup(struct usbdev_s *dev, bool enable); +static void nrf54l_setaddress(struct nrf54l_usbdev_s *priv, + uint16_t address); +static int nrf54l_txfifo_flush(uint32_t txfnum); +static int nrf54l_rxfifo_flush(void); + +/* Initialization ***********************************************************/ + +static void nrf54l_swinitialize(struct nrf54l_usbdev_s *priv); +static int nrf54l_hwinitialize(struct nrf54l_usbdev_s *priv); +static void nrf54l_vbus_worker(void *arg); +static int nrf54l_vbus_interrupt(int irq, void *context, void *arg); + +/**************************************************************************** + * 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 nrf54l_usbdev_s g_usbdev; +static mutex_t g_usbdev_lock = NXMUTEX_INITIALIZER; + +static const struct usbdev_epops_s g_epops = +{ + .configure = nrf54l_ep_configure, + .disable = nrf54l_ep_disable, + .allocreq = nrf54l_ep_allocreq, + .freereq = nrf54l_ep_freereq, +#ifdef CONFIG_USBDEV_DMA + .allocbuffer = nrf54l_ep_allocbuffer, + .freebuffer = nrf54l_ep_freebuffer, +#endif + .submit = nrf54l_ep_submit, + .cancel = nrf54l_ep_cancel, + .stall = nrf54l_ep_stall, +}; + +static const struct usbdev_ops_s g_devops = +{ + .allocep = nrf54l_ep_alloc, + .freeep = nrf54l_ep_free, + .getframe = nrf54l_getframe, + .wakeup = nrf54l_wakeup, + .selfpowered = nrf54l_selfpowered, + .pullup = nrf54l_pullup, +}; + +/* Device error strings that may be enabled for more descriptive USB trace + * output. + */ + +#ifdef CONFIG_USBDEV_TRACE_STRINGS +const struct trace_msg_t g_usb_trace_strings_deverror[] = +{ + TRACE_STR(NRF54L_TRACEERR_ALLOCFAIL), + TRACE_STR(NRF54L_TRACEERR_BADCLEARFEATURE), + TRACE_STR(NRF54L_TRACEERR_BADDEVGETSTATUS), + TRACE_STR(NRF54L_TRACEERR_BADEPNO), + TRACE_STR(NRF54L_TRACEERR_BADEPGETSTATUS), + TRACE_STR(NRF54L_TRACEERR_BADGETCONFIG), + TRACE_STR(NRF54L_TRACEERR_BADGETSETDESC), + TRACE_STR(NRF54L_TRACEERR_BADGETSTATUS), + TRACE_STR(NRF54L_TRACEERR_BADSETADDRESS), + TRACE_STR(NRF54L_TRACEERR_BADSETCONFIG), + TRACE_STR(NRF54L_TRACEERR_BADSETFEATURE), + TRACE_STR(NRF54L_TRACEERR_BADTESTMODE), + TRACE_STR(NRF54L_TRACEERR_BINDFAILED), + TRACE_STR(NRF54L_TRACEERR_DISPATCHSTALL), + TRACE_STR(NRF54L_TRACEERR_DRIVER), + TRACE_STR(NRF54L_TRACEERR_DRIVERREGISTERED), + TRACE_STR(NRF54L_TRACEERR_EP0NOSETUP), + TRACE_STR(NRF54L_TRACEERR_EP0SETUPSTALLED), + TRACE_STR(NRF54L_TRACEERR_EPINNULLPACKET), + TRACE_STR(NRF54L_TRACEERR_EPINUNEXPECTED), + TRACE_STR(NRF54L_TRACEERR_EPOUTNULLPACKET), + TRACE_STR(NRF54L_TRACEERR_EPOUTUNEXPECTED), + TRACE_STR(NRF54L_TRACEERR_INVALIDCTRLREQ), + TRACE_STR(NRF54L_TRACEERR_INVALIDPARMS), + TRACE_STR(NRF54L_TRACEERR_IRQREGISTRATION), + TRACE_STR(NRF54L_TRACEERR_NOEP), + TRACE_STR(NRF54L_TRACEERR_NOTCONFIGURED), + TRACE_STR(NRF54L_TRACEERR_EPOUTQEMPTY), + TRACE_STR(NRF54L_TRACEERR_EPINREQEMPTY), + TRACE_STR(NRF54L_TRACEERR_NOOUTSETUP), + TRACE_STR(NRF54L_TRACEERR_POLLTIMEOUT), + TRACE_STR_END +}; +#endif + +/* Interrupt event strings that may be enabled for more descriptive USB trace + * output. + */ + +#ifdef CONFIG_USBDEV_TRACE_STRINGS +const struct trace_msg_t g_usb_trace_strings_intdecode[] = +{ + TRACE_STR(NRF54L_TRACEINTID_USB), + TRACE_STR(NRF54L_TRACEINTID_INTPENDING), + TRACE_STR(NRF54L_TRACEINTID_EPOUT), + TRACE_STR(NRF54L_TRACEINTID_EPIN), + TRACE_STR(NRF54L_TRACEINTID_MISMATCH), + TRACE_STR(NRF54L_TRACEINTID_WAKEUP), + TRACE_STR(NRF54L_TRACEINTID_SUSPEND), + TRACE_STR(NRF54L_TRACEINTID_SOF), + TRACE_STR(NRF54L_TRACEINTID_RXFIFO), + TRACE_STR(NRF54L_TRACEINTID_DEVRESET), + TRACE_STR(NRF54L_TRACEINTID_ENUMDNE), + TRACE_STR(NRF54L_TRACEINTID_IISOIXFR), + TRACE_STR(NRF54L_TRACEINTID_IISOOXFR), + TRACE_STR(NRF54L_TRACEINTID_SRQ), + TRACE_STR(NRF54L_TRACEINTID_OTG), + TRACE_STR(NRF54L_TRACEINTID_EPOUT_XFRC), + TRACE_STR(NRF54L_TRACEINTID_EPOUT_EPDISD), + TRACE_STR(NRF54L_TRACEINTID_EPOUT_SETUP), + TRACE_STR(NRF54L_TRACEINTID_DISPATCH), + TRACE_STR(NRF54L_TRACEINTID_GETSTATUS), + TRACE_STR(NRF54L_TRACEINTID_EPGETSTATUS), + TRACE_STR(NRF54L_TRACEINTID_DEVGETSTATUS), + TRACE_STR(NRF54L_TRACEINTID_IFGETSTATUS), + TRACE_STR(NRF54L_TRACEINTID_CLEARFEATURE), + TRACE_STR(NRF54L_TRACEINTID_SETFEATURE), + TRACE_STR(NRF54L_TRACEINTID_SETADDRESS), + TRACE_STR(NRF54L_TRACEINTID_GETSETDESC), + TRACE_STR(NRF54L_TRACEINTID_GETCONFIG), + TRACE_STR(NRF54L_TRACEINTID_SETCONFIG), + TRACE_STR(NRF54L_TRACEINTID_GETSETIF), + TRACE_STR(NRF54L_TRACEINTID_SYNCHFRAME), + TRACE_STR(NRF54L_TRACEINTID_EPIN_XFRC), + TRACE_STR(NRF54L_TRACEINTID_EPIN_TOC), + TRACE_STR(NRF54L_TRACEINTID_EPIN_ITTXFE), + TRACE_STR(NRF54L_TRACEINTID_EPIN_EPDISD), + TRACE_STR(NRF54L_TRACEINTID_EPIN_TXFE), + TRACE_STR(NRF54L_TRACEINTID_EPIN_EMPWAIT), + TRACE_STR(NRF54L_TRACEINTID_OUTNAK), + TRACE_STR(NRF54L_TRACEINTID_OUTRECVD), + TRACE_STR(NRF54L_TRACEINTID_OUTDONE), + TRACE_STR(NRF54L_TRACEINTID_SETUPDONE), + TRACE_STR(NRF54L_TRACEINTID_SETUPRECVD), + TRACE_STR_END +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nrf54l_ready + * + * Description: + * Check whether core registers can be accessed. Call with interrupts + * disabled to serialize against the VBUS worker and regulator interrupt. + * + ****************************************************************************/ + +static bool nrf54l_ready(struct nrf54l_usbdev_s *priv) +{ + return priv->active && !priv->vbus_changed && nrf54l_usbhs_vbus(); +} + +/**************************************************************************** + * Name: nrf54l_getreg + * + * Description: + * Get the contents of an NRF54L register + * + ****************************************************************************/ + +#if defined(CONFIG_NRF54L_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG_FEATURES) +static uint32_t nrf54l_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 register 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) + { + uinfo("...\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 */ + + uinfo("[repeats %" PRId32 " more times]\n", count - 3); + } + + /* Save the new address, value, and count */ + + prevaddr = addr; + preval = val; + count = 1; + } + + /* Show the register value read */ + + uinfo("%08" PRIx32 "->%08" PRIx32 "\n", addr, val); + return val; +} +#endif + +/**************************************************************************** + * Name: nrf54l_putreg + * + * Description: + * Set the contents of an NRF54L register to a value + * + ****************************************************************************/ + +#if defined(CONFIG_NRF54L_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG_FEATURES) +static void nrf54l_putreg(uint32_t val, uint32_t addr) +{ + /* Show the register value being written */ + + uinfo("%08" PRIx32 "->%08" PRIx32 "\n", addr, val); + + /* Write the value */ + + putreg32(val, addr); +} +#endif + +/**************************************************************************** + * Name: nrf54l_req_remfirst + * + * Description: + * Remove a request from the head of an endpoint request queue + * + ****************************************************************************/ + +static struct nrf54l_req_s *nrf54l_req_remfirst(struct nrf54l_ep_s *privep) +{ + struct nrf54l_req_s *ret = privep->head; + + if (ret) + { + privep->head = ret->flink; + if (!privep->head) + { + privep->tail = NULL; + } + + ret->flink = NULL; + } + + return ret; +} + +/**************************************************************************** + * Name: nrf54l_req_addlast + * + * Description: + * Add a request to the end of an endpoint request queue + * + ****************************************************************************/ + +static bool nrf54l_req_addlast(struct nrf54l_ep_s *privep, + struct nrf54l_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: nrf54l_ep0in_setupresponse + * + * Description: + * Schedule a short transfer on Endpoint 0 (IN or OUT) + * + ****************************************************************************/ + +static void nrf54l_ep0in_setupresponse(struct nrf54l_usbdev_s *priv, + uint8_t *buf, uint32_t nbytes) +{ + nrf54l_epin_transfer(&priv->epin[EP0], buf, nbytes); + priv->ep0state = EP0STATE_SETUPRESPONSE; + nrf54l_ep0out_ctrlsetup(priv); +} + +/**************************************************************************** + * Name: nrf54l_ep0in_transmitzlp + * + * Description: + * Send a zero length packet (ZLP) on endpoint 0 IN + * + ****************************************************************************/ + +static inline void nrf54l_ep0in_transmitzlp(struct nrf54l_usbdev_s *priv) +{ + nrf54l_ep0in_setupresponse(priv, NULL, 0); +} + +/**************************************************************************** + * Name: nrf54l_ep0_mpsiz + * + * Description: + * Encode the maximum packet size for either direction of endpoint 0. + * + ****************************************************************************/ + +static int nrf54l_ep0_mpsiz(uint16_t maxpacket) +{ + switch (maxpacket) + { + case 8: + return OTG_DIEPCTL0_MPSIZ_8; + + case 16: + return OTG_DIEPCTL0_MPSIZ_16; + + case 32: + return OTG_DIEPCTL0_MPSIZ_32; + + case 64: + return OTG_DIEPCTL0_MPSIZ_64; + + default: + return -EINVAL; + } +} + +/**************************************************************************** + * Name: nrf54l_ep0in_activate + * + * Description: + * Activate the endpoint 0 IN endpoint. + * + ****************************************************************************/ + +static void nrf54l_ep0in_activate(void) +{ + uint32_t regval; + + /* Set the encoded maximum packet size of EP0 IN. */ + + regval = nrf54l_getreg(NRF54L_USBD_DIEPCTL(0)); + regval &= ~OTG_DIEPCTL0_MPSIZ_MASK; + regval |= nrf54l_ep0_mpsiz(CONFIG_USBDEV_EP0_MAXSIZE); + + nrf54l_putreg(regval, NRF54L_USBD_DIEPCTL(0)); + + /* Clear global IN NAK */ + + regval = nrf54l_getreg(NRF54L_USBD_DCTL); + regval |= OTG_DCTL_CGINAK; + nrf54l_putreg(regval, NRF54L_USBD_DCTL); +} + +/**************************************************************************** + * Name: nrf54l_ep0out_ctrlsetup + * + * Description: + * Setup to receive a SETUP packet. + * + ****************************************************************************/ + +static void nrf54l_ep0out_ctrlsetup(struct nrf54l_usbdev_s *priv) +{ + uint32_t regval; + + /* Setup the hardware to perform the SETUP transfer */ + + regval = (USB_SIZEOF_CTRLREQ * 3 << OTG_DOEPTSIZ0_XFRSIZ_SHIFT) | + (OTG_DOEPTSIZ0_PKTCNT) | + (3 << OTG_DOEPTSIZ0_STUPCNT_SHIFT); + nrf54l_putreg(regval, NRF54L_USBD_DOEPTSIZ(0)); + + /* Then clear NAKing and enable the transfer */ + + regval = nrf54l_getreg(NRF54L_USBD_DOEPCTL(0)); + regval |= (OTG_DOEPCTL0_CNAK | OTG_DOEPCTL0_EPENA); + nrf54l_putreg(regval, NRF54L_USBD_DOEPCTL(0)); +} + +/**************************************************************************** + * Name: nrf54l_txfifo_write + * + * Description: + * Send data to the endpoint's TxFIFO. + * + ****************************************************************************/ + +static void nrf54l_txfifo_write(struct nrf54l_ep_s *privep, + uint8_t *buf, int nbytes) +{ + uint32_t regaddr; + uint32_t regval; + int i; + int j; + + /* Get the TxFIFO for this endpoint (same as the endpoint number) */ + + regaddr = NRF54L_USBD_DFIFO_DEP(privep->epphy); + + /* Then transfer each word to the TxFIFO */ + + for (i = 0; i < nbytes; i += 4) + { + /* Read four bytes from the source buffer (to avoid unaligned accesses) + * and pack these into one 32-bit word (little endian). + */ + + regval = 0; + for (j = 0; j < 4 && i + j < nbytes; j++) + { + regval |= (uint32_t)*buf++ << (8 * j); + } + + /* Then write the packet data to the TxFIFO */ + + nrf54l_putreg(regval, regaddr); + } +} + +/**************************************************************************** + * Name: nrf54l_epin_transfer + * + * Description: + * Start the Tx data transfer + * + ****************************************************************************/ + +static void nrf54l_epin_transfer(struct nrf54l_ep_s *privep, + uint8_t *buf, int nbytes) +{ + uint32_t pktcnt; + uint32_t regval; + + /* Read the DIEPSIZx register */ + + regval = nrf54l_getreg(NRF54L_USBD_DIEPTSIZ(privep->epphy)); + + /* Clear the XFRSIZ, PKTCNT, and MCNT field of the DIEPSIZx register */ + + regval &= ~(OTG_DIEPTSIZ_XFRSIZ_MASK | OTG_DIEPTSIZ_PKTCNT_MASK | + OTG_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 XFRSIZ and PKTCNT */ + + regval |= (pktcnt << OTG_DIEPTSIZ_PKTCNT_SHIFT); + regval |= ((uint32_t)nbytes << OTG_DIEPTSIZ_XFRSIZ_SHIFT); + + /* If this is an isochronous endpoint, then set the multi-count field to + * the PKTCNT as well. + */ + + if (privep->eptype == USB_EP_ATTR_XFER_ISOC) + { + regval |= (pktcnt << OTG_DIEPTSIZ_MCNT_SHIFT); + } + + /* Save DIEPSIZx register value */ + + nrf54l_putreg(regval, NRF54L_USBD_DIEPTSIZ(privep->epphy)); + + /* Read the DIEPCTLx register */ + + regval = nrf54l_getreg(NRF54L_USBD_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 = nrf54l_getreg(NRF54L_USBD_DSTS); + + if ((status & OTG_DSTS_SOFFN0) == OTG_DSTS_SOFFN_EVEN) + { + regval |= OTG_DIEPCTL_SEVNFRM; + } + else + { + regval |= OTG_DIEPCTL_SODDFRM; + } + } + + /* EP enable, IN data in FIFO */ + + regval &= ~OTG_DIEPCTL_EPDIS; + regval |= (OTG_DIEPCTL_CNAK | OTG_DIEPCTL_EPENA); + nrf54l_putreg(regval, NRF54L_USBD_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. + */ + + nrf54l_txfifo_write(privep, buf, nbytes); +} + +/**************************************************************************** + * Name: nrf54l_epin_request + * + * Description: + * Begin or continue write request processing. + * + ****************************************************************************/ + +static void nrf54l_epin_request(struct nrf54l_usbdev_s *priv, + struct nrf54l_ep_s *privep) +{ + struct nrf54l_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 nrf54l_epin during 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 nrf54l_epin_txfifoempty during 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 nrf54l_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 = nrf54l_rqpeek(privep); + if (!privreq) + { + usbtrace(TRACE_DEVERROR(NRF54L_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 = NRF54L_USBD_DIEPCTL(privep->epphy); + regval = nrf54l_getreg(regaddr); + regval |= OTG_DIEPCTL_SNAK; + nrf54l_putreg(regval, regaddr); + + /* The endpoint is no longer active */ + + privep->active = false; + return; + } + + uinfo("EP%" PRId8 " req=%p: len=%" PRId16 " xfrd=%" PRId16" zlp=%" + PRId8 "\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 = NRF54L_USBD_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 = nrf54l_getreg(regaddr); + if ((int)(regval & OTG_DTXFSTS_MASK) < nwords) + { + usbtrace(TRACE_INTDECODE(NRF54L_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 = nrf54l_getreg(NRF54L_USBD_DIEPEMPMSK); + + empmsk |= OTG_DIEPEMPMSK(privep->epphy); + nrf54l_putreg(empmsk, NRF54L_USBD_DIEPEMPMSK); + +#ifdef CONFIG_DEBUG_FEATURES + /* Check if the configured TXFIFO size is sufficient for a given + * request. If not, raise an assertion here. + */ + + regval = nrf54l_getreg(NRF54L_USBD_DIEPTXF(privep->epphy)); + regval &= OTG_DIEPTXF_INEPTXFD_MASK; + regval >>= OTG_DIEPTXF_INEPTXFD_SHIFT; + uerr("EP%" PRId8 " TXLEN=%" PRId32 " nwords=%d\n", + privep->epphy, regval, nwords); + DEBUGASSERT(regval >= nwords); +#endif + + /* 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; + nrf54l_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). + */ + + nrf54l_req_complete(privep, OK); + } +} + +/**************************************************************************** + * Name: nrf54l_rxfifo_read + * + * Description: + * Read packet from the RxFIFO into a read request. + * + ****************************************************************************/ + +static void nrf54l_rxfifo_read(struct nrf54l_ep_s *privep, + uint8_t *dest, uint16_t len) +{ + uint32_t regaddr; + int i; + int j; + + /* Get the address of the RxFIFO. Note: there is only one RxFIFO so + * we might as well use the address associated with EP0. + */ + + regaddr = NRF54L_USBD_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 = nrf54l_getreg(regaddr); + + /* Write 4 x 8-bits of EP0 packet data */ + + for (j = 0; j < 4 && i + j < len; j++) + { + *dest++ = data.b[j]; + } + } +} + +/**************************************************************************** + * Name: nrf54l_rxfifo_discard + * + * Description: + * Discard packet data from the RxFIFO. + * + ****************************************************************************/ + +static void nrf54l_rxfifo_discard(struct nrf54l_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 address associated with EP0. + */ + + regaddr = NRF54L_USBD_DFIFO_DEP(EP0); + + /* Read 32-bits at time */ + + for (i = 0; i < len; i += 4) + { + volatile uint32_t data = nrf54l_getreg(regaddr); + + UNUSED(data); + } + } +} + +/**************************************************************************** + * Name: nrf54l_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 nrf54l_epout_complete(struct nrf54l_usbdev_s *priv, + struct nrf54l_ep_s *privep) +{ + struct nrf54l_req_s *privreq; + + /* Since a transfer just completed, there must be a read request at the + * head of the endpoint request queue. + */ + + privreq = nrf54l_rqpeek(privep); + DEBUGASSERT(privreq); + + if (!privreq) + { + /* An OUT transfer completed, but no packet to receive the data. This + * should not happen. + */ + + usbtrace(TRACE_DEVERROR(NRF54L_TRACEERR_EPOUTQEMPTY), + privep->epphy); + privep->active = false; + return; + } + + uinfo("EP%d: len=%zu xfrd=%zu\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); + nrf54l_req_complete(privep, OK); + privep->active = false; + + /* Now set up the next read request (if any) */ + + nrf54l_epout_request(priv, privep); +} + +/**************************************************************************** + * Name: nrf54l_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 nrf54l_ep0out_receive(struct nrf54l_ep_s *privep, + int bcnt) +{ + struct nrf54l_usbdev_s *priv; + + /* Sanity Checking */ + + DEBUGASSERT(privep && privep->dev); + priv = (struct nrf54l_usbdev_s *)privep->dev; + + uinfo("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); + + nrf54l_rxfifo_read(privep, priv->ep0data, readlen); + + /* Do we have to discard any excess bytes? */ + + nrf54l_rxfifo_discard(privep, bcnt - ((readlen + 3) & ~3)); + + /* Now we can process the setup command */ + + privep->active = false; + priv->ep0state = EP0STATE_SETUP_READY; + priv->ep0datlen = readlen; + + nrf54l_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(NRF54L_TRACEERR_NOOUTSETUP), priv->ep0state); + nrf54l_rxfifo_discard(privep, bcnt); + privep->active = false; + } +} + +/**************************************************************************** + * Name: nrf54l_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 nrf54l_epout_receive(struct nrf54l_ep_s *privep, + int bcnt) +{ + struct nrf54l_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 = nrf54l_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) + { + nrf54l_ep0out_receive(privep, bcnt); + } + else + { + /* Otherwise, the data is lost. This really should not happen if + * NAKing is working as expected. + */ + + usbtrace(TRACE_DEVERROR(NRF54L_TRACEERR_EPOUTQEMPTY), + privep->epphy); + + /* Discard the data in the RxFIFO */ + + nrf54l_rxfifo_discard(privep, bcnt); + } + + privep->active = false; + return; + } + + uinfo("EP%d: len=%zu xfrd=%zu\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); + 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 */ + + nrf54l_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. + */ + + nrf54l_rxfifo_discard(privep, bcnt - ((readlen + 3) & ~3)); + + /* Update the number of bytes transferred */ + + privreq->req.xfrd += readlen; +} + +/**************************************************************************** + * Name: nrf54l_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 nrf54l_epout_request(struct nrf54l_usbdev_s *priv, + struct nrf54l_ep_s *privep) +{ + struct nrf54l_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 = nrf54l_rqpeek(privep); + if (!privreq) + { + usbtrace(TRACE_DEVERROR(NRF54L_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 = NRF54L_USBD_DOEPCTL(privep->epphy); + regval = nrf54l_getreg(regaddr); + regval |= OTG_DOEPCTL_SNAK; + nrf54l_putreg(regval, regaddr); + + /* This endpoint is no longer actively transferring */ + + privep->active = false; + return; + } + + uinfo("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(NRF54L_TRACEERR_EPOUTNULLPACKET), 0); + nrf54l_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 = NRF54L_USBD_DOEPTSIZ(privep->epphy); + regval = nrf54l_getreg(regaddr); + regval &= ~(OTG_DOEPTSIZ_XFRSIZ_MASK | OTG_DOEPTSIZ_PKTCNT_MASK); + regval |= (xfrsize << OTG_DOEPTSIZ_XFRSIZ_SHIFT); + regval |= (pktcnt << OTG_DOEPTSIZ_PKTCNT_SHIFT); + nrf54l_putreg(regval, regaddr); + + /* Then enable the transfer */ + + regaddr = NRF54L_USBD_DOEPCTL(privep->epphy); + regval = nrf54l_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 |= OTG_DOEPCTL_SODDFRM; + } + else + { + regval |= OTG_DOEPCTL_SEVNFRM; + } + } +#endif + + /* Clearing NAKing and enable the transfer. */ + + regval |= (OTG_DOEPCTL_CNAK | OTG_DOEPCTL_EPENA); + nrf54l_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: nrf54l_ep_flush + * + * Description: + * Flush any primed descriptors from this ep + * + ****************************************************************************/ + +static void nrf54l_ep_flush(struct nrf54l_ep_s *privep) +{ + if (privep->isin) + { + nrf54l_txfifo_flush(OTG_GRSTCTL_TXFNUM_D(privep->epphy)); + } + else + { + nrf54l_rxfifo_flush(); + } +} + +/**************************************************************************** + * Name: nrf54l_req_complete + * + * Description: + * Handle termination of the request at the head of the endpoint request + * queue. + * + ****************************************************************************/ + +static void nrf54l_req_complete(struct nrf54l_ep_s *privep, int16_t result) +{ + struct nrf54l_req_s *privreq; + + /* Remove the request at the head of the request list */ + + privreq = nrf54l_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: nrf54l_req_cancel + * + * Description: + * Cancel all pending requests for an endpoint + * + ****************************************************************************/ + +static void nrf54l_req_cancel(struct nrf54l_ep_s *privep, int16_t status) +{ + if (!nrf54l_rqempty(privep) && nrf54l_ready(privep->dev)) + { + nrf54l_ep_flush(privep); + } + + while (!nrf54l_rqempty(privep)) + { + usbtrace(TRACE_COMPLETE(privep->epphy), + (nrf54l_rqpeek(privep))->req.xfrd); + nrf54l_req_complete(privep, status); + } +} + +/**************************************************************************** + * Name: nrf54l_ep_findbyaddr + * + * Description: + * Find the physical endpoint structure corresponding to a logic endpoint + * address + * + ****************************************************************************/ + +static struct nrf54l_ep_s *nrf54l_ep_findbyaddr(struct nrf54l_usbdev_s *priv, + uint16_t eplog) +{ + struct nrf54l_ep_s *privep; + uint8_t epphy = USB_EPNO(eplog); + + if (epphy >= NRF54L_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: nrf54l_req_dispatch + * + * Description: + * Provide unhandled setup actions to the class driver. This is logically + * part of the USB interrupt handler. + * + ****************************************************************************/ + +static int nrf54l_req_dispatch(struct nrf54l_usbdev_s *priv, + const struct usb_ctrlreq_s *ctrl) +{ + int ret = -EIO; + + usbtrace(TRACE_INTDECODE(NRF54L_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(NRF54L_TRACEERR_DISPATCHSTALL), 0); + priv->stalled = true; + } + + return ret; +} + +/**************************************************************************** + * Name: nrf54l_usbreset + * + * Description: + * Reset Usb engine + * + ****************************************************************************/ + +static void nrf54l_usbreset(struct nrf54l_usbdev_s *priv) +{ + struct nrf54l_ep_s *privep; + uint32_t regval; + int i; + + /* Clear the Remote Wake-up Signaling */ + + regval = nrf54l_getreg(NRF54L_USBD_DCTL); + regval &= ~OTG_DCTL_RWUSIG; + nrf54l_putreg(regval, NRF54L_USBD_DCTL); + + /* Flush the EP0 Tx FIFO */ + + nrf54l_txfifo_flush(OTG_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] = NRF54L_EP_AVAILABLE; + priv->epavail[1] = NRF54L_EP_AVAILABLE; + + /* Disable all end point interrupts */ + + for (i = 0; i < NRF54L_NENDPOINTS ; i++) + { + /* Disable endpoint interrupts */ + + nrf54l_putreg(0xff, NRF54L_USBD_DIEPINT(i)); + nrf54l_putreg(0xff, NRF54L_USBD_DOEPINT(i)); + + /* Return write requests to the class implementation */ + + privep = &priv->epin[i]; + nrf54l_req_cancel(privep, -ESHUTDOWN); + + /* Reset IN endpoint status */ + + privep->stalled = false; + privep->active = false; + privep->zlp = false; + + /* Return read requests to the class implementation */ + + privep = &priv->epout[i]; + nrf54l_req_cancel(privep, -ESHUTDOWN); + + /* Reset endpoint status */ + + privep->stalled = false; + privep->active = false; + privep->zlp = false; + } + + nrf54l_putreg(0xffffffff, NRF54L_USBD_DAINT); + + /* Mask all device endpoint interrupts except EP0 */ + + regval = (OTG_DAINT_IEP(EP0) | OTG_DAINT_OEP(EP0)); + nrf54l_putreg(regval, NRF54L_USBD_DAINTMSK); + + /* Unmask OUT interrupts */ + + regval = (OTG_DOEPMSK_XFRCM | OTG_DOEPMSK_STUPM | OTG_DOEPMSK_EPDM); + nrf54l_putreg(regval, NRF54L_USBD_DOEPMSK); + + /* Unmask IN interrupts */ + + regval = (OTG_DIEPMSK_XFRCM | OTG_DIEPMSK_EPDM | OTG_DIEPMSK_TOM); + nrf54l_putreg(regval, NRF54L_USBD_DIEPMSK); + + /* Reset device address to 0 */ + + nrf54l_setaddress(priv, 0); + priv->devstate = DEVSTATE_DEFAULT; + priv->usbdev.speed = USB_SPEED_UNKNOWN; + + /* Re-configure EP0 */ + + nrf54l_ep0_configure(priv); + + /* Setup EP0 to receive SETUP packets */ + + nrf54l_ep0out_ctrlsetup(priv); +} + +/**************************************************************************** + * Name: nrf54l_ep0out_testmode + * + * Description: + * Select test mode + * + ****************************************************************************/ + +static inline void nrf54l_ep0out_testmode(struct nrf54l_usbdev_s *priv, + uint16_t index) +{ + uint8_t testmode; + + testmode = index >> 8; + switch (testmode) + { + case 1: + priv->testmode = OTG_TESTMODE_J; + break; + + case 2: + priv->testmode = OTG_TESTMODE_K; + break; + + case 3: + priv->testmode = OTG_TESTMODE_SE0_NAK; + break; + + case 4: + priv->testmode = OTG_TESTMODE_PACKET; + break; + + case 5: + priv->testmode = OTG_TESTMODE_FORCE; + break; + + default: + usbtrace(TRACE_DEVERROR(NRF54L_TRACEERR_BADTESTMODE), testmode); + priv->dotest = false; + priv->testmode = OTG_TESTMODE_DISABLED; + priv->stalled = true; + } + + priv->dotest = true; + nrf54l_ep0in_transmitzlp(priv); +} + +/**************************************************************************** + * Name: nrf54l_ep0out_stdrequest + * + * Description: + * Handle a standard 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 nrf54l_ep0out_stdrequest(struct nrf54l_usbdev_s *priv, + struct nrf54l_ctrlreq_s *ctrlreq) +{ + struct nrf54l_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(NRF54L_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( + NRF54L_TRACEINTID_EPGETSTATUS), 0); + privep = nrf54l_ep_findbyaddr(priv, ctrlreq->index); + if (!privep) + { + usbtrace(TRACE_DEVERROR( + NRF54L_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; + nrf54l_ep0in_setupresponse(priv, priv->ep0data, 2); + } + } + break; + + case USB_REQ_RECIPIENT_DEVICE: + { + if (ctrlreq->index == 0) + { + usbtrace(TRACE_INTDECODE( + NRF54L_TRACEINTID_DEVGETSTATUS), 0); + + /* Features: Remote Wakeup and self-powered */ + + priv->ep0data[0] = (priv->selfpowered << + USB_FEATURE_SELFPOWERED); + priv->ep0data[0] |= (priv->wakeup << + USB_FEATURE_REMOTEWAKEUP); + priv->ep0data[1] = 0; + + nrf54l_ep0in_setupresponse(priv, priv->ep0data, 2); + } + else + { + usbtrace(TRACE_DEVERROR( + NRF54L_TRACEERR_BADDEVGETSTATUS), 0); + priv->stalled = true; + } + } + + break; + + case USB_REQ_RECIPIENT_INTERFACE: + { + usbtrace(TRACE_INTDECODE( + NRF54L_TRACEINTID_IFGETSTATUS), 0); + priv->ep0data[0] = 0; + priv->ep0data[1] = 0; + + nrf54l_ep0in_setupresponse(priv, priv->ep0data, 2); + } + break; + + default: + { + usbtrace(TRACE_DEVERROR(NRF54L_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(NRF54L_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 = nrf54l_ep_findbyaddr(priv, + ctrlreq->index)) != NULL) + { + nrf54l_ep_clrstall(privep); + nrf54l_ep0in_transmitzlp(priv); + } + else if (recipient == USB_REQ_RECIPIENT_DEVICE && + ctrlreq->value == USB_FEATURE_REMOTEWAKEUP) + { + priv->wakeup = 0; + nrf54l_ep0in_transmitzlp(priv); + } + else + { + /* Actually, I think we could just stall here. */ + + nrf54l_req_dispatch(priv, &priv->ctrlreq); + } + } + else + { + usbtrace(TRACE_DEVERROR(NRF54L_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(NRF54L_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 = nrf54l_ep_findbyaddr(priv, + ctrlreq->index)) != NULL) + { + nrf54l_ep_setstall(privep); + nrf54l_ep0in_transmitzlp(priv); + } + else if (recipient == USB_REQ_RECIPIENT_DEVICE && + ctrlreq->value == USB_FEATURE_REMOTEWAKEUP) + { + priv->wakeup = 1; + nrf54l_ep0in_transmitzlp(priv); + } + else if (recipient == USB_REQ_RECIPIENT_DEVICE && + ctrlreq->value == USB_FEATURE_TESTMODE && + ((ctrlreq->index & 0xff) == 0)) + { + nrf54l_ep0out_testmode(priv, ctrlreq->index); + } + else if (priv->configured) + { + /* Actually, I think we could just stall here. */ + + nrf54l_req_dispatch(priv, &priv->ctrlreq); + } + else + { + usbtrace(TRACE_DEVERROR(NRF54L_TRACEERR_BADSETFEATURE), 0); + priv->stalled = true; + } + } + else + { + usbtrace(TRACE_DEVERROR(NRF54L_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(NRF54L_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. + */ + + nrf54l_setaddress(priv, (uint16_t)priv->ctrlreq.value[0]); + nrf54l_ep0in_transmitzlp(priv); + } + else + { + usbtrace(TRACE_DEVERROR(NRF54L_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(NRF54L_TRACEINTID_GETSETDESC), 0); + if ((ctrlreq->type & USB_REQ_RECIPIENT_MASK) == + USB_REQ_RECIPIENT_DEVICE) + { + nrf54l_req_dispatch(priv, &priv->ctrlreq); + } + else + { + usbtrace(TRACE_DEVERROR(NRF54L_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(NRF54L_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) + { + nrf54l_req_dispatch(priv, &priv->ctrlreq); + } + else + { + usbtrace(TRACE_DEVERROR(NRF54L_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(NRF54L_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 = nrf54l_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(NRF54L_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(NRF54L_TRACEINTID_GETSETIF), 0); + nrf54l_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(NRF54L_TRACEINTID_SYNCHFRAME), 0); + } + break; + + default: + { + usbtrace(TRACE_DEVERROR(NRF54L_TRACEERR_INVALIDCTRLREQ), 0); + priv->stalled = true; + } + break; + } +} + +/**************************************************************************** + * Name: nrf54l_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 nrf54l_ep0out_setup(struct nrf54l_usbdev_s *priv) +{ + struct nrf54l_ctrlreq_s ctrlreq; + + /* Verify that a SETUP was received */ + + if (priv->ep0state != EP0STATE_SETUP_READY) + { + usbtrace(TRACE_DEVERROR(NRF54L_TRACEERR_EP0NOSETUP), priv->ep0state); + return; + } + + /* Terminate any pending requests */ + + nrf54l_req_cancel(&priv->epout[EP0], -EPROTO); + nrf54l_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); + + uinfo("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 */ + + nrf54l_req_dispatch(priv, &priv->ctrlreq); + } + else + { + /* Handle standard requests. */ + + nrf54l_ep0out_stdrequest(priv, &ctrlreq); + } + + /* Check if the setup processing resulted in a STALL */ + + if (priv->stalled) + { + usbtrace(TRACE_DEVERROR(NRF54L_TRACEERR_EP0SETUPSTALLED), + priv->ep0state); + nrf54l_ep0_stall(priv); + } + + /* Reset state/data associated with the SETUP request */ + + priv->ep0datlen = 0; +} + +/**************************************************************************** + * Name: nrf54l_epout + * + * Description: + * This is part of the OUT endpoint interrupt processing. This function + * handles the OUT event for a single endpoint. + * + ****************************************************************************/ + +static inline void nrf54l_epout(struct nrf54l_usbdev_s *priv, uint8_t epno) +{ + struct nrf54l_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 */ + + nrf54l_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) + { + nrf54l_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) + { + nrf54l_epout_complete(priv, &priv->epout[epno]); + } +} + +/**************************************************************************** + * Name: nrf54l_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 OTG DAINT register to determine the exact + * number of the OUT endpoint on which the interrupt occurred, and then + * read the corresponding OTG DOEPINTx register to determine the exact + * cause of the interrupt. + * + ****************************************************************************/ + +static inline void nrf54l_epout_interrupt(struct nrf54l_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 = nrf54l_getreg(NRF54L_USBD_DAINT); + regval &= nrf54l_getreg(NRF54L_USBD_DAINTMSK); + daint = (regval & OTG_DAINT_OEP_MASK) >> OTG_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 = nrf54l_getreg(NRF54L_USBD_DAINT); + daint = (regval & OTG_DAINT_OEP_MASK) >> OTG_DAINT_OEP_SHIFT; + + usbtrace(TRACE_DEVERROR(NRF54L_TRACEERR_EPOUTUNEXPECTED), + (uint16_t)regval); + + epno = 0; + while (daint) + { + if ((daint & 1) != 0) + { + regval = nrf54l_getreg(NRF54L_USBD_DOEPINT(epno)); + uerr("DOEPINT(%d) = %08" PRIx32 "\n", epno, regval); + nrf54l_putreg(0xff, NRF54L_USBD_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 = nrf54l_getreg(NRF54L_USBD_DOEPINT(epno)); + doepint &= nrf54l_getreg(NRF54L_USBD_DOEPMSK); + + /* Transfer completed interrupt. This interrupt is triggered when + * nrf54l_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 & OTG_DOEPINT_XFRC) != 0) + { + usbtrace(TRACE_INTDECODE(NRF54L_TRACEINTID_EPOUT_XFRC), + (uint16_t)doepint); + + /* Clear the bit in DOEPINTn for this interrupt */ + + nrf54l_putreg(OTG_DOEPINT_XFRC, NRF54L_USBD_DOEPINT(epno)); + + /* Handle the RX transfer data ready event */ + + nrf54l_epout(priv, epno); + } + + /* Endpoint disabled interrupt (ignored because this interrupt is + * used in polled mode by the endpoint disable logic). + */ +#if 1 + /* REVISIT: */ + + if ((doepint & OTG_DOEPINT_EPDISD) != 0) + { + usbtrace(TRACE_INTDECODE(NRF54L_TRACEINTID_EPOUT_EPDISD), + (uint16_t)doepint); + + /* Clear the bit in DOEPINTn for this interrupt */ + + nrf54l_putreg(OTG_DOEPINT_EPDISD, NRF54L_USBD_DOEPINT(epno)); + } +#endif + + /* Setup Phase Done (control EPs) */ + + if ((doepint & OTG_DOEPINT_SETUP) != 0) + { + usbtrace(TRACE_INTDECODE(NRF54L_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) + { + nrf54l_ep0out_setup(priv); + } + + nrf54l_putreg(OTG_DOEPINT_SETUP, + NRF54L_USBD_DOEPINT(epno)); + } + } + + epno++; + daint >>= 1; + } +} + +/**************************************************************************** + * Name: nrf54l_epin_runtestmode + * + * Description: + * Execute the test mode setup by the SET FEATURE request + * + ****************************************************************************/ + +static inline void nrf54l_epin_runtestmode(struct nrf54l_usbdev_s *priv) +{ + uint32_t regval = nrf54l_getreg(NRF54L_USBD_DCTL); + + regval &= ~OTG_DCTL_TCTL_MASK; + regval |= (uint32_t)priv->testmode << OTG_DCTL_TCTL_SHIFT; + nrf54l_putreg(regval , NRF54L_USBD_DCTL); + + priv->dotest = 0; + priv->testmode = OTG_TESTMODE_DISABLED; +} + +/**************************************************************************** + * Name: nrf54l_epin + * + * Description: + * This is part of the IN endpoint interrupt processing. This function + * handles the IN event for a single endpoint. + * + ****************************************************************************/ + +static inline void nrf54l_epin(struct nrf54l_usbdev_s *priv, uint8_t epno) +{ + struct nrf54l_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 */ + + nrf54l_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) + { + nrf54l_ep0out_ctrlsetup(priv); + priv->ep0state = EP0STATE_IDLE; + } + } + + /* Test mode is another special case */ + + if (priv->dotest) + { + nrf54l_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 */ + + nrf54l_epin_request(priv, privep); + } +} + +/**************************************************************************** + * Name: nrf54l_epin_txfifoempty + * + * Description: + * TxFIFO empty interrupt handling + * + ****************************************************************************/ + +static inline void nrf54l_epin_txfifoempty(struct nrf54l_usbdev_s *priv, + int epno) +{ + struct nrf54l_ep_s *privep = &priv->epin[epno]; + + /* Continue processing the write request queue. This may mean sending + * more data from the existing request or terminating the current requests + * and (perhaps) starting the IN transfer from the next write request. + */ + + nrf54l_epin_request(priv, privep); +} + +/**************************************************************************** + * Name: nrf54l_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 OTG DAINT register to determine the exact number of + * the IN endpoint on which the interrupt occurred, and then read the + * corresponding OTG DIEPINTx register to determine the exact cause of the + * interrupt. + * + ****************************************************************************/ + +static inline void nrf54l_epin_interrupt(struct nrf54l_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 = nrf54l_getreg(NRF54L_USBD_DAINT); + daint &= nrf54l_getreg(NRF54L_USBD_DAINTMSK); + daint &= OTG_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 = nrf54l_getreg(NRF54L_USBD_DAINT); + usbtrace(TRACE_DEVERROR(NRF54L_TRACEERR_EPINUNEXPECTED), + (uint16_t)daint); + + daint &= OTG_DAINT_IEP_MASK; + epno = 0; + + while (daint) + { + if ((daint & 1) != 0) + { + uerr("DIEPINT(%d) = %08" PRIx32 "\n", + epno, nrf54l_getreg(NRF54L_USBD_DIEPINT(epno))); + nrf54l_putreg(0xff, NRF54L_USBD_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 = nrf54l_getreg(NRF54L_USBD_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 = nrf54l_getreg(NRF54L_USBD_DIEPEMPMSK); + if ((empty & OTG_DIEPEMPMSK(epno)) != 0) + { + mask |= OTG_DIEPINT_TXFE; + } + + /* Now, read the interrupt status and mask out all disabled + * interrupts. + */ + + diepint = nrf54l_getreg(NRF54L_USBD_DIEPINT(epno)) & mask; + + /* Decode and process the enabled, pending interrupts */ + + /* Transfer completed interrupt */ + + if ((diepint & OTG_DIEPINT_XFRC) != 0) + { + usbtrace(TRACE_INTDECODE(NRF54L_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 &= ~OTG_DIEPEMPMSK(epno); + nrf54l_putreg(empty, NRF54L_USBD_DIEPEMPMSK); + nrf54l_putreg(OTG_DIEPINT_XFRC, NRF54L_USBD_DIEPINT(epno)); + + /* IN transfer complete */ + + nrf54l_epin(priv, epno); + } + + /* Timeout condition */ + + if ((diepint & OTG_DIEPINT_TOC) != 0) + { + usbtrace(TRACE_INTDECODE(NRF54L_TRACEINTID_EPIN_TOC), + (uint16_t)diepint); + nrf54l_putreg(OTG_DIEPINT_TOC, NRF54L_USBD_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 & OTG_DIEPINT_ITTXFE) != 0) + { + usbtrace(TRACE_INTDECODE(NRF54L_TRACEINTID_EPIN_ITTXFE), + (uint16_t)diepint); + nrf54l_epin_request(priv, &priv->epin[epno]); + nrf54l_putreg(OTG_DIEPINT_ITTXFE, NRF54L_USBD_DIEPINT(epno)); + } + + /* IN endpoint NAK effective (ignored as this used only in polled + * mode) + */ +#if 0 + if ((diepint & OTG_DIEPINT_INEPNE) != 0) + { + usbtrace(TRACE_INTDECODE(NRF54L_TRACEINTID_EPIN_INEPNE), + (uint16_t)diepint); + nrf54l_putreg(OTG_DIEPINT_INEPNE, NRF54L_USBD_DIEPINT(epno)); + } +#endif + + /* Endpoint disabled interrupt (ignored as this used only in polled + * mode) + */ +#if 0 + if ((diepint & OTG_DIEPINT_EPDISD) != 0) + { + usbtrace(TRACE_INTDECODE(NRF54L_TRACEINTID_EPIN_EPDISD), + (uint16_t)diepint); + nrf54l_putreg(OTG_DIEPINT_EPDISD, NRF54L_USBD_DIEPINT(epno)); + } +#endif + + /* Transmit FIFO empty */ + + if ((diepint & OTG_DIEPINT_TXFE) != 0) + { + usbtrace(TRACE_INTDECODE(NRF54L_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 & OTG_DIEPINT_XFRC) == 0) + { + /* Mask further FIFO empty interrupts. + * This will be re-enabled whenever we need to wait for a + * FIFO event. + */ + + empty &= ~OTG_DIEPEMPMSK(epno); + nrf54l_putreg(empty, NRF54L_USBD_DIEPEMPMSK); + + /* Handle TxFIFO empty */ + + nrf54l_epin_txfifoempty(priv, epno); + } + + /* Clear the pending TxFIFO empty interrupt */ + + nrf54l_putreg(OTG_DIEPINT_TXFE, NRF54L_USBD_DIEPINT(epno)); + } + } + + epno++; + daint >>= 1; + } +} + +/**************************************************************************** + * Name: nrf54l_resumeinterrupt + * + * Description: + * Resume/remote wakeup detected interrupt + * + ****************************************************************************/ + +static inline void nrf54l_resumeinterrupt(struct nrf54l_usbdev_s *priv) +{ + uint32_t regval; + + /* Clear remote wake-up signaling */ + + regval = nrf54l_getreg(NRF54L_USBD_DCTL); + regval &= ~OTG_DCTL_RWUSIG; + nrf54l_putreg(regval, NRF54L_USBD_DCTL); + + /* Notify the class driver of the resume event */ + + if (priv->driver) + { + CLASS_RESUME(priv->driver, &priv->usbdev); + } +} + +/**************************************************************************** + * Name: nrf54l_suspendinterrupt + * + * Description: + * USB suspend interrupt + * + ****************************************************************************/ + +static inline void nrf54l_suspendinterrupt(struct nrf54l_usbdev_s *priv) +{ + /* Notify the class driver of the suspend event */ + + if (priv->driver) + { + CLASS_SUSPEND(priv->driver, &priv->usbdev); + } +} + +/**************************************************************************** + * Name: nrf54l_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 nrf54l_rxinterrupt(struct nrf54l_usbdev_s *priv) +{ + struct nrf54l_ep_s *privep; + uint32_t regval; + int bcnt; + int epphy; + + /* Get the status from the top of the FIFO */ + + regval = nrf54l_getreg(NRF54L_USBD_GRXSTSP); + + /* Decode status fields */ + + epphy = (regval & OTG_GRXSTSD_EPNUM_MASK) >> OTG_GRXSTSD_EPNUM_SHIFT; + + if (epphy < NRF54L_NENDPOINTS) + { + privep = &priv->epout[epphy]; + + /* Handle the RX event according to the packet status field */ + + switch (regval & OTG_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'tCare. + */ + + case OTG_GRXSTSD_PKTSTS_OUTNAK: + { + usbtrace(TRACE_INTDECODE(NRF54L_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 OTG_GRXSTSD_PKTSTS_OUTRECVD: + { + usbtrace(TRACE_INTDECODE(NRF54L_TRACEINTID_OUTRECVD), epphy); + bcnt = (regval & OTG_GRXSTSD_BCNT_MASK) >> + OTG_GRXSTSD_BCNT_SHIFT; + if (bcnt > 0) + { + nrf54l_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 OTG_GRXSTSD_PKTSTS_OUTDONE: + { + usbtrace(TRACE_INTDECODE(NRF54L_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 OTG_GRXSTSD_PKTSTS_SETUPDONE: + { + usbtrace(TRACE_INTDECODE(NRF54L_TRACEINTID_SETUPDONE), epphy); + + /* Now that the Setup Phase is complete if it was an OUT enable + * the endpoint + * (Doing this here prevents the loss of the first FIFO word) + */ + + if (priv->ep0state == EP0STATE_SETUP_OUT) + { + /* Clear NAKSTS so that we can receive the data */ + + regval = nrf54l_getreg(NRF54L_USBD_DOEPCTL(0)); + regval |= OTG_DOEPCTL0_CNAK; + nrf54l_putreg(regval, NRF54L_USBD_DOEPCTL(0)); + } + } + 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 OTG_GRXSTSD_PKTSTS_SETUPRECVD: + { + uint16_t datlen; + + usbtrace(TRACE_INTDECODE(NRF54L_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. + */ + + nrf54l_rxfifo_read(&priv->epout[EP0], + (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) + { + 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(NRF54L_TRACEERR_INVALIDPARMS), + (regval & OTG_GRXSTSD_PKTSTS_MASK) >> + OTG_GRXSTSD_PKTSTS_SHIFT); + } + break; + } + } +} + +/**************************************************************************** + * Name: nrf54l_enuminterrupt + * + * Description: + * Enumeration done interrupt + * + ****************************************************************************/ + +static inline void nrf54l_enuminterrupt(struct nrf54l_usbdev_s *priv) +{ + uint32_t regval; + + regval = nrf54l_getreg(NRF54L_USBD_DSTS) & OTG_DSTS_ENUMSPD_MASK; + priv->usbdev.speed = regval == OTG_DSTS_ENUMSPD_HS ? USB_SPEED_HIGH : + USB_SPEED_FULL; + + /* Activate EP0 */ + + nrf54l_ep0in_activate(); + + /* Set USB turn-around time for the 16-bit UTMI+ PHY interface. */ + + regval = nrf54l_getreg(NRF54L_USBD_GUSBCFG); + regval &= ~OTG_GUSBCFG_TRDT_MASK; + regval |= OTG_GUSBCFG_TRDT(5); + nrf54l_putreg(regval, NRF54L_USBD_GUSBCFG); +} + +/**************************************************************************** + * Name: nrf54l_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 nrf54l_isocininterrupt(struct nrf54l_usbdev_s *priv) +{ + struct nrf54l_ep_s *privep; + uint32_t regaddr; + uint32_t doepctl; + uint32_t dsts; + bool eonum; + bool soffn; + 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 < NRF54L_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 = NRF54L_USBD_DIEPCTL(privep->epphy); + doepctl = nrf54l_getreg(regaddr); + dsts = nrf54l_getreg(NRF54L_USBD_DSTS); + + /* EONUM = 0:even frame, 1:odd frame + * SOFFN = Frame number of the received SOF + */ + + eonum = ((doepctl & OTG_DIEPCTL_EONUM) != 0); + soffn = ((dsts & OTG_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. + */ + + nrf54l_req_complete(privep, -EIO); + nrf54l_epin_disable(privep); + break; + } +} +#endif + +/**************************************************************************** + * Name: nrf54l_isocoutinterrupt + * + * Description: + * Incomplete periodic transfer interrupt + * + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_ISOCHRONOUS +static inline void nrf54l_isocoutinterrupt(struct nrf54l_usbdev_s *priv) +{ + struct nrf54l_ep_s *privep; + uint32_t regaddr; + uint32_t doepctl; + uint32_t dsts; + bool eonum; + bool soffn; + int i; + + /* 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 < NRF54L_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 = NRF54L_USBD_DOEPCTL(privep->epphy); + doepctl = nrf54l_getreg(regaddr); + dsts = nrf54l_getreg(NRF54L_USBD_DSTS); + + /* EONUM = 0:even frame, 1:odd frame + * SOFFN = Frame number of the received SOF + */ + + eonum = ((doepctl & OTG_DOEPCTL_EONUM) != 0); + soffn = ((dsts & OTG_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. + */ + + nrf54l_req_complete(privep, -EIO); + nrf54l_epout_disable(privep); + break; + } +} +#endif + +/**************************************************************************** + * Name: nrf54l_usbinterrupt + * + * Description: + * USB interrupt handler + * + ****************************************************************************/ + +static int nrf54l_usbinterrupt(int irq, void *context, void *arg) +{ + /* At present, there is only a single OTG device support. Hence it is + * pre-allocated as g_usbdev. 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. + */ + + struct nrf54l_usbdev_s *priv = &g_usbdev; + uint32_t regval; + uint32_t reserved; + + usbtrace(TRACE_INTENTRY(NRF54L_TRACEINTID_USB), 0); + + if (!nrf54l_ready(priv)) + { + return OK; + } + + /* Assure that we are in device mode */ + + DEBUGASSERT((nrf54l_getreg(NRF54L_USBD_GINTSTS) & OTG_GINTSTS_CMOD) == + OTG_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 = nrf54l_getreg(NRF54L_USBD_GINTSTS); + reserved = (regval & OTG_GINT_RESERVED); + regval &= nrf54l_getreg(NRF54L_USBD_GINTMSK); + + /* With out modifying the reserved bits, acknowledge all + * **Writable** pending irqs we will service below + */ + + nrf54l_putreg(((regval | reserved) & OTG_GINT_RC_W1), + NRF54L_USBD_GINTSTS); + + /* Break out of the loop when there are no further pending (and + * unmasked) interrupts to be processes. + */ + + if (regval == 0) + { + break; + } + + usbtrace(TRACE_INTDECODE(NRF54L_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 & OTG_GINT_OEP) != 0) + { + usbtrace(TRACE_INTDECODE(NRF54L_TRACEINTID_EPOUT), + (uint16_t)regval); + nrf54l_epout_interrupt(priv); + } + + /* 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 & OTG_GINT_IEP) != 0) + { + usbtrace(TRACE_INTDECODE(NRF54L_TRACEINTID_EPIN), + (uint16_t)regval); + nrf54l_epin_interrupt(priv); + } + + /* Host/device mode mismatch error interrupt */ + +#ifdef CONFIG_DEBUG_USB + if ((regval & OTG_GINT_MMIS) != 0) + { + usbtrace(TRACE_INTDECODE(NRF54L_TRACEINTID_MISMATCH), + (uint16_t)regval); + } +#endif + + /* Resume/remote wakeup detected interrupt */ + + if ((regval & OTG_GINT_WKUP) != 0) + { + usbtrace(TRACE_INTDECODE(NRF54L_TRACEINTID_WAKEUP), + (uint16_t)regval); + nrf54l_resumeinterrupt(priv); + } + + /* USB suspend interrupt */ + + if ((regval & OTG_GINT_USBSUSP) != 0) + { + usbtrace(TRACE_INTDECODE(NRF54L_TRACEINTID_SUSPEND), + (uint16_t)regval); + nrf54l_suspendinterrupt(priv); + } + + /* Start of frame interrupt */ + +#ifdef CONFIG_USBDEV_SOFINTERRUPT + if ((regval & OTG_GINT_SOF) != 0) + { + usbtrace(TRACE_INTDECODE(NRF54L_TRACEINTID_SOF), + (uint16_t)regval); + usbdev_sof_irq(&priv->usbdev, nrf54l_getframe(&priv->usbdev)); + } +#endif + + /* RxFIFO non-empty interrupt. Indicates that there is at least one + * packet pending to be read from the RxFIFO. + */ + + if ((regval & OTG_GINT_RXFLVL) != 0) + { + usbtrace(TRACE_INTDECODE(NRF54L_TRACEINTID_RXFIFO), + (uint16_t)regval); + nrf54l_rxinterrupt(priv); + } + + /* USB reset interrupt */ + + if ((regval & (OTG_GINT_USBRST | OTG_GINT_RSTDET)) != 0) + { + usbtrace(TRACE_INTDECODE(NRF54L_TRACEINTID_DEVRESET), + (uint16_t)regval); + + /* Perform the device reset */ + + nrf54l_usbreset(priv); + usbtrace(TRACE_INTEXIT(NRF54L_TRACEINTID_USB), 0); + return OK; + } + + /* Enumeration done interrupt */ + + if ((regval & OTG_GINT_ENUMDNE) != 0) + { + usbtrace(TRACE_INTDECODE(NRF54L_TRACEINTID_ENUMDNE), + (uint16_t)regval); + nrf54l_enuminterrupt(priv); + } + + /* 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 & OTG_GINT_IISOIXFR) != 0) + { + usbtrace(TRACE_INTDECODE(NRF54L_TRACEINTID_IISOIXFR), + (uint16_t)regval); + nrf54l_isocininterrupt(priv); + } + + /* 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 & OTG_GINT_IISOOXFR) != 0) + { + usbtrace(TRACE_INTDECODE(NRF54L_TRACEINTID_IISOOXFR), + (uint16_t)regval); + nrf54l_isocoutinterrupt(priv); + } +#endif + } + + usbtrace(TRACE_INTEXIT(NRF54L_TRACEINTID_USB), 0); + return OK; +} + +/**************************************************************************** + * Endpoint operations + ****************************************************************************/ + +/**************************************************************************** + * Name: nrf54l_enablegonak + * + * Description: + * Enable global OUT NAK mode + * + ****************************************************************************/ + +static void nrf54l_enablegonak(struct nrf54l_ep_s *privep) +{ + uint32_t regval; + + /* First, make sure that there is no GNOAKEFF interrupt pending. */ + +#if 0 + nrf54l_putreg(OTG_GINT_GONAKEFF, NRF54L_USBD_GINTSTS); +#endif + + /* Enable Global OUT NAK mode in the core. */ + + regval = nrf54l_getreg(NRF54L_USBD_DCTL); + regval |= OTG_DCTL_SGONAK; + nrf54l_putreg(regval, NRF54L_USBD_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 ((nrf54l_getreg(NRF54L_USBD_GINTSTS) & OTG_GINT_GONAKEFF) == 0); + nrf54l_putreg(OTG_GINT_GONAKEFF, NRF54L_USBD_GINTSTS); + +#else + /* Since we are in the interrupt handler, we cannot wait inline for the + * GONAKEFF because it cannot occur until service the 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 OTG DCTL register? + */ + + while ((nrf54l_getreg(NRF54L_USBD_DCTL) & OTG_DCTL_GONSTS) == 0); +#endif +} + +/**************************************************************************** + * Name: nrf54l_disablegonak + * + * Description: + * Disable global OUT NAK mode + * + ****************************************************************************/ + +static void nrf54l_disablegonak(struct nrf54l_ep_s *privep) +{ + uint32_t regval; + + /* Set the "Clear the Global OUT NAK bit" to disable global OUT NAK mode */ + + regval = nrf54l_getreg(NRF54L_USBD_DCTL); + regval |= OTG_DCTL_CGONAK; + nrf54l_putreg(regval, NRF54L_USBD_DCTL); +} + +/**************************************************************************** + * Name: nrf54l_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 nrf54l_epout_configure(struct nrf54l_ep_s *privep, + uint8_t eptype, uint16_t maxpacket) +{ + int mpsiz; + uint32_t mpsmask = OTG_DOEPCTL_MPSIZ_MASK; + uint32_t regaddr; + uint32_t regval; + + usbtrace(TRACE_EPCONFIGURE, privep->epphy); + + /* EP0 uses an encoded packet size; other endpoints use a byte count. */ + + if (privep->epphy == EP0) + { + mpsiz = nrf54l_ep0_mpsiz(maxpacket); + if (mpsiz < 0 || eptype != USB_EP_ATTR_XFER_CONTROL) + { + return -EINVAL; + } + + mpsmask = OTG_DOEPCTL0_MPSIZ_MASK; + } + else + { + mpsiz = (maxpacket << OTG_DOEPCTL_MPSIZ_SHIFT); + } + + /* EP0 is always active. Other active endpoints retain their + * configuration. + */ + + regaddr = NRF54L_USBD_DOEPCTL(privep->epphy); + regval = nrf54l_getreg(regaddr); + if (privep->epphy == EP0 || (regval & OTG_DOEPCTL_USBAEP) == 0) + { + if (regval & OTG_DOEPCTL_NAKSTS) + { + regval |= OTG_DOEPCTL_CNAK; + } + + regval &= ~(mpsmask | OTG_DOEPCTL_EPTYP_MASK); + regval |= mpsiz; + regval |= (eptype << OTG_DOEPCTL_EPTYP_SHIFT); + regval |= OTG_DOEPCTL_USBAEP; + if (privep->epphy != EP0) + { + regval |= OTG_DOEPCTL_SD0PID; + } + + nrf54l_putreg(regval, regaddr); + + /* Save the endpoint configuration */ + + privep->ep.maxpacket = maxpacket; + privep->eptype = eptype; + privep->stalled = false; + privep->active = false; + privep->zlp = false; + } + + /* Enable the interrupt for this endpoint */ + + regval = nrf54l_getreg(NRF54L_USBD_DAINTMSK); + regval |= OTG_DAINT_OEP(privep->epphy); + nrf54l_putreg(regval, NRF54L_USBD_DAINTMSK); + return OK; +} + +/**************************************************************************** + * Name: nrf54l_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 nrf54l_epin_configure(struct nrf54l_ep_s *privep, + uint8_t eptype, uint16_t maxpacket) +{ + int mpsiz; + uint32_t mpsmask = OTG_DIEPCTL_MPSIZ_MASK; + uint32_t regaddr; + uint32_t regval; + + usbtrace(TRACE_EPCONFIGURE, privep->epphy); + + /* EP0 uses an encoded packet size; other endpoints use a byte count. */ + + if (privep->epphy == EP0) + { + mpsiz = nrf54l_ep0_mpsiz(maxpacket); + if (mpsiz < 0 || eptype != USB_EP_ATTR_XFER_CONTROL) + { + return -EINVAL; + } + + mpsmask = OTG_DIEPCTL0_MPSIZ_MASK; + } + else + { + mpsiz = (maxpacket << OTG_DIEPCTL_MPSIZ_SHIFT); + } + + /* EP0 is always active. Other active endpoints retain their + * configuration. + */ + + regaddr = NRF54L_USBD_DIEPCTL(privep->epphy); + regval = nrf54l_getreg(regaddr); + if (privep->epphy == EP0 || (regval & OTG_DIEPCTL_USBAEP) == 0) + { + if (regval & OTG_DIEPCTL_NAKSTS) + { + regval |= OTG_DIEPCTL_CNAK; + } + + regval &= ~(mpsmask | OTG_DIEPCTL_EPTYP_MASK | + OTG_DIEPCTL_TXFNUM_MASK); + regval |= mpsiz; + regval |= (eptype << OTG_DIEPCTL_EPTYP_SHIFT); + regval |= (privep->epphy << OTG_DIEPCTL_TXFNUM_SHIFT); + regval |= OTG_DIEPCTL_USBAEP; + if (privep->epphy != EP0) + { + regval |= OTG_DIEPCTL_SD0PID; + } + + nrf54l_putreg(regval, regaddr); + + /* Save the endpoint configuration */ + + privep->ep.maxpacket = maxpacket; + privep->eptype = eptype; + privep->stalled = false; + privep->active = false; + privep->zlp = false; + } + + /* Enable the interrupt for this endpoint */ + + regval = nrf54l_getreg(NRF54L_USBD_DAINTMSK); + regval |= OTG_DAINT_IEP(privep->epphy); + nrf54l_putreg(regval, NRF54L_USBD_DAINTMSK); + + return OK; +} + +/**************************************************************************** + * Name: nrf54l_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 nrf54l_ep_configure(struct usbdev_ep_s *ep, + const struct usb_epdesc_s *desc, + bool last) +{ + struct nrf54l_ep_s *privep = (struct nrf54l_ep_s *)ep; + uint16_t maxpacket; + uint8_t eptype; + irqstate_t flags; + 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; + + if (maxpacket == 0 || maxpacket > 1024 || + (privep->isin && maxpacket > NRF54L_TXFIFO_WORDS * 4)) + { + return -EINVAL; + } + + flags = enter_critical_section(); + if (!nrf54l_ready(privep->dev)) + { + leave_critical_section(flags); + return -ESHUTDOWN; + } + + /* Setup Endpoint Control Register */ + + if (privep->isin) + { + ret = nrf54l_epin_configure(privep, eptype, maxpacket); + } + else + { + ret = nrf54l_epout_configure(privep, eptype, maxpacket); + } + + leave_critical_section(flags); + return ret; +} + +/**************************************************************************** + * Name: nrf54l_ep0_configure + * + * Description: + * Reset Usb engine + * + ****************************************************************************/ + +static void nrf54l_ep0_configure(struct nrf54l_usbdev_s *priv) +{ + /* Enable EP0 IN and OUT */ + + nrf54l_epin_configure(&priv->epin[EP0], USB_EP_ATTR_XFER_CONTROL, + CONFIG_USBDEV_EP0_MAXSIZE); + nrf54l_epout_configure(&priv->epout[EP0], USB_EP_ATTR_XFER_CONTROL, + CONFIG_USBDEV_EP0_MAXSIZE); +} + +/**************************************************************************** + * Name: nrf54l_epout_disable + * + * Description: + * Disable an OUT endpoint will no longer be used + * + ****************************************************************************/ + +static void nrf54l_epout_disable(struct nrf54l_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 = enter_critical_section(); + nrf54l_enablegonak(privep); + + /* Disable the required OUT endpoint by setting the EPDIS and SNAK bits + * int DOECPTL register. + */ + + regaddr = NRF54L_USBD_DOEPCTL(privep->epphy); + regval = nrf54l_getreg(regaddr); + regval &= ~OTG_DOEPCTL_USBAEP; + regval |= (OTG_DOEPCTL_EPDIS | OTG_DOEPCTL_SNAK); + nrf54l_putreg(regval, regaddr); + + /* Wait for the EPDISD interrupt which indicates that the OUT + * endpoint is completely disabled. + */ + +#if 0 /* Doesn't happen */ + regaddr = NRF54L_USBD_DOEPINT(privep->epphy); + while ((nrf54l_getreg(regaddr) & OTG_DOEPINT_EPDISD) == 0); +#else + /* REVISIT: */ + + up_udelay(10); +#endif + + /* Clear the EPDISD interrupt indication */ + + nrf54l_putreg(OTG_DOEPINT_EPDISD, NRF54L_USBD_DOEPINT(privep->epphy)); + + /* Then disable the Global OUT NAK mode to continue receiving data + * from other non-disabled OUT endpoints. + */ + + nrf54l_disablegonak(privep); + + /* Disable endpoint interrupts */ + + regval = nrf54l_getreg(NRF54L_USBD_DAINTMSK); + regval &= ~OTG_DAINT_OEP(privep->epphy); + nrf54l_putreg(regval, NRF54L_USBD_DAINTMSK); + + /* Cancel any queued read requests */ + + nrf54l_req_cancel(privep, -ESHUTDOWN); + + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: nrf54l_epin_disable + * + * Description: + * Disable an IN endpoint when it will no longer be used + * + ****************************************************************************/ + +static void nrf54l_epin_disable(struct nrf54l_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 = NRF54L_USBD_DIEPCTL(privep->epphy); + regval = nrf54l_getreg(regaddr); + if ((regval & OTG_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). + */ + + nrf54l_putreg(OTG_DIEPINT_INEPNE, NRF54L_USBD_DIEPINT(privep->epphy)); + + /* Set the endpoint in NAK mode */ + + regaddr = NRF54L_USBD_DIEPCTL(privep->epphy); + regval = nrf54l_getreg(regaddr); + regval &= ~OTG_DIEPCTL_USBAEP; + regval |= (OTG_DIEPCTL_EPDIS | OTG_DIEPCTL_SNAK); + nrf54l_putreg(regval, regaddr); + + /* Wait for the INEPNE interrupt that indicates that we are now in + * NAK mode + */ + + regaddr = NRF54L_USBD_DIEPINT(privep->epphy); + while ((nrf54l_getreg(regaddr) & OTG_DIEPINT_INEPNE) == 0); + + /* Clear the INEPNE interrupt indication */ + + nrf54l_putreg(OTG_DIEPINT_INEPNE, regaddr); +#endif + + /* Deactivate and disable the endpoint by setting the EPDIS and SNAK bits + * the DIEPCTLx register. + */ + + flags = enter_critical_section(); + regaddr = NRF54L_USBD_DIEPCTL(privep->epphy); + regval = nrf54l_getreg(regaddr); + regval &= ~OTG_DIEPCTL_USBAEP; + regval |= (OTG_DIEPCTL_EPDIS | OTG_DIEPCTL_SNAK); + nrf54l_putreg(regval, regaddr); + + /* Wait for the EPDISD interrupt which indicates that the IN + * endpoint is completely disabled. + */ + + regaddr = NRF54L_USBD_DIEPINT(privep->epphy); + while ((nrf54l_getreg(regaddr) & OTG_DIEPINT_EPDISD) == 0); + + /* Clear the EPDISD interrupt indication */ + + regval = nrf54l_getreg(regaddr); + regval |= OTG_DIEPINT_EPDISD; + nrf54l_putreg(regval, regaddr); + + /* Flush any data remaining in the TxFIFO */ + + nrf54l_txfifo_flush(OTG_GRSTCTL_TXFNUM_D(privep->epphy)); + + /* Disable endpoint interrupts */ + + regval = nrf54l_getreg(NRF54L_USBD_DAINTMSK); + regval &= ~OTG_DAINT_IEP(privep->epphy); + nrf54l_putreg(regval, NRF54L_USBD_DAINTMSK); + + /* Cancel any queued write requests */ + + nrf54l_req_cancel(privep, -ESHUTDOWN); + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: nrf54l_ep_disable + * + * Description: + * The endpoint will no longer be used + * + ****************************************************************************/ + +static int nrf54l_ep_disable(struct usbdev_ep_s *ep) +{ + struct nrf54l_ep_s *privep = (struct nrf54l_ep_s *)ep; + irqstate_t flags; + +#ifdef CONFIG_DEBUG_FEATURES + if (!ep) + { + usbtrace(TRACE_DEVERROR(NRF54L_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + + usbtrace(TRACE_EPDISABLE, privep->epphy); + + flags = enter_critical_section(); + if (!nrf54l_ready(privep->dev)) + { + nrf54l_req_cancel(privep, -ESHUTDOWN); + privep->active = false; + leave_critical_section(flags); + return OK; + } + + /* Is this an IN or an OUT endpoint */ + + if (privep->isin) + { + /* Disable the IN endpoint */ + + nrf54l_epin_disable(privep); + } + else + { + /* Disable the OUT endpoint */ + + nrf54l_epout_disable(privep); + } + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: nrf54l_ep_allocreq + * + * Description: + * Allocate an I/O request + * + ****************************************************************************/ + +static struct usbdev_req_s *nrf54l_ep_allocreq(struct usbdev_ep_s *ep) +{ + struct nrf54l_req_s *privreq; + +#ifdef CONFIG_DEBUG_FEATURES + if (!ep) + { + usbtrace(TRACE_DEVERROR(NRF54L_TRACEERR_INVALIDPARMS), 0); + return NULL; + } +#endif + + usbtrace(TRACE_EPALLOCREQ, ((struct nrf54l_ep_s *)ep)->epphy); + + privreq = kmm_malloc(sizeof(struct nrf54l_req_s)); + if (!privreq) + { + usbtrace(TRACE_DEVERROR(NRF54L_TRACEERR_ALLOCFAIL), 0); + return NULL; + } + + memset(privreq, 0, sizeof(struct nrf54l_req_s)); + return &privreq->req; +} + +/**************************************************************************** + * Name: nrf54l_ep_freereq + * + * Description: + * Free an I/O request + * + ****************************************************************************/ + +static void nrf54l_ep_freereq(struct usbdev_ep_s *ep, + struct usbdev_req_s *req) +{ + struct nrf54l_req_s *privreq = (struct nrf54l_req_s *)req; + +#ifdef CONFIG_DEBUG_FEATURES + if (!ep || !req) + { + usbtrace(TRACE_DEVERROR(NRF54L_TRACEERR_INVALIDPARMS), 0); + return; + } +#endif + + usbtrace(TRACE_EPFREEREQ, ((struct nrf54l_ep_s *)ep)->epphy); + kmm_free(privreq); +} + +/**************************************************************************** + * Name: nrf54l_ep_allocbuffer + * + * Description: + * Allocate an I/O buffer + * + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_DMA +static void *nrf54l_ep_allocbuffer(struct usbdev_ep_s *ep, unsigned bytes) +{ + usbtrace(TRACE_EPALLOCBUFFER, ((struct nrf54l_ep_s *)ep)->epphy); + +#ifdef CONFIG_USBDEV_DMAMEMORY + return usbdev_dma_alloc(bytes); +#else + return kmm_malloc(bytes); +#endif +} +#endif + +/**************************************************************************** + * Name: nrf54l_ep_freebuffer + * + * Description: + * Free an I/O buffer + * + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_DMA +static void nrf54l_ep_freebuffer(struct usbdev_ep_s *ep, void *buf) +{ + usbtrace(TRACE_EPALLOCBUFFER, ((struct nrf54l_ep_s *)ep)->epphy); + +#ifdef CONFIG_USBDEV_DMAMEMORY + usbdev_dma_free(buf); +#else + kmm_free(buf); +#endif +} +#endif + +/**************************************************************************** + * Name: nrf54l_ep_submit + * + * Description: + * Submit an I/O request to the endpoint + * + ****************************************************************************/ + +static int nrf54l_ep_submit(struct usbdev_ep_s *ep, + struct usbdev_req_s *req) +{ + struct nrf54l_req_s *privreq = (struct nrf54l_req_s *)req; + struct nrf54l_ep_s *privep = (struct nrf54l_ep_s *)ep; + struct nrf54l_usbdev_s *priv; + irqstate_t flags; + int ret = OK; + + /* Some sanity checking */ + +#ifdef CONFIG_DEBUG_FEATURES + if (!req || !req->callback || !req->buf || !ep) + { + usbtrace(TRACE_DEVERROR(NRF54L_TRACEERR_INVALIDPARMS), 0); + uinfo("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_FEATURES + if (!priv->driver) + { + usbtrace(TRACE_DEVERROR(NRF54L_TRACEERR_NOTCONFIGURED), + priv->usbdev.speed); + return -ESHUTDOWN; + } +#endif + + /* Handle the request from the class driver */ + + req->result = -EINPROGRESS; + req->xfrd = 0; + + /* Disable Interrupts */ + + flags = enter_critical_section(); + + /* If we are stalled, then drop all requests on the floor */ + + if (!priv->driver || !nrf54l_ready(priv)) + { + ret = -ESHUTDOWN; + } + else if (privep->stalled) + { + ret = -EBUSY; + } + else + { + /* Add the new request to the request queue for the endpoint. */ + + if (nrf54l_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) + { + nrf54l_epin_request(priv, privep); + } + } + + /* If the request was added to an OUT endpoint, 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); + nrf54l_epout_request(priv, privep); + } + } + } + + leave_critical_section(flags); + return ret; +} + +/**************************************************************************** + * Name: nrf54l_ep_cancel + * + * Description: + * Cancel an I/O request previously sent to an endpoint + * + ****************************************************************************/ + +static int nrf54l_ep_cancel(struct usbdev_ep_s *ep, + struct usbdev_req_s *req) +{ + struct nrf54l_ep_s *privep = (struct nrf54l_ep_s *)ep; + irqstate_t flags; + +#ifdef CONFIG_DEBUG_FEATURES + if (!ep || !req) + { + usbtrace(TRACE_DEVERROR(NRF54L_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + + usbtrace(TRACE_EPCANCEL, privep->epphy); + + flags = enter_critical_section(); + + /* 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 ... + */ + + nrf54l_req_cancel(privep, -ESHUTDOWN); + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: nrf54l_epout_setstall + * + * Description: + * Stall an OUT endpoint + * + ****************************************************************************/ + +static int nrf54l_epout_setstall(struct nrf54l_ep_s *privep) +{ +#if 1 + /* Disable OUT traffic before stalling the endpoint. */ + + uint32_t regaddr; + uint32_t regval; + + /* Put the core in the Global OUT NAK mode */ + + nrf54l_enablegonak(privep); + + /* Disable and STALL the OUT endpoint by setting the EPDIS and STALL bits + * in the DOECPTL register. + */ + + regaddr = NRF54L_USBD_DOEPCTL(privep->epphy); + regval = nrf54l_getreg(regaddr); + regval |= (OTG_DOEPCTL_EPDIS | OTG_DOEPCTL_STALL); + nrf54l_putreg(regval, regaddr); + + /* Wait for the EPDISD interrupt which indicates that the OUT + * endpoint is completely disabled. + */ + +#if 0 /* Doesn't happen */ + regaddr = NRF54L_USBD_DOEPINT(privep->epphy); + while ((nrf54l_getreg(regaddr) & OTG_DOEPINT_EPDISD) == 0); +#else + /* REVISIT: */ + + up_udelay(10); +#endif + + /* Disable Global OUT NAK mode */ + + nrf54l_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 = NRF54L_USBD_DOEPCTL(privep->epphy); + regval = nrf54l_getreg(regaddr); + regval |= OTG_DOEPCTL_STALL; + nrf54l_putreg(regval, regaddr); + + /* The endpoint is now stalled */ + + privep->stalled = true; + return OK; +#endif +} + +/**************************************************************************** + * Name: nrf54l_epin_setstall + * + * Description: + * Stall an IN endpoint + * + ****************************************************************************/ + +static int nrf54l_epin_setstall(struct nrf54l_ep_s *privep) +{ + uint32_t regaddr; + uint32_t regval; + + /* Get the IN endpoint device control register */ + + regaddr = NRF54L_USBD_DIEPCTL(privep->epphy); + regval = nrf54l_getreg(regaddr); + + /* Then stall the endpoint */ + + regval |= OTG_DIEPCTL_STALL; + nrf54l_putreg(regval, regaddr); + + /* The endpoint is now stalled */ + + privep->stalled = true; + return OK; +} + +/**************************************************************************** + * Name: nrf54l_ep_setstall + * + * Description: + * Stall an endpoint + * + ****************************************************************************/ + +static int nrf54l_ep_setstall(struct nrf54l_ep_s *privep) +{ + usbtrace(TRACE_EPSTALL, privep->epphy); + + /* Is this an IN endpoint? */ + + if (privep->isin == 1) + { + return nrf54l_epin_setstall(privep); + } + else + { + return nrf54l_epout_setstall(privep); + } +} + +/**************************************************************************** + * Name: nrf54l_ep_clrstall + * + * Description: + * Resume a stalled endpoint + * + ****************************************************************************/ + +static int nrf54l_ep_clrstall(struct nrf54l_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 = NRF54L_USBD_DIEPCTL(privep->epphy); + stallbit = OTG_DIEPCTL_STALL; + data0bit = OTG_DIEPCTL_SD0PID; + } + else + { + /* Clear the stall bit in the IN endpoint device control register */ + + regaddr = NRF54L_USBD_DOEPCTL(privep->epphy); + stallbit = OTG_DOEPCTL_STALL; + data0bit = OTG_DOEPCTL_SD0PID; + } + + /* Clear the stall bit */ + + regval = nrf54l_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; + } + + nrf54l_putreg(regval, regaddr); + + /* The endpoint is no longer stalled */ + + privep->stalled = false; + return OK; +} + +/**************************************************************************** + * Name: nrf54l_ep_stall + * + * Description: + * Stall or resume an endpoint + * + ****************************************************************************/ + +static int nrf54l_ep_stall(struct usbdev_ep_s *ep, bool resume) +{ + struct nrf54l_ep_s *privep = (struct nrf54l_ep_s *)ep; + irqstate_t flags; + int ret; + + /* Set or clear the stall condition as requested */ + + flags = enter_critical_section(); + if (!nrf54l_ready(privep->dev)) + { + leave_critical_section(flags); + return -ESHUTDOWN; + } + + if (resume) + { + ret = nrf54l_ep_clrstall(privep); + } + else + { + ret = nrf54l_ep_setstall(privep); + } + + leave_critical_section(flags); + + return ret; +} + +/**************************************************************************** + * Name: nrf54l_ep0_stall + * + * Description: + * Stall endpoint 0 + * + ****************************************************************************/ + +static void nrf54l_ep0_stall(struct nrf54l_usbdev_s *priv) +{ + nrf54l_epin_setstall(&priv->epin[EP0]); + nrf54l_epout_setstall(&priv->epout[EP0]); + priv->stalled = true; + nrf54l_ep0out_ctrlsetup(priv); +} + +/**************************************************************************** + * Device operations + ****************************************************************************/ + +/**************************************************************************** + * Name: nrf54l_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 struct usbdev_ep_s *nrf54l_ep_alloc(struct usbdev_s *dev, + uint8_t eplog, bool in, + uint8_t eptype) +{ + struct nrf54l_usbdev_s *priv = (struct nrf54l_usbdev_s *)dev; + uint16_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 = enter_critical_section(); + 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 >= NRF54L_NENDPOINTS) + { + usbtrace(TRACE_DEVERROR(NRF54L_TRACEERR_BADEPNO), (uint16_t)epphy); + leave_critical_section(flags); + 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 < NRF54L_NENDPOINTS; epno++) + { + uint16_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 */ + + leave_critical_section(flags); + return in ? &priv->epin[epno].ep : &priv->epout[epno].ep; + } + } + + /* We should not get here */ + } + + usbtrace(TRACE_DEVERROR(NRF54L_TRACEERR_NOEP), (uint16_t)eplog); + leave_critical_section(flags); + return NULL; +} + +/**************************************************************************** + * Name: nrf54l_ep_free + * + * Description: + * Free the previously allocated endpoint + * + ****************************************************************************/ + +static void nrf54l_ep_free(struct usbdev_s *dev, + struct usbdev_ep_s *ep) +{ + struct nrf54l_usbdev_s *priv = (struct nrf54l_usbdev_s *)dev; + struct nrf54l_ep_s *privep = (struct nrf54l_ep_s *)ep; + irqstate_t flags; + + usbtrace(TRACE_DEVFREEEP, (uint16_t)privep->epphy); + + if (priv && privep) + { + /* Mark the endpoint as available */ + + flags = enter_critical_section(); + priv->epavail[privep->isin] |= (1 << privep->epphy); + leave_critical_section(flags); + } +} + +/**************************************************************************** + * Name: nrf54l_getframe + * + * Description: + * Returns the current frame number + * + ****************************************************************************/ + +static int nrf54l_getframe(struct usbdev_s *dev) +{ + struct nrf54l_usbdev_s *priv = (struct nrf54l_usbdev_s *)dev; + uint32_t regval; + irqstate_t flags; + + usbtrace(TRACE_DEVGETFRAME, 0); + + /* Return the last frame number of the last SOF detected by the hardware */ + + flags = enter_critical_section(); + if (!nrf54l_ready(priv)) + { + leave_critical_section(flags); + return -ESHUTDOWN; + } + + regval = nrf54l_getreg(NRF54L_USBD_DSTS); + leave_critical_section(flags); + return (int)((regval & OTG_DSTS_SOFFN_MASK) >> OTG_DSTS_SOFFN_SHIFT); +} + +/**************************************************************************** + * Name: nrf54l_wakeup + * + * Description: + * Exit suspend mode. + * + ****************************************************************************/ + +static int nrf54l_wakeup(struct usbdev_s *dev) +{ + struct nrf54l_usbdev_s *priv = (struct nrf54l_usbdev_s *)dev; + uint32_t regval; + irqstate_t flags; + + usbtrace(TRACE_DEVWAKEUP, 0); + + /* Is wakeup enabled? */ + + flags = enter_critical_section(); + if (!nrf54l_ready(priv)) + { + leave_critical_section(flags); + return -ESHUTDOWN; + } + + if (priv->wakeup) + { + /* Yes... is the core suspended? */ + + regval = nrf54l_getreg(NRF54L_USBD_DSTS); + if ((regval & OTG_DSTS_SUSPSTS) != 0) + { + /* Activate Remote wakeup signaling */ + + regval = nrf54l_getreg(NRF54L_USBD_DCTL); + regval |= OTG_DCTL_RWUSIG; + nrf54l_putreg(regval, NRF54L_USBD_DCTL); + up_mdelay(5); + regval &= ~OTG_DCTL_RWUSIG; + nrf54l_putreg(regval, NRF54L_USBD_DCTL); + } + } + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: nrf54l_selfpowered + * + * Description: + * Sets/clears the device self-powered feature + * + ****************************************************************************/ + +static int nrf54l_selfpowered(struct usbdev_s *dev, bool selfpowered) +{ + struct nrf54l_usbdev_s *priv = (struct nrf54l_usbdev_s *)dev; + + usbtrace(TRACE_DEVSELFPOWERED, (uint16_t)selfpowered); + +#ifdef CONFIG_DEBUG_FEATURES + if (!dev) + { + usbtrace(TRACE_DEVERROR(NRF54L_TRACEERR_INVALIDPARMS), 0); + return -ENODEV; + } +#endif + + priv->selfpowered = selfpowered; + return OK; +} + +/**************************************************************************** + * Name: nrf54l_pullup + * + * Description: + * Software-controlled connect to/disconnect from USB host + * + ****************************************************************************/ + +static int nrf54l_pullup(struct usbdev_s *dev, bool enable) +{ + struct nrf54l_usbdev_s *priv = (struct nrf54l_usbdev_s *)dev; + irqstate_t flags; + int ret; + + usbtrace(TRACE_DEVPULLUP, (uint16_t)enable); + + flags = enter_critical_section(); + priv->connected = enable; + if (!enable && priv->active) + { + nrf54l_usbhs_pullup(false); + } + + ret = work_queue(LPWORK, &priv->vbuswork, nrf54l_vbus_worker, priv, 0); + leave_critical_section(flags); + return ret == -EBUSY ? OK : ret; +} + +/**************************************************************************** + * Name: nrf54l_setaddress + * + * Description: + * Set the devices USB address + * + ****************************************************************************/ + +static void nrf54l_setaddress(struct nrf54l_usbdev_s *priv, uint16_t address) +{ + uint32_t regval; + + /* Set the device address in the DCFG register */ + + regval = nrf54l_getreg(NRF54L_USBD_DCFG); + regval &= ~OTG_DCFG_DAD_MASK; + regval |= ((uint32_t)address << OTG_DCFG_DAD_SHIFT); + nrf54l_putreg(regval, NRF54L_USBD_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: nrf54l_txfifo_flush + * + * Description: + * Flush the specific TX fifo. + * + ****************************************************************************/ + +static int nrf54l_txfifo_flush(uint32_t txfnum) +{ + uint32_t regval; + uint32_t timeout; + + /* Initiate the TX FIFO flush operation */ + + regval = OTG_GRSTCTL_TXFFLSH | txfnum; + nrf54l_putreg(regval, NRF54L_USBD_GRSTCTL); + + /* Wait for the FLUSH to complete */ + + for (timeout = 0; timeout < NRF54L_FLUSH_DELAY; timeout++) + { + regval = nrf54l_getreg(NRF54L_USBD_GRSTCTL); + if ((regval & OTG_GRSTCTL_TXFFLSH) == 0) + { + break; + } + } + + /* Wait for 3 PHY Clocks */ + + up_udelay(3); + return OK; +} + +/**************************************************************************** + * Name: nrf54l_rxfifo_flush + * + * Description: + * Flush the RX fifo. + * + ****************************************************************************/ + +static int nrf54l_rxfifo_flush(void) +{ + uint32_t regval; + uint32_t timeout; + + /* Initiate the RX FIFO flush operation */ + + nrf54l_putreg(OTG_GRSTCTL_RXFFLSH, NRF54L_USBD_GRSTCTL); + + /* Wait for the FLUSH to complete */ + + for (timeout = 0; timeout < NRF54L_FLUSH_DELAY; timeout++) + { + regval = nrf54l_getreg(NRF54L_USBD_GRSTCTL); + if ((regval & OTG_GRSTCTL_RXFFLSH) == 0) + { + break; + } + } + + /* Wait for 3 PHY Clocks */ + + up_udelay(3); + return OK; +} + +/**************************************************************************** + * Name: nrf54l_swinitialize + * + * Description: + * Initialize all driver data structures. + * + ****************************************************************************/ + +static void nrf54l_swinitialize(struct nrf54l_usbdev_s *priv) +{ + struct nrf54l_ep_s *privep; + int i; + + /* Initialize the device state structure */ + + memset(priv, 0, sizeof(struct nrf54l_usbdev_s)); + + priv->usbdev.ops = &g_devops; + priv->usbdev.ep0 = &priv->epin[EP0].ep; + + priv->epavail[0] = NRF54L_EP_AVAILABLE; + priv->epavail[1] = NRF54L_EP_AVAILABLE; + + /* Initialize the endpoint lists */ + + for (i = 0; i < NRF54L_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 = NRF54L_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 < NRF54L_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 = NRF54L_EPPHYOUT2LOG(i); + + /* Control until endpoint is activated */ + + privep->eptype = USB_EP_ATTR_XFER_CONTROL; + privep->ep.maxpacket = CONFIG_USBDEV_EP0_MAXSIZE; + } +} + +/**************************************************************************** + * Name: nrf54l_hwinitialize + * + * Description: + * Configure the OTG core for operation. + * + ****************************************************************************/ + +static int nrf54l_hwinitialize(struct nrf54l_usbdev_s *priv) +{ + uint32_t regval; + uint32_t timeout; + uint32_t address; + int i; + + /* At start-up the core is in FS/HS 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). + */ + + nrf54l_putreg(OTG_GAHBCFG_TXFELVL, NRF54L_USBD_GAHBCFG); + + /* Common USB OTG core initialization */ + + /* Wait for AHB master IDLE state */ + + for (timeout = 0; timeout < NRF54L_READY_DELAY; timeout++) + { + up_udelay(3); + regval = nrf54l_getreg(NRF54L_USBD_GRSTCTL); + if ((regval & OTG_GRSTCTL_AHBIDL) != 0) + { + break; + } + } + + if (timeout == NRF54L_READY_DELAY) + { + return -ETIMEDOUT; + } + + /* Then perform the core soft reset. */ + + regval = nrf54l_getreg(NRF54L_USBD_GRSTCTL); + regval |= OTG_GRSTCTL_CSRST; + nrf54l_putreg(regval, NRF54L_USBD_GRSTCTL); + for (timeout = 0; timeout < NRF54L_READY_DELAY; timeout++) + { + up_udelay(1); + regval = nrf54l_getreg(NRF54L_USBD_GRSTCTL); + if ((regval & OTG_GRSTCTL_CSRST) == 0 || + (regval & OTG_GRSTCTL_CSRSTDONE) != 0) + { + break; + } + } + + if (timeout == NRF54L_READY_DELAY) + { + return -ETIMEDOUT; + } + + /* CSFTRSTDONE is write-one-to-clear on the LM20 core. */ + + nrf54l_putreg(regval & ~OTG_GRSTCTL_CSRST, NRF54L_USBD_GRSTCTL); + up_udelay(3); + + /* Force device mode with the LM20 16-bit UTMI+ PHY. */ + + regval = OTG_GUSBCFG_FDMOD | OTG_GUSBCFG_PHYIF | + OTG_GUSBCFG_TRDT(5); + nrf54l_putreg(regval, NRF54L_USBD_GUSBCFG); + up_mdelay(50); + + if (priv->vbus_changed || !nrf54l_usbhs_vbus()) + { + return -ENODEV; + } + + /* The FIFO depth is reported in 32-bit words by the DWC2 core. */ + + regval = nrf54l_getreg(NRF54L_USBD_GHWCFG3) >> + OTG_GHWCFG3_DFIFODEPTH_SHIFT; + if (regval < NRF54L_RXFIFO_WORDS + NRF54L_EP0_TXFIFO_WORDS + + (NRF54L_NENDPOINTS - 1) * NRF54L_TXFIFO_WORDS) + { + return -ENOSPC; + } + + /* Initialize device mode */ + + /* Restart the PHY Clock */ + + nrf54l_putreg(0, NRF54L_USBD_PCGCCTL); + + /* Device configuration register */ + + regval = nrf54l_getreg(NRF54L_USBD_DCFG); + regval &= ~OTG_DCFG_PFIVL_MASK; + regval |= OTG_DCFG_PFIVL_80PCT; + nrf54l_putreg(regval, NRF54L_USBD_DCFG); + + /* Set device high or full speed */ + + regval = nrf54l_getreg(NRF54L_USBD_DCFG); + regval &= ~OTG_DCFG_DSPD_MASK; +#ifdef CONFIG_USBDEV_DUALSPEED + regval |= OTG_DCFG_DSPD_HS; +#else + regval |= OTG_DCFG_DSPD_FSHS; +#endif + nrf54l_putreg(regval, NRF54L_USBD_DCFG); + + /* Set Rx FIFO size */ + + nrf54l_putreg(NRF54L_RXFIFO_WORDS, NRF54L_USBD_GRXFSIZ); + + address = NRF54L_RXFIFO_WORDS; + regval = (address << OTG_DIEPTXF0_TX0FD_SHIFT) | + (NRF54L_EP0_TXFIFO_WORDS << OTG_DIEPTXF0_TX0FSA_SHIFT); + nrf54l_putreg(regval, NRF54L_USBD_DIEPTXF0); + address += NRF54L_EP0_TXFIFO_WORDS; + for (i = 1; i < NRF54L_NENDPOINTS; i++) + { + regval = (address << OTG_DIEPTXF_INEPTXSA_SHIFT) | + (NRF54L_TXFIFO_WORDS << OTG_DIEPTXF_INEPTXFD_SHIFT); + nrf54l_putreg(regval, NRF54L_USBD_DIEPTXF(i)); + address += NRF54L_TXFIFO_WORDS; + } + + /* Flush the FIFOs */ + + nrf54l_txfifo_flush(OTG_GRSTCTL_TXFNUM_DALL); + nrf54l_rxfifo_flush(); + + /* Clear all pending Device Interrupts */ + + nrf54l_putreg(0, NRF54L_USBD_DIEPMSK); + nrf54l_putreg(0, NRF54L_USBD_DOEPMSK); + nrf54l_putreg(0, NRF54L_USBD_DIEPEMPMSK); + nrf54l_putreg(0xffffffff, NRF54L_USBD_DAINT); + nrf54l_putreg(0, NRF54L_USBD_DAINTMSK); + + /* Configure all IN endpoints */ + + for (i = 0; i < NRF54L_NENDPOINTS; i++) + { + regval = nrf54l_getreg(NRF54L_USBD_DIEPCTL(i)); + if ((regval & OTG_DIEPCTL_EPENA) != 0) + { + /* The endpoint is already enabled */ + + regval = OTG_DIEPCTL_EPENA | OTG_DIEPCTL_SNAK; + } + else + { + regval = 0; + } + + nrf54l_putreg(regval, NRF54L_USBD_DIEPCTL(i)); + nrf54l_putreg(0, NRF54L_USBD_DIEPTSIZ(i)); + nrf54l_putreg(0xff, NRF54L_USBD_DIEPINT(i)); + } + + /* Configure all OUT endpoints */ + + for (i = 0; i < NRF54L_NENDPOINTS; i++) + { + regval = nrf54l_getreg(NRF54L_USBD_DOEPCTL(i)); + if ((regval & OTG_DOEPCTL_EPENA) != 0) + { + /* The endpoint is already enabled */ + + regval = OTG_DOEPCTL_EPENA | OTG_DOEPCTL_SNAK; + } + else + { + regval = 0; + } + + nrf54l_putreg(regval, NRF54L_USBD_DOEPCTL(i)); + nrf54l_putreg(0, NRF54L_USBD_DOEPTSIZ(i)); + nrf54l_putreg(0xff, NRF54L_USBD_DOEPINT(i)); + } + + /* Disable all interrupts. */ + + nrf54l_putreg(0, NRF54L_USBD_GINTMSK); + + /* Clear any pending USB_OTG Interrupts */ + + nrf54l_putreg(0xffffffff, NRF54L_USBD_GOTGINT); + + /* Clear any pending interrupts */ + + regval = nrf54l_getreg(NRF54L_USBD_GINTSTS); + regval &= OTG_GINT_RESERVED; + nrf54l_putreg(regval | OTG_GINT_RC_W1, NRF54L_USBD_GINTSTS); + + /* Enable the interrupts in the INTMSK */ + + regval = (OTG_GINT_RXFLVL | OTG_GINT_USBSUSP | OTG_GINT_WKUP | + OTG_GINT_ENUMDNE | OTG_GINT_IEP | OTG_GINT_OEP | + OTG_GINT_USBRST); + +#ifdef CONFIG_USBDEV_ISOCHRONOUS + regval |= (OTG_GINT_IISOIXFR | OTG_GINT_IISOOXFR); +#endif + +#ifdef CONFIG_USBDEV_SOFINTERRUPT + regval |= OTG_GINT_SOF; +#endif + +#ifdef CONFIG_DEBUG_USB + regval |= OTG_GINT_MMIS; +#endif + + nrf54l_putreg(regval, NRF54L_USBD_GINTMSK); + + /* Enable the USB global interrupt by setting GINTMSK in the global OTG + * 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). + */ + + nrf54l_putreg(OTG_GAHBCFG_GINTMSK | OTG_GAHBCFG_TXFELVL, + NRF54L_USBD_GAHBCFG); + return OK; +} + +/**************************************************************************** + * Name: nrf54l_disconnect + * + * Description: + * Cancel requests without touching core registers after VBUS removal. + * The caller holds g_usbdev_lock and prevents new controller starts. + * + ****************************************************************************/ + +static void nrf54l_disconnect(struct nrf54l_usbdev_s *priv) +{ + irqstate_t flags; + int i; + + flags = enter_critical_section(); + up_disable_irq(NRF54L_IRQ_USBHS); + priv->active = false; + priv->usbdev.speed = USB_SPEED_UNKNOWN; + priv->configured = false; + priv->addressed = false; + priv->wakeup = false; + leave_critical_section(flags); + + nrf54l_usbhs_disable(); + + flags = enter_critical_section(); + if (priv->driver) + { + CLASS_DISCONNECT(priv->driver, &priv->usbdev); + } + + for (i = 0; i < NRF54L_NENDPOINTS; i++) + { + nrf54l_req_cancel(&priv->epin[i], -ESHUTDOWN); + nrf54l_req_cancel(&priv->epout[i], -ESHUTDOWN); + priv->epin[i].active = false; + priv->epout[i].active = false; + priv->epin[i].stalled = false; + priv->epout[i].stalled = false; + } + + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: nrf54l_vbus_worker + * + * Description: + * Perform PHY and controller startup delays outside interrupt context. + * + ****************************************************************************/ + +static void nrf54l_vbus_worker(void *arg) +{ + struct nrf54l_usbdev_s *priv = arg; + irqstate_t flags; + int ret; + + ret = nxmutex_lock(&g_usbdev_lock); + if (ret < 0) + { + return; + } + + if (!priv->initialized) + { + goto out; + } + + if (priv->active && (priv->vbus_changed || !priv->connected || + !nrf54l_usbhs_vbus())) + { + nrf54l_disconnect(priv); + } + + if (!priv->driver || !priv->connected || !nrf54l_usbhs_vbus()) + { + goto out; + } + + if (!priv->active) + { + priv->vbus_changed = false; + ret = nrf54l_usbhs_enable(); + if (ret < 0) + { + goto out; + } + + ret = nrf54l_hwinitialize(priv); + if (ret < 0) + { + nrf54l_usbhs_disable(); + uerr("ERROR: USB core initialization failed: %d\n", ret); + goto out; + } + + flags = enter_critical_section(); + if (priv->vbus_changed || !nrf54l_usbhs_vbus() || !priv->connected) + { + leave_critical_section(flags); + nrf54l_usbhs_disable(); + goto out; + } + + nrf54l_usbreset(priv); + if (!priv->connected) + { + leave_critical_section(flags); + nrf54l_usbhs_disable(); + goto out; + } + + priv->active = true; + modifyreg32(NRF54L_USBD_DCTL, OTG_DCTL_SDIS, 0); + up_enable_irq(NRF54L_IRQ_USBHS); + leave_critical_section(flags); + } + + if (priv->connected) + { + nrf54l_usbhs_pullup(true); + } + +out: + nxmutex_unlock(&g_usbdev_lock); +} + +/**************************************************************************** + * Name: nrf54l_vbus_interrupt + * + * Description: + * Stop core interrupt processing until the worker handles the transition. + * + ****************************************************************************/ + +static int nrf54l_vbus_interrupt(int irq, void *context, void *arg) +{ + struct nrf54l_usbdev_s *priv = arg; + + priv->vbus_changed = true; + up_disable_irq(NRF54L_IRQ_USBHS); + work_queue(LPWORK, &priv->vbuswork, nrf54l_vbus_worker, priv, 0); + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: arm_usbinitialize + * + * Description: + * Initialize USB hardware. + * + * Assumptions: + * - This function is called very early in the initialization sequence + * + ****************************************************************************/ + +void arm_usbinitialize(void) +{ + /* At present, there is only a single OTG device support. Hence it is + * pre-allocated as g_usbdev. 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. + */ + + struct nrf54l_usbdev_s *priv = &g_usbdev; + int ret; + + usbtrace(TRACE_DEVINIT, 0); + + if (priv->initialized) + { + return; + } + + /* Initialize the driver data structure */ + + nrf54l_swinitialize(priv); + + /* Attach the OTG interrupt handler */ + + ret = irq_attach(NRF54L_IRQ_USBHS, nrf54l_usbinterrupt, NULL); + if (ret < 0) + { + uerr("ERROR: irq_attach failed: %d\n", ret); + goto errout; + } + + /* Core register access waits until VBUS and the PHY clock are ready. */ + + priv->initialized = true; + ret = nrf54l_usbhs_initialize(nrf54l_vbus_interrupt, priv); + if (ret < 0) + { + priv->initialized = false; + irq_detach(NRF54L_IRQ_USBHS); + goto errout; + } + + return; + +errout: + uerr("ERROR: USB initialization failed: %d\n", ret); +} + +/**************************************************************************** + * Name: arm_usbuninitialize + ****************************************************************************/ + +void arm_usbuninitialize(void) +{ + /* At present, there is only a single OTG device support. Hence it is + * pre-allocated as g_usbdev. 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. + */ + + struct nrf54l_usbdev_s *priv = &g_usbdev; + + usbtrace(TRACE_DEVUNINIT, 0); + + if (!priv->initialized) + { + return; + } + + if (priv->driver) + { + usbtrace(TRACE_DEVERROR(NRF54L_TRACEERR_DRIVERREGISTERED), 0); + usbdev_unregister(priv->driver); + } + + up_disable_irq(NRF54L_IRQ_VREGUSB); + up_disable_irq(NRF54L_IRQ_USBHS); + work_cancel_sync(LPWORK, &priv->vbuswork); + nxmutex_lock(&g_usbdev_lock); + nrf54l_disconnect(priv); + nrf54l_usbhs_uninitialize(); + irq_detach(NRF54L_IRQ_USBHS); + priv->initialized = false; + nxmutex_unlock(&g_usbdev_lock); +} + +/**************************************************************************** + * 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 device support. Hence it is + * pre-allocated as g_usbdev. 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. + */ + + struct nrf54l_usbdev_s *priv = &g_usbdev; + int ret; + + usbtrace(TRACE_DEVREGISTER, 0); + + if (!driver || !driver->ops || !driver->ops->bind || + !driver->ops->unbind || !driver->ops->disconnect || + !driver->ops->setup) + { + usbtrace(TRACE_DEVERROR(NRF54L_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } + + if (!priv->initialized) + { + return -ENODEV; + } + + ret = nxmutex_lock(&g_usbdev_lock); + if (ret < 0) + { + return ret; + } + + if (priv->driver) + { + usbtrace(TRACE_DEVERROR(NRF54L_TRACEERR_DRIVER), 0); + nxmutex_unlock(&g_usbdev_lock); + return -EBUSY; + } + + /* First hook up the driver */ + + priv->driver = driver; + + /* Then bind the class driver */ + + ret = CLASS_BIND(driver, &priv->usbdev); + if (ret) + { + usbtrace(TRACE_DEVERROR(NRF54L_TRACEERR_BINDFAILED), (uint16_t)-ret); + priv->driver = NULL; + priv->connected = false; + } + else + { + ret = nrf54l_pullup(&priv->usbdev, true); + if (ret < 0) + { + priv->connected = false; + CLASS_UNBIND(driver, &priv->usbdev); + priv->driver = NULL; + } + } + + nxmutex_unlock(&g_usbdev_lock); + 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 device support. Hence it is + * pre-allocated as g_usbdev. 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. + */ + + struct nrf54l_usbdev_s *priv = &g_usbdev; + int ret; + + usbtrace(TRACE_DEVUNREGISTER, 0); + + ret = nxmutex_lock(&g_usbdev_lock); + if (ret < 0) + { + return ret; + } + + if (driver == NULL || driver != priv->driver) + { + usbtrace(TRACE_DEVERROR(NRF54L_TRACEERR_INVALIDPARMS), 0); + nxmutex_unlock(&g_usbdev_lock); + return -EINVAL; + } + + /* Reset the hardware and cancel all requests. All requests must be + * canceled while the class driver is still bound. + */ + + priv->connected = false; + nrf54l_disconnect(priv); + + /* Unbind the class driver */ + + CLASS_UNBIND(driver, &priv->usbdev); + + /* Unhook the driver */ + + priv->driver = NULL; + nxmutex_unlock(&g_usbdev_lock); + + return OK; +} + +#endif /* CONFIG_USBDEV */ diff --git a/arch/arm/src/nrf54l/nrf54l_usbhs.c b/arch/arm/src/nrf54l/nrf54l_usbhs.c new file mode 100644 index 00000000000..3c172158b47 --- /dev/null +++ b/arch/arm/src/nrf54l/nrf54l_usbhs.c @@ -0,0 +1,335 @@ +/**************************************************************************** + * arch/arm/src/nrf54l/nrf54l_usbhs.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include + +#include "arm_internal.h" +#include "nrf54l_usbhs.h" +#include "hardware/nrf54l_clock.h" +#include "hardware/nrf54l_usbhs.h" +#include "hardware/nrf54l_vregusb.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Nordic USBHS Type 2 PHY defaults for nRF54LM20A/B. */ + +#define NRF54L_USBHS_PHY_CONFIG_VALUE \ + ((12 << USBHS_PHY_CONFIG_PLLPTUNE_SHIFT) | \ + (3 << USBHS_PHY_CONFIG_COMPDISTUNE_SHIFT) | \ + (3 << USBHS_PHY_CONFIG_SQRXTUNE_SHIFT) | \ + (1 << USBHS_PHY_CONFIG_VDATREFTUNE_SHIFT) | \ + (3 << USBHS_PHY_CONFIG_TXHSXVTUNE_SHIFT) | \ + (3 << USBHS_PHY_CONFIG_TXFSLSTUNE_SHIFT) | \ + (3 << USBHS_PHY_CONFIG_TXVREFTUNE_SHIFT) | \ + (1 << USBHS_PHY_CONFIG_TXRISETUNE_SHIFT) | \ + (2 << USBHS_PHY_CONFIG_TXRESTUNE_SHIFT)) + +#define NRF54L_USBHS_CLOCK_TIMEOUT 5000 + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static xcpt_t g_vbus_handler; +static void *g_vbus_arg; +static volatile uint32_t g_vbus_generation; +static bool g_initialized; +static bool g_enabled; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nrf54l_usbhs_interrupt + * + * Description: + * Record every VBUS transition, including removal followed by insertion + * while the PHY is starting. The device driver handles the new bus state. + * + ****************************************************************************/ + +static int nrf54l_usbhs_interrupt(int irq, void *context, void *arg) +{ + uint32_t detected = getreg32(NRF54L_VREGUSB_EVENTS_VBUSDETECTED); + uint32_t removed = getreg32(NRF54L_VREGUSB_EVENTS_VBUSREMOVED); + + if (detected == 0 && removed == 0) + { + return OK; + } + + if (detected != 0) + { + putreg32(0, NRF54L_VREGUSB_EVENTS_VBUSDETECTED); + getreg32(NRF54L_VREGUSB_EVENTS_VBUSDETECTED); + } + + if (removed != 0) + { + putreg32(0, NRF54L_VREGUSB_EVENTS_VBUSREMOVED); + getreg32(NRF54L_VREGUSB_EVENTS_VBUSREMOVED); + } + + g_vbus_generation++; + return g_vbus_handler(irq, context, g_vbus_arg); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nrf54l_usbhs_initialize + * + * Description: + * Reset the PHY and start VBUS detection without accessing the DWC2 core. + * + ****************************************************************************/ + +int nrf54l_usbhs_initialize(xcpt_t handler, void *arg) +{ + int ret; + + if (handler == NULL) + { + return -EINVAL; + } + + if (g_initialized) + { + return -EBUSY; + } + + up_disable_irq(NRF54L_IRQ_VREGUSB); + ret = irq_attach(NRF54L_IRQ_VREGUSB, nrf54l_usbhs_interrupt, NULL); + if (ret < 0) + { + return ret; + } + + g_vbus_handler = handler; + g_vbus_arg = arg; + g_vbus_generation = 0; + g_initialized = true; + nrf54l_usbhs_disable(); + putreg32(VREGUSB_INT_VBUSDETECTED | VREGUSB_INT_VBUSREMOVED, + NRF54L_VREGUSB_INTENCLR); + putreg32(0, NRF54L_VREGUSB_EVENTS_VBUSDETECTED); + putreg32(0, NRF54L_VREGUSB_EVENTS_VBUSREMOVED); + putreg32(VREGUSB_INT_VBUSDETECTED | VREGUSB_INT_VBUSREMOVED, + NRF54L_VREGUSB_INTENSET); + putreg32(VREGUSB_TASKS_START, NRF54L_VREGUSB_TASKS_START); + up_enable_irq(NRF54L_IRQ_VREGUSB); + return OK; +} + +/**************************************************************************** + * Name: nrf54l_usbhs_vbus + * + * Description: + * Sample VBUS, including after a warm reset with the cable connected. + * + ****************************************************************************/ + +bool nrf54l_usbhs_vbus(void) +{ + return (getreg32(NRF54L_VREGUSB_STATUS) & VREGUSB_STATUS_VBUSDET) != 0; +} + +/**************************************************************************** + * Name: nrf54l_usbhs_enable + * + * Description: + * Start the 24 MHz clock and Type 2 PHY before releasing DWC2 reset. + * Keep the D+ pull-up off until the device driver configures the core. + * + ****************************************************************************/ + +int nrf54l_usbhs_enable(void) +{ + uint32_t generation; + unsigned int timeout; + irqstate_t flags; + int ret = OK; + + if (!g_initialized) + { + return -ENODEV; + } + + if (g_enabled) + { + return -EBUSY; + } + + flags = enter_critical_section(); + generation = g_vbus_generation; + if (!nrf54l_usbhs_vbus() || + getreg32(NRF54L_VREGUSB_EVENTS_VBUSREMOVED) != 0) + { + ret = -ENODEV; + } + + leave_critical_section(flags); + if (ret < 0) + { + return ret; + } + + putreg32(0, NRF54L_CLOCK_EVENTS_XO24MSTARTED); + putreg32(1, NRF54L_CLOCK_TASKS_XO24MSTART); + for (timeout = 0; timeout < NRF54L_USBHS_CLOCK_TIMEOUT; timeout++) + { + if (getreg32(NRF54L_CLOCK_EVENTS_XO24MSTARTED) != 0) + { + break; + } + + up_udelay(1); + } + + if (timeout == NRF54L_USBHS_CLOCK_TIMEOUT) + { + putreg32(1, NRF54L_CLOCK_TASKS_XO24MSTOP); + return -ETIMEDOUT; + } + + putreg32(USBHS_ENABLE_CORE, NRF54L_USBHS_ENABLE); + putreg32(NRF54L_USBHS_PHY_CONFIG_VALUE, NRF54L_USBHS_PHY_CONFIG); + putreg32(USBHS_PHY_OVERRIDEVALUES_ID, NRF54L_USBHS_PHY_OVERRIDEVALUES); + putreg32(USBHS_PHY_INPUTOVERRIDE_ID | USBHS_PHY_INPUTOVERRIDE_VBUSVALID | + USBHS_PHY_INPUTOVERRIDE_SUSPENDM, NRF54L_USBHS_PHY_INPUTOVERRIDE); + putreg32(USBHS_ENABLE_CORE | USBHS_ENABLE_PHY, NRF54L_USBHS_ENABLE); + putreg32(USBHS_PHY_INPUTOVERRIDE_ID | USBHS_PHY_INPUTOVERRIDE_VBUSVALID, + NRF54L_USBHS_PHY_INPUTOVERRIDE); + up_udelay(45); + putreg32(USBHS_TASKS_START, NRF54L_USBHS_TASKS_START); + + /* Reading a DWC2 register before this delay can hang the CPU. Interrupts + * remain enabled so VBUS changes during startup are recorded. + */ + + up_mdelay(1); + flags = enter_critical_section(); + if (generation != g_vbus_generation || !nrf54l_usbhs_vbus() || + getreg32(NRF54L_VREGUSB_EVENTS_VBUSREMOVED) != 0) + { + ret = -EAGAIN; + } + else + { + g_enabled = true; + } + + leave_critical_section(flags); + if (ret < 0) + { + nrf54l_usbhs_disable(); + } + + return ret; +} + +/**************************************************************************** + * Name: nrf54l_usbhs_pullup + * + * Description: + * Allow the PHY to observe VBUS after the core is ready to enumerate. + * + ****************************************************************************/ + +int nrf54l_usbhs_pullup(bool enable) +{ + uint32_t regval = USBHS_PHY_INPUTOVERRIDE_ID; + + if (!g_enabled || (enable && !nrf54l_usbhs_vbus())) + { + return -ENODEV; + } + + if (!enable) + { + regval |= USBHS_PHY_INPUTOVERRIDE_VBUSVALID; + } + + putreg32(regval, NRF54L_USBHS_PHY_INPUTOVERRIDE); + putreg32(USBHS_PHY_OVERRIDEVALUES_ID, NRF54L_USBHS_PHY_OVERRIDEVALUES); + return OK; +} + +/**************************************************************************** + * Name: nrf54l_usbhs_disable + * + * Description: + * Disconnect the PHY, assert reset, then release the 24 MHz clock. + * + ****************************************************************************/ + +void nrf54l_usbhs_disable(void) +{ + putreg32(USBHS_PHY_INPUTOVERRIDE_ID | USBHS_PHY_INPUTOVERRIDE_VBUSVALID | + USBHS_PHY_INPUTOVERRIDE_SUSPENDM, NRF54L_USBHS_PHY_INPUTOVERRIDE); + putreg32(USBHS_PHY_OVERRIDEVALUES_ID, NRF54L_USBHS_PHY_OVERRIDEVALUES); + putreg32(0, NRF54L_USBHS_ENABLE); + up_udelay(10); + putreg32(1, NRF54L_CLOCK_TASKS_XO24MSTOP); + g_enabled = false; +} + +/**************************************************************************** + * Name: nrf54l_usbhs_uninitialize + * + * Description: + * Stop VBUS detection and release the PHY and its clock. + * + ****************************************************************************/ + +void nrf54l_usbhs_uninitialize(void) +{ + if (!g_initialized) + { + return; + } + + up_disable_irq(NRF54L_IRQ_VREGUSB); + putreg32(VREGUSB_INT_VBUSDETECTED | VREGUSB_INT_VBUSREMOVED, + NRF54L_VREGUSB_INTENCLR); + irq_detach(NRF54L_IRQ_VREGUSB); + nrf54l_usbhs_disable(); + putreg32(VREGUSB_TASKS_STOP, NRF54L_VREGUSB_TASKS_STOP); + g_initialized = false; + g_vbus_handler = NULL; + g_vbus_arg = NULL; +} diff --git a/arch/arm/src/nrf54l/nrf54l_usbhs.h b/arch/arm/src/nrf54l/nrf54l_usbhs.h new file mode 100644 index 00000000000..5f9dd8ef6eb --- /dev/null +++ b/arch/arm/src/nrf54l/nrf54l_usbhs.h @@ -0,0 +1,78 @@ +/**************************************************************************** + * arch/arm/src/nrf54l/nrf54l_usbhs.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_NRF54L_NRF54L_USBHS_H +#define __ARCH_ARM_SRC_NRF54L_NRF54L_USBHS_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include + +#ifndef __ASSEMBLY__ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* The USB device driver serializes these operations. The VBUS callback runs + * in interrupt context and must defer controller start/stop to a worker. + * After initialize(), inspect vbus() to detect an already connected cable. + */ + +int nrf54l_usbhs_initialize(xcpt_t handler, void *arg); +void nrf54l_usbhs_uninitialize(void); +bool nrf54l_usbhs_vbus(void); + +/* Enable the PHY and release core reset with the D+ pull-up held off. + * Called from thread context; only a successful return permits DWC2 access. + * Stop core register accesses and its IRQ before calling disable(). + */ + +int nrf54l_usbhs_enable(void); +void nrf54l_usbhs_disable(void); +int nrf54l_usbhs_pullup(bool enable); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM_SRC_NRF54L_NRF54L_USBHS_H */