diff --git a/arch/arm64/src/imx9/Kconfig b/arch/arm64/src/imx9/Kconfig index 18183cdde58b5..5056abd44f980 100644 --- a/arch/arm64/src/imx9/Kconfig +++ b/arch/arm64/src/imx9/Kconfig @@ -67,6 +67,50 @@ config IMX9_FLEXIO2_PWM_CHANNEL_PINS hex "FlexIO outputs used for FLEXIO2 timers" default 0x0000000000000000 +config IMX9_USBDEV + bool + default n + select USBDEV + +config IMX9_USBDEV_USBC1 + bool "USB Device using controller 1" + default n + select IMX9_USBDEV + +config IMX9_USBDEV_USBC2 + depends on !IMX9_USBDEV_USBC1 + bool "USB Device using controller 2" + default n + select IMX9_USBDEV + +if IMX9_USBDEV + +menu "USB device controller driver (DCD) options" + +config IMX9_USBDEV_NOVBUS + bool "No USB VBUS sensing" + default n + +config IMX9_USBDEV_FRAME_INTERRUPT + bool "USB frame interrupt" + default n + ---help--- + Handle USB Start-Of-Frame events. Enable reading SOF from interrupt + handler vs. simply reading on demand. Probably a bad idea... Unless + there is some issue with sampling the SOF from hardware asynchronously. + +config IMX9_USBDEV_REGDEBUG + bool "Register level debug" + depends on DEBUG_USB_INFO + default n + ---help--- + Output detailed register-level USB device debug information. Requires + also CONFIG_DEBUG_USB_INFO. + +endmenu # USB device controller driver (DCD) options + +endif # IMX9_USBDEV + endmenu # iMX Peripheral Selection config IMX9_GPIO_IRQ diff --git a/arch/arm64/src/imx9/Make.defs b/arch/arm64/src/imx9/Make.defs index d7f26f8d91fbf..8b74632163550 100644 --- a/arch/arm64/src/imx9/Make.defs +++ b/arch/arm64/src/imx9/Make.defs @@ -38,3 +38,7 @@ endif ifeq ($(CONFIG_IMX9_FLEXIO_PWM),y) CHIP_CSRCS += imx9_flexio_pwm.c endif + +ifeq ($(CONFIG_IMX9_USBDEV),y) + CHIP_CSRCS += imx9_usbdev.c +endif diff --git a/arch/arm64/src/imx9/hardware/imx9_usbotg.h b/arch/arm64/src/imx9/hardware/imx9_usbotg.h new file mode 100644 index 0000000000000..c59119e8dd9f5 --- /dev/null +++ b/arch/arm64/src/imx9/hardware/imx9_usbotg.h @@ -0,0 +1,665 @@ +/**************************************************************************** + * arch/arm64/src/imx9/hardware/imx9_usbotg.h + * + * 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_ARM64_SRC_IMX9_HARDWARE_IMX9_USBOTG_H +#define __ARCH_ARM64_SRC_IMX9_HARDWARE_IMX9_USBOTG_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define IMX9_EHCI_NRHPORT 1 + +#define IMX9_USBOTG_ID_OFFSET 0x000 /* Identification */ +#define IMX9_USBHOST_HWGENERAL_OFFSET 0x004 /* Hardware General */ +#define IMX9_USBHOST_HWHOST_OFFSET 0x008 /* Host Hardware Parameters */ +#define IMX9_USBHOST_HWDEVICE_OFFSET 0x00c /* Device Hardware Parameters */ +#define IMX9_USBHOST_HWTXBUF_OFFSET 0x010 /* TX Buffer Hardware Parameters */ +#define IMX9_USBHOST_HWRXBUF_OFFSET 0x014 /* RX Buffer Hardware Parameters */ +#define IMX9_USBHOST_GPTIMER0LD_OFFSET 0x080 /* General Purpose Timer #0 Load */ +#define IMX9_USBHOST_GPTIMER0CTRL_OFFSET 0x084 /* General Purpose Timer #0 Controller */ +#define IMX9_USBHOST_GPTIMER1LD_OFFSET 0x088 /* General Purpose Timer #1 Load */ +#define IMX9_USBHOST_GPTIMER1CTRL_OFFSET 0x08c /* General Purpose Timer #1 Controller */ +#define IMX9_USBHOST_SBUSCFG_OFFSET 0x090 /* System Bus Config */ + +/* Device/host capability registers */ + +#define IMX9_USBOTG_CAPLENGTH_OFFSET 0x100 /* Capability register length (8-bit) */ +#define IMX9_USBHOST_HCIVERSION_OFFSET 0x102 /* Host interface version number (16-bit) */ +#define IMX9_USBHOST_HCSPARAMS_OFFSET 0x104 /* Host controller structural parameters */ +#define IMX9_USBHOST_HCCPARAMS_OFFSET 0x108 /* Host controller capability parameters */ +#define IMX9_USBDEV_DCIVERSION_OFFSET 0x120 /* Device interface version number */ +#define IMX9_USBDEV_DCCPARAMS_OFFSET 0x124 /* Device controller capability parameters */ + +/* Device/host/OTG operational registers */ + +#define IMX9_USBOTG_USBCMD_OFFSET 0x140 /* USB command (both) */ +#define IMX9_USBOTG_USBSTS_OFFSET 0x144 /* USB status (both) */ +#define IMX9_USBOTG_USBINTR_OFFSET 0x148 /* USB interrupt enable (both) */ +#define IMX9_USBOTG_FRINDEX_OFFSET 0x14c /* USB frame index (both) */ +#define IMX9_USBOTG_DEVICEADDR_OFFSET 0x154 /* USB device address (device) */ +#define IMX9_USBOTG_ASYNCLISTADDR_OFFSET 0x158 /* Next asynchronous list address (host) */ +#define IMX9_USBOTG_ENDPOINTLIST_OFFSET 0x158 /* Address of endpoint list in memory (device) */ +#define IMX9_USBOTG_BURSTSIZE_OFFSET 0x160 /* Programmable burst size (both) */ +#define IMX9_USBOTG_TXFILLTUNING_OFFSET 0x164 /* Host transmit pre-buffer packet tuning (host) */ +#define IMX9_USBOTG_ENDPTNAK_OFFSET 0x178 /* Endpoint NAK (device) */ +#define IMX9_USBOTG_ENDPTNAKEN_OFFSET 0x17c /* Endpoint NAK Enable (device) */ +#define IMX9_USBOTG_CONFIGFLAG_OFFSET 0x180 /* Configured flag register (not used in lpc313x) */ +#define IMX9_USBOTG_PORTSC1_OFFSET 0x184 /* Port status/control 1 (both) */ +#define IMX9_USBOTG_OTGSC_OFFSET 0x1a4 /* OTG status and control (otg) */ +#define IMX9_USBOTG_USBMODE_OFFSET 0x1a8 /* USB device mode (both) */ + +#define IMX9_USBDEV_USBCMD_OFFSET 0x140 /* USB command (both) */ +#define IMX9_USBDEV_USBSTS_OFFSET 0x144 /* USB status (both) */ +#define IMX9_USBDEV_USBINTR_OFFSET 0x148 /* USB interrupt enable (both) */ +#define IMX9_USBDEV_FRINDEX_OFFSET 0x14c /* USB frame index (both) */ +#define IMX9_USBDEV_DEVICEADDR_OFFSET 0x154 /* USB device address (device) */ +#define IMX9_USBDEV_ENDPOINTLIST_OFFSET 0x158 /* Address of endpoint list in memory (device) */ +#define IMX9_USBDEV_BURSTSIZE_OFFSET 0x160 /* Programmable burst size (both) */ +#define IMX9_USBDEV_ENDPTNAK_OFFSET 0x178 /* Endpoint NAK (device) */ +#define IMX9_USBDEV_ENDPTNAKEN_OFFSET 0x17c /* Endpoint NAK Enable (device) */ +#define IMX9_USBDEV_PORTSC1_OFFSET 0x184 /* Port status/control 1 (both) */ +#define IMX9_USBDEV_USBMODE_OFFSET 0x1a8 /* USB device mode (both) */ + +#define IMX9_USBHOST_USBCMD_OFFSET 0x140 /* USB command (both) */ +#define IMX9_USBHOST_USBSTS_OFFSET 0x144 /* USB status (both) */ +#define IMX9_USBHOST_USBINTR_OFFSET 0x148 /* USB interrupt enable (both) */ +#define IMX9_USBHOST_FRINDEX_OFFSET 0x14c /* USB frame index (both) */ +#define IMX9_USBHOST_PERIODICLIST_OFFSET 0x154 /* Frame list base address (host) */ +#define IMX9_USBHOST_ASYNCLISTADDR_OFFSET 0x158 /* Next asynchronous list address (host) */ +#define IMX9_USBHOST_TTCTRL_OFFSET 0x15c /* Asynchronous buffer status for embedded TT (host) */ +#define IMX9_USBHOST_BURSTSIZE_OFFSET 0x160 /* Programmable burst size (both) */ +#define IMX9_USBHOST_TXFILLTUNING_OFFSET 0x164 /* Host transmit pre-buffer packet tuning (host) */ +#define IMX9_USBHOST_PORTSC1_OFFSET 0x184 /* Port status/control 1 (both) */ +#define IMX9_USBHOST_USBMODE_OFFSET 0x1a8 /* USB device mode (both) */ + +/* Device endpoint registers */ + +#define IMX9_USBDEV_ENDPTSETUPSTAT_OFFSET 0x1ac /* Endpoint setup status */ +#define IMX9_USBDEV_ENDPTPRIME_OFFSET 0x1b0 /* Endpoint initialization */ +#define IMX9_USBDEV_ENDPTFLUSH_OFFSET 0x1b4 /* Endpoint de-initialization */ +#define IMX9_USBDEV_ENDPTSTATUS_OFFSET 0x1b8 /* Endpoint status */ +#define IMX9_USBDEV_ENDPTCOMPLETE_OFFSET 0x1bc /* Endpoint complete */ + +#define IMX9_USBDEV_ENDPTCTRL_OFFSET(n) (IMX9_USBDEV_ENDPTCTRL0_OFFSET + ((n) * 4)) +#define IMX9_USBDEV_ENDPTCTRL0_OFFSET 0x1c0 /* Endpoint control 0 */ +#define IMX9_USBDEV_ENDPTCTRL1_OFFSET 0x1c4 /* Endpoint control 1 */ +#define IMX9_USBDEV_ENDPTCTRL2_OFFSET 0x1c8 /* Endpoint control 2 */ +#define IMX9_USBDEV_ENDPTCTRL3_OFFSET 0x1cc /* Endpoint control 3 */ +#define IMX9_USBDEV_ENDPTCTRL4_OFFSET 0x1d0 /* Endpoint control 4 */ +#define IMX9_USBDEV_ENDPTCTRL5_OFFSET 0x1d4 /* Endpoint control 5 */ +#define IMX9_USBDEV_ENDPTCTRL6_OFFSET 0x1d8 /* Endpoint control 6 */ +#define IMX9_USBDEV_ENDPTCTRL7_OFFSET 0x1dc /* Endpoint control 7 */ + +/* USB Non-core memory map & register definition */ + +#define IMX9_USBNC_CTRL1_OFFSET 0x0200 /* USB OTG Control Register 1 */ +#define IMX9_USBNC_CTRL2_OFFSET 0x0204 /* USB OTG Control Register 2 */ +#define IMX9_USBNC_UTMIPHY_CFG1_OFFSET 0x0230 /* PHY Configure 1 */ + +/* USBOTG register bit definitions ******************************************/ + +/* Device/host capability registers */ + +/* CAPLENGTH */ + +#define USBOTG_CAPLENGTH_SHIFT (0) /* Bits 0-7: Offset from register base to operational regs */ +#define USBOTG_CAPLENGTH_MASK (0xff << USBOTG_CAPLENGTH_SHIFT) + +/* HCIVERSION */ + +#define USBHOST_HCIVERSION_SHIFT (0) /* Bits 0-15: BCD encoding of the EHCI revision number */ +#define USBHOST_HCIVERSION_MASK (0xffff << USBHOST_HCIVERSION_SHIFT) + +/* HCSPARAMS */ + +#define USBHOST_HCSPARAMS_NTT_SHIFT (24) /* Bits 24-27: Number of Transaction Translators */ +#define USBHOST_HCSPARAMS_NTT_MASK (0xf << USBHOST_HCSPARAMS_NTT_SHIFT) +#define USBHOST_HCSPARAMS_NPTT_SHIFT (20) /* Bits 20-23: Number of Ports per Transaction Translator */ +#define USBHOST_HCSPARAMS_NPTT_MASK (15 << USBHOST_HCSPARAMS_NPTT_SHIFT) +#define USBHOST_HCSPARAMS_PI (1 << 16) /* Bit 16: Port indicators */ +#define USBHOST_HCSPARAMS_NCC_SHIFT (12) /* Bits 12-15: Number of Companion Controller */ +#define USBHOST_HCSPARAMS_NCC_MASK (0xf << USBHOST_HCSPARAMS_NCC_SHIFT) +#define USBHOST_HCSPARAMS_NPCC_SHIFT (8) /* Bits 8-11: Number of Ports per Companion Controller */ +#define USBHOST_HCSPARAMS_NPCC_MASK (0xf << USBHOST_HCSPARAMS_NPCC_SHIFT) +#define USBHOST_HCSPARAMS_PPC (1 << 4) /* Bit 4: Port Power Control */ +#define USBHOST_HCSPARAMS_NPORTS_SHIF (0) /* Bits 0-3: Number of downstream ports */ +#define USBHOST_HCSPARAMS_NPORTS_MASK (0xf << USBHOST_HCSPARAMS_NPORTS_SHIFT) + +/* HCCPARAMS */ + +#define USBHOST_HCCPARAMS_EECP_SHIFT (8) /* Bits 8-15: EHCI Extended Capabilities Pointer */ +#define USBHOST_HCCPARAMS_EECP_MASK (0xff << USBHOST_HCCPARAMS_EECP_SHIFT) +#define USBHOST_HCCPARAMS_IST_SHIFT (4) /* Bits 4-7: Isochronous Scheduling Threshold */ +#define USBHOST_HCCPARAMS_IST_MASK (0xf << USBHOST_HCCPARAMS_IST_SHIFT) +#define USBHOST_HCCPARAMS_ASP (1 << 2) /* Bit 2: Asynchronous Schedule Park Capability */ +#define USBHOST_HCCPARAMS_PFL (1 << 1) /* Bit 1: Programmable Frame List Flag */ +#define USBHOST_HCCPARAMS_ADC (1 << 0) /* Bit 0: 64-bit Addressing Capability */ + +/* DCIVERSION */ + +#define USBDEV_DCIVERSION_SHIFT (0) /* Bits 0-15: BCD encoding of the device interface */ +#define USBDEV_DCIVERSION_MASK (0xffff << USBDEV_DCIVERSION_SHIFT) + +/* DCCPARAMS */ + +#define USBDEV_DCCPARAMS_HC (1 << 8) /* Bit 8: Host Capable */ +#define USBDEV_DCCPARAMS_DC (1 << 7) /* Bit 7: Device Capable */ +#define USBDEV_DCCPARAMS_DEN_SHIFT (0) /* Bits 0-4: DEN Device Endpoint Number */ +#define USBDEV_DCCPARAMS_DEN_MASK (0x1f << USBDEV_DCCPARAMS_DEN_SHIFT) + +/* Device/host operational registers */ + +/* USB Command register USBCMD -- Device Mode */ + +#define USBDEV_USBCMD_ITC_SHIFT (16) /* Bits 16-23: Interrupt threshold control */ +#define USBDEV_USBCMD_ITC_MASK (0xff << USBDEV_USBCMD_ITC_SHIFT) +# define USBDEV_USBCMD_ITCIMME (0 << USBDEV_USBCMD_ITC_SHIFT) /* Immediate (no threshold) */ +# define USBDEV_USBCMD_ITC1UF (1 << USBDEV_USBCMD_ITC_SHIFT) /* 1 micro frame */ +# define USBDEV_USBCMD_ITC2UF (2 << USBDEV_USBCMD_ITC_SHIFT) /* 2 micro frames */ +# define USBDEV_USBCMD_ITC4UF (4 << USBDEV_USBCMD_ITC_SHIFT) /* 4 micro frames */ +# define USBDEV_USBCMD_ITC8UF (8 << USBDEV_USBCMD_ITC_SHIFT) /* 8 micro frames */ +# define USBDEV_USBCMD_ITC16UF (16 << USBDEV_USBCMD_ITC_SHIFT) /* 16 micro frames */ +# define USBDEV_USBCMD_ITC32UF (32 << USBDEV_USBCMD_ITC_SHIFT) /* 32 micro frames */ +# define USBDEV_USBCMD_ITC64UF (64 << USBDEV_USBCMD_ITC_SHIFT) /* 64 micro frames */ + +#define USBDEV_USBCMD_ATDTW (1 << 14) /* Bit 14: Add dTD trip wire */ +#define USBDEV_USBCMD_SUTW (1 << 13) /* Bit 13: Setup trip wire */ +#define USBDEV_USBCMD_RST (1 << 1) /* Bit 1: 1 Controller reset */ +#define USBDEV_USBCMD_RS (1 << 0) /* Bit 0: 0 Run/Stop */ + +/* USB Command register USBCMD -- Host Mode */ + +#define USBHOST_USBCMD_ITC_SHIFT (16) /* Bits 16-13: Interrupt threshold control */ +#define USBHOST_USBCMD_ITC_MASK (0xff << USBHOST_USBCMD_ITC_SHIFT) +# define USBHOST_USBCMD_ITCIMMED (0 << USBHOST_USBCMD_ITC_SHIFT) /* Immediate (no threshold) */ +# define USBHOST_USBCMD_ITC1UF (1 << USBHOST_USBCMD_ITC_SHIFT) /* 1 micro frame */ +# define USBHOST_USBCMD_ITC2UF (2 << USBHOST_USBCMD_ITC_SHIFT) /* 2 micro frames */ +# define USBHOST_USBCMD_ITC4UF (4 << USBHOST_USBCMD_ITC_SHIFT) /* 4 micro frames */ +# define USBHOST_USBCMD_ITC8UF (8 << USBHOST_USBCMD_ITC_SHIFT) /* 8 micro frames */ +# define USBHOST_USBCMD_ITC16UF (16 << USBHOST_USBCMD_ITC_SHIFT) /* 16 micro frames */ +# define USBHOST_USBCMD_ITC32UF (32 << USBHOST_USBCMD_ITC_SHIFT) /* 32 micro frames */ +# define USBHOST_USBCMD_ITC64UF (64 << USBHOST_USBCMD_ITC_SHIFT) /* 64 micro frames */ + +#define USBHOST_USBCMD_FS2 (1 << 15) /* Bit 15: Bit 2 of the Frame List Size bits */ +#define USBHOST_USBCMD_ASPE (1 << 11) /* Bit 11: Asynchronous Schedule Park Mode Enable */ +#define USBHOST_USBCMD_ASP_SHIFT (8) /* Bits 8-9: Asynchronous schedule park mode */ +#define USBHOST_USBCMD_ASP_MASK (0x3 << USBHOST_USBCMD_ASP_SHIFT) +#define USBHOST_USBCMD_IAA (1 << 6) /* Bit 6: Interrupt next asynchronous schedule */ +#define USBHOST_USBCMD_ASE (1 << 5) /* Bit 5: Skips processing asynchronous schedule */ +#define USBHOST_USBCMD_PSE (1 << 4) /* Bit 4: Skips processing periodic schedule */ +#define USBHOST_USBCMD_FS1 (1 << 3) /* Bit 3: Bit 1 of the Frame List Size bits */ +#define USBHOST_USBCMD_FS0 (1 << 2) /* Bit 2: Bit 0 of the Frame List Size bits */ +#define USBHOST_USBCMD_RST (1 << 1) /* Bit 1: Controller reset */ +#define USBHOST_USBCMD_RS (1 << 0) /* Bit 0: Run/Stop */ + +/* USB Status register USBSTS -- Device Mode */ + +#define USBDEV_USBSTS_NAKI (1 << 16) /* Bit 16: NAK interrupt bit */ +#define USBDEV_USBSTS_SLI (1 << 8) /* Bit 8: DCSuspend */ +#define USBDEV_USBSTS_SRI (1 << 7) /* Bit 7: SOF received */ +#define USBDEV_USBSTS_URI (1 << 6) /* Bit 6: USB reset received */ +#define USBDEV_USBSTS_PCI (1 << 2) /* Bit 2: Port change detect */ +#define USBDEV_USBSTS_UEI (1 << 1) /* Bit 1: USB error interrupt */ +#define USBDEV_USBSTS_UI (1 << 0) /* Bit 0: USB interrupt */ + +/* USB Status register USBSTS -- Host Mode */ + +#define USBHOST_USBSTS_UPI (1 << 19) /* Bit 19: USB host periodic interrupt */ +#define USBHOST_USBSTS_UAI (1 << 18) /* Bit 18: USB host asynchronous interrupt */ +#define USBHOST_USBSTS_AS (1 << 15) /* Bit 15: Asynchronous schedule status */ +#define USBHOST_USBSTS_PS (1 << 14) /* Bit 14: Periodic schedule status */ +#define USBHOST_USBSTS_RCL (1 << 13) /* Bit 13: Reclamation */ +#define USBHOST_USBSTS_HCH (1 << 12) /* Bit 12: HCHalted */ +#define USBHOST_USBSTS_SRI (1 << 7) /* Bit 7: SOF received */ +#define USBHOST_USBSTS_AAI (1 << 5) /* Bit 5: Interrupt on async advance */ +#define USBHOST_USBSTS_FRI (1 << 3) /* Bit 3: Frame list roll-over */ +#define USBHOST_USBSTS_PCI (1 << 2) /* Bit 2: Port change detect */ +#define USBHOST_USBSTS_UEI (1 << 1) /* Bit 1: USB error interrupt */ +#define USBHOST_USBSTS_UI (1 << 0) /* Bit 0: USB interrupt */ + +/* USB interrupt register USBINTR -- Device Mode */ + +#define USBDEV_USBINTR_NAKE (1 << 16) /* Bit 16: NAK interrupt enable */ +#define USBDEV_USBINTR_SLE (1 << 8) /* Bit 8: Sleep enable */ +#define USBDEV_USBINTR_SRE (1 << 7) /* Bit 7: SOF received enable */ +#define USBDEV_USBINTR_URE (1 << 6) /* Bit 6: USB reset enable */ +#define USBDEV_USBINTR_PCE (1 << 2) /* Bit 2: Port change detect enable */ +#define USBDEV_USBINTR_UEE (1 << 1) /* Bit 1: USB error interrupt enable */ +#define USBDEV_USBINTR_UE (1 << 0) /* Bit 0: USB interrupt enable */ + +/* USB interrupt register USBINTR (address 0x19000148) -- Host Mode */ + +#define USBHOST_USBINTR_UPIE (1 << 19) /* Bit 19: USB host periodic interrupt enable */ +#define USBHOST_USBINTR_UAIE (1 << 18) /* Bit 18: USB host asynchronous interrupt enable */ +#define USBHOST_USBINTR_SRE (1 << 7) /* Bit 7: SOF timer interrupt enable */ +#define USBHOST_USBINTR_AAE (1 << 5) /* Bit 5: Interrupt on asynchronous advance enable */ +#define USBHOST_USBINTR_FRE (1 << 3) /* Bit 3: Frame list rollover enable */ +#define USBHOST_USBINTR_PCE (1 << 2) /* Bit 2: Port change detect enable */ +#define USBHOST_USBINTR_UEE (1 << 1) /* Bit 1: USB error interrupt enable */ +#define USBHOST_USBINTR_UE (1 << 0) /* Bit 0: USB interrupt enable */ + +/* Frame index register FRINDEX -- Device Mode */ + +#define USBDEV_FRINDEX_LFN_SHIFT (3) /* Bits 3-13: Frame number of last frame transmitted */ +#define USBDEV_FRINDEX_LFN_MASK (0x7ff << USBDEV_FRINDEX_LFN_SHIFT) +#define USBDEV_FRINDEX_CUFN_SHIFT (0) /* Bits 0-2: Current micro frame number */ +#define USBDEV_FRINDEX_CUFN_MASK (7 << USBDEV_FRINDEX_CUFN_SHIFT) + +/* Frame index register FRINDEX -- Host Mode */ + +#define USBHOST_FRINDEX_FLI_SHIFT (3) /* Bits 3-13: Frame list current index */ +#define USBHOST_FRINDEX_FLI_MASK(n) (0x7ff << ((n) + USBHOST_FRINDEX_FLI_SHIFT - 1) +#define USBHOST_FRINDEX_CUFN_SHIFT (0) /* Bits 0-2: Current micro frame number */ +#define USBHOST_FRINDEX_CUFN_MASK (0x7 << USBHOST_FRINDEX_CUFN_SHIFT) + +/* USB Device Address register DEVICEADDR -- Device Mode */ + +#define USBDEV_DEVICEADDR_SHIFT (25) /* Bits 25-31: USBADR USB device address */ +#define USBDEV_DEVICEADDR_MASK (0x7f << USBDEV_DEVICEADDR_SHIFT) +#define USBDEV_DEVICEADDR_USBADRA (1 << 24) /* Bit 24: Device address advance */ + +/* USB Periodic List Base register PERIODICLIST -- Host Mode */ + +#define USBHOST_PERIODICLIST_BASEADR_SHIFT (12) /* Bits 12-31: Base Address (Low) */ +#define USBHOST_PERIODICLIST_BASEADR_MASK (0xfffff << USBHOST_PERIODICLIST_PERBASE_SHIFT) + +/* USB Asynchronous List Address register ASYNCLISTADDR -- Host Mode */ + +#define USBHOST_ASYNCLISTADDR_ASYBASE_SHIFT (5) /* Bits 5-31: Link pointer (Low) LPL */ +#define USBHOST_ASYNCLISTADDR_ASYBASE_MASK (0x07ffffff << USBHOST_ASYNCLISTADDR_ASYBASE_SHIFT) + +/* USB Endpoint List Address register ENDPOINTLISTADDR -- Device Mode */ + +#define USBDEV_ENDPOINTLIST_EPBASE_SHIFT (11) /* Bits 11-31: Endpoint list pointer (low) */ +#define USBDEV_ENDPOINTLIST_EPBASE_MASK (0x1fffff << USBDEV_ENDPOINTLIST_EPBASE_SHIFT) + +/* USB burst size register BURSTSIZE -- Device/Host Mode */ + +#define USBDEV_BURSTSIZE_TXPBURST_SHIFT (8) /* Bits 8-15: Programmable TX burst length */ +#define USBDEV_BURSTSIZE_TXPBURST_MASK (0xff << USBDEV_BURSTSIZE_TXPBURST_SHIFT) +#define USBDEV_BURSTSIZE_RXPBURST_SHIFT (0) /* Bits 0-7: RXPBURST Programmable RX burst length */ +#define USBDEV_BURSTSIZE_RXPBURST_MASK (0xff << USBDEV_BURSTSIZE_RXPBURST_SHIFT) + +#define USBHOST_BURSTSIZE_TXPBURST_SHIFT (8) /* Bits 8-15: Programmable TX burst length */ +#define USBHOST_BURSTSIZE_TXPBURST_MASK (0xff << USBHOST_BURSTSIZE_TXPBURST_SHIFT) +#define USBHOST_BURSTSIZE_RXPBURST_SHIFT (0) /* Bits 0-7: RXPBURST Programmable RX burst length */ +#define USBHOST_BURSTSIZE_RXPBURST_MASK (0xff << USBHOST_BURSTSIZE_RXPBURST_SHIFT) + +/* USB Transfer buffer Fill Tuning register TXFIFOFILLTUNING -- Host Mode */ + +#define USBHOST_TXFILLTUNING_FIFOTHRES_SHIFT (16) /* Bits 16-21: Scheduler overhead */ +#define USBHOST_TXFILLTUNING_FIFOTHRES_MASK (0x3f << USBHOST_TXFILLTUNING_FIFOTHRES_SHIFT) +#define USBHOST_TXFILLTUNING_SCHEATLTH_SHIFT (8) /* Bits 8-12: Scheduler health counter */ +#define USBHOST_TXFILLTUNING_SCHEATLTH_MASK (0x1f << USBHOST_TXFILLTUNING_SCHEATLTH_SHIFT) +#define USBHOST_TXFILLTUNING_SCHOH_SHIFT (0) /* Bits 0-7: FIFO burst threshold */ +#define USBHOST_TXFILLTUNING_SCHOH_MASK (0x7f << USBHOST_TXFILLTUNING_SCHOH_SHIFT) + +/* USB endpoint NAK register ENDPTNAK -- Device Mode */ + +#define USBDEV_ENDPTNAK_EPTN_SHIFT (16) /* Bits 16-23: Tx endpoint NAK */ +#define USBDEV_ENDPTNAK_EPTN_MASK (0xff << USBDEV_ENDPTNAK_EPTN_SHIFT) +#define USBDEV_ENDPTNAK_EPRN_SHIFT (0) /* Bits 0-7: Rx endpoint NAK */ +#define USBDEV_ENDPTNAK_EPRN_MASK (0xff << USBDEV_ENDPTNAK_EPRN_SHIFT) + +/* USB Endpoint NAK Enable register ENDPTNAKEN -- Device Mode */ + +#define USBDEV_ENDPTNAK_EPTNE_SHIFT (16) /* Bits 16-23: Tx endpoint NAK enable */ +#define USBDEV_ENDPTNAK_EPTNE_MASK (0xff << USBDEV_ENDPTNAK_EPTNE_SHIFT) +#define USBDEV_ENDPTNAK_EPRNE_SHIFT (0) /* Bits 0-7: Rx endpoint NAK enable */ +#define USBDEV_ENDPTNAK_EPRNE_MASK (0xff << USBDEV_ENDPTNAK_EPRNE_SHIFT) + +/* Configure Flag register CONFIGFLAG -- Not used, returns 1 */ + +#define USBDEV_CONFIGFLAG_CF (1) + +/* Port Status and Control register PRTSC1 -- Device Mode */ + +#define USBDEV_PRTSC1_PSPD_SHIFT (26) /* Bits 26-27: Port speed */ +#define USBDEV_PRTSC1_PSPD_MASK (0x3 << USBDEV_PRTSC1_PSPD_SHIFT) +# define USBDEV_PRTSC1_PSPD_FS (0 << USBDEV_PRTSC1_PSPD_SHIFT) /* Full-speed */ +# define USBDEV_PRTSC1_PSPD_LS (1 << USBDEV_PRTSC1_PSPD_SHIFT) /* Low-speed */ +# define USBDEV_PRTSC1_PSPD_HS (2 << USBDEV_PRTSC1_PSPD_SHIFT) /* High-speed */ + +#define USBDEV_PRTSC1_PFSC (1 << 24) /* Bit 24: Port force full speed connect */ +#define USBDEV_PRTSC1_PHCD (1 << 23) /* Bit 23: PHY low power suspend - clock disable (PLPSCD) */ +#define USBDEV_PRTSC1_PTC_SHIFT (16) /* Bits 16-19: 19: Port test control */ +#define USBDEV_PRTSC1_PTC_MASK (0xf << USBDEV_PRTSC1_PTC_SHIFT) +# define USBDEV_PRTSC1_PTC_DISABLE (0 << USBDEV_PRTSC1_PTC_SHIFT) /* TEST_MODE_DISABLE */ +# define USBDEV_PRTSC1_PTC_JSTATE (1 << USBDEV_PRTSC1_PTC_SHIFT) /* J_STATE */ +# define USBDEV_PRTSC1_PTC_KSTATE (2 << USBDEV_PRTSC1_PTC_SHIFT) /* K_STATE */ +# define USBDEV_PRTSC1_PTC_SE0 (3 << USBDEV_PRTSC1_PTC_SHIFT) /* SE0 (host)/NAK (device) */ +# define USBDEV_PRTSC1_PTC_PACKET (4 << USBDEV_PRTSC1_PTC_SHIFT) /* Packet */ +# define USBDEV_PRTSC1_PTC_HS (5 << USBDEV_PRTSC1_PTC_SHIFT) /* FORCE_ENABLE_HS */ +# define USBDEV_PRTSC1_PTC_FS (6 << USBDEV_PRTSC1_PTC_SHIFT) /* FORCE_ENABLE_FS */ + +#define USBDEV_PRTSC1_PIC_SHIFT (14) /* Bits 14-15: Port indicator control */ +#define USBDEV_PRTSC1_PIC_MASK (0x3 << USBDEV_PRTSC1_PIC_SHIFT) +# define USBDEV_PRTSC1_PIC_OFF (0 << USBDEV_PRTSC1_PIC_SHIFT) /* 00 Port indicators are off */ +# define USBDEV_PRTSC1_PIC_AMBER (1 << USBDEV_PRTSC1_PIC_SHIFT) /* 01 amber */ +# define USBDEV_PRTSC1_PIC_GREEN (2 << USBDEV_PRTSC1_PIC_SHIFT) /* 10 green */ + +#define USBDEV_PRTSC1_HSP (1 << 9) /* Bit 9: High-speed status */ +#define USBDEV_PRTSC1_PR (1 << 8) /* Bit 8: Port reset */ +#define USBDEV_PRTSC1_SUSP (1 << 7) /* Bit 7: Suspend */ +#define USBDEV_PRTSC1_FPR (1 << 6) /* Bit 6: Force port resume */ +#define USBDEV_PRTSC1_PEC (1 << 3) /* Bit 3: Port enable/disable change */ +#define USBDEV_PRTSC1_PE (1 << 2) /* Bit 2: Port enable */ +#define USBDEV_PRTSC1_CCS (1 << 0) /* Bit 0: Current connect status */ + +/* Port Status and Control register PRTSC1 -- Host Mode */ + +#define USBHOST_PRTSC1_PSPD_SHIFT (26) /* Bits 26-27: Port speed */ +#define USBHOST_PRTSC1_PSPD_MASK (0x3 << USBHOST_PRTSC1_PSPD_SHIFT) +# define USBHOST_PRTSC1_PSPD_FS (0 << USBHOST_PRTSC1_PSPD_SHIFT) /* Full-speed */ +# define USBHOST_PRTSC1_PSPD_LS (1 << USBHOST_PRTSC1_PSPD_SHIFT) /* Low-speed */ +# define USBHOST_PRTSC1_PSPD_HS (2 << USBHOST_PRTSC1_PSPD_SHIFT) /* High-speed */ + +#define USBHOST_PRTSC1_PFSC (1 << 24) /* Bit 24: Port force full speed connect */ +#define USBHOST_PRTSC1_PHCD (1 << 23) /* Bit 23: PHY low power suspend - clock disable (PLPSCD) */ +#define USBHOST_PRTSC1_WKOC (1 << 22) /* Bit 22: Wake on over-current enable (WKOC_E) */ +#define USBHOST_PRTSC1_WKDC (1 << 21) /* Bit 21: Wake on disconnect enable (WKDSCNNT_E) */ +#define USBHOST_PRTSC1_WKCN (1 << 20) /* Bit 20: Wake on connect enable (WKCNNT_E) */ +#define USBHOST_PRTSC1_PTC_SHIFT (16) /* Bits 16-19: Port test control */ +#define USBHOST_PRTSC1_PTC_MASK (0xf << USBHOST_PRTSC1_PTC_SHIFT) +# define USBHOST_PRTSC1_PTC_DISABLE (0 << USBHOST_PRTSC1_PTC_SHIFT) /* 0000 TEST_MODE_DISABLE */ +# define USBHOST_PRTSC1_PTC_JSTATE (1 << USBHOST_PRTSC1_PTC_SHIFT) /* 0001 J_STATE */ +# define USBHOST_PRTSC1_PTC_KSTATE (2 << USBHOST_PRTSC1_PTC_SHIFT) /* 0010 K_STATE */ +# define USBHOST_PRTSC1_PTC_SE0 (3 << USBHOST_PRTSC1_PTC_SHIFT) /* 0011 SE0 (host)/NAK (device) */ +# define USBHOST_PRTSC1_PTC_PACKET (4 << USBHOST_PRTSC1_PTC_SHIFT) /* 0100 Packet */ +# define USBHOST_PRTSC1_PTC_HS (5 << USBHOST_PRTSC1_PTC_SHIFT) /* 0101 FORCE_ENABLE_HS */ +# define USBHOST_PRTSC1_PTC_FS (6 << USBHOST_PRTSC1_PTC_SHIFT) /* 0110 FORCE_ENABLE_FS */ +# define USBHOST_PRTSC1_PTC_LS (7 << USBHOST_PRTSC1_PTC_SHIFT) /* 0111 FORCE_ENABLE_LS */ + +#define USBHOST_PRTSC1_PIC_SHIFT (14) /* Bits 14-15: Port indicator control */ +#define USBHOST_PRTSC1_PIC_MASK (0x3 << USBHOST_PRTSC1_PIC_SHIFT) +# define USBHOST_PRTSC1_PIC_OFF (0 << USBHOST_PRTSC1_PIC_SHIFT) /* 00 Port indicators are off */ +# define USBHOST_PRTSC1_PIC_AMBER (1 << USBHOST_PRTSC1_PIC_SHIFT) /* 01 Amber */ +# define USBHOST_PRTSC1_PIC_GREEN (2 << USBHOST_PRTSC1_PIC_SHIFT) /* 10 Green */ + +#define USBHOST_PRTSC1_PP (1 << 12) /* Bit 12: Port power control */ +#define USBHOST_PRTSC1_LS_SHIFT (10) /* Bits 10-11: Line status */ +#define USBHOST_PRTSC1_LS_MASK (0x3 << USBHOST_PRTSC1_LS_SHIFT) +# define USBHOST_PRTSC1_LS_SE0 (0 << USBHOST_PRTSC1_LS_SHIFT) /* SE0 (USB_DP and USB_DM LOW) */ +# define USBHOST_PRTSC1_LS_JSTATE (2 << USBHOST_PRTSC1_LS_SHIFT) /* J-state (USB_DP HIGH and USB_DM LOW) */ +# define USBHOST_PRTSC1_LS_KSTATE (1 << USBHOST_PRTSC1_LS_SHIFT) /* K-state (USB_DP LOW and USB_DM HIGH) */ + +#define USBHOST_PRTSC1_HSP (1 << 9) /* Bit 9: High-speed status */ +#define USBHOST_PRTSC1_PR (1 << 8) /* Bit 8: Port reset */ +#define USBHOST_PRTSC1_SUSP (1 << 7) /* Bit 7: Suspend */ +#define USBHOST_PRTSC1_FPR (1 << 6) /* Bit 6: Force port resume */ +#define USBHOST_PRTSC1_OCC (1 << 5) /* Bit 5: Over-current change */ +#define USBHOST_PRTSC1_OCA (1 << 4) /* Bit 4: Over-current active */ +#define USBHOST_PRTSC1_PEC (1 << 3) /* Bit 3: Port disable/enable change */ +#define USBHOST_PRTSC1_PE (1 << 2) /* Bit 2: Port enable */ +#define USBHOST_PRTSC1_CSC (1 << 1) /* Bit 1: Connect status change */ +#define USBHOST_PRTSC1_CCS (1 << 0) /* Bit 0: Current connect status */ + +/* OTG Status and Control register (OTGSC) */ + +/* OTG interrupt enable */ + +#define USBOTG_OTGSC_DPIE (1 << 30) /* Bit 30: Data pulse interrupt enable */ +#define USBOTG_OTGSC_1MSE (1 << 29) /* Bit 29: 1 millisecond timer interrupt enable */ +#define USBOTG_OTGSC_BSEIE (1 << 28) /* Bit 28: B-session end interrupt enable */ +#define USBOTG_OTGSC_BSVIE (1 << 27) /* Bit 27: B-session valid interrupt enable */ +#define USBOTG_OTGSC_ASVIE (1 << 26) /* Bit 26: A-session valid interrupt enable */ +#define USBOTG_OTGSC_AVVIE (1 << 25) /* Bit 25: A-VBUS valid interrupt enable */ +#define USBOTG_OTGSC_IDIE (1 << 24) /* Bit 24: USB ID interrupt enable */ + +/* OTG interrupt status */ + +#define USBOTG_OTGSC_DPIS (1 << 22) /* Bit 22: Data pulse interrupt status */ +#define USBOTG_OTGSC_1MSS (1 << 21) /* Bit 21: 1 millisecond timer interrupt status */ +#define USBOTG_OTGSC_BSEIS (1 << 20) /* Bit 20: B-Session end interrupt status */ +#define USBOTG_OTGSC_BSVIS (1 << 19) /* Bit 19: B-Session valid interrupt status */ +#define USBOTG_OTGSC_ASVIS (1 << 18) /* Bit 18: A-Session valid interrupt status */ +#define USBOTG_OTGSC_AVVIS (1 << 17) /* Bit 17: A-VBUS valid interrupt status */ +#define USBOTG_OTGSC_IDIS (1 << 16) /* Bit 16: USB ID interrupt status */ + +/* OTG status inputs */ + +#define USBOTG_OTGSC_DPS (1 << 14) /* Bit 14: Data bus pulsing status */ +#define USBOTG_OTGSC_1MST (1 << 13) /* Bit 13: 1 millisecond timer toggle */ +#define USBOTG_OTGSC_BSE (1 << 12) /* Bit 12: B-session end */ +#define USBOTG_OTGSC_BSV (1 << 11) /* Bit 11: B-session valid */ +#define USBOTG_OTGSC_ASV (1 << 10) /* Bit 10: A-session valid */ +#define USBOTG_OTGSC_AVV (1 << 9) /* Bit 9: A-VBUS valid */ +#define USBOTG_OTGSC_ID (1 << 8) /* Bit 8: USB ID */ + +/* OTG controls */ + +#define USBOTG_OTGSC_IDPU (1 << 5) /* Bit 5: ID pull-up */ +#define USBOTG_OTGSC_DP (1 << 4) /* Bit 4: Data pulsing */ +#define USBOTG_OTGSC_OT (1 << 3) /* Bit 3: OTG termination */ +#define USBOTG_OTGSC_VC (1 << 1) /* Bit 1: VBUS_Charge */ +#define USBOTG_OTGSC_VD (1 << 0) /* Bit 0: VBUS_Discharge */ + +/* USB Mode register USBMODE -- Device Mode */ + +#define USBDEV_USBMODE_SDIS (1 << 4) /* Bit 4: Stream disable mode */ +#define USBDEV_USBMODE_SLOM (1 << 3) /* Bit 3: Setup Lockout mode */ +#define USBDEV_USBMODE_ES (1 << 2) /* Bit 2: Endian select */ +#define USBDEV_USBMODE_CM_SHIFT (0) /* Bits 0-1: Controller mode */ +#define USBDEV_USBMODE_CM_MASK (0x3 << USBDEV_USBMODE_CM_SHIFT) +# define USBDEV_USBMODE_CM_IDLE (0 << USBDEV_USBMODE_CM_SHIFT) /* Idle */ +# define USBDEV_USBMODE_CM_DEVICE (2 << USBDEV_USBMODE_CM_SHIFT) /* Device controller */ +# define USBDEV_USBMODE_CM_HOST (3 << USBDEV_USBMODE_CM_SHIFT) /* Host controller */ + +/* USB Mode register USBMODE -- Host Mode */ + +#define USBHOST_USBMODE_SDIS (1 << 4) /* Bit 4: Stream disable mode */ +#define USBHOST_USBMODE_ES (1 << 2) /* Bit 2: Endian select */ +#define USBHOST_USBMODE_CM_SHIFT (0) /* Bits 0-1: Controller mode */ +#define USBHOST_USBMODE_CM_MASK (0x3 << USBHOST_USBMODE_CM_SHIFT) +# define USBHOST_USBMODE_CM_IDLE (0 << USBHOST_USBMODE_CM_SHIFT) /* Idle */ +# define USBHOST_USBMODE_CM_DEVICE (2 << USBHOST_USBMODE_CM_SHIFT) /* Device controller */ +# define USBHOST_USBMODE_CM_HOST (3 << USBHOST_USBMODE_CM_SHIFT) /* Host controller */ + +/* Device endpoint registers */ + +/* USB Endpoint Setup Status register ENDPTSETUPSTAT */ + +#define USBDEV_ENDPTSETSTAT_STAT15 (1 << 15) /* Bit 15: Setup EP status for logical EP 15 */ +#define USBDEV_ENDPTSETSTAT_STAT14 (1 << 14) /* Bit 14: Setup EP status for logical EP 14 */ +#define USBDEV_ENDPTSETSTAT_STAT13 (1 << 13) /* Bit 13: Setup EP status for logical EP 13 */ +#define USBDEV_ENDPTSETSTAT_STAT12 (1 << 12) /* Bit 12: Setup EP status for logical EP 12 */ +#define USBDEV_ENDPTSETSTAT_STAT11 (1 << 11) /* Bit 11: Setup EP status for logical EP 11 */ +#define USBDEV_ENDPTSETSTAT_STAT10 (1 << 10) /* Bit 10: Setup EP status for logical EP 10 */ +#define USBDEV_ENDPTSETSTAT_STAT9 (1 << 9) /* Bit 9: Setup EP status for logical EP 9 */ +#define USBDEV_ENDPTSETSTAT_STAT8 (1 << 8) /* Bit 8: Setup EP status for logical EP 8 */ +#define USBDEV_ENDPTSETSTAT_STAT7 (1 << 7) /* Bit 7: Setup EP status for logical EP 7 */ +#define USBDEV_ENDPTSETSTAT_STAT6 (1 << 6) /* Bit 6: Setup EP status for logical EP 6 */ +#define USBDEV_ENDPTSETSTAT_STAT5 (1 << 5) /* Bit 5: Setup EP status for logical EP 5 */ +#define USBDEV_ENDPTSETSTAT_STAT4 (1 << 4) /* Bit 4: Setup EP status for logical EP 4 */ +#define USBDEV_ENDPTSETSTAT_STAT3 (1 << 3) /* Bit 3: Setup EP status for logical EP 3 */ +#define USBDEV_ENDPTSETSTAT_STAT2 (1 << 2) /* Bit 2: Setup EP status for logical EP 2 */ +#define USBDEV_ENDPTSETSTAT_STAT1 (1 << 1) /* Bit 1: Setup EP status for logical EP 1 */ +#define USBDEV_ENDPTSETSTAT_STAT0 (1 << 0) /* Bit 0: Setup EP status for logical EP 0 */ + +/* USB Endpoint Prime register ENDPTPRIME */ + +#define USBDEV_ENDPTPRIM_PETB7 (1 << 23) /* Bit 23: Prime EP xmt buffer for physical IN EP 7 */ +#define USBDEV_ENDPTPRIM_PETB6 (1 << 22) /* Bit 22: Prime EP xmt buffer for physical IN EP 6 */ +#define USBDEV_ENDPTPRIM_PETB5 (1 << 21) /* Bit 21: Prime EP xmt buffer for physical IN EP 5 */ +#define USBDEV_ENDPTPRIM_PETB4 (1 << 20) /* Bit 20: Prime EP xmt buffer for physical IN EP 4 */ +#define USBDEV_ENDPTPRIM_PETB3 (1 << 19) /* Bit 19: Prime EP xmt buffer for physical IN EP 3 */ +#define USBDEV_ENDPTPRIM_PETB2 (1 << 18) /* Bit 18: Prime EP xmt buffer for physical IN EP 2 */ +#define USBDEV_ENDPTPRIM_PETB1 (1 << 17) /* Bit 17: Prime EP xmt buffer for physical IN EP 1 */ +#define USBDEV_ENDPTPRIM_PETB0 (1 << 16) /* Bit 16: Prime EP xmt buffer for physical IN EP 0 */ +#define USBDEV_ENDPTPRIM_PERB7 (1 << 7) /* Bit 7: Prime EP recv buffer for physical OUT EP 7 */ +#define USBDEV_ENDPTPRIM_PERB6 (1 << 6) /* Bit 6: Prime EP recv buffer for physical OUT EP 6 */ +#define USBDEV_ENDPTPRIM_PERB5 (1 << 5) /* Bit 5: Prime EP recv buffer for physical OUT EP 5 */ +#define USBDEV_ENDPTPRIM_PERB4 (1 << 4) /* Bit 4: Prime EP recv buffer for physical OUT EP 4 */ +#define USBDEV_ENDPTPRIM_PERB3 (1 << 3) /* Bit 3: Prime EP recv buffer for physical OUT EP 3 */ +#define USBDEV_ENDPTPRIM_PERB2 (1 << 2) /* Bit 2: Prime EP recv buffer for physical OUT EP 2 */ +#define USBDEV_ENDPTPRIM_PERB1 (1 << 1) /* Bit 1: Prime EP recv buffer for physical OUT EP 1 */ +#define USBDEV_ENDPTPRIM_PERB0 (1 << 0) /* Bit 0: Prime EP recv buffer for physical OUT EP 0 */ + +/* USB Endpoint Flush register ENDPTFLUSH */ + +#define USBDEV_ENDPTFLUSH_FETB7 (1 << 23) /* Bit 23: Flush EP xmt buffer for physical IN EP 7 */ +#define USBDEV_ENDPTFLUSH_FETB6 (1 << 22) /* Bit 22: Flush EP xmt buffer for physical IN EP 6 */ +#define USBDEV_ENDPTFLUSH_FETB5 (1 << 21) /* Bit 21: Flush EP xmt buffer for physical IN EP 5 */ +#define USBDEV_ENDPTFLUSH_FETB4 (1 << 20) /* Bit 20: Flush EP xmt buffer for physical IN EP 4 */ +#define USBDEV_ENDPTFLUSH_FETB3 (1 << 19) /* Bit 19: Flush EP xmt buffer for physical IN EP 3 */ +#define USBDEV_ENDPTFLUSH_FETB2 (1 << 18) /* Bit 18: Flush EP xmt buffer for physical IN EP 2 */ +#define USBDEV_ENDPTFLUSH_FETB1 (1 << 17) /* Bit 17: Flush EP xmt buffer for physical IN EP 1 */ +#define USBDEV_ENDPTFLUSH_FETB0 (1 << 16) /* Bit 16: Flush EP xmt buffer for physical IN EP 0 */ +#define USBDEV_ENDPTFLUSH_FERB7 (1 << 7) /* Bit 7: Flush EP recv buffer for physical OUT EP 7 */ +#define USBDEV_ENDPTFLUSH_FERB6 (1 << 6) /* Bit 6: Flush EP recv buffer for physical OUT EP 6 */ +#define USBDEV_ENDPTFLUSH_FERB5 (1 << 5) /* Bit 5: Flush EP recv buffer for physical OUT EP 5 */ +#define USBDEV_ENDPTFLUSH_FERB4 (1 << 4) /* Bit 4: Flush EP recv buffer for physical OUT EP 4 */ +#define USBDEV_ENDPTFLUSH_FERB3 (1 << 3) /* Bit 3: Flush EP recv buffer for physical OUT EP 3 */ +#define USBDEV_ENDPTFLUSH_FERB2 (1 << 2) /* Bit 2: Flush EP recv buffer for physical OUT EP 2 */ +#define USBDEV_ENDPTFLUSH_FERB1 (1 << 1) /* Bit 1: Flush EP recv buffer for physical OUT EP 1 */ +#define USBDEV_ENDPTFLUSH_FERB0 (1 << 0) /* Bit 0: Flush EP recv buffer for physical OUT EP 0 */ + +/* USB Endpoint Status register ENDPTSTATUS */ + +#define USBDEV_ENDPTSTATUS_ETBR7 (1 << 23) /* Bit 23: EP xmt buffer ready for physical IN EP 7 */ +#define USBDEV_ENDPTSTATUS_ETBR6 (1 << 22) /* Bit 22: EP xmt buffer ready for physical IN EP 6 */ +#define USBDEV_ENDPTSTATUS_ETBR5 (1 << 21) /* Bit 21: EP xmt buffer ready for physical IN EP 5 */ +#define USBDEV_ENDPTSTATUS_ETBR4 (1 << 20) /* Bit 20: EP xmt buffer ready for physical IN EP 4 */ +#define USBDEV_ENDPTSTATUS_ETBR3 (1 << 19) /* Bit 19: EP xmt buffer ready for physical IN EP 3 */ +#define USBDEV_ENDPTSTATUS_ETBR2 (1 << 18) /* Bit 18: EP xmt buffer ready for physical IN EP 2 */ +#define USBDEV_ENDPTSTATUS_ETBR1 (1 << 17) /* Bit 17: EP xmt buffer ready for physical IN EP 1 */ +#define USBDEV_ENDPTSTATUS_ETBR0 (1 << 16) /* Bit 16: EP xmt buffer ready for physical IN EP 0 */ +#define USBDEV_ENDPTSTATUS_ERBR7 (1 << 7) /* Bit 7: EP recv buffer ready for physical OUT EP 7 */ +#define USBDEV_ENDPTSTATUS_ERBR6 (1 << 6) /* Bit 6: EP recv buffer ready for physical OUT EP 6 */ +#define USBDEV_ENDPTSTATUS_ERBR5 (1 << 5) /* Bit 5: EP recv buffer ready for physical OUT EP 5 */ +#define USBDEV_ENDPTSTATUS_ERBR4 (1 << 4) /* Bit 4: EP recv buffer ready for physical OUT EP 4 */ +#define USBDEV_ENDPTSTATUS_ERBR3 (1 << 3) /* Bit 3: EP recv buffer ready for physical OUT EP 3 */ +#define USBDEV_ENDPTSTATUS_ERBR2 (1 << 2) /* Bit 2: EP recv buffer ready for physical OUT EP 2 */ +#define USBDEV_ENDPTSTATUS_ERBR1 (1 << 1) /* Bit 1: EP recv buffer ready for physical OUT EP 1 */ +#define USBDEV_ENDPTSTATUS_ERBR0 (1 << 0) /* Bit 0: EP recv buffer ready for physical OUT EP 0 */ + +/* USB Endpoint Complete register ENDPTCOMPLETE */ + +#define USBDEV_ENDPTCOMPLETE_ETCE7 (1 << 23) /* Bit 23: EP xmt complete event for physical IN EP 7 */ +#define USBDEV_ENDPTCOMPLETE_ETCE6 (1 << 22) /* Bit 22: EP xmt complete event for physical IN EP 6 */ +#define USBDEV_ENDPTCOMPLETE_ETCE5 (1 << 21) /* Bit 21: EP xmt complete event for physical IN EP 5 */ +#define USBDEV_ENDPTCOMPLETE_ETCE4 (1 << 20) /* Bit 20: EP xmt complete event for physical IN EP 4 */ +#define USBDEV_ENDPTCOMPLETE_ETCE3 (1 << 19) /* Bit 19: EP xmt complete event for physical IN EP 3 */ +#define USBDEV_ENDPTCOMPLETE_ETCE2 (1 << 18) /* Bit 18: EP xmt complete event for physical IN EP 2 */ +#define USBDEV_ENDPTCOMPLETE_ETCE1 (1 << 17) /* Bit 17: EP xmt complete event for physical IN EP 1 */ +#define USBDEV_ENDPTCOMPLETE_ETCE0 (1 << 16) /* Bit 16: EP xmt complete event for physical IN EP 0 */ +#define USBDEV_ENDPTCOMPLETE_ERCE7 (1 << 7) /* Bit 7: EP recv complete event for physical OUT EP 7 */ +#define USBDEV_ENDPTCOMPLETE_ERCE6 (1 << 6) /* Bit 6: EP recv complete event for physical OUT EP 6 */ +#define USBDEV_ENDPTCOMPLETE_ERCE5 (1 << 5) /* Bit 5: EP recv complete event for physical OUT EP 5 */ +#define USBDEV_ENDPTCOMPLETE_ERCE4 (1 << 4) /* Bit 4: EP recv complete event for physical OUT EP 4 */ +#define USBDEV_ENDPTCOMPLETE_ERCE3 (1 << 3) /* Bit 3: EP recv complete event for physical OUT EP 3 */ +#define USBDEV_ENDPTCOMPLETE_ERCE2 (1 << 2) /* Bit 2: EP recv complete event for physical OUT EP 2 */ +#define USBDEV_ENDPTCOMPLETE_ERCE1 (1 << 1) /* Bit 1: EP recv complete event for physical OUT EP 1 */ +#define USBDEV_ENDPTCOMPLETE_ERCE0 (1 << 0) /* Bit 0: EP recv complete event for physical OUT EP 0 */ + +/* USB Endpoint 0 Control register ENDPTCTRL0 */ + +#define USBDEV_ENDPTCTRL0_TXE (1 << 23) /* Bit 23: Tx endpoint enable */ +#define USBDEV_ENDPTCTRL0_TXT_SHIFT (18) /* Bits 18-19: Tx endpoint type */ +#define USBDEV_ENDPTCTRL0_TXT_MASK (0x3 << USBDEV_ENDPTCTRL0_TXT_SHIFT) +# define USBDEV_ENDPTCTRL0_TXT_CTRL (0 << USBDEV_ENDPTCTRL0_TXT_SHIFT) /* Control */ + +#define USBDEV_ENDPTCTRL0_TXS (1 << 16) /* Bit 16: Tx endpoint stall */ +#define USBDEV_ENDPTCTRL0_RXE (1 << 7) /* Bit 7: Rx endpoint enable */ +#define USBDEV_ENDPTCTRL0_RXT_SHIFT (2) /* Bits 2-3: Endpoint type */ +#define USBDEV_ENDPTCTR0L_RXT_MASK (0x3 << USBDEV_ENDPTCTRL0_RXT_SHIFT) +# define USBDEV_ENDPTCTRL0_RXT_CTRL (0 << USBDEV_ENDPTCTRL0_RXT_SHIFT) /* Control */ + +#define USBDEV_ENDPTCTRL0_RXS (1 << 0) /* Bit 0: Rx endpoint stall */ + +/* USB Endpoint 1-7 control registers ENDPTCTRL1-ENDPPTCTRL7 */ + +#define USBDEV_ENDPTCTRL_TXE (1 << 23) /* Bit 23: Tx endpoint enable */ +#define USBDEV_ENDPTCTRL_TXR (1 << 22) /* Bit 22: Tx data toggle reset */ +#define USBDEV_ENDPTCTRL_TXI (1 << 21) /* Bit 21: Tx data toggle inhibit */ +#define USBDEV_ENDPTCTRL_TXT_SHIFT (18) /* Bits 18-19: Tx endpoint type */ +#define USBDEV_ENDPTCTRL_TXT_MASK (0x3 << USBDEV_ENDPTCTRL_TXT_SHIFT) +# define USBDEV_ENDPTCTRL_TXT_CTRL (0 << USBDEV_ENDPTCTRL_TXT_SHIFT) /* Control */ +# define USBDEV_ENDPTCTRL_TXT_ISOC (1 << USBDEV_ENDPTCTRL_TXT_SHIFT) /* Isochronous */ +# define USBDEV_ENDPTCTRL_TXT_BULK (2 << USBDEV_ENDPTCTRL_TXT_SHIFT) /* Bulk */ +# define USBDEV_ENDPTCTRL_TXT_INTR (3 << USBDEV_ENDPTCTRL_TXT_SHIFT) /* Interrupt */ + +#define USBDEV_ENDPTCTRL_TXS (1 << 16) /* Bit 16: Tx endpoint stall */ +#define USBDEV_ENDPTCTRL_RXE (1 << 7) /* Bit 7: Rx endpoint enable */ +#define USBDEV_ENDPTCTRL_RXR (1 << 6) /* Bit 6: Rx data toggle reset */ +#define USBDEV_ENDPTCTRL_RXI (1 << 5) /* Bit 5: Rx data toggle inhibit */ +#define USBDEV_ENDPTCTRL_RXT_SHIFT (2) /* Bits 2-3: Endpoint type */ +#define USBDEV_ENDPTCTRL_RXT_MASK (0x3 << USBDEV_ENDPTCTRL_RXT_SHIFT) +# define USBDEV_ENDPTCTRL_RXT_CTRL (0 << USBDEV_ENDPTCTRL_RXT_SHIFT) /* Control */ +# define USBDEV_ENDPTCTRL_RXT_ISOC (1 << USBDEV_ENDPTCTRL_RXT_SHIFT) /* Isochronous */ +# define USBDEV_ENDPTCTRL_RXT_BULK (2 << USBDEV_ENDPTCTRL_RXT_SHIFT) /* Bulk */ +# define USBDEV_ENDPTCTRL_RXT_INTR (3 << USBDEV_ENDPTCTRL_RXT_SHIFT) /* Interrupt */ + +#define USBDEV_ENDPTCTRL_RXS (1 << 0) /* Bit 0: Rx endpoint stall */ + +/* Device non-core registers */ + +/* USB OTG Control register 1 */ + +#define USBNC_CTRL1_WIR (1 << 31) /* Bit 31: Wake-up Interrupt Request */ +#define USBNC_CTRL1_WKUP_DPDM_EN (1 << 29) /* Bit 29: Wake-up on DP/DM change enable */ +#define USBNC_CTRL1_WKUP_VBUS_EN (1 << 17) /* Bit 17: Wake-up on VBUS change enable */ +#define USBNC_CTRL1_WKUP_ID_EN (1 << 16) /* Bit 16: Wake-up on ID change enable */ +#define USBNC_CTRL1_WKUP_SW (1 << 15) /* Bit 15: Software Wake-up */ +#define USBNC_CTRL1_WKUP_SW_EN (1 << 14) /* Bit 14: Software Wake-up Enable */ +#define USBNC_CTRL1_WIE (1 << 10) /* Bit 10: Wake-up Interrupt Enable */ +#define USBNC_CTRL1_PWR_POL (1 << 9) /* Bit 9: Power Polarity */ +#define USBNC_CTRL1_OVER_CUR_POL (1 << 8) /* Bit 8: Polarity of Overcurrent */ +#define USBNC_CTRL1_OVER_CUR_DIS (1 << 7) /* Bit 7: Disable Overcurrent Detection */ + +/* USB OTG Control register 2 */ + +#define USBNC_CTRL2_UTMI_CLK_VLD (1 << 31) /* Bit 31: UTMI clock to the USB PHY is valid */ +#define USBNC_CTRL2_SHORT_PKT_EN (1 << 23) /* Bit 23: Short Packet Interrupt enable */ +#define USBNC_CTRL2_LOWSPEED_EN (1 << 3) /* Bit 3: Low speed enable */ +#define USBNC_CTRL2_AUTURESUME_EN (1 << 2) /* Bit 2: Auto Resume Enable */ +#define USBNC_CTRL2_VBUS_SOURCE_SEL_MASK (0x3) /* Bits 0-1: VBUS source select at VBUS wakeup event */ + +/* PHY Configure 1 -- internal control register bits of the PHY clock */ + +#define USBNC_UTMIPHY_CFG1_TXPREEMPPULSETUNE0 (1 << 30) /* Bit 30: HS Transmitter Pre-Emphasis Duration Control */ +#define USBNC_UTMIPHY_CFG1_TXPREEMPAMPTUNE0_SHIFT (28) /* Bits 28-29: HS Transmitter Pre-Emphasis Current Control */ +#define USBNC_UTMIPHY_CFG1_TXPREEMPAMPTUNE0_MASK (0x3 << 28) +#define USBNC_UTMIPHY_CFG1_TXRESTUNE0_SHIFT (26) /* Bits 26-27: USB Source Impedance Adjustment */ +#define USBNC_UTMIPHY_CFG1_TXRESTUNE0_MASK (0x3 << 26) +#define USBNC_UTMIPHY_CFG1_TXRISETUNE0_SHIFT (24) /* Bits 24-25: HS Transmitter Rise/Fall Time Adjustment */ +#define USBNC_UTMIPHY_CFG1_TXRISETUNE0_MASK (0x3 << 24) +#define USBNC_UTMIPHY_CFG1_TXVREFTUNE0_SHIFT (20) /* Bits 20-23: HS DC Voltage Level Adjustment */ +#define USBNC_UTMIPHY_CFG1_TXVREFTUNE0_MASK (0xf << 20) +#define USBNC_UTMIPHY_CFG1_TXFSLSTUNE0_SHIFT (16) /* Bits 16-19: FS/LS Source Impedance Adjustment */ +#define USBNC_UTMIPHY_CFG1_TXFSLSTUNE0_MASK (0xf << 16) +#define USBNC_UTMIPHY_CFG1_PHY_POR_SW (1 << 15) /* Bit 15 : PHY software POR */ +#define USBNC_UTMIPHY_CFG1_TXHSXVTUNE_SHIFT (13) /* Bits 13-14: Transmitter High-Speed Crossover Adjustment */ +#define USBNC_UTMIPHY_CFG1_TXHSXVTUNE_MASK (0x3 << 13) +#define USBNC_UTMIPHY_CFG1_OTGTUNE0_SHIFT (10) /* Bits 10-12: VBUS Valid Threshold Adjustment */ +#define USBNC_UTMIPHY_CFG1_OTGTUNE0_MASK (0x7 << 10) +#define USBNC_UTMIPHY_CFG1_SQRXTUNE0_SHIFT (7) /* Bits 7-9: Squelch Threshold Adjustment */ +#define USBNC_UTMIPHY_CFG1_SQRXTUNE0_MASK (0x7 << 7) +#define USBNC_UTMIPHY_CFG1_COMPDISTUNE0_SHIFT (4) /* Bits 4-6: Disconnect Threshold Adjustment */ +#define USBNC_UTMIPHY_CFG1_COMPDISTUNE0_MASK (0x7 << 4) + +#endif /* __ARCH_ARM64_SRC_IMX9_HARDWARE_IMX9_USBOTG_H */ diff --git a/arch/arm64/src/imx9/imx9_usbdev.c b/arch/arm64/src/imx9/imx9_usbdev.c new file mode 100644 index 0000000000000..30f9fef33f785 --- /dev/null +++ b/arch/arm64/src/imx9/imx9_usbdev.c @@ -0,0 +1,3111 @@ +/**************************************************************************** + * arch/arm64/src/imx9/imx9_usbdev.c + * + * 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 "chip.h" +#include "arm64_internal.h" +#include "imx9_ccm.h" +#include "hardware/imx9_memorymap.h" +#include "hardware/imx9_usbotg.h" +#include "hardware/imx9_ccm.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +#ifndef CONFIG_USBDEV_EP0_MAXSIZE +# define CONFIG_USBDEV_EP0_MAXSIZE 64 +#endif + +#ifndef CONFIG_USBDEV_MAXPOWER +# define CONFIG_USBDEV_MAXPOWER 100 /* mA */ +#endif + +/* Enable reading SOF from interrupt handler vs. simply reading on demand. + * Probably a bad idea... Unless there is some issue with sampling the SOF + * from hardware asynchronously. + */ + +#ifdef CONFIG_IMX9_USB_FRAME_INTERRUPT +# define USB_FRAME_INT USBDEV_USBINTR_SRE +#else +# define USB_FRAME_INT 0 +#endif + +#ifdef CONFIG_DEBUG_FEATURES +# define USB_ERROR_INT USBDEV_USBINTR_UEE +#else +# define USB_ERROR_INT 0 +#endif + +/* Debug ********************************************************************/ + +/* Trace error codes */ + +#define IMX9_TRACEERR_ALLOCFAIL 0x0001 +#define IMX9_TRACEERR_BADCLEARFEATURE 0x0002 +#define IMX9_TRACEERR_BADDEVGETSTATUS 0x0003 +#define IMX9_TRACEERR_BADEPNO 0x0004 +#define IMX9_TRACEERR_BADEPGETSTATUS 0x0005 +#define IMX9_TRACEERR_BADEPTYPE 0x0006 +#define IMX9_TRACEERR_BADGETCONFIG 0x0007 +#define IMX9_TRACEERR_BADGETSETDESC 0x0008 +#define IMX9_TRACEERR_BADGETSTATUS 0x0009 +#define IMX9_TRACEERR_BADSETADDRESS 0x000a +#define IMX9_TRACEERR_BADSETCONFIG 0x000b +#define IMX9_TRACEERR_BADSETFEATURE 0x000c +#define IMX9_TRACEERR_BINDFAILED 0x000d +#define IMX9_TRACEERR_DISPATCHSTALL 0x000e +#define IMX9_TRACEERR_DRIVER 0x000f +#define IMX9_TRACEERR_DRIVERREGISTERED 0x0010 +#define IMX9_TRACEERR_EP0SETUPSTALLED 0x0011 +#define IMX9_TRACEERR_EPINNULLPACKET 0x0012 +#define IMX9_TRACEERR_EPOUTNULLPACKET 0x0013 +#define IMX9_TRACEERR_INVALIDCTRLREQ 0x0014 +#define IMX9_TRACEERR_INVALIDPARMS 0x0015 +#define IMX9_TRACEERR_IRQREGISTRATION 0x0016 +#define IMX9_TRACEERR_NOEP 0x0017 +#define IMX9_TRACEERR_NOTCONFIGURED 0x0018 +#define IMX9_TRACEERR_REQABORTED 0x0019 + +/* Trace interrupt codes */ + +#define IMX9_TRACEINTID_USB 0x0001 +#define IMX9_TRACEINTID_CLEARFEATURE 0x0002 +#define IMX9_TRACEINTID_DEVGETSTATUS 0x0003 +#define IMX9_TRACEINTID_DEVRESET 0x0004 +#define IMX9_TRACEINTID_DISPATCH 0x0005 +#define IMX9_TRACEINTID_EP0COMPLETE 0x0006 +#define IMX9_TRACEINTID_EP0NAK 0x0007 +#define IMX9_TRACEINTID_EP0SETUP 0x0008 +#define IMX9_TRACEINTID_EPGETSTATUS 0x0009 +#define IMX9_TRACEINTID_EPIN 0x000a +#define IMX9_TRACEINTID_EPINQEMPTY 0x000b +#define IMX9_TRACEINTID_EP0INSETADDRESS 0x000c +#define IMX9_TRACEINTID_EPOUT 0x000d +#define IMX9_TRACEINTID_EPOUTQEMPTY 0x000e +#define IMX9_TRACEINTID_EP0SETUPSETADDRESS 0x000f +#define IMX9_TRACEINTID_FRAME 0x0010 +#define IMX9_TRACEINTID_GETCONFIG 0x0011 +#define IMX9_TRACEINTID_GETSETDESC 0x0012 +#define IMX9_TRACEINTID_GETSETIF 0x0013 +#define IMX9_TRACEINTID_GETSTATUS 0x0014 +#define IMX9_TRACEINTID_IFGETSTATUS 0x0015 +#define IMX9_TRACEINTID_SETCONFIG 0x0016 +#define IMX9_TRACEINTID_SETFEATURE 0x0017 +#define IMX9_TRACEINTID_SUSPENDED 0x0018 +#define IMX9_TRACEINTID_RESUMED 0x0019 +#define IMX9_TRACEINTID_SYNCHFRAME 0x001a + +#ifdef CONFIG_USBDEV_TRACE_STRINGS +const struct trace_msg_t g_usb_trace_strings_deverror[] = +{ + TRACE_STR(IMX9_TRACEERR_ALLOCFAIL), + TRACE_STR(IMX9_TRACEERR_BADCLEARFEATURE), + TRACE_STR(IMX9_TRACEERR_BADDEVGETSTATUS), + TRACE_STR(IMX9_TRACEERR_BADEPNO), + TRACE_STR(IMX9_TRACEERR_BADEPGETSTATUS), + TRACE_STR(IMX9_TRACEERR_BADEPTYPE), + TRACE_STR(IMX9_TRACEERR_BADGETCONFIG), + TRACE_STR(IMX9_TRACEERR_BADGETSETDESC), + TRACE_STR(IMX9_TRACEERR_BADGETSTATUS), + TRACE_STR(IMX9_TRACEERR_BADSETADDRESS), + TRACE_STR(IMX9_TRACEERR_BADSETCONFIG), + TRACE_STR(IMX9_TRACEERR_BADSETFEATURE), + TRACE_STR(IMX9_TRACEERR_BINDFAILED), + TRACE_STR(IMX9_TRACEERR_DISPATCHSTALL), + TRACE_STR(IMX9_TRACEERR_DRIVER), + TRACE_STR(IMX9_TRACEERR_DRIVERREGISTERED), + TRACE_STR(IMX9_TRACEERR_EP0SETUPSTALLED), + TRACE_STR(IMX9_TRACEERR_EPINNULLPACKET), + TRACE_STR(IMX9_TRACEERR_EPOUTNULLPACKET), + TRACE_STR(IMX9_TRACEERR_INVALIDCTRLREQ), + TRACE_STR(IMX9_TRACEERR_INVALIDPARMS), + TRACE_STR(IMX9_TRACEERR_IRQREGISTRATION), + TRACE_STR(IMX9_TRACEERR_NOEP), + TRACE_STR(IMX9_TRACEERR_NOTCONFIGURED), + TRACE_STR(IMX9_TRACEERR_REQABORTED), + TRACE_STR_END +}; + +const struct trace_msg_t g_usb_trace_strings_intdecode[] = +{ + TRACE_STR(IMX9_TRACEINTID_USB), + TRACE_STR(IMX9_TRACEINTID_CLEARFEATURE), + TRACE_STR(IMX9_TRACEINTID_DEVGETSTATUS), + TRACE_STR(IMX9_TRACEINTID_DEVRESET), + TRACE_STR(IMX9_TRACEINTID_DISPATCH), + TRACE_STR(IMX9_TRACEINTID_EP0COMPLETE), + TRACE_STR(IMX9_TRACEINTID_EP0NAK), + TRACE_STR(IMX9_TRACEINTID_EP0SETUP), + TRACE_STR(IMX9_TRACEINTID_EPGETSTATUS), + TRACE_STR(IMX9_TRACEINTID_EPIN), + TRACE_STR(IMX9_TRACEINTID_EPINQEMPTY), + TRACE_STR(IMX9_TRACEINTID_EP0INSETADDRESS), + TRACE_STR(IMX9_TRACEINTID_EPOUT), + TRACE_STR(IMX9_TRACEINTID_EPOUTQEMPTY), + TRACE_STR(IMX9_TRACEINTID_EP0SETUPSETADDRESS), + TRACE_STR(IMX9_TRACEINTID_FRAME), + TRACE_STR(IMX9_TRACEINTID_GETCONFIG), + TRACE_STR(IMX9_TRACEINTID_GETSETDESC), + TRACE_STR(IMX9_TRACEINTID_GETSETIF), + TRACE_STR(IMX9_TRACEINTID_GETSTATUS), + TRACE_STR(IMX9_TRACEINTID_IFGETSTATUS), + TRACE_STR(IMX9_TRACEINTID_SETCONFIG), + TRACE_STR(IMX9_TRACEINTID_SETFEATURE), + TRACE_STR(IMX9_TRACEINTID_SUSPENDED), + TRACE_STR(IMX9_TRACEINTID_RESUMED), + TRACE_STR(IMX9_TRACEINTID_SYNCHFRAME), + TRACE_STR_END +}; +#endif + +#if defined(CONFIG_ARMV7M_DCACHE) +# define cache_aligned_alloc(s) kmm_memalign(ARMV7M_DCACHE_LINESIZE,(s)) +# define CACHE_ALIGNED_DATA aligned_data(ARMV7M_DCACHE_LINESIZE) +#else +# define cache_aligned_alloc kmm_malloc +# define CACHE_ALIGNED_DATA +#endif + +/* Hardware interface *******************************************************/ + +/* This represents a Endpoint Transfer Descriptor - note these must be 32 + * byte aligned. + */ + +struct imx9_dtd_s +{ + volatile uint32_t nextdesc; /* Address of the next DMA descripto in RAM */ + volatile uint32_t config; /* Misc. bit encoded configuration information */ + uint32_t buffer0; /* Buffer start address */ + uint32_t buffer1; /* Buffer start address */ + uint32_t buffer2; /* Buffer start address */ + uint32_t buffer3; /* Buffer start address */ + uint32_t buffer4; /* Buffer start address */ + uint32_t xfer_len; /* Software only - transfer len that was queued */ +}; + +/* DTD nextdesc field */ + +#define DTD_NEXTDESC_INVALID (1 << 0) /* Bit 0 : Next Descriptor Invalid. The "Terminate" bit. */ + +/* DTD config field */ + +#define DTD_CONFIG_LENGTH(n) ((n) << 16) /* Bits 16-31 : Total bytes to transfer */ +#define DTD_CONFIG_IOC (1 << 15) /* Bit 15 : Interrupt on Completion */ +#define DTD_CONFIG_MULT_VARIABLE (0 << 10) /* Bits 10-11 : Number of packets executed per transacation descriptor (override) */ +#define DTD_CONFIG_MULT_NUM(n) ((n) << 10) +#define DTD_CONFIG_ACTIVE (1 << 7) /* Bit 7 : Status Active */ +#define DTD_CONFIG_HALTED (1 << 6) /* Bit 6 : Status Halted */ +#define DTD_CONFIG_BUFFER_ERROR (1 << 5) /* Bit 6 : Status Buffer Error */ +#define DTD_CONFIG_TRANSACTION_ERROR (1 << 3) /* Bit 3 : Status Transaction Error */ + +/* This represents a queue head - note these must be aligned to a 2048 byte + * boundary + */ + +struct imx9_dqh_s +{ + uint32_t capability; /* Endpoint capability */ + uint32_t currdesc; /* Current dTD pointer */ + struct imx9_dtd_s overlay; /* DTD overlay */ + volatile uint32_t setup[2]; /* Set-up buffer */ + uint32_t gap[4]; /* align to 64 bytes */ +}; + +/* DQH capability field */ + +#define DQH_CAPABILITY_MULT_VARIABLE (0 << 30) /* Bits 30-31 : Number of packets executed per transaction descriptor */ +#define DQH_CAPABILITY_MULT_NUM(n) ((n) << 30) +#define DQH_CAPABILITY_ZLT (1 << 29) /* Bit 29 : Zero Length Termination Select */ +#define DQH_CAPABILITY_MAX_PACKET(n) ((n) << 16) /* Bits 16-29 : Maximum packet size of associated endpoint (<1024) */ +#define DQH_CAPABILITY_IOS (1 << 15) /* Bit 15 : Interrupt on Setup */ + +/* Endpoints ****************************************************************/ + +/* Number of endpoints */ + +#define IMX9_NLOGENDPOINTS (8) /* ep0-7 */ +#define IMX9_NPHYSENDPOINTS (16) /* x2 for IN and OUT */ + +/* Odd physical endpoint numbers are IN; even are OUT */ + +#define IMX9_EPPHYIN(epphy) (((epphy) & 1) != 0) +#define IMX9_EPPHYOUT(epphy) (((epphy) & 1) == 0) + +#define IMX9_EPPHYIN2LOG(epphy) (((uint8_t)(epphy) >> 1) |USB_DIR_IN) +#define IMX9_EPPHYOUT2LOG(epphy) (((uint8_t)(epphy) >> 1) | USB_DIR_OUT) + +/* Endpoint 0 is special... */ + +#define IMX9_EP0_OUT (0) +#define IMX9_EP0_IN (1) + +/* Each endpoint has somewhat different characteristics */ + +#define IMX9_EPALLSET (0xffff) /* All endpoints */ +#define IMX9_EPOUTSET (0x5555) /* Even phy endpoint numbers are OUT EPs */ +#define IMX9_EPINSET (0xaaaa) /* Odd endpoint numbers are IN EPs */ +#define IMX9_EPCTRLSET (0x0003) /* EP0 IN/OUT are control endpoints */ +#define IMX9_EPINTRSET (0x000c) /* Interrupt endpoints */ +#define IMX9_EPBULKSET (0x0ff0) /* Bulk endpoints */ +#define IMX9_EPISOCSET (0xf000) /* Isochronous endpoints */ + +/* Maximum packet sizes for endpoints */ + +#define IMX9_EP0MAXPACKET (64) /* EP0 max packet size (1-64) */ +#define IMX9_BULKMAXPACKET (512) /* Bulk endpoint max packet (8/16/32/64/512) */ +#define IMX9_INTRMAXPACKET (1024) /* Interrupt endpoint max packet (1 to 1024) */ +#define IMX9_ISOCMAXPACKET (512) /* Acutally 1..1023 */ + +/* Endpoint bit position in SETUPSTAT, PRIME, FLUSH, STAT, COMPLETE + * registers + */ + +#define IMX9_ENDPTSHIFT(epphy) (IMX9_EPPHYIN(epphy) ? (16 + ((epphy) >> 1)) : ((epphy) >> 1)) +#define IMX9_ENDPTMASK(epphy) (1 << IMX9_ENDPTSHIFT(epphy)) +#define IMX9_ENDPTMASK_ALL 0x00ff00ff + +/* Request queue operations *************************************************/ + +#define imx9_rqempty(ep) ((ep)->head == NULL) +#define imx9_rqpeek(ep) ((ep)->head) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* A container for a request so that the request may be retained in a list */ + +struct imx9_req_s +{ + struct usbdev_req_s req; /* Standard USB request */ + struct imx9_req_s *flink; /* Supports a singly linked list */ +}; + +/* This is the internal representation of an endpoint */ + +struct imx9_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 imx9_ep_s. + */ + + struct usbdev_ep_s ep; /* Standard endpoint structure */ + + /* IMX9XX-specific fields */ + + struct imx9_usb_s *dev; /* Reference to private driver data */ + struct imx9_req_s *head; /* Request list for this endpoint */ + struct imx9_req_s *tail; + uint8_t epphy; /* Physical EP address */ + uint8_t stalled:1; /* 1: Endpoint is stalled */ +}; + +/* Structure for ep0 short transfers */ + +struct imx9_ep0_s +{ + uint8_t buf[64] CACHE_ALIGNED_DATA; /* buffer for EP0 short transfers */ + uint16_t buf_len; /* buffer length */ + struct usb_ctrlreq_s ctrl; /* structure for EP0 short transfers */ + uint8_t state; /* state of certain EP0 operations */ +}; + +/* This structure retains the state of the USB device controller */ + +struct imx9_usb_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 imx9_usb_s. + */ + + struct usbdev_s usbdev; + + /* The bound device class driver */ + + struct usbdevclass_driver_s *driver; + + const int id; /* Id of the usb controller */ + const uintptr_t base; /* Base address of the controller */ + uint8_t paddr; /* Address assigned by SETADDRESS */ + uint8_t stalled:1; /* 1: Protocol stalled */ + uint8_t selfpowered:1; /* 1: Device is self powered */ + uint8_t paddrset:1; /* 1: Peripheral addr has been set */ + uint8_t attached:1; /* 1: Host attached */ + uint8_t suspended:1; /* 1: Suspended */ + uint32_t softprio; /* Bitset of high priority interrupts */ + uint32_t epavail; /* Bitset of available endpoints */ +#ifdef CONFIG_IMX9_USB_FRAME_INTERRUPT + uint32_t sof; /* Last start-of-frame */ +#endif + + struct imx9_ep0_s ep0; /* ep0 */ + + /* The endpoint list */ + + struct imx9_ep_s eplist[IMX9_NPHYSENDPOINTS]; + + struct imx9_dqh_s qh[IMX9_NPHYSENDPOINTS] aligned_data(2048); + + struct imx9_dtd_s td[IMX9_NPHYSENDPOINTS] aligned_data(32); +}; + +#define EP0STATE_IDLE 0 /* Idle State, leave on receiving a setup packet or epsubmit */ +#define EP0STATE_SETUP_OUT 1 /* Setup Packet received - SET/CLEAR */ +#define EP0STATE_SETUP_IN 2 /* Setup Packet received - GET */ +#define EP0STATE_SHORTREAD 3 /* Short read without a usb_request */ +#define EP0STATE_SHORTWRITE 4 /* Short write without a usb_request */ +#define EP0STATE_WAIT_NAK_OUT 5 /* Waiting for Host to illicit status phase (GET) */ +#define EP0STATE_WAIT_NAK_IN 6 /* Waiting for Host to illicit status phase (SET/CLEAR) */ +#define EP0STATE_WAIT_STATUS_OUT 7 /* Wait for status phase to complete */ +#define EP0STATE_WAIT_STATUS_IN 8 /* Wait for status phase to complete */ +#define EP0STATE_DATA_IN 9 +#define EP0STATE_DATA_OUT 10 + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Register operations ******************************************************/ + +#ifdef CONFIG_IMX9_USB_REGDEBUG +static uint32_t imx9_getreg(struct imx9_usb_s *priv, off_t offset); +static void imx9_putreg(struct imx9_usb_s *priv, off_t offset, uint32_t val); +#else +# define imx9_getreg(priv, offset) getreg32(priv->base + offset) +# define imx9_putreg(priv, offset, val) putreg32(val,priv->base + offset) +#endif + +static inline void imx9_modifyreg(struct imx9_usb_s *priv, + off_t offset, + uint32_t clear, + uint32_t set); + +/* Request queue operations *************************************************/ + +static struct imx9_req_s *imx9_rqdequeue( + struct imx9_ep_s *privep); +static bool imx9_rqenqueue(struct imx9_ep_s *privep, + struct imx9_req_s *req); + +/* Low level data transfers and request operations **************************/ + +static inline void imx9_writedtd(struct imx9_dtd_s *dtd, + const uint8_t *data, + uint32_t nbytes); +static inline void imx9_queuedtd(struct imx9_usb_s *priv, uint8_t epphy, + struct imx9_dtd_s *dtd); +static inline void imx9_ep0xfer(struct imx9_usb_s *priv, uint8_t epphy, + uint8_t *data, + uint32_t nbytes); +static void imx9_readsetup(struct imx9_usb_s *priv, uint8_t epphy, + struct usb_ctrlreq_s *ctrl); +static inline void imx9_set_address(struct imx9_usb_s *priv, + uint16_t address); + +static void imx9_flushep(struct imx9_ep_s *privep); + +static int imx9_progressep(struct imx9_ep_s *privep); +static void imx9_reqcomplete(struct imx9_ep_s *privep, + struct imx9_req_s *privreq, int16_t result); + +static void imx9_cancelrequests(struct imx9_ep_s *privep, + int16_t status); + +/* Interrupt handling *******************************************************/ + +static struct imx9_ep_s *imx9_epfindbyaddr(struct imx9_usb_s *priv, + uint16_t eplog); +static void imx9_dispatchrequest(struct imx9_usb_s *priv, + const struct usb_ctrlreq_s *ctrl); +static void imx9_ep0configure(struct imx9_usb_s *priv); +static void imx9_usbreset(struct imx9_usb_s *priv); +static inline void imx9_ep0state(struct imx9_usb_s *priv, + uint16_t state); +static void imx9_ep0setup(struct imx9_usb_s *priv); +static void imx9_ep0complete(struct imx9_usb_s *priv, + uint8_t epphy); +static void imx9_ep0nak(struct imx9_usb_s *priv, uint8_t epphy); +static bool imx9_epcomplete(struct imx9_usb_s *priv, + uint8_t epphy); +static int imx9_usbinterrupt(int irq, void *context, + void *arg); + +/* Endpoint operations ******************************************************/ + +/* USB device controller operations *****************************************/ + +static int imx9_epconfigure(struct usbdev_ep_s *ep, + const struct usb_epdesc_s *desc, bool last); +static int imx9_epdisable(struct usbdev_ep_s *ep); +static struct usbdev_req_s *imx9_epallocreq(struct usbdev_ep_s *ep); +static void imx9_epfreereq(struct usbdev_ep_s *ep, + struct usbdev_req_s *); +#ifdef CONFIG_USBDEV_DMA +static void *imx9_epallocbuffer(struct usbdev_ep_s *ep, + uint16_t bytes); +static void imx9_epfreebuffer(struct usbdev_ep_s *ep, + void *buf); +#endif +static int imx9_epsubmit(struct usbdev_ep_s *ep, + struct usbdev_req_s *req); +static int imx9_epcancel(struct usbdev_ep_s *ep, + struct usbdev_req_s *req); +static int imx9_epstall(struct usbdev_ep_s *ep, bool resume); + +static struct usbdev_ep_s *imx9_allocep(struct usbdev_s *dev, + uint8_t epno, bool in, uint8_t eptype); +static void imx9_freeep(struct usbdev_s *dev, + struct usbdev_ep_s *ep); +static int imx9_getframe(struct usbdev_s *dev); +static int imx9_wakeup(struct usbdev_s *dev); +static int imx9_selfpowered(struct usbdev_s *dev, bool selfpowered); +static int imx9_pullup(struct usbdev_s *dev, bool enable); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct imx9_usb_s g_usbdev[] = +{ +#ifdef CONFIG_IMX9_USBDEV_USBC1 + { + .id = 0, + .base = IMX9_USB_OTG1_BASE, + }, +#endif + +#ifdef CONFIG_IMX9_USBDEV_USBC2 + { + .id = 1, + .base = IMX9_USB_OTG2_BASE, + }, +#endif +}; + +static const int n_usbdevs = sizeof(g_usbdev) / sizeof(g_usbdev[0]); + +static const struct usbdev_epops_s g_epops = +{ + .configure = imx9_epconfigure, + .disable = imx9_epdisable, + .allocreq = imx9_epallocreq, + .freereq = imx9_epfreereq, +#ifdef CONFIG_USBDEV_DMA + .allocbuffer = imx9_epallocbuffer, + .freebuffer = imx9_epfreebuffer, +#endif + .submit = imx9_epsubmit, + .cancel = imx9_epcancel, + .stall = imx9_epstall, +}; + +static const struct usbdev_ops_s g_devops = +{ + .allocep = imx9_allocep, + .freeep = imx9_freeep, + .getframe = imx9_getframe, + .wakeup = imx9_wakeup, + .selfpowered = imx9_selfpowered, + .pullup = imx9_pullup, +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imx9_getreg + * + * Description: + * Get the contents of an IMX93x register + * + ****************************************************************************/ + +#ifdef CONFIG_IMX9_USB_REGDEBUG +static uint32_t imx9_getreg(struct imx9_usb_s *priv, off_t offset) +{ + 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(priv->base + offset); + + /* 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 %d more times]\n", count - 3); + } + + /* Save the new address, value, and count */ + + prevaddr = addr; + preval = val; + count = 1; + } + + /* Show the register value read */ + + uinfo("%08x->%08x\n", priv->base + offset, val); + return val; +} +#endif + +/**************************************************************************** + * Name: imx9_putreg + * + * Description: + * Set the contents of an IMX93x register to a value + * + ****************************************************************************/ + +#ifdef CONFIG_IMX9_USB_REGDEBUG +static void imx9_putreg(struct imx9_usb_s *priv, off_t offset, uint32_t val) +{ + /* Show the register value being written */ + + uinfo("%08x<-%08x\n", priv->base + offset, val); + + /* Write the value */ + + putreg32(val, priv->base + offset); +} +#endif + +/**************************************************************************** + * Name: imx9_modifyreg + * + * Description: + * Change bits in a register + * + ****************************************************************************/ + +static inline void imx9_modifyreg(struct imx9_usb_s *priv, off_t offset, + uint32_t clear, uint32_t set) +{ + uint32_t reg = imx9_getreg(priv, offset); + reg &= ~clear; + reg |= set; + imx9_putreg(priv, offset, reg); +} + +/**************************************************************************** + * Name: imx9_rqdequeue + * + * Description: + * Remove a request from an endpoint request queue + * + ****************************************************************************/ + +static struct imx9_req_s *imx9_rqdequeue(struct imx9_ep_s *privep) +{ + struct imx9_req_s *ret = privep->head; + + if (ret) + { + privep->head = ret->flink; + if (!privep->head) + { + privep->tail = NULL; + } + + ret->flink = NULL; + } + + return ret; +} + +/**************************************************************************** + * Name: imx9_rqenqueue + * + * Description: + * Add a request from an endpoint request queue + * + ****************************************************************************/ + +static bool imx9_rqenqueue(struct imx9_ep_s *privep, + struct imx9_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: imx9_writedtd + * + * Description: + * Initialise a DTD to transfer the data + * + ****************************************************************************/ + +static inline void imx9_writedtd(struct imx9_dtd_s *dtd, + const uint8_t *data, + uint32_t nbytes) +{ + dtd->nextdesc = DTD_NEXTDESC_INVALID; + dtd->config = DTD_CONFIG_LENGTH(nbytes) | DTD_CONFIG_IOC | + DTD_CONFIG_ACTIVE; + dtd->buffer0 = (uint32_t)((uintptr_t) data); + dtd->buffer1 = (uint32_t)(((uintptr_t) data) + 0x1000) & 0xfffff000; + dtd->buffer2 = (uint32_t)(((uintptr_t) data) + 0x2000) & 0xfffff000; + dtd->buffer3 = (uint32_t)(((uintptr_t) data) + 0x3000) & 0xfffff000; + dtd->buffer4 = (uint32_t)(((uintptr_t) data) + 0x4000) & 0xfffff000; + dtd->xfer_len = nbytes; + + up_flush_dcache((uintptr_t)dtd, + (uintptr_t)dtd + sizeof(struct imx9_dtd_s)); + up_flush_dcache((uintptr_t)data, + (uintptr_t)data + nbytes); +} + +/**************************************************************************** + * Name: imx9_queuedtd + * + * Description: + * Add the DTD to the device list + * + * Assumptions: + * DTD is already flushed to RAM. + * + ****************************************************************************/ + +static void imx9_queuedtd(struct imx9_usb_s *priv, uint8_t epphy, + struct imx9_dtd_s *dtd) +{ + struct imx9_dqh_s *dqh = &priv->qh[epphy]; + + /* Queue the DTD onto the Endpoint + * NOTE - this only works when no DTD is currently queued + */ + + dqh->overlay.nextdesc = (uint32_t)(uintptr_t)dtd; + dqh->overlay.config &= ~(DTD_CONFIG_ACTIVE | DTD_CONFIG_HALTED); + + up_flush_dcache((uintptr_t)dqh, + (uintptr_t)dqh + sizeof(struct imx9_dqh_s)); + + uint32_t bit = IMX9_ENDPTMASK(epphy); + + imx9_modifyreg(priv, IMX9_USBDEV_ENDPTPRIME_OFFSET, 0, bit); + + while (imx9_getreg(priv, IMX9_USBDEV_ENDPTPRIME_OFFSET) & bit); +} + +/**************************************************************************** + * Name: imx9_ep0xfer + * + * Description: + * Schedule a short transfer for Endpoint 0 (IN or OUT) + * + ****************************************************************************/ + +static inline void imx9_ep0xfer(struct imx9_usb_s *priv, uint8_t epphy, + uint8_t *buf, uint32_t nbytes) +{ + struct imx9_dtd_s *dtd = &priv->td[epphy]; + + imx9_writedtd(dtd, buf, nbytes); + + imx9_queuedtd(priv, epphy, dtd); +} + +/**************************************************************************** + * Name: imx9_readsetup + * + * Description: + * Read a Setup packet from the DTD. + * + ****************************************************************************/ + +static void imx9_readsetup(struct imx9_usb_s *priv, uint8_t epphy, + struct usb_ctrlreq_s *ctrl) +{ + struct imx9_dqh_s *dqh = &priv->qh[epphy]; + int i; + + do + { + /* Set the trip wire */ + + imx9_modifyreg(priv, IMX9_USBDEV_USBCMD_OFFSET, 0, USBDEV_USBCMD_SUTW); + + up_invalidate_dcache((uintptr_t)dqh, + (uintptr_t)dqh + sizeof(struct imx9_dqh_s)); + + /* Copy the request... */ + + for (i = 0; i < 8; i++) + { + ((uint8_t *) ctrl)[i] = ((uint8_t *) dqh->setup)[i]; + } + } + + while (!(imx9_getreg(priv, + IMX9_USBDEV_USBCMD_OFFSET) & USBDEV_USBCMD_SUTW)); + + /* Clear the trip wire */ + + imx9_modifyreg(priv, IMX9_USBDEV_USBCMD_OFFSET, USBDEV_USBCMD_SUTW, 0); + + /* Clear the Setup Interrupt */ + + imx9_putreg(priv, IMX9_USBDEV_ENDPTSETUPSTAT_OFFSET, + IMX9_ENDPTMASK(IMX9_EP0_OUT)); +} + +/**************************************************************************** + * Name: imx9_set_address + * + * Description: + * Set the devices USB address + * + ****************************************************************************/ + +static inline void imx9_set_address(struct imx9_usb_s *priv, + uint16_t address) +{ + priv->paddr = address; + priv->paddrset = address != 0; + + imx9_modifyreg(priv, IMX9_USBDEV_DEVICEADDR_OFFSET, USBDEV_DEVICEADDR_MASK, + priv->paddr << USBDEV_DEVICEADDR_SHIFT); +} + +/**************************************************************************** + * Name: imx9_flushep + * + * Description: + * Flush any primed descriptors from this ep + * + ****************************************************************************/ + +static void imx9_flushep(struct imx9_ep_s *privep) +{ + uint32_t mask = IMX9_ENDPTMASK(privep->epphy); + struct imx9_usb_s *priv = privep->dev; + + do + { + imx9_putreg(priv, IMX9_USBDEV_ENDPTFLUSH_OFFSET, mask); + while ((imx9_getreg(priv, IMX9_USBDEV_ENDPTFLUSH_OFFSET) & mask) != 0); + } + while ((imx9_getreg(priv, IMX9_USBDEV_ENDPTSTATUS_OFFSET) & mask) != 0); +} + +/**************************************************************************** + * Name: imx9_progressep + * + * Description: + * Progress the Endpoint by priming the first request into the queue head + * + ****************************************************************************/ + +static int imx9_progressep(struct imx9_ep_s *privep) +{ + struct imx9_usb_s *priv = privep->dev; + struct imx9_dtd_s *dtd = &priv->td[privep->epphy]; + struct imx9_req_s *privreq; + + /* Check the request from the head of the endpoint request queue */ + + privreq = imx9_rqpeek(privep); + if (!privreq) + { + usbtrace(TRACE_INTDECODE(IMX9_TRACEINTID_EPINQEMPTY), 0); + return OK; + } + + /* Ignore any attempt to send a zero length packet */ + + if (privreq->req.len == 0) + { + /* If the class driver is responding to a setup packet, then wait for + * the host to illicit the response + */ + + if (privep->epphy == IMX9_EP0_IN && + privep->dev->ep0.state == EP0STATE_SETUP_OUT) + { + imx9_ep0state(priv, EP0STATE_WAIT_NAK_IN); + } + else + { + if (IMX9_EPPHYIN(privep->epphy)) + { + usbtrace(TRACE_DEVERROR(IMX9_TRACEERR_EPINNULLPACKET), 0); + } + else + { + usbtrace(TRACE_DEVERROR(IMX9_TRACEERR_EPOUTNULLPACKET), 0); + } + } + + imx9_reqcomplete(privep, imx9_rqdequeue(privep), OK); + return OK; + } + + if (privep->epphy == IMX9_EP0_IN) + { + imx9_ep0state(priv, EP0STATE_DATA_IN); + } + else if (privep->epphy == IMX9_EP0_OUT) + { + imx9_ep0state(priv, EP0STATE_DATA_OUT); + } + + int bytesleft = privreq->req.len - privreq->req.xfrd; + + if (IMX9_EPPHYIN(privep->epphy)) + { + usbtrace(TRACE_WRITE(privep->epphy), privreq->req.xfrd); + } + else + { + usbtrace(TRACE_READ(privep->epphy), privreq->req.xfrd); + } + + /* Initialise the DTD to transfer the next chunk */ + + imx9_writedtd(dtd, privreq->req.buf + privreq->req.xfrd, bytesleft); + + /* Then queue onto the DQH */ + + imx9_queuedtd(priv, privep->epphy, dtd); + + return OK; +} + +/**************************************************************************** + * Name: imx9_reqcomplete + * + * Description: + * Handle termination of the request at the head of the endpoint request + * queue. + * + ****************************************************************************/ + +static void imx9_reqcomplete(struct imx9_ep_s *privep, + struct imx9_req_s *privreq, int16_t result) +{ + /* If endpoint 0, temporarily reflect the state of protocol stalled + * in the callback. + */ + + bool stalled = privep->stalled; + if (privep->epphy == IMX9_EP0_IN) + 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: imx9_cancelrequests + * + * Description: + * Cancel all pending requests for an endpoint + * + ****************************************************************************/ + +static void imx9_cancelrequests(struct imx9_ep_s *privep, int16_t status) +{ + if (!imx9_rqempty(privep)) + imx9_flushep(privep); + + while (!imx9_rqempty(privep)) + { + /* FIXME: the entry at the head should be sync'd with the DTD + * FIXME: only report the error status if the transfer hasn't completed + */ + + usbtrace(TRACE_COMPLETE(privep->epphy), + (imx9_rqpeek(privep))->req.xfrd); + imx9_reqcomplete(privep, imx9_rqdequeue(privep), status); + } +} + +/**************************************************************************** + * Name: imx9_epfindbyaddr + * + * Description: + * Find the physical endpoint structure corresponding to a logic endpoint + * address + * + ****************************************************************************/ + +static struct imx9_ep_s *imx9_epfindbyaddr(struct imx9_usb_s *priv, + uint16_t eplog) +{ + struct imx9_ep_s *privep; + int i; + + /* Endpoint zero is a special case */ + + if (USB_EPNO(eplog) == 0) + { + return &priv->eplist[0]; + } + + /* Handle the remaining */ + + for (i = 1; i < IMX9_NPHYSENDPOINTS; i++) + { + privep = &priv->eplist[i]; + + /* Same logical endpoint number? (includes direction bit) */ + + if (eplog == privep->ep.eplog) + { + /* Return endpoint found */ + + return privep; + } + } + + /* Return endpoint not found */ + + return NULL; +} + +/**************************************************************************** + * Name: imx9_dispatchrequest + * + * Description: + * Provide unhandled setup actions to the class driver. This is logically + * part of the USB interrupt handler. + * + ****************************************************************************/ + +static void imx9_dispatchrequest(struct imx9_usb_s *priv, + const struct usb_ctrlreq_s *ctrl) +{ + int ret = -EIO; + + usbtrace(TRACE_INTDECODE(IMX9_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->ep0.buf, + priv->ep0.buf_len); + } + + if (ret < 0) + { + /* Stall on failure */ + + usbtrace(TRACE_DEVERROR(IMX9_TRACEERR_DISPATCHSTALL), 0); + priv->stalled = true; + } +} + +/**************************************************************************** + * Name: imx9_ep0configure + * + * Description: + * Reset Usb engine + * + ****************************************************************************/ + +static void imx9_ep0configure(struct imx9_usb_s *priv) +{ + /* Enable ep0 IN and ep0 OUT */ + + priv->qh[IMX9_EP0_OUT].capability = + (DQH_CAPABILITY_MAX_PACKET(CONFIG_USBDEV_EP0_MAXSIZE) | + DQH_CAPABILITY_IOS | DQH_CAPABILITY_ZLT); + + priv->qh[IMX9_EP0_IN].capability = + (DQH_CAPABILITY_MAX_PACKET(CONFIG_USBDEV_EP0_MAXSIZE) | + DQH_CAPABILITY_IOS | DQH_CAPABILITY_ZLT); + + priv->qh[IMX9_EP0_OUT].currdesc = DTD_NEXTDESC_INVALID; + priv->qh[IMX9_EP0_IN].currdesc = DTD_NEXTDESC_INVALID; + + up_clean_dcache((uintptr_t)priv->qh, + (uintptr_t)priv->qh + (sizeof(struct imx9_dqh_s) * 2)); + + /* Enable EP0 */ + + imx9_modifyreg(priv, IMX9_USBDEV_ENDPTCTRL0_OFFSET, 0, + USBDEV_ENDPTCTRL0_RXE | USBDEV_ENDPTCTRL0_TXE); +} + +/**************************************************************************** + * Name: imx9_usbreset + * + * Description: + * Reset Usb engine + * + ****************************************************************************/ + +static void imx9_usbreset(struct imx9_usb_s *priv) +{ + int epphy; + + /* Disable all endpoints. Control endpoint 0 is always enabled */ + + imx9_modifyreg(priv, IMX9_USBDEV_ENDPTCTRL1_OFFSET, + USBDEV_ENDPTCTRL_RXE | USBDEV_ENDPTCTRL_TXE, 0); + imx9_modifyreg(priv, IMX9_USBDEV_ENDPTCTRL2_OFFSET, + USBDEV_ENDPTCTRL_RXE | USBDEV_ENDPTCTRL_TXE, 0); + imx9_modifyreg(priv, IMX9_USBDEV_ENDPTCTRL3_OFFSET, + USBDEV_ENDPTCTRL_RXE | USBDEV_ENDPTCTRL_TXE, 0); + imx9_modifyreg(priv, IMX9_USBDEV_ENDPTCTRL4_OFFSET, + USBDEV_ENDPTCTRL_RXE | USBDEV_ENDPTCTRL_TXE, 0); + imx9_modifyreg(priv, IMX9_USBDEV_ENDPTCTRL5_OFFSET, + USBDEV_ENDPTCTRL_RXE | USBDEV_ENDPTCTRL_TXE, 0); + + /* Clear all pending interrupts */ + + imx9_putreg(priv, IMX9_USBDEV_ENDPTNAK_OFFSET, + imx9_getreg(priv, IMX9_USBDEV_ENDPTNAK_OFFSET)); + + imx9_putreg(priv, IMX9_USBDEV_ENDPTSETUPSTAT_OFFSET, + imx9_getreg(priv, IMX9_USBDEV_ENDPTSETUPSTAT_OFFSET)); + + imx9_putreg(priv, IMX9_USBDEV_ENDPTCOMPLETE_OFFSET, + imx9_getreg(priv, IMX9_USBDEV_ENDPTCOMPLETE_OFFSET)); + + /* Wait for all prime operations to have completed and then flush all + * DTDs + */ + + while (imx9_getreg(priv, IMX9_USBDEV_ENDPTPRIME_OFFSET) != 0); + + imx9_putreg(priv, IMX9_USBDEV_ENDPTFLUSH_OFFSET, IMX9_ENDPTMASK_ALL); + + while (imx9_getreg(priv, IMX9_USBDEV_ENDPTFLUSH_OFFSET)); + + /* Reset endpoints */ + + for (epphy = 0; epphy < IMX9_NPHYSENDPOINTS; epphy++) + { + struct imx9_ep_s *privep = &priv->eplist[epphy]; + + imx9_cancelrequests(privep, -ESHUTDOWN); + + /* Reset endpoint status */ + + privep->stalled = false; + } + + /* 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); + } + + /* Set the interrupt Threshold control interval to 0 */ + + imx9_modifyreg(priv, IMX9_USBDEV_USBCMD_OFFSET, USBDEV_USBCMD_ITC_MASK, + USBDEV_USBCMD_ITCIMME); + + /* Zero out the Endpoint queue heads */ + + memset ((void *)priv->qh, 0, sizeof(priv->qh)); + memset ((void *)priv->td, 0, sizeof(priv->td)); + + up_clean_dcache((uintptr_t)priv->qh, + (uintptr_t)priv->qh + sizeof(priv->qh)); + up_clean_dcache((uintptr_t)priv->td, + (uintptr_t)priv->td + sizeof(priv->td)); + + /* Set USB address to 0 */ + + imx9_set_address(priv, 0); + + /* Initialise the Enpoint List Address */ + + imx9_putreg(priv, IMX9_USBDEV_ENDPOINTLIST_OFFSET, + (uint32_t)(uintptr_t)priv->qh); + + /* EndPoint 0 initialization */ + + imx9_ep0configure(priv); + + /* Enable Device interrupts */ + + imx9_putreg(priv, IMX9_USBDEV_USBINTR_OFFSET, + USB_FRAME_INT | USB_ERROR_INT | USBDEV_USBINTR_NAKE | + USBDEV_USBINTR_SLE | USBDEV_USBINTR_URE | USBDEV_USBINTR_PCE | + USBDEV_USBINTR_UE); +} + +/**************************************************************************** + * Name: imx9_setstate + * + * Description: + * Sets the EP0 state and manages the NAK interrupts + * + ****************************************************************************/ + +static inline void imx9_ep0state(struct imx9_usb_s *priv, + uint16_t state) +{ + priv->ep0.state = state; + + switch (state) + { + case EP0STATE_WAIT_NAK_IN: + imx9_putreg(priv, IMX9_USBDEV_ENDPTNAKEN_OFFSET, + IMX9_ENDPTMASK(IMX9_EP0_IN)); + break; + + case EP0STATE_WAIT_NAK_OUT: + imx9_putreg(priv, IMX9_USBDEV_ENDPTNAKEN_OFFSET, + IMX9_ENDPTMASK(IMX9_EP0_OUT)); + break; + + default: + imx9_putreg(priv, IMX9_USBDEV_ENDPTNAKEN_OFFSET, 0); + break; + } +} + +/**************************************************************************** + * Name: imx9_ep0setup + * + * 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 imx9_ep0setup(struct imx9_usb_s *priv) +{ + struct imx9_ep_s *privep; + struct usb_ctrlreq_s *ctrl; + uint16_t value; + uint16_t index; + uint16_t len; + + ctrl = &priv->ep0.ctrl; + + /* Terminate any pending requests - since all DTDs will have been retired + * because of the setup packet. + */ + + imx9_cancelrequests(&priv->eplist[IMX9_EP0_OUT], -EPROTO); + imx9_cancelrequests(&priv->eplist[IMX9_EP0_IN], -EPROTO); + + /* Assume NOT stalled */ + + priv->eplist[IMX9_EP0_OUT].stalled = false; + priv->eplist[IMX9_EP0_IN].stalled = false; + priv->stalled = false; + + /* Read EP0 setup data */ + + imx9_readsetup(priv, IMX9_EP0_OUT, ctrl); + + /* And extract the little-endian 16-bit values to host order */ + + value = GETUINT16(ctrl->value); + index = GETUINT16(ctrl->index); + len = GETUINT16(ctrl->len); + + priv->ep0.buf_len = len; + + uinfo("type=%02x req=%02x value=%04x index=%04x len=%04x\n", + ctrl->type, ctrl->req, value, index, len); + + /* Starting a control request - update state */ + + if (ctrl->type & USB_REQ_DIR_IN) + { + imx9_ep0state(priv, EP0STATE_SETUP_IN); + } + else + { + imx9_ep0state(priv, EP0STATE_SETUP_OUT); + + if (len > 0) + { + imx9_ep0state(priv, EP0STATE_SHORTREAD); + imx9_ep0xfer(priv, IMX9_EP0_OUT, priv->ep0.buf, len); + return; + } + } + + /* Dispatch any non-standard requests */ + + if ((ctrl->type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD) + { + imx9_dispatchrequest(priv, ctrl); + } + else + { + /* Handle standard request. Pick off the things of interest to the USB + * device controller driver; pass what is left to the class driver. + */ + + switch (ctrl->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(IMX9_TRACEINTID_GETSTATUS), 0); + if (!priv->paddrset || len != 2 || + (ctrl->type & USB_REQ_DIR_IN) == 0 || value != 0) + { + priv->stalled = true; + } + else + { + switch (ctrl->type & USB_REQ_RECIPIENT_MASK) + { + case USB_REQ_RECIPIENT_ENDPOINT: + { + usbtrace( + TRACE_INTDECODE(IMX9_TRACEINTID_EPGETSTATUS), 0); + privep = imx9_epfindbyaddr(priv, index); + if (!privep) + { + usbtrace( + TRACE_DEVERROR(IMX9_TRACEERR_BADEPGETSTATUS), + 0); + priv->stalled = true; + } + else + { + if (privep->stalled) + { + priv->ep0.buf[0] = 1; /* Stalled */ + } + else + { + priv->ep0.buf[0] = 0; /* Not stalled */ + } + + priv->ep0.buf[1] = 0; + + imx9_ep0xfer(priv, IMX9_EP0_IN, priv->ep0.buf, 2); + imx9_ep0state(priv, EP0STATE_SHORTWRITE); + } + } + break; + + case USB_REQ_RECIPIENT_DEVICE: + { + if (index == 0) + { + usbtrace( + TRACE_INTDECODE(IMX9_TRACEINTID_DEVGETSTATUS), + 0); + + /* Features: Remote Wakeup=YES; selfpowered=? */ + + priv->ep0.buf[0] = + (priv->selfpowered << USB_FEATURE_SELFPOWERED) | + (1 << USB_FEATURE_REMOTEWAKEUP); + priv->ep0.buf[1] = 0; + + imx9_ep0xfer(priv, IMX9_EP0_IN, priv->ep0.buf, 2); + imx9_ep0state(priv, EP0STATE_SHORTWRITE); + } + else + { + usbtrace( + TRACE_DEVERROR(IMX9_TRACEERR_BADDEVGETSTATUS), + 0); + priv->stalled = true; + } + } + break; + + case USB_REQ_RECIPIENT_INTERFACE: + { + usbtrace(TRACE_INTDECODE(IMX9_TRACEINTID_IFGETSTATUS), + 0); + priv->ep0.buf[0] = 0; + priv->ep0.buf[1] = 0; + + imx9_ep0xfer(priv, IMX9_EP0_IN, priv->ep0.buf, 2); + imx9_ep0state(priv, EP0STATE_SHORTWRITE); + } + break; + + default: + { + usbtrace(TRACE_DEVERROR(IMX9_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(IMX9_TRACEINTID_CLEARFEATURE), 0); + if ((ctrl->type & USB_REQ_RECIPIENT_MASK) != + USB_REQ_RECIPIENT_ENDPOINT) + { + imx9_dispatchrequest(priv, ctrl); + } + else if (priv->paddrset != 0 && + value == USB_FEATURE_ENDPOINTHALT && + len == 0 && (privep = imx9_epfindbyaddr(priv, index)) != NULL) + { + imx9_epstall(&privep->ep, true); + imx9_ep0state(priv, EP0STATE_WAIT_NAK_IN); + } + else + { + usbtrace(TRACE_DEVERROR(IMX9_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(IMX9_TRACEINTID_SETFEATURE), 0); + if (((ctrl->type & USB_REQ_RECIPIENT_MASK) == + USB_REQ_RECIPIENT_DEVICE) && value == USB_FEATURE_TESTMODE) + { + uinfo("test mode: %d\n", index); + } + else if ((ctrl->type & USB_REQ_RECIPIENT_MASK) != + USB_REQ_RECIPIENT_ENDPOINT) + { + imx9_dispatchrequest(priv, ctrl); + } + else if (priv->paddrset != 0 && + value == USB_FEATURE_ENDPOINTHALT && + len == 0 && (privep = imx9_epfindbyaddr(priv, index)) != NULL) + { + imx9_epstall(&privep->ep, false); + imx9_ep0state(priv, EP0STATE_WAIT_NAK_IN); + } + else + { + usbtrace(TRACE_DEVERROR(IMX9_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(IMX9_TRACEINTID_EP0SETUPSETADDRESS), + value); + if (((ctrl->type & USB_REQ_RECIPIENT_MASK) == + USB_REQ_RECIPIENT_DEVICE) && + index == 0 && len == 0 && value < 128) + { + /* Save the address. We cannot actually change to the next + * address until the completion of the status phase. + */ + + priv->paddr = ctrl->value[0]; + priv->paddrset = false; + imx9_ep0state(priv, EP0STATE_WAIT_NAK_IN); + } + else + { + usbtrace(TRACE_DEVERROR(IMX9_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(IMX9_TRACEINTID_GETSETDESC), 0); + if ((ctrl->type & USB_REQ_RECIPIENT_MASK) == + USB_REQ_RECIPIENT_DEVICE) + { + imx9_dispatchrequest(priv, ctrl); + } + else + { + usbtrace(TRACE_DEVERROR(IMX9_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(IMX9_TRACEINTID_GETCONFIG), 0); + if (priv->paddrset && + ((ctrl->type & USB_REQ_RECIPIENT_MASK) == + USB_REQ_RECIPIENT_DEVICE) && + value == 0 && index == 0 && len == 1) + { + imx9_dispatchrequest(priv, ctrl); + } + else + { + usbtrace(TRACE_DEVERROR(IMX9_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(IMX9_TRACEINTID_SETCONFIG), 0); + if (((ctrl->type & USB_REQ_RECIPIENT_MASK) == + USB_REQ_RECIPIENT_DEVICE) && index == 0 && len == 0) + { + imx9_dispatchrequest(priv, ctrl); + } + else + { + usbtrace(TRACE_DEVERROR(IMX9_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(IMX9_TRACEINTID_GETSETIF), 0); + imx9_dispatchrequest(priv, ctrl); + } + break; + + case USB_REQ_SYNCHFRAME: + /* type: device-to-host; recipient = endpoint + * value: 0 + * index: endpoint; + * len: 2; data = frame number + */ + + { + usbtrace(TRACE_INTDECODE(IMX9_TRACEINTID_SYNCHFRAME), 0); + } + break; + + default: + { + usbtrace(TRACE_DEVERROR(IMX9_TRACEERR_INVALIDCTRLREQ), 0); + priv->stalled = true; + } + break; + } + } + + if (priv->stalled) + { + usbtrace(TRACE_DEVERROR(IMX9_TRACEERR_EP0SETUPSTALLED), + priv->ep0.state); + imx9_epstall(&priv->eplist[IMX9_EP0_IN].ep, false); + imx9_epstall(&priv->eplist[IMX9_EP0_OUT].ep, false); + } +} + +/**************************************************************************** + * Name: imx9_ep0complete + * + * Description: + * Transfer complete handler for Endpoint 0 + * + ****************************************************************************/ + +static void imx9_ep0complete(struct imx9_usb_s *priv, uint8_t epphy) +{ + struct imx9_ep_s *privep = &priv->eplist[epphy]; + + usbtrace(TRACE_INTDECODE(IMX9_TRACEINTID_EP0COMPLETE), + (uint16_t)priv->ep0.state); + + switch (priv->ep0.state) + { + case EP0STATE_DATA_IN: + if (imx9_rqempty(privep)) + { + return; + } + + if (imx9_epcomplete(priv, epphy)) + { + imx9_ep0state(priv, EP0STATE_WAIT_NAK_OUT); + } + break; + + case EP0STATE_DATA_OUT: + if (imx9_rqempty(privep)) + { + return; + } + + if (imx9_epcomplete(priv, epphy)) + { + imx9_ep0state(priv, EP0STATE_WAIT_NAK_IN); + } + break; + + case EP0STATE_SHORTREAD: + + /* Make sure we have updated data after the DMA transfer. + * This invalidation matches the flush in writedtd(). + */ + + up_invalidate_dcache((uintptr_t)priv->ep0.buf, + (uintptr_t)priv->ep0.buf + sizeof(priv->ep0.buf)); + + imx9_dispatchrequest(priv, &priv->ep0.ctrl); + imx9_ep0state(priv, EP0STATE_WAIT_NAK_IN); + break; + + case EP0STATE_SHORTWRITE: + imx9_ep0state(priv, EP0STATE_WAIT_NAK_OUT); + break; + + case EP0STATE_WAIT_STATUS_IN: + imx9_ep0state(priv, EP0STATE_IDLE); + + /* If we've received a SETADDRESS packet, then we set the address + * now that the status phase has completed + */ + + if (!priv->paddrset && priv->paddr != 0) + { + usbtrace(TRACE_INTDECODE(IMX9_TRACEINTID_EP0INSETADDRESS), + (uint16_t)priv->paddr); + imx9_set_address(priv, priv->paddr); + } + + break; + + case EP0STATE_WAIT_STATUS_OUT: + imx9_ep0state(priv, EP0STATE_IDLE); + break; + + default: +#ifdef CONFIG_DEBUG_FEATURES + DEBUGASSERT(priv->ep0.state != EP0STATE_DATA_IN && + priv->ep0.state != EP0STATE_DATA_OUT && + priv->ep0.state != EP0STATE_SHORTWRITE && + priv->ep0.state != EP0STATE_WAIT_STATUS_IN && + priv->ep0.state != EP0STATE_WAIT_STATUS_OUT); +#endif + priv->stalled = true; + break; + } + + if (priv->stalled) + { + usbtrace(TRACE_DEVERROR(IMX9_TRACEERR_EP0SETUPSTALLED), + priv->ep0.state); + imx9_epstall(&priv->eplist[IMX9_EP0_IN].ep, false); + imx9_epstall(&priv->eplist[IMX9_EP0_OUT].ep, false); + } +} + +/**************************************************************************** + * Name: imx9_ep0nak + * + * Description: + * Handle a NAK interrupt on EP0 + * + ****************************************************************************/ + +static void imx9_ep0nak(struct imx9_usb_s *priv, uint8_t epphy) +{ + usbtrace(TRACE_INTDECODE(IMX9_TRACEINTID_EP0NAK), + (uint16_t)priv->ep0.state); + + switch (priv->ep0.state) + { + case EP0STATE_WAIT_NAK_IN: + imx9_ep0xfer(priv, IMX9_EP0_IN, NULL, 0); + imx9_ep0state(priv, EP0STATE_WAIT_STATUS_IN); + break; + + case EP0STATE_WAIT_NAK_OUT: + imx9_ep0xfer(priv, IMX9_EP0_OUT, NULL, 0); + imx9_ep0state(priv, EP0STATE_WAIT_STATUS_OUT); + break; + + default: +#ifdef CONFIG_DEBUG_FEATURES + DEBUGASSERT(priv->ep0.state != EP0STATE_WAIT_NAK_IN && + priv->ep0.state != EP0STATE_WAIT_NAK_OUT); +#endif + priv->stalled = true; + break; + } + + if (priv->stalled) + { + usbtrace(TRACE_DEVERROR(IMX9_TRACEERR_EP0SETUPSTALLED), + priv->ep0.state); + imx9_epstall(&priv->eplist[IMX9_EP0_IN].ep, false); + imx9_epstall(&priv->eplist[IMX9_EP0_OUT].ep, false); + } +} + +/**************************************************************************** + * Name: imx9_epcomplete + * + * Description: + * Transfer complete handler for Endpoints other than 0 + * returns whether the request at the head has completed + * + ****************************************************************************/ + +bool imx9_epcomplete(struct imx9_usb_s *priv, uint8_t epphy) +{ + struct imx9_ep_s *privep = &priv->eplist[epphy]; + struct imx9_req_s *privreq = privep->head; + struct imx9_dtd_s *dtd = &priv->td[epphy]; + + if (privreq == NULL) /* This shouldn't really happen */ + { + if (IMX9_EPPHYOUT(privep->epphy)) + { + usbtrace(TRACE_INTDECODE(IMX9_TRACEINTID_EPINQEMPTY), 0); + } + else + { + usbtrace(TRACE_INTDECODE(IMX9_TRACEINTID_EPOUTQEMPTY), 0); + } + + return true; + } + + /* Make sure we have updated data after the DMA transfer. + * This invalidation matches the flush in writedtd(). + */ + + up_invalidate_dcache((uintptr_t)dtd, + (uintptr_t)dtd + sizeof(struct imx9_dtd_s)); + up_invalidate_dcache((uintptr_t)dtd->buffer0, + (uintptr_t)dtd->buffer0 + dtd->xfer_len); + + int xfrd = dtd->xfer_len - (dtd->config >> 16); + + privreq->req.xfrd += xfrd; + + bool complete = true; + if (IMX9_EPPHYOUT(privep->epphy)) + { + /* read(OUT) completes when request filled, or a short transfer is + * received + */ + + usbtrace(TRACE_INTDECODE(IMX9_TRACEINTID_EPIN), complete); + } + else + { + /* write(IN) completes when request finished, unless we need to + * terminate with a ZLP + */ + + bool need_zlp = (xfrd == privep->ep.maxpacket) && + ((privreq->req.flags & USBDEV_REQFLAGS_NULLPKT) != 0); + + complete = (privreq->req.xfrd >= privreq->req.len && !need_zlp); + + usbtrace(TRACE_INTDECODE(IMX9_TRACEINTID_EPOUT), complete); + } + + /* If the transfer is complete, then dequeue and progress any further + * queued requests + */ + + if (complete) + { + privreq = imx9_rqdequeue(privep); + } + + if (!imx9_rqempty(privep)) + { + imx9_progressep(privep); + } + + /* Now it's safe to call the completion callback as it may well submit a + * new request + */ + + if (complete) + { + usbtrace(TRACE_COMPLETE(privep->epphy), privreq->req.xfrd); + imx9_reqcomplete(privep, privreq, OK); + } + + return complete; +} + +/**************************************************************************** + * Name: imx9_usbinterrupt + * + * Description: + * USB interrupt handler + * + ****************************************************************************/ + +static int imx9_usbinterrupt(int irq, void *context, void *arg) +{ + struct imx9_usb_s *priv = (struct imx9_usb_s *)arg; + uint32_t disr; + uint32_t portsc1; + uint32_t n; + + usbtrace(TRACE_INTENTRY(IMX9_TRACEINTID_USB), 0); + + /* Read the interrupts and then clear them */ + + disr = imx9_getreg(priv, IMX9_USBDEV_USBSTS_OFFSET); + imx9_putreg(priv, IMX9_USBDEV_USBSTS_OFFSET, disr); + + if (disr & USBDEV_USBSTS_URI) + { + usbtrace(TRACE_INTDECODE(IMX9_TRACEINTID_DEVRESET), 0); + + imx9_usbreset(priv); + + usbtrace(TRACE_INTEXIT(IMX9_TRACEINTID_USB), 0); + return OK; + } + + /* When the device controller enters a suspend state from an active state, + * the SLI bit will be set to a one. + */ + + if (!priv->suspended && (disr & USBDEV_USBSTS_SLI) != 0) + { + usbtrace(TRACE_INTDECODE(IMX9_TRACEINTID_SUSPENDED), 0); + + /* Inform the Class driver of the suspend event */ + + priv->suspended = 1; + if (priv->driver) + { + CLASS_SUSPEND(priv->driver, &priv->usbdev); + } + + /* TODO: Perform power management operations here. */ + } + + /* The device controller clears the SLI bit upon exiting from a suspend + * state. This bit can also be cleared by software writing a one to it. + */ + + else if (priv->suspended && (disr & USBDEV_USBSTS_SLI) == 0) + { + usbtrace(TRACE_INTDECODE(IMX9_TRACEINTID_RESUMED), 0); + + /* Inform the Class driver of the resume event */ + + priv->suspended = 0; + if (priv->driver) + { + CLASS_RESUME(priv->driver, &priv->usbdev); + } + + /* TODO: Perform power management operations here. */ + } + + if (disr & USBDEV_USBSTS_PCI) + { + portsc1 = imx9_getreg(priv, IMX9_USBDEV_PORTSC1_OFFSET); + + if (portsc1 & USBDEV_PRTSC1_HSP) + priv->usbdev.speed = USB_SPEED_HIGH; + else + priv->usbdev.speed = USB_SPEED_FULL; + + if (portsc1 & USBDEV_PRTSC1_FPR) + { + /* FIXME: this occurs because of a J-to-K transition detected + * while the port is in SUSPEND state - presumambly this + * is where the host is resuming the device? + * + * - but do we need to "ack" the interrupt + */ + } + } + +#ifdef CONFIG_IMX9_USB_FRAME_INTERRUPT + if (disr & USBDEV_USBSTS_SRI) + { + usbtrace(TRACE_INTDECODE(IMX9_TRACEINTID_FRAME), 0); + + uint32_t frindex = imx9_getreg(IMX9_USB_FRINDEX); + uint16_t frame_num = + (frindex & USBDEV_FRINDEX_LFN_MASK) >> USBDEV_FRINDEX_LFN_SHIFT; + + priv->sof = frame_num; + } +#endif + + if (disr & USBDEV_USBSTS_UEI) + { + /* FIXME: these occur when a transfer results in an error condition + * it is set alongside USBINT if the DTD also had its IOC + * bit set. + */ + } + + if (disr & USBDEV_USBSTS_UI) + { + /* Handle completion interrupts */ + + uint32_t mask = imx9_getreg(priv, IMX9_USBDEV_ENDPTCOMPLETE_OFFSET); + + if (mask) + { + /* Clear any NAK interrupt and completion interrupts */ + + imx9_putreg(priv, IMX9_USBDEV_ENDPTNAK_OFFSET, mask); + imx9_putreg(priv, IMX9_USBDEV_ENDPTCOMPLETE_OFFSET, mask); + + if (mask & IMX9_ENDPTMASK(0)) + { + imx9_ep0complete(priv, 0); + } + + if (mask & IMX9_ENDPTMASK(1)) + { + imx9_ep0complete(priv, 1); + } + + for (n = 1; n < IMX9_NLOGENDPOINTS; n++) + { + if (mask & IMX9_ENDPTMASK((n << 1))) + { + imx9_epcomplete(priv, (n << 1)); + } + + if (mask & IMX9_ENDPTMASK((n << 1)+1)) + { + imx9_epcomplete(priv, (n << 1)+1); + } + } + } + + /* Handle setup interrupts */ + + uint32_t setupstat = imx9_getreg(priv, + IMX9_USBDEV_ENDPTSETUPSTAT_OFFSET); + if (setupstat) + { + /* Clear the endpoint complete CTRL OUT and IN when a Setup is + * received + */ + + imx9_putreg(priv, IMX9_USBDEV_ENDPTCOMPLETE_OFFSET, + IMX9_ENDPTMASK(IMX9_EP0_IN) | + IMX9_ENDPTMASK(IMX9_EP0_OUT)); + + if (setupstat & IMX9_ENDPTMASK(IMX9_EP0_OUT)) + { + usbtrace(TRACE_INTDECODE(IMX9_TRACEINTID_EP0SETUP), + setupstat); + imx9_ep0setup(priv); + } + } + } + + if (disr & USBDEV_USBSTS_NAKI) + { + uint32_t pending = imx9_getreg(priv, IMX9_USBDEV_ENDPTNAK_OFFSET) & + imx9_getreg(priv, IMX9_USBDEV_ENDPTNAKEN_OFFSET); + + if (pending) + { + /* We shouldn't see NAK interrupts except on Endpoint 0 */ + + if (pending & IMX9_ENDPTMASK(0)) + { + imx9_ep0nak(priv, 0); + } + + if (pending & IMX9_ENDPTMASK(1)) + { + imx9_ep0nak(priv, 1); + } + } + + /* Clear the interrupts */ + + imx9_putreg(priv, IMX9_USBDEV_ENDPTNAK_OFFSET, pending); + } + + usbtrace(TRACE_INTEXIT(IMX9_TRACEINTID_USB), 0); + return OK; +} + +/**************************************************************************** + * Endpoint operations + ****************************************************************************/ + +/**************************************************************************** + * Name: imx9_epconfigure + * + * 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 is the last endpoint to be configured. Some + * hardware needs to take special action when all of the endpoints + * have been configured. + * + ****************************************************************************/ + +static int imx9_epconfigure(struct usbdev_ep_s *ep, + const struct usb_epdesc_s *desc, + bool last) +{ + struct imx9_ep_s *privep = (struct imx9_ep_s *)ep; + struct imx9_usb_s *priv = privep->dev; + struct imx9_dqh_s *dqh = &priv->qh[privep->epphy]; + + usbtrace(TRACE_EPCONFIGURE, privep->epphy); + DEBUGASSERT(desc->addr == ep->eplog); + + /* Initialise EP capabilities */ + + uint16_t maxsize = GETUINT16(desc->mxpacketsize); + if ((desc->attr & USB_EP_ATTR_XFERTYPE_MASK) == USB_EP_ATTR_XFER_ISOC) + { + dqh->capability = (DQH_CAPABILITY_MAX_PACKET(maxsize) | + DQH_CAPABILITY_IOS | + DQH_CAPABILITY_ZLT); + } + else + { + dqh->capability = (DQH_CAPABILITY_MAX_PACKET(maxsize) | + DQH_CAPABILITY_ZLT); + } + + up_clean_dcache((uintptr_t)dqh, + (uintptr_t)dqh + sizeof(struct imx9_dqh_s)); + + /* Setup Endpoint Control Register */ + + if (IMX9_EPPHYIN(privep->epphy)) + { + /* Reset the data toggles */ + + uint32_t cfg = USBDEV_ENDPTCTRL_TXR; + + /* Set the endpoint type */ + + switch (desc->attr & USB_EP_ATTR_XFERTYPE_MASK) + { + case USB_EP_ATTR_XFER_CONTROL: + cfg |= USBDEV_ENDPTCTRL_TXT_CTRL; break; + case USB_EP_ATTR_XFER_ISOC: + cfg |= USBDEV_ENDPTCTRL_TXT_ISOC; break; + case USB_EP_ATTR_XFER_BULK: + cfg |= USBDEV_ENDPTCTRL_TXT_BULK; break; + case USB_EP_ATTR_XFER_INT: + cfg |= USBDEV_ENDPTCTRL_TXT_INTR; break; + } + + imx9_modifyreg(priv, IMX9_USBDEV_ENDPTCTRL_OFFSET(privep->epphy >> 1), + 0xffff0000, cfg); + } + else + { + /* Reset the data toggles */ + + uint32_t cfg = USBDEV_ENDPTCTRL_RXR; + + /* Set the endpoint type */ + + switch (desc->attr & USB_EP_ATTR_XFERTYPE_MASK) + { + case USB_EP_ATTR_XFER_CONTROL: + cfg |= USBDEV_ENDPTCTRL_RXT_CTRL; break; + case USB_EP_ATTR_XFER_ISOC: + cfg |= USBDEV_ENDPTCTRL_RXT_ISOC; break; + case USB_EP_ATTR_XFER_BULK: + cfg |= USBDEV_ENDPTCTRL_RXT_BULK; break; + case USB_EP_ATTR_XFER_INT: + cfg |= USBDEV_ENDPTCTRL_RXT_INTR; break; + } + + imx9_modifyreg(priv, IMX9_USBDEV_ENDPTCTRL_OFFSET(privep->epphy >> 1), + 0xffff0000, cfg); + } + + /* Reset endpoint status */ + + privep->stalled = false; + + /* Enable the endpoint */ + + if (IMX9_EPPHYIN(privep->epphy)) + { + imx9_modifyreg(priv, IMX9_USBDEV_ENDPTCTRL_OFFSET(privep->epphy >> 1), + 0, USBDEV_ENDPTCTRL_TXE); + } + else + { + imx9_modifyreg(priv, IMX9_USBDEV_ENDPTCTRL_OFFSET(privep->epphy >> 1), + 0, USBDEV_ENDPTCTRL_RXE); + } + + return OK; +} + +/**************************************************************************** + * Name: imx9_epdisable + * + * Description: + * The endpoint will no longer be used + * + ****************************************************************************/ + +static int imx9_epdisable(struct usbdev_ep_s *ep) +{ + struct imx9_ep_s *privep = (struct imx9_ep_s *)ep; + struct imx9_usb_s *priv = privep->dev; + irqstate_t flags; + +#ifdef CONFIG_DEBUG_FEATURES + if (!ep) + { + usbtrace(TRACE_DEVERROR(IMX9_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + + usbtrace(TRACE_EPDISABLE, privep->epphy); + + flags = enter_critical_section(); + + /* Disable Endpoint */ + + if (IMX9_EPPHYIN(privep->epphy)) + { + imx9_modifyreg(priv, IMX9_USBDEV_ENDPTCTRL_OFFSET(privep->epphy >> 1), + USBDEV_ENDPTCTRL_TXE, 0); + } + else + { + imx9_modifyreg(priv, IMX9_USBDEV_ENDPTCTRL_OFFSET(privep->epphy >> 1), + USBDEV_ENDPTCTRL_RXE, 0); + } + + privep->stalled = true; + + /* Cancel any ongoing activity */ + + imx9_cancelrequests(privep, -ESHUTDOWN); + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: imx9_epallocreq + * + * Description: + * Allocate an I/O request + * + ****************************************************************************/ + +static struct usbdev_req_s *imx9_epallocreq(struct usbdev_ep_s *ep) +{ + struct imx9_req_s *privreq; + +#ifdef CONFIG_DEBUG_FEATURES + if (!ep) + { + usbtrace(TRACE_DEVERROR(IMX9_TRACEERR_INVALIDPARMS), 0); + return NULL; + } +#endif + + usbtrace(TRACE_EPALLOCREQ, ((struct imx9_ep_s *)ep)->epphy); + + privreq = kmm_malloc(sizeof(struct imx9_req_s)); + if (!privreq) + { + usbtrace(TRACE_DEVERROR(IMX9_TRACEERR_ALLOCFAIL), 0); + return NULL; + } + + memset(privreq, 0, sizeof(struct imx9_req_s)); + return &privreq->req; +} + +/**************************************************************************** + * Name: imx9_epfreereq + * + * Description: + * Free an I/O request + * + ****************************************************************************/ + +static void imx9_epfreereq(struct usbdev_ep_s *ep, + struct usbdev_req_s *req) +{ + struct imx9_req_s *privreq = (struct imx9_req_s *)req; + +#ifdef CONFIG_DEBUG_FEATURES + if (!ep || !req) + { + usbtrace(TRACE_DEVERROR(IMX9_TRACEERR_INVALIDPARMS), 0); + return; + } +#endif + + usbtrace(TRACE_EPFREEREQ, ((struct imx9_ep_s *)ep)->epphy); + kmm_free(privreq); +} + +/**************************************************************************** + * Name: imx9_epallocbuffer + * + * Description: + * Allocate an I/O buffer + * + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_DMA +static void *imx9_epallocbuffer(struct usbdev_ep_s *ep, uint16_t bytes) +{ + /* The USB peripheral DMA is very forgiving, as the dTD allows the buffer + * to start at any address. Hence, no need for alignment. + */ + + struct imx9_ep_s *privep = (struct imx9_ep_s *)ep; + UNUSED(privep); + + usbtrace(TRACE_EPALLOCBUFFER, privep->epphy); +#ifdef CONFIG_USBDEV_DMAMEMORY + return usbdev_dma_alloc(bytes); +#else + return cache_aligned_alloc(bytes); +#endif +} +#endif + +/**************************************************************************** + * Name: imx9_epfreebuffer + * + * Description: + * Free an I/O buffer + * + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_DMA +static void imx9_epfreebuffer(struct usbdev_ep_s *ep, void *buf) +{ + struct imx9_ep_s *privep = (struct imx9_ep_s *)ep; + UNUSED(privep); + + usbtrace(TRACE_EPFREEBUFFER, privep->epphy); + +#ifdef CONFIG_USBDEV_DMAMEMORY + usbdev_dma_free(buf); +#else + kmm_free(buf); +#endif +} +#endif + +/**************************************************************************** + * Name: imx9_epsubmit + * + * Description: + * Submit an I/O request to the endpoint + * + ****************************************************************************/ + +static int imx9_epsubmit(struct usbdev_ep_s *ep, + struct usbdev_req_s *req) +{ + struct imx9_req_s *privreq = (struct imx9_req_s *)req; + struct imx9_ep_s *privep = (struct imx9_ep_s *)ep; + struct imx9_usb_s *priv; + irqstate_t flags; + int ret = OK; + +#ifdef CONFIG_DEBUG_FEATURES + if (!req || !req->callback || !req->buf || !ep) + { + usbtrace(TRACE_DEVERROR(IMX9_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; + + if (!priv->driver || priv->usbdev.speed == USB_SPEED_UNKNOWN) + { + usbtrace(TRACE_DEVERROR(IMX9_TRACEERR_NOTCONFIGURED), + priv->usbdev.speed); + return -ESHUTDOWN; + } + + /* 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 (privep->stalled) + { + ret = -EBUSY; + } + else + { + /* Add the new request to the request queue for the endpoint */ + + if (IMX9_EPPHYIN(privep->epphy)) + { + usbtrace(TRACE_INREQQUEUED(privep->epphy), privreq->req.len); + } + else + { + usbtrace(TRACE_OUTREQQUEUED(privep->epphy), privreq->req.len); + } + + if (imx9_rqenqueue(privep, privreq)) + { + imx9_progressep(privep); + } + } + + leave_critical_section(flags); + return ret; +} + +/**************************************************************************** + * Name: imx9_epcancel + * + * Description: + * Cancel an I/O request previously sent to an endpoint + * + ****************************************************************************/ + +static int imx9_epcancel(struct usbdev_ep_s *ep, + struct usbdev_req_s *req) +{ + struct imx9_ep_s *privep = (struct imx9_ep_s *)ep; + irqstate_t flags; + +#ifdef CONFIG_DEBUG_FEATURES + if (!ep || !req) + { + usbtrace(TRACE_DEVERROR(IMX9_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 ... + */ + + imx9_cancelrequests(privep, -ESHUTDOWN); + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: imx9_epstall + * + * Description: + * Stall or resume and endpoint + * + ****************************************************************************/ + +static int imx9_epstall(struct usbdev_ep_s *ep, bool resume) +{ + struct imx9_ep_s *privep = (struct imx9_ep_s *)ep; + struct imx9_usb_s *priv = privep->dev; + irqstate_t flags; + + /* STALL or RESUME the endpoint */ + + flags = enter_critical_section(); + usbtrace(resume ? TRACE_EPRESUME : TRACE_EPSTALL, privep->epphy); + + uint32_t offs = IMX9_USBDEV_ENDPTCTRL_OFFSET(privep->epphy >> 1); + uint32_t ctrl_xs = IMX9_EPPHYIN(privep->epphy) ? + USBDEV_ENDPTCTRL_TXS : USBDEV_ENDPTCTRL_RXS; + uint32_t ctrl_xr = IMX9_EPPHYIN(privep->epphy) ? + USBDEV_ENDPTCTRL_TXR : USBDEV_ENDPTCTRL_RXR; + + if (resume) + { + privep->stalled = false; + + /* Clear stall and reset the data toggle */ + + imx9_modifyreg(priv, offs, ctrl_xs | ctrl_xr, ctrl_xr); + } + else + { + privep->stalled = true; + + imx9_modifyreg(priv, offs, 0, ctrl_xs); + } + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Device operations + ****************************************************************************/ + +/**************************************************************************** + * Name: imx9_allocep + * + * 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 *imx9_allocep(struct usbdev_s *dev, + uint8_t eplog, + bool in, uint8_t eptype) +{ + struct imx9_usb_s *priv = (struct imx9_usb_s *)dev; + uint32_t epset = IMX9_EPALLSET & ~IMX9_EPCTRLSET; + irqstate_t flags; + int epndx = 0; + + usbtrace(TRACE_DEVALLOCEP, (uint16_t)eplog); + + /* Ignore any direction bits in the logical address */ + + eplog = USB_EPNO(eplog); + + /* A logical address of 0 means that any endpoint will do */ + + if (eplog > 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 (eplog >= IMX9_NLOGENDPOINTS) + { + usbtrace(TRACE_DEVERROR(IMX9_TRACEERR_BADEPNO), (uint16_t)eplog); + return NULL; + } + + /* Convert the logical address to a physical OUT endpoint address and + * remove all of the candidate endpoints from the bitset except for the + * the IN/OUT pair for this logical address. + */ + + epset &= 3 << (eplog << 1); + } + + /* Get the subset matching the requested direction */ + + if (in) + { + epset &= IMX9_EPINSET; + } + else + { + epset &= IMX9_EPOUTSET; + } + + /* Get the subset matching the requested type */ + + switch (eptype) + { + case USB_EP_ATTR_XFER_INT: /* Interrupt endpoint */ + epset &= IMX9_EPINTRSET; + break; + + case USB_EP_ATTR_XFER_BULK: /* Bulk endpoint */ + epset &= IMX9_EPBULKSET; + break; + + case USB_EP_ATTR_XFER_ISOC: /* Isochronous endpoint */ + epset &= IMX9_EPISOCSET; + break; + + case USB_EP_ATTR_XFER_CONTROL: /* Control endpoint -- not a valid choice */ + default: + usbtrace(TRACE_DEVERROR(IMX9_TRACEERR_BADEPTYPE), (uint16_t)eptype); + return NULL; + } + + /* Is the resulting endpoint supported by the IMX9? */ + + if (epset) + { + /* Yes.. now see if any of the request endpoints are available */ + + flags = enter_critical_section(); + epset &= priv->epavail; + if (epset) + { + /* Select the lowest bit in the set of matching, available + * endpoints + */ + + for (epndx = 2; epndx < IMX9_NPHYSENDPOINTS; epndx++) + { + uint32_t bit = 1 << epndx; + if ((epset & bit) != 0) + { + /* Mark endpoint no longer available */ + + priv->epavail &= ~bit; + leave_critical_section(flags); + + /* And return the pointer to the standard endpoint + * structure + */ + + return &priv->eplist[epndx].ep; + } + } + + /* Shouldn't get here */ + } + + leave_critical_section(flags); + } + + usbtrace(TRACE_DEVERROR(IMX9_TRACEERR_NOEP), (uint16_t)eplog); + return NULL; +} + +/**************************************************************************** + * Name: imx9_freeep + * + * Description: + * Free the previously allocated endpoint + * + ****************************************************************************/ + +static void imx9_freeep(struct usbdev_s *dev, + struct usbdev_ep_s *ep) +{ + struct imx9_usb_s *priv = (struct imx9_usb_s *)dev; + struct imx9_ep_s *privep = (struct imx9_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 |= (1 << privep->epphy); + leave_critical_section(flags); + } +} + +/**************************************************************************** + * Name: imx9_getframe + * + * Description: + * Returns the current frame number + * + ****************************************************************************/ + +static int imx9_getframe(struct usbdev_s *dev) +{ + struct imx9_usb_s *priv = (struct imx9_usb_s *)dev; + +#ifdef CONFIG_IMX9_USB_FRAME_INTERRUPT + /* Return last valid value of SOF read by the interrupt handler */ + + usbtrace(TRACE_DEVGETFRAME, (uint16_t)priv->sof); + return priv->sof; +#else + uint32_t frindex = imx9_getreg(priv, IMX9_USBDEV_FRINDEX_OFFSET); + uint16_t frame_num = + (frindex & USBDEV_FRINDEX_LFN_MASK) >> USBDEV_FRINDEX_LFN_SHIFT; + + /* Return the last frame number detected by the hardware */ + + usbtrace(TRACE_DEVGETFRAME, frame_num); + + return (int)(frame_num); +#endif +} + +/**************************************************************************** + * Name: imx9_wakeup + * + * Description: + * Tries to wake up the host connected to this device + * + ****************************************************************************/ + +static int imx9_wakeup(struct usbdev_s *dev) +{ + irqstate_t flags; + struct imx9_usb_s *priv = (struct imx9_usb_s *)dev; + + usbtrace(TRACE_DEVWAKEUP, 0); + + flags = enter_critical_section(); + imx9_modifyreg(priv, IMX9_USBDEV_PORTSC1_OFFSET, 0, USBDEV_PRTSC1_FPR); + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: imx9_selfpowered + * + * Description: + * Sets/clears the device selfpowered feature + * + ****************************************************************************/ + +static int imx9_selfpowered(struct usbdev_s *dev, bool selfpowered) +{ + struct imx9_usb_s *priv = (struct imx9_usb_s *)dev; + + usbtrace(TRACE_DEVSELFPOWERED, (uint16_t)selfpowered); + +#ifdef CONFIG_DEBUG_FEATURES + if (!dev) + { + usbtrace(TRACE_DEVERROR(IMX9_TRACEERR_INVALIDPARMS), 0); + return -ENODEV; + } +#endif + + priv->selfpowered = selfpowered; + return OK; +} + +/**************************************************************************** + * Name: imx9_pullup + * + * Description: + * Software-controlled connect to/disconnect from USB host + * + ****************************************************************************/ + +static int imx9_pullup(struct usbdev_s *dev, bool enable) +{ + struct imx9_usb_s *priv = (struct imx9_usb_s *)dev; + + usbtrace(TRACE_DEVPULLUP, (uint16_t)enable); + + irqstate_t flags = enter_critical_section(); + if (enable) + { + imx9_modifyreg(priv, IMX9_USBDEV_USBCMD_OFFSET, 0, USBDEV_USBCMD_RS); + +#ifdef CONFIG_IMX9_USB0DEV_NOVBUS + /* Create a 'false' power event on the USB port so the MAC connects */ + + imx9_modifyreg(priv, IMX9_USBOTG_OTGSC_OFFSET, USBOTG_OTGSC_VD, 0); + imx9_modifyreg(priv, IMX9_USBOTG_OTGSC_OFFSET, 0, USBOTG_OTGSC_VC); +#endif + } + else + { + imx9_modifyreg(priv, IMX9_USBDEV_USBCMD_OFFSET, USBDEV_USBCMD_RS, 0); + } + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imx9_vbus_detect + * + * Description: + * Read the VBUS state from the USB OTG controller. This can be used + * to poll the VBUS state + * + * Input Parameters: + * id: IMX9_USBC1 or IMX9_USBC2 + * + * Returned Value: + * true if VBUS is valid; false otherwise + * + ****************************************************************************/ + +bool imx9_vbus_detect(imx9_usb_id_t id) +{ + int i; + struct imx9_usb_s *priv; + uint32_t otgsc = 0; + + /* Find the correct device to which the driver is bound to */ + + for (i = 0; i < n_usbdevs; i++) + { + if (id == g_usbdev[i].id) + { + break; + } + } + + if (i < n_usbdevs) + { + priv = &g_usbdev[i]; + otgsc = imx9_getreg(priv, IMX9_USBOTG_OTGSC_OFFSET); + } + + return (otgsc & USBOTG_OTGSC_AVV) != 0; +} + +/**************************************************************************** + * Name: arm64_usbinitialize + * + * Description: + * Initialize USB hardware. + * + * Assumptions: + * - This function is called very early in the initialization sequence + * + ****************************************************************************/ + +void arm64_usbinitialize(void) +{ + /* For now, this driver supports just one usb device, either + * USBC1 or USBC2. The configured one is in g_usbdev[0]. + */ + + struct imx9_usb_s *priv = &g_usbdev[0]; + int i; + irqstate_t flags; + + flags = enter_critical_section(); + + /* Initialize the device state structure */ + + priv->usbdev.ops = &g_devops; + priv->usbdev.ep0 = &priv->eplist[IMX9_EP0_IN].ep; + priv->epavail = IMX9_EPALLSET & ~IMX9_EPCTRLSET; + + /* Initialize the endpoint list */ + + for (i = 0; i < IMX9_NPHYSENDPOINTS; i++) + { + uint32_t bit = 1 << i; + + /* Set endpoint operations, reference to driver structure and + * the physical endpoint number (which is just the index to the + * endpoint). + */ + + priv->eplist[i].ep.ops = &g_epops; + priv->eplist[i].dev = priv; + + /* The index, i, is the physical endpoint address; Map this + * to a logical endpoint address usable by the class driver. + */ + + priv->eplist[i].epphy = i; + if (IMX9_EPPHYIN(i)) + { + priv->eplist[i].ep.eplog = IMX9_EPPHYIN2LOG(i); + } + else + { + priv->eplist[i].ep.eplog = IMX9_EPPHYOUT2LOG(i); + } + + /* The maximum packet size may depend on the type of endpoint */ + + if ((IMX9_EPCTRLSET & bit) != 0) + { + priv->eplist[i].ep.maxpacket = IMX9_EP0MAXPACKET; + } + else if ((IMX9_EPINTRSET & bit) != 0) + { + priv->eplist[i].ep.maxpacket = IMX9_INTRMAXPACKET; + } + else if ((IMX9_EPBULKSET & bit) != 0) + { + priv->eplist[i].ep.maxpacket = IMX9_BULKMAXPACKET; + } + else /* if ((IMX9_EPISOCSET & bit) != 0) */ + { + priv->eplist[i].ep.maxpacket = IMX9_ISOCMAXPACKET; + } + } + + /* Clock gate on */ + + imx9_ccm_gate_on(CCM_LPCG_USB_CONTROLLER, true); + + /* Disable USB interrupts */ + + imx9_putreg(priv, IMX9_USBDEV_USBINTR_OFFSET, 0); + + /* Soft reset PHY and enable clock - not needed for on-chip USB2 phy */ + + /* Disconnect device */ + + imx9_pullup(&priv->usbdev, false); + + /* Reset the controller */ + + imx9_modifyreg(priv, IMX9_USBDEV_USBCMD_OFFSET, 0, USBDEV_USBCMD_RST); + while (imx9_getreg(priv, IMX9_USBDEV_USBCMD_OFFSET) & USBDEV_USBCMD_RST); + + /* Power up the PHY - not needed for on-chip USB2 phy */ + + /* Program the controller to be the USB device controller */ + + imx9_putreg(priv, IMX9_USBDEV_USBMODE_OFFSET, + USBDEV_USBMODE_SDIS | USBDEV_USBMODE_SLOM | + USBDEV_USBMODE_CM_DEVICE); + + /* Attach USB controller interrupt handler */ + + irq_attach(IMX9_IRQ_USB1 + priv->id, imx9_usbinterrupt, priv); + up_enable_irq(IMX9_IRQ_USB1 + priv->id); + + leave_critical_section(flags); + + /* Reset/Re-initialize the USB hardware */ + + imx9_usbreset(priv); +} + +/**************************************************************************** + * Name: arm_usbuninitialize + ****************************************************************************/ + +void arm64_usbuninitialize(void) +{ + struct imx9_usb_s *priv = &g_usbdev[0]; + irqstate_t flags; + + usbtrace(TRACE_DEVUNINIT, 0); + + if (priv->driver) + { + usbtrace(TRACE_DEVERROR(IMX9_TRACEERR_DRIVERREGISTERED), 0); + usbdev_unregister(priv->driver); + } + + flags = enter_critical_section(); + + /* Disconnect device */ + + imx9_pullup(&priv->usbdev, false); + priv->usbdev.speed = USB_SPEED_UNKNOWN; + + /* Disable and detach IRQs */ + + up_disable_irq(IMX9_IRQ_USB1 + priv->id); + irq_detach(IMX9_IRQ_USB1 + priv->id); + + /* Reset the controller */ + + imx9_modifyreg(priv, IMX9_USBDEV_USBCMD_OFFSET, 0, USBDEV_USBCMD_RST); + while (imx9_getreg(priv, IMX9_USBDEV_USBCMD_OFFSET) & USBDEV_USBCMD_RST); + + /* Turn off USB power and clocking */ + + /* Power down the PHY */ + + /* Clock gate off - NOTE: this turns off the clock from both controllers. + * Add reference counting if expanding this to support several ones. + */ + + imx9_ccm_gate_on(CCM_LPCG_USB_CONTROLLER, false); + + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: usbdev_register + * + * Description: + * Register a USB device class driver. The class driver's bind() method + * will be called to bind it to a USB device driver. + * + ****************************************************************************/ + +int usbdev_register(struct usbdevclass_driver_s *driver) +{ + int ret; + + usbtrace(TRACE_DEVREGISTER, 0); + +#ifdef CONFIG_DEBUG_FEATURES + if (!driver || !driver->ops->bind || !driver->ops->unbind || + !driver->ops->disconnect || !driver->ops->setup) + { + usbtrace(TRACE_DEVERROR(IMX9_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } + + if (g_usbdev[0].driver) + { + usbtrace(TRACE_DEVERROR(IMX9_TRACEERR_DRIVER), 0); + return -EBUSY; + } +#endif + + /* First hook up the driver */ + + g_usbdev[0].driver = driver; + + /* Then bind the class driver */ + + ret = CLASS_BIND(driver, &g_usbdev[0].usbdev); + if (ret) + { + usbtrace(TRACE_DEVERROR(IMX9_TRACEERR_BINDFAILED), (uint16_t)-ret); + g_usbdev[0].driver = NULL; + } + else + { + /* Enable USB controller interrupts */ + + up_enable_irq(IMX9_IRQ_USB1 + g_usbdev[0].id); + } + + 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) +{ + int i; + + usbtrace(TRACE_DEVUNREGISTER, 0); + + /* Find the correct device to which the driver is bound to */ + + for (i = 0; i < n_usbdevs; i++) + { + if (driver == g_usbdev[i].driver) + { + break; + } + } + + if (i == n_usbdevs) + { + return -EINVAL; + } + + /* Unbind the class driver */ + + CLASS_UNBIND(driver, &g_usbdev[i].usbdev); + + /* Disable USB controller interrupts */ + + up_disable_irq(IMX9_IRQ_USB1 + g_usbdev[i].id); + + /* Unhook the driver */ + + g_usbdev[i].driver = NULL; + + return OK; +} + diff --git a/arch/arm64/src/imx9/imx9_usbdev.h b/arch/arm64/src/imx9/imx9_usbdev.h new file mode 100644 index 0000000000000..b18008c3545b5 --- /dev/null +++ b/arch/arm64/src/imx9/imx9_usbdev.h @@ -0,0 +1,86 @@ +/**************************************************************************** + * arch/arm64/src/imx9/imx9_usbdev.h + * + * 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_ARM64_SRC_IMX9_IMX9_USBDEV_H +#define __ARCH_ARM64_SRC_IMX9_IMX9_USBDEV_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef enum +{ + IMX9_USBC1 = 0, + IMX9_USBC2 = 1, +} imx9_usb_id_t; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: imx9_vbus_detect + * + * Description: + * Read the VBUS state from the USB OTG controller. This can be used + * to poll the VBUS state + * + * Input Parameters: + * id: IMX9_USBC1 or IMX9_USBC2 + * + * Returned Value: + * true if VBUS is valid; false otherwise + * + ****************************************************************************/ + +bool imx9_vbus_detect(imx9_usb_id_t id); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM64_SRC_IMX9_IMX9_USBDEV_H */