From b1d460802634f90ee21d7a179d2c782c498b92e1 Mon Sep 17 00:00:00 2001 From: wgzAIIT <820906721@qq.com> Date: Thu, 31 Mar 2022 09:53:16 +0800 Subject: [PATCH 1/7] update readme.md --- .../aiit_board/xidatong/readme.md | 9 +++++---- Ubiquitous/Nuttx_Fusion_XiUOS/readme.md | 13 +++++++------ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/readme.md b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/readme.md index 4dd1f3e84..01a71f401 100644 --- a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/readme.md +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/readme.md @@ -137,8 +137,9 @@ source build.sh 执行完毕会自动进入./Ubiquitous/Nuttx_Fusion_XiUOS/nuttx下,继续执行 -sudo ./tools/configure.sh xidatong:nsh -sudo make menuconfig +./tools/configure.sh xidatong:nsh +make menuconfig +视情况而定,如果需要前面加sudo ``` 2.在menuconfig界面配置需要关闭和开启的功能,按回车键进入下级菜单,按Y键选中需要开启的功能,按N键选中需要关闭的功能,配置结束后保存并退出(本例旨在演示简单的输出例程,所以没有需要配置的选项,双击快捷键ESC退出配置) @@ -152,9 +153,9 @@ sudo make menuconfig 3.继续执行以下命令,进行编译 ```shell -sudo make +make 或 -sudo make -j8 +make -j8 ``` make时加上V=1参数可以看到较为详细的编译信息,但是编译过程会比较慢。 diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/readme.md b/Ubiquitous/Nuttx_Fusion_XiUOS/readme.md index 9f1567c1b..0ff4065c1 100644 --- a/Ubiquitous/Nuttx_Fusion_XiUOS/readme.md +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/readme.md @@ -266,23 +266,22 @@ git clone https://git.trustie.net/xuos/kconfig-frontends.git #### 在Nuttx\app_match_nuttx目录下执行 ```shell -chmod +x build.sh - source build.sh ``` #### 执行完毕会跳转到Nuttx\nuttx目录,执行 ```shell -sudo ./tools/configure.sh stm32f4discovery:nsh (应用内核一起编译) +./tools/configure.sh stm32f4discovery:nsh (应用内核一起编译) -sudo ./tools/configure.sh stm32f4discovery:kostest (应用内核分开编译) +./tools/configure.sh stm32f4discovery:kostest (应用内核分开编译) +视情况而定,如果需要前面加sudo ``` #### 然后执行 ```shell -sudo make menuconfig +make menuconfig ``` ##### 开启Nuttx Support CLOCK_MONOTONIC @@ -328,7 +327,9 @@ sudo make menuconfig #### 在当前目录执行编译 ```shell -sudo make -j8 +make +或 +make -j8 ``` make时加上V=1参数可以看到较为详细的编译信息,但是编译过程会比较慢。最后在nuttx下会编译出一个nuttx.bin文件(应用内核一起编译) From 82f6abb842f0f40e842d8786953a87ab2e3075ae Mon Sep 17 00:00:00 2001 From: wgzAIIT <820906721@qq.com> Date: Fri, 1 Apr 2022 09:49:10 +0800 Subject: [PATCH 2/7] support usb on xidatong --- .../aiit_board/xidatong/include/board.h | 1 + .../aiit_board/xidatong/src/imxrt_usbhost.c | 2 +- .../arm/src/imxrt/hardware/imxrt_usbotg.h | 739 +++ .../arm/src/imxrt/hardware/imxrt_usbphy.h | 82 + .../hardware/rt105x/imxrt105x_memorymap.h | 311 + .../arch/arm/src/imxrt/imxrt_clockconfig.c | 698 +++ .../nuttx/arch/arm/src/imxrt/imxrt_ehci.c | 5452 +++++++++++++++++ .../nuttx/arch/arm/src/imxrt/imxrt_lowputc.c | 603 ++ .../nuttx/arch/arm/src/imxrt/imxrt_lpi2c.c | 1997 ++++++ .../nuttx/arch/arm/src/imxrt/imxrt_lpspi.c | 1706 ++++++ .../nuttx/arch/arm/src/imxrt/imxrt_usbdev.c | 3061 +++++++++ 11 files changed, 14651 insertions(+), 1 deletion(-) create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbotg.h create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbphy.h create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/rt105x/imxrt105x_memorymap.h create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_clockconfig.c create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_ehci.c create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lowputc.c create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpi2c.c create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpspi.c create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_usbdev.c diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/include/board.h b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/include/board.h index e6021288c..49181683a 100644 --- a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/include/board.h +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/include/board.h @@ -128,6 +128,7 @@ #define IMXRT_SYS_PLL_SELECT CCM_ANALOG_PLL_SYS_DIV_SELECT_22 #define IMXRT_USB1_PLL_DIV_SELECT CCM_ANALOG_PLL_USB1_DIV_SELECT_20 +#define IMXRT_USB2_PLL_DIV_SELECT CCM_ANALOG_PLL_USB2_DIV_SELECT_20 // #define BOARD_CPU_FREQUENCY \ // (BOARD_XTAL_FREQUENCY * (IMXRT_ARM_PLL_DIV_SELECT / 2)) / IMXRT_ARM_PODF_DIVIDER diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/src/imxrt_usbhost.c b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/src/imxrt_usbhost.c index 335a3f687..46cc10e63 100644 --- a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/src/imxrt_usbhost.c +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/src/imxrt_usbhost.c @@ -138,7 +138,7 @@ int imxrt_usbhost_initialize(void) /* Make sure we don't accidentally switch on USB bus power */ - *((uint32_t *)IMXRT_USBNC_USB_OTG1_CTRL) = USBNC_PWR_POL; + *((uint32_t *)IMXRT_USBNC_USB_OTG2_CTRL) = USBNC_PWR_POL; *((uint32_t *)0x400d9030) = (1 << 21); *((uint32_t *)0x400d9000) = 0; diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbotg.h b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbotg.h new file mode 100644 index 000000000..3d83733ce --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbotg.h @@ -0,0 +1,739 @@ +/**************************************************************************** + * arch/arm/src/imxrt/hardware/imxrt_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_ARM_SRC_IMXRT_HARDWARE_IMXRT_USBOTG_H +#define __ARCH_ARM_SRC_IMXRT_HARDWARE_IMXRT_USBOTG_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define IMXRT_EHCI_NRHPORT 1 /* There is only a single root hub port */ + +/* USBOTG register offsets (with respect to IMXRT_USB_BASE) *****************/ + + /* 0x000 - 0x0ff: Reserved */ + +/* Device/host capability registers */ + +#define IMXRT_USBOTG_HCCR_OFFSET 0x100 /* Offset to EHCI Host Controller Capabiliy registers */ +#define IMXRT_USBOTG_CAPLENGTH_OFFSET 0x100 /* Capability register length (8-bit) */ +#define IMXRT_USBHOST_HCIVERSION_OFFSET 0x102 /* Host interface version number (16-bit) */ +#define IMXRT_USBHOST_HCSPARAMS_OFFSET 0x104 /* Host controller structural parameters */ +#define IMXRT_USBHOST_HCCPARAMS_OFFSET 0x108 /* Host controller capability parameters */ +#define IMXRT_USBDEV_DCIVERSION_OFFSET 0x120 /* Device interface version number */ +#define IMXRT_USBDEV_DCCPARAMS_OFFSET 0x124 /* Device controller capability parameters */ + +/* Device/host/OTG operational registers */ + +#define IMXRT_USBOTG_HCOR_OFFSET 0x140 /* Offset to EHCI Host Controller Operational Registers */ +#define IMXRT_USBOTG_USBCMD_OFFSET 0x140 /* USB command (both) */ +#define IMXRT_USBOTG_USBSTS_OFFSET 0x144 /* USB status (both) */ +#define IMXRT_USBOTG_USBINTR_OFFSET 0x148 /* USB interrupt enable (both) */ +#define IMXRT_USBOTG_FRINDEX_OFFSET 0x14c /* USB frame index (both) */ + /* EHCI 4G Segment Selector (not supported) */ +#define IMXRT_USBOTG_PERIODICLIST_OFFSET 0x154 /* Frame list base address (host) */ +#define IMXRT_USBOTG_DEVICEADDR_OFFSET 0x154 /* USB device address (device) */ +#define IMXRT_USBOTG_ASYNCLISTADDR_OFFSET 0x158 /* Next asynchronous list address (host) */ +#define IMXRT_USBOTG_ENDPOINTLIST_OFFSET 0x158 /* Address of endpoint list in memory (device) */ +#define IMXRT_USBOTG_TTCTRL_OFFSET 0x15c /* Asynchronous buffer status for embedded TT (host) */ +#define IMXRT_USBOTG_BURSTSIZE_OFFSET 0x160 /* Programmable burst size (both) */ +#define IMXRT_USBOTG_TXFILLTUNING_OFFSET 0x164 /* Host transmit pre-buffer packet tuning (host) */ +#define IMXRT_USBOTG_BINTERVAL_OFFSET 0x174 /* Length of virtual frame (both) */ +#define IMXRT_USBOTG_ENDPTNAK_OFFSET 0x178 /* Endpoint NAK (device) */ +#define IMXRT_USBOTG_ENDPTNAKEN_OFFSET 0x17c /* Endpoint NAK Enable (device) */ +#define IMXRT_USBOTG_CONFIGFLAG_OFFSET 0x180 /* Configured flag register (not used in lpc313x) */ +#define IMXRT_USBOTG_PORTSC1_OFFSET 0x184 /* Port status/control 1 (both) */ +#define IMXRT_USBOTG_OTGSC_OFFSET 0x1a4 /* OTG status and control (otg) */ +#define IMXRT_USBOTG_USBMODE_OFFSET 0x1a8 /* USB device mode (both) */ + +#define IMXRT_USBDEV_USBCMD_OFFSET 0x140 /* USB command (both) */ +#define IMXRT_USBDEV_USBSTS_OFFSET 0x144 /* USB status (both) */ +#define IMXRT_USBDEV_USBINTR_OFFSET 0x148 /* USB interrupt enable (both) */ +#define IMXRT_USBDEV_FRINDEX_OFFSET 0x14c /* USB frame index (both) */ +#define IMXRT_USBDEV_DEVICEADDR_OFFSET 0x154 /* USB device address (device) */ +#define IMXRT_USBDEV_ENDPOINTLIST_OFFSET 0x158 /* Address of endpoint list in memory (device) */ +#define IMXRT_USBDEV_BURSTSIZE_OFFSET 0x160 /* Programmable burst size (both) */ +#define IMXRT_USBDEV_BINTERVAL_OFFSET 0x174 /* Length of virtual frame (both) */ +#define IMXRT_USBDEV_ENDPTNAK_OFFSET 0x178 /* Endpoint NAK (device) */ +#define IMXRT_USBDEV_ENDPTNAKEN_OFFSET 0x17c /* Endpoint NAK Enable (device) */ +#define IMXRT_USBDEV_PORTSC1_OFFSET 0x184 /* Port status/control 1 (both) */ +#define IMXRT_USBDEV_USBMODE_OFFSET 0x1a8 /* USB device mode (both) */ + +#define IMXRT_USBHOST_USBCMD_OFFSET 0x140 /* USB command (both) */ +#define IMXRT_USBHOST_USBSTS_OFFSET 0x144 /* USB status (both) */ +#define IMXRT_USBHOST_USBINTR_OFFSET 0x148 /* USB interrupt enable (both) */ +#define IMXRT_USBHOST_FRINDEX_OFFSET 0x14c /* USB frame index (both) */ +#define IMXRT_USBHOST_PERIODICLIST_OFFSET 0x154 /* Frame list base address (host) */ +#define IMXRT_USBHOST_ASYNCLISTADDR_OFFSET 0x158 /* Next asynchronous list address (host) */ +#define IMXRT_USBHOST_TTCTRL_OFFSET 0x15c /* Asynchronous buffer status for embedded TT (host) */ +#define IMXRT_USBHOST_BURSTSIZE_OFFSET 0x160 /* Programmable burst size (both) */ +#define IMXRT_USBHOST_TXFILLTUNING_OFFSET 0x164 /* Host transmit pre-buffer packet tuning (host) */ +#define IMXRT_USBHOST_BINTERVAL_OFFSET 0x174 /* Length of virtual frame (both) */ +#define IMXRT_USBHOST_PORTSC1_OFFSET 0x184 /* Port status/control 1 (both) */ +#define IMXRT_USBHOST_USBMODE_OFFSET 0x1a8 /* USB device mode (both) */ + +/* Device endpoint registers */ + +#define IMXRT_USBDEV_ENDPTSETUPSTAT_OFFSET 0x1ac /* Endpoint setup status */ +#define IMXRT_USBDEV_ENDPTPRIME_OFFSET 0x1b0 /* Endpoint initialization */ +#define IMXRT_USBDEV_ENDPTFLUSH_OFFSET 0x1b4 /* Endpoint de-initialization */ +#define IMXRT_USBDEV_ENDPTSTATUS_OFFSET 0x1b8 /* Endpoint status */ +#define IMXRT_USBDEV_ENDPTCOMPLETE_OFFSET 0x1bc /* Endpoint complete */ + +#define IMXRT_USBDEV_ENDPTCTRL_OFFSET(n) (IMXRT_USBDEV_ENDPTCTRL0_OFFSET + ((n) * 4)) +#define IMXRT_USBDEV_ENDPTCTRL0_OFFSET 0x1c0 /* Endpoint control 0 */ +#define IMXRT_USBDEV_ENDPTCTRL1_OFFSET 0x1c4 /* Endpoint control 1 */ +#define IMXRT_USBDEV_ENDPTCTRL2_OFFSET 0x1c8 /* Endpoint control 2 */ +#define IMXRT_USBDEV_ENDPTCTRL3_OFFSET 0x1cc /* Endpoint control 3 */ +#define IMXRT_USBDEV_ENDPTCTRL4_OFFSET 0x1d0 /* Endpoint control 4 */ +#define IMXRT_USBDEV_ENDPTCTRL5_OFFSET 0x1d4 /* Endpoint control 5 */ +#define IMXRT_USBDEV_ENDPTCTRL6_OFFSET 0x1d8 /* Endpoint control 6 */ +#define IMXRT_USBDEV_ENDPTCTRL7_OFFSET 0x1dc /* Endpoint control 7 */ + +/* USB Non-core memory map & register definition */ + +#define IMXRT_USBNC_USB_OTG1_CTRL_OFFSET 0x0800 /* OTG1 Control Register */ +#define IMXRT_USBNC_USB_OTG2_CTRL_OFFSET 0x0800 /* OTG2 Control Register */ +#define IMXRT_USBNC_USB_OTG1_PHY_CTRL_0_OFFSET 0x0818 /* OTG1 Phy Control Register */ +#define IMXRT_USBNC_USB_OTG2_PHY_CTRL_0_OFFSET 0x0818 /* OTG2 Phy Control Register */ + +/* USBOTG register (virtual) addresses **************************************/ + +/* Device/host capability registers */ + +#define IMXRT_USBOTG_HCCR_BASE (IMXRT_USB_BASE + IMXRT_USBOTG_HCCR_OFFSET) +#define IMXRT_USBOTG_CAPLENGTH (IMXRT_USB_BASE + IMXRT_USBOTG_CAPLENGTH_OFFSET) +#define IMXRT_USBHOST_HCIVERSION (IMXRT_USB_BASE + IMXRT_USBHOST_HCIVERSION_OFFSET) +#define IMXRT_USBHOST_HCSPARAMS (IMXRT_USB_BASE + IMXRT_USBHOST_HCSPARAMS_OFFSET) +#define IMXRT_USBHOST_HCCPARAMS (IMXRT_USB_BASE + IMXRT_USBHOST_HCCPARAMS_OFFSET) +#define IMXRT_USBDEV_DCIVERSION (IMXRT_USB_BASE + IMXRT_USBDEV_DCIVERSION_OFFSET) +#define IMXRT_USBDEV_DCCPARAMS (IMXRT_USB_BASE + IMXRT_USBDEV_DCCPARAMS_OFFSET) + +/* Device/host operational registers */ + +#define IMXRT_USBOTG_HCOR_BASE (IMXRT_USB_BASE + IMXRT_USBOTG_HCOR_OFFSET) +#define IMXRT_USBOTG_USBCMD (IMXRT_USB_BASE + IMXRT_USBOTG_USBCMD_OFFSET) +#define IMXRT_USBOTG_USBSTS (IMXRT_USB_BASE + IMXRT_USBOTG_USBSTS_OFFSET) +#define IMXRT_USBOTG_USBINTR (IMXRT_USB_BASE + IMXRT_USBOTG_USBINTR_OFFSET) +#define IMXRT_USBOTG_FRINDEX (IMXRT_USB_BASE + IMXRT_USBOTG_FRINDEX_OFFSET) +#define IMXRT_USBOTG_PERIODICLIST (IMXRT_USB_BASE + IMXRT_USBOTG_PERIODICLIST_OFFSET) +#define IMXRT_USBOTG_DEVICEADDR (IMXRT_USB_BASE + IMXRT_USBOTG_DEVICEADDR_OFFSET) +#define IMXRT_USBOTG_ASYNCLISTADDR (IMXRT_USB_BASE + IMXRT_USBOTG_ASYNCLISTADDR_OFFSET) +#define IMXRT_USBOTG_ENDPOINTLIST (IMXRT_USB_BASE + IMXRT_USBOTG_ENDPOINTLIST_OFFSET) +#define IMXRT_USBOTG_TTCTRL (IMXRT_USB_BASE + IMXRT_USBOTG_TTCTRL_OFFSET) +#define IMXRT_USBOTG_BURSTSIZE (IMXRT_USB_BASE + IMXRT_USBOTG_BURSTSIZE_OFFSET) +#define IMXRT_USBOTG_TXFILLTUNING (IMXRT_USB_BASE + IMXRT_USBOTG_TXFILLTUNING_OFFSET) +#define IMXRT_USBOTG_BINTERVAL (IMXRT_USB_BASE + IMXRT_USBOTG_BINTERVAL_OFFSET) +#define IMXRT_USBOTG_ENDPTNAK (IMXRT_USB_BASE + IMXRT_USBOTG_ENDPTNAK_OFFSET) +#define IMXRT_USBOTG_ENDPTNAKEN (IMXRT_USB_BASE + IMXRT_USBOTG_ENDPTNAKEN_OFFSET) +#define IMXRT_USBOTG_PORTSC1 (IMXRT_USB_BASE + IMXRT_USBOTG_PORTSC1_OFFSET) +#define IMXRT_USBOTG_OTGSC (IMXRT_USB_BASE + IMXRT_USBOTG_OTGSC_OFFSET) +#define IMXRT_USBOTG_USBMODE (IMXRT_USB_BASE + IMXRT_USBOTG_USBMODE_OFFSET) + +#define IMXRT_USBDEV_USBCMD (IMXRT_USB_BASE + IMXRT_USBDEV_USBCMD_OFFSET) +#define IMXRT_USBDEV_USBSTS (IMXRT_USB_BASE + IMXRT_USBDEV_USBSTS_OFFSET) +#define IMXRT_USBDEV_USBINTR (IMXRT_USB_BASE + IMXRT_USBDEV_USBINTR_OFFSET) +#define IMXRT_USBDEV_FRINDEX (IMXRT_USB_BASE + IMXRT_USBDEV_FRINDEX_OFFSET) +#define IMXRT_USBDEV_DEVICEADDR (IMXRT_USB_BASE + IMXRT_USBDEV_DEVICEADDR_OFFSET) +#define IMXRT_USBDEV_ENDPOINTLIST (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPOINTLIST_OFFSET) +#define IMXRT_USBDEV_BURSTSIZE (IMXRT_USB_BASE + IMXRT_USBDEV_BURSTSIZE_OFFSET) +#define IMXRT_USBDEV_BINTERVAL (IMXRT_USB_BASE + IMXRT_USBDEV_BINTERVAL_OFFSET) +#define IMXRT_USBDEV_ENDPTNAK (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTNAK_OFFSET) +#define IMXRT_USBDEV_ENDPTNAKEN (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTNAKEN_OFFSET) +#define IMXRT_USBDEV_PORTSC1 (IMXRT_USB_BASE + IMXRT_USBDEV_PORTSC1_OFFSET) +#define IMXRT_USBDEV_USBMODE (IMXRT_USB_BASE + IMXRT_USBDEV_USBMODE_OFFSET) + +#define IMXRT_USBHOST_USBCMD (IMXRT_USB_BASE + IMXRT_USBHOST_USBCMD_OFFSET) +#define IMXRT_USBHOST_USBSTS (IMXRT_USB_BASE + IMXRT_USBHOST_USBSTS_OFFSET) +#define IMXRT_USBHOST_USBINTR (IMXRT_USB_BASE + IMXRT_USBHOST_USBINTR_OFFSET) +#define IMXRT_USBHOST_FRINDEX (IMXRT_USB_BASE + IMXRT_USBHOST_FRINDEX_OFFSET) +#define IMXRT_USBHOST_PERIODICLIST (IMXRT_USB_BASE + IMXRT_USBHOST_PERIODICLIST_OFFSET) +#define IMXRT_USBHOST_ASYNCLISTADDR (IMXRT_USB_BASE + IMXRT_USBHOST_ASYNCLISTADDR_OFFSET) +#define IMXRT_USBHOST_TTCTRL (IMXRT_USB_BASE + IMXRT_USBHOST_TTCTRL_OFFSET) +#define IMXRT_USBHOST_BURSTSIZE (IMXRT_USB_BASE + IMXRT_USBHOST_BURSTSIZE_OFFSET) +#define IMXRT_USBHOST_TXFILLTUNING (IMXRT_USB_BASE + IMXRT_USBHOST_TXFILLTUNING_OFFSET) +#define IMXRT_USBHOST_BINTERVAL (IMXRT_USB_BASE + IMXRT_USBHOST_BINTERVAL_OFFSET) +#define IMXRT_USBHOST_PORTSC1 (IMXRT_USB_BASE + IMXRT_USBHOST_PORTSC1_OFFSET) +#define IMXRT_USBHOST_USBMODE (IMXRT_USB_BASE + IMXRT_USBHOST_USBMODE_OFFSET) + +/* Device endpoint registers */ + +#define IMXRT_USBDEV_ENDPTSETUPSTAT (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTSETUPSTAT_OFFSET) +#define IMXRT_USBDEV_ENDPTPRIME (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTPRIME_OFFSET) +#define IMXRT_USBDEV_ENDPTFLUSH (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTFLUSH_OFFSET) +#define IMXRT_USBDEV_ENDPTSTATUS (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTSTATUS_OFFSET) +#define IMXRT_USBDEV_ENDPTCOMPLETE (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCOMPLETE_OFFSET) + +#define IMXRT_USBDEV_ENDPTCTRL(n) (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCTRL_OFFSET(n)) +#define IMXRT_USBDEV_ENDPTCTRL0 (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCTRL0_OFFSET) +#define IMXRT_USBDEV_ENDPTCTRL1 (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCTRL1_OFFSET) +#define IMXRT_USBDEV_ENDPTCTRL2 (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCTRL2_OFFSET) +#define IMXRT_USBDEV_ENDPTCTRL3 (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCTRL3_OFFSET) +#define IMXRT_USBDEV_ENDPTCTRL4 (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCTRL4_OFFSET) +#define IMXRT_USBDEV_ENDPTCTRL5 (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCTRL5_OFFSET) +#define IMXRT_USBDEV_ENDPTCTRL6 (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCTRL6_OFFSET) +#define IMXRT_USBDEV_ENDPTCTRL7 (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCTRL7_OFFSET) + +/* Device non-core registers */ + +#define IMXRT_USBNC_USB_OTG1_CTRL (IMXRT_USB_BASE + IMXRT_USBNC_USB_OTG1_CTRL_OFFSET) +#define IMXRT_USBNC_USB_OTG1_PHY_CTRL_0 (IMXRT_USB_BASE + IMXRT_USBNC_USB_OTG1_PHY_CTRL_0_OFFSET) + +#define IMXRT_USBNC_USB_OTG2_CTRL (IMXRT_USB_BASE + IMXRT_USBNC_USB_OTG2_CTRL_OFFSET) +#define IMXRT_USBNC_USB_OTG2_PHY_CTRL_0 (IMXRT_USB_BASE + IMXRT_USBNC_USB_OTG2_PHY_CTRL_0_OFFSET) + +/* 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 (15 << 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 (15) /* Bits 12-15: Number of Companion Controller */ +#define USBHOST_HCSPARAMS_NCC_MASK (15 << USBHOST_HCSPARAMS_NCC_SHIFT) +#define USBHOST_HCSPARAMS_NPCC_SHIFT (8) /* Bits 8-11: Number of Ports per Companion Controller */ +#define USBHOST_HCSPARAMS_NPCC_MASK (15 << 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 (15 << USBHOST_HCSPARAMS_NPORTS_SHIFT) + +/* HCCPARAMS */ + +#define USBHOST_HCCPARAMS_EECP_SHIFT (8) /* Bits 8-15: EHCI Extended Capabilities Pointer */ +#define USBHOST_HCCPARAMS_EECP_MASK (255 << USBHOST_HCCPARAMS_EECP_SHIFT) +#define USBHOST_HCCPARAMS_IST_SHIFT (4) /* Bits 4-7: Isochronous Scheduling Threshold */ +#define USBHOST_HCCPARAMS_IST_MASK (15 << 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 (31 << 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 (255 << 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 (255 << 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 (3 << 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_UPIA (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 (7 << 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 (0x3c << 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_PERBASE_SHIFT (12) /* Bits 12-31: Base Address (Low) */ +#define USBHOST_PERIODICLIST_PERBASE_MASK (0x000fffff << USBHOST_PERIODICLIST_PERBASE_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 (0x001fffff << USBDEV_ENDPOINTLIST_EPBASE_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 TT Control register TTCTRL (address 0x1900015c) -- Host Mode */ + +#define USBHOST_TTCTRL_TTHA_SHIFT (24) /* Bits 24-30: Hub address */ +#define USBHOST_TTCTRL_TTHA_MASK (0x7f << USBHOST_TTCTRL_TTHA_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 (255 << USBDEV_BURSTSIZE_TXPBURST_SHIFT) +#define USBDEV_BURSTSIZE_RXPBURST_SHIFT (0) /* Bits 0-7: RXPBURST Programmable RX burst length */ +#define USBDEV_BURSTSIZE_RXPBURST_MASK (255 << USBDEV_BURSTSIZE_RXPBURST_SHIFT) + +#define USBHOST_BURSTSIZE_TXPBURST_SHIFT (8) /* Bits 8-15: Programmable TX burst length */ +#define USBHOST_BURSTSIZE_TXPBURST_MASK (255 << USBHOST_BURSTSIZE_TXPBURST_SHIFT) +#define USBHOST_BURSTSIZE_RXPBURST_SHIFT (0) /* Bits 0-7: RXPBURST Programmable RX burst length */ +#define USBHOST_BURSTSIZE_RXPBURST_MASK (255 << 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 (0x3c << 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 (0xff << USBHOST_TXFILLTUNING_SCHOH_SHIFT) + +/* USB BINTERVAL register BINTERVAL -- Device/Host Mode */ + +#define USBDEV_BINTERVAL_SHIFT (0) /* Bits 0-3: bInterval value */ +#define USBDEV_BINTERVAL_MASK (15 << USBDEV_BINTERVAL_SHIFT) + +#define USBHOST_BINTERVAL_SHIFT (0) /* Bits 0-3: bInterval value */ +#define USBHOST_BINTERVAL_MASK (15 << USBHOST_BINTERVAL_SHIFT) + +/* USB endpoint NAK register ENDPTNAK -- Device Mode */ + +#define USBDEV_ENDPTNAK_EPTN_SHIFT (16) /* Bits 16-19: Tx endpoint NAK */ +#define USBDEV_ENDPTNAK_EPTN_MASK (15 << USBDEV_ENDPTNAK_EPTN_SHIFT) +#define USBDEV_ENDPTNAK_EPRN_SHIFT (0) /* Bits 0-3: Rx endpoint NAK */ +#define USBDEV_ENDPTNAK_EPRN_MASK (15 << USBDEV_ENDPTNAK_EPRN_SHIFT) + +/* USB Endpoint NAK Enable register ENDPTNAKEN -- Device Mode */ + +#define USBDEV_ENDPTNAK_EPTNE_SHIFT (16) /* Bits 16-19: Tx endpoint NAK enable */ +#define USBDEV_ENDPTNAK_EPTNE_MASK (15 << USBDEV_ENDPTNAK_EPTNE_SHIFT) +#define USBDEV_ENDPTNAK_EPRNE_SHIFT (0) /* Bits 0-3: Rx endpoint NAK enable */ +#define USBDEV_ENDPTNAK_EPRNE_MASK (15 << USBDEV_ENDPTNAK_EPRNE_SHIFT) + +/* Port Status and Control register PRTSC1 -- Device Mode */ + +#define USBDEV_PRTSC1_PSPD_SHIFT (26) /* Bits 26-27: Port speed */ +#define USBDEV_PRTSC1_PSPD_MASK (3 << 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 (15 << 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 (3 << 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 (3 << 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 (15 << 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 (3 << 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 (3 << 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_HABA (1 << 7) /* Bit 7: Hardware assist B-disconnect to A-connect */ +#define USBOTG_OTGSC_HADP (1 << 6) /* Bit 6: Hardware assist data pulse */ +#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_HAAR (1 << 2) /* Bit 2: Hardware assist auto_reset */ +#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 (3 << 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 -- Device Mode */ + +#define USBHOST_USBMODE_VBPS (1 << 5) /* Bit 5: VBUS power select */ +#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 (3 << 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 (3 << 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 (3 << 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 (3 << 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 (3 << 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 */ + +/* Bits 0-6: + * Reserved + */ +#define USBNC_OVER_CUR_DIS (1 << 7) /* Bit 7: Disable Over current detection */ +#define USBNC_OVER_CUR_POL (1 << 8) /* Bit 8: Polarity of over current */ +#define USBNC_PWR_POL (1 << 9) /* Bit 9: Power polarity */ +#define USBNC_WIE (1<< 10) /* Bit 10: Wake up interrupt enable */ + /* Bit 11-13: Reserved */ +#define USBNC_WKUP_SW_EN (1 << 14) /* Bit 14: Software wake up enable */ +#define USBNC_WKUP_SW (1 << 15) /* Bit 15: Software wake up */ +#define USBNC_WKUP_ID_EN (1 << 16) /* Bit 16: Wakeup on ID change enable */ +#define USBNC_WKUP_VBUS_EN (1 << 17) /* Bit 17: Wakeup on VBUS change enable */ + /* Bit 18-28: Reserved */ +#define USBNC_WKUP_DPDM_EN (1 << 29) /* Bit 29: Wakeup on DPDM change enable */ + /* Bit 30: Reserved */ +#define USBNC_WIR (1 << 31) /* Bit 31: Wake up interrupt request */ + +#endif /* __ARCH_ARM_SRC_IMXRT_HARDWARE_IMXRT_USBOTG_H */ diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbphy.h b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbphy.h new file mode 100644 index 000000000..0628a19e2 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbphy.h @@ -0,0 +1,82 @@ +/**************************************************************************** + * arch/arm/src/imxrt/hardware/imxrt_usbphy.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_ARM_SRC_IMXRT_HARDWARE_IMXRT_USB_PHY_H +#define __ARCH_ARM_SRC_IMXRT_HARDWARE_IMXRT_USB_PHY_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include "hardware/imxrt_memorymap.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define IMXRT_USBPHY1_BASE_OFFSET 0x1000 /* USB PHY1 Base */ +#define IMXRT_USBPHY2_BASE_OFFSET 0x2000 /* USB PHY2 Base */ + +#define IMXRT_USBPHY1_BASE (IMXRT_ANATOP_BASE + IMXRT_USBPHY1_BASE_OFFSET) /* USB PHY1 Base */ +#define IMXRT_USBPHY2_BASE (IMXRT_ANATOP_BASE + IMXRT_USBPHY2_BASE_OFFSET) /* USB PHY2 Base */ + +/* Register Offsets *********************************************************/ + +#define IMXRT_USBPHY1_PWD_OFFSET 0x0000 /* USBPHY1 USB PHY1 Power-Down Register */ +#define IMXRT_USBPHY1_PWD_CLR_OFFSET 0x0008 /* USBPHY1 USB PHY1 Power-Down Register Clear */ +#define IMXRT_USBPHY1_CTRL_OFFSET 0x0030 /* USBPHY1 USB PHY1 General Control Register */ +#define IMXRT_USBPHY1_CTRL_CLR_OFFSET 0x0038 /* USBPHY1 USB PHY1 General Control Register Clear */ + +#define IMXRT_USBPHY2_PWD_OFFSET 0x0000 /* USBPHY2 USB PHY Power-Down Register */ +#define IMXRT_USBPHY2_PWD_CLR_OFFSET 0x0008 /* USBPHY2 USB PHY Power-Down Register Clear */ +#define IMXRT_USBPHY2_CTRL_OFFSET 0x0030 /* USBPHY2 USB PHY General Control Register */ +#define IMXRT_USBPHY2_CTRL_CLR_OFFSET 0x0038 /* USBPHY2 USB PHY General Control Register Clear */ + +/* Register addresses *******************************************************/ + +#define IMXRT_USBPHY1_PWD (IMXRT_USBPHY1_BASE + IMXRT_USBPHY1_PWD_OFFSET) /* USBPHY1 USB PHY1 Power-Down Register */ +#define IMXRT_USBPHY1_PWD_CLR (IMXRT_USBPHY1_BASE + IMXRT_USBPHY1_PWD_CLR_OFFSET) /* USBPHY1 USB PHY1 Power-Down Register Clear */ +#define IMXRT_USBPHY1_CTRL (IMXRT_USBPHY1_BASE + IMXRT_USBPHY1_CTRL_OFFSET) /* USBPHY1 USB PHY1 General Control Register */ +#define IMXRT_USBPHY1_CTRL_CLR (IMXRT_USBPHY1_BASE + IMXRT_USBPHY1_CTRL_CLR_OFFSET) /* USBPHY1 USB PHY1 General Control Register Clear */ + +#define IMXRT_USBPHY2_PWD (IMXRT_USBPHY2_BASE + IMXRT_USBPHY2_PWD_OFFSET) /* USBPHY2 USB PHY Power-Down Register */ +#define IMXRT_USBPHY2_PWD_CLR (IMXRT_USBPHY2_BASE + IMXRT_USBPHY2_PWD_CLR_OFFSET) /* USBPHY2 USB PHY Power-Down Register Clear */ +#define IMXRT_USBPHY2_CTRL (IMXRT_USBPHY2_BASE + IMXRT_USBPHY2_CTRL_OFFSET) /* USBPHY2 USB PHY General Control Register */ +#define IMXRT_USBPHY2_CTRL_CLR (IMXRT_USBPHY2_BASE + IMXRT_USBPHY2_CTRL_CLR_OFFSET) /* USBPHY2 USB PHY General Control Register Clear */ + +/* Register Bit Definitions *************************************************/ + +/* USB PHY Power-Down Register */ + +#define USBPHY_PWD_RXPWDRX (1 << 20) /* Bit 20: Power-down the entire USB PHY receiver block except for the full-speed differential receiver. */ +#define USBPHY_PWD_RXPWDDIFF (1 << 19) /* Bit 19: Power-down the USB high-speed differential receiver. */ +#define USBPHY_PWD_RXPWD1PT1 (1 << 18) /* Bit 18: Power-down the USB full-speed differential receiver. */ +#define USBPHY_PWD_RXPWDENV (1 << 17) /* Bit 17: Power-down the USB high-speed receiver envelope detector (squelch signal). */ +#define USBPHY_PWD_TXPWDV2I (1 << 12) /* Bit 12: Power-down the USB PHY transmit V-to-I converter and the current mirror. */ +#define USBPHY_PWD_TXPWDIBIAS (1 << 11) /* Bit 11: Power-down the USB PHY current bias block for the transmitter. */ +#define USBPHY_PWD_TXPWDFS (1 << 10) /* Bit 10: Power-down the USB full-speed drivers. */ + +/* USB PHY General Control Register */ + +#define USBPHY_CTRL_SFTRST (1 << 31) /* Bit 31: Soft-reset */ +#define USBPHY_CTRL_CLKGATE (1 << 30) /* Bit 30: Gate UTMI clocks */ + +#endif /* __ARCH_ARM_SRC_IMXRT_HARDWARE_IMXRT_USB_PHY_H */ diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/rt105x/imxrt105x_memorymap.h b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/rt105x/imxrt105x_memorymap.h new file mode 100644 index 000000000..dc42ac2e8 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/rt105x/imxrt105x_memorymap.h @@ -0,0 +1,311 @@ +/**************************************************************************** + * arch/arm/src/imxrt/hardware/rt105x/imxrt105x_memorymap.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_ARM_SRC_IMXRT_HARDWARE_IMXRT105X_MEMORYMAP_H +#define __ARCH_ARM_SRC_IMXRT_HARDWARE_IMXRT105X_MEMORYMAP_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* System memory map */ + +#define IMXRT_ITCM_BASE 0x00000000 /* 512KB ITCM */ + + /* 0x00080000 512KB ITCM Reserved */ + + /* 0x00100000 1MB ITCM Reserved */ +#define IMXRT_ROMCP_BASE 0x00200000 /* 96KB ROMCP */ + + /* 0x00218000 416KB ROMCP Reserved */ + + /* 0x00280000 1536KB Reserved */ + + /* 0x00400000 124MB Reserved */ +#define IMXRT_FLEXSPI_BASE 0x08000000 /* 128MB FlexSPI (Aliased) */ +#define IMXRT_SEMCA_BASE 0x10000000 /* 256MB SEMC (Aliased) */ +#define IMXRT_DTCM_BASE 0x20000000 /* 512KB DTCM */ + + /* 0x20080000 512KB DTCM Reserved */ + + /* 0x20100000 1MB Reserved */ +#define IMXRT_OCRAM_BASE 0x20200000 /* 512KB OCRAM */ + + /* 0x20280000 1536KB OCRAM Reserved */ + + /* 0x20400000 252MB Reserved */ + + /* 0x30000000 256MB Reserved */ +#define IMXRT_AIPS1_BASE 0x40000000 /* 1MB AIPS-1 */ +#define IMXRT_AIPS2_BASE 0x40100000 /* 1MB AIPS-2 */ +#define IMXRT_AIPS3_BASE 0x40200000 /* 1MB AIPS-3 */ +#define IMXRT_AIPS4_BASE 0x40300000 /* 1MB AIPS-4 */ + + /* 40400000 12MB Reserved */ +#define IMXRT_MAINCNF_BASE 0x41000000 /* 1MB "main" configuration port */ +#define IMXRT_MCNF_BASE 0x41100000 /* 1MB "m" configuration port */ + + /* 41200000 1MB Reserved for "per" GPV */ + + /* 41300000 1MB Reserved for "ems" GPV */ +#define IMXRT_CPUCNF_BASE 0x41400000 /* 1MB "cpu" configuration port */ + + /* 0x41500000 1MB GPV Reserved */ + + /* 0x41600000 1MB GPV Reserved */ + + /* 0x41700000 1MB GPV Reserved */ + + /* 0x41800000 8MB Reserved */ + + /* 0x42000000 32MB Reserved */ + + /* 0x44000000 64MB Reserved */ + + /* 0x48000000 384MB Reserved */ +#define IMXRT_FLEXCIPHER_BASE 0x60000000 /* 504MB FlexSPI/ FlexSPI ciphertext */ +#define IMXRT_FLEXSPITX_BASE 0x7f800000 /* 4MB FlexSPI TX FIFO */ +#define IMXRT_FLEXSPIRX_BASE 0x7fc00000 /* 4MB FlexSPI RX FIFO */ +#define IMXRT_EXTMEM_BASE 0x80000000 /* 1.5GB SEMC external memories shared memory space */ +#define IMXRT_CM7_BASE 0xe0000000 /* 1MB CM7 PPB */ + + /* 0xe0100000 511MB Reserved */ + +/* AIPS-1 memory map */ + + /* 0x40000000 256KB Reserved */ + + /* 0x40040000 240KB Reserved */ +#define IMXRT_AIPS1CNF_BASE 0x4007c000 /* 6KB AIPS-1 Configuration */ +#define IMXRT_DCDC_BASE 0x40080000 /* 16KB DCDC */ +#define IMXRT_PIT_BASE 0x40084000 /* 16KB PIT */ + + /* 0x40088000 16KB Reserved */ + + /* 0x4008c000 16KB Reserved */ +#define IMXRT_MTR_BASE 0x40090000 /* 16KB MTR */ +#define IMXRT_ACMP_BASE 0x40094000 /* 16KB ACMP */ + + /* 0x40098000 16KB Reserved */ + + /* 0x4009c000 16KB Reserved */ + + /* 0x400a0000 16KB Reserved */ +#define IMXRT_IOMUXCSNVSGPR_BASE 0x400a4000 /* 16KB IOMUXC_SNVS_GPR */ +#define IMXRT_IOMUXCSNVS_BASE 0x400a8000 /* 16KB IOMUXC_SNVS */ +#define IMXRT_IOMUXCGPR_BASE 0x400ac000 /* 16KB IOMUXC_GPR */ +#define IMXRT_FLEXRAM_BASE 0x400b0000 /* 16KB CM7_MX6RT(FLEXRAM) */ +#define IMXRT_EWM_BASE 0x400b4000 /* 16KB EWM */ +#define IMXRT_WDOG1_BASE 0x400b8000 /* 16KB WDOG1 */ +#define IMXRT_WDOG3_BASE 0x400bc000 /* 16KB WDOG3 */ +#define IMXRT_GPIO5_BASE 0x400c0000 /* 16KB GPIO5 */ +#define IMXRT_ADC1_BASE 0x400c4000 /* 16KB ADC1 */ +#define IMXRT_ADC2_BASE 0x400c8000 /* 16KB ADC2 */ +#define IMXRT_TRNG_BASE 0x400cc000 /* 16KB TRNG */ +#define IMXRT_WDOG2_BASE 0x400d0000 /* 16KB WDOG2 */ +#define IMXRT_SNVSHP_BASE 0x400d4000 /* 16KB SNVS_HP */ +#define IMXRT_ANATOP_BASE 0x400d8000 /* 16KB ANATOP */ +#define IMXRT_CSU_BASE 0x400dc000 /* 16KB CSU */ + + /* 0x400e0000 16KB Reserved */ + + /* 0x400e4000 16KB Reserved */ +#define IMXRT_EDMA_BASE 0x400e8000 /* 16KB EDMA */ +#define IMXRT_DMAMUX_BASE 0x400ec000 /* 16KB DMA_CH_MUX */ + + /* 400f0000 16KB Reserved */ +#define IMXRT_GPC_BASE 0x400f4000 /* 16KB GPC */ +#define IMXRT_SRC_BASE 0x400f8000 /* 16KB SRC */ +#define IMXRT_CCM_BASE 0x400fc000 /* 16KB CCM */ + +/* AIPS-2 memory map */ + + /* 0x40100000 256KB Reserved */ + + /* 0x40140000 240KB Reserved */ +#define IMXRT_AIPS2CNF_BASE 0x4017c000 /* 16KB AIPS-2 Configuration */ +#define IMXRT_ROMCPC_BASE 0x40180000 /* 16KB ROMCP controller*/ +#define IMXRT_LPUART1_BASE 0x40184000 /* 16KB LPUART1 */ +#define IMXRT_LPUART2_BASE 0x40188000 /* 16KB LPUART2 */ +#define IMXRT_LPUART3_BASE 0x4018c000 /* 16KB LPUART3 */ +#define IMXRT_LPUART4_BASE 0x40190000 /* 16KB LPUART4 */ +#define IMXRT_LPUART5_BASE 0x40194000 /* 16KB LPUART5 */ +#define IMXRT_LPUART6_BASE 0x40198000 /* 16KB LPUART6 */ +#define IMXRT_LPUART7_BASE 0x4019c000 /* 16KB LPUART7 */ +#define IMXRT_LPUART8_BASE 0x401a0000 /* 16KB LPUART8 */ + + /* 0x401a4000 16KB Reserved */ + + /* 0x401a8000 16KB Reserved */ +#define IMXRT_FLEXIO1_BASE 0x401ac000 /* 16KB FlexIO1 */ +#define IMXRT_FLEXIO2_BASE 0x401b0000 /* 16KB FlexIO2 */ + + /* 0x401b4000 16KB Reserved */ +#define IMXRT_GPIO1_BASE 0x401b8000 /* 16KB GPIO1 */ +#define IMXRT_GPIO2_BASE 0x401bc000 /* 16KB GPIO2 */ +#define IMXRT_GPIO3_BASE 0x401c0000 /* 16KB GPIO3 */ +#define IMXRT_GPIO4_BASE 0x401c4000 /* 16KB GPIO4 */ + + /* 0x401c8000 16KB Reserved */ + + /* 0x401cc000 16KB Reserved */ +#define IMXRT_CAN1_BASE 0x401d0000 /* 16KB CAN1 */ +#define IMXRT_CAN2_BASE 0x401d4000 /* 16KB CAN2 */ + + /* 0x401d8000 16KB Reserved */ +#define IMXRT_QTIMER1_BASE 0x401dc000 /* 16KB QTimer1 */ +#define IMXRT_QTIMER2_BASE 0x401e0000 /* 16KB QTimer2 */ +#define IMXRT_QTIMER3_BASE 0x401e4000 /* 16KB QTimer3 */ +#define IMXRT_QTIMER4_BASE 0x401e8000 /* 16KB QTimer4 */ +#define IMXRT_GPT1_BASE 0x401ec000 /* 16KB GPT1 */ +#define IMXRT_GPT2_BASE 0x401f0000 /* 16KB GPT2 */ +#define IMXRT_OCOTP_BASE 0x401f4000 /* 16KB OCOTP */ +#define IMXRT_IOMUXC_BASE 0x401f8000 /* 16KB IOMUXC */ +#define IMXRT_KPP_BASE 0x401fc000 /* 16KB KPP */ + +/* AIPS-3 memory map */ + + /* 0x40200000 256KB Reserved */ + + /* 0x40240000 240KB Reserved */ +#define IMXRT_AIOS3CNF_BASE 0x4027c000 /* 16KB AIPS-3 Configuration */ + + /* 0x40280000 16KB Reserved */ + + /* 0x40284000 16KB Reserved */ + + /* 0x40288000 16KB Reserved */ + + /* 0x4028c000 16KB Reserved */ + + /* 0x40290000 16KB Reserved */ + + /* 0x40294000 16KB Reserved */ + + /* 0x40298000 16KB Reserved */ + + /* 0x4029c000 16KB Reserved */ + + /* 0x402a0000 16KB Reserved */ + + /* 0x402a4000 16KB Reserved */ +#define IMXRT_FLEXSPIC_BASE 0x402a8000 /* 16KB FlexSPI controller */ + + /* 0x402ac000 16KB Reserved */ + + /* 0x402b0000 16KB Reserved */ +#define IMXRT_PXP_BASE 0x402b4000 /* 16KB PXP */ +#define IMXRT_LCDIF_BASE 0x402b8000 /* 16KB LCDIF */ +#define IMXRT_CSI_BASE 0x402bc000 /* 16KB CSI */ +#define IMXRT_USDHC1_BASE 0x402c0000 /* 16KB USDHC1 */ +#define IMXRT_USDHC2_BASE 0x402c4000 /* 16KB USDHC2 */ + + /* 0x402c8000 16KB Reserved */ + + /* 0x402cc000 16KB Reserved */ + + /* 0x402d0000 16KB Reserved */ + + /* 0x402d4000 16KB Reserved */ +#define IMXRT_ENET_BASE 0x402d8000 /* 16KB ENET */ +#define IMXRT_USBPL301_BASE 0x402dc000 /* 16KB USB(PL301) */ +#define IMXRT_USB_BASE 0x402e0200 /* 16KB USB(USB) */ + + /* 0x402e4000 16KB Reserved */ + + /* 0x402e8000 16KB Reserved */ + + /* 0x402ec000 16KB Reserved */ +#define IMXRT_SEMC_BASE 0x402f0000 /* 16KB SEMC */ + + /* 0x402f4000 16KB Reserved */ + + /* 0x402f8000 16KB Reserved */ +#define IMXRT_DCP_BASE 0x402fc000 /* 16KB DCP */ + +/* AIPS-4 memory map */ + + /* 0x40300000 256KB Reserved */ + + /* 0x40340000 240KB Reserved */ +#define IMXRT_AIPS4CNF_BASE 0x4037c000 /* 16KB AIPS-4 Configuration */ +#define IMXRT_SPDIF_BASE 0x40380000 /* 16KB SPDIF */ +#define IMXRT_SAI1_BASE 0x40384000 /* 16KB SAI1 */ +#define IMXRT_SAI2_BASE 0x40388000 /* 16KB SAI2 */ +#define IMXRT_SAI3_BASE 0x4038c000 /* 16KB SAI3 */ + + /* 0x40390000 16KB Reserved */ +#define IMXRT_LPSPI1_BASE 0x40394000 /* 16KB LPSPI1 */ +#define IMXRT_LPSPI2_BASE 0x40398000 /* 16KB LPSPI2 */ +#define IMXRT_LPSPI3_BASE 0x4039c000 /* 16KB LPSPI3 */ +#define IMXRT_LPSPI4_BASE 0x403a0000 /* 16KB LPSPI4 */ + + /* 0x403a4000 16KB Reserved */ + + /* 0x403a8000 16KB Reserved */ + + /* 0x403ac000 16KB Reserved */ +#define IMXRT_ADCETC_BASE 0x403b0000 /* 16KB ADC_ETC */ +#define IMXRT_AOI1_BASE 0x403b4000 /* 16KB AOI1 */ +#define IMXRT_AOI2_BASE 0x403b8000 /* 16KB AOI2 */ +#define IMXRT_XBAR1_BASE 0x403bc000 /* 16KB XBAR1 */ +#define IMXRT_XBAR2_BASE 0x403c0000 /* 16KB XBAR2 */ +#define IMXRT_XBAR3_BASE 0x403c4000 /* 16KB XBAR3 */ +#define IMXRT_ENC1_BASE 0x403c8000 /* 16KB ENC1 */ +#define IMXRT_ENC2_BASE 0x403cc000 /* 16KB ENC2 */ +#define IMXRT_ENC3_BASE 0x403d0000 /* 16KB ENC3 */ +#define IMXRT_ENC4_BASE 0x403d4000 /* 16KB ENC4 */ + + /* 0x403d8000 16KB Reserved */ +#define IMXRT_FLEXPWM1_BASE 0x403dc000 /* 16KB FLEXPWM1 */ +#define IMXRT_FLEXPWM2_BASE 0x403e0000 /* 16KB FLEXPWM2 */ +#define IMXRT_FLEXPWM3_BASE 0x403e4000 /* 16KB FLEXPWM3 */ +#define IMXRT_FLEXPWM4_BASE 0x403e8000 /* 16KB FLEXPWM4 */ +#define IMXRT_BEE_BASE 0x403ec000 /* 16KB BEE */ +#define IMXRT_LPI2C1_BASE 0x403f0000 /* 16KB */ +#define IMXRT_LPI2C2_BASE 0x403f4000 /* 16KB LPI2C2 */ +#define IMXRT_LPI2C3_BASE 0x403f8000 /* 16KB LPI2C3 */ +#define IMXRT_LPI2C4_BASE 0x403fc000 /* 16KB LPI2C4 */ + +/* PPB memory map */ + +#define IMXRT_TPIU_BASE 0xe0040000 /* 4KB TPIU */ +#define IMXRT_ETM_BASE 0xe0041000 /* 4KB ETM */ +#define IMXRT_CTI_BASE 0xe0042000 /* 4KB CTI */ +#define IMXRT_TSGEN_BASE 0xe0043000 /* 4KB TSGEN */ +#define IMXRT_PPBRES_BASE 0xe0044000 /* 4KB PPB RES */ + + /* 0xe0045000 236KB PPB Reserved */ +#define IMXRT_MCM_BASE 0xe0080000 /* 4KB MCM */ + + /* 0xe0081000 444KB PPB Reserved */ + + /* 0xe00f0000 52KB PPB Reserved */ +#define IMXRT_SYSROM_BASE 0xe00fd000 /* 4KB SYS ROM */ +#define IMXRT_PROCROM_BASE 0xe00fe000 /* 4KB Processor ROM */ +#define IMXRT_PPBROM_BASE 0xe00ff000 /* 4KB PPB ROM */ + +#endif /* __ARCH_ARM_SRC_IMXRT_HARDWARE_IMXRT105X_MEMORYMAP_H */ diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_clockconfig.c b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_clockconfig.c new file mode 100644 index 000000000..97fef0a96 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_clockconfig.c @@ -0,0 +1,698 @@ +/**************************************************************************** + * arch/arm/src/imxrt/imxrt_clockconfig.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 "arm_arch.h" +#include +#include "hardware/imxrt_ccm.h" +#include "hardware/imxrt_dcdc.h" +#include "imxrt_clockconfig.h" +#include "imxrt_lcd.h" +#include "hardware/imxrt_memorymap.h" +#include "hardware/imxrt_iomuxc.h" + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define VIDEO_PLL_MIN_FREQ 650000000 +#define OSC24_FREQ 24000000 + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imxrt_lcd_clockconfig + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_LCD +static void imxrt_lcd_clockconfig(void) +{ + uint32_t reg; + uint32_t reg2; + + int post; + int pre; + + uint32_t numerator; + uint32_t denominator; + uint32_t post_divider; + uint32_t pre_divider; + uint32_t loop_divider; + uint32_t target_freq; + uint32_t freq_error; + + target_freq = (CONFIG_IMXRT_LCD_HWIDTH + + CONFIG_IMXRT_LCD_HPULSE + + CONFIG_IMXRT_LCD_HFRONTPORCH + + CONFIG_IMXRT_LCD_HBACKPORCH) * + (CONFIG_IMXRT_LCD_VHEIGHT + + CONFIG_IMXRT_LCD_VPULSE + + CONFIG_IMXRT_LCD_VFRONTPORCH + + CONFIG_IMXRT_LCD_VBACKPORCH) * + CONFIG_IMXRT_LCD_REFRESH_FREQ; + + for (post_divider = 1; post_divider < 16; post_divider <<= 1) + { + if (IMXRT_LCD_VIDEO_PLL_FREQ * post_divider >= VIDEO_PLL_MIN_FREQ) + { + break; + } + } + + loop_divider = (IMXRT_LCD_VIDEO_PLL_FREQ * post_divider) / OSC24_FREQ; + numerator = (IMXRT_LCD_VIDEO_PLL_FREQ * post_divider) - + (loop_divider * OSC24_FREQ); + denominator = OSC24_FREQ; + + /* Bypass PLL first */ + + modifyreg32(IMXRT_CCM_ANALOG_PLL_VIDEO, + CCM_ANALOG_PLL_VIDEO_BYPASS_CLK_SRC_MASK, + CCM_ANALOG_PLL_VIDEO_BYPASS | + CCM_ANALOG_PLL_VIDEO_BYPASS_CLK_SRC_REF_24M); + + putreg32(CCM_ANALOG_PLL_VIDEO_NUM_A(numerator), + IMXRT_CCM_ANALOG_PLL_VIDEO_NUM); + putreg32(CCM_ANALOG_PLL_VIDEO_DENOM_B(denominator), + IMXRT_CCM_ANALOG_PLL_VIDEO_DENOM); + + /* Set post divider: + * + * ------------------------------------------------------------------------ + * | config->postDivider | PLL_VIDEO[POST_DIV_SELECT] | MISC2[VIDEO_DIV] | + * ------------------------------------------------------------------------ + * | 1 | 2 | 0 | + * ------------------------------------------------------------------------ + * | 2 | 1 | 0 | + * ------------------------------------------------------------------------ + * | 4 | 2 | 3 | + * ------------------------------------------------------------------------ + * | 8 | 1 | 3 | + * ------------------------------------------------------------------------ + * | 16 | 0 | 3 | + * ------------------------------------------------------------------------ + */ + + reg = getreg32(IMXRT_CCM_ANALOG_PLL_VIDEO); + reg &= ~(CCM_ANALOG_PLL_VIDEO_DIV_SELECT_MASK | + CCM_ANALOG_PLL_VIDEO_POWERDOWN); + reg |= CCM_ANALOG_PLL_VIDEO_ENABLE | + CCM_ANALOG_PLL_VIDEO_DIV_SELECT(loop_divider); + + reg2 = getreg32(IMXRT_CCM_ANALOG_MISC2); + reg2 &= ~CCM_ANALOG_MISC2_VIDEO_DIV_MASK; + + switch (post_divider) + { + case 16: + reg |= CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT_DIV4; + reg2 |= CCM_ANALOG_MISC2_VIDEO_DIV(3); + break; + + case 8: + reg |= CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT_DIV2; + reg2 |= CCM_ANALOG_MISC2_VIDEO_DIV(3); + break; + + case 4: + reg |= CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT_DIV1; + reg2 |= CCM_ANALOG_MISC2_VIDEO_DIV(3); + break; + + case 2: + reg |= CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT_DIV2; + reg2 |= 0; + break; + + default: + reg |= CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT_DIV1; + reg2 |= 0; + break; + } + + putreg32(reg, IMXRT_CCM_ANALOG_PLL_VIDEO); + + putreg32(reg2, IMXRT_CCM_ANALOG_MISC2); + + while ((getreg32(IMXRT_CCM_ANALOG_PLL_VIDEO) & + CCM_ANALOG_PLL_VIDEO_LOCK) == 0) + { + } + + /* Disable Bypass */ + + modifyreg32(IMXRT_CCM_ANALOG_PLL_VIDEO, + CCM_ANALOG_PLL_VIDEO_BYPASS, + 0); + + freq_error = IMXRT_LCD_VIDEO_PLL_FREQ; + pre_divider = 0; + post_divider = 0; + + for (post = 0; post < 8; post++) + { + for (pre = 0; pre < 8; pre++) + { + int32_t temp_error; + temp_error = labs((post + 1) * (pre + 1) * target_freq - + IMXRT_LCD_VIDEO_PLL_FREQ); + if (temp_error < freq_error) + { + pre_divider = pre; + post_divider = post; + freq_error = temp_error; + } + } + } + + /* Select PLL5 as LCD Clock and set Pre divider. */ + + modifyreg32(IMXRT_CCM_CSCDR2, + CCM_CSCDR2_LCDIF_PRE_CLK_SEL_MASK | + CCM_CSCDR2_LCDIF_PRED_MASK, + CCM_CSCDR2_LCDIF_PRE_CLK_SEL_PLL5 | + CCM_CSCDR2_LCDIF_PRED(pre_divider)); + + /* Set Post divider. */ + + modifyreg32(IMXRT_CCM_CBCMR, CCM_CBCMR_LCDIF_PODF_MASK, + CCM_CBCMR_LCDIF_PODF(post_divider)); +} + +#endif + +/**************************************************************************** + * Name: imxrt_pllsetup + ****************************************************************************/ + +static void imxrt_pllsetup(void) +{ +#ifdef CONFIG_ARCH_FAMILY_IMXRT102x + uint32_t pll2reg; +#endif + uint32_t pll3reg; + uint32_t reg; + +#if (defined(CONFIG_ARCH_FAMILY_IMXRT105x) || defined (CONFIG_ARCH_FAMILY_IMXRT106x)) + /* Init Arm PLL1 */ + + reg = CCM_ANALOG_PLL_ARM_DIV_SELECT(IMXRT_ARM_PLL_DIV_SELECT) | + CCM_ANALOG_PLL_ARM_ENABLE; + putreg32(reg, IMXRT_CCM_ANALOG_PLL_ARM); + while ((getreg32(IMXRT_CCM_ANALOG_PLL_ARM) & CCM_ANALOG_PLL_ARM_LOCK) == 0) + { + } + + /* Init Sys PLL2 */ + + reg = CCM_ANALOG_PLL_SYS_DIV_SELECT(IMXRT_SYS_PLL_SELECT) | + CCM_ANALOG_PLL_SYS_ENABLE; + putreg32(reg, IMXRT_CCM_ANALOG_PLL_SYS); + while ((getreg32(IMXRT_CCM_ANALOG_PLL_SYS) & CCM_ANALOG_PLL_SYS_LOCK) == 0) + { + } + + /* Init USB PLL3 */ + + /* capture it's original value */ + + pll3reg = getreg32(IMXRT_CCM_ANALOG_PFD_480); + putreg32(pll3reg | + CCM_ANALOG_PFD_480_PFD0_CLKGATE | + CCM_ANALOG_PFD_480_PFD1_CLKGATE | + CCM_ANALOG_PFD_480_PFD2_CLKGATE | + CCM_ANALOG_PFD_480_PFD3_CLKGATE, + IMXRT_CCM_ANALOG_PFD_480); + + reg = IMXRT_USB2_PLL_DIV_SELECT | + CCM_ANALOG_PLL_USB2_ENABLE | + CCM_ANALOG_PLL_USB2_EN_USB_CLKS | + CCM_ANALOG_PLL_USB2_POWER; + putreg32(reg, IMXRT_CCM_ANALOG_PLL_USB2); + + while ((getreg32(IMXRT_CCM_ANALOG_PLL_USB2) & + CCM_ANALOG_PLL_USB2_LOCK) == 0) + { + } + + putreg32(pll3reg, IMXRT_CCM_ANALOG_PFD_480); + +#ifdef CONFIG_IMXRT_LCD + /* Init Video PLL5 */ + + imxrt_lcd_clockconfig(); +#endif + + /* Init ENET PLL6 */ + + reg = CCM_ANALOG_PLL_ENET_ENET0_DIV_SELECT_50MHZ | + CCM_ANALOG_PLL_ENET_ENET1_125M_EN | + CCM_ANALOG_PLL_ENET_ENET_25M_REF_EN | + CCM_ANALOG_PLL_ENET_ENET_500M_REF_EN | + CCM_ANALOG_PLL_ENET_ENET1_DIV_SELECT_50MHZ; + + putreg32(reg, IMXRT_CCM_ANALOG_PLL_ENET); + + while ((getreg32(IMXRT_CCM_ANALOG_PLL_ENET) & + CCM_ANALOG_PLL_ENET_LOCK) == 0) + { + } + +#elif defined(CONFIG_ARCH_FAMILY_IMXRT102x) + /* Init Sys PLL2 */ + + /* First reset its fractional dividers */ + + pll2reg = getreg32(IMXRT_CCM_ANALOG_PFD_528); + putreg32(pll2reg | + CCM_ANALOG_PFD_528_PFD0_CLKGATE | + CCM_ANALOG_PFD_528_PFD1_CLKGATE | + CCM_ANALOG_PFD_528_PFD2_CLKGATE | + CCM_ANALOG_PFD_528_PFD3_CLKGATE, + IMXRT_CCM_ANALOG_PFD_528); + + reg = CCM_ANALOG_PLL_SYS_DIV_SELECT(IMXRT_SYS_PLL_DIV_SELECT) | + CCM_ANALOG_PLL_SYS_ENABLE; + putreg32(reg, IMXRT_CCM_ANALOG_PLL_SYS); + + while ((getreg32(IMXRT_CCM_ANALOG_PLL_SYS) & + CCM_ANALOG_PLL_SYS_LOCK) == 0) + { + } + + putreg32(pll2reg, IMXRT_CCM_ANALOG_PFD_528); + + /* Init USB PLL3 */ + + /* capture it's original value */ + + pll3reg = getreg32(IMXRT_CCM_ANALOG_PFD_480); + putreg32(pll3reg | + CCM_ANALOG_PFD_480_PFD0_CLKGATE | + CCM_ANALOG_PFD_480_PFD1_CLKGATE | + CCM_ANALOG_PFD_480_PFD2_CLKGATE | + CCM_ANALOG_PFD_480_PFD3_CLKGATE, + IMXRT_CCM_ANALOG_PFD_480); + + reg = CCM_ANALOG_PLL_USB2_DIV_SELECT(IMXRT_USB2_PLL_DIV_SELECT) | + CCM_ANALOG_PLL_USB2_ENABLE | CCM_ANALOG_PLL_USB2_EN_USB_CLKS | + CCM_ANALOG_PLL_USB2_POWER; + putreg32(reg, IMXRT_CCM_ANALOG_PLL_USB2); + + while ((getreg32(IMXRT_CCM_ANALOG_PLL_USB2) & + CCM_ANALOG_PLL_USB2_LOCK) == 0) + { + } + + putreg32(pll3reg, IMXRT_CCM_ANALOG_PFD_480); + + /* Init Audio PLL4 */ + + reg = CCM_ANALOG_PLL_AUDIO_DIV_SELECT(IMXRT_AUDIO_PLL_DIV_SELECT) | + CCM_ANALOG_PLL_AUDIO_ENABLE; + putreg32(reg, IMXRT_CCM_ANALOG_PLL_AUDIO); + + while ((getreg32(IMXRT_CCM_ANALOG_PLL_AUDIO) & + CCM_ANALOG_PLL_AUDIO_LOCK) == 0) + { + } + + /* Init ENET PLL6 */ + + reg = CCM_ANALOG_PLL_ENET_ENET0_DIV_SELECT_50MHZ | + CCM_ANALOG_PLL_ENET_ENET1_125M_EN | + CCM_ANALOG_PLL_ENET_ENET_25M_REF_EN | + CCM_ANALOG_PLL_ENET_ENET_500M_REF_EN; + + putreg32(reg, IMXRT_CCM_ANALOG_PLL_ENET); + + while ((getreg32(IMXRT_CCM_ANALOG_PLL_ENET) & + CCM_ANALOG_PLL_ENET_LOCK) == 0) + { + } + +#else +# error Unrecognised IMXRT family member for clock config +#endif +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imxrt_clockconfig + * + * Description: + * Called to initialize the i.MXRT. This does whatever setup is needed to + * put the SoC in a usable state. This includes the initialization of + * clocking using the settings in board.h. + * + ****************************************************************************/ + +void imxrt_clockconfig(void) +{ + /* Don't change the current basic clock configuration if we are running + * from SDRAM. In this case, some bootloader logic has already configured + * clocking and SDRAM. We are pretty much committed to using things the + * way that the bootloader has left them. + * + * Note that although this is safe at boot while nothing is using + * the clocks additional caution is required if at some later date + * we want to manipulate the PODFs while the system is running + * (for power minimisation) because changing those is not glitch free. + */ + +#ifndef CONFIG_IMXRT_BOOT_SDRAM + uint32_t reg; + + /* Set clock mux and dividers */ + + /* Set PERIPH_CLK2 MUX to OSC */ + + reg = getreg32(IMXRT_CCM_CBCMR); + reg &= ~CCM_CBCMR_PERIPH_CLK2_SEL_MASK; + reg |= CCM_CBCMR_PERIPH_CLK2_SEL_OSC_CLK; + putreg32(reg, IMXRT_CCM_CBCMR); + + while ((getreg32(IMXRT_CCM_CDHIPR) & CCM_CDHIPR_PERIPH2_CLK_SEL_BUSY) != 0) + { + } + + /* Set PERIPH_CLK MUX to PERIPH_CLK2 */ + + reg = getreg32(IMXRT_CCM_CBCDR); + reg &= ~CCM_CBCDR_PERIPH_CLK_SEL_MASK; + reg |= CCM_CBCDR_PERIPH_CLK_SEL(CCM_CBCDR_PERIPH_CLK_SEL_PERIPH_CLK2); + putreg32(reg, IMXRT_CCM_CBCDR); + while ((getreg32(IMXRT_CCM_CDHIPR) & CCM_CDHIPR_PERIPH_CLK_SEL_BUSY) != 0) + { + } + + /* Set Soc VDD and wait for it to stablise */ + + reg = getreg32(IMXRT_DCDC_REG3); + reg &= ~(DCDC_REG3_TRG_MASK); + reg |= DCDC_REG3_TRG(IMXRT_VDD_SOC); + putreg32(reg, IMXRT_DCDC_REG3); + while ((getreg32(IMXRT_DCDC_REG0) & DCDC_REG0_STS_DC_OK) == 0) + { + } + + /* OK, now nothing is depending on us, configure the PLLs */ + + imxrt_pllsetup(); + + /* Set Dividers */ + + reg = getreg32(IMXRT_CCM_CACRR); + reg &= ~CCM_CACRR_ARM_PODF_MASK; + reg |= CCM_CACRR_ARM_PODF(CCM_PODF_FROM_DIVISOR(IMXRT_ARM_PODF_DIVIDER)); + putreg32(reg, IMXRT_CCM_CACRR); + while ((getreg32(IMXRT_CCM_CDHIPR) & CCM_CDHIPR_ARM_PODF_BUSY) != 0) + { + } + + reg = getreg32(IMXRT_CCM_CBCDR); + reg &= ~CCM_CBCDR_AHB_PODF_MASK; + reg |= CCM_CBCDR_AHB_PODF(CCM_PODF_FROM_DIVISOR(IMXRT_AHB_PODF_DIVIDER)); + putreg32(reg, IMXRT_CCM_CBCDR); + while ((getreg32(IMXRT_CCM_CDHIPR) & CCM_CDHIPR_AHB_PODF_BUSY) != 0) + { + } + + /* Adjust IPG and PERCLK PODFs. Consumers of these clocks will need to + * be gated if there are any (there aren't at boot). + */ + + reg = getreg32(IMXRT_CCM_CBCDR); + reg &= ~CCM_CBCDR_IPG_PODF_MASK; + reg |= CCM_CBCDR_IPG_PODF(CCM_PODF_FROM_DIVISOR(IMXRT_IPG_PODF_DIVIDER)); + putreg32(reg, IMXRT_CCM_CBCDR); + + reg = getreg32(IMXRT_CCM_CSCMR1); + reg &= ~CCM_CSCMR1_PERCLK_PODF_MASK; + reg |= CCM_CSCMR1_PERCLK_PODF( + CCM_PODF_FROM_DIVISOR(IMXRT_PERCLK_PODF_DIVIDER)); + putreg32(reg, IMXRT_CCM_CSCMR1); + +#ifndef CONFIG_IMXRT_SEMC_INIT_DONE + /* Configure SEMC Clock only if not already done by DCD SDR */ + + reg = getreg32(IMXRT_CCM_CBCDR); + reg &= ~CCM_CBCDR_SEMC_PODF_MASK; + reg |= CCM_CBCDR_SEMC_PODF(CCM_PODF_FROM_DIVISOR(IMXRT_SEMC_PODF_DIVIDER)); + putreg32(reg, IMXRT_CCM_CBCDR); + + while ((getreg32(IMXRT_CCM_CDHIPR) & CCM_CDHIPR_SEMC_PODF_BUSY) != 0) + { + } +#endif + + /* Set PRE_PERIPH_CLK to Board Selection */ + + reg = getreg32(IMXRT_CCM_CBCMR); + reg &= ~CCM_CBCMR_PRE_PERIPH_CLK_SEL_MASK; + reg |= CCM_CBCMR_PRE_PERIPH_CLK_SEL(IMXRT_PRE_PERIPH_CLK_SEL); + putreg32(reg, IMXRT_CCM_CBCMR); + + /* Set PERIPH_CLK MUX to Board Selection */ + + reg = getreg32(IMXRT_CCM_CBCDR); + reg &= ~CCM_CBCDR_PERIPH_CLK_SEL_MASK; + reg |= CCM_CBCDR_PERIPH_CLK_SEL(IMXRT_PERIPH_CLK_SEL); + putreg32(reg, IMXRT_CCM_CBCDR); + + /* Wait handshake */ + + while ((getreg32(IMXRT_CCM_CDHIPR) & CCM_CDHIPR_PERIPH_CLK_SEL_BUSY) != 0) + { + } + + /* Set PERCLK_CLK_SEL to Board Selection */ + + reg = getreg32(IMXRT_CCM_CSCMR1); + reg &= ~CCM_CSCMR1_PERCLK_CLK_SEL_MASK; + reg |= CCM_CSCMR1_PERCLK_CLK_SEL(IMXRT_PERCLK_CLK_SEL); + putreg32(reg, IMXRT_CCM_CSCMR1); + + while ((getreg32(IMXRT_CCM_CDHIPR) & CCM_CDHIPR_PERIPH_CLK_SEL_BUSY) != 0) + { + } + + /* Setup perhiperals. At this point these are not activated so don't + * need to worry too much about switching off the clock feeds. + */ + + /* Set UART source to PLL3 80M */ + + reg = getreg32(IMXRT_CCM_CSCDR1); + reg &= ~CCM_CSCDR1_UART_CLK_SEL; + reg |= CCM_CSCDR1_UART_CLK_SEL_PLL3_80; + putreg32(reg, IMXRT_CCM_CSCDR1); + + /* Set UART divider to 1 */ + + reg = getreg32(IMXRT_CCM_CSCDR1); + reg &= ~CCM_CSCDR1_UART_CLK_PODF_MASK; + reg |= CCM_CSCDR1_UART_CLK_PODF(CCM_PODF_FROM_DIVISOR(1)); + putreg32(reg, IMXRT_CCM_CSCDR1); + +#ifdef CONFIG_IMXRT_FLEXIO1 +#ifdef CONFIG_ARCH_FAMILY_IMXRT102x + /* Set FlEXIO1 source */ + + reg = getreg32(IMXRT_CCM_CSCMR2); + reg &= ~CCM_CSCMR2_FLEXIO1_CLK_SEL_MASK; + reg |= CCM_CSCMR2_FLEXIO1_CLK_SEL(CONFIG_FLEXIO1_CLK); + putreg32(reg, IMXRT_CCM_CSCMR2); + + /* Set FlEXIO1 divider */ + + reg = getreg32(IMXRT_CCM_CS1CDR); + reg &= ~(CCM_CS1CDR_FLEXIO1_CLK_PODF_MASK | \ + CCM_CS1CDR_FLEXIO1_CLK_PRED_MASK); + reg |= CCM_CS1CDR_FLEXIO1_CLK_PODF + (CCM_PODF_FROM_DIVISOR(CONFIG_FLEXIO1_PODF_DIVIDER)); + reg |= CCM_CS1CDR_FLEXIO1_CLK_PRED + (CCM_PRED_FROM_DIVISOR(CONFIG_FLEXIO1_PRED_DIVIDER)); + putreg32(reg, IMXRT_CCM_CS1CDR); + +#elif (defined(CONFIG_ARCH_FAMILY_IMXRT105x) || defined(CONFIG_ARCH_FAMILY_IMXRT106x)) + /* Set FlEXIO1 source & divider */ + + reg = getreg32(IMXRT_CCM_CDCDR); + reg &= ~(CCM_CDCDR_FLEXIO1_CLK_SEL_MASK | + CCM_CDCDR_FLEXIO1_CLK_PODF_MASK | + CCM_CDCDR_FLEXIO1_CLK_PRED_MASK); + reg |= CCM_CDCDR_FLEXIO1_CLK_SEL(CONFIG_FLEXIO1_CLK); + reg |= CCM_CDCDR_FLEXIO1_CLK_PODF + (CCM_PODF_FROM_DIVISOR(CONFIG_FLEXIO1_PODF_DIVIDER)); + reg |= CCM_CDCDR_FLEXIO1_CLK_PRED + (CCM_PRED_FROM_DIVISOR(CONFIG_FLEXIO1_PRED_DIVIDER)); + putreg32(reg, IMXRT_CCM_CDCDR); + +#endif /* CONFIG_ARCH_FAMILY_IMXRT102x */ +#endif /* CONFIG_IMXRT_FLEXIO1 */ + +#if (defined(CONFIG_IMXRT_FLEXIO2) || defined(CONFIG_IMXRT_FLEXIO3)) + /* Set FlEXIO2 source */ + + reg = getreg32(IMXRT_CCM_CSCMR2); + reg &= ~CCM_CSCMR2_FLEXIO2_CLK_SEL_MASK; + reg |= CCM_CSCMR2_FLEXIO2_CLK_SEL(CONFIG_FLEXIO2_CLK); + putreg32(reg, IMXRT_CCM_CSCMR2); + + /* Set FlEXIO2 divider */ + + reg = getreg32(IMXRT_CCM_CS1CDR); + reg &= ~(CCM_CS1CDR_FLEXIO2_CLK_PODF_MASK | \ + CCM_CS1CDR_FLEXIO2_CLK_PRED_MASK); + reg |= CCM_CS1CDR_FLEXIO2_CLK_PODF + (CCM_PODF_FROM_DIVISOR(CONFIG_FLEXIO2_PODF_DIVIDER)); + reg |= CCM_CS1CDR_FLEXIO2_CLK_PRED + (CCM_PRED_FROM_DIVISOR(CONFIG_FLEXIO2_PRED_DIVIDER)); + putreg32(reg, IMXRT_CCM_CS1CDR); + +#endif /* CONFIG_IMXRT_FLEXIO2 */ + +#ifdef CONFIG_IMXRT_LPI2C + /* Set LPI2C source to PLL3 60M */ + + reg = getreg32(IMXRT_CCM_CSCDR2); + reg &= ~CCM_CSCDR2_LPI2C_CLK_SEL; + reg |= IMXRT_LPI2C_CLK_SELECT; + putreg32(reg, IMXRT_CCM_CSCDR2); + + /* Set LPI2C divider to 5 for 12 MHz */ + + reg = getreg32(IMXRT_CCM_CSCDR2); + reg &= ~CCM_CSCDR2_LPI2C_CLK_PODF_MASK; + reg |= CCM_CSCDR2_LPI2C_CLK_PODF( + CCM_PODF_FROM_DIVISOR(IMXRT_LSI2C_PODF_DIVIDER) + ); + putreg32(reg, IMXRT_CCM_CSCDR2); + +#endif + +#ifdef CONFIG_IMXRT_FLEXCAN + /* Set FlexCAN clock source to PLL3 80M */ + + reg = getreg32(IMXRT_CCM_CSCMR2); + reg &= ~CCM_CSCMR2_CAN_CLK_SEL_MASK; + reg |= IMXRT_CAN_CLK_SELECT; + putreg32(reg, IMXRT_CCM_CSCMR2); + + /* Set FlexCAN dividet to 1 for 80 MHz */ + + reg = getreg32(IMXRT_CCM_CSCMR2); + reg &= ~CCM_CSCMR2_CAN_CLK_PODF_MASK; + reg |= CCM_CSCMR2_CAN_CLK_PODF( + CCM_PODF_FROM_DIVISOR(IMXRT_CAN_PODF_DIVIDER) + ); + putreg32(reg, IMXRT_CCM_CSCMR2); + +#endif + +#ifdef CONFIG_IMXRT_LPSPI + /* Set LPSPI clock source to PLL3 PFD0 */ + + reg = getreg32(IMXRT_CCM_CBCMR); + reg &= ~CCM_CBCMR_LPSPI_CLK_SEL_MASK; + reg |= IMXRT_LPSPI_CLK_SELECT; + putreg32(reg, IMXRT_CCM_CBCMR); + + /* Set LPSPI divider to IMXRT_LSPI_PODF_DIVIDER */ + + reg = getreg32(IMXRT_CCM_CBCMR); + reg &= ~CCM_CBCMR_LPSPI_PODF_MASK; + reg |= CCM_CBCMR_LPSPI_PODF( + CCM_PODF_FROM_DIVISOR(IMXRT_LSPI_PODF_DIVIDER) + ); + putreg32(reg, IMXRT_CCM_CBCMR); +#endif + +#ifdef IMXRT_TRACE_PODF_DIVIDER + /* Set TRACE clock source and speed */ + + reg = getreg32(IMXRT_CCM_CBCMR); + reg &= ~CCM_CBCMR_TRACE_CLK_SEL_MASK; + reg |= IMXRT_TRACE_CLK_SELECT; + putreg32(reg, IMXRT_CCM_CBCMR); + + reg = getreg32(IMXRT_CCM_CSCDR1); + reg &= ~CCM_CSCDR1_TRACE_PODF_MASK; + reg |= CCM_CSCDR1_TRACE_PODF( + CCM_PODF_FROM_DIVISOR(IMXRT_TRACE_PODF_DIVIDER)); + putreg32(reg, IMXRT_CCM_CSCDR1); +#endif + +#ifdef CONFIG_IMXRT_USDHC + /* Optionally set USDHC1 & 2 to generate clocks + * from IMXRT_USDHC1_CLK_SELECT + */ + + reg = getreg32(IMXRT_CCM_CSCMR1); + reg &= ~(CCM_CSCMR1_USDHC1_CLK_SEL | CCM_CSCMR1_USDHC2_CLK_SEL); +#if defined(IMXRT_USDHC1_CLK_SELECT) + reg |= IMXRT_USDHC1_CLK_SELECT; +#endif +#if defined(IMXRT_USDHC2_CLK_SELECT) + reg |= IMXRT_USDHC2_CLK_SELECT; +#endif + putreg32(reg, IMXRT_CCM_CSCMR1); + + /* Now divide down clocks by IMXRT_USDHC[1|2]_PODF_DIVIDER */ + + reg = getreg32(IMXRT_CCM_CSCDR1); + reg &= ~(CCM_CSCDR1_USDHC1_PODF_MASK | CCM_CSCDR1_USDHC2_PODF_MASK); +#if defined(IMXRT_USDHC1_PODF_DIVIDER) + reg |= CCM_CSCDR1_USDHC1_PODF( + CCM_PODF_FROM_DIVISOR(IMXRT_USDHC1_PODF_DIVIDER)); +#endif +#if defined(IMXRT_USDHC2_PODF_DIVIDER) + reg |= CCM_CSCDR1_USDHC2_PODF( + CCM_PODF_FROM_DIVISOR(IMXRT_USDHC2_PODF_DIVIDER)); +#endif + putreg32(reg, IMXRT_CCM_CSCDR1); +#endif + + /* Ensure platform memory clocks remain enabled in WFI */ + + reg = getreg32(IMXRT_CCM_CGPR); + reg |= CCM_CGPR_INT_MEM_CLK_LPM; + putreg32(reg, IMXRT_CCM_CGPR); + + /* Remain in run mode */ + + modifyreg32(IMXRT_CCM_CLPCR, + CCM_CLPCR_LPM_MASK, + CCM_CLPCR_LPM_RUN); +#endif +} diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_ehci.c b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_ehci.c new file mode 100644 index 000000000..e5b28c3f0 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_ehci.c @@ -0,0 +1,5452 @@ +/**************************************************************************** + * arch/arm/src/imxrt/imxrt_ehci.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 + +#include "arm_arch.h" +#include "chip.h" +#include "hardware/imxrt_usbotg.h" +#include "imxrt_periphclks.h" + +#if defined(CONFIG_IMXRT_USBOTG) && defined(CONFIG_USBHOST) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +/* Pre-requisites */ + +#if !defined(CONFIG_SCHED_WORKQUEUE) +# error Work queue support is required (CONFIG_SCHED_WORKQUEUE) +#elif !defined(CONFIG_SCHED_HPWORK) +# error Hi-priority work queue support is required (CONFIG_SCHED_HPWORK) +#endif + +/* Configurable number of Queue Head (QH) structures. The default is one per + * Root hub port plus one for EP0. + */ + +#ifndef CONFIG_IMXRT_EHCI_NQHS +# define CONFIG_IMXRT_EHCI_NQHS (IMXRT_EHCI_NRHPORT + 1) +#endif + +/* Configurable number of Queue Element Transfer Descriptor (qTDs). The + * default is one per root hub plus three from EP0. + */ + +#ifndef CONFIG_IMXRT_EHCI_NQTDS +# define CONFIG_IMXRT_EHCI_NQTDS (IMXRT_EHCI_NRHPORT + 3) +#endif + +/* Buffers must be aligned to the cache line size */ + +#define DCACHE_LINEMASK (ARMV7M_DCACHE_LINESIZE -1) + +/* Configurable size of a request/descriptor buffers */ + +#ifndef CONFIG_IMXRT_EHCI_BUFSIZE +# define CONFIG_IMXRT_EHCI_BUFSIZE 128 +#endif + +#define IMXRT_EHCI_BUFSIZE \ + ((CONFIG_IMXRT_EHCI_BUFSIZE + DCACHE_LINEMASK) & ~DCACHE_LINEMASK) + +/* Debug options */ + +#ifndef CONFIG_DEBUG_USB_INFO +# undef CONFIG_IMXRT_EHCI_REGDEBUG +#endif + +/* Isochronous transfers are not currently supported */ + +#undef CONFIG_USBHOST_ISOC_DISABLE +#define CONFIG_USBHOST_ISOC_DISABLE 1 + +/* Registers **************************************************************** + * Traditionally, NuttX specifies register locations using individual + * register offsets from a base address. That tradition is broken here and, + * instead, register blocks are represented as structures. This is done here + * because, in principle, EHCI operational register address may not be known + * at compile time; the operational registers lie at an offset specified in + * the 'caplength' byte of the Host Controller Capability Registers. + * + * However, for the case of the IMXRT EHCI, we know apriori that locations + * of these register blocks. + */ + +/* Host Controller Capability Registers */ + +#define HCCR ((struct ehci_hccr_s *)IMXRT_USBOTG_HCCR_BASE) + +/* Host Controller Operational Registers */ + +#define HCOR ((volatile struct ehci_hcor_s *)IMXRT_USBOTG_HCOR_BASE) + +/* Interrupts *************************************************************** + * This is the set of interrupts handled by this driver. + */ + +#define EHCI_HANDLED_INTS (EHCI_INT_USBINT | EHCI_INT_USBERRINT | \ + EHCI_INT_PORTSC | EHCI_INT_SYSERROR | \ + EHCI_INT_AAINT) + +/* The periodic frame list is a 4K-page aligned array of Frame List Link + * pointers. The length of the frame list may be programmable. The + * programmability of the periodic frame list is exported to system software + * via the HCCPARAMS register. If non-programmable, the length is 1024 + * elements. If programmable, the length can be selected by system software + * as one of 256, 512, or 1024 elements. + */ + +#define FRAME_LIST_SIZE 1024 + +/* DMA **********************************************************************/ + +/* For now, we are assuming an identity mapping between physical and virtual + * address spaces. + */ + +#define imxrt_physramaddr(a) (a) +#define imxrt_virtramaddr(a) (a) + +/* USB trace ****************************************************************/ + +#ifdef HAVE_USBHOST_TRACE +# define TR_FMT1 false +# define TR_FMT2 true + +# define TRENTRY(id,fmt1,string) {string} + +# define TRACE1_FIRST ((int)__TRACE1_BASEVALUE + 1) +# define TRACE1_INDEX(id) ((int)(id) - TRACE1_FIRST) +# define TRACE1_NSTRINGS TRACE1_INDEX(__TRACE1_NSTRINGS) + +# define TRACE2_FIRST ((int)__TRACE1_NSTRINGS + 1) +# define TRACE2_INDEX(id) ((int)(id) - TRACE2_FIRST) +# define TRACE2_NSTRINGS TRACE2_INDEX(__TRACE2_NSTRINGS) +#endif + +/* Port numbers */ + +#define RHPNDX(rh) ((rh)->hport.hport.port) +#define RHPORT(rh) (RHPNDX(rh)+1) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Internal representation of the EHCI Queue Head (QH) */ + +struct imxrt_epinfo_s; +struct imxrt_qh_s +{ + /* Fields visible to hardware */ + + struct ehci_qh_s hw; /* Hardware representation of the queue head */ + + /* Internal fields used by the EHCI driver */ + + struct imxrt_epinfo_s *epinfo; /* Endpoint used for the transfer */ + uint32_t fqp; /* First qTD in the list (physical address) */ + uint8_t pad[8]; /* Padding to assure 32-byte alignment */ +}; + +/* Internal representation of the EHCI Queue Element Transfer Descriptor + * (qTD) + */ + +struct imxrt_qtd_s +{ + /* Fields visible to hardware */ + + struct ehci_qtd_s hw; /* Hardware representation of the queue head */ + + /* Internal fields used by the EHCI driver */ +}; + +/* The following is used to manage lists of free QHs and qTDs */ + +struct imxrt_list_s +{ + struct imxrt_list_s *flink; /* Link to next entry in the list + * Variable length entry data follows + */ +}; + +/* List traversal call-out functions */ + +typedef int (*foreach_qh_t)(struct imxrt_qh_s *qh, uint32_t **bp, + void *arg); +typedef int (*foreach_qtd_t)(struct imxrt_qtd_s *qtd, uint32_t **bp, + void *arg); + +/* This structure describes one endpoint. */ + +struct imxrt_epinfo_s +{ + uint8_t epno:7; /* Endpoint number */ + uint8_t dirin:1; /* 1:IN endpoint 0:OUT endpoint */ + uint8_t devaddr:7; /* Device address */ + uint8_t toggle:1; /* Next data toggle */ +#ifndef CONFIG_USBHOST_INT_DISABLE + uint8_t interval; /* Polling interval */ +#endif + uint8_t status; /* Retained token status bits (for debug purposes) */ + volatile bool iocwait; /* TRUE: Thread is waiting for transfer completion */ + uint16_t maxpacket:11; /* Maximum packet size */ + uint16_t xfrtype:2; /* See USB_EP_ATTR_XFER_* definitions in usb.h */ + uint16_t speed:2; /* See USB_*_SPEED definitions in ehci.h */ + int result; /* The result of the transfer */ + uint32_t xfrd; /* On completion, will hold the number of bytes transferred */ + sem_t iocsem; /* Semaphore used to wait for transfer completion */ +#ifdef CONFIG_USBHOST_ASYNCH + usbhost_asynch_t callback; /* Transfer complete callback */ + void *arg; /* Argument that accompanies the callback */ +#endif +}; + +/* This structure retains the state of one root hub port */ + +struct imxrt_rhport_s +{ + /* Common device fields. This must be the first thing defined in the + * structure so that it is possible to simply cast from struct usbhost_s + * to struct imxrt_rhport_s. + */ + + struct usbhost_driver_s drvr; + + /* Root hub port status */ + + volatile bool connected; /* Connected to device */ + volatile bool lowspeed; /* Low speed device attached */ + struct imxrt_epinfo_s ep0; /* EP0 endpoint info */ + + /* This is the hub port description understood by class drivers */ + + struct usbhost_roothubport_s hport; +}; + +/* This structure retains the overall state of the USB host controller */ + +struct imxrt_ehci_s +{ + volatile bool pscwait; /* TRUE: Thread is waiting for port status change event */ + + sem_t exclsem; /* Support mutually exclusive access */ + sem_t pscsem; /* Semaphore to wait for port status change events */ + + struct imxrt_epinfo_s ep0; /* Endpoint 0 */ + struct imxrt_list_s *qhfree; /* List of free Queue Head (QH) structures */ + struct imxrt_list_s *qtdfree; /* List of free Queue Element Transfer Descriptor (qTD) */ + struct work_s work; /* Supports interrupt bottom half */ + +#ifdef CONFIG_USBHOST_HUB + /* Used to pass external hub port events */ + + volatile struct usbhost_hubport_s *hport; +#endif + + /* Root hub ports */ + + struct imxrt_rhport_s rhport[IMXRT_EHCI_NRHPORT]; +}; + +#ifdef HAVE_USBHOST_TRACE +/* USB trace codes */ + +enum usbhost_trace1codes_e +{ + __TRACE1_BASEVALUE = 0, /* This will force the first value to be 1 */ + + EHCI_TRACE1_SYSTEMERROR, /* EHCI ERROR: System error */ + EHCI_TRACE1_QTDFOREACH_FAILED, /* EHCI ERROR: imxrt_qtd_foreach failed */ + EHCI_TRACE1_QHALLOC_FAILED, /* EHCI ERROR: Failed to allocate a QH */ + EHCI_TRACE1_BUFTOOBIG, /* EHCI ERROR: Buffer too big */ + EHCI_TRACE1_REQQTDALLOC_FAILED, /* EHCI ERROR: Failed to allocate request qTD */ + EHCI_TRACE1_ADDBPL_FAILED, /* EHCI ERROR: imxrt_qtd_addbpl failed */ + EHCI_TRACE1_DATAQTDALLOC_FAILED, /* EHCI ERROR: Failed to allocate data buffer qTD */ + EHCI_TRACE1_DEVDISCONNECTED, /* EHCI ERROR: Device disconnected */ + EHCI_TRACE1_QHCREATE_FAILED, /* EHCI ERROR: imxrt_qh_create failed */ + EHCI_TRACE1_QTDSETUP_FAILED, /* EHCI ERROR: imxrt_qtd_setupphase failed */ + + EHCI_TRACE1_QTDDATA_FAILED, /* EHCI ERROR: imxrt_qtd_dataphase failed */ + EHCI_TRACE1_QTDSTATUS_FAILED, /* EHCI ERROR: imxrt_qtd_statusphase failed */ + EHCI_TRACE1_TRANSFER_FAILED, /* EHCI ERROR: Transfer failed */ + EHCI_TRACE1_QHFOREACH_FAILED, /* EHCI ERROR: imxrt_qh_foreach failed: */ + EHCI_TRACE1_SYSERR_INTR, /* EHCI: Host System Error Interrupt */ + EHCI_TRACE1_USBERR_INTR, /* EHCI: USB Error Interrupt (USBERRINT) Interrupt */ + EHCI_TRACE1_EPALLOC_FAILED, /* EHCI ERROR: Failed to allocate EP info structure */ + EHCI_TRACE1_BADXFRTYPE, /* EHCI ERROR: Support for transfer type not implemented */ + EHCI_TRACE1_HCHALTED_TIMEOUT, /* EHCI ERROR: Timed out waiting for HCHalted */ + EHCI_TRACE1_QHPOOLALLOC_FAILED, /* EHCI ERROR: Failed to allocate the QH pool */ + + EHCI_TRACE1_QTDPOOLALLOC_FAILED, /* EHCI ERROR: Failed to allocate the qTD pool */ + EHCI_TRACE1_PERFLALLOC_FAILED, /* EHCI ERROR: Failed to allocate the periodic frame list */ + EHCI_TRACE1_RESET_FAILED, /* EHCI ERROR: imxrt_reset failed */ + EHCI_TRACE1_RUN_FAILED, /* EHCI ERROR: EHCI Failed to run */ + EHCI_TRACE1_IRQATTACH_FAILED, /* EHCI ERROR: Failed to attach IRQ */ + +#ifdef HAVE_USBHOST_TRACE_VERBOSE + EHCI_VTRACE1_PORTSC_CSC, /* EHCI Connect Status Change */ + EHCI_VTRACE1_PORTSC_CONNALREADY, /* EHCI Already connected */ + EHCI_VTRACE1_PORTSC_DISCALREADY, /* EHCI Already disconnected */ + EHCI_VTRACE1_TOPHALF, /* EHCI Interrupt top half */ + EHCI_VTRACE1_AAINTR, /* EHCI Async Advance Interrupt */ + + EHCI_VTRACE1_CLASSENUM, /* EHCI Hub port CLASS enumeration */ + EHCI_VTRACE1_USBINTR, /* EHCI USB Interrupt (USBINT) Interrupt */ + EHCI_VTRACE1_ENUM_DISCONN, /* EHCI Enumeration not connected */ + EHCI_VTRACE1_INITIALIZING, /* EHCI Initializing EHCI Stack */ + EHCI_VTRACE1_HCCPARAMS, /* EHCI HCCPARAMS */ + EHCI_VTRACE1_INIITIALIZED, /* EHCI USB EHCI Initialized */ +#endif + + __TRACE1_NSTRINGS, /* Separates the format 1 from the format 2 strings */ + + EHCI_TRACE2_EPSTALLED, /* EHCI EP Stalled */ + EHCI_TRACE2_EPIOERROR, /* EHCI ERROR: EP TOKEN */ + EHCI_TRACE2_CLASSENUM_FAILED, /* EHCI usbhost_enumerate() failed */ + +#ifdef HAVE_USBHOST_TRACE_VERBOSE + EHCI_VTRACE2_ASYNCXFR, /* EHCI Async transfer */ + EHCI_VTRACE2_INTRXFR, /* EHCI Interrupt Transfer */ + EHCI_VTRACE2_IOCCHECK, /* EHCI IOC */ + EHCI_VTRACE2_PORTSC, /* EHCI PORTSC */ + EHCI_VTRACE2_PORTSC_CONNECTED, /* EHCI RHPort connected */ + EHCI_VTRACE2_PORTSC_DISCONND, /* EHCI RHport disconnected */ + EHCI_VTRACE2_MONWAKEUP, /* EHCI RHPort connected wakeup */ + + EHCI_VTRACE2_EPALLOC, /* EHCI EPALLOC */ + EHCI_VTRACE2_CTRLINOUT, /* EHCI CTRLIN/OUT */ + EHCI_VTRACE2_HCIVERSION, /* EHCI HCIVERSION */ + EHCI_VTRACE2_HCSPARAMS, /* EHCI HCSPARAMS */ +#endif + + __TRACE2_NSTRINGS /* Total number of enumeration values */ +}; + +/* USB trace data structure */ + +struct imxrt_ehci_trace_s +{ +#if 0 + uint16_t id; + bool fmt2; +#endif + FAR const char *string; +}; + +#endif /* HAVE_USBHOST_TRACE */ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Register operations ******************************************************/ + +static uint16_t imxrt_read16(const uint8_t *addr); +static uint32_t imxrt_read32(const uint8_t *addr); +#if 0 /* Not used */ +static void imxrt_write16(uint16_t memval, uint8_t *addr); +static void imxrt_write32(uint32_t memval, uint8_t *addr); +#endif + +#ifdef CONFIG_ENDIAN_BIG +static uint16_t imxrt_swap16(uint16_t value); +static uint32_t imxrt_swap32(uint32_t value); +#else +# define imxrt_swap16(value) (value) +# define imxrt_swap32(value) (value) +#endif + +#ifdef CONFIG_IMXRT_EHCI_REGDEBUG +static void imxrt_printreg(volatile uint32_t *regaddr, uint32_t regval, + bool iswrite); +static void imxrt_checkreg(volatile uint32_t *regaddr, uint32_t regval, + bool iswrite); +static uint32_t imxrt_getreg(volatile uint32_t *regaddr); +static void imxrt_putreg(uint32_t regval, volatile uint32_t *regaddr); +#else +static inline uint32_t imxrt_getreg(volatile uint32_t *regaddr); +static inline void imxrt_putreg(uint32_t regval, volatile uint32_t *regaddr); +#endif +static int ehci_wait_usbsts(uint32_t maskbits, uint32_t donebits, + unsigned int delay); + +/* Semaphores ***************************************************************/ + +static int imxrt_takesem(sem_t *sem); +static int imxrt_takesem_noncancelable(sem_t *sem); +#define imxrt_givesem(s) nxsem_post(s); + +/* Allocators ***************************************************************/ + +static struct imxrt_qh_s *imxrt_qh_alloc(void); +static void imxrt_qh_free(struct imxrt_qh_s *qh); +static struct imxrt_qtd_s *imxrt_qtd_alloc(void); +static void imxrt_qtd_free(struct imxrt_qtd_s *qtd); + +/* List Management **********************************************************/ + +static int imxrt_qh_foreach(struct imxrt_qh_s *qh, uint32_t **bp, + foreach_qh_t handler, void *arg); +static int imxrt_qtd_foreach(struct imxrt_qh_s *qh, foreach_qtd_t handler, + void *arg); +static int imxrt_qtd_discard(struct imxrt_qtd_s *qtd, uint32_t **bp, + void *arg); +static int imxrt_qh_discard(struct imxrt_qh_s *qh); + +/* Cache Operations *********************************************************/ + +#if 0 /* Not used */ +static int imxrt_qtd_invalidate(struct imxrt_qtd_s *qtd, uint32_t **bp, + void *arg); +static int imxrt_qh_invalidate(struct imxrt_qh_s *qh); +#endif +static int imxrt_qtd_flush(struct imxrt_qtd_s *qtd, uint32_t **bp, + void *arg); +static int imxrt_qh_flush(struct imxrt_qh_s *qh); + +/* Endpoint Transfer Handling ***********************************************/ + +#ifdef CONFIG_IMXRT_EHCI_REGDEBUG +static void imxrt_qtd_print(struct imxrt_qtd_s *qtd); +static void imxrt_qh_print(struct imxrt_qh_s *qh); +static int imxrt_qtd_dump(struct imxrt_qtd_s *qtd, uint32_t **bp, void *arg); +static int imxrt_qh_dump(struct imxrt_qh_s *qh, uint32_t **bp, void *arg); +#else +# define imxrt_qtd_print(qtd) +# define imxrt_qh_print(qh) +# define imxrt_qtd_dump(qtd, bp, arg) OK +# define imxrt_qh_dump(qh, bp, arg) OK +#endif + +static inline uint8_t imxrt_ehci_speed(uint8_t usbspeed); +static int imxrt_ioc_setup(struct imxrt_rhport_s *rhport, + struct imxrt_epinfo_s *epinfo); +static int imxrt_ioc_wait(struct imxrt_epinfo_s *epinfo); +static void imxrt_qh_enqueue(struct imxrt_qh_s *qhead, + struct imxrt_qh_s *qh); +static struct imxrt_qh_s *imxrt_qh_create(struct imxrt_rhport_s *rhport, + struct imxrt_epinfo_s *epinfo); +static int imxrt_qtd_addbpl(struct imxrt_qtd_s *qtd, const void *buffer, + size_t buflen); +static struct imxrt_qtd_s *imxrt_qtd_setupphase( + struct imxrt_epinfo_s *epinfo, const struct usb_ctrlreq_s *req); +static struct imxrt_qtd_s *imxrt_qtd_dataphase(struct imxrt_epinfo_s *epinfo, + void *buffer, int buflen, uint32_t tokenbits); +static struct imxrt_qtd_s *imxrt_qtd_statusphase(uint32_t tokenbits); +static ssize_t imxrtimxrt_virtramaddr_async_setup( + struct imxrt_rhport_s *rhport, struct imxrt_epinfo_s *epinfo, + const struct usb_ctrlreq_s *req, uint8_t *buffer, size_t buflen); +#ifndef CONFIG_USBHOST_INT_DISABLE +static int imxrt_intr_setup(struct imxrt_rhport_s *rhport, + struct imxrt_epinfo_s *epinfo, uint8_t *buffer, size_t buflen); +#endif +static ssize_t imxrt_transfer_wait(struct imxrt_epinfo_s *epinfo); +#ifdef CONFIG_USBHOST_ASYNCH +static inline int imxrt_ioc_async_setup(struct imxrt_rhport_s *rhport, + struct imxrt_epinfo_s *epinfo, usbhost_asynch_t callback, + FAR void *arg); +static void imxrt_asynch_completion(struct imxrt_epinfo_s *epinfo); +#endif + +/* Interrupt Handling *******************************************************/ + +static int imxrt_qtd_ioccheck(struct imxrt_qtd_s *qtd, uint32_t **bp, + void *arg); +static int imxrt_qh_ioccheck(struct imxrt_qh_s *qh, uint32_t **bp, + void *arg); +#ifdef CONFIG_USBHOST_ASYNCH +static int imxrt_qtd_cancel(struct imxrt_qtd_s *qtd, uint32_t **bp, + void *arg); +static int imxrt_qh_cancel(struct imxrt_qh_s *qh, uint32_t **bp, void *arg); +#endif +static inline void imxrt_ioc_bottomhalf(void); +static inline void imxrt_portsc_bottomhalf(void); +static inline void imxrt_syserr_bottomhalf(void); +static inline void imxrt_async_advance_bottomhalf(void); +static void imxrt_ehci_bottomhalf(FAR void *arg); +static int imxrt_ehci_interrupt(int irq, FAR void *context, FAR void *arg); + +/* USB Host Controller Operations *******************************************/ + +static int imxrt_wait(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s **hport); +static int imxrt_rh_enumerate(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport); +static int imxrt_enumerate(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport); + +static int imxrt_ep0configure(FAR struct usbhost_driver_s *drvr, + usbhost_ep_t ep0, uint8_t funcaddr, uint8_t speed, + uint16_t maxpacketsize); +static int imxrt_epalloc(FAR struct usbhost_driver_s *drvr, + const FAR struct usbhost_epdesc_s *epdesc, usbhost_ep_t *ep); +static int imxrt_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep); +static int imxrt_alloc(FAR struct usbhost_driver_s *drvr, + FAR uint8_t **buffer, FAR size_t *maxlen); +static int imxrt_free(FAR struct usbhost_driver_s *drvr, + FAR uint8_t *buffer); +static int imxrt_ioalloc(FAR struct usbhost_driver_s *drvr, + FAR uint8_t **buffer, size_t buflen); +static int imxrt_iofree(FAR struct usbhost_driver_s *drvr, + FAR uint8_t *buffer); +static int imxrt_ctrlin(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, + FAR const struct usb_ctrlreq_s *req, FAR uint8_t *buffer); +static int imxrt_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, + FAR const struct usb_ctrlreq_s *req, FAR const uint8_t *buffer); +static ssize_t imxrt_transfer(FAR struct usbhost_driver_s *drvr, + usbhost_ep_t ep, FAR uint8_t *buffer, size_t buflen); +#ifdef CONFIG_USBHOST_ASYNCH +static int imxrt_asynch(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, + FAR uint8_t *buffer, size_t buflen, usbhost_asynch_t callback, + FAR void *arg); +#endif +static int imxrt_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep); +#ifdef CONFIG_USBHOST_HUB +static int imxrt_connect(FAR struct usbhost_driver_s *drvr, + FAR struct usbhost_hubport_s *hport, bool connected); +#endif +static void imxrt_disconnect(FAR struct usbhost_driver_s *drvr, + FAR struct usbhost_hubport_s *hport); + +/* Initialization ***********************************************************/ + +static int imxrt_reset(void); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* In this driver implementation, support is provided for only a single a + * single USB device. All status information can be simply retained in a + * single global instance. + */ + +static struct imxrt_ehci_s g_ehci; + +/* This is the connection/enumeration interface */ + +static struct usbhost_connection_s g_ehciconn; + +/* Maps USB chapter 9 speed to EHCI speed */ + +static const uint8_t g_ehci_speed[4] = +{ + 0, EHCI_LOW_SPEED, EHCI_FULL_SPEED, EHCI_HIGH_SPEED +}; + +/* The head of the asynchronous queue */ + +static struct imxrt_qh_s g_asynchead aligned_data(32); + +#ifndef CONFIG_USBHOST_INT_DISABLE +/* The head of the periodic queue */ + +static struct imxrt_qh_s g_intrhead aligned_data(32); + +/* The frame list */ + +#ifdef CONFIG_IMXRT_EHCI_PREALLOCATE +static uint32_t g_framelist[FRAME_LIST_SIZE] aligned_data(4096); +#else +static uint32_t *g_framelist; +#endif +#endif /* CONFIG_USBHOST_INT_DISABLE */ + +#ifdef CONFIG_IMXRT_EHCI_PREALLOCATE +/* Pools of pre-allocated data structures. These will all be linked into the + * free lists within g_ehci. These must all be aligned to 32-byte boundaries + */ + +/* Queue Head (QH) pool */ + +static struct imxrt_qh_s g_qhpool[CONFIG_IMXRT_EHCI_NQHS] + aligned_data(32); + +/* Queue Element Transfer Descriptor (qTD) pool */ + +static struct imxrt_qtd_s g_qtdpool[CONFIG_IMXRT_EHCI_NQTDS] + aligned_data(32); + +#else +/* Pools of dynamically data structures. These will all be linked into the + * free lists within g_ehci. These must all be aligned to 32-byte boundaries + */ + +/* Queue Head (QH) pool */ + +static struct imxrt_qh_s *g_qhpool; + +/* Queue Element Transfer Descriptor (qTD) pool */ + +static struct imxrt_qtd_s *g_qtdpool; + +#endif + +#ifdef HAVE_USBHOST_TRACE +/* USB trace strings */ + +static const struct imxrt_ehci_trace_s g_trace1[TRACE1_NSTRINGS] = +{ + TRENTRY(EHCI_TRACE1_SYSTEMERROR, TR_FMT1, + "EHCI ERROR: System error: %06x\n"), + TRENTRY(EHCI_TRACE1_QTDFOREACH_FAILED, TR_FMT1, + "EHCI ERROR: imxrt_qtd_foreach failed: %d\n"), + TRENTRY(EHCI_TRACE1_QHALLOC_FAILED, TR_FMT1, + "EHCI ERROR: Failed to allocate a QH\n"), + TRENTRY(EHCI_TRACE1_BUFTOOBIG, TR_FMT1, + "EHCI ERROR: Buffer too big. Remaining %d\n"), + TRENTRY(EHCI_TRACE1_REQQTDALLOC_FAILED, TR_FMT1, + "EHCI ERROR: Failed to allocate request qTD"), + TRENTRY(EHCI_TRACE1_ADDBPL_FAILED, TR_FMT1, + "EHCI ERROR: imxrt_qtd_addbpl failed: %d\n"), + TRENTRY(EHCI_TRACE1_DATAQTDALLOC_FAILED, TR_FMT1, + "EHCI ERROR: Failed to allocate data buffer qTD, 0"), + TRENTRY(EHCI_TRACE1_DEVDISCONNECTED, TR_FMT1, + "EHCI ERROR: Device disconnected %d\n"), + TRENTRY(EHCI_TRACE1_QHCREATE_FAILED, TR_FMT1, + "EHCI ERROR: imxrt_qh_create failed\n"), + TRENTRY(EHCI_TRACE1_QTDSETUP_FAILED, TR_FMT1, + "EHCI ERROR: imxrt_qtd_setupphase failed\n"), + + TRENTRY(EHCI_TRACE1_QTDDATA_FAILED, TR_FMT1, + "EHCI ERROR: imxrt_qtd_dataphase failed\n"), + TRENTRY(EHCI_TRACE1_QTDSTATUS_FAILED, TR_FMT1, + "EHCI ERROR: imxrt_qtd_statusphase failed\n"), + TRENTRY(EHCI_TRACE1_TRANSFER_FAILED, TR_FMT1, + "EHCI ERROR: Transfer failed %d\n"), + TRENTRY(EHCI_TRACE1_QHFOREACH_FAILED, TR_FMT1, + "EHCI ERROR: imxrt_qh_foreach failed: %d\n"), + TRENTRY(EHCI_TRACE1_SYSERR_INTR, TR_FMT1, + "EHCI: Host System Error Interrupt\n"), + TRENTRY(EHCI_TRACE1_USBERR_INTR, TR_FMT1, + "EHCI: USB Error Interrupt (USBERRINT) Interrupt: %06x\n"), + TRENTRY(EHCI_TRACE1_EPALLOC_FAILED, TR_FMT1, + "EHCI ERROR: Failed to allocate EP info structure\n"), + TRENTRY(EHCI_TRACE1_BADXFRTYPE, TR_FMT1, + "EHCI ERROR: Support for transfer type %d not implemented\n"), + TRENTRY(EHCI_TRACE1_HCHALTED_TIMEOUT, TR_FMT1, + "EHCI ERROR: Timed out waiting for HCHalted. USBSTS: %06x\n"), + TRENTRY(EHCI_TRACE1_QHPOOLALLOC_FAILED, TR_FMT1, + "EHCI ERROR: Failed to allocate the QH pool\n"), + + TRENTRY(EHCI_TRACE1_QTDPOOLALLOC_FAILED, TR_FMT1, + "EHCI ERROR: Failed to allocate the qTD pool\n"), + TRENTRY(EHCI_TRACE1_PERFLALLOC_FAILED, TR_FMT1, + "EHCI ERROR: Failed to allocate the periodic frame list\n"), + TRENTRY(EHCI_TRACE1_RESET_FAILED, TR_FMT1, + "EHCI ERROR: imxrt_reset failed: %d\n"), + TRENTRY(EHCI_TRACE1_RUN_FAILED, TR_FMT1, + "EHCI ERROR: EHCI Failed to run: USBSTS=%06x\n"), + TRENTRY(EHCI_TRACE1_IRQATTACH_FAILED, TR_FMT1, + "EHCI ERROR: Failed to attach IRQ%d\n"), + +#ifdef HAVE_USBHOST_TRACE_VERBOSE + TRENTRY(EHCI_VTRACE1_PORTSC_CSC, TR_FMT1, + "EHCI Connect Status Change: %06x\n"), + TRENTRY(EHCI_VTRACE1_PORTSC_CONNALREADY, TR_FMT1, + "EHCI Already connected: %06x\n"), + TRENTRY(EHCI_VTRACE1_PORTSC_DISCALREADY, TR_FMT1, + "EHCI Already disconnected: %06x\n"), + TRENTRY(EHCI_VTRACE1_TOPHALF, TR_FMT1, + "EHCI Interrupt: %06x\n"), + TRENTRY(EHCI_VTRACE1_AAINTR, TR_FMT1, + "EHCI Async Advance Interrupt\n"), + + TRENTRY(EHCI_VTRACE1_CLASSENUM, TR_FMT1, + "EHCI Hub port %d: Enumerate the device\n"), + TRENTRY(EHCI_VTRACE1_USBINTR, TR_FMT1, + "EHCI USB Interrupt (USBINT) Interrupt: %06x\n"), + TRENTRY(EHCI_VTRACE1_ENUM_DISCONN, TR_FMT1, + "EHCI Enumeration not connected\n"), + TRENTRY(EHCI_VTRACE1_INITIALIZING, TR_FMT1, + "EHCI Initializing EHCI Stack\n"), + TRENTRY(EHCI_VTRACE1_HCCPARAMS, TR_FMT1, + "EHCI HCCPARAMS=%06x\n"), + TRENTRY(EHCI_VTRACE1_INIITIALIZED, TR_FMT1, + "EHCI USB EHCI Initialized\n"), +#endif +}; + +static const struct imxrt_ehci_trace_s g_trace2[TRACE2_NSTRINGS] = +{ + TRENTRY(EHCI_TRACE2_EPSTALLED, TR_FMT2, + "EHCI EP%d Stalled: TOKEN=%04x\n"), + TRENTRY(EHCI_TRACE2_EPIOERROR, TR_FMT2, + "EHCI ERROR: EP%d TOKEN=%04x\n"), + TRENTRY(EHCI_TRACE2_CLASSENUM_FAILED, TR_FMT2, + "EHCI Hub port %d usbhost_enumerate() failed: %d\n"), + +#ifdef HAVE_USBHOST_TRACE_VERBOSE + TRENTRY(EHCI_VTRACE2_ASYNCXFR, TR_FMT2, + "EHCI Async transfer EP%d buflen=%d\n"), + TRENTRY(EHCI_VTRACE2_INTRXFR, TR_FMT2, + "EHCI Intr Transfer EP%d buflen=%d\n"), + TRENTRY(EHCI_VTRACE2_IOCCHECK, TR_FMT2, + "EHCI IOC EP%d TOKEN=%04x\n"), + TRENTRY(EHCI_VTRACE2_PORTSC, TR_FMT2, + "EHCI PORTSC%d: %04x\n"), + TRENTRY(EHCI_VTRACE2_PORTSC_CONNECTED, TR_FMT2, + "EHCI RHPort%d connected, pscwait: %d\n"), + TRENTRY(EHCI_VTRACE2_PORTSC_DISCONND, TR_FMT2, + "EHCI RHport%d disconnected, pscwait: %d\n"), + TRENTRY(EHCI_VTRACE2_MONWAKEUP, TR_FMT2, + "EHCI RHPort%d connected: %d\n"), + + TRENTRY(EHCI_VTRACE2_EPALLOC, TR_FMT2, + "EHCI EPALLOC: EP%d TYPE=%d\n"), + TRENTRY(EHCI_VTRACE2_CTRLINOUT, TR_FMT2, + "EHCI CTRLIN/OUT: RHPort%d req: %02x\n"), + TRENTRY(EHCI_VTRACE2_HCIVERSION, TR_FMT2, + "EHCI HCIVERSION %x.%02x\n"), + TRENTRY(EHCI_VTRACE2_HCSPARAMS, TR_FMT2, + "EHCI nports=%d, HCSPARAMS=%04x\n"), +#endif +}; +#endif /* HAVE_USBHOST_TRACE */ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imxrt_read16 + * + * Description: + * Read 16-bit little endian data + * + ****************************************************************************/ + +static uint16_t imxrt_read16(const uint8_t *addr) +{ +#ifdef CONFIG_ENDIAN_BIG + return (uint16_t)addr[0] << 8 | (uint16_t)addr[1]; +#else + return (uint16_t)addr[1] << 8 | (uint16_t)addr[0]; +#endif +} + +/**************************************************************************** + * Name: imxrt_read32 + * + * Description: + * Read 32-bit little endian data + * + ****************************************************************************/ + +static inline uint32_t imxrt_read32(const uint8_t *addr) +{ +#ifdef CONFIG_ENDIAN_BIG + return (uint32_t)imxrt_read16(&addr[0]) << 16 | + (uint32_t)imxrt_read16(&addr[2]); +#else + return (uint32_t)imxrt_read16(&addr[2]) << 16 | + (uint32_t)imxrt_read16(&addr[0]); +#endif +} + +/**************************************************************************** + * Name: imxrt_write16 + * + * Description: + * Write 16-bit little endian data + * + ****************************************************************************/ + +#if 0 /* Not used */ +static void imxrt_write16(uint16_t memval, uint8_t *addr) +{ +#ifdef CONFIG_ENDIAN_BIG + addr[0] = memval & 0xff; + addr[1] = memval >> 8; +#else + addr[0] = memval >> 8; + addr[1] = memval & 0xff; +#endif +} +#endif + +/**************************************************************************** + * Name: imxrt_write32 + * + * Description: + * Write 32-bit little endian data + * + ****************************************************************************/ + +#if 0 /* Not used */ +static void imxrt_write32(uint32_t memval, uint8_t *addr) +{ +#ifdef CONFIG_ENDIAN_BIG + imxrt_write16(memval >> 16, &addr[0]); + imxrt_write16(memval & 0xffff, &addr[2]); +#else + imxrt_write16(memval & 0xffff, &addr[0]); + imxrt_write16(memval >> 16, &addr[2]); +#endif +} +#endif + +/**************************************************************************** + * Name: imxrt_swap16 + * + * Description: + * Swap bytes on a 16-bit value + * + ****************************************************************************/ + +#ifdef CONFIG_ENDIAN_BIG +static uint16_t imxrt_swap16(uint16_t value) +{ + return ((value >> 8) & 0xff) | ((value & 0xff) << 8); +} +#endif + +/**************************************************************************** + * Name: imxrt_swap32 + * + * Description: + * Swap bytes on a 32-bit value + * + ****************************************************************************/ + +#ifdef CONFIG_ENDIAN_BIG +static uint32_t imxrt_swap32(uint32_t value) +{ + return (uint32_t)imxrt_swap16((uint16_t)((value >> 16) & 0xffff)) | + (uint32_t)imxrt_swap16((uint16_t)(value & 0xffff)) << 16; +} +#endif + +/**************************************************************************** + * Name: imxrt_printreg + * + * Description: + * Print the contents of a IMXRT EHCI register + * + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_EHCI_REGDEBUG +static void imxrt_printreg(volatile uint32_t *regaddr, uint32_t regval, + bool iswrite) +{ + uinfo("%08x%s%08x\n", (uintptr_t)regaddr, iswrite ? "<-" : "->", regval); +} +#endif + +/**************************************************************************** + * Name: imxrt_checkreg + * + * Description: + * Check if it is time to output debug information for accesses to a IMXRT + * EHCI register + * + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_EHCI_REGDEBUG +static void imxrt_checkreg(volatile uint32_t *regaddr, uint32_t regval, + bool iswrite) +{ + static uint32_t *prevaddr = NULL; + static uint32_t preval = 0; + static uint32_t count = 0; + static bool prevwrite = false; + + /* Is this the same value that we read from/wrote to the same register last + * time? Are we polling the register? If so, suppress the output. + */ + + if (regaddr == prevaddr && regval == preval && prevwrite == iswrite) + { + /* Yes.. Just increment the count */ + + count++; + } + else + { + /* No this is a new address or value or operation. Were there any + * duplicate accesses before this one? + */ + + if (count > 0) + { + /* Yes.. Just one? */ + + if (count == 1) + { + /* Yes.. Just one */ + + imxrt_printreg(prevaddr, preval, prevwrite); + } + else + { + /* No.. More than one. */ + + uinfo("[repeats %d more times]\n", count); + } + } + + /* Save the new address, value, count, and operation for next time */ + + prevaddr = (uint32_t *)regaddr; + preval = regval; + count = 0; + prevwrite = iswrite; + + /* Show the new register access */ + + imxrt_printreg(regaddr, regval, iswrite); + } +} +#endif + +/**************************************************************************** + * Name: imxrt_getreg + * + * Description: + * Get the contents of an IMXRT register + * + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_EHCI_REGDEBUG +static uint32_t imxrt_getreg(volatile uint32_t *regaddr) +{ + /* Read the value from the register */ + + uint32_t regval = *regaddr; + + /* Check if we need to print this value */ + + imxrt_checkreg(regaddr, regval, false); + return regval; +} +#else +static inline uint32_t imxrt_getreg(volatile uint32_t *regaddr) +{ + return *regaddr; +} +#endif + +/**************************************************************************** + * Name: imxrt_putreg + * + * Description: + * Set the contents of an IMXRT register to a value + * + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_EHCI_REGDEBUG +static void imxrt_putreg(uint32_t regval, volatile uint32_t *regaddr) +{ + /* Check if we need to print this value */ + + imxrt_checkreg(regaddr, regval, true); + + /* Write the value */ + + *regaddr = regval; +} +#else +static inline void imxrt_putreg(uint32_t regval, volatile uint32_t *regaddr) +{ + *regaddr = regval; +} +#endif + +/**************************************************************************** + * Name: ehci_wait_usbsts + * + * Description: + * Wait for either (1) a field in the USBSTS register to take a specific + * value, (2) for a timeout to occur, or (3) a error to occur. Return + * a value to indicate which terminated the wait. + * + ****************************************************************************/ + +static int ehci_wait_usbsts(uint32_t maskbits, uint32_t donebits, + unsigned int delay) +{ + uint32_t regval; + unsigned int timeout; + + timeout = 0; + do + { + /* Wait 5usec before trying again */ + + up_udelay(5); + timeout += 5; + + /* Read the USBSTS register and check for a system error */ + + regval = imxrt_getreg(&HCOR->usbsts); + if ((regval & EHCI_INT_SYSERROR) != 0) + { + usbhost_trace1(EHCI_TRACE1_SYSTEMERROR, regval); + return -EIO; + } + + /* Mask out the bits of interest */ + + regval &= maskbits; + + /* Loop until the masked bits take the specified value or until a + * timeout occurs. + */ + } + while (regval != donebits && timeout < delay); + + /* We got here because either the waited for condition or a timeout + * occurred. Return a value to indicate which. + */ + + return (regval == donebits) ? OK : -ETIMEDOUT; +} + +/**************************************************************************** + * Name: imxrt_takesem + * + * Description: + * This is just a wrapper to handle the annoying behavior of semaphore + * waits that return due to the receipt of a signal. + * + ****************************************************************************/ + +static int imxrt_takesem(sem_t *sem) +{ + return nxsem_wait_uninterruptible(sem); +} + +/**************************************************************************** + * Name: imxrt_takesem_noncancelable + * + * Description: + * This is just a wrapper to handle the annoying behavior of semaphore + * waits that return due to the receipt of a signal. This version also + * ignores attempts to cancel the thread. + * + ****************************************************************************/ + +static int imxrt_takesem_noncancelable(sem_t *sem) +{ + int result; + int ret = OK; + + do + { + result = nxsem_wait_uninterruptible(sem); + + /* The only expected error is ECANCELED which would occur if the + * calling thread were canceled. + */ + + DEBUGASSERT(result == OK || result == -ECANCELED); + if (ret == OK && result < 0) + { + ret = result; + } + } + while (result < 0); + + return ret; +} + +/**************************************************************************** + * Name: imxrt_qh_alloc + * + * Description: + * Allocate a Queue Head (QH) structure by removing it from the free list + * + * Assumption: Caller holds the exclsem + * + ****************************************************************************/ + +static struct imxrt_qh_s *imxrt_qh_alloc(void) +{ + struct imxrt_qh_s *qh; + + /* Remove the QH structure from the freelist */ + + qh = (struct imxrt_qh_s *)g_ehci.qhfree; + if (qh) + { + g_ehci.qhfree = ((struct imxrt_list_s *)qh)->flink; + memset(qh, 0, sizeof(struct imxrt_qh_s)); + } + + return qh; +} + +/**************************************************************************** + * Name: imxrt_qh_free + * + * Description: + * Free a Queue Head (QH) structure by returning it to the free list + * + * Assumption: Caller holds the exclsem + * + ****************************************************************************/ + +static void imxrt_qh_free(struct imxrt_qh_s *qh) +{ + struct imxrt_list_s *entry = (struct imxrt_list_s *)qh; + + /* Put the QH structure back into the free list */ + + entry->flink = g_ehci.qhfree; + g_ehci.qhfree = entry; +} + +/**************************************************************************** + * Name: imxrt_qtd_alloc + * + * Description: + * Allocate a Queue Element Transfer Descriptor (qTD) by removing it from + * the free list + * + * Assumption: Caller holds the exclsem + * + ****************************************************************************/ + +static struct imxrt_qtd_s *imxrt_qtd_alloc(void) +{ + struct imxrt_qtd_s *qtd; + + /* Remove the qTD from the freelist */ + + qtd = (struct imxrt_qtd_s *)g_ehci.qtdfree; + if (qtd) + { + g_ehci.qtdfree = ((struct imxrt_list_s *)qtd)->flink; + memset(qtd, 0, sizeof(struct imxrt_qtd_s)); + } + + return qtd; +} + +/**************************************************************************** + * Name: imxrt_qtd_free + * + * Description: + * Free a Queue Element Transfer Descriptor (qTD) by returning it to the + * free list + * + * Assumption: + * Caller holds the exclsem + * + ****************************************************************************/ + +static void imxrt_qtd_free(struct imxrt_qtd_s *qtd) +{ + struct imxrt_list_s *entry = (struct imxrt_list_s *)qtd; + + /* Put the qTD back into the free list */ + + entry->flink = g_ehci.qtdfree; + g_ehci.qtdfree = entry; +} + +/**************************************************************************** + * Name: imxrt_qh_foreach + * + * Description: + * Give the first entry in a list of Queue Head (QH) structures, call the + * handler for each QH structure in the list (including the one at the head + * of the list). + * + ****************************************************************************/ + +static int imxrt_qh_foreach(struct imxrt_qh_s *qh, uint32_t **bp, + foreach_qh_t handler, void *arg) +{ + struct imxrt_qh_s *next; + uintptr_t physaddr; + int ret; + + DEBUGASSERT(qh && handler); + while (qh) + { + /* Is this the end of the list? Check the horizontal link pointer + * (HLP) terminate (T) bit. If T==1, then the HLP address is not + * valid. + */ + + physaddr = imxrt_swap32(qh->hw.hlp); + if ((physaddr & QH_HLP_T) != 0) + { + /* Set the next pointer to NULL. This will terminate the loop. */ + + next = NULL; + } + + /* Is the next QH the asynchronous list head which will always be at + * the end of the asynchronous queue? + */ + + else if (imxrt_virtramaddr(physaddr & QH_HLP_MASK) == + (uintptr_t)&g_asynchead) + { + /* That will also terminate the loop */ + + next = NULL; + } + + /* Otherwise, there is a QH structure after this one that describes + * another transaction. + */ + + else + { + physaddr = imxrt_swap32(qh->hw.hlp) & QH_HLP_MASK; + next = (struct imxrt_qh_s *)imxrt_virtramaddr(physaddr); + } + + /* Perform the user action on this entry. The action might result in + * unlinking the entry! But that is okay because we already have the + * next QH pointer. + * + * Notice that we do not manage the back pointer (bp). If the call- + * out uses it, it must update it as necessary. + */ + + ret = handler(qh, bp, arg); + + /* If the handler returns any non-zero value, then terminate the + * traversal early. + */ + + if (ret != 0) + { + return ret; + } + + /* Set up to visit the next entry */ + + qh = next; + } + + return OK; +} + +/**************************************************************************** + * Name: imxrt_qtd_foreach + * + * Description: + * Give a Queue Head (QH) instance, call the handler for each qTD structure + * in the queue. + * + ****************************************************************************/ + +static int imxrt_qtd_foreach(struct imxrt_qh_s *qh, foreach_qtd_t handler, + void *arg) +{ + struct imxrt_qtd_s *qtd; + struct imxrt_qtd_s *next; + uintptr_t physaddr; + uint32_t *bp; + int ret; + + DEBUGASSERT(qh && handler); + + /* Handle the special case where the queue is empty */ + + bp = &qh->fqp; /* Start of qTDs in original list */ + physaddr = imxrt_swap32(*bp); /* Physical address of first qTD in CPU order */ + + if ((physaddr & QTD_NQP_T) != 0) + { + return 0; + } + + /* Start with the first qTD in the list */ + + qtd = (struct imxrt_qtd_s *)imxrt_virtramaddr(physaddr); + next = NULL; + + /* And loop until we encounter the end of the qTD list */ + + while (qtd) + { + /* Is this the end of the list? Check the next qTD pointer (NQP) + * terminate (T) bit. If T==1, then the NQP address is not valid. + */ + + if ((imxrt_swap32(qtd->hw.nqp) & QTD_NQP_T) != 0) + { + /* Set the next pointer to NULL. This will terminate the loop. */ + + next = NULL; + } + else + { + physaddr = imxrt_swap32(qtd->hw.nqp) & QTD_NQP_NTEP_MASK; + next = (struct imxrt_qtd_s *)imxrt_virtramaddr(physaddr); + } + + /* Perform the user action on this entry. The action might result in + * unlinking the entry! But that is okay because we already have the + * next qTD pointer. + * + * Notice that we do not manage the back pointer (bp). If the call-out + * uses it, it must update it as necessary. + */ + + ret = handler(qtd, &bp, arg); + + /* If the handler returns any non-zero value, then terminate the + * traversal early. + */ + + if (ret != 0) + { + return ret; + } + + /* Set up to visit the next entry */ + + qtd = next; + } + + return OK; +} + +/**************************************************************************** + * Name: imxrt_qtd_discard + * + * Description: + * This is a imxrt_qtd_foreach callback. It simply unlinks the QTD, + * updates the back pointer, and frees the QTD structure. + * + ****************************************************************************/ + +static int imxrt_qtd_discard(struct imxrt_qtd_s *qtd, uint32_t **bp, + void *arg) +{ + DEBUGASSERT(qtd && bp && *bp); + + /* Remove the qTD from the list by updating the forward pointer to skip + * around this qTD. We do not change that pointer because are repeatedly + * removing the aTD at the head of the QH list. + */ + + **bp = qtd->hw.nqp; + + /* Then free the qTD */ + + imxrt_qtd_free(qtd); + return OK; +} + +/**************************************************************************** + * Name: imxrt_qh_discard + * + * Description: + * Free the Queue Head (QH) and all qTD's attached to the QH. + * + * Assumptions: + * The QH structure itself has already been unlinked from whatever list it + * may have been in. + * + ****************************************************************************/ + +static int imxrt_qh_discard(struct imxrt_qh_s *qh) +{ + int ret; + + DEBUGASSERT(qh); + + /* Free all of the qTD's attached to the QH */ + + ret = imxrt_qtd_foreach(qh, imxrt_qtd_discard, NULL); + if (ret < 0) + { + usbhost_trace1(EHCI_TRACE1_QTDFOREACH_FAILED, -ret); + } + + /* Then free the QH itself */ + + imxrt_qh_free(qh); + return ret; +} + +/**************************************************************************** + * Name: imxrt_qtd_invalidate + * + * Description: + * This is a callback from imxrt_qtd_foreach. It simply invalidates D- + * cache for address range of the qTD entry. + * + ****************************************************************************/ + +#if 0 /* Not used */ +static int imxrt_qtd_invalidate(struct imxrt_qtd_s *qtd, uint32_t **bp, + void *arg) +{ + /* Invalidate the D-Cache, i.e., force reloading of the D-Cache from memory + * memory over the specified address range. + */ + + up_invalidate_dcache((uintptr_t)&qtd->hw, + (uintptr_t)&qtd->hw + sizeof(struct ehci_qtd_s)); + return OK; +} +#endif + +/**************************************************************************** + * Name: imxrt_qh_invalidate + * + * Description: + * Invalidate the Queue Head and all qTD entries in the queue. + * + ****************************************************************************/ + +#if 0 /* Not used */ +static int imxrt_qh_invalidate(struct imxrt_qh_s *qh) +{ + /* Invalidate the QH first so that we reload the qTD list head */ + + up_invalidate_dcache((uintptr_t)&qh->hw, + (uintptr_t)&qh->hw + sizeof(struct ehci_qh_s)); + + /* Then invalidate all of the qTD entries in the queue */ + + return imxrt_qtd_foreach(qh, imxrt_qtd_invalidate, NULL); +} +#endif + +/**************************************************************************** + * Name: imxrt_qtd_flush + * + * Description: + * This is a callback from imxrt_qtd_foreach. It simply flushes D-cache + * for address range of the qTD entry. + * + ****************************************************************************/ + +static int imxrt_qtd_flush(struct imxrt_qtd_s *qtd, uint32_t **bp, void *arg) +{ + /* Flush the D-Cache, i.e., make the contents of the memory match the + * contents of the D-Cache in the specified address range and invalidate + * the D-Cache to force re-loading of the data from memory when next + * accessed. + */ + + up_flush_dcache((uintptr_t)&qtd->hw, + (uintptr_t)&qtd->hw + sizeof(struct ehci_qtd_s)); + return OK; +} + +/**************************************************************************** + * Name: imxrt_qh_flush + * + * Description: + * Invalidate the Queue Head and all qTD entries in the queue. + * + ****************************************************************************/ + +static int imxrt_qh_flush(struct imxrt_qh_s *qh) +{ + /* Flush the QH first. This will write the contents of the D-cache to RAM + * and invalidate the contents of the D-cache so that the next access will + * be reloaded from D-Cache. + */ + + up_flush_dcache((uintptr_t)&qh->hw, + (uintptr_t)&qh->hw + sizeof(struct ehci_qh_s)); + + /* Then flush all of the qTD entries in the queue */ + + return imxrt_qtd_foreach(qh, imxrt_qtd_flush, NULL); +} + +/**************************************************************************** + * Name: imxrt_qtd_print + * + * Description: + * Print the context of one qTD + * + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_EHCI_REGDEBUG +static void imxrt_qtd_print(struct imxrt_qtd_s *qtd) +{ + uinfo(" QTD[%p]:\n", qtd); + uinfo(" hw:\n"); + uinfo(" nqp: %08x alt: %08x token: %08x\n", + qtd->hw.nqp, qtd->hw.alt, qtd->hw.token); + uinfo(" bpl: %08x %08x %08x %08x %08x\n", + qtd->hw.bpl[0], qtd->hw.bpl[1], qtd->hw.bpl[2], + qtd->hw.bpl[3], qtd->hw.bpl[4]); +} +#endif + +/**************************************************************************** + * Name: imxrt_qh_print + * + * Description: + * Print the context of one QH + * + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_EHCI_REGDEBUG +static void imxrt_qh_print(struct imxrt_qh_s *qh) +{ + struct imxrt_epinfo_s *epinfo; + struct ehci_overlay_s *overlay; + + uinfo("QH[%p]:\n", qh); + uinfo(" hw:\n"); + uinfo(" hlp: %08x epchar: %08x epcaps: %08x cqp: %08x\n", + qh->hw.hlp, qh->hw.epchar, qh->hw.epcaps, qh->hw.cqp); + + overlay = &qh->hw.overlay; + uinfo(" overlay:\n"); + uinfo(" nqp: %08x alt: %08x token: %08x\n", + overlay->nqp, overlay->alt, overlay->token); + uinfo(" bpl: %08x %08x %08x %08x %08x\n", + overlay->bpl[0], overlay->bpl[1], overlay->bpl[2], + overlay->bpl[3], overlay->bpl[4]); + + uinfo(" fqp:\n", qh->fqp); + + epinfo = qh->epinfo; + uinfo(" epinfo[%p]:\n", epinfo); + if (epinfo) + { + uinfo(" EP%d DIR=%s FA=%08x TYPE=%d MaxPacket=%d\n", + epinfo->epno, epinfo->dirin ? "IN" : "OUT", epinfo->devaddr, + epinfo->xfrtype, epinfo->maxpacket); + uinfo(" Toggle=%d iocwait=%d speed=%d result=%d\n", + epinfo->toggle, epinfo->iocwait, epinfo->speed, epinfo->result); + } +} +#endif + +/**************************************************************************** + * Name: imxrt_qtd_dump + * + * Description: + * This is a imxrt_qtd_foreach callout function. It dumps the context of + * one qTD + * + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_EHCI_REGDEBUG +static int imxrt_qtd_dump(struct imxrt_qtd_s *qtd, uint32_t **bp, void *arg) +{ + imxrt_qtd_print(qtd); + return OK; +} +#endif + +/**************************************************************************** + * Name: imxrt_qh_dump + * + * Description: + * This is a imxrt_qh_foreach call-out function. It dumps a QH structure + * and all of the qTD structures linked to the QH. + * + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_EHCI_REGDEBUG +static int imxrt_qh_dump(struct imxrt_qh_s *qh, uint32_t **bp, void *arg) +{ + imxrt_qh_print(qh); + return imxrt_qtd_foreach(qh, imxrt_qtd_dump, NULL); +} +#endif + +/**************************************************************************** + * Name: imxrt_ehci_speed + * + * Description: + * Map a speed enumeration value per Chapter 9 of the USB specification to + * the speed enumeration required in the EHCI queue head. + * + ****************************************************************************/ + +static inline uint8_t imxrt_ehci_speed(uint8_t usbspeed) +{ + DEBUGASSERT(usbspeed >= USB_SPEED_LOW && usbspeed <= USB_SPEED_HIGH); + return g_ehci_speed[usbspeed]; +} + +/**************************************************************************** + * Name: imxrt_ioc_setup + * + * Description: + * Set the request for the IOC event well BEFORE enabling the transfer (as + * soon as we are absolutely committed to the to avoid transfer). We do + * this to minimize race conditions. This logic would have to be expanded + * if we want to have more than one packet in flight at a time! + * + * Assumption: The caller holds the EHCI exclsem + * + ****************************************************************************/ + +static int imxrt_ioc_setup(struct imxrt_rhport_s *rhport, + struct imxrt_epinfo_s *epinfo) +{ + irqstate_t flags; + int ret = -ENODEV; + + DEBUGASSERT(rhport && epinfo && !epinfo->iocwait); +#ifdef CONFIG_USBHOST_ASYNCH + DEBUGASSERT(epinfo->callback == NULL); +#endif + + /* Is the device still connected? */ + + flags = enter_critical_section(); + if (rhport->connected) + { + /* Then set iocwait to indicate that we expect to be informed when + * either (1) the device is disconnected, or (2) the transfer + * completed. + */ + + epinfo->iocwait = true; /* We want to be awakened by IOC interrupt */ + epinfo->status = 0; /* No status yet */ + epinfo->xfrd = 0; /* Nothing transferred yet */ + epinfo->result = -EBUSY; /* Transfer in progress */ +#ifdef CONFIG_USBHOST_ASYNCH + epinfo->callback = NULL; /* No asynchronous callback */ + epinfo->arg = NULL; +#endif + ret = OK; /* We are good to go */ + } + + leave_critical_section(flags); + return ret; +} + +/**************************************************************************** + * Name: imxrt_ioc_wait + * + * Description: + * Wait for the IOC event. + * + * Assumption: The caller does *NOT* hold the EHCI exclsem. That would + * cause a deadlock when the bottom-half, worker thread needs to take the + * semaphore. + * + ****************************************************************************/ + +static int imxrt_ioc_wait(struct imxrt_epinfo_s *epinfo) +{ + int ret = OK; + + /* Wait for the IOC event. Loop to handle any false alarm semaphore + * counts. Return an error if the task is canceled. + */ + + while (epinfo->iocwait) + { + ret = imxrt_takesem(&epinfo->iocsem); + if (ret < 0) + { + break; + } + } + + return ret < 0 ? ret : epinfo->result; +} + +/**************************************************************************** + * Name: imxrt_qh_enqueue + * + * Description: + * Add a new, ready-to-go QH w/attached qTDs to the asynchronous queue. + * + * Assumptions: The caller holds the EHCI exclsem + * + ****************************************************************************/ + +static void imxrt_qh_enqueue(struct imxrt_qh_s *qhead, struct imxrt_qh_s *qh) +{ + uintptr_t physaddr; + + /* Set the internal fqp field. When we transverse the QH list later, + * we need to know the correct place to start because the overlay may no + * longer point to the first qTD entry. + */ + + qh->fqp = qh->hw.overlay.nqp; + imxrt_qh_dump(qh, NULL, NULL); + + /* Add the new QH to the head of the asynchronous queue list. + * + * First, attach the old head as the new QH HLP and flush the new QH and + * its attached qTDs to RAM. + */ + + qh->hw.hlp = qhead->hw.hlp; + imxrt_qh_flush(qh); + + /* Then set the new QH as the first QH in the asynchronous queue and flush + * the modified head to RAM. + */ + + physaddr = (uintptr_t)imxrt_physramaddr((uintptr_t)qh); + qhead->hw.hlp = imxrt_swap32(physaddr | QH_HLP_TYP_QH); + + up_flush_dcache((uintptr_t)&qhead->hw, + (uintptr_t)&qhead->hw + sizeof(struct ehci_qh_s)); +} + +/**************************************************************************** + * Name: imxrt_qh_create + * + * Description: + * Create a new Queue Head (QH) + * + ****************************************************************************/ + +static struct imxrt_qh_s *imxrt_qh_create(struct imxrt_rhport_s *rhport, + struct imxrt_epinfo_s *epinfo) +{ + struct imxrt_qh_s *qh; + uint32_t rhpndx; + uint32_t regval; + uint8_t hubaddr; + uint8_t hubport; + + /* Allocate a new queue head structure */ + + qh = imxrt_qh_alloc(); + if (qh == NULL) + { + usbhost_trace1(EHCI_TRACE1_QHALLOC_FAILED, 0); + return NULL; + } + + /* Save the endpoint information with the QH itself */ + + qh->epinfo = epinfo; + + /* Write QH endpoint characteristics: + * + * FIELD DESCRIPTION VALUE/SOURCE + * -------- ------------------------------- -------------------- + * DEVADDR Device address Endpoint structure + * I Inactivate on Next Transaction 0 + * ENDPT Endpoint number Endpoint structure + * EPS Endpoint speed Endpoint structure + * DTC Data toggle control 1 + * MAXPKT Max packet size Endpoint structure + * C Control endpoint Calculated + * RL NAK count reloaded 8 + */ + + regval = ((uint32_t)epinfo->devaddr << QH_EPCHAR_DEVADDR_SHIFT) | + ((uint32_t)epinfo->epno << QH_EPCHAR_ENDPT_SHIFT) | + ((uint32_t)imxrt_ehci_speed(epinfo->speed) << + QH_EPCHAR_EPS_SHIFT) | + QH_EPCHAR_DTC | + ((uint32_t)epinfo->maxpacket << QH_EPCHAR_MAXPKT_SHIFT) | + ((uint32_t)8 << QH_EPCHAR_RL_SHIFT); + + /* Paragraph 3.6.3: "Control Endpoint Flag (C). If the QH.EPS field + * indicates the endpoint is not a high-speed device, and the endpoint + * is an control endpoint, then software must set this bit to a one. + * Otherwise it should always set this bit to a zero." + */ + + if (epinfo->speed != USB_SPEED_HIGH && + epinfo->xfrtype == USB_EP_ATTR_XFER_CONTROL) + { + regval |= QH_EPCHAR_C; + } + + /* Save the endpoint characteristics word with the correct byte order */ + + qh->hw.epchar = imxrt_swap32(regval); + + /* Write QH endpoint capabilities + * + * FIELD DESCRIPTION VALUE/SOURCE + * -------- ------------------------------- -------------------- + * SSMASK Interrupt Schedule Mask Depends on epinfo->xfrtype + * SCMASK Split Completion Mask 0 + * HUBADDR Hub Address Always 0 for now + * PORT Port number RH port index + 1 + * MULT High band width multiplier 1 + */ + + rhpndx = RHPNDX(rhport); + +#ifdef CONFIG_USBHOST_HUB + /* REVISIT: Future HUB support will require the HUB port number + * and HUB device address to be included here: + * + * - The HUB device address is the USB device address of the USB 2.0 Hub + * below which a full- or low-speed device is attached. + * - The HUB port number is the port number on the above USB 2.0 Hub + * + * These fields are used in the split-transaction protocol. The kludge + * below should work for hubs connected directly to a root hub port, + * but would not work for devices connected to downstream hubs. + */ + +#warning Missing logic + hubaddr = rhport->ep0.devaddr; + hubport = rhpndx + 1; +#else + hubaddr = rhport->ep0.devaddr; + hubport = rhpndx + 1; +#endif + + regval = ((uint32_t)hubaddr << QH_EPCAPS_HUBADDR_SHIFT) | + ((uint32_t)hubport << QH_EPCAPS_PORT_SHIFT) | + ((uint32_t)1 << QH_EPCAPS_MULT_SHIFT); + +#ifndef CONFIG_USBHOST_INT_DISABLE + if (epinfo->xfrtype == USB_EP_ATTR_XFER_INT) + { + /* Here, the S-Mask field in the queue head is set to 1, indicating + * that the transaction for the endpoint should be executed on the bus + * during micro-frame 0 of the frame. + * + * REVISIT: The polling interval should be controlled by the which + * entry is the framelist holds the QH pointer for a given micro-frame + * and the QH pointer should be replicated for different polling rates. + * This implementation currently just sets all frame_list entry to + * all the same interrupt queue. That should work but will not give + * any control over polling rates. + */ +#warning REVISIT + + regval |= ((uint32_t)1 << QH_EPCAPS_SSMASK_SHIFT); + } +#endif + + qh->hw.epcaps = imxrt_swap32(regval); + + /* Mark this as the end of this list. This will be overwritten if/when the + * next qTD is added to the queue. + */ + + qh->hw.hlp = imxrt_swap32(QH_HLP_T); + qh->hw.overlay.nqp = imxrt_swap32(QH_NQP_T); + qh->hw.overlay.alt = imxrt_swap32(QH_AQP_T); + return qh; +} + +/**************************************************************************** + * Name: imxrt_qtd_addbpl + * + * Description: + * Add a buffer pointer list to a qTD. + * + ****************************************************************************/ + +static int imxrt_qtd_addbpl(struct imxrt_qtd_s *qtd, const void *buffer, + size_t buflen) +{ + uint32_t physaddr; + uint32_t nbytes; + uint32_t next; + int ndx; + + /* Flush the contents of the data buffer to RAM so that the correct + * contents will be accessed for an OUT DMA. + */ + + up_flush_dcache((uintptr_t)buffer, (uintptr_t)buffer + buflen); + + /* Loop, adding the aligned physical addresses of the buffer to the buffer + * page list. Only the first entry need not be aligned (because only the + * first entry has the offset field). The subsequent entries must begin on + * 4KB address boundaries. + */ + + physaddr = (uint32_t)imxrt_physramaddr((uintptr_t)buffer); + + for (ndx = 0; ndx < 5; ndx++) + { + /* Write the physical address of the buffer into the qTD buffer pointer + * list. + */ + + qtd->hw.bpl[ndx] = imxrt_swap32(physaddr); + + /* Get the next buffer pointer (in the case where we will have to + * transfer more then one chunk). This buffer must be aligned to a + * 4KB address boundary. + */ + + next = (physaddr + 4096) & ~4095; + + /* How many bytes were included in the last buffer? Was it the whole + * thing? + */ + + nbytes = next - physaddr; + if (nbytes >= buflen) + { + /* Yes... it was the whole thing. Break out of the loop early. */ + + break; + } + + /* Adjust the buffer length and physical address for the next time + * through the loop. + */ + + buflen -= nbytes; + physaddr = next; + } + + /* Handle the case of a huge buffer > 4*4KB = 16KB */ + + if (ndx >= 5) + { + usbhost_trace1(EHCI_TRACE1_BUFTOOBIG, buflen); + return -EFBIG; + } + + return OK; +} + +/**************************************************************************** + * Name: imxrt_qtd_setupphase + * + * Description: + * Create a SETUP phase request qTD. + * + ****************************************************************************/ + +static struct imxrt_qtd_s * + imxrt_qtd_setupphase(struct imxrt_epinfo_s *epinfo, + const struct usb_ctrlreq_s *req) +{ + struct imxrt_qtd_s *qtd; + uint32_t regval; + int ret; + + /* Allocate a new Queue Element Transfer Descriptor (qTD) */ + + qtd = imxrt_qtd_alloc(); + if (qtd == NULL) + { + usbhost_trace1(EHCI_TRACE1_REQQTDALLOC_FAILED, 0); + return NULL; + } + + /* Mark this as the end of the list (this will be overwritten if another + * qTD is added after this one). + */ + + qtd->hw.nqp = imxrt_swap32(QTD_NQP_T); + qtd->hw.alt = imxrt_swap32(QTD_AQP_T); + + /* Write qTD token: + * + * FIELD DESCRIPTION VALUE/SOURCE + * -------- ------------------------------- -------------------- + * STATUS Status QTD_TOKEN_ACTIVE + * PID PID Code QTD_TOKEN_PID_SETUP + * CERR Error Counter 3 + * CPAGE Current Page 0 + * IOC Interrupt on complete 0 + * NBYTES Total Bytes to Transfer USB_SIZEOF_CTRLREQ + * TOGGLE Data Toggle 0 + */ + + regval = QTD_TOKEN_ACTIVE | QTD_TOKEN_PID_SETUP | + ((uint32_t)3 << QTD_TOKEN_CERR_SHIFT) | + ((uint32_t)USB_SIZEOF_CTRLREQ << QTD_TOKEN_NBYTES_SHIFT); + + qtd->hw.token = imxrt_swap32(regval); + + /* Add the buffer data */ + + ret = imxrt_qtd_addbpl(qtd, req, USB_SIZEOF_CTRLREQ); + if (ret < 0) + { + usbhost_trace1(EHCI_TRACE1_ADDBPL_FAILED, -ret); + imxrt_qtd_free(qtd); + return NULL; + } + + /* Add the data transfer size to the count in the epinfo structure */ + + epinfo->xfrd += USB_SIZEOF_CTRLREQ; + + return qtd; +} + +/**************************************************************************** + * Name: imxrt_qtd_dataphase + * + * Description: + * Create a data transfer or SET data phase qTD. + * + ****************************************************************************/ + +static struct imxrt_qtd_s *imxrt_qtd_dataphase(struct imxrt_epinfo_s *epinfo, + void *buffer, int buflen, + uint32_t tokenbits) +{ + struct imxrt_qtd_s *qtd; + uint32_t regval; + int ret; + + /* Allocate a new Queue Element Transfer Descriptor (qTD) */ + + qtd = imxrt_qtd_alloc(); + if (qtd == NULL) + { + usbhost_trace1(EHCI_TRACE1_DATAQTDALLOC_FAILED, 0); + return NULL; + } + + /* Mark this as the end of the list (this will be overwritten if another + * qTD is added after this one). + */ + + qtd->hw.nqp = imxrt_swap32(QTD_NQP_T); + qtd->hw.alt = imxrt_swap32(QTD_AQP_T); + + /* Write qTD token: + * + * FIELD DESCRIPTION VALUE/SOURCE + * -------- ------------------------------- -------------------- + * STATUS Status QTD_TOKEN_ACTIVE + * PID PID Code Contained in tokenbits + * CERR Error Counter 3 + * CPAGE Current Page 0 + * IOC Interrupt on complete Contained in tokenbits + * NBYTES Total Bytes to Transfer buflen + * TOGGLE Data Toggle Contained in tokenbits + */ + + regval = tokenbits | QTD_TOKEN_ACTIVE | + ((uint32_t)3 << QTD_TOKEN_CERR_SHIFT) | + ((uint32_t)buflen << QTD_TOKEN_NBYTES_SHIFT); + + qtd->hw.token = imxrt_swap32(regval); + + /* Add the buffer information to the buffer pointer list */ + + ret = imxrt_qtd_addbpl(qtd, buffer, buflen); + if (ret < 0) + { + usbhost_trace1(EHCI_TRACE1_ADDBPL_FAILED, -ret); + imxrt_qtd_free(qtd); + return NULL; + } + + /* Add the data transfer size to the count in the epinfo structure */ + + epinfo->xfrd += buflen; + + return qtd; +} + +/**************************************************************************** + * Name: imxrt_qtd_statusphase + * + * Description: + * Create a STATUS phase request qTD. + * + ****************************************************************************/ + +static struct imxrt_qtd_s *imxrt_qtd_statusphase(uint32_t tokenbits) +{ + struct imxrt_qtd_s *qtd; + uint32_t regval; + + /* Allocate a new Queue Element Transfer Descriptor (qTD) */ + + qtd = imxrt_qtd_alloc(); + if (qtd == NULL) + { + usbhost_trace1(EHCI_TRACE1_REQQTDALLOC_FAILED, 0); + return NULL; + } + + /* Mark this as the end of the list (this will be overwritten if another + * qTD is added after this one). + */ + + qtd->hw.nqp = imxrt_swap32(QTD_NQP_T); + qtd->hw.alt = imxrt_swap32(QTD_AQP_T); + + /* Write qTD token: + * + * FIELD DESCRIPTION VALUE/SOURCE + * -------- ------------------------------- -------------------- + * STATUS Status QTD_TOKEN_ACTIVE + * PID PID Code Contained in tokenbits + * CERR Error Counter 3 + * CPAGE Current Page 0 + * IOC Interrupt on complete QTD_TOKEN_IOC + * NBYTES Total Bytes to Transfer 0 + * TOGGLE Data Toggle Contained in tokenbits + */ + + regval = tokenbits | QTD_TOKEN_ACTIVE | QTD_TOKEN_IOC | + ((uint32_t)3 << QTD_TOKEN_CERR_SHIFT); + + qtd->hw.token = imxrt_swap32(regval); + return qtd; +} + +/**************************************************************************** + * Name: imxrt_async_setup + * + * Description: + * Process a IN or OUT request on any asynchronous endpoint (bulk or + * control). This function will enqueue the request and wait for it to + * complete. Bulk data transfers differ in that req == NULL and there are + * not SETUP or STATUS phases. + * + * This is a blocking function; it will not return until the control + * transfer has completed. + * + * Assumption: The caller holds the EHCI exclsem. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is return on + * any failure. + * + ****************************************************************************/ + +static int imxrt_async_setup(struct imxrt_rhport_s *rhport, + struct imxrt_epinfo_s *epinfo, + const struct usb_ctrlreq_s *req, + uint8_t *buffer, size_t buflen) +{ + struct imxrt_qh_s *qh; + struct imxrt_qtd_s *qtd; + uintptr_t physaddr; + uint32_t *flink; + uint32_t *alt; + uint32_t toggle; + bool dirin = false; + int ret; + + /* Terse output only if we are tracing */ + +#ifdef CONFIG_USBHOST_TRACE + usbhost_vtrace2(EHCI_VTRACE2_ASYNCXFR, epinfo->epno, buflen); +#else + uinfo("RHport%d EP%d: buffer=%p, buflen=%d, req=%p\n", + RHPORT(rhport), epinfo->epno, buffer, buflen, req); +#endif + + DEBUGASSERT(rhport && epinfo); + + /* A buffer may or may be supplied with an EP0 SETUP transfer. A buffer + * will always be present for normal endpoint data transfers. + */ + + DEBUGASSERT(req || (buffer && buflen > 0)); + + /* Create and initialize a Queue Head (QH) structure for this transfer */ + + qh = imxrt_qh_create(rhport, epinfo); + if (qh == NULL) + { + usbhost_trace1(EHCI_TRACE1_QHCREATE_FAILED, 0); + return -ENOMEM; + } + + /* Initialize the QH link and get the next data toggle (not used for SETUP + * transfers) + */ + + flink = &qh->hw.overlay.nqp; + toggle = (uint32_t)epinfo->toggle << QTD_TOKEN_TOGGLE_SHIFT; + ret = -EIO; + + /* Is there an EP0 SETUP request? If so, req will be non-NULL and we will + * queue two or three qTDs: + * + * 1) One for the SETUP phase, + * 2) One for the DATA phase (if there is data), and + * 3) One for the STATUS phase. + * + * If this is not an EP0 SETUP request, then only a data transfer will be + * enqueued. + */ + + if (req != NULL) + { + /* Allocate a new Queue Element Transfer Descriptor (qTD) for the SETUP + * phase of the request sequence. + */ + + qtd = imxrt_qtd_setupphase(epinfo, req); + if (qtd == NULL) + { + usbhost_trace1(EHCI_TRACE1_QTDSETUP_FAILED, 0); + goto errout_with_qh; + } + + /* Link the new qTD to the QH head. */ + + physaddr = imxrt_physramaddr((uintptr_t)qtd); + *flink = imxrt_swap32(physaddr); + + /* Get the new forward link pointer and data toggle */ + + flink = &qtd->hw.nqp; + toggle = QTD_TOKEN_TOGGLE; + } + + /* A buffer may or may be supplied with an EP0 SETUP transfer. A buffer + * will always be present for normal endpoint data transfers. + */ + + alt = NULL; + if (buffer != NULL && buflen > 0) + { + uint32_t tokenbits; + + /* Extra TOKEN bits include the data toggle, the data PID, and if + * there is no request, an indication to interrupt at the end of this + * transfer. + */ + + tokenbits = toggle; + + /* Get the data token direction. + * + * If this is a SETUP request, use the direction contained in the + * request. The IOC bit is not set. + */ + + if (req) + { + if ((req->type & USB_REQ_DIR_MASK) == USB_REQ_DIR_IN) + { + tokenbits |= QTD_TOKEN_PID_IN; + dirin = true; + } + else + { + tokenbits |= QTD_TOKEN_PID_OUT; + dirin = false; + } + } + + /* Otherwise, the endpoint is uni-directional. Get the direction from + * the epinfo structure. Since this is not an EP0 SETUP request, + * nothing follows the data and we want the IOC interrupt when the + * data transfer completes. + */ + + else if (epinfo->dirin) + { + tokenbits |= (QTD_TOKEN_PID_IN | QTD_TOKEN_IOC); + dirin = true; + } + else + { + tokenbits |= (QTD_TOKEN_PID_OUT | QTD_TOKEN_IOC); + dirin = false; + } + + /* Allocate a new Queue Element Transfer Descriptor (qTD) for the data + * buffer. + */ + + qtd = imxrt_qtd_dataphase(epinfo, buffer, buflen, tokenbits); + if (qtd == NULL) + { + usbhost_trace1(EHCI_TRACE1_QTDDATA_FAILED, 0); + goto errout_with_qh; + } + + /* Link the new qTD to either QH head of the SETUP qTD. */ + + physaddr = imxrt_physramaddr((uintptr_t)qtd); + *flink = imxrt_swap32(physaddr); + + /* Set the forward link pointer to this new qTD */ + + flink = &qtd->hw.nqp; + + /* If this was an IN transfer, then setup a pointer alternate link. + * The EHCI hardware will use this link if a short packet is received. + */ + + if (dirin) + { + alt = &qtd->hw.alt; + } + } + + /* If this is an EP0 SETUP request, then enqueue one more qTD for the + * STATUS phase transfer. + */ + + if (req != NULL) + { + /* Extra TOKEN bits include the data toggle and the correct data PID. */ + + uint32_t tokenbits = toggle; + + /* The status phase direction is the opposite of the data phase. If + * this is an IN request, then we received the buffer and we will send + * the zero length packet handshake. + */ + + if ((req->type & USB_REQ_DIR_MASK) == USB_REQ_DIR_IN) + { + tokenbits |= QTD_TOKEN_PID_OUT; + } + + /* Otherwise, this in an OUT request. We send the buffer and we expect + * to receive the NULL packet handshake. + */ + + else + { + tokenbits |= QTD_TOKEN_PID_IN; + } + + /* Allocate a new Queue Element Transfer Descriptor (qTD) + * for the status + */ + + qtd = imxrt_qtd_statusphase(tokenbits); + if (qtd == NULL) + { + usbhost_trace1(EHCI_TRACE1_QTDSTATUS_FAILED, 0); + goto errout_with_qh; + } + + /* Link the new qTD to either the SETUP or data qTD. */ + + physaddr = imxrt_physramaddr((uintptr_t)qtd); + *flink = imxrt_swap32(physaddr); + + /* In an IN data qTD was also enqueued, then linked the data qTD's + * alternate pointer to this STATUS phase qTD in order to handle short + * transfers. + */ + + if (alt) + { + *alt = imxrt_swap32(physaddr); + } + } + + /* Add the new QH to the head of the asynchronous queue list */ + + imxrt_qh_enqueue(&g_asynchead, qh); + return OK; + + /* Clean-up after an error */ + +errout_with_qh: + imxrt_qh_discard(qh); + return ret; +} + +/**************************************************************************** + * Name: imxrt_intr_setup + * + * Description: + * Process a IN or OUT request on any interrupt endpoint by inserting a qTD + * into the periodic frame list. + * + * Paragraph 4.10.7 "Adding Interrupt Queue Heads to the Periodic Schedule" + * "The link path(s) from the periodic frame list to a queue head + * establishes in which frames a transaction can be executed for the + * queue head. Queue heads are linked into the periodic schedule so they + * are polled at the appropriate rate. System software sets a bit in a + * queue head's S-Mask to indicate which micro-frame with-in a 1 + * millisecond period a transaction should be executed for the queue + * head. Software must ensure that all queue heads in the periodic + * schedule have S-Mask set to a non-zero value. An S-mask with a zero + * value in the context of the periodic schedule yields undefined + * results. + * + * "If the desired poll rate is greater than one frame, system software + * can use a combination of queue head linking and S-Mask values to + * spread interrupts of equal poll rates through the schedule so that the + * periodic bandwidth is allocated and managed in the most efficient + * manner possible." + * + * Paragraph 4.6 "Periodic Schedule" + * + * "The periodic schedule is used to manage all isochronous and interrupt + * transfer streams. The base of the periodic schedule is the periodic + * frame list. Software links schedule data structures to the periodic + * frame list to produce a graph of scheduled data structures. The graph + * represents an appropriate sequence of transactions on the USB. ... + * isochronous transfers (using iTDs and siTDs) with a period of one are + * linked directly to the periodic frame list. Interrupt transfers (are + * managed with queue heads) and isochronous streams with periods other + * than one are linked following the period-one iTD/siTDs. Interrupt + * queue heads are linked into the frame list ordered by poll rate. + * Longer poll rates are linked first (e.g. closest to the periodic + * frame list), followed by shorter poll rates, with queue heads with a + * poll rate of one, on the very end." + * + * Assumption: The caller holds the EHCI exclsem. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is return on + * any failure. + * + ****************************************************************************/ + +#ifndef CONFIG_USBHOST_INT_DISABLE +static int imxrt_intr_setup(struct imxrt_rhport_s *rhport, + struct imxrt_epinfo_s *epinfo, + uint8_t *buffer, size_t buflen) +{ + struct imxrt_qh_s *qh; + struct imxrt_qtd_s *qtd; + uintptr_t physaddr; + uint32_t tokenbits; + uint32_t regval; + int ret; + + /* Terse output only if we are tracing */ + +#ifdef CONFIG_USBHOST_TRACE + usbhost_vtrace2(EHCI_VTRACE2_INTRXFR, epinfo->epno, buflen); +#else + uinfo("RHport%d EP%d: buffer=%p, buflen=%d\n", + RHPORT(rhport), epinfo->epno, buffer, buflen); +#endif + + DEBUGASSERT(rhport && epinfo && buffer && buflen > 0); + + /* Create and initialize a Queue Head (QH) structure for this transfer */ + + qh = imxrt_qh_create(rhport, epinfo); + if (qh == NULL) + { + usbhost_trace1(EHCI_TRACE1_QHCREATE_FAILED, 0); + return -ENOMEM; + } + + /* Extra TOKEN bits include the data toggle, the data PID, and an + * indication to interrupt at the end of this transfer. + */ + + tokenbits = (uint32_t)epinfo->toggle << QTD_TOKEN_TOGGLE_SHIFT; + + /* Get the data token direction. */ + + if (epinfo->dirin) + { + tokenbits |= (QTD_TOKEN_PID_IN | QTD_TOKEN_IOC); + } + else + { + tokenbits |= (QTD_TOKEN_PID_OUT | QTD_TOKEN_IOC); + } + + /* Allocate a new Queue Element Transfer Descriptor (qTD) for the data + * buffer. + */ + + qtd = imxrt_qtd_dataphase(epinfo, buffer, buflen, tokenbits); + if (qtd == NULL) + { + usbhost_trace1(EHCI_TRACE1_QTDDATA_FAILED, 0); + ret = -ENOMEM; + goto errout_with_qh; + } + + /* Link the new qTD to the QH. */ + + physaddr = imxrt_physramaddr((uintptr_t)qtd); + qh->hw.overlay.nqp = imxrt_swap32(physaddr); + + /* Disable the periodic schedule */ + + regval = imxrt_getreg(&HCOR->usbcmd); + regval &= ~EHCI_USBCMD_PSEN; + imxrt_putreg(regval, &HCOR->usbcmd); + + /* Add the new QH to the head of the interrupt transfer list */ + + imxrt_qh_enqueue(&g_intrhead, qh); + + /* Re-enable the periodic schedule */ + + regval |= EHCI_USBCMD_PSEN; + imxrt_putreg(regval, &HCOR->usbcmd); + return OK; + + /* Clean-up after an error */ + +errout_with_qh: + imxrt_qh_discard(qh); + return ret; +} +#endif /* CONFIG_USBHOST_INT_DISABLE */ + +/**************************************************************************** + * Name: imxrt_transfer_wait + * + * Description: + * Wait for an IN or OUT transfer to complete. + * + * Assumption: The caller holds the EHCI exclsem. The caller must be aware + * that the EHCI exclsem will released while waiting for the transfer to + * complete, but will be re-acquired when before returning. The state of + * EHCI resources could be very different upon return. + * + * Returned Value: + * On success, this function returns the number of bytes actually + * transferred. For control transfers, this size includes the size of the + * control request plus the size of the data (which could be short); for + * bulk transfers, this will be the number of data bytes transfers (which + * could be short). + * + ****************************************************************************/ + +static ssize_t imxrt_transfer_wait(struct imxrt_epinfo_s *epinfo) +{ + int ret; + int ret2; + + /* Release the EHCI semaphore while we wait. Other threads need the + * opportunity to access the EHCI resources while we wait. + * + * REVISIT: Is this safe? NO. This is a bug and needs rethinking. + * We need to lock all of the port-resources (not EHCI common) until + * the transfer is complete. But we can't use the common EHCI exclsem + * or we will deadlock while waiting (because the working thread that + * wakes this thread up needs the exclsem). + */ + + /* REVISIT */ + + imxrt_givesem(&g_ehci.exclsem); + + /* Wait for the IOC completion event */ + + ret = imxrt_ioc_wait(epinfo); + + /* Re-acquire the EHCI semaphore. The caller expects to be holding + * this upon return. + */ + + ret2 = imxrt_takesem_noncancelable(&g_ehci.exclsem); + if (ret >= 0 && ret2 < 0) + { + ret = ret2; + } + +#if 0 /* Does not seem to be needed */ + /* Was there a data buffer? Was this an OUT transfer? */ + + if (buffer != NULL && buflen > 0 && !dirin) + { + /* We have received data from the host -- unless there was an error. + * in any event, we will invalidate the data buffer so that we will + * reload any new data freshly DMAed into the user buffer. + * + * NOTE: This might be un-necessary. We cleaned and invalidated the + * D-Cache prior to starting the DMA so the D-Cache should still be + * invalid in this memory region. + */ + + up_invalidate_dcache((uintptr_t)buffer, (uintptr_t)buffer + buflen); + } +#endif + + /* Did imxrt_ioc_wait() or imxrt_takesem_noncancelable() report an + * error? + */ + + if (ret < 0) + { + usbhost_trace1(EHCI_TRACE1_TRANSFER_FAILED, -ret); + epinfo->iocwait = false; + return (ssize_t)ret; + } + + /* Transfer completed successfully. Return the number of bytes + * transferred. + */ + + return epinfo->xfrd; +} + +/**************************************************************************** + * Name: imxrt_ioc_async_setup + * + * Description: + * Setup to receive an asynchronous notification when a transfer completes. + * + * Input Parameters: + * epinfo - The IN or OUT endpoint descriptor for the device endpoint on + * which the transfer will be performed. + * callback - The function to be called when the completes + * arg - An arbitrary argument that will be provided with the callback. + * + * Returned Value: + * None + * + * Assumptions: + * - Called from the interrupt level + * + ****************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static inline int imxrt_ioc_async_setup(struct imxrt_rhport_s *rhport, + struct imxrt_epinfo_s *epinfo, + usbhost_asynch_t callback, + FAR void *arg) +{ + irqstate_t flags; + int ret = -ENODEV; + + DEBUGASSERT(rhport && epinfo && !epinfo->iocwait && + callback != NULL && epinfo->callback == NULL); + + /* Is the device still connected? */ + + flags = enter_critical_section(); + if (rhport->connected) + { + /* Then save callback information to used when either (1) the + * device is disconnected, or (2) the transfer completes. + */ + + epinfo->iocwait = false; /* No synchronous wakeup */ + epinfo->status = 0; /* No status yet */ + epinfo->xfrd = 0; /* Nothing transferred yet */ + epinfo->result = -EBUSY; /* Transfer in progress */ + epinfo->callback = callback; /* Asynchronous callback */ + epinfo->arg = arg; /* Argument that accompanies the callback */ + ret = OK; /* We are good to go */ + } + + leave_critical_section(flags); + return ret; +} +#endif + +/**************************************************************************** + * Name: imxrt_asynch_completion + * + * Description: + * This function is called at the interrupt level when an asynchronous + * transfer completes. It performs the pending callback. + * + * Input Parameters: + * epinfo - The IN or OUT endpoint descriptor for the device endpoint on + * which the transfer was performed. + * + * Returned Value: + * None + * + * Assumptions: + * - Called from the interrupt level + * + ****************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static void imxrt_asynch_completion(struct imxrt_epinfo_s *epinfo) +{ + usbhost_asynch_t callback; + ssize_t nbytes; + void *arg; + int result; + + DEBUGASSERT(epinfo != NULL && epinfo->iocwait == false && + epinfo->callback != NULL); + + /* Extract and reset the callback info */ + + callback = epinfo->callback; + arg = epinfo->arg; + result = epinfo->result; + nbytes = epinfo->xfrd; + + epinfo->callback = NULL; + epinfo->arg = NULL; + epinfo->result = OK; + epinfo->iocwait = false; + + /* Then perform the callback. Provide the number of bytes successfully + * transferred or the negated errno value in the event of a failure. + */ + + if (result < 0) + { + nbytes = (ssize_t)result; + } + + callback(arg, nbytes); +} +#endif + +/**************************************************************************** + * Name: imxrt_qtd_ioccheck + * + * Description: + * This function is a imxrt_qtd_foreach() callback function. It services + * one qTD in the asynchronous queue. It removes all of the qTD + * structures that are no longer active. + * + ****************************************************************************/ + +static int imxrt_qtd_ioccheck(struct imxrt_qtd_s *qtd, uint32_t **bp, + void *arg) +{ + struct imxrt_epinfo_s *epinfo = (struct imxrt_epinfo_s *)arg; + DEBUGASSERT(qtd && epinfo); + + /* Make sure we reload the QH from memory */ + + up_invalidate_dcache((uintptr_t)&qtd->hw, + (uintptr_t)&qtd->hw + sizeof(struct ehci_qtd_s)); + imxrt_qtd_print(qtd); + + /* Remove the qTD from the list + * + * NOTE that we don't check if the qTD is active nor do we check if there + * are any errors reported in the qTD. If the transfer halted due to + * an error, then qTDs in the list after the error qTD will still appear + * to be active. + */ + + **bp = qtd->hw.nqp; + + /* Subtract the number of bytes left un-transferred. The epinfo->xfrd + * field is initialized to the total number of bytes to be transferred + * (all qTDs in the list). We subtract out the number of un-transferred + * bytes on each transfer and the final result will be the number of bytes + * actually transferred. + */ + + epinfo->xfrd -= (imxrt_swap32(qtd->hw.token) & QTD_TOKEN_NBYTES_MASK) >> + QTD_TOKEN_NBYTES_SHIFT; + + /* Release this QH by returning it to the free list */ + + imxrt_qtd_free(qtd); + return OK; +} + +/**************************************************************************** + * Name: imxrt_qh_ioccheck + * + * Description: + * This function is a imxrt_qh_foreach() callback function. It services + * one QH in the asynchronous queue. It check all attached qTD structures + * and remove all of the structures that are no longer active. if all of + * the qTD structures are removed, then QH itself will also be removed. + * + ****************************************************************************/ + +static int imxrt_qh_ioccheck(struct imxrt_qh_s *qh, uint32_t **bp, void *arg) +{ + struct imxrt_epinfo_s *epinfo; + uint32_t token; + int ret; + + DEBUGASSERT(qh && bp); + + /* Make sure we reload the QH from memory */ + + up_invalidate_dcache((uintptr_t)&qh->hw, + (uintptr_t)&qh->hw + sizeof(struct ehci_qh_s)); + imxrt_qh_print(qh); + + /* Get the endpoint info pointer from the extended QH data. Only the + * g_asynchead QH can have a NULL epinfo field. + */ + + epinfo = qh->epinfo; + DEBUGASSERT(epinfo); + + /* Paragraph 3.6.3: "The nine DWords in [the Transfer Overlay] area + * represent a transaction working space for the host controller. The + * general operational model is that the host controller can detect + * whether the overlay area contains a description of an active transfer. + * If it does not contain an active transfer, then it follows the Queue + * Head Horizontal Link Pointer to the next queue head. The host + * controller will never follow the Next Transfer Queue Element or + * Alternate Queue Element pointers unless it is actively attempting to + * advance the queue ..." + */ + + /* Is the qTD still active? */ + + token = imxrt_swap32(qh->hw.overlay.token); + usbhost_vtrace2(EHCI_VTRACE2_IOCCHECK, epinfo->epno, token); + + if ((token & QH_TOKEN_ACTIVE) != 0) + { + /* Yes... we cannot process the QH while it is still active. Return + * zero to visit the next QH in the list. + */ + + *bp = &qh->hw.hlp; + return OK; + } + + /* Remove all active, attached qTD structures from the inactive QH */ + + ret = imxrt_qtd_foreach(qh, imxrt_qtd_ioccheck, (void *)qh->epinfo); + if (ret < 0) + { + usbhost_trace1(EHCI_TRACE1_QTDFOREACH_FAILED, -ret); + } + + /* If there is no longer anything attached to the QH, then remove it from + * the asynchronous queue. + */ + + if ((imxrt_swap32(qh->fqp) & QTD_NQP_T) != 0) + { + /* Set the forward link of the previous QH to point to the next + * QH in the list. + */ + + **bp = qh->hw.hlp; + up_flush_dcache((uintptr_t)*bp, (uintptr_t)*bp + sizeof(uint32_t)); + + /* Check for errors, update the data toggle */ + + if ((token & QH_TOKEN_ERRORS) == 0) + { + /* No errors.. Save the last data toggle value */ + + epinfo->toggle = (token >> QTD_TOKEN_TOGGLE_SHIFT) & 1; + + /* Report success */ + + epinfo->status = 0; + epinfo->result = OK; + } + else + { + /* An error occurred */ + + epinfo->status = (token & QH_TOKEN_STATUS_MASK) >> + QH_TOKEN_STATUS_SHIFT; + + /* The HALT condition is set on a variety of conditions: babble, + * error counter countdown to zero, or a STALL. If we can rule + * out babble (babble bit not set) and if the error counter is + * non-zero, then we can assume a STALL. In this case, we return + * -PERM to inform the class driver of the stall condition. + */ + + if ((token & (QH_TOKEN_BABBLE | QH_TOKEN_HALTED)) == + QH_TOKEN_HALTED && + (token & QH_TOKEN_CERR_MASK) != 0) + { + /* It is a stall, Note that the data toggle is reset + * after the stall. + */ + + usbhost_trace2(EHCI_TRACE2_EPSTALLED, epinfo->epno, token); + epinfo->result = -EPERM; + epinfo->toggle = 0; + } + else + { + /* Otherwise, it is some kind of data transfer error */ + + usbhost_trace2(EHCI_TRACE2_EPIOERROR, epinfo->epno, token); + epinfo->result = -EIO; + } + } + + /* Is there a thread waiting for this transfer to complete? */ + + if (epinfo->iocwait) + { + /* Yes... wake it up */ + + epinfo->iocwait = false; + imxrt_givesem(&epinfo->iocsem); + } + +#ifdef CONFIG_USBHOST_ASYNCH + /* No.. Is there a pending asynchronous transfer? */ + + else if (epinfo->callback != NULL) + { + /* Yes.. perform the callback */ + + imxrt_asynch_completion(epinfo); + } +#endif + + /* Then release this QH by returning it to the free list */ + + imxrt_qh_free(qh); + } + else + { + /* Otherwise, the horizontal link pointer of this QH will become the + * next back pointer. + */ + + *bp = &qh->hw.hlp; + } + + return OK; +} + +/**************************************************************************** + * Name: imxrt_qtd_cancel + * + * Description: + * This function is a imxrt_qtd_foreach() callback function. It removes + * each qTD attached to a QH. + * + ****************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static int imxrt_qtd_cancel(struct imxrt_qtd_s *qtd, uint32_t **bp, + void *arg) +{ + DEBUGASSERT(qtd != NULL && bp != NULL); + + /* Make sure we reload the QH from memory */ + + up_invalidate_dcache((uintptr_t)&qtd->hw, + (uintptr_t)&qtd->hw + sizeof(struct ehci_qtd_s)); + imxrt_qtd_print(qtd); + + /* Remove the qTD from the list + * + * NOTE that we don't check if the qTD is active nor do we check if there + * are any errors reported in the qTD. If the transfer halted due to + * an error, then qTDs in the list after the error qTD will still appear + * to be active. + * + * REVISIT: There is a race condition here that needs to be resolved. + */ + + **bp = qtd->hw.nqp; + + /* Release this QH by returning it to the free list */ + + imxrt_qtd_free(qtd); + return OK; +} +#endif /* CONFIG_USBHOST_ASYNCH */ + +/**************************************************************************** + * Name: imxrt_qh_cancel + * + * Description: + * This function is a imxrt_qh_foreach() callback function. It cancels + * one QH in the asynchronous queue. It will remove all attached qTD + * structures and remove all of the structures that are no longer active. + * Then QH itself will also be removed. + * + ****************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static int imxrt_qh_cancel(struct imxrt_qh_s *qh, uint32_t **bp, void *arg) +{ + struct imxrt_epinfo_s *epinfo = (struct imxrt_epinfo_s *)arg; + uint32_t regval; + int ret; + + DEBUGASSERT(qh != NULL && bp != NULL && epinfo != NULL); + + /* Make sure we reload the QH from memory */ + + up_invalidate_dcache((uintptr_t)&qh->hw, + (uintptr_t)&qh->hw + sizeof(struct ehci_qh_s)); + imxrt_qh_print(qh); + + /* Check if this is the QH that we are looking for */ + + if (qh->epinfo == epinfo) + { + /* No... keep looking */ + + return OK; + } + + /* Disable both the asynchronous and period schedules */ + + regval = imxrt_getreg(&HCOR->usbcmd); + imxrt_putreg(regval & ~(EHCI_USBCMD_ASEN | EHCI_USBCMD_PSEN), + &HCOR->usbcmd); + + /* Remove the QH from the list + * + * NOTE that we don't check if the qTD is active nor do we check if there + * are any errors reported in the qTD. If the transfer halted due to + * an error, then qTDs in the list after the error qTD will still appear + * to be active. + * + * REVISIT: There is a race condition here that needs to be resolved. + */ + + **bp = qh->hw.hlp; + up_flush_dcache((uintptr_t)*bp, (uintptr_t)*bp + sizeof(uint32_t)); + + /* Re-enable the schedules (if they were enabled before. */ + + imxrt_putreg(regval, &HCOR->usbcmd); + + /* Remove all active, attached qTD structures from the removed QH */ + + ret = imxrt_qtd_foreach(qh, imxrt_qtd_cancel, NULL); + if (ret < 0) + { + usbhost_trace1(EHCI_TRACE1_QTDFOREACH_FAILED, -ret); + } + + /* Then release this QH by returning it to the free list. Return 1 + * to stop the traverse without an error. + */ + + imxrt_qh_free(qh); + return 1; +} +#endif /* CONFIG_USBHOST_ASYNCH */ + +/**************************************************************************** + * Name: imxrt_ioc_bottomhalf + * + * Description: + * EHCI USB Interrupt (USBINT) "Bottom Half" interrupt handler + * + * "The Host Controller sets this bit to 1 on the completion of a USB + * transaction, which results in the retirement of a Transfer Descriptor + * that had its IOC bit set. + * + * "The Host Controller also sets this bit to 1 when a short packet is + * detected (actual number of bytes received was less than the expected + * number of bytes)." + * + * Assumptions: The caller holds the EHCI exclsem + * + ****************************************************************************/ + +static inline void imxrt_ioc_bottomhalf(void) +{ + struct imxrt_qh_s *qh; + uint32_t *bp; + int ret; + + /* Check the Asynchronous Queue + * Make sure that the head of the asynchronous queue is invalidated. + */ + + up_invalidate_dcache((uintptr_t)&g_asynchead.hw, + (uintptr_t)&g_asynchead.hw + + sizeof(struct ehci_qh_s)); + + /* Set the back pointer to the forward QH pointer of the asynchronous + * queue head. + */ + + bp = (uint32_t *)&g_asynchead.hw.hlp; + qh = (struct imxrt_qh_s *) + imxrt_virtramaddr(imxrt_swap32(*bp) & QH_HLP_MASK); + + /* If the asynchronous queue is empty, then the forward point in the + * asynchronous queue head will point back to the queue head. + */ + + if (qh && qh != &g_asynchead) + { + /* Then traverse and operate on every QH and qTD in the asynchronous + * queue + */ + + ret = imxrt_qh_foreach(qh, &bp, imxrt_qh_ioccheck, NULL); + if (ret < 0) + { + usbhost_trace1(EHCI_TRACE1_QHFOREACH_FAILED, -ret); + } + } + +#ifndef CONFIG_USBHOST_INT_DISABLE + + /* Check the Interrupt Queue + * Make sure that the head of the interrupt queue is invalidated. + */ + + up_invalidate_dcache((uintptr_t)&g_intrhead.hw, + (uintptr_t)&g_intrhead.hw + sizeof(struct ehci_qh_s)); + + /* Set the back pointer to the forward qTD pointer of the asynchronous + * queue head. + */ + + bp = (uint32_t *)&g_intrhead.hw.hlp; + qh = (struct imxrt_qh_s *) + imxrt_virtramaddr(imxrt_swap32(*bp) & QH_HLP_MASK); + if (qh) + { + /* Then traverse and operate on every QH and qTD in the asynchronous + * queue. + */ + + ret = imxrt_qh_foreach(qh, &bp, imxrt_qh_ioccheck, NULL); + if (ret < 0) + { + usbhost_trace1(EHCI_TRACE1_QHFOREACH_FAILED, -ret); + } + } +#endif +} + +/**************************************************************************** + * Name: imxrt_portsc_bottomhalf + * + * Description: + * EHCI Port Change Detect "Bottom Half" interrupt handler + * + * "The Host Controller sets this bit to a one when any port for which the + * Port Owner bit is set to zero ... has a change bit transition from a + * zero to a one or a Force Port Resume bit transition from a zero to a + * one as a result of a J-K transition detected on a suspended port. + * This bit will also be set as a result of the Connect Status Change + * being set to a one after system software has relinquished ownership of + * a connected port by writing a one to a port's Port Owner bit... + * + * "This bit is allowed to be maintained in the Auxiliary power well. + * Alternatively, it is also acceptable that on a D3 to D0 transition of + * the EHCI HC device, this bit is loaded with the OR of all of the PORTSC + * change bits (including: Force port resume, over-current change, + * enable/disable change and connect status change)." + * + ****************************************************************************/ + +static inline void imxrt_portsc_bottomhalf(void) +{ + struct imxrt_rhport_s *rhport; + struct usbhost_hubport_s *hport; + uint32_t portsc; + int rhpndx; + + /* Handle root hub status change on each root port */ + + for (rhpndx = 0; rhpndx < IMXRT_EHCI_NRHPORT; rhpndx++) + { + rhport = &g_ehci.rhport[rhpndx]; + portsc = imxrt_getreg(&HCOR->portsc[rhpndx]); + + usbhost_vtrace2(EHCI_VTRACE2_PORTSC, rhpndx + 1, portsc); + + /* Handle port connection status change (CSC) events */ + + if ((portsc & EHCI_PORTSC_CSC) != 0) + { + usbhost_vtrace1(EHCI_VTRACE1_PORTSC_CSC, portsc); + + /* Check current connect status */ + + if ((portsc & EHCI_PORTSC_CCS) != 0) + { + /* Connected ... Did we just become connected? */ + + if (!rhport->connected) + { + /* Yes.. connected. */ + + rhport->connected = true; + + usbhost_vtrace2(EHCI_VTRACE2_PORTSC_CONNECTED, + rhpndx + 1, g_ehci.pscwait); + + /* Notify any waiters */ + + if (g_ehci.pscwait) + { + imxrt_givesem(&g_ehci.pscsem); + g_ehci.pscwait = false; + } + } + else + { + usbhost_vtrace1(EHCI_VTRACE1_PORTSC_CONNALREADY, portsc); + } + } + else + { + /* Disconnected... Did we just become disconnected? */ + + if (rhport->connected) + { + /* Yes.. disconnect the device */ + + usbhost_vtrace2(EHCI_VTRACE2_PORTSC_DISCONND, + rhpndx + 1, g_ehci.pscwait); + + rhport->connected = false; + rhport->lowspeed = false; + + /* Are we bound to a class instance? */ + + hport = &rhport->hport.hport; + if (hport->devclass) + { + /* Yes.. Disconnect the class */ + + CLASS_DISCONNECTED(hport->devclass); + hport->devclass = NULL; + } + + /* Notify any waiters for the Root Hub Status change + * event. + */ + + if (g_ehci.pscwait) + { + imxrt_givesem(&g_ehci.pscsem); + g_ehci.pscwait = false; + } + } + else + { + usbhost_vtrace1(EHCI_VTRACE1_PORTSC_DISCALREADY, portsc); + } + } + } + + /* Clear all pending port interrupt sources by writing a '1' to the + * corresponding bit in the PORTSC register. In addition, we need + * to preserve the values of all R/W bits (RO bits don't matter) + */ + + imxrt_putreg(portsc, &HCOR->portsc[rhpndx]); + } +} + +/**************************************************************************** + * Name: imxrt_syserr_bottomhalf + * + * Description: + * EHCI Host System Error "Bottom Half" interrupt handler + * + * "The Host Controller sets this bit to 1 when a serious error occurs + * during a host system access involving the Host Controller module. ... + * When this error occurs, the Host Controller clears the Run/Stop bit in + * the Command register to prevent further execution of the scheduled TDs." + * + ****************************************************************************/ + +static inline void imxrt_syserr_bottomhalf(void) +{ + usbhost_trace1(EHCI_TRACE1_SYSERR_INTR, 0); + DEBUGPANIC(); +} + +/**************************************************************************** + * Name: imxrt_async_advance_bottomhalf + * + * Description: + * EHCI Async Advance "Bottom Half" interrupt handler + * + * "System software can force the host controller to issue an interrupt the + * next time the host controller advances the asynchronous schedule by + * writing a one to the Interrupt on Async Advance Doorbell bit in the + * USBCMD register. This status bit indicates the assertion of that + * interrupt source." + * + ****************************************************************************/ + +static inline void imxrt_async_advance_bottomhalf(void) +{ + usbhost_vtrace1(EHCI_VTRACE1_AAINTR, 0); + + /* REVISIT: Could remove all tagged QH entries here */ +} + +/**************************************************************************** + * Name: imxrt_ehci_bottomhalf + * + * Description: + * EHCI "Bottom Half" interrupt handler. Runs on a work queue thread. + * + ****************************************************************************/ + +static void imxrt_ehci_bottomhalf(FAR void *arg) +{ + uint32_t pending = (uint32_t)arg; + + /* We need to have exclusive access to the EHCI data structures. Waiting + * here is not a good thing to do on the worker thread, but there is no + * real option (other than to reschedule and delay). + */ + + imxrt_takesem_noncancelable(&g_ehci.exclsem); + + /* Handle all unmasked interrupt sources + * USB Interrupt (USBINT) + * + * "The Host Controller sets this bit to 1 on the completion of a USB + * transaction, which results in the retirement of a Transfer Descriptor + * that had its IOC bit set. + * + * "The Host Controller also sets this bit to 1 when a short packet is + * detected (actual number of bytes received was less than the expected + * number of bytes)." + * + * USB Error Interrupt (USBERRINT) + * + * "The Host Controller sets this bit to 1 when completion of a USB + * transaction results in an error condition (e.g., error counter + * underflow). If the TD on which the error interrupt occurred also + * had its IOC bit set, both this bit and USBINT bit are set. ..." + * + * We do the same thing in either case: Traverse the asynchronous queue + * and remove all of the transfers that are no longer active. + */ + + if ((pending & (EHCI_INT_USBINT | EHCI_INT_USBERRINT)) != 0) + { + if ((pending & EHCI_INT_USBERRINT) != 0) + { + usbhost_trace1(EHCI_TRACE1_USBERR_INTR, pending); + } + else + { + usbhost_vtrace1(EHCI_VTRACE1_USBINTR, pending); + } + + imxrt_ioc_bottomhalf(); + } + + /* Port Change Detect + * + * "The Host Controller sets this bit to a one when any port for which + * the Port Owner bit is set to zero ... has a change bit transition + * from a zero to a one or a Force Port Resume bit transition from a zero + * to a one as a result of a J-K transition detected on a suspended port. + * This bit will also be set as a result of the Connect Status Change + * being set to a one after system software has relinquished ownership + * of a connected port by writing a one to a port's Port Owner bit... + * + * "This bit is allowed to be maintained in the Auxiliary power well. + * Alternatively, it is also acceptable that on a D3 to D0 transition + * of the EHCI HC device, this bit is loaded with the OR of all of the + * PORTSC change bits (including: Force port resume, over-current change, + * enable/disable change and connect status change)." + */ + + if ((pending & EHCI_INT_PORTSC) != 0) + { + imxrt_portsc_bottomhalf(); + } + + /* Frame List Rollover + * + * "The Host Controller sets this bit to a one when the Frame List Index + * ... rolls over from its maximum value to zero. The exact value at + * which the rollover occurs depends on the frame list size. For example, + * if the frame list size (as programmed in the Frame List Size field of + * the USBCMD register) is 1024, the Frame Index Register rolls over + * every time FRINDEX[13] toggles. Similarly, if the size is 512, the + * Host Controller sets this bit to a one every time FRINDEX[12] + * toggles." + */ + +#if 0 /* Not used */ + if ((pending & EHCI_INT_FLROLL) != 0) + { + imxrt_flroll_bottomhalf(); + } +#endif + + /* Host System Error + * + * "The Host Controller sets this bit to 1 when a serious error occurs + * during a host system access involving the Host Controller module. ... + * When this error occurs, the Host Controller clears the Run/Stop bit + * in the Command register to prevent further execution of the scheduled + * TDs." + */ + + if ((pending & EHCI_INT_SYSERROR) != 0) + { + uerr("Syserror\n"); + imxrt_syserr_bottomhalf(); + } + + /* Interrupt on Async Advance + * + * "System software can force the host controller to issue an interrupt + * the next time the host controller advances the asynchronous schedule + * by writing a one to the Interrupt on Async Advance Doorbell bit in + * the USBCMD register. This status bit indicates the assertion of that + * interrupt source." + */ + + if ((pending & EHCI_INT_AAINT) != 0) + { + uerr("Async Advance\n"); + imxrt_async_advance_bottomhalf(); + } + + /* We are done with the EHCI structures */ + + imxrt_givesem(&g_ehci.exclsem); + + /* Re-enable relevant EHCI interrupts. Interrupts should still be enabled + * at the level of the interrupt controller. + */ + + imxrt_putreg(EHCI_HANDLED_INTS, &HCOR->usbintr); +} + +/**************************************************************************** + * Name: imxrt_ehci_interrupt + * + * Description: + * EHCI "Top Half" interrupt handler + * + ****************************************************************************/ + +static int imxrt_ehci_interrupt(int irq, FAR void *context, FAR void *arg) +{ + uint32_t usbsts; + uint32_t pending; + uint32_t regval; + + /* Read Interrupt Status and mask out interrupts that are not enabled. */ + + usbsts = imxrt_getreg(&HCOR->usbsts); + regval = imxrt_getreg(&HCOR->usbintr); + +#ifdef CONFIG_USBHOST_TRACE + usbhost_vtrace1(EHCI_VTRACE1_TOPHALF, usbsts & regval); +#else + uinfo("USBSTS: %08x USBINTR: %08x\n", usbsts, regval); +#endif + + /* Handle all unmasked interrupt sources */ + + pending = usbsts & regval; + if (pending != 0) + { + /* Schedule interrupt handling work for the high priority worker + * thread so that we are not pressed for time and so that we can + * interrupt with other USB threads gracefully. + * + * The worker should be available now because we implement a handshake + * by controlling the EHCI interrupts. + */ + + DEBUGASSERT(work_available(&g_ehci.work)); + DEBUGVERIFY(work_queue(HPWORK, &g_ehci.work, imxrt_ehci_bottomhalf, + (FAR void *)pending, 0)); + + /* Disable further EHCI interrupts so that we do not overrun the work + * queue. + */ + + imxrt_putreg(0, &HCOR->usbintr); + + /* Clear all pending status bits by writing the value of the pending + * interrupt bits back to the status register. + */ + + imxrt_putreg(usbsts & EHCI_INT_ALLINTS, &HCOR->usbsts); + } + + return OK; +} + +/**************************************************************************** + * Name: imxrt_wait + * + * Description: + * Wait for a device to be connected or disconnected to/from a hub port. + * + * Input Parameters: + * conn - The USB host connection instance obtained as a parameter from the + * call to the USB driver initialization logic. + * hport - The location to return the hub port descriptor that detected the + * connection related event. + * + * Returned Value: + * Zero (OK) is returned on success when a device is connected or + * disconnected. This function will not return until either (1) a device is + * connected or disconnect to/from any hub port or until (2) some failure + * occurs. On a failure, a negated errno value is returned indicating the + * nature of the failure + * + * Assumptions: + * - Called from a single thread so no mutual exclusion is required. + * - Never called from an interrupt handler. + * + ****************************************************************************/ + +static int imxrt_wait(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s **hport) +{ + irqstate_t flags; + int rhpndx; + int ret; + + /* Loop until the connection state changes on one of the root hub ports or + * until an error occurs. + */ + + flags = enter_critical_section(); + for (; ; ) + { + /* Check for a change in the connection state on any root hub port */ + + for (rhpndx = 0; rhpndx < IMXRT_EHCI_NRHPORT; rhpndx++) + { + struct imxrt_rhport_s *rhport; + struct usbhost_hubport_s *connport; + + /* Has the connection state changed on the RH port? */ + + rhport = &g_ehci.rhport[rhpndx]; + connport = &rhport->hport.hport; + if (rhport->connected != connport->connected) + { + /* Yes.. Return the RH port to inform the caller which + * port has the connection change. + */ + + connport->connected = rhport->connected; + *hport = connport; + leave_critical_section(flags); + + usbhost_vtrace2(EHCI_VTRACE2_MONWAKEUP, + rhpndx + 1, rhport->connected); + return OK; + } + } + +#ifdef CONFIG_USBHOST_HUB + /* Is a device connected to an external hub? */ + + if (g_ehci.hport) + { + volatile struct usbhost_hubport_s *connport; + + /* Yes.. return the external hub port */ + + connport = g_ehci.hport; + g_ehci.hport = NULL; + + *hport = (struct usbhost_hubport_s *)connport; + leave_critical_section(flags); + + usbhost_vtrace2(EHCI_VTRACE2_MONWAKEUP, + connport->port + 1, connport->connected); + return OK; + } +#endif + + /* No changes on any port. Wait for a connection/disconnection event + * and check again + */ + + g_ehci.pscwait = true; + ret = imxrt_takesem(&g_ehci.pscsem); + if (ret < 0) + { + return ret; + } + } +} + +/**************************************************************************** + * Name: imxrt_enumerate + * + * Description: + * Enumerate the connected device. As part of this enumeration process, + * the driver will (1) get the device's configuration descriptor, (2) + * extract the class ID info from the configuration descriptor, (3) call + * usbhost_findclass() to find the class that supports this device, (4) + * call the create() method on the struct usbhost_registry_s interface + * to get a class instance, and finally (5) call the connect() method + * of the struct usbhost_class_s interface. After that, the class is in + * charge of the sequence of operations. + * + * Input Parameters: + * conn - The USB host connection instance obtained as a parameter from + * the call to the USB driver initialization logic. + * hport - The descriptor of the hub port that has the newly connected + * device. + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ****************************************************************************/ + +static int imxrt_rh_enumerate(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport) +{ + struct imxrt_rhport_s *rhport; + volatile uint32_t *regaddr; + uint32_t regval; + int rhpndx; + + DEBUGASSERT(conn != NULL && hport != NULL); + rhpndx = hport->port; + + DEBUGASSERT(rhpndx >= 0 && rhpndx < IMXRT_EHCI_NRHPORT); + rhport = &g_ehci.rhport[rhpndx]; + + /* Are we connected to a device? The caller should have called the wait() + * method first to be assured that a device is connected. + */ + + while (!rhport->connected) + { + /* No, return an error */ + + usbhost_vtrace1(EHCI_VTRACE1_ENUM_DISCONN, 0); + return -ENODEV; + } + + /* USB 2.0 spec says at least 50ms delay before port reset. + * REVISIT: I think this is wrong. It needs to hold the port in + * reset for 50Msec, not wait 50Msec before resetting. + */ + + nxsig_usleep(100 * 1000); + + /* Paragraph 2.3.9: + * + * "Line Status ... These bits reflect the current logical levels of the + * D+ (bit 11) and D- (bit 10) signal lines. These bits are used for + * detection of low-speed USB devices prior to the port reset and enable + * sequence. This field is valid only when the port enable bit is zero + * and the current connect status bit is set to a one." + * + * Bits[11:10] USB State Interpretation + * ----------- --------- -------------- + * 00b SE0 Not Low-speed device, perform EHCI reset + * 10b J-state Not Low-speed device, perform EHCI reset + * 01b K-state Low-speed device, release ownership of port + * + * NOTE: Low-speed devices could be detected by examining the PORTSC PSPD + * field after resetting the device. The more conventional way here, + * however, also appears to work. + */ + + regval = imxrt_getreg(&HCOR->portsc[rhpndx]); + if ((regval & EHCI_PORTSC_LSTATUS_MASK) == EHCI_PORTSC_LSTATUS_KSTATE) + { + /* EHCI Paragraph 2.3.9: + * + * "Port Owner ... This bit unconditionally goes to a 0b when the + * Configured bit in the CONFIGFLAG register makes a 0b to 1b + * transition. This bit unconditionally goes to 1b whenever the + * Configured bit is zero. + * + * "System software uses this field to release ownership of the + * port to a selected host controller (in the event that the + * attached device is not a high-speed device). Software writes + * a one to this bit when the attached device is not a high-speed + * device. A one in this bit means that a companion host + * controller owns and controls the port. .... + * + * EHCI Paragraph 4.2: + * + * "When a port is routed to a companion HC, it remains under the + * control of the companion HC until the device is disconnected + * from the root por ... When a disconnect occurs, the disconnect + * event is detected by both the companion HC port control and the + * EHCI port ownership control. On the event, the port ownership + * is returned immediately to the EHCI controller. The companion + * HC stack detects the disconnect and acknowledges as it would + * in an ordinary standalone implementation. Subsequent connects + * will be detected by the EHCI port register and the process will + * repeat." + */ + + hport->speed = USB_SPEED_LOW; + } + else + { + /* Assume full-speed for now */ + + hport->speed = USB_SPEED_FULL; + } + + /* Put the root hub port in reset. + * + * EHCI Paragraph 2.3.9: + * + * "The HCHalted bit in the USBSTS register should be a zero before + * software attempts to use [the Port Reset] bit. The host controller + * may hold Port Reset asserted to a one when the HCHalted bit is a one. + */ + + DEBUGASSERT((imxrt_getreg(&HCOR->usbsts) & EHCI_USBSTS_HALTED) == 0); + + /* EHCI paragraph 2.3.9: + * + * "When software writes a one to [the Port Reset] bit (from a zero), the + * bus reset sequence as defined in the USB Specification Revision 2.0 + * is started. Software writes a zero to this bit to terminate the bus + * reset sequence. Software must keep this bit at a one long enough to + * ensure the reset sequence, as specified in the USB Specification + * Revision 2.0, completes. Note: when software writes this bit to a + * one, it must also write a zero to the Port Enable bit." + */ + + regaddr = &HCOR->portsc[RHPNDX(rhport)]; + regval = imxrt_getreg(regaddr); + regval &= ~EHCI_PORTSC_PE; + regval |= EHCI_PORTSC_RESET; + imxrt_putreg(regval, regaddr); + + /* USB 2.0 "Root hubs must provide an aggregate reset period of at least + * 50 ms." + */ + + nxsig_usleep(50 * 1000); + + regval = imxrt_getreg(regaddr); + regval &= ~EHCI_PORTSC_RESET; + imxrt_putreg(regval, regaddr); + + /* Wait for the port reset to complete + * + * EHCI Paragraph 2.3.9: + * + * "Note that when software writes a zero to this bit there may be a + * delay before the bit status changes to a zero. The bit status will + * not read as a zero until after the reset has completed. If the port + * is in high-speed mode after reset is complete, the host controller + * will automatically enable this port (e.g. set the Port Enable bit + * to a one). A host controller must terminate the reset and stabilize + * the state of the port within 2 milliseconds of software transitioning + * this bit from a one to a zero ..." + */ + + while ((imxrt_getreg(regaddr) & EHCI_PORTSC_RESET) != 0); + nxsig_usleep(200 * 1000); + + /* EHCI Paragraph 4.2.2: + * + * "... The reset process is actually complete when software reads a zero + * in the PortReset bit. The EHCI Driver checks the PortEnable bit in + * the PORTSC register. If set to a one, the connected device is a high- + * speed device and EHCI Driver (root hub emulator) issues a change + * report to the hub driver and the hub driver continues to enumerate + * the attached device." + * + * "At the time the EHCI Driver receives the port reset and enable request + * the LineStatus bits might indicate a low-speed device. Additionally, + * when the port reset process is complete, the PortEnable field may + * indicate that a full-speed device is attached. In either case the EHCI + * driver sets the PortOwner bit in the PORTSC register to a one to + * release port ownership to a companion host controller." + * + * LPC31xx User Manual Paragraph 6.1.3: + * + * "In a standard EHCI controller design, the EHCI host controller driver + * detects a Full speed (FS) or Low speed (LS) device by noting if the + * port enable bit is set after the port reset operation. The port enable + * will only be set in a standard EHCI controller implementation after + * the port reset operation and when the host and device negotiate a + * High-Speed connection (i.e. Chirp completes successfully). Since this + * controller has an embedded Transaction Translator, the port enable + * will always be set after the port reset operation regardless of the + * result of the host device chirp result and the resulting port speed + * will be indicated by the PSPD field in PORTSC1. + */ + + regval = imxrt_getreg(&HCOR->portsc[rhpndx]); + + if ((regval & USBDEV_PRTSC1_PSPD_MASK) == USBDEV_PRTSC1_PSPD_HS) + { + /* High speed device */ + + hport->speed = USB_SPEED_HIGH; + } + else if ((regval & USBDEV_PRTSC1_PSPD_MASK) == USBDEV_PRTSC1_PSPD_FS) + { + /* Low- or Full- speed device. Set the port ownership bit. + * + * EHCI Paragraph 4.2: + * + * "When a port is routed to a companion HC, it remains under the + * control of the companion HC until the device is disconnected + * from the root por ... When a disconnect occurs, the disconnect + * event is detected by both the companion HC port control and the + * EHCI port ownership control. On the event, the port ownership + * is returned immediately to the EHCI controller. The companion + * HC stack detects the disconnect and acknowledges as it would + * in an ordinary standalone implementation. Subsequent connects + * will be detected by the EHCI port register and the process will + * repeat." + */ + + DEBUGASSERT(hport->speed == USB_SPEED_FULL); + } + + /* Otherwise it must be a low speed device */ + + else + { + DEBUGASSERT(hport->speed == USB_SPEED_LOW); + DEBUGASSERT((regval & USBDEV_PRTSC1_PSPD_MASK) == + USBDEV_PRTSC1_PSPD_LS); + } + + return OK; +} + +static int imxrt_enumerate(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport) +{ + int ret; + + /* If this is a connection on the root hub, then we need to go to + * little more effort to get the device speed. If it is a connection + * on an external hub, then we already have that information. + */ + + DEBUGASSERT(hport); +#ifdef CONFIG_USBHOST_HUB + if (ROOTHUB(hport)) +#endif + { + ret = imxrt_rh_enumerate(conn, hport); + if (ret < 0) + { + return ret; + } + } + + /* Then let the common usbhost_enumerate do the real enumeration. */ + + usbhost_vtrace1(EHCI_VTRACE1_CLASSENUM, hport->port); + ret = usbhost_enumerate(hport, &hport->devclass); + if (ret < 0) + { + /* Failed to enumerate */ + + usbhost_trace2(EHCI_TRACE2_CLASSENUM_FAILED, hport->port + 1, -ret); + + /* If this is a root hub port, then marking the hub port not connected + * will cause imxrt_wait() to return and we will try the connection + * again. + */ + + hport->connected = false; + } + + return ret; +} + +/**************************************************************************** + * Name: imxrt_ep0configure + * + * Description: + * Configure endpoint 0. This method is normally used internally by the + * enumerate() method but is made available at the interface to support + * an external implementation of the enumeration logic. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the + * call to the class create() method. + * funcaddr - The USB address of the function containing the endpoint that + * EP0 controls. A funcaddr of zero will be received if no address is + * yet assigned to the device. + * speed - The speed of the port USB_SPEED_LOW, _FULL, or _HIGH + * maxpacketsize - The maximum number of bytes that can be sent to or + * received from the endpoint in a single data packet + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure. + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ****************************************************************************/ + +static int imxrt_ep0configure(FAR struct usbhost_driver_s *drvr, + usbhost_ep_t ep0, uint8_t funcaddr, + uint8_t speed, uint16_t maxpacketsize) +{ + struct imxrt_epinfo_s *epinfo = (struct imxrt_epinfo_s *)ep0; + int ret; + + DEBUGASSERT(drvr != NULL && epinfo != NULL && maxpacketsize < 2048); + + /* We must have exclusive access to the EHCI data structures. */ + + ret = imxrt_takesem(&g_ehci.exclsem); + if (ret >= 0) + { + /* Remember the new device address and max packet size */ + + epinfo->devaddr = funcaddr; + epinfo->speed = speed; + epinfo->maxpacket = maxpacketsize; + + imxrt_givesem(&g_ehci.exclsem); + } + + return ret; +} + +/**************************************************************************** + * Name: imxrt_epalloc + * + * Description: + * Allocate and configure one endpoint. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the + * call to the class create() method. + * epdesc - Describes the endpoint to be allocated. + * ep - A memory location provided by the caller in which to receive the + * allocated endpoint descriptor. + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure. + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ****************************************************************************/ + +static int imxrt_epalloc(FAR struct usbhost_driver_s *drvr, + const FAR struct usbhost_epdesc_s *epdesc, + usbhost_ep_t *ep) +{ + struct imxrt_epinfo_s *epinfo; + struct usbhost_hubport_s *hport; + + /* Sanity check. NOTE that this method should only be called if a device + * is connected (because we need a valid low speed indication). + */ + + DEBUGASSERT(drvr != 0 && epdesc != NULL && epdesc->hport != NULL && + ep != NULL); + hport = epdesc->hport; + + /* Terse output only if we are tracing */ + +#ifdef CONFIG_USBHOST_TRACE + usbhost_vtrace2(EHCI_VTRACE2_EPALLOC, epdesc->addr, epdesc->xfrtype); +#else + uinfo("EP%d DIR=%s FA=%08x TYPE=%d Interval=%d MaxPacket=%d\n", + epdesc->addr, epdesc->in ? "IN" : "OUT", hport->funcaddr, + epdesc->xfrtype, epdesc->interval, epdesc->mxpacketsize); +#endif + + /* Allocate a endpoint information structure */ + + epinfo = (struct imxrt_epinfo_s *) + kmm_zalloc(sizeof(struct imxrt_epinfo_s)); + if (!epinfo) + { + usbhost_trace1(EHCI_TRACE1_EPALLOC_FAILED, 0); + return -ENOMEM; + } + + /* Initialize the endpoint container (which is really just another form of + * 'struct usbhost_epdesc_s', packed differently and with additional + * information. A cleaner design might just embed struct usbhost_epdesc_s + * inside of struct imxrt_epinfo_s and just memcpy() here. + */ + + epinfo->epno = epdesc->addr; + epinfo->dirin = epdesc->in; + epinfo->devaddr = hport->funcaddr; +#ifndef CONFIG_USBHOST_INT_DISABLE + epinfo->interval = epdesc->interval; +#endif + epinfo->maxpacket = epdesc->mxpacketsize; + epinfo->xfrtype = epdesc->xfrtype; + epinfo->speed = hport->speed; + + /* The iocsem semaphore is used for signaling and, hence, should not have + * priority inheritance enabled. + */ + + nxsem_init(&epinfo->iocsem, 0, 0); + nxsem_set_protocol(&epinfo->iocsem, SEM_PRIO_NONE); + + /* Success.. return an opaque reference to the endpoint information + * structure instance + */ + + *ep = (usbhost_ep_t)epinfo; + return OK; +} + +/**************************************************************************** + * Name: imxrt_epfree + * + * Description: + * Free and endpoint previously allocated by DRVR_EPALLOC. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the + * call to the class create() method. + * ep - The endpint to be freed. + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ****************************************************************************/ + +static int imxrt_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep) +{ + struct imxrt_epinfo_s *epinfo = (struct imxrt_epinfo_s *)ep; + + /* There should not be any pending, transfers */ + + DEBUGASSERT(drvr && epinfo && epinfo->iocwait == 0); + + /* Free the container */ + + kmm_free(epinfo); + return OK; +} + +/**************************************************************************** + * Name: imxrt_alloc + * + * Description: + * Some hardware supports special memory in which request and descriptor + * data can be accessed more efficiently. This method provides a + * mechanism to allocate the request/descriptor memory. If the underlying + * hardware does not support such "special" memory, this functions may + * simply map to kmm_malloc(). + * + * This interface was optimized under a particular assumption. It was + * assumed that the driver maintains a pool of small, pre-allocated buffers + * for descriptor traffic. NOTE that size is not an input, but an output: + * The size of the pre-allocated buffer is returned. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the + * call to the class create() method. + * buffer - The address of a memory location provided by the caller in + * which to return the allocated buffer memory address. + * maxlen - The address of a memory location provided by the caller in + * which to return the maximum size of the allocated buffer memory. + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure + * + * Assumptions: + * - Called from a single thread so no mutual exclusion is required. + * - Never called from an interrupt handler. + * + ****************************************************************************/ + +static int imxrt_alloc(FAR struct usbhost_driver_s *drvr, + FAR uint8_t **buffer, FAR size_t *maxlen) +{ + int ret = -ENOMEM; + DEBUGASSERT(drvr && buffer && maxlen); + + /* The only special requirements for transfer/descriptor buffers are that + * (1) they be aligned to a cache line boundary and (2) they are a + * multiple of the cache line size in length. + */ + + *buffer = (FAR uint8_t *)kmm_memalign(ARMV7M_DCACHE_LINESIZE, + IMXRT_EHCI_BUFSIZE); + if (*buffer) + { + *maxlen = IMXRT_EHCI_BUFSIZE; + ret = OK; + } + + return ret; +} + +/**************************************************************************** + * Name: imxrt_free + * + * Description: + * Some hardware supports special memory in which request and descriptor + * data can be accessed more efficiently. This method provides a + * mechanism to free that request/descriptor memory. If the underlying + * hardware does not support such "special" memory, this functions may + * simply map to kmm_free(). + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the + * call to the class create() method. + * buffer - The address of the allocated buffer memory to be freed. + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure + * + * Assumptions: + * - Never called from an interrupt handler. + * + ****************************************************************************/ + +static int imxrt_free(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer) +{ + DEBUGASSERT(drvr && buffer); + + /* No special action is require to free the transfer/descriptor buffer + * memory + */ + + kmm_free(buffer); + return OK; +} + +/**************************************************************************** + * Name: imxrt_ioalloc + * + * Description: + * Some hardware supports special memory in which larger IO buffers can + * be accessed more efficiently. This method provides a mechanism to + * allocate the request/descriptor memory. If the underlying hardware + * does not support such "special" memory, this functions may simply map + * to kumm_malloc. + * + * This interface differs from DRVR_ALLOC in that the buffers are variable- + * sized. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the + * call to the class create() method. + * buffer - The address of a memory location provided by the caller in + * which to return the allocated buffer memory address. + * buflen - The size of the buffer required. + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ****************************************************************************/ + +static int imxrt_ioalloc(FAR struct usbhost_driver_s *drvr, + FAR uint8_t **buffer, size_t buflen) +{ + DEBUGASSERT(drvr && buffer && buflen > 0); + + /* The only special requirements for I/O buffers are that (1) they be + * aligned to a cache line boundary, (2) they are a multiple of the cache + * line size in length, and (3) they might need to be user accessible + * (depending on how the class driver implements its buffering). + */ + + buflen = (buflen + DCACHE_LINEMASK) & ~DCACHE_LINEMASK; + *buffer = (FAR uint8_t *)kumm_memalign(ARMV7M_DCACHE_LINESIZE, buflen); + return *buffer ? OK : -ENOMEM; +} + +/**************************************************************************** + * Name: imxrt_iofree + * + * Description: + * Some hardware supports special memory in which IO data can be accessed + * more efficiently. This method provides a mechanism to free that IO + * buffer memory. If the underlying hardware does not support such + * "special" memory, this functions may simply map to kumm_free(). + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the + * call to the class create() method. + * buffer - The address of the allocated buffer memory to be freed. + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ****************************************************************************/ + +static int imxrt_iofree(FAR struct usbhost_driver_s *drvr, + FAR uint8_t *buffer) +{ + DEBUGASSERT(drvr && buffer); + + /* No special action is require to free the I/O buffer memory */ + + kumm_free(buffer); + return OK; +} + +/**************************************************************************** + * Name: imxrt_ctrlin and imxrt_ctrlout + * + * Description: + * Process a IN or OUT request on the control endpoint. These methods + * will enqueue the request and wait for it to complete. Only one + * transfer may be queued; Neither these methods nor the transfer() method + * can be called again until the control transfer functions returns. + * + * These are blocking methods; these functions will not return until the + * control transfer has completed. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the + * call to the class create() method. + * ep0 - The control endpoint to send/receive the control request. + * req - Describes the request to be sent. This request must lie in + * memory created by DRVR_ALLOC. + * buffer - A buffer used for sending the request and for returning any + * responses. This buffer must be large enough to hold the + * length value in the request description. buffer must have been + * allocated using DRVR_ALLOC. + * + * NOTE: On an IN transaction, req and buffer may refer to the same + * allocated memory. + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure + * + * Assumptions: + * - Called from a single thread so no mutual exclusion is required. + * - Never called from an interrupt handler. + * + ****************************************************************************/ + +static int imxrt_ctrlin(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, + FAR const struct usb_ctrlreq_s *req, + FAR uint8_t *buffer) +{ + struct imxrt_rhport_s *rhport = (struct imxrt_rhport_s *)drvr; + struct imxrt_epinfo_s *ep0info = (struct imxrt_epinfo_s *)ep0; + uint16_t len; + ssize_t nbytes; + int ret; + + DEBUGASSERT(rhport != NULL && ep0info != NULL && req != NULL); + + len = imxrt_read16(req->len); + + /* Terse output only if we are tracing */ + +#ifdef CONFIG_USBHOST_TRACE + usbhost_vtrace2(EHCI_VTRACE2_CTRLINOUT, RHPORT(rhport), req->req); +#else + uinfo("RHPort%d type: %02x req: %02x value: %02x%02x index: %02x%02x " + "len: %04x\n", + RHPORT(rhport), req->type, req->req, req->value[1], req->value[0], + req->index[1], req->index[0], len); +#endif + + /* We must have exclusive access to the EHCI hardware and data + * structures. + */ + + ret = imxrt_takesem(&g_ehci.exclsem); + if (ret < 0) + { + return ret; + } + + /* Set the request for the IOC event well BEFORE initiating the transfer. */ + + ret = imxrt_ioc_setup(rhport, ep0info); + if (ret != OK) + { + usbhost_trace1(EHCI_TRACE1_DEVDISCONNECTED, -ret); + goto errout_with_sem; + } + + /* Now initiate the transfer */ + + ret = imxrt_async_setup(rhport, ep0info, req, buffer, len); + if (ret < 0) + { + uerr("ERROR: imxrt_async_setup failed: %d\n", ret); + goto errout_with_iocwait; + } + + /* And wait for the transfer to complete */ + + nbytes = imxrt_transfer_wait(ep0info); + imxrt_givesem(&g_ehci.exclsem); + return nbytes >= 0 ? OK : (int)nbytes; + +errout_with_iocwait: + ep0info->iocwait = false; +errout_with_sem: + imxrt_givesem(&g_ehci.exclsem); + return ret; +} + +static int imxrt_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, + FAR const struct usb_ctrlreq_s *req, + FAR const uint8_t *buffer) +{ + /* imxrt_ctrlin can handle both directions. We just need to work around + * the differences in the function signatures. + */ + + return imxrt_ctrlin(drvr, ep0, req, (uint8_t *)buffer); +} + +/**************************************************************************** + * Name: imxrt_transfer + * + * Description: + * Process a request to handle a transfer descriptor. This method will + * enqueue the transfer request, blocking until the transfer completes. + * Only one transfer may be queued; Neither this method nor the ctrlin or + * ctrlout methods can be called again until this function returns. + * + * This is a blocking method; this functions will not return until the + * transfer has completed. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the + * call to the class create() method. + * ep - The IN or OUT endpoint descriptor for the device endpoint on + * which to perform the transfer. + * buffer - A buffer containing the data to be sent (OUT endpoint) or + * received (IN endpoint). buffer must have been allocated using + * DRVR_ALLOC + * buflen - The length of the data to be sent or received. + * + * Returned Value: + * On success, a non-negative value is returned that indicates the number + * of bytes successfully transferred. On a failure, a negated errno value + * is returned that indicates the nature of the failure: + * + * EAGAIN - If devices NAKs the transfer (or NYET or other error where + * it may be appropriate to restart the entire transaction). + * EPERM - If the endpoint stalls + * EIO - On a TX or data toggle error + * EPIPE - Overrun errors + * + * Assumptions: + * - Called from a single thread so no mutual exclusion is required. + * - Never called from an interrupt handler. + * + ****************************************************************************/ + +static ssize_t imxrt_transfer(FAR struct usbhost_driver_s *drvr, + usbhost_ep_t ep, FAR uint8_t *buffer, + size_t buflen) +{ + struct imxrt_rhport_s *rhport = (struct imxrt_rhport_s *)drvr; + struct imxrt_epinfo_s *epinfo = (struct imxrt_epinfo_s *)ep; + ssize_t nbytes; + int ret; + + DEBUGASSERT(rhport && epinfo && buffer && buflen > 0); + + /* We must have exclusive access to the EHCI hardware and data + * structures. + */ + + ret = imxrt_takesem(&g_ehci.exclsem); + if (ret < 0) + { + return (ssize_t)ret; + } + + /* Set the request for the IOC event well BEFORE initiating the + * transfer. + */ + + ret = imxrt_ioc_setup(rhport, epinfo); + if (ret != OK) + { + usbhost_trace1(EHCI_TRACE1_DEVDISCONNECTED, -ret); + goto errout_with_sem; + } + + /* Initiate the transfer */ + + switch (epinfo->xfrtype) + { + case USB_EP_ATTR_XFER_BULK: + ret = imxrt_async_setup(rhport, epinfo, NULL, buffer, buflen); + break; + +#ifndef CONFIG_USBHOST_INT_DISABLE + case USB_EP_ATTR_XFER_INT: + ret = imxrt_intr_setup(rhport, epinfo, buffer, buflen); + break; +#endif + +#ifndef CONFIG_USBHOST_ISOC_DISABLE + case USB_EP_ATTR_XFER_ISOC: +# warning "Isochronous endpoint support not emplemented" +#endif + case USB_EP_ATTR_XFER_CONTROL: + default: + usbhost_trace1(EHCI_TRACE1_BADXFRTYPE, epinfo->xfrtype); + ret = -ENOSYS; + break; + } + + /* Check for errors in the setup of the transfer */ + + if (ret < 0) + { + goto errout_with_iocwait; + } + + /* Then wait for the transfer to complete */ + + nbytes = imxrt_transfer_wait(epinfo); + imxrt_givesem(&g_ehci.exclsem); + return nbytes; + +errout_with_iocwait: + epinfo->iocwait = false; +errout_with_sem: + uerr("!!!\n"); + imxrt_givesem(&g_ehci.exclsem); + return (ssize_t)ret; +} + +/**************************************************************************** + * Name: imxrt_asynch + * + * Description: + * Process a request to handle a transfer descriptor. This method will + * enqueue the transfer request and return immediately. When the transfer + * completes, the callback will be invoked with the provided transfer. + * This method is useful for receiving interrupt transfers which may come + * infrequently. + * + * Only one transfer may be queued; Neither this method nor the ctrlin or + * ctrlout methods can be called again until the transfer completes. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from + * the call to the class create() method. + * ep - The IN or OUT endpoint descriptor for the device endpoint on + * which to perform the transfer. + * buffer - A buffer containing the data to be sent (OUT endpoint) or + * received (IN endpoint). buffer must have been allocated + * using DRVR_ALLOC + * buflen - The length of the data to be sent or received. + * callback - This function will be called when the transfer completes. + * arg - The arbitrary parameter that will be passed to the callback + * function when the transfer completes. + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure + * + * Assumptions: + * - Called from a single thread so no mutual exclusion is required. + * - Never called from an interrupt handler. + * + ****************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static int imxrt_asynch(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, + FAR uint8_t *buffer, size_t buflen, + usbhost_asynch_t callback, FAR void *arg) +{ + struct imxrt_rhport_s *rhport = (struct imxrt_rhport_s *)drvr; + struct imxrt_epinfo_s *epinfo = (struct imxrt_epinfo_s *)ep; + int ret; + + DEBUGASSERT(rhport && epinfo && buffer && buflen > 0); + + /* We must have exclusive access to the EHCI hardware and data + * structures. + */ + + ret = imxrt_takesem(&g_ehci.exclsem); + if (ret < 0) + { + return ret; + } + + /* Set the request for the callback well BEFORE initiating the transfer. */ + + ret = imxrt_ioc_async_setup(rhport, epinfo, callback, arg); + if (ret != OK) + { + usbhost_trace1(EHCI_TRACE1_DEVDISCONNECTED, -ret); + goto errout_with_sem; + } + + /* Initiate the transfer */ + + switch (epinfo->xfrtype) + { + case USB_EP_ATTR_XFER_BULK: + ret = imxrt_async_setup(rhport, epinfo, NULL, buffer, buflen); + break; + +#ifndef CONFIG_USBHOST_INT_DISABLE + case USB_EP_ATTR_XFER_INT: + ret = imxrt_intr_setup(rhport, epinfo, buffer, buflen); + break; +#endif + +#ifndef CONFIG_USBHOST_ISOC_DISABLE + case USB_EP_ATTR_XFER_ISOC: +# warning "Isochronous endpoint support not emplemented" +#endif + case USB_EP_ATTR_XFER_CONTROL: + default: + usbhost_trace1(EHCI_TRACE1_BADXFRTYPE, epinfo->xfrtype); + ret = -ENOSYS; + break; + } + + /* Check for errors in the setup of the transfer */ + + if (ret < 0) + { + goto errout_with_callback; + } + + /* The transfer is in progress */ + + imxrt_givesem(&g_ehci.exclsem); + return OK; + +errout_with_callback: + epinfo->callback = NULL; + epinfo->arg = NULL; +errout_with_sem: + imxrt_givesem(&g_ehci.exclsem); + return ret; +} +#endif /* CONFIG_USBHOST_ASYNCH */ + +/**************************************************************************** + * Name: imxrt_cancel + * + * Description: + * Cancel a pending transfer on an endpoint. Canceled synchronous or + * asynchronous transfer will complete normally with the error -ESHUTDOWN. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the + * call to the class create() method. + * ep - The IN or OUT endpoint descriptor for the device endpoint on + * which an asynchronous transfer should be transferred. + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure + * + ****************************************************************************/ + +static int imxrt_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep) +{ + struct imxrt_epinfo_s *epinfo = (struct imxrt_epinfo_s *)ep; + struct imxrt_qh_s *qh; +#ifdef CONFIG_USBHOST_ASYNCH + usbhost_asynch_t callback; + void *arg; +#endif + uint32_t *bp; + irqstate_t flags; + bool iocwait; + int ret; + + DEBUGASSERT(epinfo); + + /* We must have exclusive access to the EHCI hardware and data structures. + * This will prevent servicing any transfer completion events while we + * perform the the cancellation, but will not prevent DMA-related race + * conditions. + * + * REVISIT: This won't work. This function must be callable from the + * interrupt level. + */ + + ret = imxrt_takesem(&g_ehci.exclsem); + if (ret < 0) + { + return ret; + } + + /* Sample and reset all transfer termination information. This will + * prevent any callbacks from occurring while are performing the + * cancellation. The transfer may still be in progress, however, so this + * does not eliminate other DMA-related race conditions. + */ + + flags = enter_critical_section(); +#ifdef CONFIG_USBHOST_ASYNCH + callback = epinfo->callback; + arg = epinfo->arg; +#endif + iocwait = epinfo->iocwait; + +#ifdef CONFIG_USBHOST_ASYNCH + epinfo->callback = NULL; + epinfo->arg = NULL; +#endif + epinfo->iocwait = false; + + /* This will prevent any callbacks from occurring while are performing + * the cancellation. The transfer may still be in progress, however, so + * this does not eliminate other DMA-related race conditions. + */ + + epinfo->callback = NULL; + epinfo->arg = NULL; + leave_critical_section(flags); + + /* Bail if there is no transfer in progress for this endpoint */ + +#ifdef CONFIG_USBHOST_ASYNCH + if (callback == NULL && !iocwait) +#else + if (!iocwait) +#endif + { + ret = OK; + goto errout_with_sem; + } + + /* Handle the cancellation according to the type of the transfer */ + + switch (epinfo->xfrtype) + { + case USB_EP_ATTR_XFER_CONTROL: + case USB_EP_ATTR_XFER_BULK: + { + /* Get the horizontal pointer from the head of the asynchronous + * queue. + */ + + bp = (uint32_t *)&g_asynchead.hw.hlp; + qh = (struct imxrt_qh_s *) + imxrt_virtramaddr(imxrt_swap32(*bp) & QH_HLP_MASK); + + /* If the asynchronous queue is empty, then the forward point in + * the asynchronous queue head will point back to the queue + * head. + */ + + if (qh && qh != &g_asynchead) + { + /* Claim that we successfully cancelled the transfer */ + + ret = OK; + goto exit_terminate; + } + } + break; + +#ifndef CONFIG_USBHOST_INT_DISABLE + case USB_EP_ATTR_XFER_INT: + { + /* Get the horizontal pointer from the head of the interrupt + * queue. + */ + + bp = (uint32_t *)&g_intrhead.hw.hlp; + qh = (struct imxrt_qh_s *) + imxrt_virtramaddr(imxrt_swap32(*bp) & QH_HLP_MASK); + if (qh) + { + /* if the queue is empty, then just claim that we successfully + * canceled the transfer. + */ + + ret = OK; + goto exit_terminate; + } + } + break; +#endif + +#ifndef CONFIG_USBHOST_ISOC_DISABLE + case USB_EP_ATTR_XFER_ISOC: +# warning "Isochronous endpoint support not emplemented" +#endif + default: + usbhost_trace1(EHCI_TRACE1_BADXFRTYPE, epinfo->xfrtype); + ret = -ENOSYS; + goto errout_with_sem; + } + + /* Find and remove the QH. There are four possibilities: + * + * 1) The transfer has already completed and the QH is no longer in the + * list. In this case, sam_hq_foreach will return zero + * 2a) The transfer is not active and still pending. It was removed from + * the list and sam_hq_foreach will return one. + * 2b) The is active but not yet complete. This is currently handled the + * same as 2a). REVISIT: This needs to be fixed. + * 3) Some bad happened and sam_hq_foreach returned an error code < 0. + */ + + ret = imxrt_qh_foreach(qh, &bp, imxrt_qh_cancel, epinfo); + if (ret < 0) + { + usbhost_trace1(EHCI_TRACE1_QTDFOREACH_FAILED, -ret); + } + + /* Was there a pending synchronous transfer? */ + +exit_terminate: + epinfo->result = -ESHUTDOWN; +#ifdef CONFIG_USBHOST_ASYNCH + if (iocwait) + { + /* Yes... wake it up */ + + DEBUGASSERT(callback == NULL); + imxrt_givesem(&epinfo->iocsem); + } + + /* No.. Is there a pending asynchronous transfer? */ + + else /* if (callback != NULL) */ + { + /* Yes.. perform the callback */ + + callback(arg, -ESHUTDOWN); + } + +#else + /* Wake up the waiting thread */ + + imxrt_givesem(&epinfo->iocsem); +#endif + +errout_with_sem: + imxrt_givesem(&g_ehci.exclsem); + return ret; +} + +/**************************************************************************** + * Name: imxrt_connect + * + * Description: + * New connections may be detected by an attached hub. This method is the + * mechanism that is used by the hub class to introduce a new connection + * and port description to the system. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the + * call to the class create() method. + * hport - The descriptor of the hub port that detected the connection + * related event + * connected - True: device connected; false: device disconnected + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure + * + ****************************************************************************/ + +#ifdef CONFIG_USBHOST_HUB +static int imxrt_connect(FAR struct usbhost_driver_s *drvr, + FAR struct usbhost_hubport_s *hport, + bool connected) +{ + irqstate_t flags; + + /* Set the connected/disconnected flag */ + + hport->connected = connected; + uinfo("Hub port %d connected: %s\n", + hport->port, connected ? "YES" : "NO"); + + /* Report the connection event */ + + flags = enter_critical_section(); + DEBUGASSERT(g_ehci.hport == NULL); /* REVISIT */ + + g_ehci.hport = hport; + if (g_ehci.pscwait) + { + g_ehci.pscwait = false; + imxrt_givesem(&g_ehci.pscsem); + } + + leave_critical_section(flags); + return OK; +} +#endif + +/**************************************************************************** + * Name: imxrt_disconnect + * + * Description: + * Called by the class when an error occurs and driver has been + * disconnected. The USB host driver should discard the handle to the + * class instance (it is stale) and not attempt any further interaction + * with the class driver instance (until a new instance is received from + * the create() method). The driver should not called the class' + * disconnected() method. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the + * call to the class create() method. + * hport - The port from which the device is being disconnected. Might be + * a port on a hub. + * + * Returned Value: + * None + * + * Assumptions: + * - Only a single class bound to a single device is supported. + * - Never called from an interrupt handler. + * + ****************************************************************************/ + +static void imxrt_disconnect(FAR struct usbhost_driver_s *drvr, + FAR struct usbhost_hubport_s *hport) +{ + DEBUGASSERT(hport != NULL); + hport->devclass = NULL; +} + +/**************************************************************************** + * Name: imxrt_reset + * + * Description: + * Set the HCRESET bit in the USBCMD register to reset the EHCI hardware. + * + * Table 2-9. USBCMD - USB Command Register Bit Definitions + * + * "Host Controller Reset (HCRESET) ... This control bit is used by + * software to reset the host controller. The effects of this on Root + * Hub registers are similar to a Chip Hardware Reset. + * + * "When software writes a one to this bit, the Host Controller resets its + * internal pipelines, timers, counters, state machines, etc. to their + * initial value. Any transaction currently in progress on USB is + * immediately terminated. A USB reset is not driven on downstream + * ports. + * + * "PCI Configuration registers are not affected by this reset. All + * operational registers, including port registers and port state + * machines are set to their initial values. Port ownership reverts + * to the companion host controller(s)... Software must reinitialize + * the host controller ... in order to return the host controller to + * an operational state. + * + * "This bit is set to zero by the Host Controller when the reset process + * is complete. Software cannot terminate the reset process early by + * writing a zero to this register. Software should not set this bit to + * a one when the HCHalted bit in the USBSTS register is a zero. + * Attempting to reset an actively running host controller will result + * in undefined behavior." + * + * Input Parameters: + * None. + * + * Returned Value: + * Zero (OK) is returned on success; A negated errno value is returned + * on failure. + * + * Assumptions: + * - Called during the initialization of the EHCI. + * + ****************************************************************************/ + +static int imxrt_reset(void) +{ + uint32_t regval; + unsigned int timeout; + + /* Make sure that the EHCI is halted: "When [the Run/Stop] bit is set to + * 0, the Host Controller completes the current transaction on the USB and + * then halts. The HC Halted bit in the status register indicates when the + * Host Controller has finished the transaction and has entered the + * stopped state..." + */ + + imxrt_putreg(0, &HCOR->usbcmd); + + /* "... Software should not set [HCRESET] to a one when the HCHalted bit in + * the USBSTS register is a zero. Attempting to reset an actively running + * host controller will result in undefined behavior." + */ + + timeout = 0; + do + { + /* Wait one microsecond and update the timeout counter */ + + up_udelay(1); + timeout++; + + /* Get the current value of the USBSTS register. This loop will + * terminate when either the timeout exceeds one millisecond or when + * the HCHalted bit is no longer set in the USBSTS register. + */ + + regval = imxrt_getreg(&HCOR->usbsts); + } + while (((regval & EHCI_USBSTS_HALTED) == 0) && (timeout < 1000)); + + /* Is the EHCI still running? Did we timeout? */ + + if ((regval & EHCI_USBSTS_HALTED) == 0) + { + usbhost_trace1(EHCI_TRACE1_HCHALTED_TIMEOUT, regval); + return -ETIMEDOUT; + } + + /* Now we can set the HCReset bit in the USBCMD register to initiate the + * reset + */ + + regval = imxrt_getreg(&HCOR->usbcmd); + regval |= EHCI_USBCMD_HCRESET; + imxrt_putreg(regval, &HCOR->usbcmd); + + /* Wait for the HCReset bit to become clear */ + + do + { + /* Wait five microsecondw and update the timeout counter */ + + up_udelay(5); + timeout += 5; + + /* Get the current value of the USBCMD register. This loop will + * terminate when either the timeout exceeds one second or when the + * HCReset bit is no longer set in the USBSTS register. + */ + + regval = imxrt_getreg(&HCOR->usbcmd); + } + while (((regval & EHCI_USBCMD_HCRESET) != 0) && (timeout < 1000000)); + + /* Return either success or a timeout */ + + return (regval & EHCI_USBCMD_HCRESET) != 0 ? -ETIMEDOUT : OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imxrt_ehci_initialize + * + * Description: + * Initialize USB EHCI host controller hardware. + * + * Input Parameters: + * controller -- If the device supports more than one EHCI interface, then + * this identifies which controller is being initialized. Normally, this + * is just zero. + * + * Returned Value: + * And instance of the USB host interface. The controlling task should + * use this interface to (1) call the wait() method to wait for a device + * to be connected, and (2) call the enumerate() method to bind the device + * to a class driver. + * + * Assumptions: + * - This function should called in the initialization sequence in order + * to initialize the USB device functionality. + * - Class drivers should be initialized prior to calling this function. + * Otherwise, there is a race condition if the device is already connected. + * + ****************************************************************************/ + +FAR struct usbhost_connection_s *imxrt_ehci_initialize(int controller) +{ + FAR struct usbhost_hubport_s *hport; + uint32_t regval; +# if defined(CONFIG_DEBUG_USB) && defined(CONFIG_DEBUG_INFO) + uint16_t regval16; + unsigned int nports; +# endif + uintptr_t physaddr; + int ret; + int i; + + /* Sanity checks */ + + DEBUGASSERT(controller == 0); + DEBUGASSERT(((uintptr_t) & g_asynchead & 0x1f) == 0); + DEBUGASSERT((sizeof(struct imxrt_qh_s) & 0x1f) == 0); + DEBUGASSERT((sizeof(struct imxrt_qtd_s) & 0x1f) == 0); + +# ifdef CONFIG_IMXRT_EHCI_PREALLOCATE + DEBUGASSERT(((uintptr_t) & g_qhpool & 0x1f) == 0); + DEBUGASSERT(((uintptr_t) & g_qtdpool & 0x1f) == 0); +# endif + +# ifndef CONFIG_USBHOST_INT_DISABLE + DEBUGASSERT(((uintptr_t) & g_intrhead & 0x1f) == 0); +# ifdef CONFIG_IMXRT_EHCI_PREALLOCATE + DEBUGASSERT(((uintptr_t) g_framelist & 0xfff) == 0); +# endif +# endif /* CONFIG_USBHOST_INT_DISABLE */ + + /* Software Configuration *************************************************/ + + usbhost_vtrace1(EHCI_VTRACE1_INITIALIZING, 0); + + /* Initialize the EHCI state data structure */ + + nxsem_init(&g_ehci.exclsem, 0, 1); + nxsem_init(&g_ehci.pscsem, 0, 0); + + /* The pscsem semaphore is used for signaling and, hence, should not have + * priority inheritance enabled. + */ + + nxsem_set_protocol(&g_ehci.pscsem, SEM_PRIO_NONE); + + /* Initialize EP0 */ + + nxsem_init(&g_ehci.ep0.iocsem, 0, 1); + + /* Initialize the root hub port structures */ + + for (i = 0; i < IMXRT_EHCI_NRHPORT; i++) + { + struct imxrt_rhport_s *rhport = &g_ehci.rhport[i]; + + /* Initialize the device operations */ + + rhport->drvr.ep0configure = imxrt_ep0configure; + rhport->drvr.epalloc = imxrt_epalloc; + rhport->drvr.epfree = imxrt_epfree; + rhport->drvr.alloc = imxrt_alloc; + rhport->drvr.free = imxrt_free; + rhport->drvr.ioalloc = imxrt_ioalloc; + rhport->drvr.iofree = imxrt_iofree; + rhport->drvr.ctrlin = imxrt_ctrlin; + rhport->drvr.ctrlout = imxrt_ctrlout; + rhport->drvr.transfer = imxrt_transfer; +# ifdef CONFIG_USBHOST_ASYNCH + rhport->drvr.asynch = imxrt_asynch; +# endif + rhport->drvr.cancel = imxrt_cancel; +# ifdef CONFIG_USBHOST_HUB + rhport->drvr.connect = imxrt_connect; +# endif + rhport->drvr.disconnect = imxrt_disconnect; + + /* Initialize EP0 */ + + rhport->ep0.xfrtype = USB_EP_ATTR_XFER_CONTROL; + rhport->ep0.speed = USB_SPEED_FULL; + rhport->ep0.maxpacket = 8; + + /* The EP0 iocsem semaphore is used for signaling and, hence, should + * not have priority inheritance enabled. + */ + + nxsem_init(&rhport->ep0.iocsem, 0, 0); + nxsem_set_protocol(&rhport->ep0.iocsem, SEM_PRIO_NONE); + + /* Initialize the public port representation */ + + hport = &rhport->hport.hport; + hport->drvr = &rhport->drvr; +# ifdef CONFIG_USBHOST_HUB + hport->parent = NULL; +# endif + hport->ep0 = &rhport->ep0; + hport->port = i; + hport->speed = USB_SPEED_FULL; + + /* Initialize function address generation logic */ + + usbhost_devaddr_initialize(&rhport->hport); + } + +# ifndef CONFIG_IMXRT_EHCI_PREALLOCATE + /* Allocate a pool of free Queue Head (QH) structures */ + + g_qhpool = + (struct imxrt_qh_s *)kmm_memalign(32, + CONFIG_IMXRT_EHCI_NQHS * + sizeof(struct imxrt_qh_s)); + if (!g_qhpool) + { + usbhost_trace1(EHCI_TRACE1_QHPOOLALLOC_FAILED, 0); + return NULL; + } +# endif + + /* Initialize the list of free Queue Head (QH) structures */ + + for (i = 0; i < CONFIG_IMXRT_EHCI_NQHS; i++) + { + /* Put the QH structure in a free list */ + + imxrt_qh_free(&g_qhpool[i]); + } + +# ifndef CONFIG_IMXRT_EHCI_PREALLOCATE + /* Allocate a pool of free Transfer Descriptor (qTD) structures */ + + g_qtdpool = + (struct imxrt_qtd_s *)kmm_memalign(32, + CONFIG_IMXRT_EHCI_NQTDS * + sizeof(struct imxrt_qtd_s)); + if (!g_qtdpool) + { + usbhost_trace1(EHCI_TRACE1_QTDPOOLALLOC_FAILED, 0); + kmm_free(g_qhpool); + return NULL; + } +# endif + +# if !defined(CONFIG_IMXRT_EHCI_PREALLOCATE) && !defined(CONFIG_USBHOST_INT_DISABLE) + /* Allocate the periodic framelist */ + + g_framelist = (uint32_t *) + kmm_memalign(4096, FRAME_LIST_SIZE * sizeof(uint32_t)); + if (!g_framelist) + { + usbhost_trace1(EHCI_TRACE1_PERFLALLOC_FAILED, 0); + kmm_free(g_qhpool); + kmm_free(g_qtdpool); + return NULL; + } +# endif + + /* Initialize the list of free Transfer Descriptor (qTD) structures */ + + for (i = 0; i < CONFIG_IMXRT_EHCI_NQTDS; i++) + { + /* Put the TD in a free list */ + + imxrt_qtd_free(&g_qtdpool[i]); + } + + /* EHCI Hardware Configuration ********************************************/ + + imxrt_clockall_usboh3(); + + /* Reset the controller from the OTG peripheral */ + + putreg32(USBDEV_USBCMD_RST, IMXRT_USBDEV_USBCMD); + while ((getreg32(IMXRT_USBDEV_USBCMD) & USBDEV_USBCMD_RST) != 0); + + /* Program the controller to be the USB host controller Fixed selections: + * CM = Host mode ES = 0, Little endian mode. SLOM Not used in host mode. + * VBPS = 1, off-chip power source Configurable selections: SDIS = 1, + * Stream disable mode. Eliminates overruns/underruns at the expense of + * some performance. + */ + +# ifdef CONFIG_IMXRT_EHCI_SDIS + putreg32(USBHOST_USBMODE_CM_HOST | USBHOST_USBMODE_SDIS | + USBHOST_USBMODE_VBPS, IMXRT_USBDEV_USBMODE); +# else + putreg32(USBHOST_USBMODE_CM_HOST | USBHOST_USBMODE_VBPS, + IMXRT_USBDEV_USBMODE); +# endif + + /* Reset the EHCI hardware */ + + ret = imxrt_reset(); + if (ret < 0) + { + usbhost_trace1(EHCI_TRACE1_RESET_FAILED, -ret); + return NULL; + } + + /* Re-program the USB host controller. As implemented, imxrt_reset() + * requires the host mode setup in order to work. However, we lose the + * host configuration in the reset. + */ + +# ifdef CONFIG_IMXRT_EHCI_SDIS + putreg32(USBHOST_USBMODE_CM_HOST | USBHOST_USBMODE_SDIS | + USBHOST_USBMODE_VBPS, IMXRT_USBDEV_USBMODE); +# else + putreg32(USBHOST_USBMODE_CM_HOST | USBHOST_USBMODE_VBPS, + IMXRT_USBDEV_USBMODE); +# endif + + /* Disable all interrupts */ + + imxrt_putreg(0, &HCOR->usbintr); + + /* Clear pending interrupts. Bits in the USBSTS register are cleared by + * writing a '1' to the corresponding bit. + */ + + imxrt_putreg(EHCI_INT_ALLINTS, &HCOR->usbsts); + +# if defined(CONFIG_DEBUG_USB) && defined(CONFIG_DEBUG_INFO) + /* Show the EHCI version */ + + regval16 = imxrt_swap16(HCCR->hciversion); + usbhost_vtrace2(EHCI_VTRACE2_HCIVERSION, regval16 >> 8, regval16 & 0xff); + + /* Verify that the correct number of ports is reported */ + + regval = imxrt_getreg(&HCCR->hcsparams); + nports = (regval & EHCI_HCSPARAMS_NPORTS_MASK) >> + EHCI_HCSPARAMS_NPORTS_SHIFT; + + usbhost_vtrace2(EHCI_VTRACE2_HCSPARAMS, nports, regval); + DEBUGASSERT(nports == IMXRT_EHCI_NRHPORT); + + /* Show the HCCPARAMS register */ + + regval = imxrt_getreg(&HCCR->hccparams); + usbhost_vtrace1(EHCI_VTRACE1_HCCPARAMS, regval); +# endif + + /* Initialize the head of the asynchronous queue/reclamation list. "In + * order to communicate with devices via the asynchronous schedule, system + * software must write the ASYNDLISTADDR register with the address of a + * control or bulk queue head. Software must then enable the asynchronous + * schedule by writing a one to the Asynchronous Schedule Enable bit in + * the USBCMD register. In order to communicate with devices via the + * periodic schedule, system software must enable the periodic schedule by + * writing a one to the Periodic Schedule Enable bit in the USBCMD + * register. Note that the schedules can be turned on before the first + * port is reset (and enabled)." + */ + + memset(&g_asynchead, 0, sizeof(struct imxrt_qh_s)); + physaddr = imxrt_physramaddr((uintptr_t) & g_asynchead); + g_asynchead.hw.hlp = imxrt_swap32(physaddr | QH_HLP_TYP_QH); + g_asynchead.hw.epchar = imxrt_swap32(QH_EPCHAR_H | QH_EPCHAR_EPS_FULL); + g_asynchead.hw.overlay.nqp = imxrt_swap32(QH_NQP_T); + g_asynchead.hw.overlay.alt = imxrt_swap32(QH_NQP_T); + g_asynchead.hw.overlay.token = imxrt_swap32(QH_TOKEN_HALTED); + g_asynchead.fqp = imxrt_swap32(QTD_NQP_T); + + /* Set the Current Asynchronous List Address. */ + + up_flush_dcache((uintptr_t)&g_asynchead.hw, + (uintptr_t)&g_asynchead.hw + sizeof(struct ehci_qh_s)); + + imxrt_putreg(imxrt_swap32(physaddr), &HCOR->asynclistaddr); + +# ifndef CONFIG_USBHOST_INT_DISABLE + + /* Initialize the head of the periodic list. Since Isochronous endpoints + * are not not yet supported, each element of the frame list is initialized + * to point to the Interrupt Queue Head (g_intrhead). + */ + + memset(&g_intrhead, 0, sizeof(struct imxrt_qh_s)); + g_intrhead.hw.hlp = imxrt_swap32(QH_HLP_T); + g_intrhead.hw.overlay.nqp = imxrt_swap32(QH_NQP_T); + g_intrhead.hw.overlay.alt = imxrt_swap32(QH_NQP_T); + g_intrhead.hw.overlay.token = imxrt_swap32(QH_TOKEN_HALTED); + g_intrhead.hw.epcaps = imxrt_swap32(QH_EPCAPS_SSMASK(1)); + + /* Attach the periodic QH to Period Frame List */ + + physaddr = imxrt_physramaddr((uintptr_t) & g_intrhead); + for (i = 0; i < FRAME_LIST_SIZE; i++) + { + g_framelist[i] = imxrt_swap32(physaddr) | PFL_TYP_QH; + } + + /* Set the Periodic Frame List Base Address. */ + + physaddr = imxrt_physramaddr((uintptr_t) g_framelist); + imxrt_putreg(imxrt_swap32(physaddr), &HCOR->periodiclistbase); +# endif + + /* Enable the asynchronous schedule and, possibly enable the periodic + * schedule and set the frame list size. + */ + + regval = imxrt_getreg(&HCOR->usbcmd); + regval &= ~(EHCI_USBCMD_HCRESET | EHCI_USBCMD_FLSIZE_MASK | + EHCI_USBCMD_FLSIZE_MASK | EHCI_USBCMD_PSEN | + EHCI_USBCMD_IAADB | EHCI_USBCMD_LRESET); + regval |= EHCI_USBCMD_ASEN; + +# ifndef CONFIG_USBHOST_INT_DISABLE + regval |= EHCI_USBCMD_PSEN; +# if FRAME_LIST_SIZE == 1024 + regval |= EHCI_USBCMD_FLSIZE_1024; +# elif FRAME_LIST_SIZE == 512 + regval |= EHCI_USBCMD_FLSIZE_512; +# elif FRAME_LIST_SIZE == 512 + regval |= EHCI_USBCMD_FLSIZE_256; +# else +# error Unsupported frame size list size +# endif +# endif + + imxrt_putreg(regval, &HCOR->usbcmd); + + /* Start the host controller by setting the RUN bit in the USBCMD + * register. + */ + + regval = imxrt_getreg(&HCOR->usbcmd); + regval |= EHCI_USBCMD_RUN; + imxrt_putreg(regval, &HCOR->usbcmd); + + /* Route all ports to this host controller by setting the CONFIG flag. */ + + regval = imxrt_getreg(&HCOR->configflag); + regval |= EHCI_CONFIGFLAG; + imxrt_putreg(regval, &HCOR->configflag); + + /* Wait for the EHCI to run (i.e., no longer report halted) */ + + ret = ehci_wait_usbsts(EHCI_USBSTS_HALTED, 0, 100 * 1000); + if (ret < 0) + { + usbhost_trace1(EHCI_TRACE1_RUN_FAILED, imxrt_getreg(&HCOR->usbsts)); + return NULL; + } + + /* Interrupt Configuration ************************************************/ + + ret = irq_attach(IMXRT_IRQ_USBOTG2, imxrt_ehci_interrupt, NULL); + if (ret != 0) + { + usbhost_trace1(EHCI_TRACE1_IRQATTACH_FAILED, IMXRT_IRQ_USBOTG2); + return NULL; + } + + /* Enable EHCI interrupts. Interrupts are still disabled at the level of + * the interrupt controller. + */ + + imxrt_putreg(EHCI_HANDLED_INTS, &HCOR->usbintr); + + /* Enable interrupts at the interrupt controller */ + + up_enable_irq(IMXRT_IRQ_USBOTG2); + + /* Drive Vbus +5V (the smoke test) */ + + for (i = 0; i < IMXRT_EHCI_NRHPORT; i++) + { + /* Enable VBUS power for the port */ + + imxrt_usbhost_vbusdrive(i, true); + up_mdelay(25); + } + + /* If there is a USB device in the slot at power up, then we will not get + * the status change interrupt to signal us that the device is connected. + * We need to set the initial connected state accordingly. + */ + + for (i = 0; i < IMXRT_EHCI_NRHPORT; i++) + { + g_ehci.rhport[i].connected = + ((imxrt_getreg(&HCOR->portsc[i]) & EHCI_PORTSC_CCS) != 0); + } + + usbhost_vtrace1(EHCI_VTRACE1_INIITIALIZED, 0); + + /* Initialize and return the connection interface */ + + g_ehciconn.wait = imxrt_wait; + g_ehciconn.enumerate = imxrt_enumerate; + return &g_ehciconn; +} + +/**************************************************************************** + * Name: usbhost_trformat1 and usbhost_trformat2 + * + * Description: + * This interface must be provided by platform specific logic that knows + * the HCDs encoding of USB trace data. + * + * Given an 9-bit index, return a format string suitable for use with, say, + * printf. The returned format is expected to handle two unsigned integer + * values. + * + ****************************************************************************/ + +#ifdef HAVE_USBHOST_TRACE +FAR const char *usbhost_trformat1(uint16_t id) +{ + int ndx = TRACE1_INDEX(id); + + if (ndx < TRACE1_NSTRINGS) + { + return g_trace1[ndx].string; + } + + return NULL; +} + +FAR const char *usbhost_trformat2(uint16_t id) +{ + int ndx = TRACE2_INDEX(id); + + if (ndx < TRACE2_NSTRINGS) + { + return g_trace2[ndx].string; + } + + return NULL; +} +#endif /* HAVE_USBHOST_TRACE */ + +#endif /* CONFIG_IMXRT_USBOTG && CONFIG_USBHOST */ diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lowputc.c b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lowputc.c new file mode 100644 index 000000000..3649ec4f7 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lowputc.c @@ -0,0 +1,603 @@ +/**************************************************************************** + * arch/arm/src/imxrt/imxrt_lowputc.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 "arm_arch.h" + +#include "hardware/imxrt_iomuxc.h" +#include "hardware/imxrt_pinmux.h" +#include "hardware/imxrt_ccm.h" +#include "hardware/imxrt_lpuart.h" +#include "imxrt_config.h" +#include "imxrt_periphclks.h" +#include "imxrt_iomuxc.h" +#include "imxrt_gpio.h" +#include "imxrt_lowputc.h" + +#include "arm_internal.h" + +#include /* Include last: has dependencies */ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +#ifdef HAVE_LPUART_CONSOLE +# if defined(CONFIG_LPUART1_SERIAL_CONSOLE) +# define IMXRT_CONSOLE_BASE IMXRT_LPUART1_BASE +# define IMXRT_CONSOLE_BAUD CONFIG_LPUART1_BAUD +# define IMXRT_CONSOLE_BITS CONFIG_LPUART1_BITS +# define IMXRT_CONSOLE_PARITY CONFIG_LPUART1_PARITY +# define IMXRT_CONSOLE_2STOP CONFIG_LPUART1_2STOP +# elif defined(CONFIG_LPUART2_SERIAL_CONSOLE) +# define IMXRT_CONSOLE_BASE IMXRT_LPUART2_BASE +# define IMXRT_CONSOLE_BAUD CONFIG_LPUART2_BAUD +# define IMXRT_CONSOLE_BITS CONFIG_LPUART2_BITS +# define IMXRT_CONSOLE_PARITY CONFIG_LPUART2_PARITY +# define IMXRT_CONSOLE_2STOP CONFIG_LPUART2_2STOP +# elif defined(CONFIG_LPUART3_SERIAL_CONSOLE) +# define IMXRT_CONSOLE_BASE IMXRT_LPUART3_BASE +# define IMXRT_CONSOLE_BAUD CONFIG_LPUART3_BAUD +# define IMXRT_CONSOLE_BITS CONFIG_LPUART3_BITS +# define IMXRT_CONSOLE_PARITY CONFIG_LPUART3_PARITY +# define IMXRT_CONSOLE_2STOP CONFIG_LPUART3_2STOP +# elif defined(CONFIG_LPUART4_SERIAL_CONSOLE) +# define IMXRT_CONSOLE_BASE IMXRT_LPUART4_BASE +# define IMXRT_CONSOLE_BAUD CONFIG_LPUART4_BAUD +# define IMXRT_CONSOLE_BITS CONFIG_LPUART4_BITS +# define IMXRT_CONSOLE_PARITY CONFIG_LPUART4_PARITY +# define IMXRT_CONSOLE_2STOP CONFIG_LPUART4_2STOP +# elif defined(CONFIG_LPUART5_SERIAL_CONSOLE) +# define IMXRT_CONSOLE_BASE IMXRT_LPUART5_BASE +# define IMXRT_CONSOLE_BAUD CONFIG_LPUART5_BAUD +# define IMXRT_CONSOLE_BITS CONFIG_LPUART5_BITS +# define IMXRT_CONSOLE_PARITY CONFIG_LPUART5_PARITY +# define IMXRT_CONSOLE_2STOP CONFIG_LPUART5_2STOP +# elif defined(CONFIG_LPUART6_SERIAL_CONSOLE) +# define IMXRT_CONSOLE_BASE IMXRT_LPUART6_BASE +# define IMXRT_CONSOLE_BAUD CONFIG_LPUART6_BAUD +# define IMXRT_CONSOLE_BITS CONFIG_LPUART6_BITS +# define IMXRT_CONSOLE_PARITY CONFIG_LPUART6_PARITY +# define IMXRT_CONSOLE_2STOP CONFIG_LPUART6_2STOP +# elif defined(CONFIG_LPUART7_SERIAL_CONSOLE) +# define IMXRT_CONSOLE_BASE IMXRT_LPUART7_BASE +# define IMXRT_CONSOLE_BAUD CONFIG_LPUART7_BAUD +# define IMXRT_CONSOLE_BITS CONFIG_LPUART7_BITS +# define IMXRT_CONSOLE_PARITY CONFIG_LPUART7_PARITY +# define IMXRT_CONSOLE_2STOP CONFIG_LPUART7_2STOP +# elif defined(CONFIG_LPUART8_SERIAL_CONSOLE) +# define IMXRT_CONSOLE_BASE IMXRT_LPUART8_BASE +# define IMXRT_CONSOLE_BAUD CONFIG_LPUART8_BAUD +# define IMXRT_CONSOLE_BITS CONFIG_LPUART8_BITS +# define IMXRT_CONSOLE_PARITY CONFIG_LPUART8_PARITY +# define IMXRT_CONSOLE_2STOP CONFIG_LPUART8_2STOP +# endif +#endif + +/* Clocking *****************************************************************/ + +/* The UART module receives two clocks, a peripheral_clock (ipg_clk) and the + * module_clock (ipg_perclk). The peripheral_clock is used as write clock + * of the TxFIFO, read clock of the RxFIFO and synchronization of the modem + * control input pins. It must always be running when UART is enabled. + * + * The default lpuart1 ipg_clk is 66MHz (max 66.5MHz). ipg_clk is shared + * among many modules and should not be controlled by the UART logic. + * + * The module_clock is for all the state machines, writing RxFIFO, reading + * TxFIFO, etc. It must always be running when UART is sending or receiving + * characters. This clock is used in order to allow frequency scaling on + * peripheral_clock without changing configuration of baud rate. + * + * The default ipg_perclk is 80MHz (max 80MHz). ipg_perclk is gated by + * CCGR5[CG12], lpuart1_clk_enable. The clock generation sequence is: + * + * pll3_sw_clk (480M) -> CCGR5[CG12] -> 3 bit divider cg podf=6 -> + * PLL3_80M (80Mhz) -> CDCDR1: lpuart1_clk_podf -> + * 6 bit divider default=1 -> LPUART1_CLK_ROOT + * + * REVISIT: This logic assumes that all dividers are at the default value + * and that the value of the ipg_perclk is 80MHz. + */ + +#define IPG_PERCLK_FREQUENCY 80000000 + +/* The BRM sub-block receives ref_clk (module_clock clock after divider). + * From this clock, and with integer and non-integer division, BRM generates + * a 16x baud rate clock. + */ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifdef HAVE_LPUART_CONSOLE +static const struct uart_config_s g_console_config = +{ + .baud = IMXRT_CONSOLE_BAUD, /* Configured baud */ + .parity = IMXRT_CONSOLE_PARITY, /* 0=none, 1=odd, 2=even */ + .bits = IMXRT_CONSOLE_BITS, /* Number of bits (5-9) */ + .stopbits2 = IMXRT_CONSOLE_2STOP, /* true: Configure with 2 stop bits instead of 1 */ +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +void imxrt_lpuart_clock_enable (uint32_t base) +{ + if (base == IMXRT_LPUART1_BASE) + { + imxrt_clockall_lpuart1(); + } + else if (base == IMXRT_LPUART2_BASE) + { + imxrt_clockall_lpuart2(); + } + else if (base == IMXRT_LPUART3_BASE) + { + imxrt_clockall_lpuart3(); + } + else if (base == IMXRT_LPUART4_BASE) + { + imxrt_clockall_lpuart4(); + } + else if (base == IMXRT_LPUART5_BASE) + { + imxrt_clockall_lpuart5(); + } + else if (base == IMXRT_LPUART6_BASE) + { + imxrt_clockall_lpuart6(); + } + else if (base == IMXRT_LPUART7_BASE) + { + imxrt_clockall_lpuart7(); + } + else if (base == IMXRT_LPUART8_BASE) + { + imxrt_clockall_lpuart8(); + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imxrt_lowsetup + * + * Description: + * Called at the very beginning of _start. Performs low level + * initialization including setup of the console UART. This UART done + * early so that the serial console is available for debugging very early + * in the boot sequence. + * + ****************************************************************************/ + +void imxrt_lowsetup(void) +{ +#ifndef CONFIG_SUPPRESS_LPUART_CONFIG +#ifdef HAVE_LPUART_DEVICE + +#ifdef CONFIG_IMXRT_LPUART1 + + /* Configure LPUART1 pins: RXD and TXD. Also configure RTS and CTS if flow + * control is enabled. + */ + + imxrt_config_gpio(GPIO_LPUART1_RX); + imxrt_config_gpio(GPIO_LPUART1_TX); +#ifdef CONFIG_LPUART1_OFLOWCONTROL + imxrt_config_gpio(GPIO_LPUART1_CTS); +#endif +#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART1_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART1_IFLOWCONTROL))) + imxrt_config_gpio(GPIO_LPUART1_RTS); +#endif +#endif + +#ifdef CONFIG_IMXRT_LPUART2 + + /* Configure LPUART2 pins: RXD and TXD. Also configure RTS and CTS if flow + * control is enabled. + */ + + imxrt_config_gpio(GPIO_LPUART2_RX); + imxrt_config_gpio(GPIO_LPUART2_TX); +#ifdef CONFIG_LPUART2_OFLOWCONTROL + imxrt_config_gpio(GPIO_LPUART2_CTS); +#endif +#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART2_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART2_IFLOWCONTROL))) + imxrt_config_gpio(GPIO_LPUART2_RTS); +#endif +#endif + +#ifdef CONFIG_IMXRT_LPUART3 + + /* Configure LPUART3 pins: RXD and TXD. Also configure RTS and CTS if flow + * control is enabled. + */ + + imxrt_config_gpio(GPIO_LPUART3_RX); + imxrt_config_gpio(GPIO_LPUART3_TX); +#ifdef CONFIG_LPUART3_OFLOWCONTROL + imxrt_config_gpio(GPIO_LPUART3_CTS); +#endif +#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART3_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART3_IFLOWCONTROL))) + imxrt_config_gpio(GPIO_LPUART3_RTS); +#endif +#endif + +#ifdef CONFIG_IMXRT_LPUART4 + + /* Configure LPUART4 pins: RXD and TXD. Also configure RTS and CTS if flow + * control is enabled. + */ + + imxrt_config_gpio(GPIO_LPUART4_RX); + imxrt_config_gpio(GPIO_LPUART4_TX); +#ifdef CONFIG_LPUART4_OFLOWCONTROL + imxrt_config_gpio(GPIO_LPUART4_CTS); +#endif +#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART4_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART4_IFLOWCONTROL))) + imxrt_config_gpio(GPIO_LPUART4_RTS); +#endif +#endif + +#ifdef CONFIG_IMXRT_LPUART5 + + /* Configure LPUART5 pins: RXD and TXD. Also configure RTS and CTS if flow + * control is enabled. + */ + + imxrt_config_gpio(GPIO_LPUART5_RX); + imxrt_config_gpio(GPIO_LPUART5_TX); +#ifdef CONFIG_LPUART5_OFLOWCONTROL + imxrt_config_gpio(GPIO_LPUART5_CTS); +#endif +#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART5_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART5_IFLOWCONTROL))) + imxrt_config_gpio(GPIO_LPUART5_RTS); +#endif +#endif + +#ifdef CONFIG_IMXRT_LPUART6 + + /* Configure LPUART6 pins: RXD and TXD. Also configure RTS and CTS if flow + * control is enabled. + */ + + imxrt_config_gpio(GPIO_LPUART6_RX); + imxrt_config_gpio(GPIO_LPUART6_TX); +#ifdef CONFIG_LPUART6_OFLOWCONTROL + imxrt_config_gpio(GPIO_LPUART6_CTS); +#endif +#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART6_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART6_IFLOWCONTROL))) + imxrt_config_gpio(GPIO_LPUART6_RTS); +#endif +#endif + +#ifdef CONFIG_IMXRT_LPUART7 + + /* Configure LPUART7 pins: RXD and TXD. Also configure RTS and CTS if flow + * control is enabled. + */ + + imxrt_config_gpio(GPIO_LPUART7_RX); + imxrt_config_gpio(GPIO_LPUART7_TX); +#ifdef CONFIG_LPUART7_OFLOWCONTROL + imxrt_config_gpio(GPIO_LPUART7_CTS); +#endif +#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART7_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART7_IFLOWCONTROL))) + imxrt_config_gpio(GPIO_LPUART7_RTS); +#endif +#endif + +#ifdef CONFIG_IMXRT_LPUART8 + + /* Configure LPUART8 pins: RXD and TXD. Also configure RTS and CTS if flow + * control is enabled. + */ + + imxrt_config_gpio(GPIO_LPUART8_RX); + imxrt_config_gpio(GPIO_LPUART8_TX); +#ifdef CONFIG_LPUART8_OFLOWCONTROL + imxrt_config_gpio(GPIO_LPUART8_CTS); +#endif +#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART8_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART8_IFLOWCONTROL))) + imxrt_config_gpio(GPIO_LPUART8_RTS); +#endif +#endif + +#ifdef HAVE_LPUART_CONSOLE + /* Configure the serial console for initial, non-interrupt driver mode */ + + imxrt_lpuart_configure(IMXRT_CONSOLE_BASE, &g_console_config); +#endif +#endif /* HAVE_LPUART_DEVICE */ +#endif /* CONFIG_SUPPRESS_LPUART_CONFIG */ +} + +/**************************************************************************** + * Name: imxrt_lpuart_configure + * + * Description: + * Configure a UART for non-interrupt driven operation + * + ****************************************************************************/ + +#ifdef HAVE_LPUART_DEVICE +int imxrt_lpuart_configure(uint32_t base, + FAR const struct uart_config_s *config) +{ + uint32_t src_freq = 0; + uint32_t pll3_div = 0; + uint32_t uart_div = 0; + uint32_t lpuart_freq = 0; + uint16_t sbr; + uint16_t temp_sbr; + uint32_t osr; + uint32_t temp_osr; + uint32_t temp_diff; + uint32_t calculated_baud; + uint32_t baud_diff; + uint32_t regval; + uint32_t regval2; + + if ((getreg32(IMXRT_CCM_CSCDR1) & CCM_CSCDR1_UART_CLK_SEL) != 0) + { + src_freq = BOARD_XTAL_FREQUENCY; + } + else + { + if ((getreg32(IMXRT_CCM_ANALOG_PLL_USB2) & + CCM_ANALOG_PLL_USB2_DIV_SELECT_MASK) != 0) + { + pll3_div = 22; + } + else + { + pll3_div = 20; + } + + src_freq = (BOARD_XTAL_FREQUENCY * pll3_div) / 6; + } + + uart_div = (getreg32(IMXRT_CCM_CSCDR1) & + CCM_CSCDR1_UART_CLK_PODF_MASK) + 1; + lpuart_freq = src_freq / uart_div; + + /* This LPUART instantiation uses a slightly different baud rate + * calculation. The idea is to use the best OSR (over-sampling rate) + * possible. + * + * NOTE: OSR is typically hard-set to 16 in other LPUART instantiations + * loop to find the best OSR value possible, one that generates minimum + * baud_diff iterate through the rest of the supported values of OSR + */ + + baud_diff = config->baud; + osr = 0; + sbr = 0; + + for (temp_osr = 4; temp_osr <= 32; temp_osr++) + { + /* Calculate the temporary sbr value */ + + temp_sbr = (lpuart_freq / (config->baud * temp_osr)); + + /* Set temp_sbr to 1 if the sourceClockInHz can not satisfy the + * desired baud rate. + */ + + if (temp_sbr == 0) + { + temp_sbr = 1; + } + + /* Calculate the baud rate based on the temporary OSR and SBR values */ + + calculated_baud = (lpuart_freq / (temp_osr * temp_sbr)); + temp_diff = calculated_baud - config->baud; + + /* Select the better value between srb and (sbr + 1) */ + + if (temp_diff > (config->baud - + (lpuart_freq / (temp_osr * (temp_sbr + 1))))) + { + temp_diff = config->baud - + (lpuart_freq / (temp_osr * (temp_sbr + 1))); + temp_sbr++; + } + + if (temp_diff <= baud_diff) + { + baud_diff = temp_diff; + osr = temp_osr; + sbr = temp_sbr; + } + } + + if (baud_diff > ((config->baud / 100) * 3)) + { + /* Unacceptable baud rate difference of more than 3% */ + + return ERROR; + } + + /* Enable lpuart clock */ + + imxrt_lpuart_clock_enable(base); + + /* Reset all internal logic and registers, except the Global Register */ + + regval = getreg32(base + IMXRT_LPUART_GLOBAL_OFFSET); + regval |= LPUART_GLOBAL_RST; + putreg32(regval, base + IMXRT_LPUART_GLOBAL_OFFSET); + + regval &= ~LPUART_GLOBAL_RST; + putreg32(regval, base + IMXRT_LPUART_GLOBAL_OFFSET); + + /* Construct MODIR register */ + + regval = 0; + + if (config->userts) + { + regval |= LPUART_MODIR_RXRTSE; + } + else if (config->users485) + { + /* Both TX and RX side can't control RTS, so this gives + * the RX side precedence. This should have been filtered + * in layers above anyway, but it's just a precaution. + */ + + regval |= LPUART_MODIR_TXRTSE; + } + + if (config->usects) + { + regval |= LPUART_MODIR_TXCTSE; + } + + if (config->invrts) + { + regval |= LPUART_MODIR_TXRTSPOL; + } + + putreg32(regval, base + IMXRT_LPUART_MODIR_OFFSET); + + regval = 0; + + if ((osr > 3) && (osr < 8)) + { + regval |= LPUART_BAUD_BOTHEDGE; + } + + if (config->stopbits2) + { + regval |= LPUART_BAUD_SBNS; + } + + regval |= LPUART_BAUD_OSR(osr) | LPUART_BAUD_SBR(sbr); + putreg32(regval, base + IMXRT_LPUART_BAUD_OFFSET); + + regval = 0; + if (config->parity == 1) + { + regval |= LPUART_CTRL_PE | LPUART_CTRL_PT_ODD; + } + else if (config->parity == 2) + { + regval |= LPUART_CTRL_PE | LPUART_CTRL_PT_EVEN; + } + + if (config->bits == 9 || (config->bits == 8 && config->parity != 0)) + { + regval |= LPUART_CTRL_M; + } + else if ((config->bits == 8)) + { + regval &= ~LPUART_CTRL_M; + } + else + { + /* Here should be added support of other bit modes. */ + +#warning missing logic + return ERROR; + } + + regval2 = getreg32(base + IMXRT_LPUART_FIFO_OFFSET); + regval2 |= LPUART_FIFO_RXFLUSH | LPUART_FIFO_TXFLUSH | + LPUART_FIFO_RXFE | LPUART_FIFO_RXIDEN_1 | LPUART_FIFO_TXFE; + putreg32(regval2 , base + IMXRT_LPUART_FIFO_OFFSET); + + regval |= LPUART_CTRL_RE | LPUART_CTRL_TE; + putreg32(regval, base + IMXRT_LPUART_CTRL_OFFSET); + + return OK; +} +#endif /* HAVE_LPUART_DEVICE */ + +/**************************************************************************** + * Name: imxrt_lowputc + * + * Description: + * Output a byte with as few system dependencies as possible. This will + * even work BEFORE the console is initialized if we are booting from U- + * Boot (and the same UART is used for the console, of course.) + * + ****************************************************************************/ + +#if defined(HAVE_LPUART_DEVICE) && defined(CONFIG_DEBUG_FEATURES) +void imxrt_lowputc(int ch) +{ + while ((getreg32(IMXRT_CONSOLE_BASE + IMXRT_LPUART_STAT_OFFSET) & + LPUART_STAT_TDRE) == 0) + { + } + + /* If the character to output is a newline, then pre-pend a carriage + * return + */ + + if (ch == '\n') + { + /* Send the carriage return by writing it into the UART_TXD register. */ + + putreg32((uint32_t)'\r', IMXRT_CONSOLE_BASE + + IMXRT_LPUART_DATA_OFFSET); + + /* Wait for the transmit register to be emptied. When the TXFE bit is + * non-zero, the TX Buffer FIFO is empty. + */ + + while ((getreg32(IMXRT_CONSOLE_BASE + IMXRT_LPUART_STAT_OFFSET) & + LPUART_STAT_TDRE) == 0) + { + } + } + + /* Send the character by writing it into the UART_TXD register. */ + + putreg32((uint32_t)ch, IMXRT_CONSOLE_BASE + IMXRT_LPUART_DATA_OFFSET); +} +#endif diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpi2c.c b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpi2c.c new file mode 100644 index 000000000..20d8e1914 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpi2c.c @@ -0,0 +1,1997 @@ +/**************************************************************************** + * arch/arm/src/imxrt/imxrt_lpi2c.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 "arm_arch.h" + +#include "imxrt_lpi2c.h" +#include "imxrt_gpio.h" + +#include "hardware/imxrt_pinmux.h" +#include "hardware/imxrt_ccm.h" +#include "imxrt_periphclks.h" + +/* At least one I2C peripheral must be enabled */ + +#if defined(CONFIG_IMXRT_LPI2C1) || defined(CONFIG_IMXRT_LPI2C2) || \ + defined(CONFIG_IMXRT_LPI2C3) || defined(CONFIG_IMXRT_LPI2C4) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +/* CONFIG_I2C_POLLED may be set so that I2C interrupts will not be used. + * Instead, CPU-intensive polling will be used. + */ + +/* Interrupt wait timeout in seconds and milliseconds */ + +#if !defined(CONFIG_IMXRT_LPI2C_TIMEOSEC) && \ + !defined(CONFIG_IMXRT_LPI2C_TIMEOMS) +# define CONFIG_IMXRT_LPI2C_TIMEOSEC 0 +# define CONFIG_IMXRT_LPI2C_TIMEOMS 500 /* Default is 500 milliseconds */ +#elif !defined(CONFIG_IMXRT_LPI2C_TIMEOSEC) +# define CONFIG_IMXRT_LPI2C_TIMEOSEC 0 /* User provided milliseconds */ +#elif !defined(CONFIG_IMXRT_LPI2C_TIMEOMS) +# define CONFIG_IMXRT_LPI2C_TIMEOMS 0 /* User provided seconds */ +#endif + +/* Interrupt wait time timeout in system timer ticks */ + +#ifndef CONFIG_IMXRT_LPI2C_TIMEOTICKS +# define CONFIG_IMXRT_LPI2C_TIMEOTICKS \ + (SEC2TICK(CONFIG_IMXRT_LPI2C_TIMEOSEC) + \ + MSEC2TICK(CONFIG_IMXRT_LPI2C_TIMEOMS)) +#endif + +#ifndef CONFIG_IMXRT_LPI2C_DYNTIMEO_STARTSTOP +# define CONFIG_IMXRT_LPI2C_DYNTIMEO_STARTSTOP \ + TICK2USEC(CONFIG_IMXRT_LPI2C_TIMEOTICKS) +#endif + +/* Debug ********************************************************************/ + +/* I2C event trace logic. NOTE: trace uses the internal, non-standard, + * low-level debug interface syslog() but does not require that any other + * debug is enabled. + */ + +#ifndef CONFIG_I2C_TRACE +# define imxrt_lpi2c_tracereset(p) +# define imxrt_lpi2c_tracenew(p,s) +# define imxrt_lpi2c_traceevent(p,e,a) +# define imxrt_lpi2c_tracedump(p) +#endif + +#ifndef CONFIG_I2C_NTRACE +# define CONFIG_I2C_NTRACE 32 +#endif + +#ifdef CONFIG_I2C_SLAVE +# error I2C slave logic is not supported yet for IMXRT +#endif + +#define LPI2C_MASTER 1 +#define LPI2C_SLAVE 2 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Interrupt state */ + +enum imxrt_intstate_e +{ + INTSTATE_IDLE = 0, /* No I2C activity */ + INTSTATE_WAITING, /* Waiting for completion of interrupt activity */ + INTSTATE_DONE, /* Interrupt activity complete */ +}; + +/* Trace events */ + +enum imxrt_trace_e +{ + I2CEVENT_NONE = 0, /* No events have occurred with this status */ + I2CEVENT_SENDADDR, /* Start/Master bit set and address sent, param = msgc */ + I2CEVENT_SENDBYTE, /* Send byte, param = dcnt */ + I2CEVENT_RCVBYTE, /* Read more dta, param = dcnt */ + I2CEVENT_NOSTART, /* BTF on last byte with no restart, param = msgc */ + I2CEVENT_STARTRESTART, /* Last byte sent, re-starting, param = msgc */ + I2CEVENT_STOP, /* Last byte sten, send stop, param = 0 */ + I2CEVENT_ERROR /* Error occurred, param = 0 */ +}; + +/* Trace data */ + +struct imxrt_trace_s +{ + uint32_t status; /* I2C 32-bit SR2|SR1 status */ + uint32_t count; /* Interrupt count when status change */ + enum imxrt_intstate_e event; /* Last event that occurred with this status */ + uint32_t parm; /* Parameter associated with the event */ + clock_t time; /* First of event or first status */ +}; + +/* I2C Device hardware configuration */ + +struct imxrt_lpi2c_config_s +{ + uint32_t base; /* LPI2C base address */ + uint16_t busy_idle; /* LPI2C Bus Idle Timeout */ + uint8_t filtscl; /* Glitch Filter for SCL pin */ + uint8_t filtsda; /* Glitch Filter for SDA pin */ + uint32_t scl_pin; /* Peripheral configuration for SCL as SCL */ + uint32_t sda_pin; /* Peripheral configuration for SDA as SDA */ +#if defined(CONFIG_I2C_RESET) + uint32_t reset_scl_pin; /* GPIO configuration for SCL as SCL */ + uint32_t reset_sda_pin; /* GPIO configuration for SDA as SDA */ +#endif + uint8_t mode; /* Master or Slave mode */ +#ifndef CONFIG_I2C_POLLED + uint32_t irq; /* Event IRQ */ +#endif +}; + +/* I2C Device Private Data */ + +struct imxrt_lpi2c_priv_s +{ + /* Standard I2C operations */ + + const struct i2c_ops_s *ops; + + /* Port configuration */ + + const struct imxrt_lpi2c_config_s *config; + + int refs; /* Reference count */ + sem_t sem_excl; /* Mutual exclusion semaphore */ +#ifndef CONFIG_I2C_POLLED + sem_t sem_isr; /* Interrupt wait semaphore */ +#endif + volatile uint8_t intstate; /* Interrupt handshake (see enum imxrt_intstate_e) */ + + uint8_t msgc; /* Message count */ + struct i2c_msg_s *msgv; /* Message list */ + uint8_t *ptr; /* Current message buffer */ + uint32_t frequency; /* Current I2C frequency */ + int dcnt; /* Current message length */ + uint16_t flags; /* Current message flags */ + + /* I2C trace support */ + +#ifdef CONFIG_I2C_TRACE + int tndx; /* Trace array index */ + clock_t start_time; /* Time when the trace was started */ + + /* The actual trace data */ + + struct imxrt_trace_s trace[CONFIG_I2C_NTRACE]; +#endif + + uint32_t status; /* End of transfer SR2|SR1 status */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static inline uint32_t + imxrt_lpi2c_getreg(FAR struct imxrt_lpi2c_priv_s *priv, uint16_t offset); +static inline void imxrt_lpi2c_putreg(FAR struct imxrt_lpi2c_priv_s *priv, + uint16_t offset, uint32_t value); +static inline void imxrt_lpi2c_modifyreg(FAR struct imxrt_lpi2c_priv_s *priv, + uint16_t offset, uint32_t clearbits, + uint32_t setbits); +static inline int imxrt_lpi2c_sem_wait(FAR struct imxrt_lpi2c_priv_s *priv); +#ifdef CONFIG_I2C_RESET +static int + imxrt_lpi2c_sem_wait_noncancelable(FAR struct imxrt_lpi2c_priv_s *priv); +#endif + +#ifdef CONFIG_IMXRT_LPI2C_DYNTIMEO +static useconds_t imxrt_lpi2c_tousecs(int msgc, FAR struct i2c_msg_s *msgs); +#endif /* CONFIG_IMXRT_LPI2C_DYNTIMEO */ + +static inline int + imxrt_lpi2c_sem_waitdone(FAR struct imxrt_lpi2c_priv_s *priv); +static inline void + imxrt_lpi2c_sem_waitstop(FAR struct imxrt_lpi2c_priv_s *priv); +static inline void + imxrt_lpi2c_sem_post(FAR struct imxrt_lpi2c_priv_s *priv); +static inline void + imxrt_lpi2c_sem_init(FAR struct imxrt_lpi2c_priv_s *priv); +static inline void + imxrt_lpi2c_sem_destroy(FAR struct imxrt_lpi2c_priv_s *priv); + +#ifdef CONFIG_I2C_TRACE +static void imxrt_lpi2c_tracereset(FAR struct imxrt_lpi2c_priv_s *priv); +static void imxrt_lpi2c_tracenew(FAR struct imxrt_lpi2c_priv_s *priv, + uint32_t status); +static void imxrt_lpi2c_traceevent(FAR struct imxrt_lpi2c_priv_s *priv, + enum imxrt_trace_e event, uint32_t parm); +static void imxrt_lpi2c_tracedump(FAR struct imxrt_lpi2c_priv_s *priv); +#endif /* CONFIG_I2C_TRACE */ + +static void imxrt_lpi2c_setclock(FAR struct imxrt_lpi2c_priv_s *priv, + uint32_t frequency); +static inline void imxrt_lpi2c_sendstart(FAR struct imxrt_lpi2c_priv_s *priv, + uint8_t address); +static inline void imxrt_lpi2c_sendstop(FAR struct imxrt_lpi2c_priv_s *priv); +static inline uint32_t + imxrt_lpi2c_getstatus(FAR struct imxrt_lpi2c_priv_s *priv); + +static int imxrt_lpi2c_isr_process(struct imxrt_lpi2c_priv_s * priv); + +#ifndef CONFIG_I2C_POLLED +static int imxrt_lpi2c_isr(int irq, void *context, FAR void *arg); +#endif /* !CONFIG_I2C_POLLED */ + +void imxrt_lpi2c_clock_enable (uint32_t base); +void imxrt_lpi2c_clock_disable (uint32_t base); +static int imxrt_lpi2c_init(FAR struct imxrt_lpi2c_priv_s *priv); +static int imxrt_lpi2c_deinit(FAR struct imxrt_lpi2c_priv_s *priv); +static int imxrt_lpi2c_transfer(FAR struct i2c_master_s *dev, + FAR struct i2c_msg_s *msgs, int count); +#ifdef CONFIG_I2C_RESET +static int imxrt_lpi2c_reset(FAR struct i2c_master_s *dev); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Trace events strings */ + +#ifdef CONFIG_I2C_TRACE +static const char *g_trace_names[] = +{ + "NONE ", + "SENDADDR ", + "SENDBYTE ", + "RCVBYTE ", + "NOSTART ", + "START/RESTART ", + "STOP ", + "ERROR " +}; +#endif + +/* I2C interface */ + +static const struct i2c_ops_s imxrt_lpi2c_ops = +{ + .transfer = imxrt_lpi2c_transfer +#ifdef CONFIG_I2C_RESET + , .reset = imxrt_lpi2c_reset +#endif +}; + +/* I2C device structures */ + +#ifdef CONFIG_IMXRT_LPI2C1 +static const struct imxrt_lpi2c_config_s imxrt_lpi2c1_config = +{ + .base = IMXRT_LPI2C1_BASE, + .busy_idle = CONFIG_LPI2C1_BUSYIDLE, + .filtscl = CONFIG_LPI2C1_FILTSCL, + .filtsda = CONFIG_LPI2C1_FILTSDA, + .scl_pin = GPIO_LPI2C1_SCL, + .sda_pin = GPIO_LPI2C1_SDA, +#if defined(CONFIG_I2C_RESET) + .reset_scl_pin = GPIO_LPI2C1_SCL_RESET, + .reset_sda_pin = GPIO_LPI2C1_SDA_RESET, +#endif +#ifndef CONFIG_I2C_SLAVE + .mode = LPI2C_MASTER, +#else + .mode = LPI2C_SLAVE, +#endif +#ifndef CONFIG_I2C_POLLED + .irq = IMXRT_IRQ_LPI2C1, +#endif +}; + +static struct imxrt_lpi2c_priv_s imxrt_lpi2c1_priv = +{ + .ops = &imxrt_lpi2c_ops, + .config = &imxrt_lpi2c1_config, + .refs = 0, + .intstate = INTSTATE_IDLE, + .msgc = 0, + .msgv = NULL, + .ptr = NULL, + .dcnt = 0, + .flags = 0, + .status = 0 +}; +#endif + +#ifdef CONFIG_IMXRT_LPI2C2 +static const struct imxrt_lpi2c_config_s imxrt_lpi2c2_config = +{ + .base = IMXRT_LPI2C2_BASE, + .busy_idle = CONFIG_LPI2C2_BUSYIDLE, + .filtscl = CONFIG_LPI2C2_FILTSCL, + .filtsda = CONFIG_LPI2C2_FILTSDA, + .scl_pin = GPIO_LPI2C2_SCL, + .sda_pin = GPIO_LPI2C2_SDA, +#if defined(CONFIG_I2C_RESET) + .reset_scl_pin = GPIO_LPI2C2_SCL_RESET, + .reset_sda_pin = GPIO_LPI2C2_SDA_RESET, +#endif +#ifndef CONFIG_I2C_SLAVE + .mode = LPI2C_MASTER, +#else + .mode = LPI2C_SLAVE, +#endif +#ifndef CONFIG_I2C_POLLED + .irq = IMXRT_IRQ_LPI2C2, +#endif +}; + +static struct imxrt_lpi2c_priv_s imxrt_lpi2c2_priv = +{ + .ops = &imxrt_lpi2c_ops, + .config = &imxrt_lpi2c2_config, + .refs = 0, + .intstate = INTSTATE_IDLE, + .msgc = 0, + .msgv = NULL, + .ptr = NULL, + .dcnt = 0, + .flags = 0, + .status = 0 +}; +#endif + +#ifdef CONFIG_IMXRT_LPI2C3 +static const struct imxrt_lpi2c_config_s imxrt_lpi2c3_config = +{ + .base = IMXRT_LPI2C3_BASE, + .busy_idle = CONFIG_LPI2C3_BUSYIDLE, + .filtscl = CONFIG_LPI2C3_FILTSCL, + .filtsda = CONFIG_LPI2C3_FILTSDA, + .scl_pin = GPIO_LPI2C3_SCL, + .sda_pin = GPIO_LPI2C3_SDA, +#if defined(CONFIG_I2C_RESET) + .reset_scl_pin = GPIO_LPI2C3_SCL_RESET, + .reset_sda_pin = GPIO_LPI2C3_SDA_RESET, +#endif +#ifndef CONFIG_I2C_SLAVE + .mode = LPI2C_MASTER, +#else + .mode = LPI2C_SLAVE, +#endif +#ifndef CONFIG_I2C_POLLED + .irq = IMXRT_IRQ_LPI2C3, +#endif +}; + +static struct imxrt_lpi2c_priv_s imxrt_lpi2c3_priv = +{ + .ops = &imxrt_lpi2c_ops, + .config = &imxrt_lpi2c3_config, + .refs = 0, + .intstate = INTSTATE_IDLE, + .msgc = 0, + .msgv = NULL, + .ptr = NULL, + .dcnt = 0, + .flags = 0, + .status = 0 +}; +#endif + +#ifdef CONFIG_IMXRT_LPI2C4 +static const struct imxrt_lpi2c_config_s imxrt_lpi2c4_config = +{ + .base = IMXRT_LPI2C4_BASE, + .busy_idle = CONFIG_LPI2C4_BUSYIDLE, + .filtscl = CONFIG_LPI2C4_FILTSCL, + .filtsda = CONFIG_LPI2C4_FILTSDA, + .scl_pin = GPIO_LPI2C4_SCL, + .sda_pin = GPIO_LPI2C4_SDA, +#if defined(CONFIG_I2C_RESET) + .reset_scl_pin = GPIO_LPI2C4_SCL_RESET, + .reset_sda_pin = GPIO_LPI2C4_SDA_RESET, +#endif +#ifndef CONFIG_I2C_SLAVE + .mode = LPI2C_MASTER, +#else + .mode = LPI2C_SLAVE, +#endif +#ifndef CONFIG_I2C_POLLED + .irq = IMXRT_IRQ_LPI2C4, +#endif +}; + +static struct imxrt_lpi2c_priv_s imxrt_lpi2c4_priv = +{ + .ops = &imxrt_lpi2c_ops, + .config = &imxrt_lpi2c4_config, + .refs = 0, + .intstate = INTSTATE_IDLE, + .msgc = 0, + .msgv = NULL, + .ptr = NULL, + .dcnt = 0, + .flags = 0, + .status = 0 +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imxrt_lpi2c_getreg + * + * Description: + * Get a 32-bit register value by offset + * + ****************************************************************************/ + +static inline uint32_t + imxrt_lpi2c_getreg(FAR struct imxrt_lpi2c_priv_s *priv, uint16_t offset) +{ + return getreg32(priv->config->base + offset); +} + +/**************************************************************************** + * Name: imxrt_lpi2c_putreg + * + * Description: + * Put a 32-bit register value by offset + * + ****************************************************************************/ + +static inline void imxrt_lpi2c_putreg(FAR struct imxrt_lpi2c_priv_s *priv, + uint16_t offset, uint32_t value) +{ + putreg32(value, priv->config->base + offset); +} + +/**************************************************************************** + * Name: imxrt_lpi2c_modifyreg + * + * Description: + * Modify a 32-bit register value by offset + * + ****************************************************************************/ + +static inline void imxrt_lpi2c_modifyreg(FAR struct imxrt_lpi2c_priv_s *priv, + uint16_t offset, uint32_t clearbits, + uint32_t setbits) +{ + modifyreg32(priv->config->base + offset, clearbits, setbits); +} + +/**************************************************************************** + * Name: imxrt_lpi2c_sem_wait + * + * Description: + * Take the exclusive access, waiting as necessary. May be interrupted by + * a signal. + * + ****************************************************************************/ + +static inline int imxrt_lpi2c_sem_wait(FAR struct imxrt_lpi2c_priv_s *priv) +{ + return nxsem_wait(&priv->sem_excl); +} + +/**************************************************************************** + * Name: imxrt_lpi2c_sem_wait_noncancelable + * + * Description: + * Take the exclusive access, waiting as necessary. + * + ****************************************************************************/ + +#ifdef CONFIG_I2C_RESET +static int + imxrt_lpi2c_sem_wait_noncancelable(FAR struct imxrt_lpi2c_priv_s *priv) +{ + return nxsem_wait_uninterruptible(&priv->sem_excl); +} +#endif + +/**************************************************************************** + * Name: imxrt_lpi2c_tousecs + * + * Description: + * Return a micro-second delay based on the number of bytes left to be + * processed. + * + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_LPI2C_DYNTIMEO +static useconds_t imxrt_lpi2c_tousecs(int msgc, FAR struct i2c_msg_s *msgs) +{ + size_t bytecount = 0; + int i; + + /* Count the number of bytes left to process */ + + for (i = 0; i < msgc; i++) + { + bytecount += msgs[i].length; + } + + /* Then return a number of microseconds based on a user provided scaling + * factor. + */ + + return (useconds_t)(CONFIG_IMXRT_LPI2C_DYNTIMEO_USECPERBYTE * bytecount); +} +#endif + +/**************************************************************************** + * Name: imxrt_lpi2c_sem_waitdone + * + * Description: + * Wait for a transfer to complete + * + ****************************************************************************/ + +#ifndef CONFIG_I2C_POLLED +static inline int + imxrt_lpi2c_sem_waitdone(FAR struct imxrt_lpi2c_priv_s *priv) +{ + struct timespec abstime; + irqstate_t flags; + uint32_t regval; + int ret; + + flags = enter_critical_section(); + + /* Enable Interrupts when master mode */ + + if (priv->config->mode == LPI2C_MASTER) + { + if ((priv->flags & I2C_M_READ) != 0) + { + regval = LPI2C_MIER_TDIE | LPI2C_MIER_RDIE | LPI2C_MIER_NDIE | \ + LPI2C_MIER_ALIE | LPI2C_MIER_SDIE; + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MIER_OFFSET, regval); + } + else + { + regval = LPI2C_MIER_TDIE | LPI2C_MIER_NDIE | \ + LPI2C_MIER_ALIE | LPI2C_MIER_SDIE; + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MIER_OFFSET, regval); + } + } + + /* Enable Interrupts when slave mode */ + + else + { +#warning Missing logic for I2C Slave mode + } + + /* Signal the interrupt handler that we are waiting. NOTE: Interrupts + * are currently disabled but will be temporarily re-enabled below when + * nxsem_timedwait() sleeps. + */ + + priv->intstate = INTSTATE_WAITING; + do + { + /* Get the current time */ + + clock_gettime(CLOCK_REALTIME, &abstime); + + /* Calculate a time in the future */ + +#if CONFIG_IMXRT_LPI2C_TIMEOSEC > 0 + abstime.tv_sec += CONFIG_IMXRT_LPI2C_TIMEOSEC; +#endif + + /* Add a value proportional to the number of bytes in the transfer */ + +#ifdef CONFIG_IMXRT_LPI2C_DYNTIMEO + abstime.tv_nsec += 1000 * imxrt_lpi2c_tousecs(priv->msgc, priv->msgv); + if (abstime.tv_nsec >= 1000 * 1000 * 1000) + { + abstime.tv_sec++; + abstime.tv_nsec -= 1000 * 1000 * 1000; + } + +#elif CONFIG_IMXRT_LPI2C_TIMEOMS > 0 + abstime.tv_nsec += CONFIG_IMXRT_LPI2C_TIMEOMS * 1000 * 1000; + if (abstime.tv_nsec >= 1000 * 1000 * 1000) + { + abstime.tv_sec++; + abstime.tv_nsec -= 1000 * 1000 * 1000; + } +#endif + + /* Wait until either the transfer is complete or the timeout expires */ + + ret = nxsem_timedwait_uninterruptible(&priv->sem_isr, &abstime); + if (ret < 0) + { + /* Break out of the loop on irrecoverable errors. This would + * include timeouts and mystery errors reported by nxsem_timedwait. + */ + + break; + } + } + + /* Loop until the interrupt level transfer is complete. */ + + while (priv->intstate != INTSTATE_DONE); + + /* Set the interrupt state back to IDLE */ + + priv->intstate = INTSTATE_IDLE; + + /* Disable I2C interrupts */ + + /* Enable Interrupts when master mode */ + + if (priv->config->mode == LPI2C_MASTER) + { + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MIER_OFFSET, 0); + } + + /* Enable Interrupts when slave mode */ + + else + { + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_SIER_OFFSET, 0); + } + + leave_critical_section(flags); + return ret; +} +#else +static inline int + imxrt_lpi2c_sem_waitdone(FAR struct imxrt_lpi2c_priv_s *priv) +{ + clock_t timeout; + clock_t start; + clock_t elapsed; + int ret; + + /* Get the timeout value */ + +#ifdef CONFIG_IMXRT_LPI2C_DYNTIMEO + timeout = USEC2TICK(imxrt_lpi2c_tousecs(priv->msgc, priv->msgv)); +#else + timeout = CONFIG_IMXRT_LPI2C_TIMEOTICKS; +#endif + + /* Signal the interrupt handler that we are waiting. NOTE: Interrupts + * are currently disabled but will be temporarily re-enabled below when + * nxsem_timedwait() sleeps. + */ + + priv->intstate = INTSTATE_WAITING; + start = clock_systime_ticks(); + + do + { + /* Calculate the elapsed time */ + + elapsed = clock_systime_ticks() - start; + + /* Poll by simply calling the timer interrupt handler until it + * reports that it is done. + */ + + imxrt_lpi2c_isr_process(priv); + } + + /* Loop until the transfer is complete. */ + + while (priv->intstate != INTSTATE_DONE && elapsed < timeout); + + i2cinfo("intstate: %d elapsed: %ld threshold: %ld status: %08x\n", + priv->intstate, (long)elapsed, (long)timeout, priv->status); + + /* Set the interrupt state back to IDLE */ + + ret = priv->intstate == INTSTATE_DONE ? OK : -ETIMEDOUT; + priv->intstate = INTSTATE_IDLE; + return ret; +} +#endif + +/**************************************************************************** + * Name: imxrt_lpi2c_sem_waitstop + * + * Description: + * Wait for a STOP to complete + * + ****************************************************************************/ + +static inline void + imxrt_lpi2c_sem_waitstop(FAR struct imxrt_lpi2c_priv_s *priv) +{ + clock_t start; + clock_t elapsed; + clock_t timeout; + uint32_t regval; + + /* Select a timeout */ + +#ifdef CONFIG_IMXRT_LPI2C_DYNTIMEO + timeout = USEC2TICK(CONFIG_IMXRT_LPI2C_DYNTIMEO_STARTSTOP); +#else + timeout = CONFIG_IMXRT_LPI2C_TIMEOTICKS; +#endif + + /* Wait as stop might still be in progress; but stop might also + * be set because of a timeout error: "The [STOP] bit is set and + * cleared by software, cleared by hardware when a Stop condition is + * detected, set by hardware when a timeout error is detected." + */ + + start = clock_systime_ticks(); + do + { + /* Calculate the elapsed time */ + + elapsed = clock_systime_ticks() - start; + + /* Check for STOP condition */ + + if (priv->config->mode == LPI2C_MASTER) + { + regval = imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MSR_OFFSET); + if ((regval & LPI2C_MSR_SDF) == LPI2C_MSR_SDF) + { + return; + } + } + + /* Enable Interrupts when slave mode */ + + else + { + regval = imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_SSR_OFFSET); + if ((regval & LPI2C_SSR_SDF) == LPI2C_SSR_SDF) + { + return; + } + } + + /* Check for NACK error */ + + if (priv->config->mode == LPI2C_MASTER) + { + regval = imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MSR_OFFSET); + if ((regval & LPI2C_MSR_NDF) == LPI2C_MSR_NDF) + { + return; + } + } + + /* Enable Interrupts when slave mode */ + + else + { +#warning Missing logic for I2C Slave + } + } + + /* Loop until the stop is complete or a timeout occurs. */ + + while (elapsed < timeout); + + /* If we get here then a timeout occurred with the STOP condition + * still pending. + */ + + i2cinfo("Timeout with Status Register: %" PRIx32 "\n", regval); +} + +/**************************************************************************** + * Name: imxrt_lpi2c_sem_post + * + * Description: + * Release the mutual exclusion semaphore + * + ****************************************************************************/ + +static inline void imxrt_lpi2c_sem_post(struct imxrt_lpi2c_priv_s *priv) +{ + nxsem_post(&priv->sem_excl); +} + +/**************************************************************************** + * Name: imxrt_lpi2c_sem_init + * + * Description: + * Initialize semaphores + * + ****************************************************************************/ + +static inline void imxrt_lpi2c_sem_init(FAR struct imxrt_lpi2c_priv_s *priv) +{ + nxsem_init(&priv->sem_excl, 0, 1); + +#ifndef CONFIG_I2C_POLLED + /* This semaphore is used for signaling and, hence, should not have + * priority inheritance enabled. + */ + + nxsem_init(&priv->sem_isr, 0, 0); + nxsem_set_protocol(&priv->sem_isr, SEM_PRIO_NONE); +#endif +} + +/**************************************************************************** + * Name: imxrt_lpi2c_sem_destroy + * + * Description: + * Destroy semaphores. + * + ****************************************************************************/ + +static inline void + imxrt_lpi2c_sem_destroy(FAR struct imxrt_lpi2c_priv_s *priv) +{ + nxsem_destroy(&priv->sem_excl); +#ifndef CONFIG_I2C_POLLED + nxsem_destroy(&priv->sem_isr); +#endif +} + +/**************************************************************************** + * Name: imxrt_lpi2c_trace* + * + * Description: + * I2C trace instrumentation + * + ****************************************************************************/ + +#ifdef CONFIG_I2C_TRACE +static void imxrt_lpi2c_traceclear(FAR struct imxrt_lpi2c_priv_s *priv) +{ + struct imxrt_trace_s *trace = &priv->trace[priv->tndx]; + + trace->status = 0; /* I2C 32-bit SR2|SR1 status */ + trace->count = 0; /* Interrupt count when status change */ + trace->event = I2CEVENT_NONE; /* Last event that occurred with this status */ + trace->parm = 0; /* Parameter associated with the event */ + trace->time = 0; /* Time of first status or event */ +} + +static void imxrt_lpi2c_tracereset(FAR struct imxrt_lpi2c_priv_s *priv) +{ + /* Reset the trace info for a new data collection */ + + priv->tndx = 0; + priv->start_time = clock_systime_ticks(); + imxrt_lpi2c_traceclear(priv); +} + +static void imxrt_lpi2c_tracenew(FAR struct imxrt_lpi2c_priv_s *priv, + uint32_t status) +{ + struct imxrt_trace_s *trace = &priv->trace[priv->tndx]; + + /* Is the current entry uninitialized? Has the status changed? */ + + if (trace->count == 0 || status != trace->status) + { + /* Yes.. Was it the status changed? */ + + if (trace->count != 0) + { + /* Yes.. bump up the trace index (unless out of trace entries) */ + + if (priv->tndx >= (CONFIG_I2C_NTRACE - 1)) + { + i2cerr("ERROR: Trace table overflow\n"); + return; + } + + priv->tndx++; + trace = &priv->trace[priv->tndx]; + } + + /* Initialize the new trace entry */ + + imxrt_lpi2c_traceclear(priv); + trace->status = status; + trace->count = 1; + trace->time = clock_systime_ticks(); + } + else + { + /* Just increment the count of times that we have seen this status */ + + trace->count++; + } +} + +static void imxrt_lpi2c_traceevent(FAR struct imxrt_lpi2c_priv_s *priv, + enum imxrt_trace_e event, uint32_t parm) +{ + struct imxrt_trace_s *trace; + + if (event != I2CEVENT_NONE) + { + trace = &priv->trace[priv->tndx]; + + /* Initialize the new trace entry */ + + trace->event = event; + trace->parm = parm; + + /* Bump up the trace index (unless we are out of trace entries) */ + + if (priv->tndx >= (CONFIG_I2C_NTRACE - 1)) + { + i2cerr("ERROR: Trace table overflow\n"); + return; + } + + priv->tndx++; + imxrt_lpi2c_traceclear(priv); + } +} + +static void imxrt_lpi2c_tracedump(FAR struct imxrt_lpi2c_priv_s *priv) +{ + struct imxrt_trace_s *trace; + int i; + + syslog(LOG_DEBUG, "Elapsed time: %ld\n", + (long)(clock_systime_ticks() - priv->start_time)); + + for (i = 0; i < priv->tndx; i++) + { + trace = &priv->trace[i]; + syslog(LOG_DEBUG, + "%2d. STATUS: %08x COUNT: %3d EVENT: %s(%2d) PARM: %08x " + "TIME: %d\n", + i + 1, trace->status, trace->count, + g_trace_names[trace->event], + trace->event, trace->parm, trace->time - priv->start_time); + } +} +#endif /* CONFIG_I2C_TRACE */ + +/**************************************************************************** + * Name: imxrt_lpi2c_setclock + * + * Description: + * Set the I2C clock + * + ****************************************************************************/ + +static void imxrt_lpi2c_setclock(FAR struct imxrt_lpi2c_priv_s *priv, + uint32_t frequency) +{ + uint32_t src_freq = 0; + uint32_t pll3_div = 0; + uint32_t lpi2c_clk_div; + uint32_t regval; + uint32_t men; + uint32_t prescale = 0; + uint32_t best_prescale = 0; + uint32_t best_clk_hi = 0; + uint32_t abs_error = 0; + uint32_t best_error = 0xffffffff; + uint32_t clk_hi_cycle; + uint32_t computed_rate; + uint32_t count; + + /* Has the I2C bus frequency changed? */ + + if (priv->config->mode == LPI2C_MASTER) + { + if (frequency != priv->frequency) + { + /* Disable the selected LPI2C peripheral to configure the new + * clock if it is enabled. + */ + + men = imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MCR_OFFSET) & + LPI2C_MCR_MEN; + if (men) + { + imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCR_OFFSET, + LPI2C_MCR_MEN, 0); + } + + /* Get the LPI2C clock source frequency */ + + if ((getreg32(IMXRT_CCM_CSCDR2) & CCM_CSCDR2_LPI2C_CLK_SEL) == + CCM_CSCDR2_LPI2C_CLK_SEL_OSC_CLK) + { + src_freq = BOARD_XTAL_FREQUENCY; + } + else + { + if ((getreg32(IMXRT_CCM_ANALOG_PLL_USB2) & + CCM_ANALOG_PLL_USB2_DIV_SELECT_MASK) != 0) + { + pll3_div = 22; + } + else + { + pll3_div = 20; + } + + lpi2c_clk_div = (getreg32(IMXRT_CCM_CSCDR2) & + CCM_CSCDR2_LPI2C_CLK_PODF_MASK) >> + CCM_CSCDR2_LPI2C_CLK_PODF_SHIFT; + lpi2c_clk_div = lpi2c_clk_div + 1; + src_freq = (BOARD_XTAL_FREQUENCY * pll3_div) / + (8 * lpi2c_clk_div) ; + } + + /* LPI2C output frequency = (Source Clock (Hz)/ 2^prescale) / + * (CLKLO + 1 + CLKHI + 1 + ROUNDDOWN((2 + FILTSCL) / 2^prescale) + * + * Assume CLKLO = 2 * CLKHI, SETHOLD = CLKHI, DATAVD = CLKHI / 2 + */ + + for (prescale = 1; + (prescale <= 128) && (best_error != 0); + prescale *= 2) + { + for (clk_hi_cycle = 1; clk_hi_cycle < 32; clk_hi_cycle++) + { + if (clk_hi_cycle == 1) + { + computed_rate = (src_freq / prescale) / + (6 + (2 / prescale)); + } + else + { + computed_rate = (src_freq / prescale) / + ((3 * clk_hi_cycle + 2) + + (2 / prescale)); + } + + if (frequency > computed_rate) + { + abs_error = frequency - computed_rate; + } + else + { + abs_error = computed_rate - frequency; + } + + if (abs_error < best_error) + { + best_prescale = prescale; + best_clk_hi = clk_hi_cycle; + best_error = abs_error; + + if (abs_error == 0) + { + break; + } + } + } + } + + regval = LPI2C_MCCR0_CLKHI(best_clk_hi); + + if (best_clk_hi < 2) + { + regval |= LPI2C_MCCR0_CLKLO(3) | LPI2C_MCCR0_SETHOLD(2) | + LPI2C_MCCR0_DATAVD(1); + } + else + { + regval |= LPI2C_MCCR0_CLKLO(2 * best_clk_hi) | + LPI2C_MCCR0_SETHOLD(best_clk_hi) | + LPI2C_MCCR0_DATAVD(best_clk_hi / 2); + } + + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MCCR0_OFFSET, regval); + + for (count = 0; count < 8; count++) + { + if (best_prescale == (1 << count)) + { + best_prescale = count; + break; + } + } + + imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCFGR1_OFFSET, + LPI2C_MCFGR1_PRESCALE_MASK, + LPI2C_MCFGR1_PRESCALE(best_prescale)); + + /* Re-enable LPI2C if it was enabled previously */ + + if (men) + { + imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCR_OFFSET, 0, + LPI2C_MCR_MEN); + } + + /* Save the new LPI2C frequency */ + + priv->frequency = frequency; + } + } +} + +/**************************************************************************** + * Name: imxrt_lpi2c_sendstart + * + * Description: + * Send the START conditions/force Master mode + * + ****************************************************************************/ + +static inline void imxrt_lpi2c_sendstart(FAR struct imxrt_lpi2c_priv_s *priv, + uint8_t address) +{ + uint32_t txcount = 0; + uint32_t status = 0; + uint8_t addr; + + /* Generate START condition and send the address */ + + /* Turn off auto_stop option */ + + imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCFGR1_OFFSET, + LPI2C_MCFGR1_IGNACK, 0); + + do + { + txcount = (imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MFSR_OFFSET) & + LPI2C_MFSR_TXCOUNT_MASK) >> LPI2C_MFSR_TXCOUNT_SHIFT; + txcount = 4 - txcount; + + status = imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MSR_OFFSET); + + if (status & LPI2C_MSR_ERROR_MASK) + { + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MSR_OFFSET, + status & LPI2C_MSR_ERROR_MASK); + } + } + while (txcount == 0); + + if ((priv->flags & I2C_M_READ) != 0) + { + addr = I2C_READADDR8(address); + } + else + { + addr = I2C_WRITEADDR8(address); + } + + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MTDR_OFFSET, + (LPI2C_MTDR_CMD_START | LPI2C_MTDR_DATA(addr))); +} + +/**************************************************************************** + * Name: imxrt_lpi2c_sendstop + * + * Description: + * Send the STOP conditions + * + ****************************************************************************/ + +static inline void imxrt_lpi2c_sendstop(FAR struct imxrt_lpi2c_priv_s *priv) +{ + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MTDR_OFFSET, LPI2C_MTDR_CMD_STOP); +} + +/**************************************************************************** + * Name: imxrt_lpi2c_getstatus + * + * Description: + * Get 32-bit status + * + ****************************************************************************/ + +static inline uint32_t + imxrt_lpi2c_getstatus(FAR struct imxrt_lpi2c_priv_s *priv) +{ + return imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MSR_OFFSET); +} + +/**************************************************************************** + * Name: imxrt_lpi2c_isr_process + * + * Description: + * Common Interrupt Service Routine + * + ****************************************************************************/ + +static int imxrt_lpi2c_isr_process(struct imxrt_lpi2c_priv_s *priv) +{ + uint32_t status = imxrt_lpi2c_getstatus(priv); + + /* Check for new trace setup */ + + imxrt_lpi2c_tracenew(priv, status); + + /* After an error we can get an SDF */ + + if (priv->intstate == INTSTATE_DONE && (status & LPI2C_MSR_SDF) != 0) + { + imxrt_lpi2c_traceevent(priv, I2CEVENT_STOP, 0); + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MSR_OFFSET, LPI2C_MSR_SDF); + } + + /* Check if there is more bytes to send */ + + else if (((priv->flags & I2C_M_READ) == 0) && + (status & LPI2C_MSR_TDF) != 0) + { + if (priv->dcnt > 0) + { + imxrt_lpi2c_traceevent(priv, I2CEVENT_SENDBYTE, priv->dcnt); + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MTDR_OFFSET, + LPI2C_MTDR_CMD_TXD | + LPI2C_MTDR_DATA(*priv->ptr++)); + priv->dcnt--; + + if ((priv->msgc <= 0) && (priv->dcnt == 0)) + { + imxrt_lpi2c_sendstop(priv); + } + } + } + + /* Check if there is more bytes to read */ + + else if (((priv->flags & I2C_M_READ) != 0) && + (status & LPI2C_MSR_RDF) != 0) + { + /* Read a byte, if dcnt goes < 0, then read dummy bytes to ack ISRs */ + + if (priv->dcnt > 0) + { + imxrt_lpi2c_traceevent(priv, I2CEVENT_RCVBYTE, priv->dcnt); + + /* No interrupts or context switches should occur in the following + * sequence. Otherwise, additional bytes may be sent by the device. + */ + +#ifdef CONFIG_I2C_POLLED + irqstate_t flags = enter_critical_section(); +#endif + + /* Receive a byte */ + + *priv->ptr++ = imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MRDR_OFFSET) & + LPI2C_MRDR_DATA_MASK; + priv->dcnt--; + +#ifdef CONFIG_I2C_POLLED + leave_critical_section(flags); +#endif + if ((priv->msgc <= 0) && (priv->dcnt == 0)) + { + imxrt_lpi2c_sendstop(priv); + } + } + else + { + imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MRDR_OFFSET); + } + } + + if (priv->dcnt <= 0) + { + if (priv->msgc > 0 && priv->msgv != NULL) + { + priv->ptr = priv->msgv->buffer; + priv->dcnt = priv->msgv->length; + priv->flags = priv->msgv->flags; + + if ((priv->msgv->flags & I2C_M_NOSTART) == 0) + { + imxrt_lpi2c_traceevent(priv, I2CEVENT_STARTRESTART, + priv->msgc); + imxrt_lpi2c_sendstart(priv, priv->msgv->addr); + } + else + { + imxrt_lpi2c_traceevent(priv, I2CEVENT_NOSTART, priv->msgc); + } + + priv->msgv++; + priv->msgc--; + + if ((priv->flags & I2C_M_READ) != 0) + { +#ifndef CONFIG_I2C_POLLED + /* Stop TX interrupt */ + + imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MIER_OFFSET, + LPI2C_MIER_TDIE, LPI2C_MIER_RDIE); +#endif + /* Set LPI2C in read mode */ + + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MTDR_OFFSET, + LPI2C_MTDR_CMD_RXD | + LPI2C_MTDR_DATA((priv->dcnt - 1))); + } + else + { + /* Send the first byte from tx buffer */ + + imxrt_lpi2c_traceevent(priv, I2CEVENT_SENDBYTE, priv->dcnt); + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MTDR_OFFSET, + LPI2C_MTDR_CMD_TXD | + LPI2C_MTDR_DATA(*priv->ptr++)); + priv->dcnt--; + if ((priv->msgc <= 0) && (priv->dcnt == 0)) + { + imxrt_lpi2c_sendstop(priv); + } + } + } + else if (priv->msgv && ((status & LPI2C_MSR_SDF) != 0)) + { + imxrt_lpi2c_traceevent(priv, I2CEVENT_STOP, 0); + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MSR_OFFSET, LPI2C_MSR_SDF); + + /* Check is there thread waiting for this event (there should be) */ + +#ifndef CONFIG_I2C_POLLED + if (priv->intstate == INTSTATE_WAITING) + { + /* Update Status once at the end */ + + priv->status = status; + + /* inform the thread that transfer is complete + * and wake it up + */ + + nxsem_post(&priv->sem_isr); + priv->intstate = INTSTATE_DONE; + } +#else + priv->status = status; + priv->intstate = INTSTATE_DONE; +#endif + /* Mark that this transaction stopped */ + + priv->msgv = NULL; + } +#ifndef CONFIG_I2C_POLLED + else + { + imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MIER_OFFSET, + LPI2C_MIER_TDIE | LPI2C_MIER_RDIE, 0); + } +#endif + } + + /* Check for errors */ + + if ((status & LPI2C_MSR_EPF) != 0) + { + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MSR_OFFSET, LPI2C_MSR_EPF); + } + + if ((status & LPI2C_MSR_ERROR_MASK) != 0) + { + imxrt_lpi2c_traceevent(priv, I2CEVENT_ERROR, 0); + + /* Clear the TX and RX FIFOs */ + + imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCR_OFFSET, 0, + LPI2C_MCR_RTF | LPI2C_MCR_RRF); + + /* Clear the error */ + + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MSR_OFFSET, + (status & (LPI2C_MSR_NDF | LPI2C_MSR_ALF | + LPI2C_MSR_FEF))); + +#ifndef CONFIG_I2C_POLLED + if (priv->intstate == INTSTATE_WAITING) + { + /* Update Status once at the end */ + + priv->status = status; + + /* inform the thread that transfer is complete + * and wake it up + */ + + nxsem_post(&priv->sem_isr); + priv->intstate = INTSTATE_DONE; + } +#else + priv->status = status; + priv->intstate = INTSTATE_DONE; +#endif + } + + return OK; +} + +/**************************************************************************** + * Name: imxrt_lpi2c_isr + * + * Description: + * Common I2C interrupt service routine + * + ****************************************************************************/ + +#ifndef CONFIG_I2C_POLLED +static int imxrt_lpi2c_isr(int irq, void *context, FAR void *arg) +{ + struct imxrt_lpi2c_priv_s *priv = (struct imxrt_lpi2c_priv_s *)arg; + + DEBUGASSERT(priv != NULL); + return imxrt_lpi2c_isr_process(priv); +} +#endif + +/**************************************************************************** + * Name: imxrt_lpi2c_clock_enable + * + * Description: + * Ungate LPI2C clock + * + ****************************************************************************/ + +void imxrt_lpi2c_clock_enable (uint32_t base) +{ + if (base == IMXRT_LPI2C1_BASE) + { + imxrt_clockall_lpi2c1(); + } + else if (base == IMXRT_LPI2C2_BASE) + { + imxrt_clockall_lpi2c2(); + } + else if (base == IMXRT_LPI2C3_BASE) + { + imxrt_clockall_lpi2c3(); + } + else if (base == IMXRT_LPI2C4_BASE) + { + imxrt_clockall_lpi2c4_serial(); + } +} + +/**************************************************************************** + * Name: imxrt_lpi2c_clock_disable + * + * Description: + * Gate LPI2C clock + * + ****************************************************************************/ + +void imxrt_lpi2c_clock_disable (uint32_t base) +{ + if (base == IMXRT_LPI2C1_BASE) + { + imxrt_clockoff_lpi2c1(); + } + else if (base == IMXRT_LPI2C2_BASE) + { + imxrt_clockoff_lpi2c2(); + } + else if (base == IMXRT_LPI2C3_BASE) + { + imxrt_clockoff_lpi2c3(); + } + else if (base == IMXRT_LPI2C4_BASE) + { + imxrt_clockoff_lpi2c4_serial(); + } +} + +/**************************************************************************** + * Name: imxrt_lpi2c_init + * + * Description: + * Setup the I2C hardware, ready for operation with defaults + * + ****************************************************************************/ + +static int imxrt_lpi2c_init(FAR struct imxrt_lpi2c_priv_s *priv) +{ + /* Power-up and configure GPIOs */ + + /* Configure pins */ + + imxrt_config_gpio(priv->config->scl_pin); + imxrt_config_gpio(priv->config->sda_pin); + + /* Enable power and reset the peripheral */ + + imxrt_lpi2c_clock_enable(priv->config->base); + + /* Reset LPI2C before configuring it */ + + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MCR_OFFSET, LPI2C_MCR_RST); + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MCR_OFFSET, 0); + + /* Disable doze mode (Set DOZEN bit in 1 to disable) */ + + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MCR_OFFSET, LPI2C_MCR_DOZEN); + + /* Disable host request */ + + imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCFGR0_OFFSET, + LPI2C_MCFG0_HREN | LPI2C_MCFG0_HRSEL, + LPI2C_MCFG0_HRPOL); + + /* Pin config and ignore NACK disable */ + + imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCFGR1_OFFSET, + LPI2C_MCFGR1_IGNACK | LPI2C_MCFGR1_PINCFG_MASK, 0); + + /* Set tx and rx watermarks */ + + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MFCR_OFFSET, + LPI2C_MFCR_TXWATER(0) | LPI2C_MFCR_RXWATER(0)); + + /* Force a frequency update */ + + priv->frequency = 0; + imxrt_lpi2c_setclock(priv, 100000); + + /* Set scl, sda glitch filters and busy idle */ + + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MCFGR2_OFFSET, + LPI2C_MCFG2_BUSIDLE(priv->config->busy_idle) | + LPI2C_MCFG2_FILTSCL_CYCLES(priv->config->filtscl) | + LPI2C_MCFG2_FILTSDA_CYCLES(priv->config->filtsda)); + + /* Set pin low cycles to 0 (disable) */ + + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MCFGR3_OFFSET, + LPI2C_MCFG3_PINLOW_CYCLES(0)); + + /* Attach ISRs */ + +#ifndef CONFIG_I2C_POLLED + irq_attach(priv->config->irq, imxrt_lpi2c_isr, priv); + up_enable_irq(priv->config->irq); +#endif + + /* Enable I2C */ + + imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCR_OFFSET, 0, LPI2C_MCR_MEN); + return OK; +} + +/**************************************************************************** + * Name: imxrt_lpi2c_deinit + * + * Description: + * Shutdown the I2C hardware + * + ****************************************************************************/ + +static int imxrt_lpi2c_deinit(FAR struct imxrt_lpi2c_priv_s *priv) +{ + /* Disable I2C */ + + imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCR_OFFSET, LPI2C_MCR_MEN, 0); + + /* Reset LPI2C */ + + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MCR_OFFSET, LPI2C_MCR_RST); + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MCR_OFFSET, 0); + + /* Disable and detach interrupts */ + +#ifndef CONFIG_I2C_POLLED + up_disable_irq(priv->config->irq); + irq_detach(priv->config->irq); +#endif + + /* Disable clocking */ + + imxrt_lpi2c_clock_disable(priv->config->base); + + return OK; +} + +/**************************************************************************** + * Device Driver Operations + ****************************************************************************/ + +/**************************************************************************** + * Name: imxrt_lpi2c_transfer + * + * Description: + * Generic I2C transfer function + * + ****************************************************************************/ + +static int imxrt_lpi2c_transfer(FAR struct i2c_master_s *dev, + FAR struct i2c_msg_s *msgs, int count) +{ + FAR struct imxrt_lpi2c_priv_s *priv = (struct imxrt_lpi2c_priv_s *)dev; + int ret; + + DEBUGASSERT(count > 0); + + /* Ensure that address or flags don't change meanwhile */ + + ret = imxrt_lpi2c_sem_wait(priv); + if (ret < 0) + { + return ret; + } + + /* Clear any pending error interrupts */ + + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MSR_OFFSET, 0xffffffff); + + /* Old transfers are done */ + + /* Reset ptr and dcnt to ensure an unexpected data interrupt doesn't + * overwrite stale data. + */ + + priv->dcnt = 0; + priv->ptr = NULL; + + priv->msgv = msgs; + priv->msgc = count; + priv->flags = msgs->flags; + + i2cinfo("Flags %x, len %d \n", msgs->flags, msgs->length); + + /* Reset I2C trace logic */ + + imxrt_lpi2c_tracereset(priv); + + /* Set I2C clock frequency */ + + imxrt_lpi2c_setclock(priv, msgs->frequency); + + priv->status = 0; + + /* Wait for an ISR, if there was a timeout, fetch latest status to get + * the BUSY flag. + */ + + if (imxrt_lpi2c_sem_waitdone(priv) < 0) + { + ret = -ETIMEDOUT; + + i2cerr("ERROR: Timed out: MCR: status: 0x%" PRIx32 "\n", priv->status); + } + + /* Check for error status conditions */ + + else if ((priv->status & LPI2C_MSR_ERROR_MASK) != 0) + { + /* I2C_SR1_ERRORMASK is the 'OR' of the following individual bits: */ + + if (priv->status & LPI2C_MSR_ALF) + { + /* Arbitration Lost (master mode) */ + + i2cerr("Arbitration lost\n"); + ret = -EAGAIN; + } + else if (priv->status & LPI2C_MSR_NDF) + { + /* Acknowledge Failure */ + + i2cerr("Ack failure\n"); + ret = -ENXIO; + } + else + { + /* FIFO Error */ + + i2cerr("Transfer without start condition\n"); + ret = -EINVAL; + } + } + + /* Dump the trace result */ + + imxrt_lpi2c_tracedump(priv); + + /* Ensure that any ISR happening after we finish can't overwrite any user + * data. + */ + + priv->dcnt = 0; + priv->ptr = NULL; + + imxrt_lpi2c_sem_post(priv); + return ret; +} + +/**************************************************************************** + * Name: imxrt_lpi2c_reset + * + * Description: + * Perform an I2C bus reset in an attempt to break loose stuck I2C devices. + * + * Input Parameters: + * dev - Device-specific state data + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_I2C_RESET +static int imxrt_lpi2c_reset(FAR struct i2c_master_s *dev) +{ + FAR struct imxrt_lpi2c_priv_s *priv = (FAR struct imxrt_lpi2c_priv_s *)dev; + unsigned int clock_count; + unsigned int stretch_count; + uint32_t scl_gpio; + uint32_t sda_gpio; + uint32_t frequency; + int ret; + + DEBUGASSERT(dev); + + /* Our caller must own a ref */ + + DEBUGASSERT(priv->refs > 0); + + /* Lock out other clients */ + + ret = imxrt_lpi2c_sem_wait_noncancelable(priv); + if (ret < 0) + { + return ret; + } + + ret = -EIO; + + /* Save the current frequency */ + + frequency = priv->frequency; + + /* De-init the port */ + + imxrt_lpi2c_deinit(priv); + + /* Use GPIO configuration to un-wedge the bus */ + + scl_gpio = priv->config->reset_scl_pin | GPIO_SION_ENABLE; + sda_gpio = priv->config->reset_sda_pin | GPIO_SION_ENABLE; + + imxrt_config_gpio(scl_gpio); + imxrt_config_gpio(sda_gpio); + + /* Let SDA go high */ + + imxrt_gpio_write(sda_gpio, 1); + + /* Clock the bus until any slaves currently driving it let it go. */ + + clock_count = 0; + while (!imxrt_gpio_read(sda_gpio)) + { + /* Give up if we have tried too hard */ + + if (clock_count++ > 10) + { + goto out; + } + + /* Sniff to make sure that clock stretching has finished. + * + * If the bus never relaxes, the reset has failed. + */ + + stretch_count = 0; + while (!imxrt_gpio_read(scl_gpio)) + { + /* Give up if we have tried too hard */ + + if (stretch_count++ > 10) + { + goto out; + } + + up_udelay(10); + } + + /* Drive SCL low */ + + imxrt_gpio_write(scl_gpio, 0); + up_udelay(10); + + /* Drive SCL high again */ + + imxrt_gpio_write(scl_gpio, 1); + up_udelay(10); + } + + /* Generate a start followed by a stop to reset slave + * state machines. + */ + + imxrt_gpio_write(sda_gpio, 0); + up_udelay(10); + imxrt_gpio_write(scl_gpio, 0); + up_udelay(10); + imxrt_gpio_write(scl_gpio, 1); + up_udelay(10); + imxrt_gpio_write(sda_gpio, 1); + up_udelay(10); + + imxrt_config_gpio(sda_gpio); + imxrt_config_gpio(scl_gpio); + + /* Re-init the port */ + + imxrt_lpi2c_init(priv); + + /* Restore the frequency */ + + imxrt_lpi2c_setclock(priv, frequency); + ret = OK; + +out: + + /* Release the port for re-use by other clients */ + + imxrt_lpi2c_sem_post(priv); + return ret; +} +#endif /* CONFIG_I2C_RESET */ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imxrt_i2cbus_initialize + * + * Description: + * Initialize one I2C bus + * + ****************************************************************************/ + +FAR struct i2c_master_s *imxrt_i2cbus_initialize(int port) +{ + struct imxrt_lpi2c_priv_s * priv = NULL; + irqstate_t flags; + + /* Get I2C private structure */ + + switch (port) + { +#ifdef CONFIG_IMXRT_LPI2C1 + case 1: + priv = (struct imxrt_lpi2c_priv_s *)&imxrt_lpi2c1_priv; + break; +#endif +#ifdef CONFIG_IMXRT_LPI2C2 + case 2: + priv = (struct imxrt_lpi2c_priv_s *)&imxrt_lpi2c2_priv; + break; +#endif +#ifdef CONFIG_IMXRT_LPI2C3 + case 3: + priv = (struct imxrt_lpi2c_priv_s *)&imxrt_lpi2c3_priv; + break; +#endif +#ifdef CONFIG_IMXRT_LPI2C4 + case 4: + priv = (struct imxrt_lpi2c_priv_s *)&imxrt_lpi2c4_priv; + break; +#endif + default: + return NULL; + } + + /* Initialize private data for the first time, increment reference count, + * power-up hardware and configure GPIOs. + */ + + flags = enter_critical_section(); + + if ((volatile int)priv->refs++ == 0) + { + imxrt_lpi2c_sem_init(priv); + imxrt_lpi2c_init(priv); + } + + leave_critical_section(flags); + + return (struct i2c_master_s *)priv; +} + +/**************************************************************************** + * Name: imxrt_i2cbus_uninitialize + * + * Description: + * Uninitialize an I2C bus + * + ****************************************************************************/ + +int imxrt_i2cbus_uninitialize(FAR struct i2c_master_s *dev) +{ + FAR struct imxrt_lpi2c_priv_s *priv = (struct imxrt_lpi2c_priv_s *)dev; + irqstate_t flags; + + DEBUGASSERT(dev); + + /* Decrement reference count and check for underflow */ + + if (priv->refs == 0) + { + return ERROR; + } + + flags = enter_critical_section(); + + if (--priv->refs > 0) + { + leave_critical_section(flags); + return OK; + } + + leave_critical_section(flags); + + /* Disable power and other HW resource (GPIO's) */ + + imxrt_lpi2c_deinit(priv); + + /* Release unused resources */ + + imxrt_lpi2c_sem_destroy(priv); + return OK; +} + +#endif /* CONFIG_IMXRT_LPI2C1 || CONFIG_IMXRT_LPI2C2 || \ + * CONFIG_IMXRT_LPI2C3 || CONFIG_IMXRT_LPI2C4 */ diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpspi.c b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpspi.c new file mode 100644 index 000000000..3abe7d9d2 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpspi.c @@ -0,0 +1,1706 @@ +/**************************************************************************** + * arch/arm/src/imxrt/imxrt_lpspi.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. + * + ****************************************************************************/ + +/**************************************************************************** + * The external functions, imxrt_lpspi1/2/3/4select and + * imxrt_lpspi1/2/3/4status must be provided by board-specific logic. + * They are implementations of the select and status methods of the SPI + * interface defined by struct imxrt_lpspi_ops_s (see + * include/nuttx/spi/spi.h). All other methods (including + * imxrt_lpspibus_initialize()) are provided by common IMXRT logic. + * To use this common SPI logic on your board: + * + * 1. Provide logic in imxrt_boardinitialize() to configure SPI chip + * select pins. + * 2. Provide imxrt_lpspi1/2/3/4select() and imxrt_lpspi1/2/3/4status() + * functions in your board-specific logic. These functions will + * perform chip selection and status operations using GPIOs in the way + * your board is configured. + * 3. Add a calls to imxrt_lpspibus_initialize() in your low level + * application initialization logic + * 4. The handle returned by imxrt_lpspibus_initialize() may then be + * used to bind the SPI driver to higher level logic (e.g., calling + * mmcsd_lpspislotinitialize(), for example, will bind the SPI + * driver to the SPI MMC/SD driver). + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "arm_internal.h" +#include "arm_arch.h" + +#include "chip.h" + +#include "imxrt_lpspi.h" +#include "imxrt_gpio.h" +#include "hardware/imxrt_pinmux.h" +#include "hardware/imxrt_lpspi.h" +#include "hardware/imxrt_ccm.h" +#include "imxrt_periphclks.h" + +#if defined(CONFIG_IMXRT_LPSPI1) || defined(CONFIG_IMXRT_LPSPI2) || \ + defined(CONFIG_IMXRT_LPSPI3) || defined(CONFIG_IMXRT_LPSPI4) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +/* SPI interrupts */ + +#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS +# error "Interrupt driven SPI not yet supported" +#endif + +#if defined(CONFIG_IMXRT_LPSPI_DMA) +# error "DMA mode is not yet supported" +#endif + +/* Can't have both interrupt driven SPI and SPI DMA */ + +#if defined(CONFIG_IMXRT_LPSPI_INTERRUPTS) && defined(CONFIG_IMXRT_LPSPI_DMA) +# error "Cannot enable both interrupt mode and DMA mode for SPI" +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct imxrt_lpspidev_s +{ + struct spi_dev_s spidev; /* Externally visible part of the SPI interface */ + uint32_t spibase; /* SPIn base address */ +#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS + uint8_t spiirq; /* SPI IRQ number */ +#endif + sem_t exclsem; /* Held while chip is selected for mutual exclusion */ + uint32_t frequency; /* Requested clock frequency */ + uint32_t actual; /* Actual clock frequency */ + int8_t nbits; /* Width of word in bits */ + uint8_t mode; /* Mode 0,1,2,3 */ +}; + +enum imxrt_delay_e +{ + LPSPI_PCS_TO_SCK = 1, /* PCS-to-SCK delay. */ + LPSPI_LAST_SCK_TO_PCS, /* Last SCK edge to PCS delay. */ + LPSPI_BETWEEN_TRANSFER /* Delay between transfers. */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Helpers */ + +static inline uint32_t +imxrt_lpspi_getreg32(FAR struct imxrt_lpspidev_s *priv, + uint8_t offset); +static inline void imxrt_lpspi_putreg32(FAR struct imxrt_lpspidev_s *priv, + uint8_t offset, uint32_t value); +static inline uint32_t imxrt_lpspi_readword( + FAR struct imxrt_lpspidev_s *priv); +static inline void imxrt_lpspi_writeword(FAR struct imxrt_lpspidev_s *priv, + uint16_t byte); +static inline bool imxrt_lpspi_9to16bitmode( + FAR struct imxrt_lpspidev_s *priv); +static inline void imxrt_lpspi_master_set_delays(FAR struct imxrt_lpspidev_s + *priv, uint32_t delay_ns, + enum imxrt_delay_e type); +static inline void imxrt_lpspi_master_set_delay_scaler( + FAR struct imxrt_lpspidev_s *priv, + uint32_t scaler, + enum imxrt_delay_e type); + +/* SPI methods */ + +static int imxrt_lpspi_lock(FAR struct spi_dev_s *dev, bool lock); +static uint32_t imxrt_lpspi_setfrequency(FAR struct spi_dev_s *dev, + uint32_t frequency); +static void imxrt_lpspi_setmode(FAR struct spi_dev_s *dev, + enum spi_mode_e mode); +static void imxrt_lpspi_setbits(FAR struct spi_dev_s *dev, int nbits); +#ifdef CONFIG_SPI_HWFEATURES +static int imxrt_lpspi_hwfeatures(FAR struct spi_dev_s *dev, + imxrt_lpspi_hwfeatures_t features); +#endif +static uint32_t imxrt_lpspi_send(FAR struct spi_dev_s *dev, uint32_t wd); +static void imxrt_lpspi_exchange(FAR struct spi_dev_s *dev, + FAR const void *txbuffer, + FAR void *rxbuffer, + size_t nwords); +#ifndef CONFIG_SPI_EXCHANGE +static void imxrt_lpspi_sndblock(FAR struct spi_dev_s *dev, + FAR const void *txbuffer, size_t nwords); +static void imxrt_lpspi_recvblock(FAR struct spi_dev_s *dev, + FAR void *rxbuffer, + size_t nwords); +#endif + +/* Initialization */ + +static void imxrt_lpspi_bus_initialize(FAR struct imxrt_lpspidev_s *priv); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_LPSPI1 +static const struct spi_ops_s g_spi1ops = +{ + .lock = imxrt_lpspi_lock, + .select = imxrt_lpspi1select, + .setfrequency = imxrt_lpspi_setfrequency, + .setmode = imxrt_lpspi_setmode, + .setbits = imxrt_lpspi_setbits, +#ifdef CONFIG_SPI_HWFEATURES + .hwfeatures = imxrt_lpspi_hwfeatures, +#endif + .status = imxrt_lpspi1status, +#ifdef CONFIG_SPI_CMDDATA + .cmddata = imxrt_lpspi1cmddata, +#endif + .send = imxrt_lpspi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = imxrt_lpspi_exchange, +#else + .sndblock = imxrt_lpspi_sndblock, + .recvblock = imxrt_lpspi_recvblock, +#endif +#ifdef CONFIG_SPI_CALLBACK + .registercallback = imxrt_lpspi1register, /* Provided externally */ +#else + .registercallback = 0, /* Not implemented */ +#endif +}; + +static struct imxrt_lpspidev_s g_lpspi1dev = +{ + .spidev = + { + &g_spi1ops + }, + .spibase = IMXRT_LPSPI1_BASE, +#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS + .spiirq = IMXRT_IRQ_LPSPI1, +#endif +#ifdef CONFIG_IMXRT_LPSPI_DMA + .rxch = DMAMAP_LPSPI1_RX, + .txch = DMAMAP_LPSPI1_TX, +#endif +}; +#endif + +#ifdef CONFIG_IMXRT_LPSPI2 +static const struct spi_ops_s g_spi2ops = +{ + .lock = imxrt_lpspi_lock, + .select = imxrt_lpspi2select, + .setfrequency = imxrt_lpspi_setfrequency, + .setmode = imxrt_lpspi_setmode, + .setbits = imxrt_lpspi_setbits, +#ifdef CONFIG_SPI_HWFEATURES + .hwfeatures = imxrt_lpspi_hwfeatures, +#endif + .status = imxrt_lpspi2status, +#ifdef CONFIG_SPI_CMDDATA + .cmddata = imxrt_lpspi2cmddata, +#endif + .send = imxrt_lpspi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = imxrt_lpspi_exchange, +#else + .sndblock = imxrt_lpspi_sndblock, + .recvblock = imxrt_lpspi_recvblock, +#endif +#ifdef CONFIG_SPI_CALLBACK + .registercallback = imxrt_lpspi2register, /* Provided externally */ +#else + .registercallback = 0, /* Not implemented */ +#endif +}; + +static struct imxrt_lpspidev_s g_lpspi2dev = +{ + .spidev = + { + &g_spi2ops + }, + .spibase = IMXRT_LPSPI2_BASE, +#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS + .spiirq = IMXRT_IRQ_LPSPI2, +#endif +#ifdef CONFIG_IMXRT_LPSPI_DMA + .rxch = DMAMAP_LPSPI2_RX, + .txch = DMAMAP_LPSPI2_TX, +#endif +}; +#endif + +#ifdef CONFIG_IMXRT_LPSPI3 +static const struct spi_ops_s g_spi3ops = +{ + .lock = imxrt_lpspi_lock, + .select = imxrt_lpspi3select, + .setfrequency = imxrt_lpspi_setfrequency, + .setmode = imxrt_lpspi_setmode, + .setbits = imxrt_lpspi_setbits, +#ifdef CONFIG_SPI_HWFEATURES + .hwfeatures = imxrt_lpspi_hwfeatures, +#endif + .status = imxrt_lpspi3status, +#ifdef CONFIG_SPI_CMDDATA + .cmddata = imxrt_lpspi3cmddata, +#endif + .send = imxrt_lpspi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = imxrt_lpspi_exchange, +#else + .sndblock = imxrt_lpspi_sndblock, + .recvblock = imxrt_lpspi_recvblock, +#endif +#ifdef CONFIG_SPI_CALLBACK + .registercallback = imxrt_lpspi3register, /* Provided externally */ +#else + .registercallback = 0, /* Not implemented */ +#endif +}; + +static struct imxrt_lpspidev_s g_lpspi3dev = +{ + .spidev = + { + &g_spi3ops + }, + .spibase = IMXRT_LPSPI3_BASE, +#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS + .spiirq = IMXRT_IRQ_LPSPI3, +#endif +#ifdef CONFIG_IMXRT_LPSPI_DMA + .rxch = DMAMAP_LPSPI3_RX, + .txch = DMAMAP_LPSPI3_TX, +#endif +}; +#endif + +#ifdef CONFIG_IMXRT_LPSPI4 +static const struct spi_ops_s g_spi4ops = +{ + .lock = imxrt_lpspi_lock, + .select = imxrt_lpspi4select, + .setfrequency = imxrt_lpspi_setfrequency, + .setmode = imxrt_lpspi_setmode, + .setbits = imxrt_lpspi_setbits, +#ifdef CONFIG_SPI_HWFEATURES + .hwfeatures = imxrt_lpspi_hwfeatures, +#endif + .status = imxrt_lpspi4status, +#ifdef CONFIG_SPI_CMDDATA + .cmddata = imxrt_lpspi4cmddata, +#endif + .send = imxrt_lpspi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = imxrt_lpspi_exchange, +#else + .sndblock = imxrt_lpspi_sndblock, + .recvblock = imxrt_lpspi_recvblock, +#endif +#ifdef CONFIG_SPI_CALLBACK + .registercallback = imxrt_lpspi4register, /* Provided externally */ +#else + .registercallback = 0, /* Not implemented */ +#endif +}; + +static struct imxrt_lpspidev_s g_lpspi4dev = +{ + .spidev = + { + &g_spi4ops + }, + .spibase = IMXRT_LPSPI4_BASE, +#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS + .spiirq = IMXRT_IRQ_LPSPI4, +#endif +#ifdef CONFIG_IMXRT_LPSPI_DMA + .rxch = DMAMAP_LPSPI4_RX, + .txch = DMAMAP_LPSPI4_TX, +#endif +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imxrt_lpspi_getreg8 + * + * Description: + * Get the contents of the SPI register at offset + * + * Input Parameters: + * priv - private SPI device structure + * offset - offset to the register of interest + * + * Returned Value: + * The contents of the 8-bit register + * + ****************************************************************************/ + +static inline uint8_t imxrt_lpspi_getreg8(FAR struct imxrt_lpspidev_s *priv, + uint8_t offset) +{ + return getreg8(priv->spibase + offset); +} + +/**************************************************************************** + * Name: imxrt_lpspi_putreg8 + * + * Description: + * Write a 8-bit value to the SPI register at offset + * + * Input Parameters: + * priv - private SPI device structure + * offset - offset to the register of interest + * value - the 8-bit value to be written + * + ****************************************************************************/ + +static inline void imxrt_lpspi_putreg8(FAR struct imxrt_lpspidev_s *priv, + uint8_t offset, uint8_t value) +{ + putreg8(value, priv->spibase + offset); +} + +/**************************************************************************** + * Name: imxrt_lpspi_getreg + * + * Description: + * Get the contents of the SPI register at offset + * + * Input Parameters: + * priv - private SPI device structure + * offset - offset to the register of interest + * + * Returned Value: + * The contents of the 32-bit register + * + ****************************************************************************/ + +static inline uint32_t +imxrt_lpspi_getreg32(FAR struct imxrt_lpspidev_s *priv, + uint8_t offset) +{ + return getreg32(priv->spibase + offset); +} + +/**************************************************************************** + * Name: imxrt_lpspi_putreg + * + * Description: + * Write a 16-bit value to the SPI register at offset + * + * Input Parameters: + * priv - private SPI device structure + * offset - offset to the register of interest + * value - the 32-bit value to be written + * + * Returned Value: + * The contents of the 32-bit register + * + ****************************************************************************/ + +static inline void imxrt_lpspi_putreg32(FAR struct imxrt_lpspidev_s *priv, + uint8_t offset, uint32_t value) +{ + putreg32(value, priv->spibase + offset); +} + +/**************************************************************************** + * Name: imxrt_lpspi_readword + * + * Description: + * Read one word from SPI + * + * Input Parameters: + * priv - Device-specific state data + * + * Returned Value: + * word as read + * + ****************************************************************************/ + +static inline uint32_t +imxrt_lpspi_readword(FAR struct imxrt_lpspidev_s *priv) +{ + /* Wait until the receive buffer is not empty */ + + while ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_SR_OFFSET) + & LPSPI_SR_RDF) == 0); + + /* Then return the received byte */ + + return imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_RDR_OFFSET); +} + +/**************************************************************************** + * Name: imxrt_lpspi_writeword + * + * Description: + * Write one word to SPI + * + * Input Parameters: + * priv - Device-specific state data + * word - word to send + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void imxrt_lpspi_writeword(FAR struct imxrt_lpspidev_s *priv, + uint16_t word) +{ + /* Wait until the transmit buffer is empty */ + + while ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_SR_OFFSET) + & LPSPI_SR_TDF) == 0); + + /* Then send the word */ + + imxrt_lpspi_putreg32(priv, IMXRT_LPSPI_TDR_OFFSET, word); +} + +/**************************************************************************** + * Name: imxrt_lpspi_readbyte + * + * Description: + * Read one byte from SPI + * + * Input Parameters: + * priv - Device-specific state data + * + * Returned Value: + * Byte as read + * + ****************************************************************************/ + +static inline uint8_t imxrt_lpspi_readbyte(FAR struct imxrt_lpspidev_s *priv) +{ + /* Wait until the receive buffer is not empty */ + + while ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_SR_OFFSET) + & LPSPI_SR_RDF) == 0); + + /* Then return the received byte */ + + return imxrt_lpspi_getreg8(priv, IMXRT_LPSPI_RDR_OFFSET); +} + +/**************************************************************************** + * Name: imxrt_lpspi_writebyte + * + * Description: + * Write one 8-bit frame to the SPI FIFO + * + * Input Parameters: + * priv - Device-specific state data + * byte - Byte to send + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void imxrt_lpspi_writebyte(FAR struct imxrt_lpspidev_s *priv, + uint8_t byte) +{ + /* Wait until the transmit buffer is empty */ + + while ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_SR_OFFSET) + & LPSPI_SR_TDF) == 0); + + /* Then send the byte */ + + imxrt_lpspi_putreg8(priv, IMXRT_LPSPI_TDR_OFFSET, byte); +} + +/**************************************************************************** + * Name: imxrt_lpspi_9to16bitmode + * + * Description: + * Check if the SPI is operating in more then 8 bit mode + * + * Input Parameters: + * priv - Device-specific state data + * + * Returned Value: + * true: >8 bit mode-bit mode, false: <= 8-bit mode + * + ****************************************************************************/ + +static inline bool +imxrt_lpspi_9to16bitmode(FAR struct imxrt_lpspidev_s *priv) +{ + bool ret; + + if (((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_TCR_OFFSET) & + LPSPI_TCR_FRAMESZ_MASK) + 1) < 9) + { + ret = false; + } + else + { + ret = true; + } + + return ret; +} + +/**************************************************************************** + * Name: imxrt_lpspi_modifyreg + * + * Description: + * Clear and set bits in register + * + * Input Parameters: + * priv - Device-specific state data + * offset - Register offset + * clrbits - The bits to clear + * setbits - The bits to set + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void imxrt_lpspi_modifyreg32(FAR struct imxrt_lpspidev_s *priv, + uint8_t offset, uint32_t clrbits, + uint32_t setbits) +{ + modifyreg32(priv->spibase + offset, clrbits, setbits); +} + +/**************************************************************************** + * Name: imxrt_lpspi_master_set_delays + * + * Description: + * SET LPSPI Delay times + * + * Input Parameters: + * priv - Device-specific state data + * scaler - scaler value + * type - delay time type + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void imxrt_lpspi_master_set_delay_scaler( + FAR struct imxrt_lpspidev_s *priv, + uint32_t scaler, + enum imxrt_delay_e type) +{ + switch (type) + { + case LPSPI_PCS_TO_SCK: + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CCR_OFFSET, + LPSPI_CCR_PCSSCK_MASK, 0); + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CCR_OFFSET, 0, + LPSPI_CCR_PCSSCK(scaler)); + break; + + case LPSPI_LAST_SCK_TO_PCS: + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CCR_OFFSET, + LPSPI_CCR_SCKPCS_MASK, 0); + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CCR_OFFSET, 0, + LPSPI_CCR_SCKPCS(scaler)); + break; + + case LPSPI_BETWEEN_TRANSFER: + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CCR_OFFSET, + LPSPI_CCR_DBT_MASK, 0); + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CCR_OFFSET, 0, + LPSPI_CCR_DBT(scaler)); + break; + } +} + +/**************************************************************************** + * Name: imxrt_lpspi_master_set_delays + * + * Description: + * SET LPSPI Delay times + * + * Input Parameters: + * priv - Device-specific state data + * delay_ns - delay time in nano seconds + * type - delay time type + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void imxrt_lpspi_master_set_delays( + FAR struct imxrt_lpspidev_s *priv, + uint32_t delay_ns, + enum imxrt_delay_e type) +{ + uint32_t pll3_div; + uint32_t pll_freq; + uint32_t src_freq; + uint64_t real_delay; + uint64_t best_delay; + uint32_t scaler; + uint32_t best_scaler; + uint32_t diff; + uint32_t min_diff; + uint64_t initial_delay_ns; + uint32_t clock_div_prescaler; + uint32_t additional_scaler; + + if ((getreg32(IMXRT_CCM_ANALOG_PLL_USB2) & + CCM_ANALOG_PLL_USB2_DIV_SELECT_MASK) != 0) + { + pll3_div = 22; + } + else + { + pll3_div = 20; + } + + pll_freq = BOARD_XTAL_FREQUENCY * pll3_div; + + /* Assumption this formula will work only if the LPSPI Clock Source is PLL3 + * PFD0 so check if LPSPI clock source is set to 1 (PLL3 PFD0) in CCM_CBCMR + * register bits 4-5 + */ + + src_freq = pll_freq / + ((getreg32(IMXRT_CCM_ANALOG_PFD_480) + & CCM_ANALOG_PFD_480_PFD0_FRAC_MASK) >> + CCM_ANALOG_PFD_480_PFD0_FRAC_SHIFT); + src_freq *= 18; + src_freq /= ((getreg32(IMXRT_CCM_CBCMR) & CCM_CBCMR_LPSPI_PODF_MASK) >> + CCM_CBCMR_LPSPI_PODF_SHIFT) + 1; + + clock_div_prescaler = src_freq / + (1 << ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_TCR_OFFSET) & + LPSPI_TCR_PRESCALE_MASK) >> LPSPI_TCR_PRESCALE_SHIFT)); + + min_diff = 0xffffffff; + + /* Initialize scaler to max value to generate the max delay */ + + best_scaler = 0xff; + + if (type == LPSPI_BETWEEN_TRANSFER) + { + /* First calculate the initial, default delay, note min delay is 2 + * clock cycles. Due to large size of * calculated values (uint64_t), + * we need to break up the calculation into several steps to ensure + * accurate calculated results + */ + + initial_delay_ns = 1000000000U; + initial_delay_ns *= 2; + initial_delay_ns /= clock_div_prescaler; + + /* Calculate the maximum delay */ + + best_delay = 1000000000U; + + /* based on DBT+2, or 255 + 2 */ + + best_delay *= 257; + best_delay /= clock_div_prescaler; + + additional_scaler = 1U; + } + else + { + /* First calculate the initial, default delay, min delay is 1 clock + * cycle. Due to large size of calculated values (uint64_t), we need to + * break up the calculation into several steps to ensure accurate + * calculated * results. + */ + + initial_delay_ns = 1000000000U; + initial_delay_ns /= clock_div_prescaler; + + /* Calculate the maximum delay */ + + best_delay = 1000000000U; + + /* Based on SCKPCS+1 or PCSSCK+1, or 255 + 1 */ + + best_delay *= 256; + best_delay /= clock_div_prescaler; + + additional_scaler = 0; + } + + /* If the initial, default delay is already greater than the desired delay, + * then * set the delay to their initial value (0) and return the delay. In + * other words, * there is no way to decrease the delay value further. + */ + + if (initial_delay_ns >= delay_ns) + { + imxrt_lpspi_master_set_delay_scaler(priv, 0, type); + } + else + { + /* If min_diff = 0, the exit for loop */ + + for (scaler = 0; (scaler < 256) && min_diff; scaler++) + { + /* Calculate the real delay value as we cycle through the scaler + * values. Due to large size of calculated values (uint64_t), + * we need to break up the calculation into several steps to + * ensure accurate calculated results + */ + + real_delay = 1000000000U; + real_delay *= (scaler + 1 + additional_scaler); + real_delay /= clock_div_prescaler; + + /* calculate the delay difference based on the conditional + * statement that states that the calculated delay must not be + * less then the desired delay + */ + + if (real_delay >= delay_ns) + { + diff = real_delay - delay_ns; + if (min_diff > diff) + { + /* A better match found */ + + min_diff = diff; + best_scaler = scaler; + best_delay = real_delay; + } + } + } + + imxrt_lpspi_master_set_delay_scaler(priv, best_scaler, type); + } +} + +/**************************************************************************** + * Name: imxrt_lpspi_lock + * + * Description: + * On SPI buses where there are multiple devices, it will be necessary to + * lock SPI to have exclusive access to the buses for a sequence of + * transfers. The bus should be locked before the chip is selected. After + * locking the SPI bus, the caller should then also call the setfrequency, + * setbits, and setmode methods to make sure that the SPI is properly + * configured for the device. If the SPI bus is being shared, then it + * may have been left in an incompatible state. + * + * Input Parameters: + * dev - Device-specific state data + * lock - true: Lock spi bus, false: unlock SPI bus + * + * Returned Value: + * None + * + ****************************************************************************/ + +static int imxrt_lpspi_lock(FAR struct spi_dev_s *dev, bool lock) +{ + FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)dev; + int ret; + + if (lock) + { + ret = nxsem_wait_uninterruptible(&priv->exclsem); + } + else + { + ret = nxsem_post(&priv->exclsem); + } + + return ret; +} + +/**************************************************************************** + * Name: imxrt_lpspi_setfrequency + * + * Description: + * Set the SPI frequency. + * + * Input Parameters: + * dev - Device-specific state data + * frequency - The SPI frequency requested + * + * Returned Value: + * Returns the actual frequency selected + * + ****************************************************************************/ + +static uint32_t imxrt_lpspi_setfrequency(FAR struct spi_dev_s *dev, + uint32_t frequency) +{ + FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)dev; + + uint32_t men; + uint32_t pll_freq; + uint32_t pll3_div; + uint32_t src_freq = 0; + uint32_t prescaler; + uint32_t best_prescaler; + uint32_t scaler; + uint32_t best_scaler; + uint32_t real_frequency; + uint32_t best_frequency; + uint32_t diff; + uint32_t min_diff; + + /* Has the LPSPI bus frequency changed? */ + + if (frequency != priv->frequency) + { + /* Disable LPSPI if it is enabled */ + + men = imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CR_OFFSET) & LPSPI_CR_MEN; + if (men) + { + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, + LPSPI_CR_MEN, 0); + } + + if ((getreg32(IMXRT_CCM_ANALOG_PLL_USB2) & + CCM_ANALOG_PLL_USB2_DIV_SELECT_MASK) != 0) + { + pll3_div = 22; + } + else + { + pll3_div = 20; + } + + pll_freq = BOARD_XTAL_FREQUENCY * pll3_div; + + /* Assumption this formula will work only if the LPSPI Clock Source is + * PLL3 PFD0 * so check if LPSPI clock source is set to 1 (PLL3 PFD0) + * in CCM_CBCMR register bits 4-5 + */ + + src_freq = pll_freq / + ((getreg32(IMXRT_CCM_ANALOG_PFD_480) + & CCM_ANALOG_PFD_480_PFD0_FRAC_MASK) >> + CCM_ANALOG_PFD_480_PFD0_FRAC_SHIFT); + src_freq *= 18; + src_freq /= ((getreg32(IMXRT_CCM_CBCMR) & CCM_CBCMR_LPSPI_PODF_MASK) >> + CCM_CBCMR_LPSPI_PODF_SHIFT) + 1; + + min_diff = 0xffffffff; + + best_prescaler = 7; + best_scaler = 255; + + best_frequency = 0; + + for (prescaler = 0; (prescaler < 8) && min_diff; prescaler++) + { + for (scaler = 0; (scaler < 256) && min_diff; scaler++) + { + real_frequency = src_freq / ((1 << prescaler) * (scaler + 2)); + + /* Calculate the frequency difference based on conditional + * statement that states that the calculated frequency must not + * exceed desired frequency. + */ + + if (frequency >= real_frequency) + { + diff = frequency - real_frequency; + if (min_diff > diff) + { + /* A better match found */ + + min_diff = diff; + best_prescaler = prescaler; + best_scaler = scaler; + best_frequency = real_frequency; + } + } + } + } + + /* Write the best values in the CCR register */ + + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CCR_OFFSET, + LPSPI_CCR_SCKDIV_MASK, + LPSPI_CCR_SCKDIV(best_scaler)); + + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_TCR_OFFSET, + LPSPI_TCR_PRESCALE_MASK, + LPSPI_TCR_PRESCALE(best_prescaler)); + + priv->frequency = frequency; + priv->actual = best_frequency; + + imxrt_lpspi_master_set_delays(priv, 1000000000 / best_frequency, + LPSPI_PCS_TO_SCK); + imxrt_lpspi_master_set_delays(priv, 1000000000 / best_frequency, + LPSPI_LAST_SCK_TO_PCS); + imxrt_lpspi_master_set_delays(priv, 1000000000 / best_frequency, + LPSPI_BETWEEN_TRANSFER); + + /* Re-enable LPSPI if it was enabled previously */ + + if (men) + { + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0, + LPSPI_CR_MEN); + } + } + + return priv->actual; +} + +/**************************************************************************** + * Name: imxrt_lpspi_setmode + * + * Description: + * Set the SPI mode. see enum spi_mode_e mode for mode definitions + * + * Input Parameters: + * dev - Device-specific state data + * mode - The SPI mode requested + * + * Returned Value: + * Returns the actual frequency selected + * + ****************************************************************************/ + +static void imxrt_lpspi_setmode(FAR struct spi_dev_s *dev, + enum spi_mode_e mode) +{ + FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)dev; + uint32_t setbits; + uint32_t clrbits; + uint32_t men; + + spiinfo("mode=%d\n", mode); + + /* Has the mode changed? */ + + if (mode != priv->mode) + { + /* Disable LPSPI if it is enabled */ + + men = imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CR_OFFSET) & LPSPI_CR_MEN; + if (men) + { + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, + LPSPI_CR_MEN, 0); + } + + switch (mode) + { + case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */ + setbits = 0; + clrbits = LPSPI_TCR_CPOL | LPSPI_TCR_CPHA; + break; + + case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */ + setbits = LPSPI_TCR_CPHA; + clrbits = LPSPI_TCR_CPOL; + break; + + case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */ + setbits = LPSPI_TCR_CPOL; + clrbits = LPSPI_TCR_CPHA; + break; + + case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */ + setbits = LPSPI_TCR_CPOL | LPSPI_TCR_CPHA; + clrbits = 0; + break; + + default: + return; + } + + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_TCR_OFFSET, + clrbits, setbits); + + while ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_RSR_OFFSET) & + LPSPI_RSR_RXEMPTY) != LPSPI_RSR_RXEMPTY) + { + /* Flush SPI read FIFO */ + + imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_RSR_OFFSET); + } + + /* Save the mode so that subsequent re-configurations will be faster */ + + priv->mode = mode; + + /* Re-enable LPSPI if it was enabled previously */ + + if (men) + { + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0, + LPSPI_CR_MEN); + } + } +} + +/**************************************************************************** + * Name: imxrt_lpspi_setbits + * + * Description: + * Set the number of bits per word. + * + * Input Parameters: + * dev - Device-specific state data + * nbits - The number of bits requested + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void imxrt_lpspi_setbits(FAR struct spi_dev_s *dev, int nbits) +{ + FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)dev; + uint32_t men; + + spiinfo("nbits=%d\n", nbits); + + /* Has the number of bits changed? */ + + if (nbits != priv->nbits) + { + if (nbits < 2 || nbits > 4096) + { + return; + } + + /* Disable LPSPI if it is enabled */ + + men = imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CR_OFFSET) & LPSPI_CR_MEN; + if (men) + { + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, + LPSPI_CR_MEN, 0); + } + + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_TCR_OFFSET, + LPSPI_TCR_FRAMESZ_MASK, + LPSPI_TCR_FRAMESZ(nbits - 1)); + + /* Save the selection so the subsequent re-configurations + * will be faster. + */ + + priv->nbits = nbits; + + /* Re-enable LPSPI if it was enabled previously */ + + if (men) + { + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0, + LPSPI_CR_MEN); + } + } +} + +/**************************************************************************** + * Name: imxrt_lpspi_hwfeatures + * + * Description: + * Set hardware-specific feature flags. + * + * Input Parameters: + * dev - Device-specific state data + * features - H/W feature flags + * + * Returned Value: + * Zero (OK) if the selected H/W features are enabled; A negated errno + * value if any H/W feature is not supportable. + * + ****************************************************************************/ + +#ifdef CONFIG_SPI_HWFEATURES +static int imxrt_lpspi_hwfeatures(FAR struct spi_dev_s *dev, + imxrt_lpspi_hwfeatures_t features) +{ +#ifdef CONFIG_SPI_BITORDER + FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)dev; + uint32_t setbits; + uint32_t clrbits; + + spiinfo("features=%08x\n", features); + + /* Transfer data LSB first? */ + + if ((features & HWFEAT_LSBFIRST) != 0) + { + setbits = LPSPI_TCR_LSBF; + clrbits = 0; + } + else + { + setbits = 0; + clrbits = LPSPI_TCR_LSBF; + } + + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_TCR_OFFSET, clrbits, setbits); + + /* Other H/W features are not supported */ + + return ((features & ~HWFEAT_LSBFIRST) == 0) ? OK : -ENOSYS; +#else + return -ENOSYS; +#endif +} +#endif + +/**************************************************************************** + * Name: imxrt_lpspi_send + * + * Description: + * Exchange one word on SPI + * + * Input Parameters: + * dev - Device-specific state data + * wd - The word to send. the size of the data is determined by the + * number of bits selected for the SPI interface. + * + * Returned Value: + * response + * + ****************************************************************************/ + +static uint32_t imxrt_lpspi_send(FAR struct spi_dev_s *dev, uint32_t wd) +{ + FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)dev; + uint32_t regval; + uint32_t ret; + + DEBUGASSERT(priv && priv->spibase); + + imxrt_lpspi_writeword(priv, wd); + + while ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_SR_OFFSET) & + LPSPI_SR_RDF) != LPSPI_SR_RDF); + + ret = imxrt_lpspi_readword(priv); + + /* Check and clear any error flags (Reading from the SR clears the error + * flags). + */ + + regval = imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_SR_OFFSET); + + spiinfo( + "Sent: %04" PRIx32 " Return: %04" PRIx32 " Status: %02" PRIx32 "\n", + wd, ret, regval); + + UNUSED(regval); + return ret; +} + +/**************************************************************************** + * Name: imxrt_lpspi_exchange (no DMA). aka imxrt_lpspi_exchange_nodma + * + * Description: + * Exchange a block of data on SPI without using DMA + * + * Input Parameters: + * dev - Device-specific state data + * txbuffer - A pointer to the buffer of data to be sent + * rxbuffer - A pointer to a buffer in which to receive data + * nwords - the length of data to be exchanged in units of words. + * The wordsize is determined by the number of bits-per-word + * selected for the SPI interface. If nbits <= 8, the data is + * packed into uint8_t's; if nbits >8, the data is packed + * into uint16_t's + * + * Returned Value: + * None + * + ****************************************************************************/ + +#if !defined(CONFIG_IMXRT_LPSPI_DMA) || defined(CONFIG_IMXRT_DMACAPABLE) +#if !defined(CONFIG_IMXRT_LPSPI_DMA) +static void imxrt_lpspi_exchange(FAR struct spi_dev_s *dev, + FAR const void *txbuffer, + FAR void *rxbuffer, + size_t nwords) +#else +static void imxrt_lpspi_exchange_nodma(FAR struct spi_dev_s *dev, + FAR const void *txbuffer, + FAR void *rxbuffer, size_t nwords) +#endif +{ + FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)dev; + DEBUGASSERT(priv && priv->spibase); + + spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords); + + /* 8- or 16-bit mode? */ + + if (imxrt_lpspi_9to16bitmode(priv)) + { + /* 16-bit mode */ + + const uint16_t *src = (const uint16_t *)txbuffer; + uint16_t *dest = (uint16_t *) rxbuffer; + uint16_t word; + + while (nwords-- > 0) + { + /* Get the next word to write. Is there a source buffer? */ + + if (src) + { + word = *src++; + } + else + { + word = 0xffff; + } + + /* Exchange one word */ + + word = (uint16_t) imxrt_lpspi_send(dev, (uint32_t) word); + + /* Is there a buffer to receive the return value? */ + + if (dest) + { + *dest++ = word; + } + } + } + else + { + /* 8-bit mode */ + + const uint8_t *src = (const uint8_t *)txbuffer; + uint8_t *dest = (uint8_t *) rxbuffer; + uint8_t word; + + while (nwords-- > 0) + { + /* Get the next word to write. Is there a source buffer? */ + + if (src) + { + word = *src++; + } + else + { + word = 0xff; + } + + /* Exchange one word */ + + word = (uint8_t) imxrt_lpspi_send(dev, (uint32_t) word); + + /* Is there a buffer to receive the return value? */ + + if (dest) + { + *dest++ = word; + } + } + } +} +#endif /* !CONFIG_IMXRT_LPSPI_DMA || CONFIG_IMXRT_DMACAPABLE */ + +/**************************************************************************** + * Name: imxrt_lpspi_sndblock + * + * Description: + * Send a block of data on SPI + * + * Input Parameters: + * dev - Device-specific state data + * txbuffer - A pointer to the buffer of data to be sent + * nwords - the length of data to send from the buffer in number of + * words. The wordsize is determined by the number of + * bits-per-word selected for the SPI interface. If nbits <= 8, + * the data is packed into uint8_t's; if nbits >8, the data is + * packed into uint16_t's + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifndef CONFIG_SPI_EXCHANGE +static void imxrt_lpspi_sndblock(FAR struct spi_dev_s *dev, + FAR const void *txbuffer, size_t nwords) +{ + spiinfo("txbuffer=%p nwords=%d\n", txbuffer, nwords); + return imxrt_lpspi_exchange(dev, txbuffer, NULL, nwords); +} +#endif + +/**************************************************************************** + * Name: imxrt_lpspi_recvblock + * + * Description: + * Receive a block of data from SPI + * + * Input Parameters: + * dev - Device-specific state data + * rxbuffer - A pointer to the buffer in which to receive data + * nwords - the length of data that can be received in the buffer in + * number of words. The wordsize is determined by the number of + * bits-per-word selected for the SPI interface. If nbits <= 8, + * the data is packed into uint8_t's; if nbits >8, the data is + * packed into uint16_t's + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifndef CONFIG_SPI_EXCHANGE +static void imxrt_lpspi_recvblock(FAR struct spi_dev_s *dev, + FAR void *rxbuffer, size_t nwords) +{ + spiinfo("rxbuffer=%p nwords=%d\n", rxbuffer, nwords); + return imxrt_lpspi_exchange(dev, NULL, rxbuffer, nwords); +} +#endif + +/**************************************************************************** + * Name: imxrt_lpspi_clock_enable + * + * Description: + * Ungate LPSPI clock + * + ****************************************************************************/ + +void imxrt_lpspi_clock_enable(uint32_t base) +{ + if (base == IMXRT_LPSPI1_BASE) + { + imxrt_clockall_lpspi1(); + } + else if (base == IMXRT_LPSPI2_BASE) + { + imxrt_clockall_lpspi2(); + } + else if (base == IMXRT_LPSPI3_BASE) + { + imxrt_clockall_lpspi3(); + } + else if (base == IMXRT_LPSPI4_BASE) + { + imxrt_clockall_lpspi4(); + } +} + +/**************************************************************************** + * Name: imxrt_lpspi_clock_disable + * + * Description: + * Gate LPSPI clock + * + ****************************************************************************/ + +void imxrt_lpspi_clock_disable(uint32_t base) +{ + if (base == IMXRT_LPSPI1_BASE) + { + imxrt_clockoff_lpspi1(); + } + else if (base == IMXRT_LPSPI2_BASE) + { + imxrt_clockoff_lpspi2(); + } + else if (base == IMXRT_LPSPI3_BASE) + { + imxrt_clockoff_lpspi3(); + } + else if (base == IMXRT_LPSPI4_BASE) + { + imxrt_clockoff_lpspi4(); + } +} + +/**************************************************************************** + * Name: imxrt_lpspi_bus_initialize + * + * Description: + * Initialize the selected SPI bus in its default state + * (Master, 8-bit, mode 0, etc.) + * + * Input Parameters: + * priv - private SPI device structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void imxrt_lpspi_bus_initialize(struct imxrt_lpspidev_s *priv) +{ + uint32_t reg = 0; + + /* Enable power and reset the peripheral */ + + imxrt_lpspi_clock_enable(priv->spibase); + + /* Reset to known status */ + + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0, LPSPI_CR_RST); + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0, + LPSPI_CR_RTF | LPSPI_CR_RRF); + imxrt_lpspi_putreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0x00); + + /* Set LPSPI to master */ + + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CFGR1_OFFSET, 0, + LPSPI_CFGR1_MASTER); + + /* Set specific PCS to active high or low + * TODO: Not needed for now + */ + + /* Set Configuration Register 1 related setting. */ + + reg = imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CFGR1_OFFSET); + reg &= ~(LPSPI_CFGR1_OUTCFG | LPSPI_CFGR1_PINCFG_MASK + | LPSPI_CFGR1_NOSTALL); + reg |= LPSPI_CFGR1_OUTCFG_RETAIN | LPSPI_CFGR1_PINCFG_SIN_SOUT; + imxrt_lpspi_putreg32(priv, IMXRT_LPSPI_CFGR1_OFFSET, reg); + + /* Set frequency and delay times */ + + imxrt_lpspi_setfrequency((FAR struct spi_dev_s *)priv, 400000); + + /* Set default watermarks */ + + imxrt_lpspi_putreg32(priv, IMXRT_LPSPI_FCR_OFFSET, + LPSPI_FCR_TXWATER(0) | LPSPI_FCR_RXWATER(0)); + + /* Set Transmit Command Register */ + + imxrt_lpspi_setbits((FAR struct spi_dev_s *)priv, 8); + + imxrt_lpspi_setmode((FAR struct spi_dev_s *)priv, SPIDEV_MODE0); + + /* Initialize the SPI semaphore that enforces mutually exclusive access */ + + nxsem_init(&priv->exclsem, 0, 1); + + /* Enable LPSPI */ + + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0, LPSPI_CR_MEN); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imxrt_lpspibus_initialize + * + * Description: + * Initialize the selected SPI bus + * + * Input Parameters: + * Port number (for hardware that has multiple SPI interfaces) + * + * Returned Value: + * Valid SPI device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +FAR struct spi_dev_s *imxrt_lpspibus_initialize(int bus) +{ + FAR struct imxrt_lpspidev_s *priv = NULL; + + irqstate_t flags = enter_critical_section(); + +#ifdef CONFIG_IMXRT_LPSPI1 + if (bus == 1) + { + /* Select SPI1 */ + + priv = &g_lpspi1dev; + + /* Only configure if the bus is not already configured */ + + if ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CR_OFFSET) + & LPSPI_CR_MEN) == 0) + { + /* Configure SPI1 pins: SCK, MISO, and MOSI */ + + imxrt_config_gpio(GPIO_LPSPI1_SCK); + imxrt_config_gpio(GPIO_LPSPI1_MISO); + imxrt_config_gpio(GPIO_LPSPI1_MOSI); +#ifdef GPIO_LPSPI1_CS + imxrt_config_gpio(GPIO_LPSPI1_CS); +#endif +#if defined(GPIO_LPSPI1_DC) && defined(CONFIG_SPI_CMDDATA) + imxrt_config_gpio(GPIO_LPSPI1_DC); +#endif + + /* Set up default configuration: Master, 8-bit, etc. */ + + imxrt_lpspi_bus_initialize(priv); + } + } + else +#endif +#ifdef CONFIG_IMXRT_LPSPI2 + if (bus == 2) + { + /* Select SPI2 */ + + priv = &g_lpspi2dev; + + /* Only configure if the bus is not already configured */ + + if ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CR_OFFSET) + & LPSPI_CR_MEN) == 0) + { + /* Configure SPI2 pins: SCK, MISO, and MOSI */ + + imxrt_config_gpio(GPIO_LPSPI2_SCK); + imxrt_config_gpio(GPIO_LPSPI2_MISO); + imxrt_config_gpio(GPIO_LPSPI2_MOSI); +#ifdef GPIO_LPSPI2_CS + imxrt_config_gpio(GPIO_LPSPI2_CS); +#endif +#if defined(GPIO_LPSPI2_DC) && defined(CONFIG_SPI_CMDDATA) + imxrt_config_gpio(GPIO_LPSPI2_DC); +#endif + + /* Set up default configuration: Master, 8-bit, etc. */ + + imxrt_lpspi_bus_initialize(priv); + } + } + else +#endif +#ifdef CONFIG_IMXRT_LPSPI3 + if (bus == 3) + { + /* Select SPI3 */ + + priv = &g_lpspi3dev; + + /* Only configure if the bus is not already configured */ + + if ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CR_OFFSET) + & LPSPI_CR_MEN) == 0) + { + /* Configure SPI3 pins: SCK, MISO, and MOSI */ + + imxrt_config_gpio(GPIO_LPSPI3_SCK); + imxrt_config_gpio(GPIO_LPSPI3_MISO); + imxrt_config_gpio(GPIO_LPSPI3_MOSI); +#ifdef GPIO_LPSPI3_CS + imxrt_config_gpio(GPIO_LPSPI3_CS); +#endif +#if defined(GPIO_LPSPI3_DC) && defined(CONFIG_SPI_CMDDATA) + imxrt_config_gpio(GPIO_LPSPI3_DC); +#endif + + /* Set up default configuration: Master, 8-bit, etc. */ + + imxrt_lpspi_bus_initialize(priv); + } + } + else +#endif +#ifdef CONFIG_IMXRT_LPSPI4 + if (bus == 4) + { + /* Select SPI4 */ + + priv = &g_lpspi4dev; + + /* Only configure if the bus is not already configured */ + + if ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CR_OFFSET) + & LPSPI_CR_MEN) == 0) + { + /* Configure SPI4 pins: SCK, MISO, and MOSI */ + + imxrt_config_gpio(GPIO_LPSPI4_SCK); + imxrt_config_gpio(GPIO_LPSPI4_MISO); + imxrt_config_gpio(GPIO_LPSPI4_MOSI); +#ifdef GPIO_LPSPI4_CS + imxrt_config_gpio(GPIO_LPSPI4_CS); +#endif +#if defined(GPIO_LPSPI4_DC) && defined(CONFIG_SPI_CMDDATA) + imxrt_config_gpio(GPIO_LPSPI4_DC); +#endif + + /* Set up default configuration: Master, 8-bit, etc. */ + + imxrt_lpspi_bus_initialize(priv); + } + } + else +#endif + { + spierr("ERROR: Unsupported SPI bus: %d\n", bus); + return NULL; + } + + leave_critical_section(flags); + + return (FAR struct spi_dev_s *)priv; +} + +#endif /* CONFIG_IMXRT_LPSPI1 */ diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_usbdev.c b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_usbdev.c new file mode 100644 index 000000000..3f0d6ff03 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_usbdev.c @@ -0,0 +1,3061 @@ +/**************************************************************************** + * arch/arm/src/imxrt/imxrt_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 "chip.h" +#include "arm_arch.h" +#include "arm_internal.h" + +#include "hardware/imxrt_usbotg.h" +#include "hardware/imxrt_usbphy.h" +#include "hardware/rt106x/imxrt106x_ccm.h" +#include "imxrt_periphclks.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_IMXRT_USBDEV_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 IMXRT_TRACEERR_ALLOCFAIL 0x0001 +#define IMXRT_TRACEERR_BADCLEARFEATURE 0x0002 +#define IMXRT_TRACEERR_BADDEVGETSTATUS 0x0003 +#define IMXRT_TRACEERR_BADEPNO 0x0004 +#define IMXRT_TRACEERR_BADEPGETSTATUS 0x0005 +#define IMXRT_TRACEERR_BADEPTYPE 0x0006 +#define IMXRT_TRACEERR_BADGETCONFIG 0x0007 +#define IMXRT_TRACEERR_BADGETSETDESC 0x0008 +#define IMXRT_TRACEERR_BADGETSTATUS 0x0009 +#define IMXRT_TRACEERR_BADSETADDRESS 0x000a +#define IMXRT_TRACEERR_BADSETCONFIG 0x000b +#define IMXRT_TRACEERR_BADSETFEATURE 0x000c +#define IMXRT_TRACEERR_BINDFAILED 0x000d +#define IMXRT_TRACEERR_DISPATCHSTALL 0x000e +#define IMXRT_TRACEERR_DRIVER 0x000f +#define IMXRT_TRACEERR_DRIVERREGISTERED 0x0010 +#define IMXRT_TRACEERR_EP0SETUPSTALLED 0x0011 +#define IMXRT_TRACEERR_EPINNULLPACKET 0x0012 +#define IMXRT_TRACEERR_EPOUTNULLPACKET 0x0013 +#define IMXRT_TRACEERR_INVALIDCTRLREQ 0x0014 +#define IMXRT_TRACEERR_INVALIDPARMS 0x0015 +#define IMXRT_TRACEERR_IRQREGISTRATION 0x0016 +#define IMXRT_TRACEERR_NOEP 0x0017 +#define IMXRT_TRACEERR_NOTCONFIGURED 0x0018 +#define IMXRT_TRACEERR_REQABORTED 0x0019 + +/* Trace interrupt codes */ + +#define IMXRT_TRACEINTID_USB 0x0001 +#define IMXRT_TRACEINTID_CLEARFEATURE 0x0002 +#define IMXRT_TRACEINTID_DEVGETSTATUS 0x0003 +#define IMXRT_TRACEINTID_DEVRESET 0x0004 +#define IMXRT_TRACEINTID_DISPATCH 0x0005 +#define IMXRT_TRACEINTID_EP0COMPLETE 0x0006 +#define IMXRT_TRACEINTID_EP0NAK 0x0007 +#define IMXRT_TRACEINTID_EP0SETUP 0x0008 +#define IMXRT_TRACEINTID_EPGETSTATUS 0x0009 +#define IMXRT_TRACEINTID_EPIN 0x000a +#define IMXRT_TRACEINTID_EPINQEMPTY 0x000b +#define IMXRT_TRACEINTID_EP0INSETADDRESS 0x000c +#define IMXRT_TRACEINTID_EPOUT 0x000d +#define IMXRT_TRACEINTID_EPOUTQEMPTY 0x000e +#define IMXRT_TRACEINTID_EP0SETUPSETADDRESS 0x000f +#define IMXRT_TRACEINTID_FRAME 0x0010 +#define IMXRT_TRACEINTID_GETCONFIG 0x0011 +#define IMXRT_TRACEINTID_GETSETDESC 0x0012 +#define IMXRT_TRACEINTID_GETSETIF 0x0013 +#define IMXRT_TRACEINTID_GETSTATUS 0x0014 +#define IMXRT_TRACEINTID_IFGETSTATUS 0x0015 +#define IMXRT_TRACEINTID_SETCONFIG 0x0016 +#define IMXRT_TRACEINTID_SETFEATURE 0x0017 +#define IMXRT_TRACEINTID_SUSPENDED 0x0018 +#define IMXRT_TRACEINTID_RESUMED 0x0019 +#define IMXRT_TRACEINTID_SYNCHFRAME 0x001a + +#ifdef CONFIG_USBDEV_TRACE_STRINGS +const struct trace_msg_t g_usb_trace_strings_deverror[] = +{ + TRACE_STR(IMXRT_TRACEERR_ALLOCFAIL), + TRACE_STR(IMXRT_TRACEERR_BADCLEARFEATURE), + TRACE_STR(IMXRT_TRACEERR_BADDEVGETSTATUS), + TRACE_STR(IMXRT_TRACEERR_BADEPNO), + TRACE_STR(IMXRT_TRACEERR_BADEPGETSTATUS), + TRACE_STR(IMXRT_TRACEERR_BADEPTYPE), + TRACE_STR(IMXRT_TRACEERR_BADGETCONFIG), + TRACE_STR(IMXRT_TRACEERR_BADGETSETDESC), + TRACE_STR(IMXRT_TRACEERR_BADGETSTATUS), + TRACE_STR(IMXRT_TRACEERR_BADSETADDRESS), + TRACE_STR(IMXRT_TRACEERR_BADSETCONFIG), + TRACE_STR(IMXRT_TRACEERR_BADSETFEATURE), + TRACE_STR(IMXRT_TRACEERR_BINDFAILED), + TRACE_STR(IMXRT_TRACEERR_DISPATCHSTALL), + TRACE_STR(IMXRT_TRACEERR_DRIVER), + TRACE_STR(IMXRT_TRACEERR_DRIVERREGISTERED), + TRACE_STR(IMXRT_TRACEERR_EP0SETUPSTALLED), + TRACE_STR(IMXRT_TRACEERR_EPINNULLPACKET), + TRACE_STR(IMXRT_TRACEERR_EPOUTNULLPACKET), + TRACE_STR(IMXRT_TRACEERR_INVALIDCTRLREQ), + TRACE_STR(IMXRT_TRACEERR_INVALIDPARMS), + TRACE_STR(IMXRT_TRACEERR_IRQREGISTRATION), + TRACE_STR(IMXRT_TRACEERR_NOEP), + TRACE_STR(IMXRT_TRACEERR_NOTCONFIGURED), + TRACE_STR(IMXRT_TRACEERR_REQABORTED), + TRACE_STR_END +}; + +const struct trace_msg_t g_usb_trace_strings_intdecode[] = +{ + TRACE_STR(IMXRT_TRACEINTID_USB), + TRACE_STR(IMXRT_TRACEINTID_CLEARFEATURE), + TRACE_STR(IMXRT_TRACEINTID_DEVGETSTATUS), + TRACE_STR(IMXRT_TRACEINTID_DEVRESET), + TRACE_STR(IMXRT_TRACEINTID_DISPATCH), + TRACE_STR(IMXRT_TRACEINTID_EP0COMPLETE), + TRACE_STR(IMXRT_TRACEINTID_EP0NAK), + TRACE_STR(IMXRT_TRACEINTID_EP0SETUP), + TRACE_STR(IMXRT_TRACEINTID_EPGETSTATUS), + TRACE_STR(IMXRT_TRACEINTID_EPIN), + TRACE_STR(IMXRT_TRACEINTID_EPINQEMPTY), + TRACE_STR(IMXRT_TRACEINTID_EP0INSETADDRESS), + TRACE_STR(IMXRT_TRACEINTID_EPOUT), + TRACE_STR(IMXRT_TRACEINTID_EPOUTQEMPTY), + TRACE_STR(IMXRT_TRACEINTID_EP0SETUPSETADDRESS), + TRACE_STR(IMXRT_TRACEINTID_FRAME), + TRACE_STR(IMXRT_TRACEINTID_GETCONFIG), + TRACE_STR(IMXRT_TRACEINTID_GETSETDESC), + TRACE_STR(IMXRT_TRACEINTID_GETSETIF), + TRACE_STR(IMXRT_TRACEINTID_GETSTATUS), + TRACE_STR(IMXRT_TRACEINTID_IFGETSTATUS), + TRACE_STR(IMXRT_TRACEINTID_SETCONFIG), + TRACE_STR(IMXRT_TRACEINTID_SETFEATURE), + TRACE_STR(IMXRT_TRACEINTID_SUSPENDED), + TRACE_STR(IMXRT_TRACEINTID_RESUMED), + TRACE_STR(IMXRT_TRACEINTID_SYNCHFRAME), + TRACE_STR_END +}; +#endif + +/* Hardware interface *******************************************************/ + +/* This represents a Endpoint Transfer Descriptor - note these must be 32 + * byte aligned. + */ + +struct imxrt_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 - not these must be aligned to a 2048 byte + * boundary + */ + +struct imxrt_dqh_s +{ + uint32_t capability; /* Endpoint capability */ + uint32_t currdesc; /* Current dTD pointer */ + struct imxrt_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 IMXRT_NLOGENDPOINTS (8) /* ep0-7 */ +#define IMXRT_NPHYSENDPOINTS (16) /* x2 for IN and OUT */ + +/* Odd physical endpoint numbers are IN; even are OUT */ + +#define IMXRT_EPPHYIN(epphy) (((epphy) & 1) != 0) +#define IMXRT_EPPHYOUT(epphy) (((epphy) & 1) == 0) + +#define IMXRT_EPPHYIN2LOG(epphy) (((uint8_t)(epphy) >> 1) |USB_DIR_IN) +#define IMXRT_EPPHYOUT2LOG(epphy) (((uint8_t)(epphy) >> 1) | USB_DIR_OUT) + +/* Endpoint 0 is special... */ + +#define IMXRT_EP0_OUT (0) +#define IMXRT_EP0_IN (1) + +/* Each endpoint has somewhat different characteristics */ + +#define IMXRT_EPALLSET (0xffff) /* All endpoints */ +#define IMXRT_EPOUTSET (0x5555) /* Even phy endpoint numbers are OUT EPs */ +#define IMXRT_EPINSET (0xaaaa) /* Odd endpoint numbers are IN EPs */ +#define IMXRT_EPCTRLSET (0x0003) /* EP0 IN/OUT are control endpoints */ +#define IMXRT_EPINTRSET (0xfffc) /* Interrupt endpoints */ +#define IMXRT_EPBULKSET (0xfffc) /* Bulk endpoints */ +#define IMXRT_EPISOCSET (0xfffc) /* Isochronous endpoints */ + +/* Maximum packet sizes for endpoints */ + +#define IMXRT_EP0MAXPACKET (64) /* EP0 max packet size (1-64) */ +#define IMXRT_BULKMAXPACKET (512) /* Bulk endpoint max packet (8/16/32/64/512) */ +#define IMXRT_INTRMAXPACKET (1024) /* Interrupt endpoint max packet (1 to 1024) */ +#define IMXRT_ISOCMAXPACKET (512) /* Acutally 1..1023 */ + +/* Endpoint bit position in SETUPSTAT, PRIME, FLUSH, STAT, COMPLETE + * registers + */ + +#define IMXRT_ENDPTSHIFT(epphy) (IMXRT_EPPHYIN(epphy) ? (16 + ((epphy) >> 1)) : ((epphy) >> 1)) +#define IMXRT_ENDPTMASK(epphy) (1 << IMXRT_ENDPTSHIFT(epphy)) +#define IMXRT_ENDPTMASK_ALL 0x00ff00ff + +/* Request queue operations *************************************************/ + +#define imxrt_rqempty(ep) ((ep)->head == NULL) +#define imxrt_rqpeek(ep) ((ep)->head) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* A container for a request so that the request may be retained in a list */ + +struct imxrt_req_s +{ + struct usbdev_req_s req; /* Standard USB request */ + struct imxrt_req_s *flink; /* Supports a singly linked list */ +}; + +/* This is the internal representation of an endpoint */ + +struct imxrt_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 imxrt_ep_s. + */ + + struct usbdev_ep_s ep; /* Standard endpoint structure */ + + /* IMXRTXX-specific fields */ + + struct imxrt_usbdev_s *dev; /* Reference to private driver data */ + struct imxrt_req_s *head; /* Request list for this endpoint */ + struct imxrt_req_s *tail; + uint8_t epphy; /* Physical EP address */ + uint8_t stalled:1; /* 1: Endpoint is stalled */ +}; + +/* This structure retains the state of the USB device controller */ + +struct imxrt_usbdev_s +{ + /* Common device fields. This must be the first thing defined in the + * structure so that it is possible to simply cast from struct usbdev_s + * to struct imxrt_usbdev_s. + */ + + struct usbdev_s usbdev; + + /* The bound device class driver */ + + struct usbdevclass_driver_s *driver; + + /* IMXRTXX-specific fields */ + + uint8_t ep0state; /* State of certain EP0 operations */ + uint8_t ep0buf[64]; /* buffer for EP0 short transfers */ + 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_IMXRT_USBDEV_FRAME_INTERRUPT + uint32_t sof; /* Last start-of-frame */ +#endif + + uint16_t ep0buf_len; + struct usb_ctrlreq_s ep0ctrl; + + /* The endpoint list */ + + struct imxrt_ep_s eplist[IMXRT_NPHYSENDPOINTS]; +}; + +#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_IMXRT_USBDEV_REGDEBUG +static uint32_t imxrt_getreg(uint32_t addr); +static void imxrt_putreg(uint32_t val, uint32_t addr); +#else +# define imxrt_getreg(addr) getreg32(addr) +# define imxrt_putreg(val,addr) putreg32(val,addr) +#endif + +static inline void imxrt_clrbits(uint32_t mask, uint32_t addr); +static inline void imxrt_setbits(uint32_t mask, uint32_t addr); +static inline void imxrt_chgbits(uint32_t mask, uint32_t val, uint32_t addr); + +/* Request queue operations *************************************************/ + +static FAR struct imxrt_req_s *imxrt_rqdequeue( + FAR struct imxrt_ep_s *privep); +static bool imxrt_rqenqueue(FAR struct imxrt_ep_s *privep, + FAR struct imxrt_req_s *req); + +/* Low level data transfers and request operations **************************/ + +static inline void imxrt_writedtd(struct imxrt_dtd_s *dtd, + const uint8_t *data, + uint32_t nbytes); +static inline void imxrt_queuedtd(uint8_t epphy, struct imxrt_dtd_s *dtd); +static inline void imxrt_ep0xfer(uint8_t epphy, uint8_t *data, + uint32_t nbytes); +static void imxrt_readsetup(uint8_t epphy, + struct usb_ctrlreq_s *ctrl); + +static inline void imxrt_set_address(struct imxrt_usbdev_s *priv, + uint16_t address); + +static void imxrt_flushep(struct imxrt_ep_s *privep); + +static int imxrt_progressep(struct imxrt_ep_s *privep); +static void imxrt_reqcomplete(struct imxrt_ep_s *privep, + struct imxrt_req_s *privreq, int16_t result); + +static void imxrt_cancelrequests(struct imxrt_ep_s *privep, + int16_t status); + +/* Interrupt handling *******************************************************/ + +static struct imxrt_ep_s *imxrt_epfindbyaddr(struct imxrt_usbdev_s *priv, + uint16_t eplog); +static void imxrt_dispatchrequest(struct imxrt_usbdev_s *priv, + const struct usb_ctrlreq_s *ctrl); +static void imxrt_ep0configure(struct imxrt_usbdev_s *priv); +static void imxrt_usbreset(struct imxrt_usbdev_s *priv); + +static inline void imxrt_ep0state(struct imxrt_usbdev_s *priv, + uint16_t state); +static void imxrt_ep0setup(struct imxrt_usbdev_s *priv); + +static void imxrt_ep0complete(struct imxrt_usbdev_s *priv, + uint8_t epphy); +static void imxrt_ep0nak(struct imxrt_usbdev_s *priv, uint8_t epphy); +static bool imxrt_epcomplete(struct imxrt_usbdev_s *priv, + uint8_t epphy); + +static int imxrt_usbinterrupt(int irq, FAR void *context, + FAR void *arg); + +/* Endpoint operations ******************************************************/ + +/* USB device controller operations *****************************************/ + +static int imxrt_epconfigure(FAR struct usbdev_ep_s *ep, + const struct usb_epdesc_s *desc, bool last); +static int imxrt_epdisable(FAR struct usbdev_ep_s *ep); +static FAR struct usbdev_req_s *imxrt_epallocreq(FAR struct usbdev_ep_s *ep); +static void imxrt_epfreereq(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *); +#ifdef CONFIG_USBDEV_DMA +static void *imxrt_epallocbuffer(FAR struct usbdev_ep_s *ep, + uint16_t bytes); +static void imxrt_epfreebuffer(FAR struct usbdev_ep_s *ep, + FAR void *buf); +#endif +static int imxrt_epsubmit(FAR struct usbdev_ep_s *ep, + struct usbdev_req_s *req); +static int imxrt_epcancel(FAR struct usbdev_ep_s *ep, + struct usbdev_req_s *req); +static int imxrt_epstall(FAR struct usbdev_ep_s *ep, bool resume); + +static FAR struct usbdev_ep_s *imxrt_allocep(FAR struct usbdev_s *dev, + uint8_t epno, bool in, uint8_t eptype); +static void imxrt_freeep(FAR struct usbdev_s *dev, + FAR struct usbdev_ep_s *ep); +static int imxrt_getframe(struct usbdev_s *dev); +static int imxrt_wakeup(struct usbdev_s *dev); +static int imxrt_selfpowered(struct usbdev_s *dev, bool selfpowered); +static int imxrt_pullup(struct usbdev_s *dev, bool enable); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Since there is only a single USB interface, all status information can be + * be simply retained in a single global instance. + */ + +static struct imxrt_usbdev_s g_usbdev; + +static struct imxrt_dqh_s g_qh[IMXRT_NPHYSENDPOINTS] + aligned_data(2048); + +static struct imxrt_dtd_s g_td[IMXRT_NPHYSENDPOINTS] + aligned_data(32); + +static const struct usbdev_epops_s g_epops = +{ + .configure = imxrt_epconfigure, + .disable = imxrt_epdisable, + .allocreq = imxrt_epallocreq, + .freereq = imxrt_epfreereq, +#ifdef CONFIG_USBDEV_DMA + .allocbuffer = imxrt_epallocbuffer, + .freebuffer = imxrt_epfreebuffer, +#endif + .submit = imxrt_epsubmit, + .cancel = imxrt_epcancel, + .stall = imxrt_epstall, +}; + +static const struct usbdev_ops_s g_devops = +{ + .allocep = imxrt_allocep, + .freeep = imxrt_freeep, + .getframe = imxrt_getframe, + .wakeup = imxrt_wakeup, + .selfpowered = imxrt_selfpowered, + .pullup = imxrt_pullup, +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imxrt_getreg + * + * Description: + * Get the contents of an IMXRT3x register + * + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_USBDEV_REGDEBUG +static uint32_t imxrt_getreg(uint32_t addr) +{ + static uint32_t prevaddr = 0; + static uint32_t preval = 0; + static uint32_t count = 0; + + /* Read the value from the register */ + + uint32_t val = getreg32(addr); + + /* Is this the same value that we read from the same register last time? + * Are we polling the register? If so, suppress some of the output. + */ + + if (addr == prevaddr && val == preval) + { + if (count == 0xffffffff || ++count > 3) + { + if (count == 4) + { + uinfo("...\n"); + } + + return val; + } + } + + /* No this is a new address or value */ + + else + { + /* Did we print "..." for the previous value? */ + + if (count > 3) + { + /* Yes.. then show how many times the value repeated */ + + uinfo("[repeats %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", addr, val); + return val; +} +#endif + +/**************************************************************************** + * Name: imxrt_putreg + * + * Description: + * Set the contents of an IMXRT3x register to a value + * + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_USBDEV_REGDEBUG +static void imxrt_putreg(uint32_t val, uint32_t addr) +{ + /* Show the register value being written */ + + uinfo("%08x<-%08x\n", addr, val); + + /* Write the value */ + + putreg32(val, addr); +} +#endif + +/**************************************************************************** + * Name: imxrt_clrbits + * + * Description: + * Clear bits in a register + * + ****************************************************************************/ + +static inline void imxrt_clrbits(uint32_t mask, uint32_t addr) +{ + uint32_t reg = imxrt_getreg(addr); + reg &= ~mask; + imxrt_putreg(reg, addr); +} + +/**************************************************************************** + * Name: imxrt_setbits + * + * Description: + * Set bits in a register + * + ****************************************************************************/ + +static inline void imxrt_setbits(uint32_t mask, uint32_t addr) +{ + uint32_t reg = imxrt_getreg(addr); + reg |= mask; + imxrt_putreg(reg, addr); +} + +/**************************************************************************** + * Name: imxrt_chgbits + * + * Description: + * Change bits in a register + * + ****************************************************************************/ + +static inline void imxrt_chgbits(uint32_t mask, uint32_t val, uint32_t addr) +{ + uint32_t reg = imxrt_getreg(addr); + reg &= ~mask; + reg |= val; + imxrt_putreg(reg, addr); +} + +/**************************************************************************** + * Name: imxrt_rqdequeue + * + * Description: + * Remove a request from an endpoint request queue + * + ****************************************************************************/ + +static FAR struct imxrt_req_s *imxrt_rqdequeue(FAR struct imxrt_ep_s *privep) +{ + FAR struct imxrt_req_s *ret = privep->head; + + if (ret) + { + privep->head = ret->flink; + if (!privep->head) + { + privep->tail = NULL; + } + + ret->flink = NULL; + } + + return ret; +} + +/**************************************************************************** + * Name: imxrt_rqenqueue + * + * Description: + * Add a request from an endpoint request queue + * + ****************************************************************************/ + +static bool imxrt_rqenqueue(FAR struct imxrt_ep_s *privep, + FAR struct imxrt_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: imxrt_writedtd + * + * Description: + * Initialise a DTD to transfer the data + * + ****************************************************************************/ + +static inline void imxrt_writedtd(struct imxrt_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) data); + dtd->buffer1 = (((uint32_t) data) + 0x1000) & 0xfffff000; + dtd->buffer2 = (((uint32_t) data) + 0x2000) & 0xfffff000; + dtd->buffer3 = (((uint32_t) data) + 0x3000) & 0xfffff000; + dtd->buffer4 = (((uint32_t) data) + 0x4000) & 0xfffff000; + dtd->xfer_len = nbytes; + + up_flush_dcache((uintptr_t)dtd, + (uintptr_t)dtd + sizeof(struct imxrt_dtd_s)); + up_flush_dcache((uintptr_t)data, + (uintptr_t)data + nbytes); +} + +/**************************************************************************** + * Name: imxrt_queuedtd + * + * Description: + * Add the DTD to the device list + * + * Assumptions: + * DTD is already flushed to RAM. + * + ****************************************************************************/ + +static void imxrt_queuedtd(uint8_t epphy, struct imxrt_dtd_s *dtd) +{ + struct imxrt_dqh_s *dqh = &g_qh[epphy]; + + /* Queue the DTD onto the Endpoint + * NOTE - this only works when no DTD is currently queued + */ + + dqh->overlay.nextdesc = (uint32_t) dtd; + dqh->overlay.config &= ~(DTD_CONFIG_ACTIVE | DTD_CONFIG_HALTED); + + up_flush_dcache((uintptr_t)dqh, + (uintptr_t)dqh + sizeof(struct imxrt_dqh_s)); + + uint32_t bit = IMXRT_ENDPTMASK(epphy); + + imxrt_setbits (bit, IMXRT_USBDEV_ENDPTPRIME); + + while (imxrt_getreg (IMXRT_USBDEV_ENDPTPRIME) & bit) + ; +} + +/**************************************************************************** + * Name: imxrt_ep0xfer + * + * Description: + * Schedule a short transfer for Endpoint 0 (IN or OUT) + * + ****************************************************************************/ + +static inline void imxrt_ep0xfer(uint8_t epphy, uint8_t *buf, + uint32_t nbytes) +{ + struct imxrt_dtd_s *dtd = &g_td[epphy]; + + imxrt_writedtd(dtd, buf, nbytes); + + imxrt_queuedtd(epphy, dtd); +} + +/**************************************************************************** + * Name: imxrt_readsetup + * + * Description: + * Read a Setup packet from the DTD. + * + ****************************************************************************/ + +static void imxrt_readsetup(uint8_t epphy, struct usb_ctrlreq_s *ctrl) +{ + struct imxrt_dqh_s *dqh = &g_qh[epphy]; + int i; + + do + { + /* Set the trip wire */ + + imxrt_setbits(USBDEV_USBCMD_SUTW, IMXRT_USBDEV_USBCMD); + + up_invalidate_dcache((uintptr_t)dqh, + (uintptr_t)dqh + sizeof(struct imxrt_dqh_s)); + + /* Copy the request... */ + + for (i = 0; i < 8; i++) + { + ((uint8_t *) ctrl)[i] = ((uint8_t *) dqh->setup)[i]; + } + } + while (!(imxrt_getreg(IMXRT_USBDEV_USBCMD) & USBDEV_USBCMD_SUTW)); + + /* Clear the trip wire */ + + imxrt_clrbits(USBDEV_USBCMD_SUTW, IMXRT_USBDEV_USBCMD); + + /* Clear the Setup Interrupt */ + + imxrt_putreg (IMXRT_ENDPTMASK(IMXRT_EP0_OUT), IMXRT_USBDEV_ENDPTSETUPSTAT); +} + +/**************************************************************************** + * Name: imxrt_set_address + * + * Description: + * Set the devices USB address + * + ****************************************************************************/ + +static inline void imxrt_set_address(struct imxrt_usbdev_s *priv, + uint16_t address) +{ + priv->paddr = address; + priv->paddrset = address != 0; + + imxrt_chgbits(USBDEV_DEVICEADDR_MASK, + priv->paddr << USBDEV_DEVICEADDR_SHIFT, + IMXRT_USBDEV_DEVICEADDR); +} + +/**************************************************************************** + * Name: imxrt_flushep + * + * Description: + * Flush any primed descriptors from this ep + * + ****************************************************************************/ + +static void imxrt_flushep(struct imxrt_ep_s *privep) +{ + uint32_t mask = IMXRT_ENDPTMASK(privep->epphy); + do + { + imxrt_putreg (mask, IMXRT_USBDEV_ENDPTFLUSH); + while ((imxrt_getreg(IMXRT_USBDEV_ENDPTFLUSH) & mask) != 0) + ; + } + while ((imxrt_getreg(IMXRT_USBDEV_ENDPTSTATUS) & mask) != 0); +} + +/**************************************************************************** + * Name: imxrt_progressep + * + * Description: + * Progress the Endpoint by priming the first request into the queue head + * + ****************************************************************************/ + +static int imxrt_progressep(struct imxrt_ep_s *privep) +{ + struct imxrt_dtd_s *dtd = &g_td[privep->epphy]; + struct imxrt_req_s *privreq; + + /* Check the request from the head of the endpoint request queue */ + + privreq = imxrt_rqpeek(privep); + if (!privreq) + { + usbtrace(TRACE_INTDECODE(IMXRT_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 thr response + */ + + if (privep->epphy == IMXRT_EP0_IN && + privep->dev->ep0state == EP0STATE_SETUP_OUT) + { + imxrt_ep0state (privep->dev, EP0STATE_WAIT_NAK_IN); + } + else + { + if (IMXRT_EPPHYIN(privep->epphy)) + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_EPINNULLPACKET), 0); + } + else + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_EPOUTNULLPACKET), 0); + } + } + + imxrt_reqcomplete(privep, imxrt_rqdequeue(privep), OK); + return OK; + } + + if (privep->epphy == IMXRT_EP0_IN) + { + imxrt_ep0state (privep->dev, EP0STATE_DATA_IN); + } + else if (privep->epphy == IMXRT_EP0_OUT) + { + imxrt_ep0state (privep->dev, EP0STATE_DATA_OUT); + } + + int bytesleft = privreq->req.len - privreq->req.xfrd; + + if (IMXRT_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 */ + + imxrt_writedtd (dtd, privreq->req.buf + privreq->req.xfrd, bytesleft); + + /* Then queue onto the DQH */ + + imxrt_queuedtd(privep->epphy, dtd); + + return OK; +} + +/**************************************************************************** + * Name: imxrt_reqcomplete + * + * Description: + * Handle termination of the request at the head of the endpoint request + * queue. + * + ****************************************************************************/ + +static void imxrt_reqcomplete(struct imxrt_ep_s *privep, + struct imxrt_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 == IMXRT_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: imxrt_cancelrequests + * + * Description: + * Cancel all pending requests for an endpoint + * + ****************************************************************************/ + +static void imxrt_cancelrequests(struct imxrt_ep_s *privep, int16_t status) +{ + if (!imxrt_rqempty(privep)) + imxrt_flushep(privep); + + while (!imxrt_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), + (imxrt_rqpeek(privep))->req.xfrd); + imxrt_reqcomplete(privep, imxrt_rqdequeue(privep), status); + } +} + +/**************************************************************************** + * Name: imxrt_epfindbyaddr + * + * Description: + * Find the physical endpoint structure corresponding to a logic endpoint + * address + * + ****************************************************************************/ + +static struct imxrt_ep_s *imxrt_epfindbyaddr(struct imxrt_usbdev_s *priv, + uint16_t eplog) +{ + struct imxrt_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 < IMXRT_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: imxrt_dispatchrequest + * + * Description: + * Provide unhandled setup actions to the class driver. This is logically + * part of the USB interrupt handler. + * + ****************************************************************************/ + +static void imxrt_dispatchrequest(struct imxrt_usbdev_s *priv, + const struct usb_ctrlreq_s *ctrl) +{ + int ret = -EIO; + + usbtrace(TRACE_INTDECODE(IMXRT_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->ep0buf, + priv->ep0buf_len); + } + + if (ret < 0) + { + /* Stall on failure */ + + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_DISPATCHSTALL), 0); + priv->stalled = true; + } +} + +/**************************************************************************** + * Name: imxrt_ep0configure + * + * Description: + * Reset Usb engine + * + ****************************************************************************/ + +static void imxrt_ep0configure(struct imxrt_usbdev_s *priv) +{ + /* Enable ep0 IN and ep0 OUT */ + + g_qh[IMXRT_EP0_OUT].capability = + (DQH_CAPABILITY_MAX_PACKET(CONFIG_USBDEV_EP0_MAXSIZE) | + DQH_CAPABILITY_IOS | DQH_CAPABILITY_ZLT); + + g_qh[IMXRT_EP0_IN].capability = + (DQH_CAPABILITY_MAX_PACKET(CONFIG_USBDEV_EP0_MAXSIZE) | + DQH_CAPABILITY_IOS | DQH_CAPABILITY_ZLT); + + g_qh[IMXRT_EP0_OUT].currdesc = DTD_NEXTDESC_INVALID; + g_qh[IMXRT_EP0_IN].currdesc = DTD_NEXTDESC_INVALID; + + up_flush_dcache((uintptr_t)g_qh, + (uintptr_t)g_qh + (sizeof(struct imxrt_dqh_s) * 2)); + + /* Enable EP0 */ + + imxrt_setbits (USBDEV_ENDPTCTRL0_RXE | USBDEV_ENDPTCTRL0_TXE, + IMXRT_USBDEV_ENDPTCTRL0); +} + +/**************************************************************************** + * Name: imxrt_usbreset + * + * Description: + * Reset Usb engine + * + ****************************************************************************/ + +static void imxrt_usbreset(struct imxrt_usbdev_s *priv) +{ + int epphy; + + /* Disable all endpoints. Control endpoint 0 is always enabled */ + + imxrt_clrbits (USBDEV_ENDPTCTRL_RXE | USBDEV_ENDPTCTRL_TXE, + IMXRT_USBDEV_ENDPTCTRL1); + imxrt_clrbits (USBDEV_ENDPTCTRL_RXE | USBDEV_ENDPTCTRL_TXE, + IMXRT_USBDEV_ENDPTCTRL2); + imxrt_clrbits (USBDEV_ENDPTCTRL_RXE | USBDEV_ENDPTCTRL_TXE, + IMXRT_USBDEV_ENDPTCTRL3); + imxrt_clrbits (USBDEV_ENDPTCTRL_RXE | USBDEV_ENDPTCTRL_TXE, + IMXRT_USBDEV_ENDPTCTRL4); + imxrt_clrbits (USBDEV_ENDPTCTRL_RXE | USBDEV_ENDPTCTRL_TXE, + IMXRT_USBDEV_ENDPTCTRL5); + + /* Clear all pending interrupts */ + + imxrt_putreg (imxrt_getreg(IMXRT_USBDEV_ENDPTNAK), + IMXRT_USBDEV_ENDPTNAK); + imxrt_putreg (imxrt_getreg(IMXRT_USBDEV_ENDPTSETUPSTAT), + IMXRT_USBDEV_ENDPTSETUPSTAT); + imxrt_putreg (imxrt_getreg(IMXRT_USBDEV_ENDPTCOMPLETE), + IMXRT_USBDEV_ENDPTCOMPLETE); + + /* Wait for all prime operations to have completed and then flush all + * DTDs + */ + + while (imxrt_getreg (IMXRT_USBDEV_ENDPTPRIME) != 0) + ; + imxrt_putreg (IMXRT_ENDPTMASK_ALL, IMXRT_USBDEV_ENDPTFLUSH); + while (imxrt_getreg (IMXRT_USBDEV_ENDPTFLUSH)) + ; + + /* Reset endpoints */ + + for (epphy = 0; epphy < IMXRT_NPHYSENDPOINTS; epphy++) + { + struct imxrt_ep_s *privep = &priv->eplist[epphy]; + + imxrt_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 */ + + imxrt_chgbits(USBDEV_USBCMD_ITC_MASK, USBDEV_USBCMD_ITCIMME, + IMXRT_USBDEV_USBCMD); + + /* Zero out the Endpoint queue heads */ + + memset ((void *) g_qh, 0, sizeof (g_qh)); + memset ((void *) g_td, 0, sizeof (g_td)); + + up_flush_dcache((uintptr_t)g_qh, (uintptr_t)g_qh + sizeof(g_qh)); + up_flush_dcache((uintptr_t)g_td, (uintptr_t)g_td + sizeof(g_td)); + + /* Set USB address to 0 */ + + imxrt_set_address (priv, 0); + + /* Initialise the Enpoint List Address */ + + imxrt_putreg ((uint32_t)g_qh, IMXRT_USBDEV_ENDPOINTLIST); + + /* EndPoint 0 initialization */ + + imxrt_ep0configure(priv); + + /* Enable Device interrupts */ + + imxrt_putreg(USB_FRAME_INT | USB_ERROR_INT | USBDEV_USBINTR_NAKE | + USBDEV_USBINTR_SLE | USBDEV_USBINTR_URE | USBDEV_USBINTR_PCE | + USBDEV_USBINTR_UE, IMXRT_USBDEV_USBINTR); +} + +/**************************************************************************** + * Name: imxrt_setstate + * + * Description: + * Sets the EP0 state and manages the NAK interrupts + * + ****************************************************************************/ + +static inline void imxrt_ep0state(struct imxrt_usbdev_s *priv, + uint16_t state) +{ + priv->ep0state = state; + + switch (state) + { + case EP0STATE_WAIT_NAK_IN: + imxrt_putreg (IMXRT_ENDPTMASK(IMXRT_EP0_IN), IMXRT_USBDEV_ENDPTNAKEN); + break; + + case EP0STATE_WAIT_NAK_OUT: + imxrt_putreg (IMXRT_ENDPTMASK(IMXRT_EP0_OUT), IMXRT_USBDEV_ENDPTNAKEN); + break; + + default: + imxrt_putreg(0, IMXRT_USBDEV_ENDPTNAKEN); + break; + } +} + +/**************************************************************************** + * Name: imxrt_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 imxrt_ep0setup(struct imxrt_usbdev_s *priv) +{ + struct imxrt_ep_s *privep; + struct usb_ctrlreq_s *ctrl; + uint16_t value; + uint16_t index; + uint16_t len; + + ctrl = &priv->ep0ctrl; + + /* Terminate any pending requests - since all DTDs will have been retired + * because of the setup packet. + */ + + imxrt_cancelrequests(&priv->eplist[IMXRT_EP0_OUT], -EPROTO); + imxrt_cancelrequests(&priv->eplist[IMXRT_EP0_IN], -EPROTO); + + /* Assume NOT stalled */ + + priv->eplist[IMXRT_EP0_OUT].stalled = false; + priv->eplist[IMXRT_EP0_IN].stalled = false; + priv->stalled = false; + + /* Read EP0 setup data */ + + imxrt_readsetup(IMXRT_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->ep0buf_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) + { + imxrt_ep0state (priv, EP0STATE_SETUP_IN); + } + else + { + imxrt_ep0state (priv, EP0STATE_SETUP_OUT); + + if (len > 0) + { + imxrt_ep0state(priv, EP0STATE_SHORTREAD); + imxrt_ep0xfer(IMXRT_EP0_OUT, priv->ep0buf, len); + return; + } + } + + /* Dispatch any non-standard requests */ + + if ((ctrl->type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD) + { + imxrt_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(IMXRT_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(IMXRT_TRACEINTID_EPGETSTATUS), 0); + privep = imxrt_epfindbyaddr(priv, index); + if (!privep) + { + usbtrace( + TRACE_DEVERROR(IMXRT_TRACEERR_BADEPGETSTATUS), + 0); + priv->stalled = true; + } + else + { + if (privep->stalled) + { + priv->ep0buf[0] = 1; /* Stalled */ + } + else + { + priv->ep0buf[0] = 0; /* Not stalled */ + } + + priv->ep0buf[1] = 0; + + imxrt_ep0xfer (IMXRT_EP0_IN, priv->ep0buf, 2); + imxrt_ep0state (priv, EP0STATE_SHORTWRITE); + } + } + break; + + case USB_REQ_RECIPIENT_DEVICE: + { + if (index == 0) + { + usbtrace( + TRACE_INTDECODE(IMXRT_TRACEINTID_DEVGETSTATUS), + 0); + + /* Features: Remote Wakeup=YES; selfpowered=? */ + + priv->ep0buf[0] = + (priv->selfpowered << + USB_FEATURE_SELFPOWERED) | + (1 << USB_FEATURE_REMOTEWAKEUP); + priv->ep0buf[1] = 0; + + imxrt_ep0xfer(IMXRT_EP0_IN, priv->ep0buf, 2); + imxrt_ep0state (priv, EP0STATE_SHORTWRITE); + } + else + { + usbtrace( + TRACE_DEVERROR(IMXRT_TRACEERR_BADDEVGETSTATUS), + 0); + priv->stalled = true; + } + } + break; + + case USB_REQ_RECIPIENT_INTERFACE: + { + usbtrace(TRACE_INTDECODE(IMXRT_TRACEINTID_IFGETSTATUS), + 0); + priv->ep0buf[0] = 0; + priv->ep0buf[1] = 0; + + imxrt_ep0xfer(IMXRT_EP0_IN, priv->ep0buf, 2); + imxrt_ep0state (priv, EP0STATE_SHORTWRITE); + } + break; + + default: + { + usbtrace(TRACE_DEVERROR(IMXRT_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(IMXRT_TRACEINTID_CLEARFEATURE), 0); + if ((ctrl->type & USB_REQ_RECIPIENT_MASK) != + USB_REQ_RECIPIENT_ENDPOINT) + { + imxrt_dispatchrequest(priv, ctrl); + } + else if (priv->paddrset != 0 && + value == USB_FEATURE_ENDPOINTHALT && + len == 0 && (privep = imxrt_epfindbyaddr(priv, index)) != NULL) + { + imxrt_epstall(&privep->ep, true); + imxrt_ep0state (priv, EP0STATE_WAIT_NAK_IN); + } + else + { + usbtrace(TRACE_DEVERROR(IMXRT_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(IMXRT_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) + { + imxrt_dispatchrequest(priv, ctrl); + } + else if (priv->paddrset != 0 && + value == USB_FEATURE_ENDPOINTHALT && + len == 0 && (privep = imxrt_epfindbyaddr(priv, index)) != NULL) + { + imxrt_epstall(&privep->ep, false); + imxrt_ep0state (priv, EP0STATE_WAIT_NAK_IN); + } + else + { + usbtrace(TRACE_DEVERROR(IMXRT_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(IMXRT_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; + imxrt_ep0state (priv, EP0STATE_WAIT_NAK_IN); + } + else + { + usbtrace(TRACE_DEVERROR(IMXRT_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(IMXRT_TRACEINTID_GETSETDESC), 0); + if ((ctrl->type & USB_REQ_RECIPIENT_MASK) == + USB_REQ_RECIPIENT_DEVICE) + { + imxrt_dispatchrequest(priv, ctrl); + } + else + { + usbtrace(TRACE_DEVERROR(IMXRT_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(IMXRT_TRACEINTID_GETCONFIG), 0); + if (priv->paddrset && + ((ctrl->type & USB_REQ_RECIPIENT_MASK) == + USB_REQ_RECIPIENT_DEVICE) && + value == 0 && index == 0 && len == 1) + { + imxrt_dispatchrequest(priv, ctrl); + } + else + { + usbtrace(TRACE_DEVERROR(IMXRT_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(IMXRT_TRACEINTID_SETCONFIG), 0); + if (((ctrl->type & USB_REQ_RECIPIENT_MASK) == + USB_REQ_RECIPIENT_DEVICE) && index == 0 && len == 0) + { + imxrt_dispatchrequest(priv, ctrl); + } + else + { + usbtrace(TRACE_DEVERROR(IMXRT_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(IMXRT_TRACEINTID_GETSETIF), 0); + imxrt_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(IMXRT_TRACEINTID_SYNCHFRAME), 0); + } + break; + + default: + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_INVALIDCTRLREQ), 0); + priv->stalled = true; + } + break; + } + } + + if (priv->stalled) + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_EP0SETUPSTALLED), + priv->ep0state); + imxrt_epstall(&priv->eplist[IMXRT_EP0_IN].ep, false); + imxrt_epstall(&priv->eplist[IMXRT_EP0_OUT].ep, false); + } +} + +/**************************************************************************** + * Name: imxrt_ep0complete + * + * Description: + * Transfer complete handler for Endpoint 0 + * + ****************************************************************************/ + +static void imxrt_ep0complete(struct imxrt_usbdev_s *priv, uint8_t epphy) +{ + struct imxrt_ep_s *privep = &priv->eplist[epphy]; + + usbtrace(TRACE_INTDECODE(IMXRT_TRACEINTID_EP0COMPLETE), + (uint16_t)priv->ep0state); + + switch (priv->ep0state) + { + case EP0STATE_DATA_IN: + if (imxrt_rqempty(privep)) + { + return; + } + + if (imxrt_epcomplete (priv, epphy)) + { + imxrt_ep0state (priv, EP0STATE_WAIT_NAK_OUT); + } + break; + + case EP0STATE_DATA_OUT: + if (imxrt_rqempty(privep)) + { + return; + } + + if (imxrt_epcomplete (priv, epphy)) + { + imxrt_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->ep0buf, + (uintptr_t)priv->ep0buf + sizeof(priv->ep0buf)); + + imxrt_dispatchrequest(priv, &priv->ep0ctrl); + imxrt_ep0state (priv, EP0STATE_WAIT_NAK_IN); + break; + + case EP0STATE_SHORTWRITE: + imxrt_ep0state (priv, EP0STATE_WAIT_NAK_OUT); + break; + + case EP0STATE_WAIT_STATUS_IN: + imxrt_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(IMXRT_TRACEINTID_EP0INSETADDRESS), + (uint16_t)priv->paddr); + imxrt_set_address (priv, priv->paddr); + } + + break; + + case EP0STATE_WAIT_STATUS_OUT: + imxrt_ep0state (priv, EP0STATE_IDLE); + break; + + default: +#ifdef CONFIG_DEBUG_FEATURES + DEBUGASSERT(priv->ep0state != EP0STATE_DATA_IN && + priv->ep0state != EP0STATE_DATA_OUT && + priv->ep0state != EP0STATE_SHORTWRITE && + priv->ep0state != EP0STATE_WAIT_STATUS_IN && + priv->ep0state != EP0STATE_WAIT_STATUS_OUT); +#endif + priv->stalled = true; + break; + } + + if (priv->stalled) + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_EP0SETUPSTALLED), + priv->ep0state); + imxrt_epstall(&priv->eplist[IMXRT_EP0_IN].ep, false); + imxrt_epstall(&priv->eplist[IMXRT_EP0_OUT].ep, false); + } +} + +/**************************************************************************** + * Name: imxrt_ep0nak + * + * Description: + * Handle a NAK interrupt on EP0 + * + ****************************************************************************/ + +static void imxrt_ep0nak(struct imxrt_usbdev_s *priv, uint8_t epphy) +{ + usbtrace(TRACE_INTDECODE(IMXRT_TRACEINTID_EP0NAK), + (uint16_t)priv->ep0state); + + switch (priv->ep0state) + { + case EP0STATE_WAIT_NAK_IN: + imxrt_ep0xfer (IMXRT_EP0_IN, NULL, 0); + imxrt_ep0state (priv, EP0STATE_WAIT_STATUS_IN); + break; + + case EP0STATE_WAIT_NAK_OUT: + imxrt_ep0xfer (IMXRT_EP0_OUT, NULL, 0); + imxrt_ep0state (priv, EP0STATE_WAIT_STATUS_OUT); + break; + + default: +#ifdef CONFIG_DEBUG_FEATURES + DEBUGASSERT(priv->ep0state != EP0STATE_WAIT_NAK_IN && + priv->ep0state != EP0STATE_WAIT_NAK_OUT); +#endif + priv->stalled = true; + break; + } + + if (priv->stalled) + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_EP0SETUPSTALLED), + priv->ep0state); + imxrt_epstall(&priv->eplist[IMXRT_EP0_IN].ep, false); + imxrt_epstall(&priv->eplist[IMXRT_EP0_OUT].ep, false); + } +} + +/**************************************************************************** + * Name: imxrt_epcomplete + * + * Description: + * Transfer complete handler for Endpoints other than 0 + * returns whether the request at the head has completed + * + ****************************************************************************/ + +bool imxrt_epcomplete(struct imxrt_usbdev_s *priv, uint8_t epphy) +{ + struct imxrt_ep_s *privep = &priv->eplist[epphy]; + struct imxrt_req_s *privreq = privep->head; + struct imxrt_dtd_s *dtd = &g_td[epphy]; + + if (privreq == NULL) /* This shouldn't really happen */ + { + if (IMXRT_EPPHYOUT(privep->epphy)) + { + usbtrace(TRACE_INTDECODE(IMXRT_TRACEINTID_EPINQEMPTY), 0); + } + else + { + usbtrace(TRACE_INTDECODE(IMXRT_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 imxrt_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 (IMXRT_EPPHYOUT(privep->epphy)) + { + /* read(OUT) completes when request filled, or a short transfer is + * received + */ + + usbtrace(TRACE_INTDECODE(IMXRT_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(IMXRT_TRACEINTID_EPOUT), complete); + } + + /* If the transfer is complete, then dequeue and progress any further + * queued requests + */ + + if (complete) + { + privreq = imxrt_rqdequeue (privep); + } + + if (!imxrt_rqempty(privep)) + { + imxrt_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); + imxrt_reqcomplete(privep, privreq, OK); + } + + return complete; +} + +/**************************************************************************** + * Name: imxrt_usbinterrupt + * + * Description: + * USB interrupt handler + * + ****************************************************************************/ + +static int imxrt_usbinterrupt(int irq, FAR void *context, FAR void *arg) +{ + struct imxrt_usbdev_s *priv = &g_usbdev; + uint32_t disr; + uint32_t portsc1; + uint32_t n; + + usbtrace(TRACE_INTENTRY(IMXRT_TRACEINTID_USB), 0); + + /* Read the interrupts and then clear them */ + + disr = imxrt_getreg(IMXRT_USBDEV_USBSTS); + imxrt_putreg(disr, IMXRT_USBDEV_USBSTS); + + if (disr & USBDEV_USBSTS_URI) + { + usbtrace(TRACE_INTDECODE(IMXRT_TRACEINTID_DEVRESET), 0); + + imxrt_usbreset(priv); + + usbtrace(TRACE_INTEXIT(IMXRT_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(IMXRT_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(IMXRT_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 = imxrt_getreg(IMXRT_USBDEV_PORTSC1); + + 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_IMXRT_USBDEV_FRAME_INTERRUPT + if (disr & USBDEV_USBSTS_SRI) + { + usbtrace(TRACE_INTDECODE(IMXRT_TRACEINTID_FRAME), 0); + + uint32_t frindex = imxrt_getreg(IMXRT_USBDEV_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 = imxrt_getreg (IMXRT_USBDEV_ENDPTCOMPLETE); + + if (mask) + { + /* Clear any NAK interrupt and completion interrupts */ + + imxrt_putreg (mask, IMXRT_USBDEV_ENDPTNAK); + imxrt_putreg (mask, IMXRT_USBDEV_ENDPTCOMPLETE); + + if (mask & IMXRT_ENDPTMASK(0)) + { + imxrt_ep0complete(priv, 0); + } + + if (mask & IMXRT_ENDPTMASK(1)) + { + imxrt_ep0complete(priv, 1); + } + + for (n = 1; n < IMXRT_NLOGENDPOINTS; n++) + { + if (mask & IMXRT_ENDPTMASK((n << 1))) + { + imxrt_epcomplete (priv, (n << 1)); + } + + if (mask & IMXRT_ENDPTMASK((n << 1)+1)) + { + imxrt_epcomplete(priv, (n << 1)+1); + } + } + } + + /* Handle setup interrupts */ + + uint32_t setupstat = imxrt_getreg(IMXRT_USBDEV_ENDPTSETUPSTAT); + if (setupstat) + { + /* Clear the endpoint complete CTRL OUT and IN when a Setup is + * received + */ + + imxrt_putreg(IMXRT_ENDPTMASK(IMXRT_EP0_IN) | + IMXRT_ENDPTMASK(IMXRT_EP0_OUT), + IMXRT_USBDEV_ENDPTCOMPLETE); + + if (setupstat & IMXRT_ENDPTMASK(IMXRT_EP0_OUT)) + { + usbtrace(TRACE_INTDECODE(IMXRT_TRACEINTID_EP0SETUP), + setupstat); + imxrt_ep0setup(priv); + } + } + } + + if (disr & USBDEV_USBSTS_NAKI) + { + uint32_t pending = imxrt_getreg(IMXRT_USBDEV_ENDPTNAK) & + imxrt_getreg(IMXRT_USBDEV_ENDPTNAKEN); + if (pending) + { + /* We shouldn't see NAK interrupts except on Endpoint 0 */ + + if (pending & IMXRT_ENDPTMASK(0)) + { + imxrt_ep0nak(priv, 0); + } + + if (pending & IMXRT_ENDPTMASK(1)) + { + imxrt_ep0nak(priv, 1); + } + } + + /* Clear the interrupts */ + + imxrt_putreg(pending, IMXRT_USBDEV_ENDPTNAK); + } + + usbtrace(TRACE_INTEXIT(IMXRT_TRACEINTID_USB), 0); + return OK; +} + +/**************************************************************************** + * Endpoint operations + ****************************************************************************/ + +/**************************************************************************** + * Name: imxrt_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 imxrt_epconfigure(FAR struct usbdev_ep_s *ep, + FAR const struct usb_epdesc_s *desc, + bool last) +{ + FAR struct imxrt_ep_s *privep = (FAR struct imxrt_ep_s *)ep; + struct imxrt_dqh_s *dqh = &g_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_flush_dcache((uintptr_t)dqh, + (uintptr_t)dqh + sizeof(struct imxrt_dqh_s)); + + /* Setup Endpoint Control Register */ + + if (IMXRT_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; + } + + imxrt_chgbits (0xffff0000, cfg, + IMXRT_USBDEV_ENDPTCTRL(privep->epphy >> 1)); + } + 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; + } + + imxrt_chgbits (0x0000ffff, cfg, + IMXRT_USBDEV_ENDPTCTRL(privep->epphy >> 1)); + } + + /* Reset endpoint status */ + + privep->stalled = false; + + /* Enable the endpoint */ + + if (IMXRT_EPPHYIN(privep->epphy)) + { + imxrt_setbits (USBDEV_ENDPTCTRL_TXE, + IMXRT_USBDEV_ENDPTCTRL(privep->epphy >> 1)); + } + else + { + imxrt_setbits (USBDEV_ENDPTCTRL_RXE, + IMXRT_USBDEV_ENDPTCTRL(privep->epphy >> 1)); + } + + return OK; +} + +/**************************************************************************** + * Name: imxrt_epdisable + * + * Description: + * The endpoint will no longer be used + * + ****************************************************************************/ + +static int imxrt_epdisable(FAR struct usbdev_ep_s *ep) +{ + FAR struct imxrt_ep_s *privep = (FAR struct imxrt_ep_s *)ep; + irqstate_t flags; + +#ifdef CONFIG_DEBUG_FEATURES + if (!ep) + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + + usbtrace(TRACE_EPDISABLE, privep->epphy); + + flags = enter_critical_section(); + + /* Disable Endpoint */ + + if (IMXRT_EPPHYIN(privep->epphy)) + { + imxrt_clrbits (USBDEV_ENDPTCTRL_TXE, + IMXRT_USBDEV_ENDPTCTRL(privep->epphy >> 1)); + } + else + { + imxrt_clrbits (USBDEV_ENDPTCTRL_RXE, + IMXRT_USBDEV_ENDPTCTRL(privep->epphy >> 1)); + } + + privep->stalled = true; + + /* Cancel any ongoing activity */ + + imxrt_cancelrequests(privep, -ESHUTDOWN); + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: imxrt_epallocreq + * + * Description: + * Allocate an I/O request + * + ****************************************************************************/ + +static FAR struct usbdev_req_s *imxrt_epallocreq(FAR struct usbdev_ep_s *ep) +{ + FAR struct imxrt_req_s *privreq; + +#ifdef CONFIG_DEBUG_FEATURES + if (!ep) + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_INVALIDPARMS), 0); + return NULL; + } +#endif + + usbtrace(TRACE_EPALLOCREQ, ((FAR struct imxrt_ep_s *)ep)->epphy); + + privreq = (FAR struct imxrt_req_s *)kmm_malloc(sizeof(struct imxrt_req_s)); + if (!privreq) + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_ALLOCFAIL), 0); + return NULL; + } + + memset(privreq, 0, sizeof(struct imxrt_req_s)); + return &privreq->req; +} + +/**************************************************************************** + * Name: imxrt_epfreereq + * + * Description: + * Free an I/O request + * + ****************************************************************************/ + +static void imxrt_epfreereq(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req) +{ + FAR struct imxrt_req_s *privreq = (FAR struct imxrt_req_s *)req; + +#ifdef CONFIG_DEBUG_FEATURES + if (!ep || !req) + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_INVALIDPARMS), 0); + return; + } +#endif + + usbtrace(TRACE_EPFREEREQ, ((FAR struct imxrt_ep_s *)ep)->epphy); + kmm_free(privreq); +} + +/**************************************************************************** + * Name: imxrt_epallocbuffer + * + * Description: + * Allocate an I/O buffer + * + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_DMA +static void *imxrt_epallocbuffer(FAR 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. + */ + + FAR struct imxrt_ep_s *privep = (FAR struct imxrt_ep_s *)ep; + + usbtrace(TRACE_EPALLOCBUFFER, privep->epphy); + +#ifdef CONFIG_USBDEV_DMAMEMORY + return usbdev_dma_alloc(bytes); +#else + return kmm_malloc(bytes); +#endif +} +#endif + +/**************************************************************************** + * Name: imxrt_epfreebuffer + * + * Description: + * Free an I/O buffer + * + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_DMA +static void imxrt_epfreebuffer(FAR struct usbdev_ep_s *ep, FAR void *buf) +{ + FAR struct imxrt_ep_s *privep = (FAR struct imxrt_ep_s *)ep; + + usbtrace(TRACE_EPFREEBUFFER, privep->epphy); + +#ifdef CONFIG_USBDEV_DMAMEMORY + usbdev_dma_free(buf); +#else + kmm_free(buf); +#endif +} +#endif + +/**************************************************************************** + * Name: imxrt_epsubmit + * + * Description: + * Submit an I/O request to the endpoint + * + ****************************************************************************/ + +static int imxrt_epsubmit(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req) +{ + FAR struct imxrt_req_s *privreq = (FAR struct imxrt_req_s *)req; + FAR struct imxrt_ep_s *privep = (FAR struct imxrt_ep_s *)ep; + FAR struct imxrt_usbdev_s *priv; + irqstate_t flags; + int ret = OK; + +#ifdef CONFIG_DEBUG_FEATURES + if (!req || !req->callback || !req->buf || !ep) + { + usbtrace(TRACE_DEVERROR(IMXRT_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(IMXRT_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 (IMXRT_EPPHYIN(privep->epphy)) + { + usbtrace(TRACE_INREQQUEUED(privep->epphy), privreq->req.len); + } + else + { + usbtrace(TRACE_OUTREQQUEUED(privep->epphy), privreq->req.len); + } + + if (imxrt_rqenqueue(privep, privreq)) + { + imxrt_progressep(privep); + } + } + + leave_critical_section(flags); + return ret; +} + +/**************************************************************************** + * Name: imxrt_epcancel + * + * Description: + * Cancel an I/O request previously sent to an endpoint + * + ****************************************************************************/ + +static int imxrt_epcancel(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req) +{ + FAR struct imxrt_ep_s *privep = (FAR struct imxrt_ep_s *)ep; + irqstate_t flags; + +#ifdef CONFIG_DEBUG_FEATURES + if (!ep || !req) + { + usbtrace(TRACE_DEVERROR(IMXRT_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 ... + */ + + imxrt_cancelrequests(privep, -ESHUTDOWN); + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: imxrt_epstall + * + * Description: + * Stall or resume and endpoint + * + ****************************************************************************/ + +static int imxrt_epstall(FAR struct usbdev_ep_s *ep, bool resume) +{ + FAR struct imxrt_ep_s *privep = (FAR struct imxrt_ep_s *)ep; + irqstate_t flags; + + /* STALL or RESUME the endpoint */ + + flags = enter_critical_section(); + usbtrace(resume ? TRACE_EPRESUME : TRACE_EPSTALL, privep->epphy); + + uint32_t addr = IMXRT_USBDEV_ENDPTCTRL(privep->epphy >> 1); + uint32_t ctrl_xs = IMXRT_EPPHYIN(privep->epphy) ? + USBDEV_ENDPTCTRL_TXS : USBDEV_ENDPTCTRL_RXS; + uint32_t ctrl_xr = IMXRT_EPPHYIN(privep->epphy) ? + USBDEV_ENDPTCTRL_TXR : USBDEV_ENDPTCTRL_RXR; + + if (resume) + { + privep->stalled = false; + + /* Clear stall and reset the data toggle */ + + imxrt_chgbits (ctrl_xs | ctrl_xr, ctrl_xr, addr); + } + else + { + privep->stalled = true; + + imxrt_setbits (ctrl_xs, addr); + } + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Device operations + ****************************************************************************/ + +/**************************************************************************** + * Name: imxrt_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 FAR struct usbdev_ep_s *imxrt_allocep(FAR struct usbdev_s *dev, + uint8_t eplog, + bool in, uint8_t eptype) +{ + FAR struct imxrt_usbdev_s *priv = (FAR struct imxrt_usbdev_s *)dev; + uint32_t epset = IMXRT_EPALLSET & ~IMXRT_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 >= IMXRT_NLOGENDPOINTS) + { + usbtrace(TRACE_DEVERROR(IMXRT_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 &= IMXRT_EPINSET; + } + else + { + epset &= IMXRT_EPOUTSET; + } + + /* Get the subset matching the requested type */ + + switch (eptype) + { + case USB_EP_ATTR_XFER_INT: /* Interrupt endpoint */ + epset &= IMXRT_EPINTRSET; + break; + + case USB_EP_ATTR_XFER_BULK: /* Bulk endpoint */ + epset &= IMXRT_EPBULKSET; + break; + + case USB_EP_ATTR_XFER_ISOC: /* Isochronous endpoint */ + epset &= IMXRT_EPISOCSET; + break; + + case USB_EP_ATTR_XFER_CONTROL: /* Control endpoint -- not a valid choice */ + default: + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_BADEPTYPE), (uint16_t)eptype); + return NULL; + } + + /* Is the resulting endpoint supported by the IMXRT3x? */ + + 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 < IMXRT_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(IMXRT_TRACEERR_NOEP), (uint16_t)eplog); + return NULL; +} + +/**************************************************************************** + * Name: imxrt_freeep + * + * Description: + * Free the previously allocated endpoint + * + ****************************************************************************/ + +static void imxrt_freeep(FAR struct usbdev_s *dev, + FAR struct usbdev_ep_s *ep) +{ + FAR struct imxrt_usbdev_s *priv = (FAR struct imxrt_usbdev_s *)dev; + FAR struct imxrt_ep_s *privep = (FAR struct imxrt_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: imxrt_getframe + * + * Description: + * Returns the current frame number + * + ****************************************************************************/ + +static int imxrt_getframe(struct usbdev_s *dev) +{ +#ifdef CONFIG_IMXRT_USBDEV_FRAME_INTERRUPT + FAR struct imxrt_usbdev_s *priv = (FAR struct imxrt_usbdev_s *)dev; + + /* 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 = imxrt_getreg(IMXRT_USBDEV_FRINDEX); + 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: imxrt_wakeup + * + * Description: + * Tries to wake up the host connected to this device + * + ****************************************************************************/ + +static int imxrt_wakeup(struct usbdev_s *dev) +{ + irqstate_t flags; + + usbtrace(TRACE_DEVWAKEUP, 0); + + flags = enter_critical_section(); + imxrt_setbits(USBDEV_PRTSC1_FPR, IMXRT_USBDEV_PORTSC1); + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: imxrt_selfpowered + * + * Description: + * Sets/clears the device selfpowered feature + * + ****************************************************************************/ + +static int imxrt_selfpowered(struct usbdev_s *dev, bool selfpowered) +{ + FAR struct imxrt_usbdev_s *priv = (FAR struct imxrt_usbdev_s *)dev; + + usbtrace(TRACE_DEVSELFPOWERED, (uint16_t)selfpowered); + +#ifdef CONFIG_DEBUG_FEATURES + if (!dev) + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_INVALIDPARMS), 0); + return -ENODEV; + } +#endif + + priv->selfpowered = selfpowered; + return OK; +} + +/**************************************************************************** + * Name: imxrt_pullup + * + * Description: + * Software-controlled connect to/disconnect from USB host + * + ****************************************************************************/ + +static int imxrt_pullup(struct usbdev_s *dev, bool enable) +{ + usbtrace(TRACE_DEVPULLUP, (uint16_t)enable); + + irqstate_t flags = enter_critical_section(); + if (enable) + { + imxrt_setbits (USBDEV_USBCMD_RS, IMXRT_USBDEV_USBCMD); + +#ifdef CONFIG_IMXRT_USB0DEV_NOVBUS + /* Create a 'false' power event on the USB port so the MAC connects */ + + imxrt_clrbits (USBOTG_OTGSC_VD, IMXRT_USBOTG_OTGSC); + imxrt_setbits (USBOTG_OTGSC_VC, IMXRT_USBOTG_OTGSC); +#endif + } + else + { + imxrt_clrbits (USBDEV_USBCMD_RS, IMXRT_USBDEV_USBCMD); + } + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: arm_usbinitialize + * + * Description: + * Initialize USB hardware. + * + * Assumptions: + * - This function is called very early in the initialization sequence + * - PLL initialization is not performed here but should been in + * the low-level boot logic: USB1 PLL must be configured for operation + * at 480MHz + * + ****************************************************************************/ + +void arm_usbinitialize(void) +{ + struct imxrt_usbdev_s *priv = &g_usbdev; + int i; + irqstate_t flags; + + flags = enter_critical_section(); + + /* Initialize the device state structure */ + + memset(priv, 0, sizeof(struct imxrt_usbdev_s)); + priv->usbdev.ops = &g_devops; + priv->usbdev.ep0 = &priv->eplist[IMXRT_EP0_IN].ep; + priv->epavail = IMXRT_EPALLSET & ~IMXRT_EPCTRLSET; + + /* Initialize the endpoint list */ + + for (i = 0; i < IMXRT_NPHYSENDPOINTS; i++) + { + uint32_t bit = 1 << i; + + /* Set endpoint operations, reference to driver structure (not + * really necessary because there is only one controller), and + * the physical endpoint number (which is just the index to the + * endpoint). + */ + + 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 (IMXRT_EPPHYIN(i)) + { + priv->eplist[i].ep.eplog = IMXRT_EPPHYIN2LOG(i); + } + else + { + priv->eplist[i].ep.eplog = IMXRT_EPPHYOUT2LOG(i); + } + + /* The maximum packet size may depend on the type of endpoint */ + + if ((IMXRT_EPCTRLSET & bit) != 0) + { + priv->eplist[i].ep.maxpacket = IMXRT_EP0MAXPACKET; + } + else if ((IMXRT_EPINTRSET & bit) != 0) + { + priv->eplist[i].ep.maxpacket = IMXRT_INTRMAXPACKET; + } + else if ((IMXRT_EPBULKSET & bit) != 0) + { + priv->eplist[i].ep.maxpacket = IMXRT_BULKMAXPACKET; + } + else /* if ((IMXRT_EPISOCSET & bit) != 0) */ + { + priv->eplist[i].ep.maxpacket = IMXRT_ISOCMAXPACKET; + } + } + + /* Clock run */ + + imxrt_clockall_usboh3(); + + /* Disable USB interrupts */ + + imxrt_putreg(0, IMXRT_USBDEV_USBINTR); + + /* Soft reset PHY and enable clock */ + + putreg32(USBPHY_CTRL_SFTRST | USBPHY_CTRL_CLKGATE, IMXRT_USBPHY2_CTRL_CLR); + + /* Disconnect device */ + + imxrt_pullup(&priv->usbdev, false); + + /* Reset the controller */ + + imxrt_setbits (USBDEV_USBCMD_RST, IMXRT_USBDEV_USBCMD); + while (imxrt_getreg (IMXRT_USBDEV_USBCMD) & USBDEV_USBCMD_RST) + ; + + /* Power up the PHY (turn off power disable) - USBPHYx_PWDn + * Manual: The USB PHY Power-Down Register provides overall control of the + * PHY power state. Before programming this register, the PHY clocks must + * be enabled in registers USBPHYx_CTRLn and + * CCM_ANALOG_USBPHYx_PLL_480_CTRLn. + */ + + imxrt_putreg(0, IMXRT_USBPHY2_PWD); + + /* Program the controller to be the USB device controller */ + + imxrt_putreg (USBDEV_USBMODE_SDIS | USBDEV_USBMODE_SLOM | + USBDEV_USBMODE_CM_DEVICE, IMXRT_USBDEV_USBMODE); + + /* Attach USB controller interrupt handler */ + + irq_attach(IMXRT_IRQ_USBOTG2, imxrt_usbinterrupt, NULL); + up_enable_irq(IMXRT_IRQ_USBOTG2); + + leave_critical_section(flags); + + /* Reset/Re-initialize the USB hardware */ + + imxrt_usbreset(priv); + + return; +} + +/**************************************************************************** + * Name: arm_usbuninitialize + ****************************************************************************/ + +void arm_usbuninitialize(void) +{ + struct imxrt_usbdev_s *priv = &g_usbdev; + irqstate_t flags; + + usbtrace(TRACE_DEVUNINIT, 0); + + if (priv->driver) + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_DRIVERREGISTERED), 0); + usbdev_unregister(priv->driver); + } + + flags = enter_critical_section(); + + /* Disconnect device */ + + imxrt_pullup(&priv->usbdev, false); + priv->usbdev.speed = USB_SPEED_UNKNOWN; + + /* Disable and detach IRQs */ + + up_disable_irq(IMXRT_IRQ_USBOTG2); + irq_detach(IMXRT_IRQ_USBOTG2); + + /* Reset the controller */ + + imxrt_setbits (USBDEV_USBCMD_RST, IMXRT_USBDEV_USBCMD); + while (imxrt_getreg (IMXRT_USBDEV_USBCMD) & USBDEV_USBCMD_RST) + ; + + /* Turn off USB power and clocking */ + + /* Power down the PHY */ + + imxrt_putreg(0xffffffff, IMXRT_USBPHY2_PWD); + + /* Stop clock + * NOTE: This will interfere with USB OTG 2 and should probably be removed + * if Device or Host code is expanded to support both OTG Cores. + */ + + imxrt_clockoff_usboh3(); + + 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(IMXRT_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } + + if (g_usbdev.driver) + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_DRIVER), 0); + return -EBUSY; + } +#endif + + /* First hook up the driver */ + + g_usbdev.driver = driver; + + /* Then bind the class driver */ + + ret = CLASS_BIND(driver, &g_usbdev.usbdev); + if (ret) + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_BINDFAILED), (uint16_t)-ret); + g_usbdev.driver = NULL; + } + else + { + /* Enable USB controller interrupts */ + + up_enable_irq(IMXRT_IRQ_USBOTG2); + } + + 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) +{ + usbtrace(TRACE_DEVUNREGISTER, 0); + +#ifdef CONFIG_DEBUG_FEATURES + if (driver != g_usbdev.driver) + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + + /* Unbind the class driver */ + + CLASS_UNBIND(driver, &g_usbdev.usbdev); + + /* Disable USB controller interrupts */ + + up_disable_irq(IMXRT_IRQ_USBOTG2); + + /* Unhook the driver */ + + g_usbdev.driver = NULL; + return OK; +} + From e5fe777270363b7a9dac00057ee7932ee6c7df32 Mon Sep 17 00:00:00 2001 From: wgzAIIT <820906721@qq.com> Date: Fri, 1 Apr 2022 10:02:06 +0800 Subject: [PATCH 3/7] Revert "support usb on xidatong" This reverts commit 82f6abb842f0f40e842d8786953a87ab2e3075ae. --- .../aiit_board/xidatong/include/board.h | 1 - .../aiit_board/xidatong/src/imxrt_usbhost.c | 2 +- .../arm/src/imxrt/hardware/imxrt_usbotg.h | 739 --- .../arm/src/imxrt/hardware/imxrt_usbphy.h | 82 - .../hardware/rt105x/imxrt105x_memorymap.h | 311 - .../arch/arm/src/imxrt/imxrt_clockconfig.c | 698 --- .../nuttx/arch/arm/src/imxrt/imxrt_ehci.c | 5452 ----------------- .../nuttx/arch/arm/src/imxrt/imxrt_lowputc.c | 603 -- .../nuttx/arch/arm/src/imxrt/imxrt_lpi2c.c | 1997 ------ .../nuttx/arch/arm/src/imxrt/imxrt_lpspi.c | 1706 ------ .../nuttx/arch/arm/src/imxrt/imxrt_usbdev.c | 3061 --------- 11 files changed, 1 insertion(+), 14651 deletions(-) delete mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbotg.h delete mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbphy.h delete mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/rt105x/imxrt105x_memorymap.h delete mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_clockconfig.c delete mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_ehci.c delete mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lowputc.c delete mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpi2c.c delete mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpspi.c delete mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_usbdev.c diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/include/board.h b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/include/board.h index 49181683a..e6021288c 100644 --- a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/include/board.h +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/include/board.h @@ -128,7 +128,6 @@ #define IMXRT_SYS_PLL_SELECT CCM_ANALOG_PLL_SYS_DIV_SELECT_22 #define IMXRT_USB1_PLL_DIV_SELECT CCM_ANALOG_PLL_USB1_DIV_SELECT_20 -#define IMXRT_USB2_PLL_DIV_SELECT CCM_ANALOG_PLL_USB2_DIV_SELECT_20 // #define BOARD_CPU_FREQUENCY \ // (BOARD_XTAL_FREQUENCY * (IMXRT_ARM_PLL_DIV_SELECT / 2)) / IMXRT_ARM_PODF_DIVIDER diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/src/imxrt_usbhost.c b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/src/imxrt_usbhost.c index 46cc10e63..335a3f687 100644 --- a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/src/imxrt_usbhost.c +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/src/imxrt_usbhost.c @@ -138,7 +138,7 @@ int imxrt_usbhost_initialize(void) /* Make sure we don't accidentally switch on USB bus power */ - *((uint32_t *)IMXRT_USBNC_USB_OTG2_CTRL) = USBNC_PWR_POL; + *((uint32_t *)IMXRT_USBNC_USB_OTG1_CTRL) = USBNC_PWR_POL; *((uint32_t *)0x400d9030) = (1 << 21); *((uint32_t *)0x400d9000) = 0; diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbotg.h b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbotg.h deleted file mode 100644 index 3d83733ce..000000000 --- a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbotg.h +++ /dev/null @@ -1,739 +0,0 @@ -/**************************************************************************** - * arch/arm/src/imxrt/hardware/imxrt_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_ARM_SRC_IMXRT_HARDWARE_IMXRT_USBOTG_H -#define __ARCH_ARM_SRC_IMXRT_HARDWARE_IMXRT_USBOTG_H - -/**************************************************************************** - * Included Files - ****************************************************************************/ - -#include - -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -#define IMXRT_EHCI_NRHPORT 1 /* There is only a single root hub port */ - -/* USBOTG register offsets (with respect to IMXRT_USB_BASE) *****************/ - - /* 0x000 - 0x0ff: Reserved */ - -/* Device/host capability registers */ - -#define IMXRT_USBOTG_HCCR_OFFSET 0x100 /* Offset to EHCI Host Controller Capabiliy registers */ -#define IMXRT_USBOTG_CAPLENGTH_OFFSET 0x100 /* Capability register length (8-bit) */ -#define IMXRT_USBHOST_HCIVERSION_OFFSET 0x102 /* Host interface version number (16-bit) */ -#define IMXRT_USBHOST_HCSPARAMS_OFFSET 0x104 /* Host controller structural parameters */ -#define IMXRT_USBHOST_HCCPARAMS_OFFSET 0x108 /* Host controller capability parameters */ -#define IMXRT_USBDEV_DCIVERSION_OFFSET 0x120 /* Device interface version number */ -#define IMXRT_USBDEV_DCCPARAMS_OFFSET 0x124 /* Device controller capability parameters */ - -/* Device/host/OTG operational registers */ - -#define IMXRT_USBOTG_HCOR_OFFSET 0x140 /* Offset to EHCI Host Controller Operational Registers */ -#define IMXRT_USBOTG_USBCMD_OFFSET 0x140 /* USB command (both) */ -#define IMXRT_USBOTG_USBSTS_OFFSET 0x144 /* USB status (both) */ -#define IMXRT_USBOTG_USBINTR_OFFSET 0x148 /* USB interrupt enable (both) */ -#define IMXRT_USBOTG_FRINDEX_OFFSET 0x14c /* USB frame index (both) */ - /* EHCI 4G Segment Selector (not supported) */ -#define IMXRT_USBOTG_PERIODICLIST_OFFSET 0x154 /* Frame list base address (host) */ -#define IMXRT_USBOTG_DEVICEADDR_OFFSET 0x154 /* USB device address (device) */ -#define IMXRT_USBOTG_ASYNCLISTADDR_OFFSET 0x158 /* Next asynchronous list address (host) */ -#define IMXRT_USBOTG_ENDPOINTLIST_OFFSET 0x158 /* Address of endpoint list in memory (device) */ -#define IMXRT_USBOTG_TTCTRL_OFFSET 0x15c /* Asynchronous buffer status for embedded TT (host) */ -#define IMXRT_USBOTG_BURSTSIZE_OFFSET 0x160 /* Programmable burst size (both) */ -#define IMXRT_USBOTG_TXFILLTUNING_OFFSET 0x164 /* Host transmit pre-buffer packet tuning (host) */ -#define IMXRT_USBOTG_BINTERVAL_OFFSET 0x174 /* Length of virtual frame (both) */ -#define IMXRT_USBOTG_ENDPTNAK_OFFSET 0x178 /* Endpoint NAK (device) */ -#define IMXRT_USBOTG_ENDPTNAKEN_OFFSET 0x17c /* Endpoint NAK Enable (device) */ -#define IMXRT_USBOTG_CONFIGFLAG_OFFSET 0x180 /* Configured flag register (not used in lpc313x) */ -#define IMXRT_USBOTG_PORTSC1_OFFSET 0x184 /* Port status/control 1 (both) */ -#define IMXRT_USBOTG_OTGSC_OFFSET 0x1a4 /* OTG status and control (otg) */ -#define IMXRT_USBOTG_USBMODE_OFFSET 0x1a8 /* USB device mode (both) */ - -#define IMXRT_USBDEV_USBCMD_OFFSET 0x140 /* USB command (both) */ -#define IMXRT_USBDEV_USBSTS_OFFSET 0x144 /* USB status (both) */ -#define IMXRT_USBDEV_USBINTR_OFFSET 0x148 /* USB interrupt enable (both) */ -#define IMXRT_USBDEV_FRINDEX_OFFSET 0x14c /* USB frame index (both) */ -#define IMXRT_USBDEV_DEVICEADDR_OFFSET 0x154 /* USB device address (device) */ -#define IMXRT_USBDEV_ENDPOINTLIST_OFFSET 0x158 /* Address of endpoint list in memory (device) */ -#define IMXRT_USBDEV_BURSTSIZE_OFFSET 0x160 /* Programmable burst size (both) */ -#define IMXRT_USBDEV_BINTERVAL_OFFSET 0x174 /* Length of virtual frame (both) */ -#define IMXRT_USBDEV_ENDPTNAK_OFFSET 0x178 /* Endpoint NAK (device) */ -#define IMXRT_USBDEV_ENDPTNAKEN_OFFSET 0x17c /* Endpoint NAK Enable (device) */ -#define IMXRT_USBDEV_PORTSC1_OFFSET 0x184 /* Port status/control 1 (both) */ -#define IMXRT_USBDEV_USBMODE_OFFSET 0x1a8 /* USB device mode (both) */ - -#define IMXRT_USBHOST_USBCMD_OFFSET 0x140 /* USB command (both) */ -#define IMXRT_USBHOST_USBSTS_OFFSET 0x144 /* USB status (both) */ -#define IMXRT_USBHOST_USBINTR_OFFSET 0x148 /* USB interrupt enable (both) */ -#define IMXRT_USBHOST_FRINDEX_OFFSET 0x14c /* USB frame index (both) */ -#define IMXRT_USBHOST_PERIODICLIST_OFFSET 0x154 /* Frame list base address (host) */ -#define IMXRT_USBHOST_ASYNCLISTADDR_OFFSET 0x158 /* Next asynchronous list address (host) */ -#define IMXRT_USBHOST_TTCTRL_OFFSET 0x15c /* Asynchronous buffer status for embedded TT (host) */ -#define IMXRT_USBHOST_BURSTSIZE_OFFSET 0x160 /* Programmable burst size (both) */ -#define IMXRT_USBHOST_TXFILLTUNING_OFFSET 0x164 /* Host transmit pre-buffer packet tuning (host) */ -#define IMXRT_USBHOST_BINTERVAL_OFFSET 0x174 /* Length of virtual frame (both) */ -#define IMXRT_USBHOST_PORTSC1_OFFSET 0x184 /* Port status/control 1 (both) */ -#define IMXRT_USBHOST_USBMODE_OFFSET 0x1a8 /* USB device mode (both) */ - -/* Device endpoint registers */ - -#define IMXRT_USBDEV_ENDPTSETUPSTAT_OFFSET 0x1ac /* Endpoint setup status */ -#define IMXRT_USBDEV_ENDPTPRIME_OFFSET 0x1b0 /* Endpoint initialization */ -#define IMXRT_USBDEV_ENDPTFLUSH_OFFSET 0x1b4 /* Endpoint de-initialization */ -#define IMXRT_USBDEV_ENDPTSTATUS_OFFSET 0x1b8 /* Endpoint status */ -#define IMXRT_USBDEV_ENDPTCOMPLETE_OFFSET 0x1bc /* Endpoint complete */ - -#define IMXRT_USBDEV_ENDPTCTRL_OFFSET(n) (IMXRT_USBDEV_ENDPTCTRL0_OFFSET + ((n) * 4)) -#define IMXRT_USBDEV_ENDPTCTRL0_OFFSET 0x1c0 /* Endpoint control 0 */ -#define IMXRT_USBDEV_ENDPTCTRL1_OFFSET 0x1c4 /* Endpoint control 1 */ -#define IMXRT_USBDEV_ENDPTCTRL2_OFFSET 0x1c8 /* Endpoint control 2 */ -#define IMXRT_USBDEV_ENDPTCTRL3_OFFSET 0x1cc /* Endpoint control 3 */ -#define IMXRT_USBDEV_ENDPTCTRL4_OFFSET 0x1d0 /* Endpoint control 4 */ -#define IMXRT_USBDEV_ENDPTCTRL5_OFFSET 0x1d4 /* Endpoint control 5 */ -#define IMXRT_USBDEV_ENDPTCTRL6_OFFSET 0x1d8 /* Endpoint control 6 */ -#define IMXRT_USBDEV_ENDPTCTRL7_OFFSET 0x1dc /* Endpoint control 7 */ - -/* USB Non-core memory map & register definition */ - -#define IMXRT_USBNC_USB_OTG1_CTRL_OFFSET 0x0800 /* OTG1 Control Register */ -#define IMXRT_USBNC_USB_OTG2_CTRL_OFFSET 0x0800 /* OTG2 Control Register */ -#define IMXRT_USBNC_USB_OTG1_PHY_CTRL_0_OFFSET 0x0818 /* OTG1 Phy Control Register */ -#define IMXRT_USBNC_USB_OTG2_PHY_CTRL_0_OFFSET 0x0818 /* OTG2 Phy Control Register */ - -/* USBOTG register (virtual) addresses **************************************/ - -/* Device/host capability registers */ - -#define IMXRT_USBOTG_HCCR_BASE (IMXRT_USB_BASE + IMXRT_USBOTG_HCCR_OFFSET) -#define IMXRT_USBOTG_CAPLENGTH (IMXRT_USB_BASE + IMXRT_USBOTG_CAPLENGTH_OFFSET) -#define IMXRT_USBHOST_HCIVERSION (IMXRT_USB_BASE + IMXRT_USBHOST_HCIVERSION_OFFSET) -#define IMXRT_USBHOST_HCSPARAMS (IMXRT_USB_BASE + IMXRT_USBHOST_HCSPARAMS_OFFSET) -#define IMXRT_USBHOST_HCCPARAMS (IMXRT_USB_BASE + IMXRT_USBHOST_HCCPARAMS_OFFSET) -#define IMXRT_USBDEV_DCIVERSION (IMXRT_USB_BASE + IMXRT_USBDEV_DCIVERSION_OFFSET) -#define IMXRT_USBDEV_DCCPARAMS (IMXRT_USB_BASE + IMXRT_USBDEV_DCCPARAMS_OFFSET) - -/* Device/host operational registers */ - -#define IMXRT_USBOTG_HCOR_BASE (IMXRT_USB_BASE + IMXRT_USBOTG_HCOR_OFFSET) -#define IMXRT_USBOTG_USBCMD (IMXRT_USB_BASE + IMXRT_USBOTG_USBCMD_OFFSET) -#define IMXRT_USBOTG_USBSTS (IMXRT_USB_BASE + IMXRT_USBOTG_USBSTS_OFFSET) -#define IMXRT_USBOTG_USBINTR (IMXRT_USB_BASE + IMXRT_USBOTG_USBINTR_OFFSET) -#define IMXRT_USBOTG_FRINDEX (IMXRT_USB_BASE + IMXRT_USBOTG_FRINDEX_OFFSET) -#define IMXRT_USBOTG_PERIODICLIST (IMXRT_USB_BASE + IMXRT_USBOTG_PERIODICLIST_OFFSET) -#define IMXRT_USBOTG_DEVICEADDR (IMXRT_USB_BASE + IMXRT_USBOTG_DEVICEADDR_OFFSET) -#define IMXRT_USBOTG_ASYNCLISTADDR (IMXRT_USB_BASE + IMXRT_USBOTG_ASYNCLISTADDR_OFFSET) -#define IMXRT_USBOTG_ENDPOINTLIST (IMXRT_USB_BASE + IMXRT_USBOTG_ENDPOINTLIST_OFFSET) -#define IMXRT_USBOTG_TTCTRL (IMXRT_USB_BASE + IMXRT_USBOTG_TTCTRL_OFFSET) -#define IMXRT_USBOTG_BURSTSIZE (IMXRT_USB_BASE + IMXRT_USBOTG_BURSTSIZE_OFFSET) -#define IMXRT_USBOTG_TXFILLTUNING (IMXRT_USB_BASE + IMXRT_USBOTG_TXFILLTUNING_OFFSET) -#define IMXRT_USBOTG_BINTERVAL (IMXRT_USB_BASE + IMXRT_USBOTG_BINTERVAL_OFFSET) -#define IMXRT_USBOTG_ENDPTNAK (IMXRT_USB_BASE + IMXRT_USBOTG_ENDPTNAK_OFFSET) -#define IMXRT_USBOTG_ENDPTNAKEN (IMXRT_USB_BASE + IMXRT_USBOTG_ENDPTNAKEN_OFFSET) -#define IMXRT_USBOTG_PORTSC1 (IMXRT_USB_BASE + IMXRT_USBOTG_PORTSC1_OFFSET) -#define IMXRT_USBOTG_OTGSC (IMXRT_USB_BASE + IMXRT_USBOTG_OTGSC_OFFSET) -#define IMXRT_USBOTG_USBMODE (IMXRT_USB_BASE + IMXRT_USBOTG_USBMODE_OFFSET) - -#define IMXRT_USBDEV_USBCMD (IMXRT_USB_BASE + IMXRT_USBDEV_USBCMD_OFFSET) -#define IMXRT_USBDEV_USBSTS (IMXRT_USB_BASE + IMXRT_USBDEV_USBSTS_OFFSET) -#define IMXRT_USBDEV_USBINTR (IMXRT_USB_BASE + IMXRT_USBDEV_USBINTR_OFFSET) -#define IMXRT_USBDEV_FRINDEX (IMXRT_USB_BASE + IMXRT_USBDEV_FRINDEX_OFFSET) -#define IMXRT_USBDEV_DEVICEADDR (IMXRT_USB_BASE + IMXRT_USBDEV_DEVICEADDR_OFFSET) -#define IMXRT_USBDEV_ENDPOINTLIST (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPOINTLIST_OFFSET) -#define IMXRT_USBDEV_BURSTSIZE (IMXRT_USB_BASE + IMXRT_USBDEV_BURSTSIZE_OFFSET) -#define IMXRT_USBDEV_BINTERVAL (IMXRT_USB_BASE + IMXRT_USBDEV_BINTERVAL_OFFSET) -#define IMXRT_USBDEV_ENDPTNAK (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTNAK_OFFSET) -#define IMXRT_USBDEV_ENDPTNAKEN (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTNAKEN_OFFSET) -#define IMXRT_USBDEV_PORTSC1 (IMXRT_USB_BASE + IMXRT_USBDEV_PORTSC1_OFFSET) -#define IMXRT_USBDEV_USBMODE (IMXRT_USB_BASE + IMXRT_USBDEV_USBMODE_OFFSET) - -#define IMXRT_USBHOST_USBCMD (IMXRT_USB_BASE + IMXRT_USBHOST_USBCMD_OFFSET) -#define IMXRT_USBHOST_USBSTS (IMXRT_USB_BASE + IMXRT_USBHOST_USBSTS_OFFSET) -#define IMXRT_USBHOST_USBINTR (IMXRT_USB_BASE + IMXRT_USBHOST_USBINTR_OFFSET) -#define IMXRT_USBHOST_FRINDEX (IMXRT_USB_BASE + IMXRT_USBHOST_FRINDEX_OFFSET) -#define IMXRT_USBHOST_PERIODICLIST (IMXRT_USB_BASE + IMXRT_USBHOST_PERIODICLIST_OFFSET) -#define IMXRT_USBHOST_ASYNCLISTADDR (IMXRT_USB_BASE + IMXRT_USBHOST_ASYNCLISTADDR_OFFSET) -#define IMXRT_USBHOST_TTCTRL (IMXRT_USB_BASE + IMXRT_USBHOST_TTCTRL_OFFSET) -#define IMXRT_USBHOST_BURSTSIZE (IMXRT_USB_BASE + IMXRT_USBHOST_BURSTSIZE_OFFSET) -#define IMXRT_USBHOST_TXFILLTUNING (IMXRT_USB_BASE + IMXRT_USBHOST_TXFILLTUNING_OFFSET) -#define IMXRT_USBHOST_BINTERVAL (IMXRT_USB_BASE + IMXRT_USBHOST_BINTERVAL_OFFSET) -#define IMXRT_USBHOST_PORTSC1 (IMXRT_USB_BASE + IMXRT_USBHOST_PORTSC1_OFFSET) -#define IMXRT_USBHOST_USBMODE (IMXRT_USB_BASE + IMXRT_USBHOST_USBMODE_OFFSET) - -/* Device endpoint registers */ - -#define IMXRT_USBDEV_ENDPTSETUPSTAT (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTSETUPSTAT_OFFSET) -#define IMXRT_USBDEV_ENDPTPRIME (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTPRIME_OFFSET) -#define IMXRT_USBDEV_ENDPTFLUSH (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTFLUSH_OFFSET) -#define IMXRT_USBDEV_ENDPTSTATUS (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTSTATUS_OFFSET) -#define IMXRT_USBDEV_ENDPTCOMPLETE (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCOMPLETE_OFFSET) - -#define IMXRT_USBDEV_ENDPTCTRL(n) (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCTRL_OFFSET(n)) -#define IMXRT_USBDEV_ENDPTCTRL0 (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCTRL0_OFFSET) -#define IMXRT_USBDEV_ENDPTCTRL1 (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCTRL1_OFFSET) -#define IMXRT_USBDEV_ENDPTCTRL2 (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCTRL2_OFFSET) -#define IMXRT_USBDEV_ENDPTCTRL3 (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCTRL3_OFFSET) -#define IMXRT_USBDEV_ENDPTCTRL4 (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCTRL4_OFFSET) -#define IMXRT_USBDEV_ENDPTCTRL5 (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCTRL5_OFFSET) -#define IMXRT_USBDEV_ENDPTCTRL6 (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCTRL6_OFFSET) -#define IMXRT_USBDEV_ENDPTCTRL7 (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCTRL7_OFFSET) - -/* Device non-core registers */ - -#define IMXRT_USBNC_USB_OTG1_CTRL (IMXRT_USB_BASE + IMXRT_USBNC_USB_OTG1_CTRL_OFFSET) -#define IMXRT_USBNC_USB_OTG1_PHY_CTRL_0 (IMXRT_USB_BASE + IMXRT_USBNC_USB_OTG1_PHY_CTRL_0_OFFSET) - -#define IMXRT_USBNC_USB_OTG2_CTRL (IMXRT_USB_BASE + IMXRT_USBNC_USB_OTG2_CTRL_OFFSET) -#define IMXRT_USBNC_USB_OTG2_PHY_CTRL_0 (IMXRT_USB_BASE + IMXRT_USBNC_USB_OTG2_PHY_CTRL_0_OFFSET) - -/* 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 (15 << 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 (15) /* Bits 12-15: Number of Companion Controller */ -#define USBHOST_HCSPARAMS_NCC_MASK (15 << USBHOST_HCSPARAMS_NCC_SHIFT) -#define USBHOST_HCSPARAMS_NPCC_SHIFT (8) /* Bits 8-11: Number of Ports per Companion Controller */ -#define USBHOST_HCSPARAMS_NPCC_MASK (15 << 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 (15 << USBHOST_HCSPARAMS_NPORTS_SHIFT) - -/* HCCPARAMS */ - -#define USBHOST_HCCPARAMS_EECP_SHIFT (8) /* Bits 8-15: EHCI Extended Capabilities Pointer */ -#define USBHOST_HCCPARAMS_EECP_MASK (255 << USBHOST_HCCPARAMS_EECP_SHIFT) -#define USBHOST_HCCPARAMS_IST_SHIFT (4) /* Bits 4-7: Isochronous Scheduling Threshold */ -#define USBHOST_HCCPARAMS_IST_MASK (15 << 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 (31 << 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 (255 << 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 (255 << 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 (3 << 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_UPIA (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 (7 << 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 (0x3c << 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_PERBASE_SHIFT (12) /* Bits 12-31: Base Address (Low) */ -#define USBHOST_PERIODICLIST_PERBASE_MASK (0x000fffff << USBHOST_PERIODICLIST_PERBASE_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 (0x001fffff << USBDEV_ENDPOINTLIST_EPBASE_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 TT Control register TTCTRL (address 0x1900015c) -- Host Mode */ - -#define USBHOST_TTCTRL_TTHA_SHIFT (24) /* Bits 24-30: Hub address */ -#define USBHOST_TTCTRL_TTHA_MASK (0x7f << USBHOST_TTCTRL_TTHA_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 (255 << USBDEV_BURSTSIZE_TXPBURST_SHIFT) -#define USBDEV_BURSTSIZE_RXPBURST_SHIFT (0) /* Bits 0-7: RXPBURST Programmable RX burst length */ -#define USBDEV_BURSTSIZE_RXPBURST_MASK (255 << USBDEV_BURSTSIZE_RXPBURST_SHIFT) - -#define USBHOST_BURSTSIZE_TXPBURST_SHIFT (8) /* Bits 8-15: Programmable TX burst length */ -#define USBHOST_BURSTSIZE_TXPBURST_MASK (255 << USBHOST_BURSTSIZE_TXPBURST_SHIFT) -#define USBHOST_BURSTSIZE_RXPBURST_SHIFT (0) /* Bits 0-7: RXPBURST Programmable RX burst length */ -#define USBHOST_BURSTSIZE_RXPBURST_MASK (255 << 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 (0x3c << 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 (0xff << USBHOST_TXFILLTUNING_SCHOH_SHIFT) - -/* USB BINTERVAL register BINTERVAL -- Device/Host Mode */ - -#define USBDEV_BINTERVAL_SHIFT (0) /* Bits 0-3: bInterval value */ -#define USBDEV_BINTERVAL_MASK (15 << USBDEV_BINTERVAL_SHIFT) - -#define USBHOST_BINTERVAL_SHIFT (0) /* Bits 0-3: bInterval value */ -#define USBHOST_BINTERVAL_MASK (15 << USBHOST_BINTERVAL_SHIFT) - -/* USB endpoint NAK register ENDPTNAK -- Device Mode */ - -#define USBDEV_ENDPTNAK_EPTN_SHIFT (16) /* Bits 16-19: Tx endpoint NAK */ -#define USBDEV_ENDPTNAK_EPTN_MASK (15 << USBDEV_ENDPTNAK_EPTN_SHIFT) -#define USBDEV_ENDPTNAK_EPRN_SHIFT (0) /* Bits 0-3: Rx endpoint NAK */ -#define USBDEV_ENDPTNAK_EPRN_MASK (15 << USBDEV_ENDPTNAK_EPRN_SHIFT) - -/* USB Endpoint NAK Enable register ENDPTNAKEN -- Device Mode */ - -#define USBDEV_ENDPTNAK_EPTNE_SHIFT (16) /* Bits 16-19: Tx endpoint NAK enable */ -#define USBDEV_ENDPTNAK_EPTNE_MASK (15 << USBDEV_ENDPTNAK_EPTNE_SHIFT) -#define USBDEV_ENDPTNAK_EPRNE_SHIFT (0) /* Bits 0-3: Rx endpoint NAK enable */ -#define USBDEV_ENDPTNAK_EPRNE_MASK (15 << USBDEV_ENDPTNAK_EPRNE_SHIFT) - -/* Port Status and Control register PRTSC1 -- Device Mode */ - -#define USBDEV_PRTSC1_PSPD_SHIFT (26) /* Bits 26-27: Port speed */ -#define USBDEV_PRTSC1_PSPD_MASK (3 << 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 (15 << 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 (3 << 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 (3 << 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 (15 << 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 (3 << 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 (3 << 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_HABA (1 << 7) /* Bit 7: Hardware assist B-disconnect to A-connect */ -#define USBOTG_OTGSC_HADP (1 << 6) /* Bit 6: Hardware assist data pulse */ -#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_HAAR (1 << 2) /* Bit 2: Hardware assist auto_reset */ -#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 (3 << 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 -- Device Mode */ - -#define USBHOST_USBMODE_VBPS (1 << 5) /* Bit 5: VBUS power select */ -#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 (3 << 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 (3 << 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 (3 << 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 (3 << 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 (3 << 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 */ - -/* Bits 0-6: - * Reserved - */ -#define USBNC_OVER_CUR_DIS (1 << 7) /* Bit 7: Disable Over current detection */ -#define USBNC_OVER_CUR_POL (1 << 8) /* Bit 8: Polarity of over current */ -#define USBNC_PWR_POL (1 << 9) /* Bit 9: Power polarity */ -#define USBNC_WIE (1<< 10) /* Bit 10: Wake up interrupt enable */ - /* Bit 11-13: Reserved */ -#define USBNC_WKUP_SW_EN (1 << 14) /* Bit 14: Software wake up enable */ -#define USBNC_WKUP_SW (1 << 15) /* Bit 15: Software wake up */ -#define USBNC_WKUP_ID_EN (1 << 16) /* Bit 16: Wakeup on ID change enable */ -#define USBNC_WKUP_VBUS_EN (1 << 17) /* Bit 17: Wakeup on VBUS change enable */ - /* Bit 18-28: Reserved */ -#define USBNC_WKUP_DPDM_EN (1 << 29) /* Bit 29: Wakeup on DPDM change enable */ - /* Bit 30: Reserved */ -#define USBNC_WIR (1 << 31) /* Bit 31: Wake up interrupt request */ - -#endif /* __ARCH_ARM_SRC_IMXRT_HARDWARE_IMXRT_USBOTG_H */ diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbphy.h b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbphy.h deleted file mode 100644 index 0628a19e2..000000000 --- a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbphy.h +++ /dev/null @@ -1,82 +0,0 @@ -/**************************************************************************** - * arch/arm/src/imxrt/hardware/imxrt_usbphy.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_ARM_SRC_IMXRT_HARDWARE_IMXRT_USB_PHY_H -#define __ARCH_ARM_SRC_IMXRT_HARDWARE_IMXRT_USB_PHY_H - -/**************************************************************************** - * Included Files - ****************************************************************************/ - -#include -#include "hardware/imxrt_memorymap.h" - -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -#define IMXRT_USBPHY1_BASE_OFFSET 0x1000 /* USB PHY1 Base */ -#define IMXRT_USBPHY2_BASE_OFFSET 0x2000 /* USB PHY2 Base */ - -#define IMXRT_USBPHY1_BASE (IMXRT_ANATOP_BASE + IMXRT_USBPHY1_BASE_OFFSET) /* USB PHY1 Base */ -#define IMXRT_USBPHY2_BASE (IMXRT_ANATOP_BASE + IMXRT_USBPHY2_BASE_OFFSET) /* USB PHY2 Base */ - -/* Register Offsets *********************************************************/ - -#define IMXRT_USBPHY1_PWD_OFFSET 0x0000 /* USBPHY1 USB PHY1 Power-Down Register */ -#define IMXRT_USBPHY1_PWD_CLR_OFFSET 0x0008 /* USBPHY1 USB PHY1 Power-Down Register Clear */ -#define IMXRT_USBPHY1_CTRL_OFFSET 0x0030 /* USBPHY1 USB PHY1 General Control Register */ -#define IMXRT_USBPHY1_CTRL_CLR_OFFSET 0x0038 /* USBPHY1 USB PHY1 General Control Register Clear */ - -#define IMXRT_USBPHY2_PWD_OFFSET 0x0000 /* USBPHY2 USB PHY Power-Down Register */ -#define IMXRT_USBPHY2_PWD_CLR_OFFSET 0x0008 /* USBPHY2 USB PHY Power-Down Register Clear */ -#define IMXRT_USBPHY2_CTRL_OFFSET 0x0030 /* USBPHY2 USB PHY General Control Register */ -#define IMXRT_USBPHY2_CTRL_CLR_OFFSET 0x0038 /* USBPHY2 USB PHY General Control Register Clear */ - -/* Register addresses *******************************************************/ - -#define IMXRT_USBPHY1_PWD (IMXRT_USBPHY1_BASE + IMXRT_USBPHY1_PWD_OFFSET) /* USBPHY1 USB PHY1 Power-Down Register */ -#define IMXRT_USBPHY1_PWD_CLR (IMXRT_USBPHY1_BASE + IMXRT_USBPHY1_PWD_CLR_OFFSET) /* USBPHY1 USB PHY1 Power-Down Register Clear */ -#define IMXRT_USBPHY1_CTRL (IMXRT_USBPHY1_BASE + IMXRT_USBPHY1_CTRL_OFFSET) /* USBPHY1 USB PHY1 General Control Register */ -#define IMXRT_USBPHY1_CTRL_CLR (IMXRT_USBPHY1_BASE + IMXRT_USBPHY1_CTRL_CLR_OFFSET) /* USBPHY1 USB PHY1 General Control Register Clear */ - -#define IMXRT_USBPHY2_PWD (IMXRT_USBPHY2_BASE + IMXRT_USBPHY2_PWD_OFFSET) /* USBPHY2 USB PHY Power-Down Register */ -#define IMXRT_USBPHY2_PWD_CLR (IMXRT_USBPHY2_BASE + IMXRT_USBPHY2_PWD_CLR_OFFSET) /* USBPHY2 USB PHY Power-Down Register Clear */ -#define IMXRT_USBPHY2_CTRL (IMXRT_USBPHY2_BASE + IMXRT_USBPHY2_CTRL_OFFSET) /* USBPHY2 USB PHY General Control Register */ -#define IMXRT_USBPHY2_CTRL_CLR (IMXRT_USBPHY2_BASE + IMXRT_USBPHY2_CTRL_CLR_OFFSET) /* USBPHY2 USB PHY General Control Register Clear */ - -/* Register Bit Definitions *************************************************/ - -/* USB PHY Power-Down Register */ - -#define USBPHY_PWD_RXPWDRX (1 << 20) /* Bit 20: Power-down the entire USB PHY receiver block except for the full-speed differential receiver. */ -#define USBPHY_PWD_RXPWDDIFF (1 << 19) /* Bit 19: Power-down the USB high-speed differential receiver. */ -#define USBPHY_PWD_RXPWD1PT1 (1 << 18) /* Bit 18: Power-down the USB full-speed differential receiver. */ -#define USBPHY_PWD_RXPWDENV (1 << 17) /* Bit 17: Power-down the USB high-speed receiver envelope detector (squelch signal). */ -#define USBPHY_PWD_TXPWDV2I (1 << 12) /* Bit 12: Power-down the USB PHY transmit V-to-I converter and the current mirror. */ -#define USBPHY_PWD_TXPWDIBIAS (1 << 11) /* Bit 11: Power-down the USB PHY current bias block for the transmitter. */ -#define USBPHY_PWD_TXPWDFS (1 << 10) /* Bit 10: Power-down the USB full-speed drivers. */ - -/* USB PHY General Control Register */ - -#define USBPHY_CTRL_SFTRST (1 << 31) /* Bit 31: Soft-reset */ -#define USBPHY_CTRL_CLKGATE (1 << 30) /* Bit 30: Gate UTMI clocks */ - -#endif /* __ARCH_ARM_SRC_IMXRT_HARDWARE_IMXRT_USB_PHY_H */ diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/rt105x/imxrt105x_memorymap.h b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/rt105x/imxrt105x_memorymap.h deleted file mode 100644 index dc42ac2e8..000000000 --- a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/rt105x/imxrt105x_memorymap.h +++ /dev/null @@ -1,311 +0,0 @@ -/**************************************************************************** - * arch/arm/src/imxrt/hardware/rt105x/imxrt105x_memorymap.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_ARM_SRC_IMXRT_HARDWARE_IMXRT105X_MEMORYMAP_H -#define __ARCH_ARM_SRC_IMXRT_HARDWARE_IMXRT105X_MEMORYMAP_H - -/**************************************************************************** - * Included Files - ****************************************************************************/ - -#include - -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -/* System memory map */ - -#define IMXRT_ITCM_BASE 0x00000000 /* 512KB ITCM */ - - /* 0x00080000 512KB ITCM Reserved */ - - /* 0x00100000 1MB ITCM Reserved */ -#define IMXRT_ROMCP_BASE 0x00200000 /* 96KB ROMCP */ - - /* 0x00218000 416KB ROMCP Reserved */ - - /* 0x00280000 1536KB Reserved */ - - /* 0x00400000 124MB Reserved */ -#define IMXRT_FLEXSPI_BASE 0x08000000 /* 128MB FlexSPI (Aliased) */ -#define IMXRT_SEMCA_BASE 0x10000000 /* 256MB SEMC (Aliased) */ -#define IMXRT_DTCM_BASE 0x20000000 /* 512KB DTCM */ - - /* 0x20080000 512KB DTCM Reserved */ - - /* 0x20100000 1MB Reserved */ -#define IMXRT_OCRAM_BASE 0x20200000 /* 512KB OCRAM */ - - /* 0x20280000 1536KB OCRAM Reserved */ - - /* 0x20400000 252MB Reserved */ - - /* 0x30000000 256MB Reserved */ -#define IMXRT_AIPS1_BASE 0x40000000 /* 1MB AIPS-1 */ -#define IMXRT_AIPS2_BASE 0x40100000 /* 1MB AIPS-2 */ -#define IMXRT_AIPS3_BASE 0x40200000 /* 1MB AIPS-3 */ -#define IMXRT_AIPS4_BASE 0x40300000 /* 1MB AIPS-4 */ - - /* 40400000 12MB Reserved */ -#define IMXRT_MAINCNF_BASE 0x41000000 /* 1MB "main" configuration port */ -#define IMXRT_MCNF_BASE 0x41100000 /* 1MB "m" configuration port */ - - /* 41200000 1MB Reserved for "per" GPV */ - - /* 41300000 1MB Reserved for "ems" GPV */ -#define IMXRT_CPUCNF_BASE 0x41400000 /* 1MB "cpu" configuration port */ - - /* 0x41500000 1MB GPV Reserved */ - - /* 0x41600000 1MB GPV Reserved */ - - /* 0x41700000 1MB GPV Reserved */ - - /* 0x41800000 8MB Reserved */ - - /* 0x42000000 32MB Reserved */ - - /* 0x44000000 64MB Reserved */ - - /* 0x48000000 384MB Reserved */ -#define IMXRT_FLEXCIPHER_BASE 0x60000000 /* 504MB FlexSPI/ FlexSPI ciphertext */ -#define IMXRT_FLEXSPITX_BASE 0x7f800000 /* 4MB FlexSPI TX FIFO */ -#define IMXRT_FLEXSPIRX_BASE 0x7fc00000 /* 4MB FlexSPI RX FIFO */ -#define IMXRT_EXTMEM_BASE 0x80000000 /* 1.5GB SEMC external memories shared memory space */ -#define IMXRT_CM7_BASE 0xe0000000 /* 1MB CM7 PPB */ - - /* 0xe0100000 511MB Reserved */ - -/* AIPS-1 memory map */ - - /* 0x40000000 256KB Reserved */ - - /* 0x40040000 240KB Reserved */ -#define IMXRT_AIPS1CNF_BASE 0x4007c000 /* 6KB AIPS-1 Configuration */ -#define IMXRT_DCDC_BASE 0x40080000 /* 16KB DCDC */ -#define IMXRT_PIT_BASE 0x40084000 /* 16KB PIT */ - - /* 0x40088000 16KB Reserved */ - - /* 0x4008c000 16KB Reserved */ -#define IMXRT_MTR_BASE 0x40090000 /* 16KB MTR */ -#define IMXRT_ACMP_BASE 0x40094000 /* 16KB ACMP */ - - /* 0x40098000 16KB Reserved */ - - /* 0x4009c000 16KB Reserved */ - - /* 0x400a0000 16KB Reserved */ -#define IMXRT_IOMUXCSNVSGPR_BASE 0x400a4000 /* 16KB IOMUXC_SNVS_GPR */ -#define IMXRT_IOMUXCSNVS_BASE 0x400a8000 /* 16KB IOMUXC_SNVS */ -#define IMXRT_IOMUXCGPR_BASE 0x400ac000 /* 16KB IOMUXC_GPR */ -#define IMXRT_FLEXRAM_BASE 0x400b0000 /* 16KB CM7_MX6RT(FLEXRAM) */ -#define IMXRT_EWM_BASE 0x400b4000 /* 16KB EWM */ -#define IMXRT_WDOG1_BASE 0x400b8000 /* 16KB WDOG1 */ -#define IMXRT_WDOG3_BASE 0x400bc000 /* 16KB WDOG3 */ -#define IMXRT_GPIO5_BASE 0x400c0000 /* 16KB GPIO5 */ -#define IMXRT_ADC1_BASE 0x400c4000 /* 16KB ADC1 */ -#define IMXRT_ADC2_BASE 0x400c8000 /* 16KB ADC2 */ -#define IMXRT_TRNG_BASE 0x400cc000 /* 16KB TRNG */ -#define IMXRT_WDOG2_BASE 0x400d0000 /* 16KB WDOG2 */ -#define IMXRT_SNVSHP_BASE 0x400d4000 /* 16KB SNVS_HP */ -#define IMXRT_ANATOP_BASE 0x400d8000 /* 16KB ANATOP */ -#define IMXRT_CSU_BASE 0x400dc000 /* 16KB CSU */ - - /* 0x400e0000 16KB Reserved */ - - /* 0x400e4000 16KB Reserved */ -#define IMXRT_EDMA_BASE 0x400e8000 /* 16KB EDMA */ -#define IMXRT_DMAMUX_BASE 0x400ec000 /* 16KB DMA_CH_MUX */ - - /* 400f0000 16KB Reserved */ -#define IMXRT_GPC_BASE 0x400f4000 /* 16KB GPC */ -#define IMXRT_SRC_BASE 0x400f8000 /* 16KB SRC */ -#define IMXRT_CCM_BASE 0x400fc000 /* 16KB CCM */ - -/* AIPS-2 memory map */ - - /* 0x40100000 256KB Reserved */ - - /* 0x40140000 240KB Reserved */ -#define IMXRT_AIPS2CNF_BASE 0x4017c000 /* 16KB AIPS-2 Configuration */ -#define IMXRT_ROMCPC_BASE 0x40180000 /* 16KB ROMCP controller*/ -#define IMXRT_LPUART1_BASE 0x40184000 /* 16KB LPUART1 */ -#define IMXRT_LPUART2_BASE 0x40188000 /* 16KB LPUART2 */ -#define IMXRT_LPUART3_BASE 0x4018c000 /* 16KB LPUART3 */ -#define IMXRT_LPUART4_BASE 0x40190000 /* 16KB LPUART4 */ -#define IMXRT_LPUART5_BASE 0x40194000 /* 16KB LPUART5 */ -#define IMXRT_LPUART6_BASE 0x40198000 /* 16KB LPUART6 */ -#define IMXRT_LPUART7_BASE 0x4019c000 /* 16KB LPUART7 */ -#define IMXRT_LPUART8_BASE 0x401a0000 /* 16KB LPUART8 */ - - /* 0x401a4000 16KB Reserved */ - - /* 0x401a8000 16KB Reserved */ -#define IMXRT_FLEXIO1_BASE 0x401ac000 /* 16KB FlexIO1 */ -#define IMXRT_FLEXIO2_BASE 0x401b0000 /* 16KB FlexIO2 */ - - /* 0x401b4000 16KB Reserved */ -#define IMXRT_GPIO1_BASE 0x401b8000 /* 16KB GPIO1 */ -#define IMXRT_GPIO2_BASE 0x401bc000 /* 16KB GPIO2 */ -#define IMXRT_GPIO3_BASE 0x401c0000 /* 16KB GPIO3 */ -#define IMXRT_GPIO4_BASE 0x401c4000 /* 16KB GPIO4 */ - - /* 0x401c8000 16KB Reserved */ - - /* 0x401cc000 16KB Reserved */ -#define IMXRT_CAN1_BASE 0x401d0000 /* 16KB CAN1 */ -#define IMXRT_CAN2_BASE 0x401d4000 /* 16KB CAN2 */ - - /* 0x401d8000 16KB Reserved */ -#define IMXRT_QTIMER1_BASE 0x401dc000 /* 16KB QTimer1 */ -#define IMXRT_QTIMER2_BASE 0x401e0000 /* 16KB QTimer2 */ -#define IMXRT_QTIMER3_BASE 0x401e4000 /* 16KB QTimer3 */ -#define IMXRT_QTIMER4_BASE 0x401e8000 /* 16KB QTimer4 */ -#define IMXRT_GPT1_BASE 0x401ec000 /* 16KB GPT1 */ -#define IMXRT_GPT2_BASE 0x401f0000 /* 16KB GPT2 */ -#define IMXRT_OCOTP_BASE 0x401f4000 /* 16KB OCOTP */ -#define IMXRT_IOMUXC_BASE 0x401f8000 /* 16KB IOMUXC */ -#define IMXRT_KPP_BASE 0x401fc000 /* 16KB KPP */ - -/* AIPS-3 memory map */ - - /* 0x40200000 256KB Reserved */ - - /* 0x40240000 240KB Reserved */ -#define IMXRT_AIOS3CNF_BASE 0x4027c000 /* 16KB AIPS-3 Configuration */ - - /* 0x40280000 16KB Reserved */ - - /* 0x40284000 16KB Reserved */ - - /* 0x40288000 16KB Reserved */ - - /* 0x4028c000 16KB Reserved */ - - /* 0x40290000 16KB Reserved */ - - /* 0x40294000 16KB Reserved */ - - /* 0x40298000 16KB Reserved */ - - /* 0x4029c000 16KB Reserved */ - - /* 0x402a0000 16KB Reserved */ - - /* 0x402a4000 16KB Reserved */ -#define IMXRT_FLEXSPIC_BASE 0x402a8000 /* 16KB FlexSPI controller */ - - /* 0x402ac000 16KB Reserved */ - - /* 0x402b0000 16KB Reserved */ -#define IMXRT_PXP_BASE 0x402b4000 /* 16KB PXP */ -#define IMXRT_LCDIF_BASE 0x402b8000 /* 16KB LCDIF */ -#define IMXRT_CSI_BASE 0x402bc000 /* 16KB CSI */ -#define IMXRT_USDHC1_BASE 0x402c0000 /* 16KB USDHC1 */ -#define IMXRT_USDHC2_BASE 0x402c4000 /* 16KB USDHC2 */ - - /* 0x402c8000 16KB Reserved */ - - /* 0x402cc000 16KB Reserved */ - - /* 0x402d0000 16KB Reserved */ - - /* 0x402d4000 16KB Reserved */ -#define IMXRT_ENET_BASE 0x402d8000 /* 16KB ENET */ -#define IMXRT_USBPL301_BASE 0x402dc000 /* 16KB USB(PL301) */ -#define IMXRT_USB_BASE 0x402e0200 /* 16KB USB(USB) */ - - /* 0x402e4000 16KB Reserved */ - - /* 0x402e8000 16KB Reserved */ - - /* 0x402ec000 16KB Reserved */ -#define IMXRT_SEMC_BASE 0x402f0000 /* 16KB SEMC */ - - /* 0x402f4000 16KB Reserved */ - - /* 0x402f8000 16KB Reserved */ -#define IMXRT_DCP_BASE 0x402fc000 /* 16KB DCP */ - -/* AIPS-4 memory map */ - - /* 0x40300000 256KB Reserved */ - - /* 0x40340000 240KB Reserved */ -#define IMXRT_AIPS4CNF_BASE 0x4037c000 /* 16KB AIPS-4 Configuration */ -#define IMXRT_SPDIF_BASE 0x40380000 /* 16KB SPDIF */ -#define IMXRT_SAI1_BASE 0x40384000 /* 16KB SAI1 */ -#define IMXRT_SAI2_BASE 0x40388000 /* 16KB SAI2 */ -#define IMXRT_SAI3_BASE 0x4038c000 /* 16KB SAI3 */ - - /* 0x40390000 16KB Reserved */ -#define IMXRT_LPSPI1_BASE 0x40394000 /* 16KB LPSPI1 */ -#define IMXRT_LPSPI2_BASE 0x40398000 /* 16KB LPSPI2 */ -#define IMXRT_LPSPI3_BASE 0x4039c000 /* 16KB LPSPI3 */ -#define IMXRT_LPSPI4_BASE 0x403a0000 /* 16KB LPSPI4 */ - - /* 0x403a4000 16KB Reserved */ - - /* 0x403a8000 16KB Reserved */ - - /* 0x403ac000 16KB Reserved */ -#define IMXRT_ADCETC_BASE 0x403b0000 /* 16KB ADC_ETC */ -#define IMXRT_AOI1_BASE 0x403b4000 /* 16KB AOI1 */ -#define IMXRT_AOI2_BASE 0x403b8000 /* 16KB AOI2 */ -#define IMXRT_XBAR1_BASE 0x403bc000 /* 16KB XBAR1 */ -#define IMXRT_XBAR2_BASE 0x403c0000 /* 16KB XBAR2 */ -#define IMXRT_XBAR3_BASE 0x403c4000 /* 16KB XBAR3 */ -#define IMXRT_ENC1_BASE 0x403c8000 /* 16KB ENC1 */ -#define IMXRT_ENC2_BASE 0x403cc000 /* 16KB ENC2 */ -#define IMXRT_ENC3_BASE 0x403d0000 /* 16KB ENC3 */ -#define IMXRT_ENC4_BASE 0x403d4000 /* 16KB ENC4 */ - - /* 0x403d8000 16KB Reserved */ -#define IMXRT_FLEXPWM1_BASE 0x403dc000 /* 16KB FLEXPWM1 */ -#define IMXRT_FLEXPWM2_BASE 0x403e0000 /* 16KB FLEXPWM2 */ -#define IMXRT_FLEXPWM3_BASE 0x403e4000 /* 16KB FLEXPWM3 */ -#define IMXRT_FLEXPWM4_BASE 0x403e8000 /* 16KB FLEXPWM4 */ -#define IMXRT_BEE_BASE 0x403ec000 /* 16KB BEE */ -#define IMXRT_LPI2C1_BASE 0x403f0000 /* 16KB */ -#define IMXRT_LPI2C2_BASE 0x403f4000 /* 16KB LPI2C2 */ -#define IMXRT_LPI2C3_BASE 0x403f8000 /* 16KB LPI2C3 */ -#define IMXRT_LPI2C4_BASE 0x403fc000 /* 16KB LPI2C4 */ - -/* PPB memory map */ - -#define IMXRT_TPIU_BASE 0xe0040000 /* 4KB TPIU */ -#define IMXRT_ETM_BASE 0xe0041000 /* 4KB ETM */ -#define IMXRT_CTI_BASE 0xe0042000 /* 4KB CTI */ -#define IMXRT_TSGEN_BASE 0xe0043000 /* 4KB TSGEN */ -#define IMXRT_PPBRES_BASE 0xe0044000 /* 4KB PPB RES */ - - /* 0xe0045000 236KB PPB Reserved */ -#define IMXRT_MCM_BASE 0xe0080000 /* 4KB MCM */ - - /* 0xe0081000 444KB PPB Reserved */ - - /* 0xe00f0000 52KB PPB Reserved */ -#define IMXRT_SYSROM_BASE 0xe00fd000 /* 4KB SYS ROM */ -#define IMXRT_PROCROM_BASE 0xe00fe000 /* 4KB Processor ROM */ -#define IMXRT_PPBROM_BASE 0xe00ff000 /* 4KB PPB ROM */ - -#endif /* __ARCH_ARM_SRC_IMXRT_HARDWARE_IMXRT105X_MEMORYMAP_H */ diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_clockconfig.c b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_clockconfig.c deleted file mode 100644 index 97fef0a96..000000000 --- a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_clockconfig.c +++ /dev/null @@ -1,698 +0,0 @@ -/**************************************************************************** - * arch/arm/src/imxrt/imxrt_clockconfig.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 "arm_arch.h" -#include -#include "hardware/imxrt_ccm.h" -#include "hardware/imxrt_dcdc.h" -#include "imxrt_clockconfig.h" -#include "imxrt_lcd.h" -#include "hardware/imxrt_memorymap.h" -#include "hardware/imxrt_iomuxc.h" - -#include - -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -#define VIDEO_PLL_MIN_FREQ 650000000 -#define OSC24_FREQ 24000000 - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: imxrt_lcd_clockconfig - ****************************************************************************/ - -#ifdef CONFIG_IMXRT_LCD -static void imxrt_lcd_clockconfig(void) -{ - uint32_t reg; - uint32_t reg2; - - int post; - int pre; - - uint32_t numerator; - uint32_t denominator; - uint32_t post_divider; - uint32_t pre_divider; - uint32_t loop_divider; - uint32_t target_freq; - uint32_t freq_error; - - target_freq = (CONFIG_IMXRT_LCD_HWIDTH + - CONFIG_IMXRT_LCD_HPULSE + - CONFIG_IMXRT_LCD_HFRONTPORCH + - CONFIG_IMXRT_LCD_HBACKPORCH) * - (CONFIG_IMXRT_LCD_VHEIGHT + - CONFIG_IMXRT_LCD_VPULSE + - CONFIG_IMXRT_LCD_VFRONTPORCH + - CONFIG_IMXRT_LCD_VBACKPORCH) * - CONFIG_IMXRT_LCD_REFRESH_FREQ; - - for (post_divider = 1; post_divider < 16; post_divider <<= 1) - { - if (IMXRT_LCD_VIDEO_PLL_FREQ * post_divider >= VIDEO_PLL_MIN_FREQ) - { - break; - } - } - - loop_divider = (IMXRT_LCD_VIDEO_PLL_FREQ * post_divider) / OSC24_FREQ; - numerator = (IMXRT_LCD_VIDEO_PLL_FREQ * post_divider) - - (loop_divider * OSC24_FREQ); - denominator = OSC24_FREQ; - - /* Bypass PLL first */ - - modifyreg32(IMXRT_CCM_ANALOG_PLL_VIDEO, - CCM_ANALOG_PLL_VIDEO_BYPASS_CLK_SRC_MASK, - CCM_ANALOG_PLL_VIDEO_BYPASS | - CCM_ANALOG_PLL_VIDEO_BYPASS_CLK_SRC_REF_24M); - - putreg32(CCM_ANALOG_PLL_VIDEO_NUM_A(numerator), - IMXRT_CCM_ANALOG_PLL_VIDEO_NUM); - putreg32(CCM_ANALOG_PLL_VIDEO_DENOM_B(denominator), - IMXRT_CCM_ANALOG_PLL_VIDEO_DENOM); - - /* Set post divider: - * - * ------------------------------------------------------------------------ - * | config->postDivider | PLL_VIDEO[POST_DIV_SELECT] | MISC2[VIDEO_DIV] | - * ------------------------------------------------------------------------ - * | 1 | 2 | 0 | - * ------------------------------------------------------------------------ - * | 2 | 1 | 0 | - * ------------------------------------------------------------------------ - * | 4 | 2 | 3 | - * ------------------------------------------------------------------------ - * | 8 | 1 | 3 | - * ------------------------------------------------------------------------ - * | 16 | 0 | 3 | - * ------------------------------------------------------------------------ - */ - - reg = getreg32(IMXRT_CCM_ANALOG_PLL_VIDEO); - reg &= ~(CCM_ANALOG_PLL_VIDEO_DIV_SELECT_MASK | - CCM_ANALOG_PLL_VIDEO_POWERDOWN); - reg |= CCM_ANALOG_PLL_VIDEO_ENABLE | - CCM_ANALOG_PLL_VIDEO_DIV_SELECT(loop_divider); - - reg2 = getreg32(IMXRT_CCM_ANALOG_MISC2); - reg2 &= ~CCM_ANALOG_MISC2_VIDEO_DIV_MASK; - - switch (post_divider) - { - case 16: - reg |= CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT_DIV4; - reg2 |= CCM_ANALOG_MISC2_VIDEO_DIV(3); - break; - - case 8: - reg |= CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT_DIV2; - reg2 |= CCM_ANALOG_MISC2_VIDEO_DIV(3); - break; - - case 4: - reg |= CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT_DIV1; - reg2 |= CCM_ANALOG_MISC2_VIDEO_DIV(3); - break; - - case 2: - reg |= CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT_DIV2; - reg2 |= 0; - break; - - default: - reg |= CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT_DIV1; - reg2 |= 0; - break; - } - - putreg32(reg, IMXRT_CCM_ANALOG_PLL_VIDEO); - - putreg32(reg2, IMXRT_CCM_ANALOG_MISC2); - - while ((getreg32(IMXRT_CCM_ANALOG_PLL_VIDEO) & - CCM_ANALOG_PLL_VIDEO_LOCK) == 0) - { - } - - /* Disable Bypass */ - - modifyreg32(IMXRT_CCM_ANALOG_PLL_VIDEO, - CCM_ANALOG_PLL_VIDEO_BYPASS, - 0); - - freq_error = IMXRT_LCD_VIDEO_PLL_FREQ; - pre_divider = 0; - post_divider = 0; - - for (post = 0; post < 8; post++) - { - for (pre = 0; pre < 8; pre++) - { - int32_t temp_error; - temp_error = labs((post + 1) * (pre + 1) * target_freq - - IMXRT_LCD_VIDEO_PLL_FREQ); - if (temp_error < freq_error) - { - pre_divider = pre; - post_divider = post; - freq_error = temp_error; - } - } - } - - /* Select PLL5 as LCD Clock and set Pre divider. */ - - modifyreg32(IMXRT_CCM_CSCDR2, - CCM_CSCDR2_LCDIF_PRE_CLK_SEL_MASK | - CCM_CSCDR2_LCDIF_PRED_MASK, - CCM_CSCDR2_LCDIF_PRE_CLK_SEL_PLL5 | - CCM_CSCDR2_LCDIF_PRED(pre_divider)); - - /* Set Post divider. */ - - modifyreg32(IMXRT_CCM_CBCMR, CCM_CBCMR_LCDIF_PODF_MASK, - CCM_CBCMR_LCDIF_PODF(post_divider)); -} - -#endif - -/**************************************************************************** - * Name: imxrt_pllsetup - ****************************************************************************/ - -static void imxrt_pllsetup(void) -{ -#ifdef CONFIG_ARCH_FAMILY_IMXRT102x - uint32_t pll2reg; -#endif - uint32_t pll3reg; - uint32_t reg; - -#if (defined(CONFIG_ARCH_FAMILY_IMXRT105x) || defined (CONFIG_ARCH_FAMILY_IMXRT106x)) - /* Init Arm PLL1 */ - - reg = CCM_ANALOG_PLL_ARM_DIV_SELECT(IMXRT_ARM_PLL_DIV_SELECT) | - CCM_ANALOG_PLL_ARM_ENABLE; - putreg32(reg, IMXRT_CCM_ANALOG_PLL_ARM); - while ((getreg32(IMXRT_CCM_ANALOG_PLL_ARM) & CCM_ANALOG_PLL_ARM_LOCK) == 0) - { - } - - /* Init Sys PLL2 */ - - reg = CCM_ANALOG_PLL_SYS_DIV_SELECT(IMXRT_SYS_PLL_SELECT) | - CCM_ANALOG_PLL_SYS_ENABLE; - putreg32(reg, IMXRT_CCM_ANALOG_PLL_SYS); - while ((getreg32(IMXRT_CCM_ANALOG_PLL_SYS) & CCM_ANALOG_PLL_SYS_LOCK) == 0) - { - } - - /* Init USB PLL3 */ - - /* capture it's original value */ - - pll3reg = getreg32(IMXRT_CCM_ANALOG_PFD_480); - putreg32(pll3reg | - CCM_ANALOG_PFD_480_PFD0_CLKGATE | - CCM_ANALOG_PFD_480_PFD1_CLKGATE | - CCM_ANALOG_PFD_480_PFD2_CLKGATE | - CCM_ANALOG_PFD_480_PFD3_CLKGATE, - IMXRT_CCM_ANALOG_PFD_480); - - reg = IMXRT_USB2_PLL_DIV_SELECT | - CCM_ANALOG_PLL_USB2_ENABLE | - CCM_ANALOG_PLL_USB2_EN_USB_CLKS | - CCM_ANALOG_PLL_USB2_POWER; - putreg32(reg, IMXRT_CCM_ANALOG_PLL_USB2); - - while ((getreg32(IMXRT_CCM_ANALOG_PLL_USB2) & - CCM_ANALOG_PLL_USB2_LOCK) == 0) - { - } - - putreg32(pll3reg, IMXRT_CCM_ANALOG_PFD_480); - -#ifdef CONFIG_IMXRT_LCD - /* Init Video PLL5 */ - - imxrt_lcd_clockconfig(); -#endif - - /* Init ENET PLL6 */ - - reg = CCM_ANALOG_PLL_ENET_ENET0_DIV_SELECT_50MHZ | - CCM_ANALOG_PLL_ENET_ENET1_125M_EN | - CCM_ANALOG_PLL_ENET_ENET_25M_REF_EN | - CCM_ANALOG_PLL_ENET_ENET_500M_REF_EN | - CCM_ANALOG_PLL_ENET_ENET1_DIV_SELECT_50MHZ; - - putreg32(reg, IMXRT_CCM_ANALOG_PLL_ENET); - - while ((getreg32(IMXRT_CCM_ANALOG_PLL_ENET) & - CCM_ANALOG_PLL_ENET_LOCK) == 0) - { - } - -#elif defined(CONFIG_ARCH_FAMILY_IMXRT102x) - /* Init Sys PLL2 */ - - /* First reset its fractional dividers */ - - pll2reg = getreg32(IMXRT_CCM_ANALOG_PFD_528); - putreg32(pll2reg | - CCM_ANALOG_PFD_528_PFD0_CLKGATE | - CCM_ANALOG_PFD_528_PFD1_CLKGATE | - CCM_ANALOG_PFD_528_PFD2_CLKGATE | - CCM_ANALOG_PFD_528_PFD3_CLKGATE, - IMXRT_CCM_ANALOG_PFD_528); - - reg = CCM_ANALOG_PLL_SYS_DIV_SELECT(IMXRT_SYS_PLL_DIV_SELECT) | - CCM_ANALOG_PLL_SYS_ENABLE; - putreg32(reg, IMXRT_CCM_ANALOG_PLL_SYS); - - while ((getreg32(IMXRT_CCM_ANALOG_PLL_SYS) & - CCM_ANALOG_PLL_SYS_LOCK) == 0) - { - } - - putreg32(pll2reg, IMXRT_CCM_ANALOG_PFD_528); - - /* Init USB PLL3 */ - - /* capture it's original value */ - - pll3reg = getreg32(IMXRT_CCM_ANALOG_PFD_480); - putreg32(pll3reg | - CCM_ANALOG_PFD_480_PFD0_CLKGATE | - CCM_ANALOG_PFD_480_PFD1_CLKGATE | - CCM_ANALOG_PFD_480_PFD2_CLKGATE | - CCM_ANALOG_PFD_480_PFD3_CLKGATE, - IMXRT_CCM_ANALOG_PFD_480); - - reg = CCM_ANALOG_PLL_USB2_DIV_SELECT(IMXRT_USB2_PLL_DIV_SELECT) | - CCM_ANALOG_PLL_USB2_ENABLE | CCM_ANALOG_PLL_USB2_EN_USB_CLKS | - CCM_ANALOG_PLL_USB2_POWER; - putreg32(reg, IMXRT_CCM_ANALOG_PLL_USB2); - - while ((getreg32(IMXRT_CCM_ANALOG_PLL_USB2) & - CCM_ANALOG_PLL_USB2_LOCK) == 0) - { - } - - putreg32(pll3reg, IMXRT_CCM_ANALOG_PFD_480); - - /* Init Audio PLL4 */ - - reg = CCM_ANALOG_PLL_AUDIO_DIV_SELECT(IMXRT_AUDIO_PLL_DIV_SELECT) | - CCM_ANALOG_PLL_AUDIO_ENABLE; - putreg32(reg, IMXRT_CCM_ANALOG_PLL_AUDIO); - - while ((getreg32(IMXRT_CCM_ANALOG_PLL_AUDIO) & - CCM_ANALOG_PLL_AUDIO_LOCK) == 0) - { - } - - /* Init ENET PLL6 */ - - reg = CCM_ANALOG_PLL_ENET_ENET0_DIV_SELECT_50MHZ | - CCM_ANALOG_PLL_ENET_ENET1_125M_EN | - CCM_ANALOG_PLL_ENET_ENET_25M_REF_EN | - CCM_ANALOG_PLL_ENET_ENET_500M_REF_EN; - - putreg32(reg, IMXRT_CCM_ANALOG_PLL_ENET); - - while ((getreg32(IMXRT_CCM_ANALOG_PLL_ENET) & - CCM_ANALOG_PLL_ENET_LOCK) == 0) - { - } - -#else -# error Unrecognised IMXRT family member for clock config -#endif -} - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: imxrt_clockconfig - * - * Description: - * Called to initialize the i.MXRT. This does whatever setup is needed to - * put the SoC in a usable state. This includes the initialization of - * clocking using the settings in board.h. - * - ****************************************************************************/ - -void imxrt_clockconfig(void) -{ - /* Don't change the current basic clock configuration if we are running - * from SDRAM. In this case, some bootloader logic has already configured - * clocking and SDRAM. We are pretty much committed to using things the - * way that the bootloader has left them. - * - * Note that although this is safe at boot while nothing is using - * the clocks additional caution is required if at some later date - * we want to manipulate the PODFs while the system is running - * (for power minimisation) because changing those is not glitch free. - */ - -#ifndef CONFIG_IMXRT_BOOT_SDRAM - uint32_t reg; - - /* Set clock mux and dividers */ - - /* Set PERIPH_CLK2 MUX to OSC */ - - reg = getreg32(IMXRT_CCM_CBCMR); - reg &= ~CCM_CBCMR_PERIPH_CLK2_SEL_MASK; - reg |= CCM_CBCMR_PERIPH_CLK2_SEL_OSC_CLK; - putreg32(reg, IMXRT_CCM_CBCMR); - - while ((getreg32(IMXRT_CCM_CDHIPR) & CCM_CDHIPR_PERIPH2_CLK_SEL_BUSY) != 0) - { - } - - /* Set PERIPH_CLK MUX to PERIPH_CLK2 */ - - reg = getreg32(IMXRT_CCM_CBCDR); - reg &= ~CCM_CBCDR_PERIPH_CLK_SEL_MASK; - reg |= CCM_CBCDR_PERIPH_CLK_SEL(CCM_CBCDR_PERIPH_CLK_SEL_PERIPH_CLK2); - putreg32(reg, IMXRT_CCM_CBCDR); - while ((getreg32(IMXRT_CCM_CDHIPR) & CCM_CDHIPR_PERIPH_CLK_SEL_BUSY) != 0) - { - } - - /* Set Soc VDD and wait for it to stablise */ - - reg = getreg32(IMXRT_DCDC_REG3); - reg &= ~(DCDC_REG3_TRG_MASK); - reg |= DCDC_REG3_TRG(IMXRT_VDD_SOC); - putreg32(reg, IMXRT_DCDC_REG3); - while ((getreg32(IMXRT_DCDC_REG0) & DCDC_REG0_STS_DC_OK) == 0) - { - } - - /* OK, now nothing is depending on us, configure the PLLs */ - - imxrt_pllsetup(); - - /* Set Dividers */ - - reg = getreg32(IMXRT_CCM_CACRR); - reg &= ~CCM_CACRR_ARM_PODF_MASK; - reg |= CCM_CACRR_ARM_PODF(CCM_PODF_FROM_DIVISOR(IMXRT_ARM_PODF_DIVIDER)); - putreg32(reg, IMXRT_CCM_CACRR); - while ((getreg32(IMXRT_CCM_CDHIPR) & CCM_CDHIPR_ARM_PODF_BUSY) != 0) - { - } - - reg = getreg32(IMXRT_CCM_CBCDR); - reg &= ~CCM_CBCDR_AHB_PODF_MASK; - reg |= CCM_CBCDR_AHB_PODF(CCM_PODF_FROM_DIVISOR(IMXRT_AHB_PODF_DIVIDER)); - putreg32(reg, IMXRT_CCM_CBCDR); - while ((getreg32(IMXRT_CCM_CDHIPR) & CCM_CDHIPR_AHB_PODF_BUSY) != 0) - { - } - - /* Adjust IPG and PERCLK PODFs. Consumers of these clocks will need to - * be gated if there are any (there aren't at boot). - */ - - reg = getreg32(IMXRT_CCM_CBCDR); - reg &= ~CCM_CBCDR_IPG_PODF_MASK; - reg |= CCM_CBCDR_IPG_PODF(CCM_PODF_FROM_DIVISOR(IMXRT_IPG_PODF_DIVIDER)); - putreg32(reg, IMXRT_CCM_CBCDR); - - reg = getreg32(IMXRT_CCM_CSCMR1); - reg &= ~CCM_CSCMR1_PERCLK_PODF_MASK; - reg |= CCM_CSCMR1_PERCLK_PODF( - CCM_PODF_FROM_DIVISOR(IMXRT_PERCLK_PODF_DIVIDER)); - putreg32(reg, IMXRT_CCM_CSCMR1); - -#ifndef CONFIG_IMXRT_SEMC_INIT_DONE - /* Configure SEMC Clock only if not already done by DCD SDR */ - - reg = getreg32(IMXRT_CCM_CBCDR); - reg &= ~CCM_CBCDR_SEMC_PODF_MASK; - reg |= CCM_CBCDR_SEMC_PODF(CCM_PODF_FROM_DIVISOR(IMXRT_SEMC_PODF_DIVIDER)); - putreg32(reg, IMXRT_CCM_CBCDR); - - while ((getreg32(IMXRT_CCM_CDHIPR) & CCM_CDHIPR_SEMC_PODF_BUSY) != 0) - { - } -#endif - - /* Set PRE_PERIPH_CLK to Board Selection */ - - reg = getreg32(IMXRT_CCM_CBCMR); - reg &= ~CCM_CBCMR_PRE_PERIPH_CLK_SEL_MASK; - reg |= CCM_CBCMR_PRE_PERIPH_CLK_SEL(IMXRT_PRE_PERIPH_CLK_SEL); - putreg32(reg, IMXRT_CCM_CBCMR); - - /* Set PERIPH_CLK MUX to Board Selection */ - - reg = getreg32(IMXRT_CCM_CBCDR); - reg &= ~CCM_CBCDR_PERIPH_CLK_SEL_MASK; - reg |= CCM_CBCDR_PERIPH_CLK_SEL(IMXRT_PERIPH_CLK_SEL); - putreg32(reg, IMXRT_CCM_CBCDR); - - /* Wait handshake */ - - while ((getreg32(IMXRT_CCM_CDHIPR) & CCM_CDHIPR_PERIPH_CLK_SEL_BUSY) != 0) - { - } - - /* Set PERCLK_CLK_SEL to Board Selection */ - - reg = getreg32(IMXRT_CCM_CSCMR1); - reg &= ~CCM_CSCMR1_PERCLK_CLK_SEL_MASK; - reg |= CCM_CSCMR1_PERCLK_CLK_SEL(IMXRT_PERCLK_CLK_SEL); - putreg32(reg, IMXRT_CCM_CSCMR1); - - while ((getreg32(IMXRT_CCM_CDHIPR) & CCM_CDHIPR_PERIPH_CLK_SEL_BUSY) != 0) - { - } - - /* Setup perhiperals. At this point these are not activated so don't - * need to worry too much about switching off the clock feeds. - */ - - /* Set UART source to PLL3 80M */ - - reg = getreg32(IMXRT_CCM_CSCDR1); - reg &= ~CCM_CSCDR1_UART_CLK_SEL; - reg |= CCM_CSCDR1_UART_CLK_SEL_PLL3_80; - putreg32(reg, IMXRT_CCM_CSCDR1); - - /* Set UART divider to 1 */ - - reg = getreg32(IMXRT_CCM_CSCDR1); - reg &= ~CCM_CSCDR1_UART_CLK_PODF_MASK; - reg |= CCM_CSCDR1_UART_CLK_PODF(CCM_PODF_FROM_DIVISOR(1)); - putreg32(reg, IMXRT_CCM_CSCDR1); - -#ifdef CONFIG_IMXRT_FLEXIO1 -#ifdef CONFIG_ARCH_FAMILY_IMXRT102x - /* Set FlEXIO1 source */ - - reg = getreg32(IMXRT_CCM_CSCMR2); - reg &= ~CCM_CSCMR2_FLEXIO1_CLK_SEL_MASK; - reg |= CCM_CSCMR2_FLEXIO1_CLK_SEL(CONFIG_FLEXIO1_CLK); - putreg32(reg, IMXRT_CCM_CSCMR2); - - /* Set FlEXIO1 divider */ - - reg = getreg32(IMXRT_CCM_CS1CDR); - reg &= ~(CCM_CS1CDR_FLEXIO1_CLK_PODF_MASK | \ - CCM_CS1CDR_FLEXIO1_CLK_PRED_MASK); - reg |= CCM_CS1CDR_FLEXIO1_CLK_PODF - (CCM_PODF_FROM_DIVISOR(CONFIG_FLEXIO1_PODF_DIVIDER)); - reg |= CCM_CS1CDR_FLEXIO1_CLK_PRED - (CCM_PRED_FROM_DIVISOR(CONFIG_FLEXIO1_PRED_DIVIDER)); - putreg32(reg, IMXRT_CCM_CS1CDR); - -#elif (defined(CONFIG_ARCH_FAMILY_IMXRT105x) || defined(CONFIG_ARCH_FAMILY_IMXRT106x)) - /* Set FlEXIO1 source & divider */ - - reg = getreg32(IMXRT_CCM_CDCDR); - reg &= ~(CCM_CDCDR_FLEXIO1_CLK_SEL_MASK | - CCM_CDCDR_FLEXIO1_CLK_PODF_MASK | - CCM_CDCDR_FLEXIO1_CLK_PRED_MASK); - reg |= CCM_CDCDR_FLEXIO1_CLK_SEL(CONFIG_FLEXIO1_CLK); - reg |= CCM_CDCDR_FLEXIO1_CLK_PODF - (CCM_PODF_FROM_DIVISOR(CONFIG_FLEXIO1_PODF_DIVIDER)); - reg |= CCM_CDCDR_FLEXIO1_CLK_PRED - (CCM_PRED_FROM_DIVISOR(CONFIG_FLEXIO1_PRED_DIVIDER)); - putreg32(reg, IMXRT_CCM_CDCDR); - -#endif /* CONFIG_ARCH_FAMILY_IMXRT102x */ -#endif /* CONFIG_IMXRT_FLEXIO1 */ - -#if (defined(CONFIG_IMXRT_FLEXIO2) || defined(CONFIG_IMXRT_FLEXIO3)) - /* Set FlEXIO2 source */ - - reg = getreg32(IMXRT_CCM_CSCMR2); - reg &= ~CCM_CSCMR2_FLEXIO2_CLK_SEL_MASK; - reg |= CCM_CSCMR2_FLEXIO2_CLK_SEL(CONFIG_FLEXIO2_CLK); - putreg32(reg, IMXRT_CCM_CSCMR2); - - /* Set FlEXIO2 divider */ - - reg = getreg32(IMXRT_CCM_CS1CDR); - reg &= ~(CCM_CS1CDR_FLEXIO2_CLK_PODF_MASK | \ - CCM_CS1CDR_FLEXIO2_CLK_PRED_MASK); - reg |= CCM_CS1CDR_FLEXIO2_CLK_PODF - (CCM_PODF_FROM_DIVISOR(CONFIG_FLEXIO2_PODF_DIVIDER)); - reg |= CCM_CS1CDR_FLEXIO2_CLK_PRED - (CCM_PRED_FROM_DIVISOR(CONFIG_FLEXIO2_PRED_DIVIDER)); - putreg32(reg, IMXRT_CCM_CS1CDR); - -#endif /* CONFIG_IMXRT_FLEXIO2 */ - -#ifdef CONFIG_IMXRT_LPI2C - /* Set LPI2C source to PLL3 60M */ - - reg = getreg32(IMXRT_CCM_CSCDR2); - reg &= ~CCM_CSCDR2_LPI2C_CLK_SEL; - reg |= IMXRT_LPI2C_CLK_SELECT; - putreg32(reg, IMXRT_CCM_CSCDR2); - - /* Set LPI2C divider to 5 for 12 MHz */ - - reg = getreg32(IMXRT_CCM_CSCDR2); - reg &= ~CCM_CSCDR2_LPI2C_CLK_PODF_MASK; - reg |= CCM_CSCDR2_LPI2C_CLK_PODF( - CCM_PODF_FROM_DIVISOR(IMXRT_LSI2C_PODF_DIVIDER) - ); - putreg32(reg, IMXRT_CCM_CSCDR2); - -#endif - -#ifdef CONFIG_IMXRT_FLEXCAN - /* Set FlexCAN clock source to PLL3 80M */ - - reg = getreg32(IMXRT_CCM_CSCMR2); - reg &= ~CCM_CSCMR2_CAN_CLK_SEL_MASK; - reg |= IMXRT_CAN_CLK_SELECT; - putreg32(reg, IMXRT_CCM_CSCMR2); - - /* Set FlexCAN dividet to 1 for 80 MHz */ - - reg = getreg32(IMXRT_CCM_CSCMR2); - reg &= ~CCM_CSCMR2_CAN_CLK_PODF_MASK; - reg |= CCM_CSCMR2_CAN_CLK_PODF( - CCM_PODF_FROM_DIVISOR(IMXRT_CAN_PODF_DIVIDER) - ); - putreg32(reg, IMXRT_CCM_CSCMR2); - -#endif - -#ifdef CONFIG_IMXRT_LPSPI - /* Set LPSPI clock source to PLL3 PFD0 */ - - reg = getreg32(IMXRT_CCM_CBCMR); - reg &= ~CCM_CBCMR_LPSPI_CLK_SEL_MASK; - reg |= IMXRT_LPSPI_CLK_SELECT; - putreg32(reg, IMXRT_CCM_CBCMR); - - /* Set LPSPI divider to IMXRT_LSPI_PODF_DIVIDER */ - - reg = getreg32(IMXRT_CCM_CBCMR); - reg &= ~CCM_CBCMR_LPSPI_PODF_MASK; - reg |= CCM_CBCMR_LPSPI_PODF( - CCM_PODF_FROM_DIVISOR(IMXRT_LSPI_PODF_DIVIDER) - ); - putreg32(reg, IMXRT_CCM_CBCMR); -#endif - -#ifdef IMXRT_TRACE_PODF_DIVIDER - /* Set TRACE clock source and speed */ - - reg = getreg32(IMXRT_CCM_CBCMR); - reg &= ~CCM_CBCMR_TRACE_CLK_SEL_MASK; - reg |= IMXRT_TRACE_CLK_SELECT; - putreg32(reg, IMXRT_CCM_CBCMR); - - reg = getreg32(IMXRT_CCM_CSCDR1); - reg &= ~CCM_CSCDR1_TRACE_PODF_MASK; - reg |= CCM_CSCDR1_TRACE_PODF( - CCM_PODF_FROM_DIVISOR(IMXRT_TRACE_PODF_DIVIDER)); - putreg32(reg, IMXRT_CCM_CSCDR1); -#endif - -#ifdef CONFIG_IMXRT_USDHC - /* Optionally set USDHC1 & 2 to generate clocks - * from IMXRT_USDHC1_CLK_SELECT - */ - - reg = getreg32(IMXRT_CCM_CSCMR1); - reg &= ~(CCM_CSCMR1_USDHC1_CLK_SEL | CCM_CSCMR1_USDHC2_CLK_SEL); -#if defined(IMXRT_USDHC1_CLK_SELECT) - reg |= IMXRT_USDHC1_CLK_SELECT; -#endif -#if defined(IMXRT_USDHC2_CLK_SELECT) - reg |= IMXRT_USDHC2_CLK_SELECT; -#endif - putreg32(reg, IMXRT_CCM_CSCMR1); - - /* Now divide down clocks by IMXRT_USDHC[1|2]_PODF_DIVIDER */ - - reg = getreg32(IMXRT_CCM_CSCDR1); - reg &= ~(CCM_CSCDR1_USDHC1_PODF_MASK | CCM_CSCDR1_USDHC2_PODF_MASK); -#if defined(IMXRT_USDHC1_PODF_DIVIDER) - reg |= CCM_CSCDR1_USDHC1_PODF( - CCM_PODF_FROM_DIVISOR(IMXRT_USDHC1_PODF_DIVIDER)); -#endif -#if defined(IMXRT_USDHC2_PODF_DIVIDER) - reg |= CCM_CSCDR1_USDHC2_PODF( - CCM_PODF_FROM_DIVISOR(IMXRT_USDHC2_PODF_DIVIDER)); -#endif - putreg32(reg, IMXRT_CCM_CSCDR1); -#endif - - /* Ensure platform memory clocks remain enabled in WFI */ - - reg = getreg32(IMXRT_CCM_CGPR); - reg |= CCM_CGPR_INT_MEM_CLK_LPM; - putreg32(reg, IMXRT_CCM_CGPR); - - /* Remain in run mode */ - - modifyreg32(IMXRT_CCM_CLPCR, - CCM_CLPCR_LPM_MASK, - CCM_CLPCR_LPM_RUN); -#endif -} diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_ehci.c b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_ehci.c deleted file mode 100644 index e5b28c3f0..000000000 --- a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_ehci.c +++ /dev/null @@ -1,5452 +0,0 @@ -/**************************************************************************** - * arch/arm/src/imxrt/imxrt_ehci.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 - -#include "arm_arch.h" -#include "chip.h" -#include "hardware/imxrt_usbotg.h" -#include "imxrt_periphclks.h" - -#if defined(CONFIG_IMXRT_USBOTG) && defined(CONFIG_USBHOST) - -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -/* Configuration ************************************************************/ - -/* Pre-requisites */ - -#if !defined(CONFIG_SCHED_WORKQUEUE) -# error Work queue support is required (CONFIG_SCHED_WORKQUEUE) -#elif !defined(CONFIG_SCHED_HPWORK) -# error Hi-priority work queue support is required (CONFIG_SCHED_HPWORK) -#endif - -/* Configurable number of Queue Head (QH) structures. The default is one per - * Root hub port plus one for EP0. - */ - -#ifndef CONFIG_IMXRT_EHCI_NQHS -# define CONFIG_IMXRT_EHCI_NQHS (IMXRT_EHCI_NRHPORT + 1) -#endif - -/* Configurable number of Queue Element Transfer Descriptor (qTDs). The - * default is one per root hub plus three from EP0. - */ - -#ifndef CONFIG_IMXRT_EHCI_NQTDS -# define CONFIG_IMXRT_EHCI_NQTDS (IMXRT_EHCI_NRHPORT + 3) -#endif - -/* Buffers must be aligned to the cache line size */ - -#define DCACHE_LINEMASK (ARMV7M_DCACHE_LINESIZE -1) - -/* Configurable size of a request/descriptor buffers */ - -#ifndef CONFIG_IMXRT_EHCI_BUFSIZE -# define CONFIG_IMXRT_EHCI_BUFSIZE 128 -#endif - -#define IMXRT_EHCI_BUFSIZE \ - ((CONFIG_IMXRT_EHCI_BUFSIZE + DCACHE_LINEMASK) & ~DCACHE_LINEMASK) - -/* Debug options */ - -#ifndef CONFIG_DEBUG_USB_INFO -# undef CONFIG_IMXRT_EHCI_REGDEBUG -#endif - -/* Isochronous transfers are not currently supported */ - -#undef CONFIG_USBHOST_ISOC_DISABLE -#define CONFIG_USBHOST_ISOC_DISABLE 1 - -/* Registers **************************************************************** - * Traditionally, NuttX specifies register locations using individual - * register offsets from a base address. That tradition is broken here and, - * instead, register blocks are represented as structures. This is done here - * because, in principle, EHCI operational register address may not be known - * at compile time; the operational registers lie at an offset specified in - * the 'caplength' byte of the Host Controller Capability Registers. - * - * However, for the case of the IMXRT EHCI, we know apriori that locations - * of these register blocks. - */ - -/* Host Controller Capability Registers */ - -#define HCCR ((struct ehci_hccr_s *)IMXRT_USBOTG_HCCR_BASE) - -/* Host Controller Operational Registers */ - -#define HCOR ((volatile struct ehci_hcor_s *)IMXRT_USBOTG_HCOR_BASE) - -/* Interrupts *************************************************************** - * This is the set of interrupts handled by this driver. - */ - -#define EHCI_HANDLED_INTS (EHCI_INT_USBINT | EHCI_INT_USBERRINT | \ - EHCI_INT_PORTSC | EHCI_INT_SYSERROR | \ - EHCI_INT_AAINT) - -/* The periodic frame list is a 4K-page aligned array of Frame List Link - * pointers. The length of the frame list may be programmable. The - * programmability of the periodic frame list is exported to system software - * via the HCCPARAMS register. If non-programmable, the length is 1024 - * elements. If programmable, the length can be selected by system software - * as one of 256, 512, or 1024 elements. - */ - -#define FRAME_LIST_SIZE 1024 - -/* DMA **********************************************************************/ - -/* For now, we are assuming an identity mapping between physical and virtual - * address spaces. - */ - -#define imxrt_physramaddr(a) (a) -#define imxrt_virtramaddr(a) (a) - -/* USB trace ****************************************************************/ - -#ifdef HAVE_USBHOST_TRACE -# define TR_FMT1 false -# define TR_FMT2 true - -# define TRENTRY(id,fmt1,string) {string} - -# define TRACE1_FIRST ((int)__TRACE1_BASEVALUE + 1) -# define TRACE1_INDEX(id) ((int)(id) - TRACE1_FIRST) -# define TRACE1_NSTRINGS TRACE1_INDEX(__TRACE1_NSTRINGS) - -# define TRACE2_FIRST ((int)__TRACE1_NSTRINGS + 1) -# define TRACE2_INDEX(id) ((int)(id) - TRACE2_FIRST) -# define TRACE2_NSTRINGS TRACE2_INDEX(__TRACE2_NSTRINGS) -#endif - -/* Port numbers */ - -#define RHPNDX(rh) ((rh)->hport.hport.port) -#define RHPORT(rh) (RHPNDX(rh)+1) - -/**************************************************************************** - * Private Types - ****************************************************************************/ - -/* Internal representation of the EHCI Queue Head (QH) */ - -struct imxrt_epinfo_s; -struct imxrt_qh_s -{ - /* Fields visible to hardware */ - - struct ehci_qh_s hw; /* Hardware representation of the queue head */ - - /* Internal fields used by the EHCI driver */ - - struct imxrt_epinfo_s *epinfo; /* Endpoint used for the transfer */ - uint32_t fqp; /* First qTD in the list (physical address) */ - uint8_t pad[8]; /* Padding to assure 32-byte alignment */ -}; - -/* Internal representation of the EHCI Queue Element Transfer Descriptor - * (qTD) - */ - -struct imxrt_qtd_s -{ - /* Fields visible to hardware */ - - struct ehci_qtd_s hw; /* Hardware representation of the queue head */ - - /* Internal fields used by the EHCI driver */ -}; - -/* The following is used to manage lists of free QHs and qTDs */ - -struct imxrt_list_s -{ - struct imxrt_list_s *flink; /* Link to next entry in the list - * Variable length entry data follows - */ -}; - -/* List traversal call-out functions */ - -typedef int (*foreach_qh_t)(struct imxrt_qh_s *qh, uint32_t **bp, - void *arg); -typedef int (*foreach_qtd_t)(struct imxrt_qtd_s *qtd, uint32_t **bp, - void *arg); - -/* This structure describes one endpoint. */ - -struct imxrt_epinfo_s -{ - uint8_t epno:7; /* Endpoint number */ - uint8_t dirin:1; /* 1:IN endpoint 0:OUT endpoint */ - uint8_t devaddr:7; /* Device address */ - uint8_t toggle:1; /* Next data toggle */ -#ifndef CONFIG_USBHOST_INT_DISABLE - uint8_t interval; /* Polling interval */ -#endif - uint8_t status; /* Retained token status bits (for debug purposes) */ - volatile bool iocwait; /* TRUE: Thread is waiting for transfer completion */ - uint16_t maxpacket:11; /* Maximum packet size */ - uint16_t xfrtype:2; /* See USB_EP_ATTR_XFER_* definitions in usb.h */ - uint16_t speed:2; /* See USB_*_SPEED definitions in ehci.h */ - int result; /* The result of the transfer */ - uint32_t xfrd; /* On completion, will hold the number of bytes transferred */ - sem_t iocsem; /* Semaphore used to wait for transfer completion */ -#ifdef CONFIG_USBHOST_ASYNCH - usbhost_asynch_t callback; /* Transfer complete callback */ - void *arg; /* Argument that accompanies the callback */ -#endif -}; - -/* This structure retains the state of one root hub port */ - -struct imxrt_rhport_s -{ - /* Common device fields. This must be the first thing defined in the - * structure so that it is possible to simply cast from struct usbhost_s - * to struct imxrt_rhport_s. - */ - - struct usbhost_driver_s drvr; - - /* Root hub port status */ - - volatile bool connected; /* Connected to device */ - volatile bool lowspeed; /* Low speed device attached */ - struct imxrt_epinfo_s ep0; /* EP0 endpoint info */ - - /* This is the hub port description understood by class drivers */ - - struct usbhost_roothubport_s hport; -}; - -/* This structure retains the overall state of the USB host controller */ - -struct imxrt_ehci_s -{ - volatile bool pscwait; /* TRUE: Thread is waiting for port status change event */ - - sem_t exclsem; /* Support mutually exclusive access */ - sem_t pscsem; /* Semaphore to wait for port status change events */ - - struct imxrt_epinfo_s ep0; /* Endpoint 0 */ - struct imxrt_list_s *qhfree; /* List of free Queue Head (QH) structures */ - struct imxrt_list_s *qtdfree; /* List of free Queue Element Transfer Descriptor (qTD) */ - struct work_s work; /* Supports interrupt bottom half */ - -#ifdef CONFIG_USBHOST_HUB - /* Used to pass external hub port events */ - - volatile struct usbhost_hubport_s *hport; -#endif - - /* Root hub ports */ - - struct imxrt_rhport_s rhport[IMXRT_EHCI_NRHPORT]; -}; - -#ifdef HAVE_USBHOST_TRACE -/* USB trace codes */ - -enum usbhost_trace1codes_e -{ - __TRACE1_BASEVALUE = 0, /* This will force the first value to be 1 */ - - EHCI_TRACE1_SYSTEMERROR, /* EHCI ERROR: System error */ - EHCI_TRACE1_QTDFOREACH_FAILED, /* EHCI ERROR: imxrt_qtd_foreach failed */ - EHCI_TRACE1_QHALLOC_FAILED, /* EHCI ERROR: Failed to allocate a QH */ - EHCI_TRACE1_BUFTOOBIG, /* EHCI ERROR: Buffer too big */ - EHCI_TRACE1_REQQTDALLOC_FAILED, /* EHCI ERROR: Failed to allocate request qTD */ - EHCI_TRACE1_ADDBPL_FAILED, /* EHCI ERROR: imxrt_qtd_addbpl failed */ - EHCI_TRACE1_DATAQTDALLOC_FAILED, /* EHCI ERROR: Failed to allocate data buffer qTD */ - EHCI_TRACE1_DEVDISCONNECTED, /* EHCI ERROR: Device disconnected */ - EHCI_TRACE1_QHCREATE_FAILED, /* EHCI ERROR: imxrt_qh_create failed */ - EHCI_TRACE1_QTDSETUP_FAILED, /* EHCI ERROR: imxrt_qtd_setupphase failed */ - - EHCI_TRACE1_QTDDATA_FAILED, /* EHCI ERROR: imxrt_qtd_dataphase failed */ - EHCI_TRACE1_QTDSTATUS_FAILED, /* EHCI ERROR: imxrt_qtd_statusphase failed */ - EHCI_TRACE1_TRANSFER_FAILED, /* EHCI ERROR: Transfer failed */ - EHCI_TRACE1_QHFOREACH_FAILED, /* EHCI ERROR: imxrt_qh_foreach failed: */ - EHCI_TRACE1_SYSERR_INTR, /* EHCI: Host System Error Interrupt */ - EHCI_TRACE1_USBERR_INTR, /* EHCI: USB Error Interrupt (USBERRINT) Interrupt */ - EHCI_TRACE1_EPALLOC_FAILED, /* EHCI ERROR: Failed to allocate EP info structure */ - EHCI_TRACE1_BADXFRTYPE, /* EHCI ERROR: Support for transfer type not implemented */ - EHCI_TRACE1_HCHALTED_TIMEOUT, /* EHCI ERROR: Timed out waiting for HCHalted */ - EHCI_TRACE1_QHPOOLALLOC_FAILED, /* EHCI ERROR: Failed to allocate the QH pool */ - - EHCI_TRACE1_QTDPOOLALLOC_FAILED, /* EHCI ERROR: Failed to allocate the qTD pool */ - EHCI_TRACE1_PERFLALLOC_FAILED, /* EHCI ERROR: Failed to allocate the periodic frame list */ - EHCI_TRACE1_RESET_FAILED, /* EHCI ERROR: imxrt_reset failed */ - EHCI_TRACE1_RUN_FAILED, /* EHCI ERROR: EHCI Failed to run */ - EHCI_TRACE1_IRQATTACH_FAILED, /* EHCI ERROR: Failed to attach IRQ */ - -#ifdef HAVE_USBHOST_TRACE_VERBOSE - EHCI_VTRACE1_PORTSC_CSC, /* EHCI Connect Status Change */ - EHCI_VTRACE1_PORTSC_CONNALREADY, /* EHCI Already connected */ - EHCI_VTRACE1_PORTSC_DISCALREADY, /* EHCI Already disconnected */ - EHCI_VTRACE1_TOPHALF, /* EHCI Interrupt top half */ - EHCI_VTRACE1_AAINTR, /* EHCI Async Advance Interrupt */ - - EHCI_VTRACE1_CLASSENUM, /* EHCI Hub port CLASS enumeration */ - EHCI_VTRACE1_USBINTR, /* EHCI USB Interrupt (USBINT) Interrupt */ - EHCI_VTRACE1_ENUM_DISCONN, /* EHCI Enumeration not connected */ - EHCI_VTRACE1_INITIALIZING, /* EHCI Initializing EHCI Stack */ - EHCI_VTRACE1_HCCPARAMS, /* EHCI HCCPARAMS */ - EHCI_VTRACE1_INIITIALIZED, /* EHCI USB EHCI Initialized */ -#endif - - __TRACE1_NSTRINGS, /* Separates the format 1 from the format 2 strings */ - - EHCI_TRACE2_EPSTALLED, /* EHCI EP Stalled */ - EHCI_TRACE2_EPIOERROR, /* EHCI ERROR: EP TOKEN */ - EHCI_TRACE2_CLASSENUM_FAILED, /* EHCI usbhost_enumerate() failed */ - -#ifdef HAVE_USBHOST_TRACE_VERBOSE - EHCI_VTRACE2_ASYNCXFR, /* EHCI Async transfer */ - EHCI_VTRACE2_INTRXFR, /* EHCI Interrupt Transfer */ - EHCI_VTRACE2_IOCCHECK, /* EHCI IOC */ - EHCI_VTRACE2_PORTSC, /* EHCI PORTSC */ - EHCI_VTRACE2_PORTSC_CONNECTED, /* EHCI RHPort connected */ - EHCI_VTRACE2_PORTSC_DISCONND, /* EHCI RHport disconnected */ - EHCI_VTRACE2_MONWAKEUP, /* EHCI RHPort connected wakeup */ - - EHCI_VTRACE2_EPALLOC, /* EHCI EPALLOC */ - EHCI_VTRACE2_CTRLINOUT, /* EHCI CTRLIN/OUT */ - EHCI_VTRACE2_HCIVERSION, /* EHCI HCIVERSION */ - EHCI_VTRACE2_HCSPARAMS, /* EHCI HCSPARAMS */ -#endif - - __TRACE2_NSTRINGS /* Total number of enumeration values */ -}; - -/* USB trace data structure */ - -struct imxrt_ehci_trace_s -{ -#if 0 - uint16_t id; - bool fmt2; -#endif - FAR const char *string; -}; - -#endif /* HAVE_USBHOST_TRACE */ - -/**************************************************************************** - * Private Function Prototypes - ****************************************************************************/ - -/* Register operations ******************************************************/ - -static uint16_t imxrt_read16(const uint8_t *addr); -static uint32_t imxrt_read32(const uint8_t *addr); -#if 0 /* Not used */ -static void imxrt_write16(uint16_t memval, uint8_t *addr); -static void imxrt_write32(uint32_t memval, uint8_t *addr); -#endif - -#ifdef CONFIG_ENDIAN_BIG -static uint16_t imxrt_swap16(uint16_t value); -static uint32_t imxrt_swap32(uint32_t value); -#else -# define imxrt_swap16(value) (value) -# define imxrt_swap32(value) (value) -#endif - -#ifdef CONFIG_IMXRT_EHCI_REGDEBUG -static void imxrt_printreg(volatile uint32_t *regaddr, uint32_t regval, - bool iswrite); -static void imxrt_checkreg(volatile uint32_t *regaddr, uint32_t regval, - bool iswrite); -static uint32_t imxrt_getreg(volatile uint32_t *regaddr); -static void imxrt_putreg(uint32_t regval, volatile uint32_t *regaddr); -#else -static inline uint32_t imxrt_getreg(volatile uint32_t *regaddr); -static inline void imxrt_putreg(uint32_t regval, volatile uint32_t *regaddr); -#endif -static int ehci_wait_usbsts(uint32_t maskbits, uint32_t donebits, - unsigned int delay); - -/* Semaphores ***************************************************************/ - -static int imxrt_takesem(sem_t *sem); -static int imxrt_takesem_noncancelable(sem_t *sem); -#define imxrt_givesem(s) nxsem_post(s); - -/* Allocators ***************************************************************/ - -static struct imxrt_qh_s *imxrt_qh_alloc(void); -static void imxrt_qh_free(struct imxrt_qh_s *qh); -static struct imxrt_qtd_s *imxrt_qtd_alloc(void); -static void imxrt_qtd_free(struct imxrt_qtd_s *qtd); - -/* List Management **********************************************************/ - -static int imxrt_qh_foreach(struct imxrt_qh_s *qh, uint32_t **bp, - foreach_qh_t handler, void *arg); -static int imxrt_qtd_foreach(struct imxrt_qh_s *qh, foreach_qtd_t handler, - void *arg); -static int imxrt_qtd_discard(struct imxrt_qtd_s *qtd, uint32_t **bp, - void *arg); -static int imxrt_qh_discard(struct imxrt_qh_s *qh); - -/* Cache Operations *********************************************************/ - -#if 0 /* Not used */ -static int imxrt_qtd_invalidate(struct imxrt_qtd_s *qtd, uint32_t **bp, - void *arg); -static int imxrt_qh_invalidate(struct imxrt_qh_s *qh); -#endif -static int imxrt_qtd_flush(struct imxrt_qtd_s *qtd, uint32_t **bp, - void *arg); -static int imxrt_qh_flush(struct imxrt_qh_s *qh); - -/* Endpoint Transfer Handling ***********************************************/ - -#ifdef CONFIG_IMXRT_EHCI_REGDEBUG -static void imxrt_qtd_print(struct imxrt_qtd_s *qtd); -static void imxrt_qh_print(struct imxrt_qh_s *qh); -static int imxrt_qtd_dump(struct imxrt_qtd_s *qtd, uint32_t **bp, void *arg); -static int imxrt_qh_dump(struct imxrt_qh_s *qh, uint32_t **bp, void *arg); -#else -# define imxrt_qtd_print(qtd) -# define imxrt_qh_print(qh) -# define imxrt_qtd_dump(qtd, bp, arg) OK -# define imxrt_qh_dump(qh, bp, arg) OK -#endif - -static inline uint8_t imxrt_ehci_speed(uint8_t usbspeed); -static int imxrt_ioc_setup(struct imxrt_rhport_s *rhport, - struct imxrt_epinfo_s *epinfo); -static int imxrt_ioc_wait(struct imxrt_epinfo_s *epinfo); -static void imxrt_qh_enqueue(struct imxrt_qh_s *qhead, - struct imxrt_qh_s *qh); -static struct imxrt_qh_s *imxrt_qh_create(struct imxrt_rhport_s *rhport, - struct imxrt_epinfo_s *epinfo); -static int imxrt_qtd_addbpl(struct imxrt_qtd_s *qtd, const void *buffer, - size_t buflen); -static struct imxrt_qtd_s *imxrt_qtd_setupphase( - struct imxrt_epinfo_s *epinfo, const struct usb_ctrlreq_s *req); -static struct imxrt_qtd_s *imxrt_qtd_dataphase(struct imxrt_epinfo_s *epinfo, - void *buffer, int buflen, uint32_t tokenbits); -static struct imxrt_qtd_s *imxrt_qtd_statusphase(uint32_t tokenbits); -static ssize_t imxrtimxrt_virtramaddr_async_setup( - struct imxrt_rhport_s *rhport, struct imxrt_epinfo_s *epinfo, - const struct usb_ctrlreq_s *req, uint8_t *buffer, size_t buflen); -#ifndef CONFIG_USBHOST_INT_DISABLE -static int imxrt_intr_setup(struct imxrt_rhport_s *rhport, - struct imxrt_epinfo_s *epinfo, uint8_t *buffer, size_t buflen); -#endif -static ssize_t imxrt_transfer_wait(struct imxrt_epinfo_s *epinfo); -#ifdef CONFIG_USBHOST_ASYNCH -static inline int imxrt_ioc_async_setup(struct imxrt_rhport_s *rhport, - struct imxrt_epinfo_s *epinfo, usbhost_asynch_t callback, - FAR void *arg); -static void imxrt_asynch_completion(struct imxrt_epinfo_s *epinfo); -#endif - -/* Interrupt Handling *******************************************************/ - -static int imxrt_qtd_ioccheck(struct imxrt_qtd_s *qtd, uint32_t **bp, - void *arg); -static int imxrt_qh_ioccheck(struct imxrt_qh_s *qh, uint32_t **bp, - void *arg); -#ifdef CONFIG_USBHOST_ASYNCH -static int imxrt_qtd_cancel(struct imxrt_qtd_s *qtd, uint32_t **bp, - void *arg); -static int imxrt_qh_cancel(struct imxrt_qh_s *qh, uint32_t **bp, void *arg); -#endif -static inline void imxrt_ioc_bottomhalf(void); -static inline void imxrt_portsc_bottomhalf(void); -static inline void imxrt_syserr_bottomhalf(void); -static inline void imxrt_async_advance_bottomhalf(void); -static void imxrt_ehci_bottomhalf(FAR void *arg); -static int imxrt_ehci_interrupt(int irq, FAR void *context, FAR void *arg); - -/* USB Host Controller Operations *******************************************/ - -static int imxrt_wait(FAR struct usbhost_connection_s *conn, - FAR struct usbhost_hubport_s **hport); -static int imxrt_rh_enumerate(FAR struct usbhost_connection_s *conn, - FAR struct usbhost_hubport_s *hport); -static int imxrt_enumerate(FAR struct usbhost_connection_s *conn, - FAR struct usbhost_hubport_s *hport); - -static int imxrt_ep0configure(FAR struct usbhost_driver_s *drvr, - usbhost_ep_t ep0, uint8_t funcaddr, uint8_t speed, - uint16_t maxpacketsize); -static int imxrt_epalloc(FAR struct usbhost_driver_s *drvr, - const FAR struct usbhost_epdesc_s *epdesc, usbhost_ep_t *ep); -static int imxrt_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep); -static int imxrt_alloc(FAR struct usbhost_driver_s *drvr, - FAR uint8_t **buffer, FAR size_t *maxlen); -static int imxrt_free(FAR struct usbhost_driver_s *drvr, - FAR uint8_t *buffer); -static int imxrt_ioalloc(FAR struct usbhost_driver_s *drvr, - FAR uint8_t **buffer, size_t buflen); -static int imxrt_iofree(FAR struct usbhost_driver_s *drvr, - FAR uint8_t *buffer); -static int imxrt_ctrlin(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, - FAR const struct usb_ctrlreq_s *req, FAR uint8_t *buffer); -static int imxrt_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, - FAR const struct usb_ctrlreq_s *req, FAR const uint8_t *buffer); -static ssize_t imxrt_transfer(FAR struct usbhost_driver_s *drvr, - usbhost_ep_t ep, FAR uint8_t *buffer, size_t buflen); -#ifdef CONFIG_USBHOST_ASYNCH -static int imxrt_asynch(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, - FAR uint8_t *buffer, size_t buflen, usbhost_asynch_t callback, - FAR void *arg); -#endif -static int imxrt_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep); -#ifdef CONFIG_USBHOST_HUB -static int imxrt_connect(FAR struct usbhost_driver_s *drvr, - FAR struct usbhost_hubport_s *hport, bool connected); -#endif -static void imxrt_disconnect(FAR struct usbhost_driver_s *drvr, - FAR struct usbhost_hubport_s *hport); - -/* Initialization ***********************************************************/ - -static int imxrt_reset(void); - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -/* In this driver implementation, support is provided for only a single a - * single USB device. All status information can be simply retained in a - * single global instance. - */ - -static struct imxrt_ehci_s g_ehci; - -/* This is the connection/enumeration interface */ - -static struct usbhost_connection_s g_ehciconn; - -/* Maps USB chapter 9 speed to EHCI speed */ - -static const uint8_t g_ehci_speed[4] = -{ - 0, EHCI_LOW_SPEED, EHCI_FULL_SPEED, EHCI_HIGH_SPEED -}; - -/* The head of the asynchronous queue */ - -static struct imxrt_qh_s g_asynchead aligned_data(32); - -#ifndef CONFIG_USBHOST_INT_DISABLE -/* The head of the periodic queue */ - -static struct imxrt_qh_s g_intrhead aligned_data(32); - -/* The frame list */ - -#ifdef CONFIG_IMXRT_EHCI_PREALLOCATE -static uint32_t g_framelist[FRAME_LIST_SIZE] aligned_data(4096); -#else -static uint32_t *g_framelist; -#endif -#endif /* CONFIG_USBHOST_INT_DISABLE */ - -#ifdef CONFIG_IMXRT_EHCI_PREALLOCATE -/* Pools of pre-allocated data structures. These will all be linked into the - * free lists within g_ehci. These must all be aligned to 32-byte boundaries - */ - -/* Queue Head (QH) pool */ - -static struct imxrt_qh_s g_qhpool[CONFIG_IMXRT_EHCI_NQHS] - aligned_data(32); - -/* Queue Element Transfer Descriptor (qTD) pool */ - -static struct imxrt_qtd_s g_qtdpool[CONFIG_IMXRT_EHCI_NQTDS] - aligned_data(32); - -#else -/* Pools of dynamically data structures. These will all be linked into the - * free lists within g_ehci. These must all be aligned to 32-byte boundaries - */ - -/* Queue Head (QH) pool */ - -static struct imxrt_qh_s *g_qhpool; - -/* Queue Element Transfer Descriptor (qTD) pool */ - -static struct imxrt_qtd_s *g_qtdpool; - -#endif - -#ifdef HAVE_USBHOST_TRACE -/* USB trace strings */ - -static const struct imxrt_ehci_trace_s g_trace1[TRACE1_NSTRINGS] = -{ - TRENTRY(EHCI_TRACE1_SYSTEMERROR, TR_FMT1, - "EHCI ERROR: System error: %06x\n"), - TRENTRY(EHCI_TRACE1_QTDFOREACH_FAILED, TR_FMT1, - "EHCI ERROR: imxrt_qtd_foreach failed: %d\n"), - TRENTRY(EHCI_TRACE1_QHALLOC_FAILED, TR_FMT1, - "EHCI ERROR: Failed to allocate a QH\n"), - TRENTRY(EHCI_TRACE1_BUFTOOBIG, TR_FMT1, - "EHCI ERROR: Buffer too big. Remaining %d\n"), - TRENTRY(EHCI_TRACE1_REQQTDALLOC_FAILED, TR_FMT1, - "EHCI ERROR: Failed to allocate request qTD"), - TRENTRY(EHCI_TRACE1_ADDBPL_FAILED, TR_FMT1, - "EHCI ERROR: imxrt_qtd_addbpl failed: %d\n"), - TRENTRY(EHCI_TRACE1_DATAQTDALLOC_FAILED, TR_FMT1, - "EHCI ERROR: Failed to allocate data buffer qTD, 0"), - TRENTRY(EHCI_TRACE1_DEVDISCONNECTED, TR_FMT1, - "EHCI ERROR: Device disconnected %d\n"), - TRENTRY(EHCI_TRACE1_QHCREATE_FAILED, TR_FMT1, - "EHCI ERROR: imxrt_qh_create failed\n"), - TRENTRY(EHCI_TRACE1_QTDSETUP_FAILED, TR_FMT1, - "EHCI ERROR: imxrt_qtd_setupphase failed\n"), - - TRENTRY(EHCI_TRACE1_QTDDATA_FAILED, TR_FMT1, - "EHCI ERROR: imxrt_qtd_dataphase failed\n"), - TRENTRY(EHCI_TRACE1_QTDSTATUS_FAILED, TR_FMT1, - "EHCI ERROR: imxrt_qtd_statusphase failed\n"), - TRENTRY(EHCI_TRACE1_TRANSFER_FAILED, TR_FMT1, - "EHCI ERROR: Transfer failed %d\n"), - TRENTRY(EHCI_TRACE1_QHFOREACH_FAILED, TR_FMT1, - "EHCI ERROR: imxrt_qh_foreach failed: %d\n"), - TRENTRY(EHCI_TRACE1_SYSERR_INTR, TR_FMT1, - "EHCI: Host System Error Interrupt\n"), - TRENTRY(EHCI_TRACE1_USBERR_INTR, TR_FMT1, - "EHCI: USB Error Interrupt (USBERRINT) Interrupt: %06x\n"), - TRENTRY(EHCI_TRACE1_EPALLOC_FAILED, TR_FMT1, - "EHCI ERROR: Failed to allocate EP info structure\n"), - TRENTRY(EHCI_TRACE1_BADXFRTYPE, TR_FMT1, - "EHCI ERROR: Support for transfer type %d not implemented\n"), - TRENTRY(EHCI_TRACE1_HCHALTED_TIMEOUT, TR_FMT1, - "EHCI ERROR: Timed out waiting for HCHalted. USBSTS: %06x\n"), - TRENTRY(EHCI_TRACE1_QHPOOLALLOC_FAILED, TR_FMT1, - "EHCI ERROR: Failed to allocate the QH pool\n"), - - TRENTRY(EHCI_TRACE1_QTDPOOLALLOC_FAILED, TR_FMT1, - "EHCI ERROR: Failed to allocate the qTD pool\n"), - TRENTRY(EHCI_TRACE1_PERFLALLOC_FAILED, TR_FMT1, - "EHCI ERROR: Failed to allocate the periodic frame list\n"), - TRENTRY(EHCI_TRACE1_RESET_FAILED, TR_FMT1, - "EHCI ERROR: imxrt_reset failed: %d\n"), - TRENTRY(EHCI_TRACE1_RUN_FAILED, TR_FMT1, - "EHCI ERROR: EHCI Failed to run: USBSTS=%06x\n"), - TRENTRY(EHCI_TRACE1_IRQATTACH_FAILED, TR_FMT1, - "EHCI ERROR: Failed to attach IRQ%d\n"), - -#ifdef HAVE_USBHOST_TRACE_VERBOSE - TRENTRY(EHCI_VTRACE1_PORTSC_CSC, TR_FMT1, - "EHCI Connect Status Change: %06x\n"), - TRENTRY(EHCI_VTRACE1_PORTSC_CONNALREADY, TR_FMT1, - "EHCI Already connected: %06x\n"), - TRENTRY(EHCI_VTRACE1_PORTSC_DISCALREADY, TR_FMT1, - "EHCI Already disconnected: %06x\n"), - TRENTRY(EHCI_VTRACE1_TOPHALF, TR_FMT1, - "EHCI Interrupt: %06x\n"), - TRENTRY(EHCI_VTRACE1_AAINTR, TR_FMT1, - "EHCI Async Advance Interrupt\n"), - - TRENTRY(EHCI_VTRACE1_CLASSENUM, TR_FMT1, - "EHCI Hub port %d: Enumerate the device\n"), - TRENTRY(EHCI_VTRACE1_USBINTR, TR_FMT1, - "EHCI USB Interrupt (USBINT) Interrupt: %06x\n"), - TRENTRY(EHCI_VTRACE1_ENUM_DISCONN, TR_FMT1, - "EHCI Enumeration not connected\n"), - TRENTRY(EHCI_VTRACE1_INITIALIZING, TR_FMT1, - "EHCI Initializing EHCI Stack\n"), - TRENTRY(EHCI_VTRACE1_HCCPARAMS, TR_FMT1, - "EHCI HCCPARAMS=%06x\n"), - TRENTRY(EHCI_VTRACE1_INIITIALIZED, TR_FMT1, - "EHCI USB EHCI Initialized\n"), -#endif -}; - -static const struct imxrt_ehci_trace_s g_trace2[TRACE2_NSTRINGS] = -{ - TRENTRY(EHCI_TRACE2_EPSTALLED, TR_FMT2, - "EHCI EP%d Stalled: TOKEN=%04x\n"), - TRENTRY(EHCI_TRACE2_EPIOERROR, TR_FMT2, - "EHCI ERROR: EP%d TOKEN=%04x\n"), - TRENTRY(EHCI_TRACE2_CLASSENUM_FAILED, TR_FMT2, - "EHCI Hub port %d usbhost_enumerate() failed: %d\n"), - -#ifdef HAVE_USBHOST_TRACE_VERBOSE - TRENTRY(EHCI_VTRACE2_ASYNCXFR, TR_FMT2, - "EHCI Async transfer EP%d buflen=%d\n"), - TRENTRY(EHCI_VTRACE2_INTRXFR, TR_FMT2, - "EHCI Intr Transfer EP%d buflen=%d\n"), - TRENTRY(EHCI_VTRACE2_IOCCHECK, TR_FMT2, - "EHCI IOC EP%d TOKEN=%04x\n"), - TRENTRY(EHCI_VTRACE2_PORTSC, TR_FMT2, - "EHCI PORTSC%d: %04x\n"), - TRENTRY(EHCI_VTRACE2_PORTSC_CONNECTED, TR_FMT2, - "EHCI RHPort%d connected, pscwait: %d\n"), - TRENTRY(EHCI_VTRACE2_PORTSC_DISCONND, TR_FMT2, - "EHCI RHport%d disconnected, pscwait: %d\n"), - TRENTRY(EHCI_VTRACE2_MONWAKEUP, TR_FMT2, - "EHCI RHPort%d connected: %d\n"), - - TRENTRY(EHCI_VTRACE2_EPALLOC, TR_FMT2, - "EHCI EPALLOC: EP%d TYPE=%d\n"), - TRENTRY(EHCI_VTRACE2_CTRLINOUT, TR_FMT2, - "EHCI CTRLIN/OUT: RHPort%d req: %02x\n"), - TRENTRY(EHCI_VTRACE2_HCIVERSION, TR_FMT2, - "EHCI HCIVERSION %x.%02x\n"), - TRENTRY(EHCI_VTRACE2_HCSPARAMS, TR_FMT2, - "EHCI nports=%d, HCSPARAMS=%04x\n"), -#endif -}; -#endif /* HAVE_USBHOST_TRACE */ - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: imxrt_read16 - * - * Description: - * Read 16-bit little endian data - * - ****************************************************************************/ - -static uint16_t imxrt_read16(const uint8_t *addr) -{ -#ifdef CONFIG_ENDIAN_BIG - return (uint16_t)addr[0] << 8 | (uint16_t)addr[1]; -#else - return (uint16_t)addr[1] << 8 | (uint16_t)addr[0]; -#endif -} - -/**************************************************************************** - * Name: imxrt_read32 - * - * Description: - * Read 32-bit little endian data - * - ****************************************************************************/ - -static inline uint32_t imxrt_read32(const uint8_t *addr) -{ -#ifdef CONFIG_ENDIAN_BIG - return (uint32_t)imxrt_read16(&addr[0]) << 16 | - (uint32_t)imxrt_read16(&addr[2]); -#else - return (uint32_t)imxrt_read16(&addr[2]) << 16 | - (uint32_t)imxrt_read16(&addr[0]); -#endif -} - -/**************************************************************************** - * Name: imxrt_write16 - * - * Description: - * Write 16-bit little endian data - * - ****************************************************************************/ - -#if 0 /* Not used */ -static void imxrt_write16(uint16_t memval, uint8_t *addr) -{ -#ifdef CONFIG_ENDIAN_BIG - addr[0] = memval & 0xff; - addr[1] = memval >> 8; -#else - addr[0] = memval >> 8; - addr[1] = memval & 0xff; -#endif -} -#endif - -/**************************************************************************** - * Name: imxrt_write32 - * - * Description: - * Write 32-bit little endian data - * - ****************************************************************************/ - -#if 0 /* Not used */ -static void imxrt_write32(uint32_t memval, uint8_t *addr) -{ -#ifdef CONFIG_ENDIAN_BIG - imxrt_write16(memval >> 16, &addr[0]); - imxrt_write16(memval & 0xffff, &addr[2]); -#else - imxrt_write16(memval & 0xffff, &addr[0]); - imxrt_write16(memval >> 16, &addr[2]); -#endif -} -#endif - -/**************************************************************************** - * Name: imxrt_swap16 - * - * Description: - * Swap bytes on a 16-bit value - * - ****************************************************************************/ - -#ifdef CONFIG_ENDIAN_BIG -static uint16_t imxrt_swap16(uint16_t value) -{ - return ((value >> 8) & 0xff) | ((value & 0xff) << 8); -} -#endif - -/**************************************************************************** - * Name: imxrt_swap32 - * - * Description: - * Swap bytes on a 32-bit value - * - ****************************************************************************/ - -#ifdef CONFIG_ENDIAN_BIG -static uint32_t imxrt_swap32(uint32_t value) -{ - return (uint32_t)imxrt_swap16((uint16_t)((value >> 16) & 0xffff)) | - (uint32_t)imxrt_swap16((uint16_t)(value & 0xffff)) << 16; -} -#endif - -/**************************************************************************** - * Name: imxrt_printreg - * - * Description: - * Print the contents of a IMXRT EHCI register - * - ****************************************************************************/ - -#ifdef CONFIG_IMXRT_EHCI_REGDEBUG -static void imxrt_printreg(volatile uint32_t *regaddr, uint32_t regval, - bool iswrite) -{ - uinfo("%08x%s%08x\n", (uintptr_t)regaddr, iswrite ? "<-" : "->", regval); -} -#endif - -/**************************************************************************** - * Name: imxrt_checkreg - * - * Description: - * Check if it is time to output debug information for accesses to a IMXRT - * EHCI register - * - ****************************************************************************/ - -#ifdef CONFIG_IMXRT_EHCI_REGDEBUG -static void imxrt_checkreg(volatile uint32_t *regaddr, uint32_t regval, - bool iswrite) -{ - static uint32_t *prevaddr = NULL; - static uint32_t preval = 0; - static uint32_t count = 0; - static bool prevwrite = false; - - /* Is this the same value that we read from/wrote to the same register last - * time? Are we polling the register? If so, suppress the output. - */ - - if (regaddr == prevaddr && regval == preval && prevwrite == iswrite) - { - /* Yes.. Just increment the count */ - - count++; - } - else - { - /* No this is a new address or value or operation. Were there any - * duplicate accesses before this one? - */ - - if (count > 0) - { - /* Yes.. Just one? */ - - if (count == 1) - { - /* Yes.. Just one */ - - imxrt_printreg(prevaddr, preval, prevwrite); - } - else - { - /* No.. More than one. */ - - uinfo("[repeats %d more times]\n", count); - } - } - - /* Save the new address, value, count, and operation for next time */ - - prevaddr = (uint32_t *)regaddr; - preval = regval; - count = 0; - prevwrite = iswrite; - - /* Show the new register access */ - - imxrt_printreg(regaddr, regval, iswrite); - } -} -#endif - -/**************************************************************************** - * Name: imxrt_getreg - * - * Description: - * Get the contents of an IMXRT register - * - ****************************************************************************/ - -#ifdef CONFIG_IMXRT_EHCI_REGDEBUG -static uint32_t imxrt_getreg(volatile uint32_t *regaddr) -{ - /* Read the value from the register */ - - uint32_t regval = *regaddr; - - /* Check if we need to print this value */ - - imxrt_checkreg(regaddr, regval, false); - return regval; -} -#else -static inline uint32_t imxrt_getreg(volatile uint32_t *regaddr) -{ - return *regaddr; -} -#endif - -/**************************************************************************** - * Name: imxrt_putreg - * - * Description: - * Set the contents of an IMXRT register to a value - * - ****************************************************************************/ - -#ifdef CONFIG_IMXRT_EHCI_REGDEBUG -static void imxrt_putreg(uint32_t regval, volatile uint32_t *regaddr) -{ - /* Check if we need to print this value */ - - imxrt_checkreg(regaddr, regval, true); - - /* Write the value */ - - *regaddr = regval; -} -#else -static inline void imxrt_putreg(uint32_t regval, volatile uint32_t *regaddr) -{ - *regaddr = regval; -} -#endif - -/**************************************************************************** - * Name: ehci_wait_usbsts - * - * Description: - * Wait for either (1) a field in the USBSTS register to take a specific - * value, (2) for a timeout to occur, or (3) a error to occur. Return - * a value to indicate which terminated the wait. - * - ****************************************************************************/ - -static int ehci_wait_usbsts(uint32_t maskbits, uint32_t donebits, - unsigned int delay) -{ - uint32_t regval; - unsigned int timeout; - - timeout = 0; - do - { - /* Wait 5usec before trying again */ - - up_udelay(5); - timeout += 5; - - /* Read the USBSTS register and check for a system error */ - - regval = imxrt_getreg(&HCOR->usbsts); - if ((regval & EHCI_INT_SYSERROR) != 0) - { - usbhost_trace1(EHCI_TRACE1_SYSTEMERROR, regval); - return -EIO; - } - - /* Mask out the bits of interest */ - - regval &= maskbits; - - /* Loop until the masked bits take the specified value or until a - * timeout occurs. - */ - } - while (regval != donebits && timeout < delay); - - /* We got here because either the waited for condition or a timeout - * occurred. Return a value to indicate which. - */ - - return (regval == donebits) ? OK : -ETIMEDOUT; -} - -/**************************************************************************** - * Name: imxrt_takesem - * - * Description: - * This is just a wrapper to handle the annoying behavior of semaphore - * waits that return due to the receipt of a signal. - * - ****************************************************************************/ - -static int imxrt_takesem(sem_t *sem) -{ - return nxsem_wait_uninterruptible(sem); -} - -/**************************************************************************** - * Name: imxrt_takesem_noncancelable - * - * Description: - * This is just a wrapper to handle the annoying behavior of semaphore - * waits that return due to the receipt of a signal. This version also - * ignores attempts to cancel the thread. - * - ****************************************************************************/ - -static int imxrt_takesem_noncancelable(sem_t *sem) -{ - int result; - int ret = OK; - - do - { - result = nxsem_wait_uninterruptible(sem); - - /* The only expected error is ECANCELED which would occur if the - * calling thread were canceled. - */ - - DEBUGASSERT(result == OK || result == -ECANCELED); - if (ret == OK && result < 0) - { - ret = result; - } - } - while (result < 0); - - return ret; -} - -/**************************************************************************** - * Name: imxrt_qh_alloc - * - * Description: - * Allocate a Queue Head (QH) structure by removing it from the free list - * - * Assumption: Caller holds the exclsem - * - ****************************************************************************/ - -static struct imxrt_qh_s *imxrt_qh_alloc(void) -{ - struct imxrt_qh_s *qh; - - /* Remove the QH structure from the freelist */ - - qh = (struct imxrt_qh_s *)g_ehci.qhfree; - if (qh) - { - g_ehci.qhfree = ((struct imxrt_list_s *)qh)->flink; - memset(qh, 0, sizeof(struct imxrt_qh_s)); - } - - return qh; -} - -/**************************************************************************** - * Name: imxrt_qh_free - * - * Description: - * Free a Queue Head (QH) structure by returning it to the free list - * - * Assumption: Caller holds the exclsem - * - ****************************************************************************/ - -static void imxrt_qh_free(struct imxrt_qh_s *qh) -{ - struct imxrt_list_s *entry = (struct imxrt_list_s *)qh; - - /* Put the QH structure back into the free list */ - - entry->flink = g_ehci.qhfree; - g_ehci.qhfree = entry; -} - -/**************************************************************************** - * Name: imxrt_qtd_alloc - * - * Description: - * Allocate a Queue Element Transfer Descriptor (qTD) by removing it from - * the free list - * - * Assumption: Caller holds the exclsem - * - ****************************************************************************/ - -static struct imxrt_qtd_s *imxrt_qtd_alloc(void) -{ - struct imxrt_qtd_s *qtd; - - /* Remove the qTD from the freelist */ - - qtd = (struct imxrt_qtd_s *)g_ehci.qtdfree; - if (qtd) - { - g_ehci.qtdfree = ((struct imxrt_list_s *)qtd)->flink; - memset(qtd, 0, sizeof(struct imxrt_qtd_s)); - } - - return qtd; -} - -/**************************************************************************** - * Name: imxrt_qtd_free - * - * Description: - * Free a Queue Element Transfer Descriptor (qTD) by returning it to the - * free list - * - * Assumption: - * Caller holds the exclsem - * - ****************************************************************************/ - -static void imxrt_qtd_free(struct imxrt_qtd_s *qtd) -{ - struct imxrt_list_s *entry = (struct imxrt_list_s *)qtd; - - /* Put the qTD back into the free list */ - - entry->flink = g_ehci.qtdfree; - g_ehci.qtdfree = entry; -} - -/**************************************************************************** - * Name: imxrt_qh_foreach - * - * Description: - * Give the first entry in a list of Queue Head (QH) structures, call the - * handler for each QH structure in the list (including the one at the head - * of the list). - * - ****************************************************************************/ - -static int imxrt_qh_foreach(struct imxrt_qh_s *qh, uint32_t **bp, - foreach_qh_t handler, void *arg) -{ - struct imxrt_qh_s *next; - uintptr_t physaddr; - int ret; - - DEBUGASSERT(qh && handler); - while (qh) - { - /* Is this the end of the list? Check the horizontal link pointer - * (HLP) terminate (T) bit. If T==1, then the HLP address is not - * valid. - */ - - physaddr = imxrt_swap32(qh->hw.hlp); - if ((physaddr & QH_HLP_T) != 0) - { - /* Set the next pointer to NULL. This will terminate the loop. */ - - next = NULL; - } - - /* Is the next QH the asynchronous list head which will always be at - * the end of the asynchronous queue? - */ - - else if (imxrt_virtramaddr(physaddr & QH_HLP_MASK) == - (uintptr_t)&g_asynchead) - { - /* That will also terminate the loop */ - - next = NULL; - } - - /* Otherwise, there is a QH structure after this one that describes - * another transaction. - */ - - else - { - physaddr = imxrt_swap32(qh->hw.hlp) & QH_HLP_MASK; - next = (struct imxrt_qh_s *)imxrt_virtramaddr(physaddr); - } - - /* Perform the user action on this entry. The action might result in - * unlinking the entry! But that is okay because we already have the - * next QH pointer. - * - * Notice that we do not manage the back pointer (bp). If the call- - * out uses it, it must update it as necessary. - */ - - ret = handler(qh, bp, arg); - - /* If the handler returns any non-zero value, then terminate the - * traversal early. - */ - - if (ret != 0) - { - return ret; - } - - /* Set up to visit the next entry */ - - qh = next; - } - - return OK; -} - -/**************************************************************************** - * Name: imxrt_qtd_foreach - * - * Description: - * Give a Queue Head (QH) instance, call the handler for each qTD structure - * in the queue. - * - ****************************************************************************/ - -static int imxrt_qtd_foreach(struct imxrt_qh_s *qh, foreach_qtd_t handler, - void *arg) -{ - struct imxrt_qtd_s *qtd; - struct imxrt_qtd_s *next; - uintptr_t physaddr; - uint32_t *bp; - int ret; - - DEBUGASSERT(qh && handler); - - /* Handle the special case where the queue is empty */ - - bp = &qh->fqp; /* Start of qTDs in original list */ - physaddr = imxrt_swap32(*bp); /* Physical address of first qTD in CPU order */ - - if ((physaddr & QTD_NQP_T) != 0) - { - return 0; - } - - /* Start with the first qTD in the list */ - - qtd = (struct imxrt_qtd_s *)imxrt_virtramaddr(physaddr); - next = NULL; - - /* And loop until we encounter the end of the qTD list */ - - while (qtd) - { - /* Is this the end of the list? Check the next qTD pointer (NQP) - * terminate (T) bit. If T==1, then the NQP address is not valid. - */ - - if ((imxrt_swap32(qtd->hw.nqp) & QTD_NQP_T) != 0) - { - /* Set the next pointer to NULL. This will terminate the loop. */ - - next = NULL; - } - else - { - physaddr = imxrt_swap32(qtd->hw.nqp) & QTD_NQP_NTEP_MASK; - next = (struct imxrt_qtd_s *)imxrt_virtramaddr(physaddr); - } - - /* Perform the user action on this entry. The action might result in - * unlinking the entry! But that is okay because we already have the - * next qTD pointer. - * - * Notice that we do not manage the back pointer (bp). If the call-out - * uses it, it must update it as necessary. - */ - - ret = handler(qtd, &bp, arg); - - /* If the handler returns any non-zero value, then terminate the - * traversal early. - */ - - if (ret != 0) - { - return ret; - } - - /* Set up to visit the next entry */ - - qtd = next; - } - - return OK; -} - -/**************************************************************************** - * Name: imxrt_qtd_discard - * - * Description: - * This is a imxrt_qtd_foreach callback. It simply unlinks the QTD, - * updates the back pointer, and frees the QTD structure. - * - ****************************************************************************/ - -static int imxrt_qtd_discard(struct imxrt_qtd_s *qtd, uint32_t **bp, - void *arg) -{ - DEBUGASSERT(qtd && bp && *bp); - - /* Remove the qTD from the list by updating the forward pointer to skip - * around this qTD. We do not change that pointer because are repeatedly - * removing the aTD at the head of the QH list. - */ - - **bp = qtd->hw.nqp; - - /* Then free the qTD */ - - imxrt_qtd_free(qtd); - return OK; -} - -/**************************************************************************** - * Name: imxrt_qh_discard - * - * Description: - * Free the Queue Head (QH) and all qTD's attached to the QH. - * - * Assumptions: - * The QH structure itself has already been unlinked from whatever list it - * may have been in. - * - ****************************************************************************/ - -static int imxrt_qh_discard(struct imxrt_qh_s *qh) -{ - int ret; - - DEBUGASSERT(qh); - - /* Free all of the qTD's attached to the QH */ - - ret = imxrt_qtd_foreach(qh, imxrt_qtd_discard, NULL); - if (ret < 0) - { - usbhost_trace1(EHCI_TRACE1_QTDFOREACH_FAILED, -ret); - } - - /* Then free the QH itself */ - - imxrt_qh_free(qh); - return ret; -} - -/**************************************************************************** - * Name: imxrt_qtd_invalidate - * - * Description: - * This is a callback from imxrt_qtd_foreach. It simply invalidates D- - * cache for address range of the qTD entry. - * - ****************************************************************************/ - -#if 0 /* Not used */ -static int imxrt_qtd_invalidate(struct imxrt_qtd_s *qtd, uint32_t **bp, - void *arg) -{ - /* Invalidate the D-Cache, i.e., force reloading of the D-Cache from memory - * memory over the specified address range. - */ - - up_invalidate_dcache((uintptr_t)&qtd->hw, - (uintptr_t)&qtd->hw + sizeof(struct ehci_qtd_s)); - return OK; -} -#endif - -/**************************************************************************** - * Name: imxrt_qh_invalidate - * - * Description: - * Invalidate the Queue Head and all qTD entries in the queue. - * - ****************************************************************************/ - -#if 0 /* Not used */ -static int imxrt_qh_invalidate(struct imxrt_qh_s *qh) -{ - /* Invalidate the QH first so that we reload the qTD list head */ - - up_invalidate_dcache((uintptr_t)&qh->hw, - (uintptr_t)&qh->hw + sizeof(struct ehci_qh_s)); - - /* Then invalidate all of the qTD entries in the queue */ - - return imxrt_qtd_foreach(qh, imxrt_qtd_invalidate, NULL); -} -#endif - -/**************************************************************************** - * Name: imxrt_qtd_flush - * - * Description: - * This is a callback from imxrt_qtd_foreach. It simply flushes D-cache - * for address range of the qTD entry. - * - ****************************************************************************/ - -static int imxrt_qtd_flush(struct imxrt_qtd_s *qtd, uint32_t **bp, void *arg) -{ - /* Flush the D-Cache, i.e., make the contents of the memory match the - * contents of the D-Cache in the specified address range and invalidate - * the D-Cache to force re-loading of the data from memory when next - * accessed. - */ - - up_flush_dcache((uintptr_t)&qtd->hw, - (uintptr_t)&qtd->hw + sizeof(struct ehci_qtd_s)); - return OK; -} - -/**************************************************************************** - * Name: imxrt_qh_flush - * - * Description: - * Invalidate the Queue Head and all qTD entries in the queue. - * - ****************************************************************************/ - -static int imxrt_qh_flush(struct imxrt_qh_s *qh) -{ - /* Flush the QH first. This will write the contents of the D-cache to RAM - * and invalidate the contents of the D-cache so that the next access will - * be reloaded from D-Cache. - */ - - up_flush_dcache((uintptr_t)&qh->hw, - (uintptr_t)&qh->hw + sizeof(struct ehci_qh_s)); - - /* Then flush all of the qTD entries in the queue */ - - return imxrt_qtd_foreach(qh, imxrt_qtd_flush, NULL); -} - -/**************************************************************************** - * Name: imxrt_qtd_print - * - * Description: - * Print the context of one qTD - * - ****************************************************************************/ - -#ifdef CONFIG_IMXRT_EHCI_REGDEBUG -static void imxrt_qtd_print(struct imxrt_qtd_s *qtd) -{ - uinfo(" QTD[%p]:\n", qtd); - uinfo(" hw:\n"); - uinfo(" nqp: %08x alt: %08x token: %08x\n", - qtd->hw.nqp, qtd->hw.alt, qtd->hw.token); - uinfo(" bpl: %08x %08x %08x %08x %08x\n", - qtd->hw.bpl[0], qtd->hw.bpl[1], qtd->hw.bpl[2], - qtd->hw.bpl[3], qtd->hw.bpl[4]); -} -#endif - -/**************************************************************************** - * Name: imxrt_qh_print - * - * Description: - * Print the context of one QH - * - ****************************************************************************/ - -#ifdef CONFIG_IMXRT_EHCI_REGDEBUG -static void imxrt_qh_print(struct imxrt_qh_s *qh) -{ - struct imxrt_epinfo_s *epinfo; - struct ehci_overlay_s *overlay; - - uinfo("QH[%p]:\n", qh); - uinfo(" hw:\n"); - uinfo(" hlp: %08x epchar: %08x epcaps: %08x cqp: %08x\n", - qh->hw.hlp, qh->hw.epchar, qh->hw.epcaps, qh->hw.cqp); - - overlay = &qh->hw.overlay; - uinfo(" overlay:\n"); - uinfo(" nqp: %08x alt: %08x token: %08x\n", - overlay->nqp, overlay->alt, overlay->token); - uinfo(" bpl: %08x %08x %08x %08x %08x\n", - overlay->bpl[0], overlay->bpl[1], overlay->bpl[2], - overlay->bpl[3], overlay->bpl[4]); - - uinfo(" fqp:\n", qh->fqp); - - epinfo = qh->epinfo; - uinfo(" epinfo[%p]:\n", epinfo); - if (epinfo) - { - uinfo(" EP%d DIR=%s FA=%08x TYPE=%d MaxPacket=%d\n", - epinfo->epno, epinfo->dirin ? "IN" : "OUT", epinfo->devaddr, - epinfo->xfrtype, epinfo->maxpacket); - uinfo(" Toggle=%d iocwait=%d speed=%d result=%d\n", - epinfo->toggle, epinfo->iocwait, epinfo->speed, epinfo->result); - } -} -#endif - -/**************************************************************************** - * Name: imxrt_qtd_dump - * - * Description: - * This is a imxrt_qtd_foreach callout function. It dumps the context of - * one qTD - * - ****************************************************************************/ - -#ifdef CONFIG_IMXRT_EHCI_REGDEBUG -static int imxrt_qtd_dump(struct imxrt_qtd_s *qtd, uint32_t **bp, void *arg) -{ - imxrt_qtd_print(qtd); - return OK; -} -#endif - -/**************************************************************************** - * Name: imxrt_qh_dump - * - * Description: - * This is a imxrt_qh_foreach call-out function. It dumps a QH structure - * and all of the qTD structures linked to the QH. - * - ****************************************************************************/ - -#ifdef CONFIG_IMXRT_EHCI_REGDEBUG -static int imxrt_qh_dump(struct imxrt_qh_s *qh, uint32_t **bp, void *arg) -{ - imxrt_qh_print(qh); - return imxrt_qtd_foreach(qh, imxrt_qtd_dump, NULL); -} -#endif - -/**************************************************************************** - * Name: imxrt_ehci_speed - * - * Description: - * Map a speed enumeration value per Chapter 9 of the USB specification to - * the speed enumeration required in the EHCI queue head. - * - ****************************************************************************/ - -static inline uint8_t imxrt_ehci_speed(uint8_t usbspeed) -{ - DEBUGASSERT(usbspeed >= USB_SPEED_LOW && usbspeed <= USB_SPEED_HIGH); - return g_ehci_speed[usbspeed]; -} - -/**************************************************************************** - * Name: imxrt_ioc_setup - * - * Description: - * Set the request for the IOC event well BEFORE enabling the transfer (as - * soon as we are absolutely committed to the to avoid transfer). We do - * this to minimize race conditions. This logic would have to be expanded - * if we want to have more than one packet in flight at a time! - * - * Assumption: The caller holds the EHCI exclsem - * - ****************************************************************************/ - -static int imxrt_ioc_setup(struct imxrt_rhport_s *rhport, - struct imxrt_epinfo_s *epinfo) -{ - irqstate_t flags; - int ret = -ENODEV; - - DEBUGASSERT(rhport && epinfo && !epinfo->iocwait); -#ifdef CONFIG_USBHOST_ASYNCH - DEBUGASSERT(epinfo->callback == NULL); -#endif - - /* Is the device still connected? */ - - flags = enter_critical_section(); - if (rhport->connected) - { - /* Then set iocwait to indicate that we expect to be informed when - * either (1) the device is disconnected, or (2) the transfer - * completed. - */ - - epinfo->iocwait = true; /* We want to be awakened by IOC interrupt */ - epinfo->status = 0; /* No status yet */ - epinfo->xfrd = 0; /* Nothing transferred yet */ - epinfo->result = -EBUSY; /* Transfer in progress */ -#ifdef CONFIG_USBHOST_ASYNCH - epinfo->callback = NULL; /* No asynchronous callback */ - epinfo->arg = NULL; -#endif - ret = OK; /* We are good to go */ - } - - leave_critical_section(flags); - return ret; -} - -/**************************************************************************** - * Name: imxrt_ioc_wait - * - * Description: - * Wait for the IOC event. - * - * Assumption: The caller does *NOT* hold the EHCI exclsem. That would - * cause a deadlock when the bottom-half, worker thread needs to take the - * semaphore. - * - ****************************************************************************/ - -static int imxrt_ioc_wait(struct imxrt_epinfo_s *epinfo) -{ - int ret = OK; - - /* Wait for the IOC event. Loop to handle any false alarm semaphore - * counts. Return an error if the task is canceled. - */ - - while (epinfo->iocwait) - { - ret = imxrt_takesem(&epinfo->iocsem); - if (ret < 0) - { - break; - } - } - - return ret < 0 ? ret : epinfo->result; -} - -/**************************************************************************** - * Name: imxrt_qh_enqueue - * - * Description: - * Add a new, ready-to-go QH w/attached qTDs to the asynchronous queue. - * - * Assumptions: The caller holds the EHCI exclsem - * - ****************************************************************************/ - -static void imxrt_qh_enqueue(struct imxrt_qh_s *qhead, struct imxrt_qh_s *qh) -{ - uintptr_t physaddr; - - /* Set the internal fqp field. When we transverse the QH list later, - * we need to know the correct place to start because the overlay may no - * longer point to the first qTD entry. - */ - - qh->fqp = qh->hw.overlay.nqp; - imxrt_qh_dump(qh, NULL, NULL); - - /* Add the new QH to the head of the asynchronous queue list. - * - * First, attach the old head as the new QH HLP and flush the new QH and - * its attached qTDs to RAM. - */ - - qh->hw.hlp = qhead->hw.hlp; - imxrt_qh_flush(qh); - - /* Then set the new QH as the first QH in the asynchronous queue and flush - * the modified head to RAM. - */ - - physaddr = (uintptr_t)imxrt_physramaddr((uintptr_t)qh); - qhead->hw.hlp = imxrt_swap32(physaddr | QH_HLP_TYP_QH); - - up_flush_dcache((uintptr_t)&qhead->hw, - (uintptr_t)&qhead->hw + sizeof(struct ehci_qh_s)); -} - -/**************************************************************************** - * Name: imxrt_qh_create - * - * Description: - * Create a new Queue Head (QH) - * - ****************************************************************************/ - -static struct imxrt_qh_s *imxrt_qh_create(struct imxrt_rhport_s *rhport, - struct imxrt_epinfo_s *epinfo) -{ - struct imxrt_qh_s *qh; - uint32_t rhpndx; - uint32_t regval; - uint8_t hubaddr; - uint8_t hubport; - - /* Allocate a new queue head structure */ - - qh = imxrt_qh_alloc(); - if (qh == NULL) - { - usbhost_trace1(EHCI_TRACE1_QHALLOC_FAILED, 0); - return NULL; - } - - /* Save the endpoint information with the QH itself */ - - qh->epinfo = epinfo; - - /* Write QH endpoint characteristics: - * - * FIELD DESCRIPTION VALUE/SOURCE - * -------- ------------------------------- -------------------- - * DEVADDR Device address Endpoint structure - * I Inactivate on Next Transaction 0 - * ENDPT Endpoint number Endpoint structure - * EPS Endpoint speed Endpoint structure - * DTC Data toggle control 1 - * MAXPKT Max packet size Endpoint structure - * C Control endpoint Calculated - * RL NAK count reloaded 8 - */ - - regval = ((uint32_t)epinfo->devaddr << QH_EPCHAR_DEVADDR_SHIFT) | - ((uint32_t)epinfo->epno << QH_EPCHAR_ENDPT_SHIFT) | - ((uint32_t)imxrt_ehci_speed(epinfo->speed) << - QH_EPCHAR_EPS_SHIFT) | - QH_EPCHAR_DTC | - ((uint32_t)epinfo->maxpacket << QH_EPCHAR_MAXPKT_SHIFT) | - ((uint32_t)8 << QH_EPCHAR_RL_SHIFT); - - /* Paragraph 3.6.3: "Control Endpoint Flag (C). If the QH.EPS field - * indicates the endpoint is not a high-speed device, and the endpoint - * is an control endpoint, then software must set this bit to a one. - * Otherwise it should always set this bit to a zero." - */ - - if (epinfo->speed != USB_SPEED_HIGH && - epinfo->xfrtype == USB_EP_ATTR_XFER_CONTROL) - { - regval |= QH_EPCHAR_C; - } - - /* Save the endpoint characteristics word with the correct byte order */ - - qh->hw.epchar = imxrt_swap32(regval); - - /* Write QH endpoint capabilities - * - * FIELD DESCRIPTION VALUE/SOURCE - * -------- ------------------------------- -------------------- - * SSMASK Interrupt Schedule Mask Depends on epinfo->xfrtype - * SCMASK Split Completion Mask 0 - * HUBADDR Hub Address Always 0 for now - * PORT Port number RH port index + 1 - * MULT High band width multiplier 1 - */ - - rhpndx = RHPNDX(rhport); - -#ifdef CONFIG_USBHOST_HUB - /* REVISIT: Future HUB support will require the HUB port number - * and HUB device address to be included here: - * - * - The HUB device address is the USB device address of the USB 2.0 Hub - * below which a full- or low-speed device is attached. - * - The HUB port number is the port number on the above USB 2.0 Hub - * - * These fields are used in the split-transaction protocol. The kludge - * below should work for hubs connected directly to a root hub port, - * but would not work for devices connected to downstream hubs. - */ - -#warning Missing logic - hubaddr = rhport->ep0.devaddr; - hubport = rhpndx + 1; -#else - hubaddr = rhport->ep0.devaddr; - hubport = rhpndx + 1; -#endif - - regval = ((uint32_t)hubaddr << QH_EPCAPS_HUBADDR_SHIFT) | - ((uint32_t)hubport << QH_EPCAPS_PORT_SHIFT) | - ((uint32_t)1 << QH_EPCAPS_MULT_SHIFT); - -#ifndef CONFIG_USBHOST_INT_DISABLE - if (epinfo->xfrtype == USB_EP_ATTR_XFER_INT) - { - /* Here, the S-Mask field in the queue head is set to 1, indicating - * that the transaction for the endpoint should be executed on the bus - * during micro-frame 0 of the frame. - * - * REVISIT: The polling interval should be controlled by the which - * entry is the framelist holds the QH pointer for a given micro-frame - * and the QH pointer should be replicated for different polling rates. - * This implementation currently just sets all frame_list entry to - * all the same interrupt queue. That should work but will not give - * any control over polling rates. - */ -#warning REVISIT - - regval |= ((uint32_t)1 << QH_EPCAPS_SSMASK_SHIFT); - } -#endif - - qh->hw.epcaps = imxrt_swap32(regval); - - /* Mark this as the end of this list. This will be overwritten if/when the - * next qTD is added to the queue. - */ - - qh->hw.hlp = imxrt_swap32(QH_HLP_T); - qh->hw.overlay.nqp = imxrt_swap32(QH_NQP_T); - qh->hw.overlay.alt = imxrt_swap32(QH_AQP_T); - return qh; -} - -/**************************************************************************** - * Name: imxrt_qtd_addbpl - * - * Description: - * Add a buffer pointer list to a qTD. - * - ****************************************************************************/ - -static int imxrt_qtd_addbpl(struct imxrt_qtd_s *qtd, const void *buffer, - size_t buflen) -{ - uint32_t physaddr; - uint32_t nbytes; - uint32_t next; - int ndx; - - /* Flush the contents of the data buffer to RAM so that the correct - * contents will be accessed for an OUT DMA. - */ - - up_flush_dcache((uintptr_t)buffer, (uintptr_t)buffer + buflen); - - /* Loop, adding the aligned physical addresses of the buffer to the buffer - * page list. Only the first entry need not be aligned (because only the - * first entry has the offset field). The subsequent entries must begin on - * 4KB address boundaries. - */ - - physaddr = (uint32_t)imxrt_physramaddr((uintptr_t)buffer); - - for (ndx = 0; ndx < 5; ndx++) - { - /* Write the physical address of the buffer into the qTD buffer pointer - * list. - */ - - qtd->hw.bpl[ndx] = imxrt_swap32(physaddr); - - /* Get the next buffer pointer (in the case where we will have to - * transfer more then one chunk). This buffer must be aligned to a - * 4KB address boundary. - */ - - next = (physaddr + 4096) & ~4095; - - /* How many bytes were included in the last buffer? Was it the whole - * thing? - */ - - nbytes = next - physaddr; - if (nbytes >= buflen) - { - /* Yes... it was the whole thing. Break out of the loop early. */ - - break; - } - - /* Adjust the buffer length and physical address for the next time - * through the loop. - */ - - buflen -= nbytes; - physaddr = next; - } - - /* Handle the case of a huge buffer > 4*4KB = 16KB */ - - if (ndx >= 5) - { - usbhost_trace1(EHCI_TRACE1_BUFTOOBIG, buflen); - return -EFBIG; - } - - return OK; -} - -/**************************************************************************** - * Name: imxrt_qtd_setupphase - * - * Description: - * Create a SETUP phase request qTD. - * - ****************************************************************************/ - -static struct imxrt_qtd_s * - imxrt_qtd_setupphase(struct imxrt_epinfo_s *epinfo, - const struct usb_ctrlreq_s *req) -{ - struct imxrt_qtd_s *qtd; - uint32_t regval; - int ret; - - /* Allocate a new Queue Element Transfer Descriptor (qTD) */ - - qtd = imxrt_qtd_alloc(); - if (qtd == NULL) - { - usbhost_trace1(EHCI_TRACE1_REQQTDALLOC_FAILED, 0); - return NULL; - } - - /* Mark this as the end of the list (this will be overwritten if another - * qTD is added after this one). - */ - - qtd->hw.nqp = imxrt_swap32(QTD_NQP_T); - qtd->hw.alt = imxrt_swap32(QTD_AQP_T); - - /* Write qTD token: - * - * FIELD DESCRIPTION VALUE/SOURCE - * -------- ------------------------------- -------------------- - * STATUS Status QTD_TOKEN_ACTIVE - * PID PID Code QTD_TOKEN_PID_SETUP - * CERR Error Counter 3 - * CPAGE Current Page 0 - * IOC Interrupt on complete 0 - * NBYTES Total Bytes to Transfer USB_SIZEOF_CTRLREQ - * TOGGLE Data Toggle 0 - */ - - regval = QTD_TOKEN_ACTIVE | QTD_TOKEN_PID_SETUP | - ((uint32_t)3 << QTD_TOKEN_CERR_SHIFT) | - ((uint32_t)USB_SIZEOF_CTRLREQ << QTD_TOKEN_NBYTES_SHIFT); - - qtd->hw.token = imxrt_swap32(regval); - - /* Add the buffer data */ - - ret = imxrt_qtd_addbpl(qtd, req, USB_SIZEOF_CTRLREQ); - if (ret < 0) - { - usbhost_trace1(EHCI_TRACE1_ADDBPL_FAILED, -ret); - imxrt_qtd_free(qtd); - return NULL; - } - - /* Add the data transfer size to the count in the epinfo structure */ - - epinfo->xfrd += USB_SIZEOF_CTRLREQ; - - return qtd; -} - -/**************************************************************************** - * Name: imxrt_qtd_dataphase - * - * Description: - * Create a data transfer or SET data phase qTD. - * - ****************************************************************************/ - -static struct imxrt_qtd_s *imxrt_qtd_dataphase(struct imxrt_epinfo_s *epinfo, - void *buffer, int buflen, - uint32_t tokenbits) -{ - struct imxrt_qtd_s *qtd; - uint32_t regval; - int ret; - - /* Allocate a new Queue Element Transfer Descriptor (qTD) */ - - qtd = imxrt_qtd_alloc(); - if (qtd == NULL) - { - usbhost_trace1(EHCI_TRACE1_DATAQTDALLOC_FAILED, 0); - return NULL; - } - - /* Mark this as the end of the list (this will be overwritten if another - * qTD is added after this one). - */ - - qtd->hw.nqp = imxrt_swap32(QTD_NQP_T); - qtd->hw.alt = imxrt_swap32(QTD_AQP_T); - - /* Write qTD token: - * - * FIELD DESCRIPTION VALUE/SOURCE - * -------- ------------------------------- -------------------- - * STATUS Status QTD_TOKEN_ACTIVE - * PID PID Code Contained in tokenbits - * CERR Error Counter 3 - * CPAGE Current Page 0 - * IOC Interrupt on complete Contained in tokenbits - * NBYTES Total Bytes to Transfer buflen - * TOGGLE Data Toggle Contained in tokenbits - */ - - regval = tokenbits | QTD_TOKEN_ACTIVE | - ((uint32_t)3 << QTD_TOKEN_CERR_SHIFT) | - ((uint32_t)buflen << QTD_TOKEN_NBYTES_SHIFT); - - qtd->hw.token = imxrt_swap32(regval); - - /* Add the buffer information to the buffer pointer list */ - - ret = imxrt_qtd_addbpl(qtd, buffer, buflen); - if (ret < 0) - { - usbhost_trace1(EHCI_TRACE1_ADDBPL_FAILED, -ret); - imxrt_qtd_free(qtd); - return NULL; - } - - /* Add the data transfer size to the count in the epinfo structure */ - - epinfo->xfrd += buflen; - - return qtd; -} - -/**************************************************************************** - * Name: imxrt_qtd_statusphase - * - * Description: - * Create a STATUS phase request qTD. - * - ****************************************************************************/ - -static struct imxrt_qtd_s *imxrt_qtd_statusphase(uint32_t tokenbits) -{ - struct imxrt_qtd_s *qtd; - uint32_t regval; - - /* Allocate a new Queue Element Transfer Descriptor (qTD) */ - - qtd = imxrt_qtd_alloc(); - if (qtd == NULL) - { - usbhost_trace1(EHCI_TRACE1_REQQTDALLOC_FAILED, 0); - return NULL; - } - - /* Mark this as the end of the list (this will be overwritten if another - * qTD is added after this one). - */ - - qtd->hw.nqp = imxrt_swap32(QTD_NQP_T); - qtd->hw.alt = imxrt_swap32(QTD_AQP_T); - - /* Write qTD token: - * - * FIELD DESCRIPTION VALUE/SOURCE - * -------- ------------------------------- -------------------- - * STATUS Status QTD_TOKEN_ACTIVE - * PID PID Code Contained in tokenbits - * CERR Error Counter 3 - * CPAGE Current Page 0 - * IOC Interrupt on complete QTD_TOKEN_IOC - * NBYTES Total Bytes to Transfer 0 - * TOGGLE Data Toggle Contained in tokenbits - */ - - regval = tokenbits | QTD_TOKEN_ACTIVE | QTD_TOKEN_IOC | - ((uint32_t)3 << QTD_TOKEN_CERR_SHIFT); - - qtd->hw.token = imxrt_swap32(regval); - return qtd; -} - -/**************************************************************************** - * Name: imxrt_async_setup - * - * Description: - * Process a IN or OUT request on any asynchronous endpoint (bulk or - * control). This function will enqueue the request and wait for it to - * complete. Bulk data transfers differ in that req == NULL and there are - * not SETUP or STATUS phases. - * - * This is a blocking function; it will not return until the control - * transfer has completed. - * - * Assumption: The caller holds the EHCI exclsem. - * - * Returned Value: - * Zero (OK) is returned on success; a negated errno value is return on - * any failure. - * - ****************************************************************************/ - -static int imxrt_async_setup(struct imxrt_rhport_s *rhport, - struct imxrt_epinfo_s *epinfo, - const struct usb_ctrlreq_s *req, - uint8_t *buffer, size_t buflen) -{ - struct imxrt_qh_s *qh; - struct imxrt_qtd_s *qtd; - uintptr_t physaddr; - uint32_t *flink; - uint32_t *alt; - uint32_t toggle; - bool dirin = false; - int ret; - - /* Terse output only if we are tracing */ - -#ifdef CONFIG_USBHOST_TRACE - usbhost_vtrace2(EHCI_VTRACE2_ASYNCXFR, epinfo->epno, buflen); -#else - uinfo("RHport%d EP%d: buffer=%p, buflen=%d, req=%p\n", - RHPORT(rhport), epinfo->epno, buffer, buflen, req); -#endif - - DEBUGASSERT(rhport && epinfo); - - /* A buffer may or may be supplied with an EP0 SETUP transfer. A buffer - * will always be present for normal endpoint data transfers. - */ - - DEBUGASSERT(req || (buffer && buflen > 0)); - - /* Create and initialize a Queue Head (QH) structure for this transfer */ - - qh = imxrt_qh_create(rhport, epinfo); - if (qh == NULL) - { - usbhost_trace1(EHCI_TRACE1_QHCREATE_FAILED, 0); - return -ENOMEM; - } - - /* Initialize the QH link and get the next data toggle (not used for SETUP - * transfers) - */ - - flink = &qh->hw.overlay.nqp; - toggle = (uint32_t)epinfo->toggle << QTD_TOKEN_TOGGLE_SHIFT; - ret = -EIO; - - /* Is there an EP0 SETUP request? If so, req will be non-NULL and we will - * queue two or three qTDs: - * - * 1) One for the SETUP phase, - * 2) One for the DATA phase (if there is data), and - * 3) One for the STATUS phase. - * - * If this is not an EP0 SETUP request, then only a data transfer will be - * enqueued. - */ - - if (req != NULL) - { - /* Allocate a new Queue Element Transfer Descriptor (qTD) for the SETUP - * phase of the request sequence. - */ - - qtd = imxrt_qtd_setupphase(epinfo, req); - if (qtd == NULL) - { - usbhost_trace1(EHCI_TRACE1_QTDSETUP_FAILED, 0); - goto errout_with_qh; - } - - /* Link the new qTD to the QH head. */ - - physaddr = imxrt_physramaddr((uintptr_t)qtd); - *flink = imxrt_swap32(physaddr); - - /* Get the new forward link pointer and data toggle */ - - flink = &qtd->hw.nqp; - toggle = QTD_TOKEN_TOGGLE; - } - - /* A buffer may or may be supplied with an EP0 SETUP transfer. A buffer - * will always be present for normal endpoint data transfers. - */ - - alt = NULL; - if (buffer != NULL && buflen > 0) - { - uint32_t tokenbits; - - /* Extra TOKEN bits include the data toggle, the data PID, and if - * there is no request, an indication to interrupt at the end of this - * transfer. - */ - - tokenbits = toggle; - - /* Get the data token direction. - * - * If this is a SETUP request, use the direction contained in the - * request. The IOC bit is not set. - */ - - if (req) - { - if ((req->type & USB_REQ_DIR_MASK) == USB_REQ_DIR_IN) - { - tokenbits |= QTD_TOKEN_PID_IN; - dirin = true; - } - else - { - tokenbits |= QTD_TOKEN_PID_OUT; - dirin = false; - } - } - - /* Otherwise, the endpoint is uni-directional. Get the direction from - * the epinfo structure. Since this is not an EP0 SETUP request, - * nothing follows the data and we want the IOC interrupt when the - * data transfer completes. - */ - - else if (epinfo->dirin) - { - tokenbits |= (QTD_TOKEN_PID_IN | QTD_TOKEN_IOC); - dirin = true; - } - else - { - tokenbits |= (QTD_TOKEN_PID_OUT | QTD_TOKEN_IOC); - dirin = false; - } - - /* Allocate a new Queue Element Transfer Descriptor (qTD) for the data - * buffer. - */ - - qtd = imxrt_qtd_dataphase(epinfo, buffer, buflen, tokenbits); - if (qtd == NULL) - { - usbhost_trace1(EHCI_TRACE1_QTDDATA_FAILED, 0); - goto errout_with_qh; - } - - /* Link the new qTD to either QH head of the SETUP qTD. */ - - physaddr = imxrt_physramaddr((uintptr_t)qtd); - *flink = imxrt_swap32(physaddr); - - /* Set the forward link pointer to this new qTD */ - - flink = &qtd->hw.nqp; - - /* If this was an IN transfer, then setup a pointer alternate link. - * The EHCI hardware will use this link if a short packet is received. - */ - - if (dirin) - { - alt = &qtd->hw.alt; - } - } - - /* If this is an EP0 SETUP request, then enqueue one more qTD for the - * STATUS phase transfer. - */ - - if (req != NULL) - { - /* Extra TOKEN bits include the data toggle and the correct data PID. */ - - uint32_t tokenbits = toggle; - - /* The status phase direction is the opposite of the data phase. If - * this is an IN request, then we received the buffer and we will send - * the zero length packet handshake. - */ - - if ((req->type & USB_REQ_DIR_MASK) == USB_REQ_DIR_IN) - { - tokenbits |= QTD_TOKEN_PID_OUT; - } - - /* Otherwise, this in an OUT request. We send the buffer and we expect - * to receive the NULL packet handshake. - */ - - else - { - tokenbits |= QTD_TOKEN_PID_IN; - } - - /* Allocate a new Queue Element Transfer Descriptor (qTD) - * for the status - */ - - qtd = imxrt_qtd_statusphase(tokenbits); - if (qtd == NULL) - { - usbhost_trace1(EHCI_TRACE1_QTDSTATUS_FAILED, 0); - goto errout_with_qh; - } - - /* Link the new qTD to either the SETUP or data qTD. */ - - physaddr = imxrt_physramaddr((uintptr_t)qtd); - *flink = imxrt_swap32(physaddr); - - /* In an IN data qTD was also enqueued, then linked the data qTD's - * alternate pointer to this STATUS phase qTD in order to handle short - * transfers. - */ - - if (alt) - { - *alt = imxrt_swap32(physaddr); - } - } - - /* Add the new QH to the head of the asynchronous queue list */ - - imxrt_qh_enqueue(&g_asynchead, qh); - return OK; - - /* Clean-up after an error */ - -errout_with_qh: - imxrt_qh_discard(qh); - return ret; -} - -/**************************************************************************** - * Name: imxrt_intr_setup - * - * Description: - * Process a IN or OUT request on any interrupt endpoint by inserting a qTD - * into the periodic frame list. - * - * Paragraph 4.10.7 "Adding Interrupt Queue Heads to the Periodic Schedule" - * "The link path(s) from the periodic frame list to a queue head - * establishes in which frames a transaction can be executed for the - * queue head. Queue heads are linked into the periodic schedule so they - * are polled at the appropriate rate. System software sets a bit in a - * queue head's S-Mask to indicate which micro-frame with-in a 1 - * millisecond period a transaction should be executed for the queue - * head. Software must ensure that all queue heads in the periodic - * schedule have S-Mask set to a non-zero value. An S-mask with a zero - * value in the context of the periodic schedule yields undefined - * results. - * - * "If the desired poll rate is greater than one frame, system software - * can use a combination of queue head linking and S-Mask values to - * spread interrupts of equal poll rates through the schedule so that the - * periodic bandwidth is allocated and managed in the most efficient - * manner possible." - * - * Paragraph 4.6 "Periodic Schedule" - * - * "The periodic schedule is used to manage all isochronous and interrupt - * transfer streams. The base of the periodic schedule is the periodic - * frame list. Software links schedule data structures to the periodic - * frame list to produce a graph of scheduled data structures. The graph - * represents an appropriate sequence of transactions on the USB. ... - * isochronous transfers (using iTDs and siTDs) with a period of one are - * linked directly to the periodic frame list. Interrupt transfers (are - * managed with queue heads) and isochronous streams with periods other - * than one are linked following the period-one iTD/siTDs. Interrupt - * queue heads are linked into the frame list ordered by poll rate. - * Longer poll rates are linked first (e.g. closest to the periodic - * frame list), followed by shorter poll rates, with queue heads with a - * poll rate of one, on the very end." - * - * Assumption: The caller holds the EHCI exclsem. - * - * Returned Value: - * Zero (OK) is returned on success; a negated errno value is return on - * any failure. - * - ****************************************************************************/ - -#ifndef CONFIG_USBHOST_INT_DISABLE -static int imxrt_intr_setup(struct imxrt_rhport_s *rhport, - struct imxrt_epinfo_s *epinfo, - uint8_t *buffer, size_t buflen) -{ - struct imxrt_qh_s *qh; - struct imxrt_qtd_s *qtd; - uintptr_t physaddr; - uint32_t tokenbits; - uint32_t regval; - int ret; - - /* Terse output only if we are tracing */ - -#ifdef CONFIG_USBHOST_TRACE - usbhost_vtrace2(EHCI_VTRACE2_INTRXFR, epinfo->epno, buflen); -#else - uinfo("RHport%d EP%d: buffer=%p, buflen=%d\n", - RHPORT(rhport), epinfo->epno, buffer, buflen); -#endif - - DEBUGASSERT(rhport && epinfo && buffer && buflen > 0); - - /* Create and initialize a Queue Head (QH) structure for this transfer */ - - qh = imxrt_qh_create(rhport, epinfo); - if (qh == NULL) - { - usbhost_trace1(EHCI_TRACE1_QHCREATE_FAILED, 0); - return -ENOMEM; - } - - /* Extra TOKEN bits include the data toggle, the data PID, and an - * indication to interrupt at the end of this transfer. - */ - - tokenbits = (uint32_t)epinfo->toggle << QTD_TOKEN_TOGGLE_SHIFT; - - /* Get the data token direction. */ - - if (epinfo->dirin) - { - tokenbits |= (QTD_TOKEN_PID_IN | QTD_TOKEN_IOC); - } - else - { - tokenbits |= (QTD_TOKEN_PID_OUT | QTD_TOKEN_IOC); - } - - /* Allocate a new Queue Element Transfer Descriptor (qTD) for the data - * buffer. - */ - - qtd = imxrt_qtd_dataphase(epinfo, buffer, buflen, tokenbits); - if (qtd == NULL) - { - usbhost_trace1(EHCI_TRACE1_QTDDATA_FAILED, 0); - ret = -ENOMEM; - goto errout_with_qh; - } - - /* Link the new qTD to the QH. */ - - physaddr = imxrt_physramaddr((uintptr_t)qtd); - qh->hw.overlay.nqp = imxrt_swap32(physaddr); - - /* Disable the periodic schedule */ - - regval = imxrt_getreg(&HCOR->usbcmd); - regval &= ~EHCI_USBCMD_PSEN; - imxrt_putreg(regval, &HCOR->usbcmd); - - /* Add the new QH to the head of the interrupt transfer list */ - - imxrt_qh_enqueue(&g_intrhead, qh); - - /* Re-enable the periodic schedule */ - - regval |= EHCI_USBCMD_PSEN; - imxrt_putreg(regval, &HCOR->usbcmd); - return OK; - - /* Clean-up after an error */ - -errout_with_qh: - imxrt_qh_discard(qh); - return ret; -} -#endif /* CONFIG_USBHOST_INT_DISABLE */ - -/**************************************************************************** - * Name: imxrt_transfer_wait - * - * Description: - * Wait for an IN or OUT transfer to complete. - * - * Assumption: The caller holds the EHCI exclsem. The caller must be aware - * that the EHCI exclsem will released while waiting for the transfer to - * complete, but will be re-acquired when before returning. The state of - * EHCI resources could be very different upon return. - * - * Returned Value: - * On success, this function returns the number of bytes actually - * transferred. For control transfers, this size includes the size of the - * control request plus the size of the data (which could be short); for - * bulk transfers, this will be the number of data bytes transfers (which - * could be short). - * - ****************************************************************************/ - -static ssize_t imxrt_transfer_wait(struct imxrt_epinfo_s *epinfo) -{ - int ret; - int ret2; - - /* Release the EHCI semaphore while we wait. Other threads need the - * opportunity to access the EHCI resources while we wait. - * - * REVISIT: Is this safe? NO. This is a bug and needs rethinking. - * We need to lock all of the port-resources (not EHCI common) until - * the transfer is complete. But we can't use the common EHCI exclsem - * or we will deadlock while waiting (because the working thread that - * wakes this thread up needs the exclsem). - */ - - /* REVISIT */ - - imxrt_givesem(&g_ehci.exclsem); - - /* Wait for the IOC completion event */ - - ret = imxrt_ioc_wait(epinfo); - - /* Re-acquire the EHCI semaphore. The caller expects to be holding - * this upon return. - */ - - ret2 = imxrt_takesem_noncancelable(&g_ehci.exclsem); - if (ret >= 0 && ret2 < 0) - { - ret = ret2; - } - -#if 0 /* Does not seem to be needed */ - /* Was there a data buffer? Was this an OUT transfer? */ - - if (buffer != NULL && buflen > 0 && !dirin) - { - /* We have received data from the host -- unless there was an error. - * in any event, we will invalidate the data buffer so that we will - * reload any new data freshly DMAed into the user buffer. - * - * NOTE: This might be un-necessary. We cleaned and invalidated the - * D-Cache prior to starting the DMA so the D-Cache should still be - * invalid in this memory region. - */ - - up_invalidate_dcache((uintptr_t)buffer, (uintptr_t)buffer + buflen); - } -#endif - - /* Did imxrt_ioc_wait() or imxrt_takesem_noncancelable() report an - * error? - */ - - if (ret < 0) - { - usbhost_trace1(EHCI_TRACE1_TRANSFER_FAILED, -ret); - epinfo->iocwait = false; - return (ssize_t)ret; - } - - /* Transfer completed successfully. Return the number of bytes - * transferred. - */ - - return epinfo->xfrd; -} - -/**************************************************************************** - * Name: imxrt_ioc_async_setup - * - * Description: - * Setup to receive an asynchronous notification when a transfer completes. - * - * Input Parameters: - * epinfo - The IN or OUT endpoint descriptor for the device endpoint on - * which the transfer will be performed. - * callback - The function to be called when the completes - * arg - An arbitrary argument that will be provided with the callback. - * - * Returned Value: - * None - * - * Assumptions: - * - Called from the interrupt level - * - ****************************************************************************/ - -#ifdef CONFIG_USBHOST_ASYNCH -static inline int imxrt_ioc_async_setup(struct imxrt_rhport_s *rhport, - struct imxrt_epinfo_s *epinfo, - usbhost_asynch_t callback, - FAR void *arg) -{ - irqstate_t flags; - int ret = -ENODEV; - - DEBUGASSERT(rhport && epinfo && !epinfo->iocwait && - callback != NULL && epinfo->callback == NULL); - - /* Is the device still connected? */ - - flags = enter_critical_section(); - if (rhport->connected) - { - /* Then save callback information to used when either (1) the - * device is disconnected, or (2) the transfer completes. - */ - - epinfo->iocwait = false; /* No synchronous wakeup */ - epinfo->status = 0; /* No status yet */ - epinfo->xfrd = 0; /* Nothing transferred yet */ - epinfo->result = -EBUSY; /* Transfer in progress */ - epinfo->callback = callback; /* Asynchronous callback */ - epinfo->arg = arg; /* Argument that accompanies the callback */ - ret = OK; /* We are good to go */ - } - - leave_critical_section(flags); - return ret; -} -#endif - -/**************************************************************************** - * Name: imxrt_asynch_completion - * - * Description: - * This function is called at the interrupt level when an asynchronous - * transfer completes. It performs the pending callback. - * - * Input Parameters: - * epinfo - The IN or OUT endpoint descriptor for the device endpoint on - * which the transfer was performed. - * - * Returned Value: - * None - * - * Assumptions: - * - Called from the interrupt level - * - ****************************************************************************/ - -#ifdef CONFIG_USBHOST_ASYNCH -static void imxrt_asynch_completion(struct imxrt_epinfo_s *epinfo) -{ - usbhost_asynch_t callback; - ssize_t nbytes; - void *arg; - int result; - - DEBUGASSERT(epinfo != NULL && epinfo->iocwait == false && - epinfo->callback != NULL); - - /* Extract and reset the callback info */ - - callback = epinfo->callback; - arg = epinfo->arg; - result = epinfo->result; - nbytes = epinfo->xfrd; - - epinfo->callback = NULL; - epinfo->arg = NULL; - epinfo->result = OK; - epinfo->iocwait = false; - - /* Then perform the callback. Provide the number of bytes successfully - * transferred or the negated errno value in the event of a failure. - */ - - if (result < 0) - { - nbytes = (ssize_t)result; - } - - callback(arg, nbytes); -} -#endif - -/**************************************************************************** - * Name: imxrt_qtd_ioccheck - * - * Description: - * This function is a imxrt_qtd_foreach() callback function. It services - * one qTD in the asynchronous queue. It removes all of the qTD - * structures that are no longer active. - * - ****************************************************************************/ - -static int imxrt_qtd_ioccheck(struct imxrt_qtd_s *qtd, uint32_t **bp, - void *arg) -{ - struct imxrt_epinfo_s *epinfo = (struct imxrt_epinfo_s *)arg; - DEBUGASSERT(qtd && epinfo); - - /* Make sure we reload the QH from memory */ - - up_invalidate_dcache((uintptr_t)&qtd->hw, - (uintptr_t)&qtd->hw + sizeof(struct ehci_qtd_s)); - imxrt_qtd_print(qtd); - - /* Remove the qTD from the list - * - * NOTE that we don't check if the qTD is active nor do we check if there - * are any errors reported in the qTD. If the transfer halted due to - * an error, then qTDs in the list after the error qTD will still appear - * to be active. - */ - - **bp = qtd->hw.nqp; - - /* Subtract the number of bytes left un-transferred. The epinfo->xfrd - * field is initialized to the total number of bytes to be transferred - * (all qTDs in the list). We subtract out the number of un-transferred - * bytes on each transfer and the final result will be the number of bytes - * actually transferred. - */ - - epinfo->xfrd -= (imxrt_swap32(qtd->hw.token) & QTD_TOKEN_NBYTES_MASK) >> - QTD_TOKEN_NBYTES_SHIFT; - - /* Release this QH by returning it to the free list */ - - imxrt_qtd_free(qtd); - return OK; -} - -/**************************************************************************** - * Name: imxrt_qh_ioccheck - * - * Description: - * This function is a imxrt_qh_foreach() callback function. It services - * one QH in the asynchronous queue. It check all attached qTD structures - * and remove all of the structures that are no longer active. if all of - * the qTD structures are removed, then QH itself will also be removed. - * - ****************************************************************************/ - -static int imxrt_qh_ioccheck(struct imxrt_qh_s *qh, uint32_t **bp, void *arg) -{ - struct imxrt_epinfo_s *epinfo; - uint32_t token; - int ret; - - DEBUGASSERT(qh && bp); - - /* Make sure we reload the QH from memory */ - - up_invalidate_dcache((uintptr_t)&qh->hw, - (uintptr_t)&qh->hw + sizeof(struct ehci_qh_s)); - imxrt_qh_print(qh); - - /* Get the endpoint info pointer from the extended QH data. Only the - * g_asynchead QH can have a NULL epinfo field. - */ - - epinfo = qh->epinfo; - DEBUGASSERT(epinfo); - - /* Paragraph 3.6.3: "The nine DWords in [the Transfer Overlay] area - * represent a transaction working space for the host controller. The - * general operational model is that the host controller can detect - * whether the overlay area contains a description of an active transfer. - * If it does not contain an active transfer, then it follows the Queue - * Head Horizontal Link Pointer to the next queue head. The host - * controller will never follow the Next Transfer Queue Element or - * Alternate Queue Element pointers unless it is actively attempting to - * advance the queue ..." - */ - - /* Is the qTD still active? */ - - token = imxrt_swap32(qh->hw.overlay.token); - usbhost_vtrace2(EHCI_VTRACE2_IOCCHECK, epinfo->epno, token); - - if ((token & QH_TOKEN_ACTIVE) != 0) - { - /* Yes... we cannot process the QH while it is still active. Return - * zero to visit the next QH in the list. - */ - - *bp = &qh->hw.hlp; - return OK; - } - - /* Remove all active, attached qTD structures from the inactive QH */ - - ret = imxrt_qtd_foreach(qh, imxrt_qtd_ioccheck, (void *)qh->epinfo); - if (ret < 0) - { - usbhost_trace1(EHCI_TRACE1_QTDFOREACH_FAILED, -ret); - } - - /* If there is no longer anything attached to the QH, then remove it from - * the asynchronous queue. - */ - - if ((imxrt_swap32(qh->fqp) & QTD_NQP_T) != 0) - { - /* Set the forward link of the previous QH to point to the next - * QH in the list. - */ - - **bp = qh->hw.hlp; - up_flush_dcache((uintptr_t)*bp, (uintptr_t)*bp + sizeof(uint32_t)); - - /* Check for errors, update the data toggle */ - - if ((token & QH_TOKEN_ERRORS) == 0) - { - /* No errors.. Save the last data toggle value */ - - epinfo->toggle = (token >> QTD_TOKEN_TOGGLE_SHIFT) & 1; - - /* Report success */ - - epinfo->status = 0; - epinfo->result = OK; - } - else - { - /* An error occurred */ - - epinfo->status = (token & QH_TOKEN_STATUS_MASK) >> - QH_TOKEN_STATUS_SHIFT; - - /* The HALT condition is set on a variety of conditions: babble, - * error counter countdown to zero, or a STALL. If we can rule - * out babble (babble bit not set) and if the error counter is - * non-zero, then we can assume a STALL. In this case, we return - * -PERM to inform the class driver of the stall condition. - */ - - if ((token & (QH_TOKEN_BABBLE | QH_TOKEN_HALTED)) == - QH_TOKEN_HALTED && - (token & QH_TOKEN_CERR_MASK) != 0) - { - /* It is a stall, Note that the data toggle is reset - * after the stall. - */ - - usbhost_trace2(EHCI_TRACE2_EPSTALLED, epinfo->epno, token); - epinfo->result = -EPERM; - epinfo->toggle = 0; - } - else - { - /* Otherwise, it is some kind of data transfer error */ - - usbhost_trace2(EHCI_TRACE2_EPIOERROR, epinfo->epno, token); - epinfo->result = -EIO; - } - } - - /* Is there a thread waiting for this transfer to complete? */ - - if (epinfo->iocwait) - { - /* Yes... wake it up */ - - epinfo->iocwait = false; - imxrt_givesem(&epinfo->iocsem); - } - -#ifdef CONFIG_USBHOST_ASYNCH - /* No.. Is there a pending asynchronous transfer? */ - - else if (epinfo->callback != NULL) - { - /* Yes.. perform the callback */ - - imxrt_asynch_completion(epinfo); - } -#endif - - /* Then release this QH by returning it to the free list */ - - imxrt_qh_free(qh); - } - else - { - /* Otherwise, the horizontal link pointer of this QH will become the - * next back pointer. - */ - - *bp = &qh->hw.hlp; - } - - return OK; -} - -/**************************************************************************** - * Name: imxrt_qtd_cancel - * - * Description: - * This function is a imxrt_qtd_foreach() callback function. It removes - * each qTD attached to a QH. - * - ****************************************************************************/ - -#ifdef CONFIG_USBHOST_ASYNCH -static int imxrt_qtd_cancel(struct imxrt_qtd_s *qtd, uint32_t **bp, - void *arg) -{ - DEBUGASSERT(qtd != NULL && bp != NULL); - - /* Make sure we reload the QH from memory */ - - up_invalidate_dcache((uintptr_t)&qtd->hw, - (uintptr_t)&qtd->hw + sizeof(struct ehci_qtd_s)); - imxrt_qtd_print(qtd); - - /* Remove the qTD from the list - * - * NOTE that we don't check if the qTD is active nor do we check if there - * are any errors reported in the qTD. If the transfer halted due to - * an error, then qTDs in the list after the error qTD will still appear - * to be active. - * - * REVISIT: There is a race condition here that needs to be resolved. - */ - - **bp = qtd->hw.nqp; - - /* Release this QH by returning it to the free list */ - - imxrt_qtd_free(qtd); - return OK; -} -#endif /* CONFIG_USBHOST_ASYNCH */ - -/**************************************************************************** - * Name: imxrt_qh_cancel - * - * Description: - * This function is a imxrt_qh_foreach() callback function. It cancels - * one QH in the asynchronous queue. It will remove all attached qTD - * structures and remove all of the structures that are no longer active. - * Then QH itself will also be removed. - * - ****************************************************************************/ - -#ifdef CONFIG_USBHOST_ASYNCH -static int imxrt_qh_cancel(struct imxrt_qh_s *qh, uint32_t **bp, void *arg) -{ - struct imxrt_epinfo_s *epinfo = (struct imxrt_epinfo_s *)arg; - uint32_t regval; - int ret; - - DEBUGASSERT(qh != NULL && bp != NULL && epinfo != NULL); - - /* Make sure we reload the QH from memory */ - - up_invalidate_dcache((uintptr_t)&qh->hw, - (uintptr_t)&qh->hw + sizeof(struct ehci_qh_s)); - imxrt_qh_print(qh); - - /* Check if this is the QH that we are looking for */ - - if (qh->epinfo == epinfo) - { - /* No... keep looking */ - - return OK; - } - - /* Disable both the asynchronous and period schedules */ - - regval = imxrt_getreg(&HCOR->usbcmd); - imxrt_putreg(regval & ~(EHCI_USBCMD_ASEN | EHCI_USBCMD_PSEN), - &HCOR->usbcmd); - - /* Remove the QH from the list - * - * NOTE that we don't check if the qTD is active nor do we check if there - * are any errors reported in the qTD. If the transfer halted due to - * an error, then qTDs in the list after the error qTD will still appear - * to be active. - * - * REVISIT: There is a race condition here that needs to be resolved. - */ - - **bp = qh->hw.hlp; - up_flush_dcache((uintptr_t)*bp, (uintptr_t)*bp + sizeof(uint32_t)); - - /* Re-enable the schedules (if they were enabled before. */ - - imxrt_putreg(regval, &HCOR->usbcmd); - - /* Remove all active, attached qTD structures from the removed QH */ - - ret = imxrt_qtd_foreach(qh, imxrt_qtd_cancel, NULL); - if (ret < 0) - { - usbhost_trace1(EHCI_TRACE1_QTDFOREACH_FAILED, -ret); - } - - /* Then release this QH by returning it to the free list. Return 1 - * to stop the traverse without an error. - */ - - imxrt_qh_free(qh); - return 1; -} -#endif /* CONFIG_USBHOST_ASYNCH */ - -/**************************************************************************** - * Name: imxrt_ioc_bottomhalf - * - * Description: - * EHCI USB Interrupt (USBINT) "Bottom Half" interrupt handler - * - * "The Host Controller sets this bit to 1 on the completion of a USB - * transaction, which results in the retirement of a Transfer Descriptor - * that had its IOC bit set. - * - * "The Host Controller also sets this bit to 1 when a short packet is - * detected (actual number of bytes received was less than the expected - * number of bytes)." - * - * Assumptions: The caller holds the EHCI exclsem - * - ****************************************************************************/ - -static inline void imxrt_ioc_bottomhalf(void) -{ - struct imxrt_qh_s *qh; - uint32_t *bp; - int ret; - - /* Check the Asynchronous Queue - * Make sure that the head of the asynchronous queue is invalidated. - */ - - up_invalidate_dcache((uintptr_t)&g_asynchead.hw, - (uintptr_t)&g_asynchead.hw + - sizeof(struct ehci_qh_s)); - - /* Set the back pointer to the forward QH pointer of the asynchronous - * queue head. - */ - - bp = (uint32_t *)&g_asynchead.hw.hlp; - qh = (struct imxrt_qh_s *) - imxrt_virtramaddr(imxrt_swap32(*bp) & QH_HLP_MASK); - - /* If the asynchronous queue is empty, then the forward point in the - * asynchronous queue head will point back to the queue head. - */ - - if (qh && qh != &g_asynchead) - { - /* Then traverse and operate on every QH and qTD in the asynchronous - * queue - */ - - ret = imxrt_qh_foreach(qh, &bp, imxrt_qh_ioccheck, NULL); - if (ret < 0) - { - usbhost_trace1(EHCI_TRACE1_QHFOREACH_FAILED, -ret); - } - } - -#ifndef CONFIG_USBHOST_INT_DISABLE - - /* Check the Interrupt Queue - * Make sure that the head of the interrupt queue is invalidated. - */ - - up_invalidate_dcache((uintptr_t)&g_intrhead.hw, - (uintptr_t)&g_intrhead.hw + sizeof(struct ehci_qh_s)); - - /* Set the back pointer to the forward qTD pointer of the asynchronous - * queue head. - */ - - bp = (uint32_t *)&g_intrhead.hw.hlp; - qh = (struct imxrt_qh_s *) - imxrt_virtramaddr(imxrt_swap32(*bp) & QH_HLP_MASK); - if (qh) - { - /* Then traverse and operate on every QH and qTD in the asynchronous - * queue. - */ - - ret = imxrt_qh_foreach(qh, &bp, imxrt_qh_ioccheck, NULL); - if (ret < 0) - { - usbhost_trace1(EHCI_TRACE1_QHFOREACH_FAILED, -ret); - } - } -#endif -} - -/**************************************************************************** - * Name: imxrt_portsc_bottomhalf - * - * Description: - * EHCI Port Change Detect "Bottom Half" interrupt handler - * - * "The Host Controller sets this bit to a one when any port for which the - * Port Owner bit is set to zero ... has a change bit transition from a - * zero to a one or a Force Port Resume bit transition from a zero to a - * one as a result of a J-K transition detected on a suspended port. - * This bit will also be set as a result of the Connect Status Change - * being set to a one after system software has relinquished ownership of - * a connected port by writing a one to a port's Port Owner bit... - * - * "This bit is allowed to be maintained in the Auxiliary power well. - * Alternatively, it is also acceptable that on a D3 to D0 transition of - * the EHCI HC device, this bit is loaded with the OR of all of the PORTSC - * change bits (including: Force port resume, over-current change, - * enable/disable change and connect status change)." - * - ****************************************************************************/ - -static inline void imxrt_portsc_bottomhalf(void) -{ - struct imxrt_rhport_s *rhport; - struct usbhost_hubport_s *hport; - uint32_t portsc; - int rhpndx; - - /* Handle root hub status change on each root port */ - - for (rhpndx = 0; rhpndx < IMXRT_EHCI_NRHPORT; rhpndx++) - { - rhport = &g_ehci.rhport[rhpndx]; - portsc = imxrt_getreg(&HCOR->portsc[rhpndx]); - - usbhost_vtrace2(EHCI_VTRACE2_PORTSC, rhpndx + 1, portsc); - - /* Handle port connection status change (CSC) events */ - - if ((portsc & EHCI_PORTSC_CSC) != 0) - { - usbhost_vtrace1(EHCI_VTRACE1_PORTSC_CSC, portsc); - - /* Check current connect status */ - - if ((portsc & EHCI_PORTSC_CCS) != 0) - { - /* Connected ... Did we just become connected? */ - - if (!rhport->connected) - { - /* Yes.. connected. */ - - rhport->connected = true; - - usbhost_vtrace2(EHCI_VTRACE2_PORTSC_CONNECTED, - rhpndx + 1, g_ehci.pscwait); - - /* Notify any waiters */ - - if (g_ehci.pscwait) - { - imxrt_givesem(&g_ehci.pscsem); - g_ehci.pscwait = false; - } - } - else - { - usbhost_vtrace1(EHCI_VTRACE1_PORTSC_CONNALREADY, portsc); - } - } - else - { - /* Disconnected... Did we just become disconnected? */ - - if (rhport->connected) - { - /* Yes.. disconnect the device */ - - usbhost_vtrace2(EHCI_VTRACE2_PORTSC_DISCONND, - rhpndx + 1, g_ehci.pscwait); - - rhport->connected = false; - rhport->lowspeed = false; - - /* Are we bound to a class instance? */ - - hport = &rhport->hport.hport; - if (hport->devclass) - { - /* Yes.. Disconnect the class */ - - CLASS_DISCONNECTED(hport->devclass); - hport->devclass = NULL; - } - - /* Notify any waiters for the Root Hub Status change - * event. - */ - - if (g_ehci.pscwait) - { - imxrt_givesem(&g_ehci.pscsem); - g_ehci.pscwait = false; - } - } - else - { - usbhost_vtrace1(EHCI_VTRACE1_PORTSC_DISCALREADY, portsc); - } - } - } - - /* Clear all pending port interrupt sources by writing a '1' to the - * corresponding bit in the PORTSC register. In addition, we need - * to preserve the values of all R/W bits (RO bits don't matter) - */ - - imxrt_putreg(portsc, &HCOR->portsc[rhpndx]); - } -} - -/**************************************************************************** - * Name: imxrt_syserr_bottomhalf - * - * Description: - * EHCI Host System Error "Bottom Half" interrupt handler - * - * "The Host Controller sets this bit to 1 when a serious error occurs - * during a host system access involving the Host Controller module. ... - * When this error occurs, the Host Controller clears the Run/Stop bit in - * the Command register to prevent further execution of the scheduled TDs." - * - ****************************************************************************/ - -static inline void imxrt_syserr_bottomhalf(void) -{ - usbhost_trace1(EHCI_TRACE1_SYSERR_INTR, 0); - DEBUGPANIC(); -} - -/**************************************************************************** - * Name: imxrt_async_advance_bottomhalf - * - * Description: - * EHCI Async Advance "Bottom Half" interrupt handler - * - * "System software can force the host controller to issue an interrupt the - * next time the host controller advances the asynchronous schedule by - * writing a one to the Interrupt on Async Advance Doorbell bit in the - * USBCMD register. This status bit indicates the assertion of that - * interrupt source." - * - ****************************************************************************/ - -static inline void imxrt_async_advance_bottomhalf(void) -{ - usbhost_vtrace1(EHCI_VTRACE1_AAINTR, 0); - - /* REVISIT: Could remove all tagged QH entries here */ -} - -/**************************************************************************** - * Name: imxrt_ehci_bottomhalf - * - * Description: - * EHCI "Bottom Half" interrupt handler. Runs on a work queue thread. - * - ****************************************************************************/ - -static void imxrt_ehci_bottomhalf(FAR void *arg) -{ - uint32_t pending = (uint32_t)arg; - - /* We need to have exclusive access to the EHCI data structures. Waiting - * here is not a good thing to do on the worker thread, but there is no - * real option (other than to reschedule and delay). - */ - - imxrt_takesem_noncancelable(&g_ehci.exclsem); - - /* Handle all unmasked interrupt sources - * USB Interrupt (USBINT) - * - * "The Host Controller sets this bit to 1 on the completion of a USB - * transaction, which results in the retirement of a Transfer Descriptor - * that had its IOC bit set. - * - * "The Host Controller also sets this bit to 1 when a short packet is - * detected (actual number of bytes received was less than the expected - * number of bytes)." - * - * USB Error Interrupt (USBERRINT) - * - * "The Host Controller sets this bit to 1 when completion of a USB - * transaction results in an error condition (e.g., error counter - * underflow). If the TD on which the error interrupt occurred also - * had its IOC bit set, both this bit and USBINT bit are set. ..." - * - * We do the same thing in either case: Traverse the asynchronous queue - * and remove all of the transfers that are no longer active. - */ - - if ((pending & (EHCI_INT_USBINT | EHCI_INT_USBERRINT)) != 0) - { - if ((pending & EHCI_INT_USBERRINT) != 0) - { - usbhost_trace1(EHCI_TRACE1_USBERR_INTR, pending); - } - else - { - usbhost_vtrace1(EHCI_VTRACE1_USBINTR, pending); - } - - imxrt_ioc_bottomhalf(); - } - - /* Port Change Detect - * - * "The Host Controller sets this bit to a one when any port for which - * the Port Owner bit is set to zero ... has a change bit transition - * from a zero to a one or a Force Port Resume bit transition from a zero - * to a one as a result of a J-K transition detected on a suspended port. - * This bit will also be set as a result of the Connect Status Change - * being set to a one after system software has relinquished ownership - * of a connected port by writing a one to a port's Port Owner bit... - * - * "This bit is allowed to be maintained in the Auxiliary power well. - * Alternatively, it is also acceptable that on a D3 to D0 transition - * of the EHCI HC device, this bit is loaded with the OR of all of the - * PORTSC change bits (including: Force port resume, over-current change, - * enable/disable change and connect status change)." - */ - - if ((pending & EHCI_INT_PORTSC) != 0) - { - imxrt_portsc_bottomhalf(); - } - - /* Frame List Rollover - * - * "The Host Controller sets this bit to a one when the Frame List Index - * ... rolls over from its maximum value to zero. The exact value at - * which the rollover occurs depends on the frame list size. For example, - * if the frame list size (as programmed in the Frame List Size field of - * the USBCMD register) is 1024, the Frame Index Register rolls over - * every time FRINDEX[13] toggles. Similarly, if the size is 512, the - * Host Controller sets this bit to a one every time FRINDEX[12] - * toggles." - */ - -#if 0 /* Not used */ - if ((pending & EHCI_INT_FLROLL) != 0) - { - imxrt_flroll_bottomhalf(); - } -#endif - - /* Host System Error - * - * "The Host Controller sets this bit to 1 when a serious error occurs - * during a host system access involving the Host Controller module. ... - * When this error occurs, the Host Controller clears the Run/Stop bit - * in the Command register to prevent further execution of the scheduled - * TDs." - */ - - if ((pending & EHCI_INT_SYSERROR) != 0) - { - uerr("Syserror\n"); - imxrt_syserr_bottomhalf(); - } - - /* Interrupt on Async Advance - * - * "System software can force the host controller to issue an interrupt - * the next time the host controller advances the asynchronous schedule - * by writing a one to the Interrupt on Async Advance Doorbell bit in - * the USBCMD register. This status bit indicates the assertion of that - * interrupt source." - */ - - if ((pending & EHCI_INT_AAINT) != 0) - { - uerr("Async Advance\n"); - imxrt_async_advance_bottomhalf(); - } - - /* We are done with the EHCI structures */ - - imxrt_givesem(&g_ehci.exclsem); - - /* Re-enable relevant EHCI interrupts. Interrupts should still be enabled - * at the level of the interrupt controller. - */ - - imxrt_putreg(EHCI_HANDLED_INTS, &HCOR->usbintr); -} - -/**************************************************************************** - * Name: imxrt_ehci_interrupt - * - * Description: - * EHCI "Top Half" interrupt handler - * - ****************************************************************************/ - -static int imxrt_ehci_interrupt(int irq, FAR void *context, FAR void *arg) -{ - uint32_t usbsts; - uint32_t pending; - uint32_t regval; - - /* Read Interrupt Status and mask out interrupts that are not enabled. */ - - usbsts = imxrt_getreg(&HCOR->usbsts); - regval = imxrt_getreg(&HCOR->usbintr); - -#ifdef CONFIG_USBHOST_TRACE - usbhost_vtrace1(EHCI_VTRACE1_TOPHALF, usbsts & regval); -#else - uinfo("USBSTS: %08x USBINTR: %08x\n", usbsts, regval); -#endif - - /* Handle all unmasked interrupt sources */ - - pending = usbsts & regval; - if (pending != 0) - { - /* Schedule interrupt handling work for the high priority worker - * thread so that we are not pressed for time and so that we can - * interrupt with other USB threads gracefully. - * - * The worker should be available now because we implement a handshake - * by controlling the EHCI interrupts. - */ - - DEBUGASSERT(work_available(&g_ehci.work)); - DEBUGVERIFY(work_queue(HPWORK, &g_ehci.work, imxrt_ehci_bottomhalf, - (FAR void *)pending, 0)); - - /* Disable further EHCI interrupts so that we do not overrun the work - * queue. - */ - - imxrt_putreg(0, &HCOR->usbintr); - - /* Clear all pending status bits by writing the value of the pending - * interrupt bits back to the status register. - */ - - imxrt_putreg(usbsts & EHCI_INT_ALLINTS, &HCOR->usbsts); - } - - return OK; -} - -/**************************************************************************** - * Name: imxrt_wait - * - * Description: - * Wait for a device to be connected or disconnected to/from a hub port. - * - * Input Parameters: - * conn - The USB host connection instance obtained as a parameter from the - * call to the USB driver initialization logic. - * hport - The location to return the hub port descriptor that detected the - * connection related event. - * - * Returned Value: - * Zero (OK) is returned on success when a device is connected or - * disconnected. This function will not return until either (1) a device is - * connected or disconnect to/from any hub port or until (2) some failure - * occurs. On a failure, a negated errno value is returned indicating the - * nature of the failure - * - * Assumptions: - * - Called from a single thread so no mutual exclusion is required. - * - Never called from an interrupt handler. - * - ****************************************************************************/ - -static int imxrt_wait(FAR struct usbhost_connection_s *conn, - FAR struct usbhost_hubport_s **hport) -{ - irqstate_t flags; - int rhpndx; - int ret; - - /* Loop until the connection state changes on one of the root hub ports or - * until an error occurs. - */ - - flags = enter_critical_section(); - for (; ; ) - { - /* Check for a change in the connection state on any root hub port */ - - for (rhpndx = 0; rhpndx < IMXRT_EHCI_NRHPORT; rhpndx++) - { - struct imxrt_rhport_s *rhport; - struct usbhost_hubport_s *connport; - - /* Has the connection state changed on the RH port? */ - - rhport = &g_ehci.rhport[rhpndx]; - connport = &rhport->hport.hport; - if (rhport->connected != connport->connected) - { - /* Yes.. Return the RH port to inform the caller which - * port has the connection change. - */ - - connport->connected = rhport->connected; - *hport = connport; - leave_critical_section(flags); - - usbhost_vtrace2(EHCI_VTRACE2_MONWAKEUP, - rhpndx + 1, rhport->connected); - return OK; - } - } - -#ifdef CONFIG_USBHOST_HUB - /* Is a device connected to an external hub? */ - - if (g_ehci.hport) - { - volatile struct usbhost_hubport_s *connport; - - /* Yes.. return the external hub port */ - - connport = g_ehci.hport; - g_ehci.hport = NULL; - - *hport = (struct usbhost_hubport_s *)connport; - leave_critical_section(flags); - - usbhost_vtrace2(EHCI_VTRACE2_MONWAKEUP, - connport->port + 1, connport->connected); - return OK; - } -#endif - - /* No changes on any port. Wait for a connection/disconnection event - * and check again - */ - - g_ehci.pscwait = true; - ret = imxrt_takesem(&g_ehci.pscsem); - if (ret < 0) - { - return ret; - } - } -} - -/**************************************************************************** - * Name: imxrt_enumerate - * - * Description: - * Enumerate the connected device. As part of this enumeration process, - * the driver will (1) get the device's configuration descriptor, (2) - * extract the class ID info from the configuration descriptor, (3) call - * usbhost_findclass() to find the class that supports this device, (4) - * call the create() method on the struct usbhost_registry_s interface - * to get a class instance, and finally (5) call the connect() method - * of the struct usbhost_class_s interface. After that, the class is in - * charge of the sequence of operations. - * - * Input Parameters: - * conn - The USB host connection instance obtained as a parameter from - * the call to the USB driver initialization logic. - * hport - The descriptor of the hub port that has the newly connected - * device. - * - * Returned Value: - * On success, zero (OK) is returned. On a failure, a negated errno value - * is returned indicating the nature of the failure - * - * Assumptions: - * This function will *not* be called from an interrupt handler. - * - ****************************************************************************/ - -static int imxrt_rh_enumerate(FAR struct usbhost_connection_s *conn, - FAR struct usbhost_hubport_s *hport) -{ - struct imxrt_rhport_s *rhport; - volatile uint32_t *regaddr; - uint32_t regval; - int rhpndx; - - DEBUGASSERT(conn != NULL && hport != NULL); - rhpndx = hport->port; - - DEBUGASSERT(rhpndx >= 0 && rhpndx < IMXRT_EHCI_NRHPORT); - rhport = &g_ehci.rhport[rhpndx]; - - /* Are we connected to a device? The caller should have called the wait() - * method first to be assured that a device is connected. - */ - - while (!rhport->connected) - { - /* No, return an error */ - - usbhost_vtrace1(EHCI_VTRACE1_ENUM_DISCONN, 0); - return -ENODEV; - } - - /* USB 2.0 spec says at least 50ms delay before port reset. - * REVISIT: I think this is wrong. It needs to hold the port in - * reset for 50Msec, not wait 50Msec before resetting. - */ - - nxsig_usleep(100 * 1000); - - /* Paragraph 2.3.9: - * - * "Line Status ... These bits reflect the current logical levels of the - * D+ (bit 11) and D- (bit 10) signal lines. These bits are used for - * detection of low-speed USB devices prior to the port reset and enable - * sequence. This field is valid only when the port enable bit is zero - * and the current connect status bit is set to a one." - * - * Bits[11:10] USB State Interpretation - * ----------- --------- -------------- - * 00b SE0 Not Low-speed device, perform EHCI reset - * 10b J-state Not Low-speed device, perform EHCI reset - * 01b K-state Low-speed device, release ownership of port - * - * NOTE: Low-speed devices could be detected by examining the PORTSC PSPD - * field after resetting the device. The more conventional way here, - * however, also appears to work. - */ - - regval = imxrt_getreg(&HCOR->portsc[rhpndx]); - if ((regval & EHCI_PORTSC_LSTATUS_MASK) == EHCI_PORTSC_LSTATUS_KSTATE) - { - /* EHCI Paragraph 2.3.9: - * - * "Port Owner ... This bit unconditionally goes to a 0b when the - * Configured bit in the CONFIGFLAG register makes a 0b to 1b - * transition. This bit unconditionally goes to 1b whenever the - * Configured bit is zero. - * - * "System software uses this field to release ownership of the - * port to a selected host controller (in the event that the - * attached device is not a high-speed device). Software writes - * a one to this bit when the attached device is not a high-speed - * device. A one in this bit means that a companion host - * controller owns and controls the port. .... - * - * EHCI Paragraph 4.2: - * - * "When a port is routed to a companion HC, it remains under the - * control of the companion HC until the device is disconnected - * from the root por ... When a disconnect occurs, the disconnect - * event is detected by both the companion HC port control and the - * EHCI port ownership control. On the event, the port ownership - * is returned immediately to the EHCI controller. The companion - * HC stack detects the disconnect and acknowledges as it would - * in an ordinary standalone implementation. Subsequent connects - * will be detected by the EHCI port register and the process will - * repeat." - */ - - hport->speed = USB_SPEED_LOW; - } - else - { - /* Assume full-speed for now */ - - hport->speed = USB_SPEED_FULL; - } - - /* Put the root hub port in reset. - * - * EHCI Paragraph 2.3.9: - * - * "The HCHalted bit in the USBSTS register should be a zero before - * software attempts to use [the Port Reset] bit. The host controller - * may hold Port Reset asserted to a one when the HCHalted bit is a one. - */ - - DEBUGASSERT((imxrt_getreg(&HCOR->usbsts) & EHCI_USBSTS_HALTED) == 0); - - /* EHCI paragraph 2.3.9: - * - * "When software writes a one to [the Port Reset] bit (from a zero), the - * bus reset sequence as defined in the USB Specification Revision 2.0 - * is started. Software writes a zero to this bit to terminate the bus - * reset sequence. Software must keep this bit at a one long enough to - * ensure the reset sequence, as specified in the USB Specification - * Revision 2.0, completes. Note: when software writes this bit to a - * one, it must also write a zero to the Port Enable bit." - */ - - regaddr = &HCOR->portsc[RHPNDX(rhport)]; - regval = imxrt_getreg(regaddr); - regval &= ~EHCI_PORTSC_PE; - regval |= EHCI_PORTSC_RESET; - imxrt_putreg(regval, regaddr); - - /* USB 2.0 "Root hubs must provide an aggregate reset period of at least - * 50 ms." - */ - - nxsig_usleep(50 * 1000); - - regval = imxrt_getreg(regaddr); - regval &= ~EHCI_PORTSC_RESET; - imxrt_putreg(regval, regaddr); - - /* Wait for the port reset to complete - * - * EHCI Paragraph 2.3.9: - * - * "Note that when software writes a zero to this bit there may be a - * delay before the bit status changes to a zero. The bit status will - * not read as a zero until after the reset has completed. If the port - * is in high-speed mode after reset is complete, the host controller - * will automatically enable this port (e.g. set the Port Enable bit - * to a one). A host controller must terminate the reset and stabilize - * the state of the port within 2 milliseconds of software transitioning - * this bit from a one to a zero ..." - */ - - while ((imxrt_getreg(regaddr) & EHCI_PORTSC_RESET) != 0); - nxsig_usleep(200 * 1000); - - /* EHCI Paragraph 4.2.2: - * - * "... The reset process is actually complete when software reads a zero - * in the PortReset bit. The EHCI Driver checks the PortEnable bit in - * the PORTSC register. If set to a one, the connected device is a high- - * speed device and EHCI Driver (root hub emulator) issues a change - * report to the hub driver and the hub driver continues to enumerate - * the attached device." - * - * "At the time the EHCI Driver receives the port reset and enable request - * the LineStatus bits might indicate a low-speed device. Additionally, - * when the port reset process is complete, the PortEnable field may - * indicate that a full-speed device is attached. In either case the EHCI - * driver sets the PortOwner bit in the PORTSC register to a one to - * release port ownership to a companion host controller." - * - * LPC31xx User Manual Paragraph 6.1.3: - * - * "In a standard EHCI controller design, the EHCI host controller driver - * detects a Full speed (FS) or Low speed (LS) device by noting if the - * port enable bit is set after the port reset operation. The port enable - * will only be set in a standard EHCI controller implementation after - * the port reset operation and when the host and device negotiate a - * High-Speed connection (i.e. Chirp completes successfully). Since this - * controller has an embedded Transaction Translator, the port enable - * will always be set after the port reset operation regardless of the - * result of the host device chirp result and the resulting port speed - * will be indicated by the PSPD field in PORTSC1. - */ - - regval = imxrt_getreg(&HCOR->portsc[rhpndx]); - - if ((regval & USBDEV_PRTSC1_PSPD_MASK) == USBDEV_PRTSC1_PSPD_HS) - { - /* High speed device */ - - hport->speed = USB_SPEED_HIGH; - } - else if ((regval & USBDEV_PRTSC1_PSPD_MASK) == USBDEV_PRTSC1_PSPD_FS) - { - /* Low- or Full- speed device. Set the port ownership bit. - * - * EHCI Paragraph 4.2: - * - * "When a port is routed to a companion HC, it remains under the - * control of the companion HC until the device is disconnected - * from the root por ... When a disconnect occurs, the disconnect - * event is detected by both the companion HC port control and the - * EHCI port ownership control. On the event, the port ownership - * is returned immediately to the EHCI controller. The companion - * HC stack detects the disconnect and acknowledges as it would - * in an ordinary standalone implementation. Subsequent connects - * will be detected by the EHCI port register and the process will - * repeat." - */ - - DEBUGASSERT(hport->speed == USB_SPEED_FULL); - } - - /* Otherwise it must be a low speed device */ - - else - { - DEBUGASSERT(hport->speed == USB_SPEED_LOW); - DEBUGASSERT((regval & USBDEV_PRTSC1_PSPD_MASK) == - USBDEV_PRTSC1_PSPD_LS); - } - - return OK; -} - -static int imxrt_enumerate(FAR struct usbhost_connection_s *conn, - FAR struct usbhost_hubport_s *hport) -{ - int ret; - - /* If this is a connection on the root hub, then we need to go to - * little more effort to get the device speed. If it is a connection - * on an external hub, then we already have that information. - */ - - DEBUGASSERT(hport); -#ifdef CONFIG_USBHOST_HUB - if (ROOTHUB(hport)) -#endif - { - ret = imxrt_rh_enumerate(conn, hport); - if (ret < 0) - { - return ret; - } - } - - /* Then let the common usbhost_enumerate do the real enumeration. */ - - usbhost_vtrace1(EHCI_VTRACE1_CLASSENUM, hport->port); - ret = usbhost_enumerate(hport, &hport->devclass); - if (ret < 0) - { - /* Failed to enumerate */ - - usbhost_trace2(EHCI_TRACE2_CLASSENUM_FAILED, hport->port + 1, -ret); - - /* If this is a root hub port, then marking the hub port not connected - * will cause imxrt_wait() to return and we will try the connection - * again. - */ - - hport->connected = false; - } - - return ret; -} - -/**************************************************************************** - * Name: imxrt_ep0configure - * - * Description: - * Configure endpoint 0. This method is normally used internally by the - * enumerate() method but is made available at the interface to support - * an external implementation of the enumeration logic. - * - * Input Parameters: - * drvr - The USB host driver instance obtained as a parameter from the - * call to the class create() method. - * funcaddr - The USB address of the function containing the endpoint that - * EP0 controls. A funcaddr of zero will be received if no address is - * yet assigned to the device. - * speed - The speed of the port USB_SPEED_LOW, _FULL, or _HIGH - * maxpacketsize - The maximum number of bytes that can be sent to or - * received from the endpoint in a single data packet - * - * Returned Value: - * On success, zero (OK) is returned. On a failure, a negated errno value - * is returned indicating the nature of the failure. - * - * Assumptions: - * This function will *not* be called from an interrupt handler. - * - ****************************************************************************/ - -static int imxrt_ep0configure(FAR struct usbhost_driver_s *drvr, - usbhost_ep_t ep0, uint8_t funcaddr, - uint8_t speed, uint16_t maxpacketsize) -{ - struct imxrt_epinfo_s *epinfo = (struct imxrt_epinfo_s *)ep0; - int ret; - - DEBUGASSERT(drvr != NULL && epinfo != NULL && maxpacketsize < 2048); - - /* We must have exclusive access to the EHCI data structures. */ - - ret = imxrt_takesem(&g_ehci.exclsem); - if (ret >= 0) - { - /* Remember the new device address and max packet size */ - - epinfo->devaddr = funcaddr; - epinfo->speed = speed; - epinfo->maxpacket = maxpacketsize; - - imxrt_givesem(&g_ehci.exclsem); - } - - return ret; -} - -/**************************************************************************** - * Name: imxrt_epalloc - * - * Description: - * Allocate and configure one endpoint. - * - * Input Parameters: - * drvr - The USB host driver instance obtained as a parameter from the - * call to the class create() method. - * epdesc - Describes the endpoint to be allocated. - * ep - A memory location provided by the caller in which to receive the - * allocated endpoint descriptor. - * - * Returned Value: - * On success, zero (OK) is returned. On a failure, a negated errno value - * is returned indicating the nature of the failure. - * - * Assumptions: - * This function will *not* be called from an interrupt handler. - * - ****************************************************************************/ - -static int imxrt_epalloc(FAR struct usbhost_driver_s *drvr, - const FAR struct usbhost_epdesc_s *epdesc, - usbhost_ep_t *ep) -{ - struct imxrt_epinfo_s *epinfo; - struct usbhost_hubport_s *hport; - - /* Sanity check. NOTE that this method should only be called if a device - * is connected (because we need a valid low speed indication). - */ - - DEBUGASSERT(drvr != 0 && epdesc != NULL && epdesc->hport != NULL && - ep != NULL); - hport = epdesc->hport; - - /* Terse output only if we are tracing */ - -#ifdef CONFIG_USBHOST_TRACE - usbhost_vtrace2(EHCI_VTRACE2_EPALLOC, epdesc->addr, epdesc->xfrtype); -#else - uinfo("EP%d DIR=%s FA=%08x TYPE=%d Interval=%d MaxPacket=%d\n", - epdesc->addr, epdesc->in ? "IN" : "OUT", hport->funcaddr, - epdesc->xfrtype, epdesc->interval, epdesc->mxpacketsize); -#endif - - /* Allocate a endpoint information structure */ - - epinfo = (struct imxrt_epinfo_s *) - kmm_zalloc(sizeof(struct imxrt_epinfo_s)); - if (!epinfo) - { - usbhost_trace1(EHCI_TRACE1_EPALLOC_FAILED, 0); - return -ENOMEM; - } - - /* Initialize the endpoint container (which is really just another form of - * 'struct usbhost_epdesc_s', packed differently and with additional - * information. A cleaner design might just embed struct usbhost_epdesc_s - * inside of struct imxrt_epinfo_s and just memcpy() here. - */ - - epinfo->epno = epdesc->addr; - epinfo->dirin = epdesc->in; - epinfo->devaddr = hport->funcaddr; -#ifndef CONFIG_USBHOST_INT_DISABLE - epinfo->interval = epdesc->interval; -#endif - epinfo->maxpacket = epdesc->mxpacketsize; - epinfo->xfrtype = epdesc->xfrtype; - epinfo->speed = hport->speed; - - /* The iocsem semaphore is used for signaling and, hence, should not have - * priority inheritance enabled. - */ - - nxsem_init(&epinfo->iocsem, 0, 0); - nxsem_set_protocol(&epinfo->iocsem, SEM_PRIO_NONE); - - /* Success.. return an opaque reference to the endpoint information - * structure instance - */ - - *ep = (usbhost_ep_t)epinfo; - return OK; -} - -/**************************************************************************** - * Name: imxrt_epfree - * - * Description: - * Free and endpoint previously allocated by DRVR_EPALLOC. - * - * Input Parameters: - * drvr - The USB host driver instance obtained as a parameter from the - * call to the class create() method. - * ep - The endpint to be freed. - * - * Returned Value: - * On success, zero (OK) is returned. On a failure, a negated errno value - * is returned indicating the nature of the failure - * - * Assumptions: - * This function will *not* be called from an interrupt handler. - * - ****************************************************************************/ - -static int imxrt_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep) -{ - struct imxrt_epinfo_s *epinfo = (struct imxrt_epinfo_s *)ep; - - /* There should not be any pending, transfers */ - - DEBUGASSERT(drvr && epinfo && epinfo->iocwait == 0); - - /* Free the container */ - - kmm_free(epinfo); - return OK; -} - -/**************************************************************************** - * Name: imxrt_alloc - * - * Description: - * Some hardware supports special memory in which request and descriptor - * data can be accessed more efficiently. This method provides a - * mechanism to allocate the request/descriptor memory. If the underlying - * hardware does not support such "special" memory, this functions may - * simply map to kmm_malloc(). - * - * This interface was optimized under a particular assumption. It was - * assumed that the driver maintains a pool of small, pre-allocated buffers - * for descriptor traffic. NOTE that size is not an input, but an output: - * The size of the pre-allocated buffer is returned. - * - * Input Parameters: - * drvr - The USB host driver instance obtained as a parameter from the - * call to the class create() method. - * buffer - The address of a memory location provided by the caller in - * which to return the allocated buffer memory address. - * maxlen - The address of a memory location provided by the caller in - * which to return the maximum size of the allocated buffer memory. - * - * Returned Value: - * On success, zero (OK) is returned. On a failure, a negated errno value - * is returned indicating the nature of the failure - * - * Assumptions: - * - Called from a single thread so no mutual exclusion is required. - * - Never called from an interrupt handler. - * - ****************************************************************************/ - -static int imxrt_alloc(FAR struct usbhost_driver_s *drvr, - FAR uint8_t **buffer, FAR size_t *maxlen) -{ - int ret = -ENOMEM; - DEBUGASSERT(drvr && buffer && maxlen); - - /* The only special requirements for transfer/descriptor buffers are that - * (1) they be aligned to a cache line boundary and (2) they are a - * multiple of the cache line size in length. - */ - - *buffer = (FAR uint8_t *)kmm_memalign(ARMV7M_DCACHE_LINESIZE, - IMXRT_EHCI_BUFSIZE); - if (*buffer) - { - *maxlen = IMXRT_EHCI_BUFSIZE; - ret = OK; - } - - return ret; -} - -/**************************************************************************** - * Name: imxrt_free - * - * Description: - * Some hardware supports special memory in which request and descriptor - * data can be accessed more efficiently. This method provides a - * mechanism to free that request/descriptor memory. If the underlying - * hardware does not support such "special" memory, this functions may - * simply map to kmm_free(). - * - * Input Parameters: - * drvr - The USB host driver instance obtained as a parameter from the - * call to the class create() method. - * buffer - The address of the allocated buffer memory to be freed. - * - * Returned Value: - * On success, zero (OK) is returned. On a failure, a negated errno value - * is returned indicating the nature of the failure - * - * Assumptions: - * - Never called from an interrupt handler. - * - ****************************************************************************/ - -static int imxrt_free(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer) -{ - DEBUGASSERT(drvr && buffer); - - /* No special action is require to free the transfer/descriptor buffer - * memory - */ - - kmm_free(buffer); - return OK; -} - -/**************************************************************************** - * Name: imxrt_ioalloc - * - * Description: - * Some hardware supports special memory in which larger IO buffers can - * be accessed more efficiently. This method provides a mechanism to - * allocate the request/descriptor memory. If the underlying hardware - * does not support such "special" memory, this functions may simply map - * to kumm_malloc. - * - * This interface differs from DRVR_ALLOC in that the buffers are variable- - * sized. - * - * Input Parameters: - * drvr - The USB host driver instance obtained as a parameter from the - * call to the class create() method. - * buffer - The address of a memory location provided by the caller in - * which to return the allocated buffer memory address. - * buflen - The size of the buffer required. - * - * Returned Value: - * On success, zero (OK) is returned. On a failure, a negated errno value - * is returned indicating the nature of the failure - * - * Assumptions: - * This function will *not* be called from an interrupt handler. - * - ****************************************************************************/ - -static int imxrt_ioalloc(FAR struct usbhost_driver_s *drvr, - FAR uint8_t **buffer, size_t buflen) -{ - DEBUGASSERT(drvr && buffer && buflen > 0); - - /* The only special requirements for I/O buffers are that (1) they be - * aligned to a cache line boundary, (2) they are a multiple of the cache - * line size in length, and (3) they might need to be user accessible - * (depending on how the class driver implements its buffering). - */ - - buflen = (buflen + DCACHE_LINEMASK) & ~DCACHE_LINEMASK; - *buffer = (FAR uint8_t *)kumm_memalign(ARMV7M_DCACHE_LINESIZE, buflen); - return *buffer ? OK : -ENOMEM; -} - -/**************************************************************************** - * Name: imxrt_iofree - * - * Description: - * Some hardware supports special memory in which IO data can be accessed - * more efficiently. This method provides a mechanism to free that IO - * buffer memory. If the underlying hardware does not support such - * "special" memory, this functions may simply map to kumm_free(). - * - * Input Parameters: - * drvr - The USB host driver instance obtained as a parameter from the - * call to the class create() method. - * buffer - The address of the allocated buffer memory to be freed. - * - * Returned Value: - * On success, zero (OK) is returned. On a failure, a negated errno value - * is returned indicating the nature of the failure - * - * Assumptions: - * This function will *not* be called from an interrupt handler. - * - ****************************************************************************/ - -static int imxrt_iofree(FAR struct usbhost_driver_s *drvr, - FAR uint8_t *buffer) -{ - DEBUGASSERT(drvr && buffer); - - /* No special action is require to free the I/O buffer memory */ - - kumm_free(buffer); - return OK; -} - -/**************************************************************************** - * Name: imxrt_ctrlin and imxrt_ctrlout - * - * Description: - * Process a IN or OUT request on the control endpoint. These methods - * will enqueue the request and wait for it to complete. Only one - * transfer may be queued; Neither these methods nor the transfer() method - * can be called again until the control transfer functions returns. - * - * These are blocking methods; these functions will not return until the - * control transfer has completed. - * - * Input Parameters: - * drvr - The USB host driver instance obtained as a parameter from the - * call to the class create() method. - * ep0 - The control endpoint to send/receive the control request. - * req - Describes the request to be sent. This request must lie in - * memory created by DRVR_ALLOC. - * buffer - A buffer used for sending the request and for returning any - * responses. This buffer must be large enough to hold the - * length value in the request description. buffer must have been - * allocated using DRVR_ALLOC. - * - * NOTE: On an IN transaction, req and buffer may refer to the same - * allocated memory. - * - * Returned Value: - * On success, zero (OK) is returned. On a failure, a negated errno value - * is returned indicating the nature of the failure - * - * Assumptions: - * - Called from a single thread so no mutual exclusion is required. - * - Never called from an interrupt handler. - * - ****************************************************************************/ - -static int imxrt_ctrlin(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, - FAR const struct usb_ctrlreq_s *req, - FAR uint8_t *buffer) -{ - struct imxrt_rhport_s *rhport = (struct imxrt_rhport_s *)drvr; - struct imxrt_epinfo_s *ep0info = (struct imxrt_epinfo_s *)ep0; - uint16_t len; - ssize_t nbytes; - int ret; - - DEBUGASSERT(rhport != NULL && ep0info != NULL && req != NULL); - - len = imxrt_read16(req->len); - - /* Terse output only if we are tracing */ - -#ifdef CONFIG_USBHOST_TRACE - usbhost_vtrace2(EHCI_VTRACE2_CTRLINOUT, RHPORT(rhport), req->req); -#else - uinfo("RHPort%d type: %02x req: %02x value: %02x%02x index: %02x%02x " - "len: %04x\n", - RHPORT(rhport), req->type, req->req, req->value[1], req->value[0], - req->index[1], req->index[0], len); -#endif - - /* We must have exclusive access to the EHCI hardware and data - * structures. - */ - - ret = imxrt_takesem(&g_ehci.exclsem); - if (ret < 0) - { - return ret; - } - - /* Set the request for the IOC event well BEFORE initiating the transfer. */ - - ret = imxrt_ioc_setup(rhport, ep0info); - if (ret != OK) - { - usbhost_trace1(EHCI_TRACE1_DEVDISCONNECTED, -ret); - goto errout_with_sem; - } - - /* Now initiate the transfer */ - - ret = imxrt_async_setup(rhport, ep0info, req, buffer, len); - if (ret < 0) - { - uerr("ERROR: imxrt_async_setup failed: %d\n", ret); - goto errout_with_iocwait; - } - - /* And wait for the transfer to complete */ - - nbytes = imxrt_transfer_wait(ep0info); - imxrt_givesem(&g_ehci.exclsem); - return nbytes >= 0 ? OK : (int)nbytes; - -errout_with_iocwait: - ep0info->iocwait = false; -errout_with_sem: - imxrt_givesem(&g_ehci.exclsem); - return ret; -} - -static int imxrt_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, - FAR const struct usb_ctrlreq_s *req, - FAR const uint8_t *buffer) -{ - /* imxrt_ctrlin can handle both directions. We just need to work around - * the differences in the function signatures. - */ - - return imxrt_ctrlin(drvr, ep0, req, (uint8_t *)buffer); -} - -/**************************************************************************** - * Name: imxrt_transfer - * - * Description: - * Process a request to handle a transfer descriptor. This method will - * enqueue the transfer request, blocking until the transfer completes. - * Only one transfer may be queued; Neither this method nor the ctrlin or - * ctrlout methods can be called again until this function returns. - * - * This is a blocking method; this functions will not return until the - * transfer has completed. - * - * Input Parameters: - * drvr - The USB host driver instance obtained as a parameter from the - * call to the class create() method. - * ep - The IN or OUT endpoint descriptor for the device endpoint on - * which to perform the transfer. - * buffer - A buffer containing the data to be sent (OUT endpoint) or - * received (IN endpoint). buffer must have been allocated using - * DRVR_ALLOC - * buflen - The length of the data to be sent or received. - * - * Returned Value: - * On success, a non-negative value is returned that indicates the number - * of bytes successfully transferred. On a failure, a negated errno value - * is returned that indicates the nature of the failure: - * - * EAGAIN - If devices NAKs the transfer (or NYET or other error where - * it may be appropriate to restart the entire transaction). - * EPERM - If the endpoint stalls - * EIO - On a TX or data toggle error - * EPIPE - Overrun errors - * - * Assumptions: - * - Called from a single thread so no mutual exclusion is required. - * - Never called from an interrupt handler. - * - ****************************************************************************/ - -static ssize_t imxrt_transfer(FAR struct usbhost_driver_s *drvr, - usbhost_ep_t ep, FAR uint8_t *buffer, - size_t buflen) -{ - struct imxrt_rhport_s *rhport = (struct imxrt_rhport_s *)drvr; - struct imxrt_epinfo_s *epinfo = (struct imxrt_epinfo_s *)ep; - ssize_t nbytes; - int ret; - - DEBUGASSERT(rhport && epinfo && buffer && buflen > 0); - - /* We must have exclusive access to the EHCI hardware and data - * structures. - */ - - ret = imxrt_takesem(&g_ehci.exclsem); - if (ret < 0) - { - return (ssize_t)ret; - } - - /* Set the request for the IOC event well BEFORE initiating the - * transfer. - */ - - ret = imxrt_ioc_setup(rhport, epinfo); - if (ret != OK) - { - usbhost_trace1(EHCI_TRACE1_DEVDISCONNECTED, -ret); - goto errout_with_sem; - } - - /* Initiate the transfer */ - - switch (epinfo->xfrtype) - { - case USB_EP_ATTR_XFER_BULK: - ret = imxrt_async_setup(rhport, epinfo, NULL, buffer, buflen); - break; - -#ifndef CONFIG_USBHOST_INT_DISABLE - case USB_EP_ATTR_XFER_INT: - ret = imxrt_intr_setup(rhport, epinfo, buffer, buflen); - break; -#endif - -#ifndef CONFIG_USBHOST_ISOC_DISABLE - case USB_EP_ATTR_XFER_ISOC: -# warning "Isochronous endpoint support not emplemented" -#endif - case USB_EP_ATTR_XFER_CONTROL: - default: - usbhost_trace1(EHCI_TRACE1_BADXFRTYPE, epinfo->xfrtype); - ret = -ENOSYS; - break; - } - - /* Check for errors in the setup of the transfer */ - - if (ret < 0) - { - goto errout_with_iocwait; - } - - /* Then wait for the transfer to complete */ - - nbytes = imxrt_transfer_wait(epinfo); - imxrt_givesem(&g_ehci.exclsem); - return nbytes; - -errout_with_iocwait: - epinfo->iocwait = false; -errout_with_sem: - uerr("!!!\n"); - imxrt_givesem(&g_ehci.exclsem); - return (ssize_t)ret; -} - -/**************************************************************************** - * Name: imxrt_asynch - * - * Description: - * Process a request to handle a transfer descriptor. This method will - * enqueue the transfer request and return immediately. When the transfer - * completes, the callback will be invoked with the provided transfer. - * This method is useful for receiving interrupt transfers which may come - * infrequently. - * - * Only one transfer may be queued; Neither this method nor the ctrlin or - * ctrlout methods can be called again until the transfer completes. - * - * Input Parameters: - * drvr - The USB host driver instance obtained as a parameter from - * the call to the class create() method. - * ep - The IN or OUT endpoint descriptor for the device endpoint on - * which to perform the transfer. - * buffer - A buffer containing the data to be sent (OUT endpoint) or - * received (IN endpoint). buffer must have been allocated - * using DRVR_ALLOC - * buflen - The length of the data to be sent or received. - * callback - This function will be called when the transfer completes. - * arg - The arbitrary parameter that will be passed to the callback - * function when the transfer completes. - * - * Returned Value: - * On success, zero (OK) is returned. On a failure, a negated errno value - * is returned indicating the nature of the failure - * - * Assumptions: - * - Called from a single thread so no mutual exclusion is required. - * - Never called from an interrupt handler. - * - ****************************************************************************/ - -#ifdef CONFIG_USBHOST_ASYNCH -static int imxrt_asynch(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, - FAR uint8_t *buffer, size_t buflen, - usbhost_asynch_t callback, FAR void *arg) -{ - struct imxrt_rhport_s *rhport = (struct imxrt_rhport_s *)drvr; - struct imxrt_epinfo_s *epinfo = (struct imxrt_epinfo_s *)ep; - int ret; - - DEBUGASSERT(rhport && epinfo && buffer && buflen > 0); - - /* We must have exclusive access to the EHCI hardware and data - * structures. - */ - - ret = imxrt_takesem(&g_ehci.exclsem); - if (ret < 0) - { - return ret; - } - - /* Set the request for the callback well BEFORE initiating the transfer. */ - - ret = imxrt_ioc_async_setup(rhport, epinfo, callback, arg); - if (ret != OK) - { - usbhost_trace1(EHCI_TRACE1_DEVDISCONNECTED, -ret); - goto errout_with_sem; - } - - /* Initiate the transfer */ - - switch (epinfo->xfrtype) - { - case USB_EP_ATTR_XFER_BULK: - ret = imxrt_async_setup(rhport, epinfo, NULL, buffer, buflen); - break; - -#ifndef CONFIG_USBHOST_INT_DISABLE - case USB_EP_ATTR_XFER_INT: - ret = imxrt_intr_setup(rhport, epinfo, buffer, buflen); - break; -#endif - -#ifndef CONFIG_USBHOST_ISOC_DISABLE - case USB_EP_ATTR_XFER_ISOC: -# warning "Isochronous endpoint support not emplemented" -#endif - case USB_EP_ATTR_XFER_CONTROL: - default: - usbhost_trace1(EHCI_TRACE1_BADXFRTYPE, epinfo->xfrtype); - ret = -ENOSYS; - break; - } - - /* Check for errors in the setup of the transfer */ - - if (ret < 0) - { - goto errout_with_callback; - } - - /* The transfer is in progress */ - - imxrt_givesem(&g_ehci.exclsem); - return OK; - -errout_with_callback: - epinfo->callback = NULL; - epinfo->arg = NULL; -errout_with_sem: - imxrt_givesem(&g_ehci.exclsem); - return ret; -} -#endif /* CONFIG_USBHOST_ASYNCH */ - -/**************************************************************************** - * Name: imxrt_cancel - * - * Description: - * Cancel a pending transfer on an endpoint. Canceled synchronous or - * asynchronous transfer will complete normally with the error -ESHUTDOWN. - * - * Input Parameters: - * drvr - The USB host driver instance obtained as a parameter from the - * call to the class create() method. - * ep - The IN or OUT endpoint descriptor for the device endpoint on - * which an asynchronous transfer should be transferred. - * - * Returned Value: - * On success, zero (OK) is returned. On a failure, a negated errno value - * is returned indicating the nature of the failure - * - ****************************************************************************/ - -static int imxrt_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep) -{ - struct imxrt_epinfo_s *epinfo = (struct imxrt_epinfo_s *)ep; - struct imxrt_qh_s *qh; -#ifdef CONFIG_USBHOST_ASYNCH - usbhost_asynch_t callback; - void *arg; -#endif - uint32_t *bp; - irqstate_t flags; - bool iocwait; - int ret; - - DEBUGASSERT(epinfo); - - /* We must have exclusive access to the EHCI hardware and data structures. - * This will prevent servicing any transfer completion events while we - * perform the the cancellation, but will not prevent DMA-related race - * conditions. - * - * REVISIT: This won't work. This function must be callable from the - * interrupt level. - */ - - ret = imxrt_takesem(&g_ehci.exclsem); - if (ret < 0) - { - return ret; - } - - /* Sample and reset all transfer termination information. This will - * prevent any callbacks from occurring while are performing the - * cancellation. The transfer may still be in progress, however, so this - * does not eliminate other DMA-related race conditions. - */ - - flags = enter_critical_section(); -#ifdef CONFIG_USBHOST_ASYNCH - callback = epinfo->callback; - arg = epinfo->arg; -#endif - iocwait = epinfo->iocwait; - -#ifdef CONFIG_USBHOST_ASYNCH - epinfo->callback = NULL; - epinfo->arg = NULL; -#endif - epinfo->iocwait = false; - - /* This will prevent any callbacks from occurring while are performing - * the cancellation. The transfer may still be in progress, however, so - * this does not eliminate other DMA-related race conditions. - */ - - epinfo->callback = NULL; - epinfo->arg = NULL; - leave_critical_section(flags); - - /* Bail if there is no transfer in progress for this endpoint */ - -#ifdef CONFIG_USBHOST_ASYNCH - if (callback == NULL && !iocwait) -#else - if (!iocwait) -#endif - { - ret = OK; - goto errout_with_sem; - } - - /* Handle the cancellation according to the type of the transfer */ - - switch (epinfo->xfrtype) - { - case USB_EP_ATTR_XFER_CONTROL: - case USB_EP_ATTR_XFER_BULK: - { - /* Get the horizontal pointer from the head of the asynchronous - * queue. - */ - - bp = (uint32_t *)&g_asynchead.hw.hlp; - qh = (struct imxrt_qh_s *) - imxrt_virtramaddr(imxrt_swap32(*bp) & QH_HLP_MASK); - - /* If the asynchronous queue is empty, then the forward point in - * the asynchronous queue head will point back to the queue - * head. - */ - - if (qh && qh != &g_asynchead) - { - /* Claim that we successfully cancelled the transfer */ - - ret = OK; - goto exit_terminate; - } - } - break; - -#ifndef CONFIG_USBHOST_INT_DISABLE - case USB_EP_ATTR_XFER_INT: - { - /* Get the horizontal pointer from the head of the interrupt - * queue. - */ - - bp = (uint32_t *)&g_intrhead.hw.hlp; - qh = (struct imxrt_qh_s *) - imxrt_virtramaddr(imxrt_swap32(*bp) & QH_HLP_MASK); - if (qh) - { - /* if the queue is empty, then just claim that we successfully - * canceled the transfer. - */ - - ret = OK; - goto exit_terminate; - } - } - break; -#endif - -#ifndef CONFIG_USBHOST_ISOC_DISABLE - case USB_EP_ATTR_XFER_ISOC: -# warning "Isochronous endpoint support not emplemented" -#endif - default: - usbhost_trace1(EHCI_TRACE1_BADXFRTYPE, epinfo->xfrtype); - ret = -ENOSYS; - goto errout_with_sem; - } - - /* Find and remove the QH. There are four possibilities: - * - * 1) The transfer has already completed and the QH is no longer in the - * list. In this case, sam_hq_foreach will return zero - * 2a) The transfer is not active and still pending. It was removed from - * the list and sam_hq_foreach will return one. - * 2b) The is active but not yet complete. This is currently handled the - * same as 2a). REVISIT: This needs to be fixed. - * 3) Some bad happened and sam_hq_foreach returned an error code < 0. - */ - - ret = imxrt_qh_foreach(qh, &bp, imxrt_qh_cancel, epinfo); - if (ret < 0) - { - usbhost_trace1(EHCI_TRACE1_QTDFOREACH_FAILED, -ret); - } - - /* Was there a pending synchronous transfer? */ - -exit_terminate: - epinfo->result = -ESHUTDOWN; -#ifdef CONFIG_USBHOST_ASYNCH - if (iocwait) - { - /* Yes... wake it up */ - - DEBUGASSERT(callback == NULL); - imxrt_givesem(&epinfo->iocsem); - } - - /* No.. Is there a pending asynchronous transfer? */ - - else /* if (callback != NULL) */ - { - /* Yes.. perform the callback */ - - callback(arg, -ESHUTDOWN); - } - -#else - /* Wake up the waiting thread */ - - imxrt_givesem(&epinfo->iocsem); -#endif - -errout_with_sem: - imxrt_givesem(&g_ehci.exclsem); - return ret; -} - -/**************************************************************************** - * Name: imxrt_connect - * - * Description: - * New connections may be detected by an attached hub. This method is the - * mechanism that is used by the hub class to introduce a new connection - * and port description to the system. - * - * Input Parameters: - * drvr - The USB host driver instance obtained as a parameter from the - * call to the class create() method. - * hport - The descriptor of the hub port that detected the connection - * related event - * connected - True: device connected; false: device disconnected - * - * Returned Value: - * On success, zero (OK) is returned. On a failure, a negated errno value - * is returned indicating the nature of the failure - * - ****************************************************************************/ - -#ifdef CONFIG_USBHOST_HUB -static int imxrt_connect(FAR struct usbhost_driver_s *drvr, - FAR struct usbhost_hubport_s *hport, - bool connected) -{ - irqstate_t flags; - - /* Set the connected/disconnected flag */ - - hport->connected = connected; - uinfo("Hub port %d connected: %s\n", - hport->port, connected ? "YES" : "NO"); - - /* Report the connection event */ - - flags = enter_critical_section(); - DEBUGASSERT(g_ehci.hport == NULL); /* REVISIT */ - - g_ehci.hport = hport; - if (g_ehci.pscwait) - { - g_ehci.pscwait = false; - imxrt_givesem(&g_ehci.pscsem); - } - - leave_critical_section(flags); - return OK; -} -#endif - -/**************************************************************************** - * Name: imxrt_disconnect - * - * Description: - * Called by the class when an error occurs and driver has been - * disconnected. The USB host driver should discard the handle to the - * class instance (it is stale) and not attempt any further interaction - * with the class driver instance (until a new instance is received from - * the create() method). The driver should not called the class' - * disconnected() method. - * - * Input Parameters: - * drvr - The USB host driver instance obtained as a parameter from the - * call to the class create() method. - * hport - The port from which the device is being disconnected. Might be - * a port on a hub. - * - * Returned Value: - * None - * - * Assumptions: - * - Only a single class bound to a single device is supported. - * - Never called from an interrupt handler. - * - ****************************************************************************/ - -static void imxrt_disconnect(FAR struct usbhost_driver_s *drvr, - FAR struct usbhost_hubport_s *hport) -{ - DEBUGASSERT(hport != NULL); - hport->devclass = NULL; -} - -/**************************************************************************** - * Name: imxrt_reset - * - * Description: - * Set the HCRESET bit in the USBCMD register to reset the EHCI hardware. - * - * Table 2-9. USBCMD - USB Command Register Bit Definitions - * - * "Host Controller Reset (HCRESET) ... This control bit is used by - * software to reset the host controller. The effects of this on Root - * Hub registers are similar to a Chip Hardware Reset. - * - * "When software writes a one to this bit, the Host Controller resets its - * internal pipelines, timers, counters, state machines, etc. to their - * initial value. Any transaction currently in progress on USB is - * immediately terminated. A USB reset is not driven on downstream - * ports. - * - * "PCI Configuration registers are not affected by this reset. All - * operational registers, including port registers and port state - * machines are set to their initial values. Port ownership reverts - * to the companion host controller(s)... Software must reinitialize - * the host controller ... in order to return the host controller to - * an operational state. - * - * "This bit is set to zero by the Host Controller when the reset process - * is complete. Software cannot terminate the reset process early by - * writing a zero to this register. Software should not set this bit to - * a one when the HCHalted bit in the USBSTS register is a zero. - * Attempting to reset an actively running host controller will result - * in undefined behavior." - * - * Input Parameters: - * None. - * - * Returned Value: - * Zero (OK) is returned on success; A negated errno value is returned - * on failure. - * - * Assumptions: - * - Called during the initialization of the EHCI. - * - ****************************************************************************/ - -static int imxrt_reset(void) -{ - uint32_t regval; - unsigned int timeout; - - /* Make sure that the EHCI is halted: "When [the Run/Stop] bit is set to - * 0, the Host Controller completes the current transaction on the USB and - * then halts. The HC Halted bit in the status register indicates when the - * Host Controller has finished the transaction and has entered the - * stopped state..." - */ - - imxrt_putreg(0, &HCOR->usbcmd); - - /* "... Software should not set [HCRESET] to a one when the HCHalted bit in - * the USBSTS register is a zero. Attempting to reset an actively running - * host controller will result in undefined behavior." - */ - - timeout = 0; - do - { - /* Wait one microsecond and update the timeout counter */ - - up_udelay(1); - timeout++; - - /* Get the current value of the USBSTS register. This loop will - * terminate when either the timeout exceeds one millisecond or when - * the HCHalted bit is no longer set in the USBSTS register. - */ - - regval = imxrt_getreg(&HCOR->usbsts); - } - while (((regval & EHCI_USBSTS_HALTED) == 0) && (timeout < 1000)); - - /* Is the EHCI still running? Did we timeout? */ - - if ((regval & EHCI_USBSTS_HALTED) == 0) - { - usbhost_trace1(EHCI_TRACE1_HCHALTED_TIMEOUT, regval); - return -ETIMEDOUT; - } - - /* Now we can set the HCReset bit in the USBCMD register to initiate the - * reset - */ - - regval = imxrt_getreg(&HCOR->usbcmd); - regval |= EHCI_USBCMD_HCRESET; - imxrt_putreg(regval, &HCOR->usbcmd); - - /* Wait for the HCReset bit to become clear */ - - do - { - /* Wait five microsecondw and update the timeout counter */ - - up_udelay(5); - timeout += 5; - - /* Get the current value of the USBCMD register. This loop will - * terminate when either the timeout exceeds one second or when the - * HCReset bit is no longer set in the USBSTS register. - */ - - regval = imxrt_getreg(&HCOR->usbcmd); - } - while (((regval & EHCI_USBCMD_HCRESET) != 0) && (timeout < 1000000)); - - /* Return either success or a timeout */ - - return (regval & EHCI_USBCMD_HCRESET) != 0 ? -ETIMEDOUT : OK; -} - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: imxrt_ehci_initialize - * - * Description: - * Initialize USB EHCI host controller hardware. - * - * Input Parameters: - * controller -- If the device supports more than one EHCI interface, then - * this identifies which controller is being initialized. Normally, this - * is just zero. - * - * Returned Value: - * And instance of the USB host interface. The controlling task should - * use this interface to (1) call the wait() method to wait for a device - * to be connected, and (2) call the enumerate() method to bind the device - * to a class driver. - * - * Assumptions: - * - This function should called in the initialization sequence in order - * to initialize the USB device functionality. - * - Class drivers should be initialized prior to calling this function. - * Otherwise, there is a race condition if the device is already connected. - * - ****************************************************************************/ - -FAR struct usbhost_connection_s *imxrt_ehci_initialize(int controller) -{ - FAR struct usbhost_hubport_s *hport; - uint32_t regval; -# if defined(CONFIG_DEBUG_USB) && defined(CONFIG_DEBUG_INFO) - uint16_t regval16; - unsigned int nports; -# endif - uintptr_t physaddr; - int ret; - int i; - - /* Sanity checks */ - - DEBUGASSERT(controller == 0); - DEBUGASSERT(((uintptr_t) & g_asynchead & 0x1f) == 0); - DEBUGASSERT((sizeof(struct imxrt_qh_s) & 0x1f) == 0); - DEBUGASSERT((sizeof(struct imxrt_qtd_s) & 0x1f) == 0); - -# ifdef CONFIG_IMXRT_EHCI_PREALLOCATE - DEBUGASSERT(((uintptr_t) & g_qhpool & 0x1f) == 0); - DEBUGASSERT(((uintptr_t) & g_qtdpool & 0x1f) == 0); -# endif - -# ifndef CONFIG_USBHOST_INT_DISABLE - DEBUGASSERT(((uintptr_t) & g_intrhead & 0x1f) == 0); -# ifdef CONFIG_IMXRT_EHCI_PREALLOCATE - DEBUGASSERT(((uintptr_t) g_framelist & 0xfff) == 0); -# endif -# endif /* CONFIG_USBHOST_INT_DISABLE */ - - /* Software Configuration *************************************************/ - - usbhost_vtrace1(EHCI_VTRACE1_INITIALIZING, 0); - - /* Initialize the EHCI state data structure */ - - nxsem_init(&g_ehci.exclsem, 0, 1); - nxsem_init(&g_ehci.pscsem, 0, 0); - - /* The pscsem semaphore is used for signaling and, hence, should not have - * priority inheritance enabled. - */ - - nxsem_set_protocol(&g_ehci.pscsem, SEM_PRIO_NONE); - - /* Initialize EP0 */ - - nxsem_init(&g_ehci.ep0.iocsem, 0, 1); - - /* Initialize the root hub port structures */ - - for (i = 0; i < IMXRT_EHCI_NRHPORT; i++) - { - struct imxrt_rhport_s *rhport = &g_ehci.rhport[i]; - - /* Initialize the device operations */ - - rhport->drvr.ep0configure = imxrt_ep0configure; - rhport->drvr.epalloc = imxrt_epalloc; - rhport->drvr.epfree = imxrt_epfree; - rhport->drvr.alloc = imxrt_alloc; - rhport->drvr.free = imxrt_free; - rhport->drvr.ioalloc = imxrt_ioalloc; - rhport->drvr.iofree = imxrt_iofree; - rhport->drvr.ctrlin = imxrt_ctrlin; - rhport->drvr.ctrlout = imxrt_ctrlout; - rhport->drvr.transfer = imxrt_transfer; -# ifdef CONFIG_USBHOST_ASYNCH - rhport->drvr.asynch = imxrt_asynch; -# endif - rhport->drvr.cancel = imxrt_cancel; -# ifdef CONFIG_USBHOST_HUB - rhport->drvr.connect = imxrt_connect; -# endif - rhport->drvr.disconnect = imxrt_disconnect; - - /* Initialize EP0 */ - - rhport->ep0.xfrtype = USB_EP_ATTR_XFER_CONTROL; - rhport->ep0.speed = USB_SPEED_FULL; - rhport->ep0.maxpacket = 8; - - /* The EP0 iocsem semaphore is used for signaling and, hence, should - * not have priority inheritance enabled. - */ - - nxsem_init(&rhport->ep0.iocsem, 0, 0); - nxsem_set_protocol(&rhport->ep0.iocsem, SEM_PRIO_NONE); - - /* Initialize the public port representation */ - - hport = &rhport->hport.hport; - hport->drvr = &rhport->drvr; -# ifdef CONFIG_USBHOST_HUB - hport->parent = NULL; -# endif - hport->ep0 = &rhport->ep0; - hport->port = i; - hport->speed = USB_SPEED_FULL; - - /* Initialize function address generation logic */ - - usbhost_devaddr_initialize(&rhport->hport); - } - -# ifndef CONFIG_IMXRT_EHCI_PREALLOCATE - /* Allocate a pool of free Queue Head (QH) structures */ - - g_qhpool = - (struct imxrt_qh_s *)kmm_memalign(32, - CONFIG_IMXRT_EHCI_NQHS * - sizeof(struct imxrt_qh_s)); - if (!g_qhpool) - { - usbhost_trace1(EHCI_TRACE1_QHPOOLALLOC_FAILED, 0); - return NULL; - } -# endif - - /* Initialize the list of free Queue Head (QH) structures */ - - for (i = 0; i < CONFIG_IMXRT_EHCI_NQHS; i++) - { - /* Put the QH structure in a free list */ - - imxrt_qh_free(&g_qhpool[i]); - } - -# ifndef CONFIG_IMXRT_EHCI_PREALLOCATE - /* Allocate a pool of free Transfer Descriptor (qTD) structures */ - - g_qtdpool = - (struct imxrt_qtd_s *)kmm_memalign(32, - CONFIG_IMXRT_EHCI_NQTDS * - sizeof(struct imxrt_qtd_s)); - if (!g_qtdpool) - { - usbhost_trace1(EHCI_TRACE1_QTDPOOLALLOC_FAILED, 0); - kmm_free(g_qhpool); - return NULL; - } -# endif - -# if !defined(CONFIG_IMXRT_EHCI_PREALLOCATE) && !defined(CONFIG_USBHOST_INT_DISABLE) - /* Allocate the periodic framelist */ - - g_framelist = (uint32_t *) - kmm_memalign(4096, FRAME_LIST_SIZE * sizeof(uint32_t)); - if (!g_framelist) - { - usbhost_trace1(EHCI_TRACE1_PERFLALLOC_FAILED, 0); - kmm_free(g_qhpool); - kmm_free(g_qtdpool); - return NULL; - } -# endif - - /* Initialize the list of free Transfer Descriptor (qTD) structures */ - - for (i = 0; i < CONFIG_IMXRT_EHCI_NQTDS; i++) - { - /* Put the TD in a free list */ - - imxrt_qtd_free(&g_qtdpool[i]); - } - - /* EHCI Hardware Configuration ********************************************/ - - imxrt_clockall_usboh3(); - - /* Reset the controller from the OTG peripheral */ - - putreg32(USBDEV_USBCMD_RST, IMXRT_USBDEV_USBCMD); - while ((getreg32(IMXRT_USBDEV_USBCMD) & USBDEV_USBCMD_RST) != 0); - - /* Program the controller to be the USB host controller Fixed selections: - * CM = Host mode ES = 0, Little endian mode. SLOM Not used in host mode. - * VBPS = 1, off-chip power source Configurable selections: SDIS = 1, - * Stream disable mode. Eliminates overruns/underruns at the expense of - * some performance. - */ - -# ifdef CONFIG_IMXRT_EHCI_SDIS - putreg32(USBHOST_USBMODE_CM_HOST | USBHOST_USBMODE_SDIS | - USBHOST_USBMODE_VBPS, IMXRT_USBDEV_USBMODE); -# else - putreg32(USBHOST_USBMODE_CM_HOST | USBHOST_USBMODE_VBPS, - IMXRT_USBDEV_USBMODE); -# endif - - /* Reset the EHCI hardware */ - - ret = imxrt_reset(); - if (ret < 0) - { - usbhost_trace1(EHCI_TRACE1_RESET_FAILED, -ret); - return NULL; - } - - /* Re-program the USB host controller. As implemented, imxrt_reset() - * requires the host mode setup in order to work. However, we lose the - * host configuration in the reset. - */ - -# ifdef CONFIG_IMXRT_EHCI_SDIS - putreg32(USBHOST_USBMODE_CM_HOST | USBHOST_USBMODE_SDIS | - USBHOST_USBMODE_VBPS, IMXRT_USBDEV_USBMODE); -# else - putreg32(USBHOST_USBMODE_CM_HOST | USBHOST_USBMODE_VBPS, - IMXRT_USBDEV_USBMODE); -# endif - - /* Disable all interrupts */ - - imxrt_putreg(0, &HCOR->usbintr); - - /* Clear pending interrupts. Bits in the USBSTS register are cleared by - * writing a '1' to the corresponding bit. - */ - - imxrt_putreg(EHCI_INT_ALLINTS, &HCOR->usbsts); - -# if defined(CONFIG_DEBUG_USB) && defined(CONFIG_DEBUG_INFO) - /* Show the EHCI version */ - - regval16 = imxrt_swap16(HCCR->hciversion); - usbhost_vtrace2(EHCI_VTRACE2_HCIVERSION, regval16 >> 8, regval16 & 0xff); - - /* Verify that the correct number of ports is reported */ - - regval = imxrt_getreg(&HCCR->hcsparams); - nports = (regval & EHCI_HCSPARAMS_NPORTS_MASK) >> - EHCI_HCSPARAMS_NPORTS_SHIFT; - - usbhost_vtrace2(EHCI_VTRACE2_HCSPARAMS, nports, regval); - DEBUGASSERT(nports == IMXRT_EHCI_NRHPORT); - - /* Show the HCCPARAMS register */ - - regval = imxrt_getreg(&HCCR->hccparams); - usbhost_vtrace1(EHCI_VTRACE1_HCCPARAMS, regval); -# endif - - /* Initialize the head of the asynchronous queue/reclamation list. "In - * order to communicate with devices via the asynchronous schedule, system - * software must write the ASYNDLISTADDR register with the address of a - * control or bulk queue head. Software must then enable the asynchronous - * schedule by writing a one to the Asynchronous Schedule Enable bit in - * the USBCMD register. In order to communicate with devices via the - * periodic schedule, system software must enable the periodic schedule by - * writing a one to the Periodic Schedule Enable bit in the USBCMD - * register. Note that the schedules can be turned on before the first - * port is reset (and enabled)." - */ - - memset(&g_asynchead, 0, sizeof(struct imxrt_qh_s)); - physaddr = imxrt_physramaddr((uintptr_t) & g_asynchead); - g_asynchead.hw.hlp = imxrt_swap32(physaddr | QH_HLP_TYP_QH); - g_asynchead.hw.epchar = imxrt_swap32(QH_EPCHAR_H | QH_EPCHAR_EPS_FULL); - g_asynchead.hw.overlay.nqp = imxrt_swap32(QH_NQP_T); - g_asynchead.hw.overlay.alt = imxrt_swap32(QH_NQP_T); - g_asynchead.hw.overlay.token = imxrt_swap32(QH_TOKEN_HALTED); - g_asynchead.fqp = imxrt_swap32(QTD_NQP_T); - - /* Set the Current Asynchronous List Address. */ - - up_flush_dcache((uintptr_t)&g_asynchead.hw, - (uintptr_t)&g_asynchead.hw + sizeof(struct ehci_qh_s)); - - imxrt_putreg(imxrt_swap32(physaddr), &HCOR->asynclistaddr); - -# ifndef CONFIG_USBHOST_INT_DISABLE - - /* Initialize the head of the periodic list. Since Isochronous endpoints - * are not not yet supported, each element of the frame list is initialized - * to point to the Interrupt Queue Head (g_intrhead). - */ - - memset(&g_intrhead, 0, sizeof(struct imxrt_qh_s)); - g_intrhead.hw.hlp = imxrt_swap32(QH_HLP_T); - g_intrhead.hw.overlay.nqp = imxrt_swap32(QH_NQP_T); - g_intrhead.hw.overlay.alt = imxrt_swap32(QH_NQP_T); - g_intrhead.hw.overlay.token = imxrt_swap32(QH_TOKEN_HALTED); - g_intrhead.hw.epcaps = imxrt_swap32(QH_EPCAPS_SSMASK(1)); - - /* Attach the periodic QH to Period Frame List */ - - physaddr = imxrt_physramaddr((uintptr_t) & g_intrhead); - for (i = 0; i < FRAME_LIST_SIZE; i++) - { - g_framelist[i] = imxrt_swap32(physaddr) | PFL_TYP_QH; - } - - /* Set the Periodic Frame List Base Address. */ - - physaddr = imxrt_physramaddr((uintptr_t) g_framelist); - imxrt_putreg(imxrt_swap32(physaddr), &HCOR->periodiclistbase); -# endif - - /* Enable the asynchronous schedule and, possibly enable the periodic - * schedule and set the frame list size. - */ - - regval = imxrt_getreg(&HCOR->usbcmd); - regval &= ~(EHCI_USBCMD_HCRESET | EHCI_USBCMD_FLSIZE_MASK | - EHCI_USBCMD_FLSIZE_MASK | EHCI_USBCMD_PSEN | - EHCI_USBCMD_IAADB | EHCI_USBCMD_LRESET); - regval |= EHCI_USBCMD_ASEN; - -# ifndef CONFIG_USBHOST_INT_DISABLE - regval |= EHCI_USBCMD_PSEN; -# if FRAME_LIST_SIZE == 1024 - regval |= EHCI_USBCMD_FLSIZE_1024; -# elif FRAME_LIST_SIZE == 512 - regval |= EHCI_USBCMD_FLSIZE_512; -# elif FRAME_LIST_SIZE == 512 - regval |= EHCI_USBCMD_FLSIZE_256; -# else -# error Unsupported frame size list size -# endif -# endif - - imxrt_putreg(regval, &HCOR->usbcmd); - - /* Start the host controller by setting the RUN bit in the USBCMD - * register. - */ - - regval = imxrt_getreg(&HCOR->usbcmd); - regval |= EHCI_USBCMD_RUN; - imxrt_putreg(regval, &HCOR->usbcmd); - - /* Route all ports to this host controller by setting the CONFIG flag. */ - - regval = imxrt_getreg(&HCOR->configflag); - regval |= EHCI_CONFIGFLAG; - imxrt_putreg(regval, &HCOR->configflag); - - /* Wait for the EHCI to run (i.e., no longer report halted) */ - - ret = ehci_wait_usbsts(EHCI_USBSTS_HALTED, 0, 100 * 1000); - if (ret < 0) - { - usbhost_trace1(EHCI_TRACE1_RUN_FAILED, imxrt_getreg(&HCOR->usbsts)); - return NULL; - } - - /* Interrupt Configuration ************************************************/ - - ret = irq_attach(IMXRT_IRQ_USBOTG2, imxrt_ehci_interrupt, NULL); - if (ret != 0) - { - usbhost_trace1(EHCI_TRACE1_IRQATTACH_FAILED, IMXRT_IRQ_USBOTG2); - return NULL; - } - - /* Enable EHCI interrupts. Interrupts are still disabled at the level of - * the interrupt controller. - */ - - imxrt_putreg(EHCI_HANDLED_INTS, &HCOR->usbintr); - - /* Enable interrupts at the interrupt controller */ - - up_enable_irq(IMXRT_IRQ_USBOTG2); - - /* Drive Vbus +5V (the smoke test) */ - - for (i = 0; i < IMXRT_EHCI_NRHPORT; i++) - { - /* Enable VBUS power for the port */ - - imxrt_usbhost_vbusdrive(i, true); - up_mdelay(25); - } - - /* If there is a USB device in the slot at power up, then we will not get - * the status change interrupt to signal us that the device is connected. - * We need to set the initial connected state accordingly. - */ - - for (i = 0; i < IMXRT_EHCI_NRHPORT; i++) - { - g_ehci.rhport[i].connected = - ((imxrt_getreg(&HCOR->portsc[i]) & EHCI_PORTSC_CCS) != 0); - } - - usbhost_vtrace1(EHCI_VTRACE1_INIITIALIZED, 0); - - /* Initialize and return the connection interface */ - - g_ehciconn.wait = imxrt_wait; - g_ehciconn.enumerate = imxrt_enumerate; - return &g_ehciconn; -} - -/**************************************************************************** - * Name: usbhost_trformat1 and usbhost_trformat2 - * - * Description: - * This interface must be provided by platform specific logic that knows - * the HCDs encoding of USB trace data. - * - * Given an 9-bit index, return a format string suitable for use with, say, - * printf. The returned format is expected to handle two unsigned integer - * values. - * - ****************************************************************************/ - -#ifdef HAVE_USBHOST_TRACE -FAR const char *usbhost_trformat1(uint16_t id) -{ - int ndx = TRACE1_INDEX(id); - - if (ndx < TRACE1_NSTRINGS) - { - return g_trace1[ndx].string; - } - - return NULL; -} - -FAR const char *usbhost_trformat2(uint16_t id) -{ - int ndx = TRACE2_INDEX(id); - - if (ndx < TRACE2_NSTRINGS) - { - return g_trace2[ndx].string; - } - - return NULL; -} -#endif /* HAVE_USBHOST_TRACE */ - -#endif /* CONFIG_IMXRT_USBOTG && CONFIG_USBHOST */ diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lowputc.c b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lowputc.c deleted file mode 100644 index 3649ec4f7..000000000 --- a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lowputc.c +++ /dev/null @@ -1,603 +0,0 @@ -/**************************************************************************** - * arch/arm/src/imxrt/imxrt_lowputc.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 "arm_arch.h" - -#include "hardware/imxrt_iomuxc.h" -#include "hardware/imxrt_pinmux.h" -#include "hardware/imxrt_ccm.h" -#include "hardware/imxrt_lpuart.h" -#include "imxrt_config.h" -#include "imxrt_periphclks.h" -#include "imxrt_iomuxc.h" -#include "imxrt_gpio.h" -#include "imxrt_lowputc.h" - -#include "arm_internal.h" - -#include /* Include last: has dependencies */ - -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -/* Configuration ************************************************************/ - -#ifdef HAVE_LPUART_CONSOLE -# if defined(CONFIG_LPUART1_SERIAL_CONSOLE) -# define IMXRT_CONSOLE_BASE IMXRT_LPUART1_BASE -# define IMXRT_CONSOLE_BAUD CONFIG_LPUART1_BAUD -# define IMXRT_CONSOLE_BITS CONFIG_LPUART1_BITS -# define IMXRT_CONSOLE_PARITY CONFIG_LPUART1_PARITY -# define IMXRT_CONSOLE_2STOP CONFIG_LPUART1_2STOP -# elif defined(CONFIG_LPUART2_SERIAL_CONSOLE) -# define IMXRT_CONSOLE_BASE IMXRT_LPUART2_BASE -# define IMXRT_CONSOLE_BAUD CONFIG_LPUART2_BAUD -# define IMXRT_CONSOLE_BITS CONFIG_LPUART2_BITS -# define IMXRT_CONSOLE_PARITY CONFIG_LPUART2_PARITY -# define IMXRT_CONSOLE_2STOP CONFIG_LPUART2_2STOP -# elif defined(CONFIG_LPUART3_SERIAL_CONSOLE) -# define IMXRT_CONSOLE_BASE IMXRT_LPUART3_BASE -# define IMXRT_CONSOLE_BAUD CONFIG_LPUART3_BAUD -# define IMXRT_CONSOLE_BITS CONFIG_LPUART3_BITS -# define IMXRT_CONSOLE_PARITY CONFIG_LPUART3_PARITY -# define IMXRT_CONSOLE_2STOP CONFIG_LPUART3_2STOP -# elif defined(CONFIG_LPUART4_SERIAL_CONSOLE) -# define IMXRT_CONSOLE_BASE IMXRT_LPUART4_BASE -# define IMXRT_CONSOLE_BAUD CONFIG_LPUART4_BAUD -# define IMXRT_CONSOLE_BITS CONFIG_LPUART4_BITS -# define IMXRT_CONSOLE_PARITY CONFIG_LPUART4_PARITY -# define IMXRT_CONSOLE_2STOP CONFIG_LPUART4_2STOP -# elif defined(CONFIG_LPUART5_SERIAL_CONSOLE) -# define IMXRT_CONSOLE_BASE IMXRT_LPUART5_BASE -# define IMXRT_CONSOLE_BAUD CONFIG_LPUART5_BAUD -# define IMXRT_CONSOLE_BITS CONFIG_LPUART5_BITS -# define IMXRT_CONSOLE_PARITY CONFIG_LPUART5_PARITY -# define IMXRT_CONSOLE_2STOP CONFIG_LPUART5_2STOP -# elif defined(CONFIG_LPUART6_SERIAL_CONSOLE) -# define IMXRT_CONSOLE_BASE IMXRT_LPUART6_BASE -# define IMXRT_CONSOLE_BAUD CONFIG_LPUART6_BAUD -# define IMXRT_CONSOLE_BITS CONFIG_LPUART6_BITS -# define IMXRT_CONSOLE_PARITY CONFIG_LPUART6_PARITY -# define IMXRT_CONSOLE_2STOP CONFIG_LPUART6_2STOP -# elif defined(CONFIG_LPUART7_SERIAL_CONSOLE) -# define IMXRT_CONSOLE_BASE IMXRT_LPUART7_BASE -# define IMXRT_CONSOLE_BAUD CONFIG_LPUART7_BAUD -# define IMXRT_CONSOLE_BITS CONFIG_LPUART7_BITS -# define IMXRT_CONSOLE_PARITY CONFIG_LPUART7_PARITY -# define IMXRT_CONSOLE_2STOP CONFIG_LPUART7_2STOP -# elif defined(CONFIG_LPUART8_SERIAL_CONSOLE) -# define IMXRT_CONSOLE_BASE IMXRT_LPUART8_BASE -# define IMXRT_CONSOLE_BAUD CONFIG_LPUART8_BAUD -# define IMXRT_CONSOLE_BITS CONFIG_LPUART8_BITS -# define IMXRT_CONSOLE_PARITY CONFIG_LPUART8_PARITY -# define IMXRT_CONSOLE_2STOP CONFIG_LPUART8_2STOP -# endif -#endif - -/* Clocking *****************************************************************/ - -/* The UART module receives two clocks, a peripheral_clock (ipg_clk) and the - * module_clock (ipg_perclk). The peripheral_clock is used as write clock - * of the TxFIFO, read clock of the RxFIFO and synchronization of the modem - * control input pins. It must always be running when UART is enabled. - * - * The default lpuart1 ipg_clk is 66MHz (max 66.5MHz). ipg_clk is shared - * among many modules and should not be controlled by the UART logic. - * - * The module_clock is for all the state machines, writing RxFIFO, reading - * TxFIFO, etc. It must always be running when UART is sending or receiving - * characters. This clock is used in order to allow frequency scaling on - * peripheral_clock without changing configuration of baud rate. - * - * The default ipg_perclk is 80MHz (max 80MHz). ipg_perclk is gated by - * CCGR5[CG12], lpuart1_clk_enable. The clock generation sequence is: - * - * pll3_sw_clk (480M) -> CCGR5[CG12] -> 3 bit divider cg podf=6 -> - * PLL3_80M (80Mhz) -> CDCDR1: lpuart1_clk_podf -> - * 6 bit divider default=1 -> LPUART1_CLK_ROOT - * - * REVISIT: This logic assumes that all dividers are at the default value - * and that the value of the ipg_perclk is 80MHz. - */ - -#define IPG_PERCLK_FREQUENCY 80000000 - -/* The BRM sub-block receives ref_clk (module_clock clock after divider). - * From this clock, and with integer and non-integer division, BRM generates - * a 16x baud rate clock. - */ - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -#ifdef HAVE_LPUART_CONSOLE -static const struct uart_config_s g_console_config = -{ - .baud = IMXRT_CONSOLE_BAUD, /* Configured baud */ - .parity = IMXRT_CONSOLE_PARITY, /* 0=none, 1=odd, 2=even */ - .bits = IMXRT_CONSOLE_BITS, /* Number of bits (5-9) */ - .stopbits2 = IMXRT_CONSOLE_2STOP, /* true: Configure with 2 stop bits instead of 1 */ -}; -#endif - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -void imxrt_lpuart_clock_enable (uint32_t base) -{ - if (base == IMXRT_LPUART1_BASE) - { - imxrt_clockall_lpuart1(); - } - else if (base == IMXRT_LPUART2_BASE) - { - imxrt_clockall_lpuart2(); - } - else if (base == IMXRT_LPUART3_BASE) - { - imxrt_clockall_lpuart3(); - } - else if (base == IMXRT_LPUART4_BASE) - { - imxrt_clockall_lpuart4(); - } - else if (base == IMXRT_LPUART5_BASE) - { - imxrt_clockall_lpuart5(); - } - else if (base == IMXRT_LPUART6_BASE) - { - imxrt_clockall_lpuart6(); - } - else if (base == IMXRT_LPUART7_BASE) - { - imxrt_clockall_lpuart7(); - } - else if (base == IMXRT_LPUART8_BASE) - { - imxrt_clockall_lpuart8(); - } -} - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: imxrt_lowsetup - * - * Description: - * Called at the very beginning of _start. Performs low level - * initialization including setup of the console UART. This UART done - * early so that the serial console is available for debugging very early - * in the boot sequence. - * - ****************************************************************************/ - -void imxrt_lowsetup(void) -{ -#ifndef CONFIG_SUPPRESS_LPUART_CONFIG -#ifdef HAVE_LPUART_DEVICE - -#ifdef CONFIG_IMXRT_LPUART1 - - /* Configure LPUART1 pins: RXD and TXD. Also configure RTS and CTS if flow - * control is enabled. - */ - - imxrt_config_gpio(GPIO_LPUART1_RX); - imxrt_config_gpio(GPIO_LPUART1_TX); -#ifdef CONFIG_LPUART1_OFLOWCONTROL - imxrt_config_gpio(GPIO_LPUART1_CTS); -#endif -#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART1_RS485RTSCONTROL)) || \ - (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART1_IFLOWCONTROL))) - imxrt_config_gpio(GPIO_LPUART1_RTS); -#endif -#endif - -#ifdef CONFIG_IMXRT_LPUART2 - - /* Configure LPUART2 pins: RXD and TXD. Also configure RTS and CTS if flow - * control is enabled. - */ - - imxrt_config_gpio(GPIO_LPUART2_RX); - imxrt_config_gpio(GPIO_LPUART2_TX); -#ifdef CONFIG_LPUART2_OFLOWCONTROL - imxrt_config_gpio(GPIO_LPUART2_CTS); -#endif -#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART2_RS485RTSCONTROL)) || \ - (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART2_IFLOWCONTROL))) - imxrt_config_gpio(GPIO_LPUART2_RTS); -#endif -#endif - -#ifdef CONFIG_IMXRT_LPUART3 - - /* Configure LPUART3 pins: RXD and TXD. Also configure RTS and CTS if flow - * control is enabled. - */ - - imxrt_config_gpio(GPIO_LPUART3_RX); - imxrt_config_gpio(GPIO_LPUART3_TX); -#ifdef CONFIG_LPUART3_OFLOWCONTROL - imxrt_config_gpio(GPIO_LPUART3_CTS); -#endif -#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART3_RS485RTSCONTROL)) || \ - (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART3_IFLOWCONTROL))) - imxrt_config_gpio(GPIO_LPUART3_RTS); -#endif -#endif - -#ifdef CONFIG_IMXRT_LPUART4 - - /* Configure LPUART4 pins: RXD and TXD. Also configure RTS and CTS if flow - * control is enabled. - */ - - imxrt_config_gpio(GPIO_LPUART4_RX); - imxrt_config_gpio(GPIO_LPUART4_TX); -#ifdef CONFIG_LPUART4_OFLOWCONTROL - imxrt_config_gpio(GPIO_LPUART4_CTS); -#endif -#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART4_RS485RTSCONTROL)) || \ - (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART4_IFLOWCONTROL))) - imxrt_config_gpio(GPIO_LPUART4_RTS); -#endif -#endif - -#ifdef CONFIG_IMXRT_LPUART5 - - /* Configure LPUART5 pins: RXD and TXD. Also configure RTS and CTS if flow - * control is enabled. - */ - - imxrt_config_gpio(GPIO_LPUART5_RX); - imxrt_config_gpio(GPIO_LPUART5_TX); -#ifdef CONFIG_LPUART5_OFLOWCONTROL - imxrt_config_gpio(GPIO_LPUART5_CTS); -#endif -#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART5_RS485RTSCONTROL)) || \ - (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART5_IFLOWCONTROL))) - imxrt_config_gpio(GPIO_LPUART5_RTS); -#endif -#endif - -#ifdef CONFIG_IMXRT_LPUART6 - - /* Configure LPUART6 pins: RXD and TXD. Also configure RTS and CTS if flow - * control is enabled. - */ - - imxrt_config_gpio(GPIO_LPUART6_RX); - imxrt_config_gpio(GPIO_LPUART6_TX); -#ifdef CONFIG_LPUART6_OFLOWCONTROL - imxrt_config_gpio(GPIO_LPUART6_CTS); -#endif -#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART6_RS485RTSCONTROL)) || \ - (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART6_IFLOWCONTROL))) - imxrt_config_gpio(GPIO_LPUART6_RTS); -#endif -#endif - -#ifdef CONFIG_IMXRT_LPUART7 - - /* Configure LPUART7 pins: RXD and TXD. Also configure RTS and CTS if flow - * control is enabled. - */ - - imxrt_config_gpio(GPIO_LPUART7_RX); - imxrt_config_gpio(GPIO_LPUART7_TX); -#ifdef CONFIG_LPUART7_OFLOWCONTROL - imxrt_config_gpio(GPIO_LPUART7_CTS); -#endif -#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART7_RS485RTSCONTROL)) || \ - (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART7_IFLOWCONTROL))) - imxrt_config_gpio(GPIO_LPUART7_RTS); -#endif -#endif - -#ifdef CONFIG_IMXRT_LPUART8 - - /* Configure LPUART8 pins: RXD and TXD. Also configure RTS and CTS if flow - * control is enabled. - */ - - imxrt_config_gpio(GPIO_LPUART8_RX); - imxrt_config_gpio(GPIO_LPUART8_TX); -#ifdef CONFIG_LPUART8_OFLOWCONTROL - imxrt_config_gpio(GPIO_LPUART8_CTS); -#endif -#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART8_RS485RTSCONTROL)) || \ - (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART8_IFLOWCONTROL))) - imxrt_config_gpio(GPIO_LPUART8_RTS); -#endif -#endif - -#ifdef HAVE_LPUART_CONSOLE - /* Configure the serial console for initial, non-interrupt driver mode */ - - imxrt_lpuart_configure(IMXRT_CONSOLE_BASE, &g_console_config); -#endif -#endif /* HAVE_LPUART_DEVICE */ -#endif /* CONFIG_SUPPRESS_LPUART_CONFIG */ -} - -/**************************************************************************** - * Name: imxrt_lpuart_configure - * - * Description: - * Configure a UART for non-interrupt driven operation - * - ****************************************************************************/ - -#ifdef HAVE_LPUART_DEVICE -int imxrt_lpuart_configure(uint32_t base, - FAR const struct uart_config_s *config) -{ - uint32_t src_freq = 0; - uint32_t pll3_div = 0; - uint32_t uart_div = 0; - uint32_t lpuart_freq = 0; - uint16_t sbr; - uint16_t temp_sbr; - uint32_t osr; - uint32_t temp_osr; - uint32_t temp_diff; - uint32_t calculated_baud; - uint32_t baud_diff; - uint32_t regval; - uint32_t regval2; - - if ((getreg32(IMXRT_CCM_CSCDR1) & CCM_CSCDR1_UART_CLK_SEL) != 0) - { - src_freq = BOARD_XTAL_FREQUENCY; - } - else - { - if ((getreg32(IMXRT_CCM_ANALOG_PLL_USB2) & - CCM_ANALOG_PLL_USB2_DIV_SELECT_MASK) != 0) - { - pll3_div = 22; - } - else - { - pll3_div = 20; - } - - src_freq = (BOARD_XTAL_FREQUENCY * pll3_div) / 6; - } - - uart_div = (getreg32(IMXRT_CCM_CSCDR1) & - CCM_CSCDR1_UART_CLK_PODF_MASK) + 1; - lpuart_freq = src_freq / uart_div; - - /* This LPUART instantiation uses a slightly different baud rate - * calculation. The idea is to use the best OSR (over-sampling rate) - * possible. - * - * NOTE: OSR is typically hard-set to 16 in other LPUART instantiations - * loop to find the best OSR value possible, one that generates minimum - * baud_diff iterate through the rest of the supported values of OSR - */ - - baud_diff = config->baud; - osr = 0; - sbr = 0; - - for (temp_osr = 4; temp_osr <= 32; temp_osr++) - { - /* Calculate the temporary sbr value */ - - temp_sbr = (lpuart_freq / (config->baud * temp_osr)); - - /* Set temp_sbr to 1 if the sourceClockInHz can not satisfy the - * desired baud rate. - */ - - if (temp_sbr == 0) - { - temp_sbr = 1; - } - - /* Calculate the baud rate based on the temporary OSR and SBR values */ - - calculated_baud = (lpuart_freq / (temp_osr * temp_sbr)); - temp_diff = calculated_baud - config->baud; - - /* Select the better value between srb and (sbr + 1) */ - - if (temp_diff > (config->baud - - (lpuart_freq / (temp_osr * (temp_sbr + 1))))) - { - temp_diff = config->baud - - (lpuart_freq / (temp_osr * (temp_sbr + 1))); - temp_sbr++; - } - - if (temp_diff <= baud_diff) - { - baud_diff = temp_diff; - osr = temp_osr; - sbr = temp_sbr; - } - } - - if (baud_diff > ((config->baud / 100) * 3)) - { - /* Unacceptable baud rate difference of more than 3% */ - - return ERROR; - } - - /* Enable lpuart clock */ - - imxrt_lpuart_clock_enable(base); - - /* Reset all internal logic and registers, except the Global Register */ - - regval = getreg32(base + IMXRT_LPUART_GLOBAL_OFFSET); - regval |= LPUART_GLOBAL_RST; - putreg32(regval, base + IMXRT_LPUART_GLOBAL_OFFSET); - - regval &= ~LPUART_GLOBAL_RST; - putreg32(regval, base + IMXRT_LPUART_GLOBAL_OFFSET); - - /* Construct MODIR register */ - - regval = 0; - - if (config->userts) - { - regval |= LPUART_MODIR_RXRTSE; - } - else if (config->users485) - { - /* Both TX and RX side can't control RTS, so this gives - * the RX side precedence. This should have been filtered - * in layers above anyway, but it's just a precaution. - */ - - regval |= LPUART_MODIR_TXRTSE; - } - - if (config->usects) - { - regval |= LPUART_MODIR_TXCTSE; - } - - if (config->invrts) - { - regval |= LPUART_MODIR_TXRTSPOL; - } - - putreg32(regval, base + IMXRT_LPUART_MODIR_OFFSET); - - regval = 0; - - if ((osr > 3) && (osr < 8)) - { - regval |= LPUART_BAUD_BOTHEDGE; - } - - if (config->stopbits2) - { - regval |= LPUART_BAUD_SBNS; - } - - regval |= LPUART_BAUD_OSR(osr) | LPUART_BAUD_SBR(sbr); - putreg32(regval, base + IMXRT_LPUART_BAUD_OFFSET); - - regval = 0; - if (config->parity == 1) - { - regval |= LPUART_CTRL_PE | LPUART_CTRL_PT_ODD; - } - else if (config->parity == 2) - { - regval |= LPUART_CTRL_PE | LPUART_CTRL_PT_EVEN; - } - - if (config->bits == 9 || (config->bits == 8 && config->parity != 0)) - { - regval |= LPUART_CTRL_M; - } - else if ((config->bits == 8)) - { - regval &= ~LPUART_CTRL_M; - } - else - { - /* Here should be added support of other bit modes. */ - -#warning missing logic - return ERROR; - } - - regval2 = getreg32(base + IMXRT_LPUART_FIFO_OFFSET); - regval2 |= LPUART_FIFO_RXFLUSH | LPUART_FIFO_TXFLUSH | - LPUART_FIFO_RXFE | LPUART_FIFO_RXIDEN_1 | LPUART_FIFO_TXFE; - putreg32(regval2 , base + IMXRT_LPUART_FIFO_OFFSET); - - regval |= LPUART_CTRL_RE | LPUART_CTRL_TE; - putreg32(regval, base + IMXRT_LPUART_CTRL_OFFSET); - - return OK; -} -#endif /* HAVE_LPUART_DEVICE */ - -/**************************************************************************** - * Name: imxrt_lowputc - * - * Description: - * Output a byte with as few system dependencies as possible. This will - * even work BEFORE the console is initialized if we are booting from U- - * Boot (and the same UART is used for the console, of course.) - * - ****************************************************************************/ - -#if defined(HAVE_LPUART_DEVICE) && defined(CONFIG_DEBUG_FEATURES) -void imxrt_lowputc(int ch) -{ - while ((getreg32(IMXRT_CONSOLE_BASE + IMXRT_LPUART_STAT_OFFSET) & - LPUART_STAT_TDRE) == 0) - { - } - - /* If the character to output is a newline, then pre-pend a carriage - * return - */ - - if (ch == '\n') - { - /* Send the carriage return by writing it into the UART_TXD register. */ - - putreg32((uint32_t)'\r', IMXRT_CONSOLE_BASE + - IMXRT_LPUART_DATA_OFFSET); - - /* Wait for the transmit register to be emptied. When the TXFE bit is - * non-zero, the TX Buffer FIFO is empty. - */ - - while ((getreg32(IMXRT_CONSOLE_BASE + IMXRT_LPUART_STAT_OFFSET) & - LPUART_STAT_TDRE) == 0) - { - } - } - - /* Send the character by writing it into the UART_TXD register. */ - - putreg32((uint32_t)ch, IMXRT_CONSOLE_BASE + IMXRT_LPUART_DATA_OFFSET); -} -#endif diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpi2c.c b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpi2c.c deleted file mode 100644 index 20d8e1914..000000000 --- a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpi2c.c +++ /dev/null @@ -1,1997 +0,0 @@ -/**************************************************************************** - * arch/arm/src/imxrt/imxrt_lpi2c.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 "arm_arch.h" - -#include "imxrt_lpi2c.h" -#include "imxrt_gpio.h" - -#include "hardware/imxrt_pinmux.h" -#include "hardware/imxrt_ccm.h" -#include "imxrt_periphclks.h" - -/* At least one I2C peripheral must be enabled */ - -#if defined(CONFIG_IMXRT_LPI2C1) || defined(CONFIG_IMXRT_LPI2C2) || \ - defined(CONFIG_IMXRT_LPI2C3) || defined(CONFIG_IMXRT_LPI2C4) - -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -/* Configuration ************************************************************/ - -/* CONFIG_I2C_POLLED may be set so that I2C interrupts will not be used. - * Instead, CPU-intensive polling will be used. - */ - -/* Interrupt wait timeout in seconds and milliseconds */ - -#if !defined(CONFIG_IMXRT_LPI2C_TIMEOSEC) && \ - !defined(CONFIG_IMXRT_LPI2C_TIMEOMS) -# define CONFIG_IMXRT_LPI2C_TIMEOSEC 0 -# define CONFIG_IMXRT_LPI2C_TIMEOMS 500 /* Default is 500 milliseconds */ -#elif !defined(CONFIG_IMXRT_LPI2C_TIMEOSEC) -# define CONFIG_IMXRT_LPI2C_TIMEOSEC 0 /* User provided milliseconds */ -#elif !defined(CONFIG_IMXRT_LPI2C_TIMEOMS) -# define CONFIG_IMXRT_LPI2C_TIMEOMS 0 /* User provided seconds */ -#endif - -/* Interrupt wait time timeout in system timer ticks */ - -#ifndef CONFIG_IMXRT_LPI2C_TIMEOTICKS -# define CONFIG_IMXRT_LPI2C_TIMEOTICKS \ - (SEC2TICK(CONFIG_IMXRT_LPI2C_TIMEOSEC) + \ - MSEC2TICK(CONFIG_IMXRT_LPI2C_TIMEOMS)) -#endif - -#ifndef CONFIG_IMXRT_LPI2C_DYNTIMEO_STARTSTOP -# define CONFIG_IMXRT_LPI2C_DYNTIMEO_STARTSTOP \ - TICK2USEC(CONFIG_IMXRT_LPI2C_TIMEOTICKS) -#endif - -/* Debug ********************************************************************/ - -/* I2C event trace logic. NOTE: trace uses the internal, non-standard, - * low-level debug interface syslog() but does not require that any other - * debug is enabled. - */ - -#ifndef CONFIG_I2C_TRACE -# define imxrt_lpi2c_tracereset(p) -# define imxrt_lpi2c_tracenew(p,s) -# define imxrt_lpi2c_traceevent(p,e,a) -# define imxrt_lpi2c_tracedump(p) -#endif - -#ifndef CONFIG_I2C_NTRACE -# define CONFIG_I2C_NTRACE 32 -#endif - -#ifdef CONFIG_I2C_SLAVE -# error I2C slave logic is not supported yet for IMXRT -#endif - -#define LPI2C_MASTER 1 -#define LPI2C_SLAVE 2 - -/**************************************************************************** - * Private Types - ****************************************************************************/ - -/* Interrupt state */ - -enum imxrt_intstate_e -{ - INTSTATE_IDLE = 0, /* No I2C activity */ - INTSTATE_WAITING, /* Waiting for completion of interrupt activity */ - INTSTATE_DONE, /* Interrupt activity complete */ -}; - -/* Trace events */ - -enum imxrt_trace_e -{ - I2CEVENT_NONE = 0, /* No events have occurred with this status */ - I2CEVENT_SENDADDR, /* Start/Master bit set and address sent, param = msgc */ - I2CEVENT_SENDBYTE, /* Send byte, param = dcnt */ - I2CEVENT_RCVBYTE, /* Read more dta, param = dcnt */ - I2CEVENT_NOSTART, /* BTF on last byte with no restart, param = msgc */ - I2CEVENT_STARTRESTART, /* Last byte sent, re-starting, param = msgc */ - I2CEVENT_STOP, /* Last byte sten, send stop, param = 0 */ - I2CEVENT_ERROR /* Error occurred, param = 0 */ -}; - -/* Trace data */ - -struct imxrt_trace_s -{ - uint32_t status; /* I2C 32-bit SR2|SR1 status */ - uint32_t count; /* Interrupt count when status change */ - enum imxrt_intstate_e event; /* Last event that occurred with this status */ - uint32_t parm; /* Parameter associated with the event */ - clock_t time; /* First of event or first status */ -}; - -/* I2C Device hardware configuration */ - -struct imxrt_lpi2c_config_s -{ - uint32_t base; /* LPI2C base address */ - uint16_t busy_idle; /* LPI2C Bus Idle Timeout */ - uint8_t filtscl; /* Glitch Filter for SCL pin */ - uint8_t filtsda; /* Glitch Filter for SDA pin */ - uint32_t scl_pin; /* Peripheral configuration for SCL as SCL */ - uint32_t sda_pin; /* Peripheral configuration for SDA as SDA */ -#if defined(CONFIG_I2C_RESET) - uint32_t reset_scl_pin; /* GPIO configuration for SCL as SCL */ - uint32_t reset_sda_pin; /* GPIO configuration for SDA as SDA */ -#endif - uint8_t mode; /* Master or Slave mode */ -#ifndef CONFIG_I2C_POLLED - uint32_t irq; /* Event IRQ */ -#endif -}; - -/* I2C Device Private Data */ - -struct imxrt_lpi2c_priv_s -{ - /* Standard I2C operations */ - - const struct i2c_ops_s *ops; - - /* Port configuration */ - - const struct imxrt_lpi2c_config_s *config; - - int refs; /* Reference count */ - sem_t sem_excl; /* Mutual exclusion semaphore */ -#ifndef CONFIG_I2C_POLLED - sem_t sem_isr; /* Interrupt wait semaphore */ -#endif - volatile uint8_t intstate; /* Interrupt handshake (see enum imxrt_intstate_e) */ - - uint8_t msgc; /* Message count */ - struct i2c_msg_s *msgv; /* Message list */ - uint8_t *ptr; /* Current message buffer */ - uint32_t frequency; /* Current I2C frequency */ - int dcnt; /* Current message length */ - uint16_t flags; /* Current message flags */ - - /* I2C trace support */ - -#ifdef CONFIG_I2C_TRACE - int tndx; /* Trace array index */ - clock_t start_time; /* Time when the trace was started */ - - /* The actual trace data */ - - struct imxrt_trace_s trace[CONFIG_I2C_NTRACE]; -#endif - - uint32_t status; /* End of transfer SR2|SR1 status */ -}; - -/**************************************************************************** - * Private Function Prototypes - ****************************************************************************/ - -static inline uint32_t - imxrt_lpi2c_getreg(FAR struct imxrt_lpi2c_priv_s *priv, uint16_t offset); -static inline void imxrt_lpi2c_putreg(FAR struct imxrt_lpi2c_priv_s *priv, - uint16_t offset, uint32_t value); -static inline void imxrt_lpi2c_modifyreg(FAR struct imxrt_lpi2c_priv_s *priv, - uint16_t offset, uint32_t clearbits, - uint32_t setbits); -static inline int imxrt_lpi2c_sem_wait(FAR struct imxrt_lpi2c_priv_s *priv); -#ifdef CONFIG_I2C_RESET -static int - imxrt_lpi2c_sem_wait_noncancelable(FAR struct imxrt_lpi2c_priv_s *priv); -#endif - -#ifdef CONFIG_IMXRT_LPI2C_DYNTIMEO -static useconds_t imxrt_lpi2c_tousecs(int msgc, FAR struct i2c_msg_s *msgs); -#endif /* CONFIG_IMXRT_LPI2C_DYNTIMEO */ - -static inline int - imxrt_lpi2c_sem_waitdone(FAR struct imxrt_lpi2c_priv_s *priv); -static inline void - imxrt_lpi2c_sem_waitstop(FAR struct imxrt_lpi2c_priv_s *priv); -static inline void - imxrt_lpi2c_sem_post(FAR struct imxrt_lpi2c_priv_s *priv); -static inline void - imxrt_lpi2c_sem_init(FAR struct imxrt_lpi2c_priv_s *priv); -static inline void - imxrt_lpi2c_sem_destroy(FAR struct imxrt_lpi2c_priv_s *priv); - -#ifdef CONFIG_I2C_TRACE -static void imxrt_lpi2c_tracereset(FAR struct imxrt_lpi2c_priv_s *priv); -static void imxrt_lpi2c_tracenew(FAR struct imxrt_lpi2c_priv_s *priv, - uint32_t status); -static void imxrt_lpi2c_traceevent(FAR struct imxrt_lpi2c_priv_s *priv, - enum imxrt_trace_e event, uint32_t parm); -static void imxrt_lpi2c_tracedump(FAR struct imxrt_lpi2c_priv_s *priv); -#endif /* CONFIG_I2C_TRACE */ - -static void imxrt_lpi2c_setclock(FAR struct imxrt_lpi2c_priv_s *priv, - uint32_t frequency); -static inline void imxrt_lpi2c_sendstart(FAR struct imxrt_lpi2c_priv_s *priv, - uint8_t address); -static inline void imxrt_lpi2c_sendstop(FAR struct imxrt_lpi2c_priv_s *priv); -static inline uint32_t - imxrt_lpi2c_getstatus(FAR struct imxrt_lpi2c_priv_s *priv); - -static int imxrt_lpi2c_isr_process(struct imxrt_lpi2c_priv_s * priv); - -#ifndef CONFIG_I2C_POLLED -static int imxrt_lpi2c_isr(int irq, void *context, FAR void *arg); -#endif /* !CONFIG_I2C_POLLED */ - -void imxrt_lpi2c_clock_enable (uint32_t base); -void imxrt_lpi2c_clock_disable (uint32_t base); -static int imxrt_lpi2c_init(FAR struct imxrt_lpi2c_priv_s *priv); -static int imxrt_lpi2c_deinit(FAR struct imxrt_lpi2c_priv_s *priv); -static int imxrt_lpi2c_transfer(FAR struct i2c_master_s *dev, - FAR struct i2c_msg_s *msgs, int count); -#ifdef CONFIG_I2C_RESET -static int imxrt_lpi2c_reset(FAR struct i2c_master_s *dev); -#endif - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -/* Trace events strings */ - -#ifdef CONFIG_I2C_TRACE -static const char *g_trace_names[] = -{ - "NONE ", - "SENDADDR ", - "SENDBYTE ", - "RCVBYTE ", - "NOSTART ", - "START/RESTART ", - "STOP ", - "ERROR " -}; -#endif - -/* I2C interface */ - -static const struct i2c_ops_s imxrt_lpi2c_ops = -{ - .transfer = imxrt_lpi2c_transfer -#ifdef CONFIG_I2C_RESET - , .reset = imxrt_lpi2c_reset -#endif -}; - -/* I2C device structures */ - -#ifdef CONFIG_IMXRT_LPI2C1 -static const struct imxrt_lpi2c_config_s imxrt_lpi2c1_config = -{ - .base = IMXRT_LPI2C1_BASE, - .busy_idle = CONFIG_LPI2C1_BUSYIDLE, - .filtscl = CONFIG_LPI2C1_FILTSCL, - .filtsda = CONFIG_LPI2C1_FILTSDA, - .scl_pin = GPIO_LPI2C1_SCL, - .sda_pin = GPIO_LPI2C1_SDA, -#if defined(CONFIG_I2C_RESET) - .reset_scl_pin = GPIO_LPI2C1_SCL_RESET, - .reset_sda_pin = GPIO_LPI2C1_SDA_RESET, -#endif -#ifndef CONFIG_I2C_SLAVE - .mode = LPI2C_MASTER, -#else - .mode = LPI2C_SLAVE, -#endif -#ifndef CONFIG_I2C_POLLED - .irq = IMXRT_IRQ_LPI2C1, -#endif -}; - -static struct imxrt_lpi2c_priv_s imxrt_lpi2c1_priv = -{ - .ops = &imxrt_lpi2c_ops, - .config = &imxrt_lpi2c1_config, - .refs = 0, - .intstate = INTSTATE_IDLE, - .msgc = 0, - .msgv = NULL, - .ptr = NULL, - .dcnt = 0, - .flags = 0, - .status = 0 -}; -#endif - -#ifdef CONFIG_IMXRT_LPI2C2 -static const struct imxrt_lpi2c_config_s imxrt_lpi2c2_config = -{ - .base = IMXRT_LPI2C2_BASE, - .busy_idle = CONFIG_LPI2C2_BUSYIDLE, - .filtscl = CONFIG_LPI2C2_FILTSCL, - .filtsda = CONFIG_LPI2C2_FILTSDA, - .scl_pin = GPIO_LPI2C2_SCL, - .sda_pin = GPIO_LPI2C2_SDA, -#if defined(CONFIG_I2C_RESET) - .reset_scl_pin = GPIO_LPI2C2_SCL_RESET, - .reset_sda_pin = GPIO_LPI2C2_SDA_RESET, -#endif -#ifndef CONFIG_I2C_SLAVE - .mode = LPI2C_MASTER, -#else - .mode = LPI2C_SLAVE, -#endif -#ifndef CONFIG_I2C_POLLED - .irq = IMXRT_IRQ_LPI2C2, -#endif -}; - -static struct imxrt_lpi2c_priv_s imxrt_lpi2c2_priv = -{ - .ops = &imxrt_lpi2c_ops, - .config = &imxrt_lpi2c2_config, - .refs = 0, - .intstate = INTSTATE_IDLE, - .msgc = 0, - .msgv = NULL, - .ptr = NULL, - .dcnt = 0, - .flags = 0, - .status = 0 -}; -#endif - -#ifdef CONFIG_IMXRT_LPI2C3 -static const struct imxrt_lpi2c_config_s imxrt_lpi2c3_config = -{ - .base = IMXRT_LPI2C3_BASE, - .busy_idle = CONFIG_LPI2C3_BUSYIDLE, - .filtscl = CONFIG_LPI2C3_FILTSCL, - .filtsda = CONFIG_LPI2C3_FILTSDA, - .scl_pin = GPIO_LPI2C3_SCL, - .sda_pin = GPIO_LPI2C3_SDA, -#if defined(CONFIG_I2C_RESET) - .reset_scl_pin = GPIO_LPI2C3_SCL_RESET, - .reset_sda_pin = GPIO_LPI2C3_SDA_RESET, -#endif -#ifndef CONFIG_I2C_SLAVE - .mode = LPI2C_MASTER, -#else - .mode = LPI2C_SLAVE, -#endif -#ifndef CONFIG_I2C_POLLED - .irq = IMXRT_IRQ_LPI2C3, -#endif -}; - -static struct imxrt_lpi2c_priv_s imxrt_lpi2c3_priv = -{ - .ops = &imxrt_lpi2c_ops, - .config = &imxrt_lpi2c3_config, - .refs = 0, - .intstate = INTSTATE_IDLE, - .msgc = 0, - .msgv = NULL, - .ptr = NULL, - .dcnt = 0, - .flags = 0, - .status = 0 -}; -#endif - -#ifdef CONFIG_IMXRT_LPI2C4 -static const struct imxrt_lpi2c_config_s imxrt_lpi2c4_config = -{ - .base = IMXRT_LPI2C4_BASE, - .busy_idle = CONFIG_LPI2C4_BUSYIDLE, - .filtscl = CONFIG_LPI2C4_FILTSCL, - .filtsda = CONFIG_LPI2C4_FILTSDA, - .scl_pin = GPIO_LPI2C4_SCL, - .sda_pin = GPIO_LPI2C4_SDA, -#if defined(CONFIG_I2C_RESET) - .reset_scl_pin = GPIO_LPI2C4_SCL_RESET, - .reset_sda_pin = GPIO_LPI2C4_SDA_RESET, -#endif -#ifndef CONFIG_I2C_SLAVE - .mode = LPI2C_MASTER, -#else - .mode = LPI2C_SLAVE, -#endif -#ifndef CONFIG_I2C_POLLED - .irq = IMXRT_IRQ_LPI2C4, -#endif -}; - -static struct imxrt_lpi2c_priv_s imxrt_lpi2c4_priv = -{ - .ops = &imxrt_lpi2c_ops, - .config = &imxrt_lpi2c4_config, - .refs = 0, - .intstate = INTSTATE_IDLE, - .msgc = 0, - .msgv = NULL, - .ptr = NULL, - .dcnt = 0, - .flags = 0, - .status = 0 -}; -#endif - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: imxrt_lpi2c_getreg - * - * Description: - * Get a 32-bit register value by offset - * - ****************************************************************************/ - -static inline uint32_t - imxrt_lpi2c_getreg(FAR struct imxrt_lpi2c_priv_s *priv, uint16_t offset) -{ - return getreg32(priv->config->base + offset); -} - -/**************************************************************************** - * Name: imxrt_lpi2c_putreg - * - * Description: - * Put a 32-bit register value by offset - * - ****************************************************************************/ - -static inline void imxrt_lpi2c_putreg(FAR struct imxrt_lpi2c_priv_s *priv, - uint16_t offset, uint32_t value) -{ - putreg32(value, priv->config->base + offset); -} - -/**************************************************************************** - * Name: imxrt_lpi2c_modifyreg - * - * Description: - * Modify a 32-bit register value by offset - * - ****************************************************************************/ - -static inline void imxrt_lpi2c_modifyreg(FAR struct imxrt_lpi2c_priv_s *priv, - uint16_t offset, uint32_t clearbits, - uint32_t setbits) -{ - modifyreg32(priv->config->base + offset, clearbits, setbits); -} - -/**************************************************************************** - * Name: imxrt_lpi2c_sem_wait - * - * Description: - * Take the exclusive access, waiting as necessary. May be interrupted by - * a signal. - * - ****************************************************************************/ - -static inline int imxrt_lpi2c_sem_wait(FAR struct imxrt_lpi2c_priv_s *priv) -{ - return nxsem_wait(&priv->sem_excl); -} - -/**************************************************************************** - * Name: imxrt_lpi2c_sem_wait_noncancelable - * - * Description: - * Take the exclusive access, waiting as necessary. - * - ****************************************************************************/ - -#ifdef CONFIG_I2C_RESET -static int - imxrt_lpi2c_sem_wait_noncancelable(FAR struct imxrt_lpi2c_priv_s *priv) -{ - return nxsem_wait_uninterruptible(&priv->sem_excl); -} -#endif - -/**************************************************************************** - * Name: imxrt_lpi2c_tousecs - * - * Description: - * Return a micro-second delay based on the number of bytes left to be - * processed. - * - ****************************************************************************/ - -#ifdef CONFIG_IMXRT_LPI2C_DYNTIMEO -static useconds_t imxrt_lpi2c_tousecs(int msgc, FAR struct i2c_msg_s *msgs) -{ - size_t bytecount = 0; - int i; - - /* Count the number of bytes left to process */ - - for (i = 0; i < msgc; i++) - { - bytecount += msgs[i].length; - } - - /* Then return a number of microseconds based on a user provided scaling - * factor. - */ - - return (useconds_t)(CONFIG_IMXRT_LPI2C_DYNTIMEO_USECPERBYTE * bytecount); -} -#endif - -/**************************************************************************** - * Name: imxrt_lpi2c_sem_waitdone - * - * Description: - * Wait for a transfer to complete - * - ****************************************************************************/ - -#ifndef CONFIG_I2C_POLLED -static inline int - imxrt_lpi2c_sem_waitdone(FAR struct imxrt_lpi2c_priv_s *priv) -{ - struct timespec abstime; - irqstate_t flags; - uint32_t regval; - int ret; - - flags = enter_critical_section(); - - /* Enable Interrupts when master mode */ - - if (priv->config->mode == LPI2C_MASTER) - { - if ((priv->flags & I2C_M_READ) != 0) - { - regval = LPI2C_MIER_TDIE | LPI2C_MIER_RDIE | LPI2C_MIER_NDIE | \ - LPI2C_MIER_ALIE | LPI2C_MIER_SDIE; - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MIER_OFFSET, regval); - } - else - { - regval = LPI2C_MIER_TDIE | LPI2C_MIER_NDIE | \ - LPI2C_MIER_ALIE | LPI2C_MIER_SDIE; - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MIER_OFFSET, regval); - } - } - - /* Enable Interrupts when slave mode */ - - else - { -#warning Missing logic for I2C Slave mode - } - - /* Signal the interrupt handler that we are waiting. NOTE: Interrupts - * are currently disabled but will be temporarily re-enabled below when - * nxsem_timedwait() sleeps. - */ - - priv->intstate = INTSTATE_WAITING; - do - { - /* Get the current time */ - - clock_gettime(CLOCK_REALTIME, &abstime); - - /* Calculate a time in the future */ - -#if CONFIG_IMXRT_LPI2C_TIMEOSEC > 0 - abstime.tv_sec += CONFIG_IMXRT_LPI2C_TIMEOSEC; -#endif - - /* Add a value proportional to the number of bytes in the transfer */ - -#ifdef CONFIG_IMXRT_LPI2C_DYNTIMEO - abstime.tv_nsec += 1000 * imxrt_lpi2c_tousecs(priv->msgc, priv->msgv); - if (abstime.tv_nsec >= 1000 * 1000 * 1000) - { - abstime.tv_sec++; - abstime.tv_nsec -= 1000 * 1000 * 1000; - } - -#elif CONFIG_IMXRT_LPI2C_TIMEOMS > 0 - abstime.tv_nsec += CONFIG_IMXRT_LPI2C_TIMEOMS * 1000 * 1000; - if (abstime.tv_nsec >= 1000 * 1000 * 1000) - { - abstime.tv_sec++; - abstime.tv_nsec -= 1000 * 1000 * 1000; - } -#endif - - /* Wait until either the transfer is complete or the timeout expires */ - - ret = nxsem_timedwait_uninterruptible(&priv->sem_isr, &abstime); - if (ret < 0) - { - /* Break out of the loop on irrecoverable errors. This would - * include timeouts and mystery errors reported by nxsem_timedwait. - */ - - break; - } - } - - /* Loop until the interrupt level transfer is complete. */ - - while (priv->intstate != INTSTATE_DONE); - - /* Set the interrupt state back to IDLE */ - - priv->intstate = INTSTATE_IDLE; - - /* Disable I2C interrupts */ - - /* Enable Interrupts when master mode */ - - if (priv->config->mode == LPI2C_MASTER) - { - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MIER_OFFSET, 0); - } - - /* Enable Interrupts when slave mode */ - - else - { - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_SIER_OFFSET, 0); - } - - leave_critical_section(flags); - return ret; -} -#else -static inline int - imxrt_lpi2c_sem_waitdone(FAR struct imxrt_lpi2c_priv_s *priv) -{ - clock_t timeout; - clock_t start; - clock_t elapsed; - int ret; - - /* Get the timeout value */ - -#ifdef CONFIG_IMXRT_LPI2C_DYNTIMEO - timeout = USEC2TICK(imxrt_lpi2c_tousecs(priv->msgc, priv->msgv)); -#else - timeout = CONFIG_IMXRT_LPI2C_TIMEOTICKS; -#endif - - /* Signal the interrupt handler that we are waiting. NOTE: Interrupts - * are currently disabled but will be temporarily re-enabled below when - * nxsem_timedwait() sleeps. - */ - - priv->intstate = INTSTATE_WAITING; - start = clock_systime_ticks(); - - do - { - /* Calculate the elapsed time */ - - elapsed = clock_systime_ticks() - start; - - /* Poll by simply calling the timer interrupt handler until it - * reports that it is done. - */ - - imxrt_lpi2c_isr_process(priv); - } - - /* Loop until the transfer is complete. */ - - while (priv->intstate != INTSTATE_DONE && elapsed < timeout); - - i2cinfo("intstate: %d elapsed: %ld threshold: %ld status: %08x\n", - priv->intstate, (long)elapsed, (long)timeout, priv->status); - - /* Set the interrupt state back to IDLE */ - - ret = priv->intstate == INTSTATE_DONE ? OK : -ETIMEDOUT; - priv->intstate = INTSTATE_IDLE; - return ret; -} -#endif - -/**************************************************************************** - * Name: imxrt_lpi2c_sem_waitstop - * - * Description: - * Wait for a STOP to complete - * - ****************************************************************************/ - -static inline void - imxrt_lpi2c_sem_waitstop(FAR struct imxrt_lpi2c_priv_s *priv) -{ - clock_t start; - clock_t elapsed; - clock_t timeout; - uint32_t regval; - - /* Select a timeout */ - -#ifdef CONFIG_IMXRT_LPI2C_DYNTIMEO - timeout = USEC2TICK(CONFIG_IMXRT_LPI2C_DYNTIMEO_STARTSTOP); -#else - timeout = CONFIG_IMXRT_LPI2C_TIMEOTICKS; -#endif - - /* Wait as stop might still be in progress; but stop might also - * be set because of a timeout error: "The [STOP] bit is set and - * cleared by software, cleared by hardware when a Stop condition is - * detected, set by hardware when a timeout error is detected." - */ - - start = clock_systime_ticks(); - do - { - /* Calculate the elapsed time */ - - elapsed = clock_systime_ticks() - start; - - /* Check for STOP condition */ - - if (priv->config->mode == LPI2C_MASTER) - { - regval = imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MSR_OFFSET); - if ((regval & LPI2C_MSR_SDF) == LPI2C_MSR_SDF) - { - return; - } - } - - /* Enable Interrupts when slave mode */ - - else - { - regval = imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_SSR_OFFSET); - if ((regval & LPI2C_SSR_SDF) == LPI2C_SSR_SDF) - { - return; - } - } - - /* Check for NACK error */ - - if (priv->config->mode == LPI2C_MASTER) - { - regval = imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MSR_OFFSET); - if ((regval & LPI2C_MSR_NDF) == LPI2C_MSR_NDF) - { - return; - } - } - - /* Enable Interrupts when slave mode */ - - else - { -#warning Missing logic for I2C Slave - } - } - - /* Loop until the stop is complete or a timeout occurs. */ - - while (elapsed < timeout); - - /* If we get here then a timeout occurred with the STOP condition - * still pending. - */ - - i2cinfo("Timeout with Status Register: %" PRIx32 "\n", regval); -} - -/**************************************************************************** - * Name: imxrt_lpi2c_sem_post - * - * Description: - * Release the mutual exclusion semaphore - * - ****************************************************************************/ - -static inline void imxrt_lpi2c_sem_post(struct imxrt_lpi2c_priv_s *priv) -{ - nxsem_post(&priv->sem_excl); -} - -/**************************************************************************** - * Name: imxrt_lpi2c_sem_init - * - * Description: - * Initialize semaphores - * - ****************************************************************************/ - -static inline void imxrt_lpi2c_sem_init(FAR struct imxrt_lpi2c_priv_s *priv) -{ - nxsem_init(&priv->sem_excl, 0, 1); - -#ifndef CONFIG_I2C_POLLED - /* This semaphore is used for signaling and, hence, should not have - * priority inheritance enabled. - */ - - nxsem_init(&priv->sem_isr, 0, 0); - nxsem_set_protocol(&priv->sem_isr, SEM_PRIO_NONE); -#endif -} - -/**************************************************************************** - * Name: imxrt_lpi2c_sem_destroy - * - * Description: - * Destroy semaphores. - * - ****************************************************************************/ - -static inline void - imxrt_lpi2c_sem_destroy(FAR struct imxrt_lpi2c_priv_s *priv) -{ - nxsem_destroy(&priv->sem_excl); -#ifndef CONFIG_I2C_POLLED - nxsem_destroy(&priv->sem_isr); -#endif -} - -/**************************************************************************** - * Name: imxrt_lpi2c_trace* - * - * Description: - * I2C trace instrumentation - * - ****************************************************************************/ - -#ifdef CONFIG_I2C_TRACE -static void imxrt_lpi2c_traceclear(FAR struct imxrt_lpi2c_priv_s *priv) -{ - struct imxrt_trace_s *trace = &priv->trace[priv->tndx]; - - trace->status = 0; /* I2C 32-bit SR2|SR1 status */ - trace->count = 0; /* Interrupt count when status change */ - trace->event = I2CEVENT_NONE; /* Last event that occurred with this status */ - trace->parm = 0; /* Parameter associated with the event */ - trace->time = 0; /* Time of first status or event */ -} - -static void imxrt_lpi2c_tracereset(FAR struct imxrt_lpi2c_priv_s *priv) -{ - /* Reset the trace info for a new data collection */ - - priv->tndx = 0; - priv->start_time = clock_systime_ticks(); - imxrt_lpi2c_traceclear(priv); -} - -static void imxrt_lpi2c_tracenew(FAR struct imxrt_lpi2c_priv_s *priv, - uint32_t status) -{ - struct imxrt_trace_s *trace = &priv->trace[priv->tndx]; - - /* Is the current entry uninitialized? Has the status changed? */ - - if (trace->count == 0 || status != trace->status) - { - /* Yes.. Was it the status changed? */ - - if (trace->count != 0) - { - /* Yes.. bump up the trace index (unless out of trace entries) */ - - if (priv->tndx >= (CONFIG_I2C_NTRACE - 1)) - { - i2cerr("ERROR: Trace table overflow\n"); - return; - } - - priv->tndx++; - trace = &priv->trace[priv->tndx]; - } - - /* Initialize the new trace entry */ - - imxrt_lpi2c_traceclear(priv); - trace->status = status; - trace->count = 1; - trace->time = clock_systime_ticks(); - } - else - { - /* Just increment the count of times that we have seen this status */ - - trace->count++; - } -} - -static void imxrt_lpi2c_traceevent(FAR struct imxrt_lpi2c_priv_s *priv, - enum imxrt_trace_e event, uint32_t parm) -{ - struct imxrt_trace_s *trace; - - if (event != I2CEVENT_NONE) - { - trace = &priv->trace[priv->tndx]; - - /* Initialize the new trace entry */ - - trace->event = event; - trace->parm = parm; - - /* Bump up the trace index (unless we are out of trace entries) */ - - if (priv->tndx >= (CONFIG_I2C_NTRACE - 1)) - { - i2cerr("ERROR: Trace table overflow\n"); - return; - } - - priv->tndx++; - imxrt_lpi2c_traceclear(priv); - } -} - -static void imxrt_lpi2c_tracedump(FAR struct imxrt_lpi2c_priv_s *priv) -{ - struct imxrt_trace_s *trace; - int i; - - syslog(LOG_DEBUG, "Elapsed time: %ld\n", - (long)(clock_systime_ticks() - priv->start_time)); - - for (i = 0; i < priv->tndx; i++) - { - trace = &priv->trace[i]; - syslog(LOG_DEBUG, - "%2d. STATUS: %08x COUNT: %3d EVENT: %s(%2d) PARM: %08x " - "TIME: %d\n", - i + 1, trace->status, trace->count, - g_trace_names[trace->event], - trace->event, trace->parm, trace->time - priv->start_time); - } -} -#endif /* CONFIG_I2C_TRACE */ - -/**************************************************************************** - * Name: imxrt_lpi2c_setclock - * - * Description: - * Set the I2C clock - * - ****************************************************************************/ - -static void imxrt_lpi2c_setclock(FAR struct imxrt_lpi2c_priv_s *priv, - uint32_t frequency) -{ - uint32_t src_freq = 0; - uint32_t pll3_div = 0; - uint32_t lpi2c_clk_div; - uint32_t regval; - uint32_t men; - uint32_t prescale = 0; - uint32_t best_prescale = 0; - uint32_t best_clk_hi = 0; - uint32_t abs_error = 0; - uint32_t best_error = 0xffffffff; - uint32_t clk_hi_cycle; - uint32_t computed_rate; - uint32_t count; - - /* Has the I2C bus frequency changed? */ - - if (priv->config->mode == LPI2C_MASTER) - { - if (frequency != priv->frequency) - { - /* Disable the selected LPI2C peripheral to configure the new - * clock if it is enabled. - */ - - men = imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MCR_OFFSET) & - LPI2C_MCR_MEN; - if (men) - { - imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCR_OFFSET, - LPI2C_MCR_MEN, 0); - } - - /* Get the LPI2C clock source frequency */ - - if ((getreg32(IMXRT_CCM_CSCDR2) & CCM_CSCDR2_LPI2C_CLK_SEL) == - CCM_CSCDR2_LPI2C_CLK_SEL_OSC_CLK) - { - src_freq = BOARD_XTAL_FREQUENCY; - } - else - { - if ((getreg32(IMXRT_CCM_ANALOG_PLL_USB2) & - CCM_ANALOG_PLL_USB2_DIV_SELECT_MASK) != 0) - { - pll3_div = 22; - } - else - { - pll3_div = 20; - } - - lpi2c_clk_div = (getreg32(IMXRT_CCM_CSCDR2) & - CCM_CSCDR2_LPI2C_CLK_PODF_MASK) >> - CCM_CSCDR2_LPI2C_CLK_PODF_SHIFT; - lpi2c_clk_div = lpi2c_clk_div + 1; - src_freq = (BOARD_XTAL_FREQUENCY * pll3_div) / - (8 * lpi2c_clk_div) ; - } - - /* LPI2C output frequency = (Source Clock (Hz)/ 2^prescale) / - * (CLKLO + 1 + CLKHI + 1 + ROUNDDOWN((2 + FILTSCL) / 2^prescale) - * - * Assume CLKLO = 2 * CLKHI, SETHOLD = CLKHI, DATAVD = CLKHI / 2 - */ - - for (prescale = 1; - (prescale <= 128) && (best_error != 0); - prescale *= 2) - { - for (clk_hi_cycle = 1; clk_hi_cycle < 32; clk_hi_cycle++) - { - if (clk_hi_cycle == 1) - { - computed_rate = (src_freq / prescale) / - (6 + (2 / prescale)); - } - else - { - computed_rate = (src_freq / prescale) / - ((3 * clk_hi_cycle + 2) + - (2 / prescale)); - } - - if (frequency > computed_rate) - { - abs_error = frequency - computed_rate; - } - else - { - abs_error = computed_rate - frequency; - } - - if (abs_error < best_error) - { - best_prescale = prescale; - best_clk_hi = clk_hi_cycle; - best_error = abs_error; - - if (abs_error == 0) - { - break; - } - } - } - } - - regval = LPI2C_MCCR0_CLKHI(best_clk_hi); - - if (best_clk_hi < 2) - { - regval |= LPI2C_MCCR0_CLKLO(3) | LPI2C_MCCR0_SETHOLD(2) | - LPI2C_MCCR0_DATAVD(1); - } - else - { - regval |= LPI2C_MCCR0_CLKLO(2 * best_clk_hi) | - LPI2C_MCCR0_SETHOLD(best_clk_hi) | - LPI2C_MCCR0_DATAVD(best_clk_hi / 2); - } - - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MCCR0_OFFSET, regval); - - for (count = 0; count < 8; count++) - { - if (best_prescale == (1 << count)) - { - best_prescale = count; - break; - } - } - - imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCFGR1_OFFSET, - LPI2C_MCFGR1_PRESCALE_MASK, - LPI2C_MCFGR1_PRESCALE(best_prescale)); - - /* Re-enable LPI2C if it was enabled previously */ - - if (men) - { - imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCR_OFFSET, 0, - LPI2C_MCR_MEN); - } - - /* Save the new LPI2C frequency */ - - priv->frequency = frequency; - } - } -} - -/**************************************************************************** - * Name: imxrt_lpi2c_sendstart - * - * Description: - * Send the START conditions/force Master mode - * - ****************************************************************************/ - -static inline void imxrt_lpi2c_sendstart(FAR struct imxrt_lpi2c_priv_s *priv, - uint8_t address) -{ - uint32_t txcount = 0; - uint32_t status = 0; - uint8_t addr; - - /* Generate START condition and send the address */ - - /* Turn off auto_stop option */ - - imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCFGR1_OFFSET, - LPI2C_MCFGR1_IGNACK, 0); - - do - { - txcount = (imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MFSR_OFFSET) & - LPI2C_MFSR_TXCOUNT_MASK) >> LPI2C_MFSR_TXCOUNT_SHIFT; - txcount = 4 - txcount; - - status = imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MSR_OFFSET); - - if (status & LPI2C_MSR_ERROR_MASK) - { - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MSR_OFFSET, - status & LPI2C_MSR_ERROR_MASK); - } - } - while (txcount == 0); - - if ((priv->flags & I2C_M_READ) != 0) - { - addr = I2C_READADDR8(address); - } - else - { - addr = I2C_WRITEADDR8(address); - } - - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MTDR_OFFSET, - (LPI2C_MTDR_CMD_START | LPI2C_MTDR_DATA(addr))); -} - -/**************************************************************************** - * Name: imxrt_lpi2c_sendstop - * - * Description: - * Send the STOP conditions - * - ****************************************************************************/ - -static inline void imxrt_lpi2c_sendstop(FAR struct imxrt_lpi2c_priv_s *priv) -{ - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MTDR_OFFSET, LPI2C_MTDR_CMD_STOP); -} - -/**************************************************************************** - * Name: imxrt_lpi2c_getstatus - * - * Description: - * Get 32-bit status - * - ****************************************************************************/ - -static inline uint32_t - imxrt_lpi2c_getstatus(FAR struct imxrt_lpi2c_priv_s *priv) -{ - return imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MSR_OFFSET); -} - -/**************************************************************************** - * Name: imxrt_lpi2c_isr_process - * - * Description: - * Common Interrupt Service Routine - * - ****************************************************************************/ - -static int imxrt_lpi2c_isr_process(struct imxrt_lpi2c_priv_s *priv) -{ - uint32_t status = imxrt_lpi2c_getstatus(priv); - - /* Check for new trace setup */ - - imxrt_lpi2c_tracenew(priv, status); - - /* After an error we can get an SDF */ - - if (priv->intstate == INTSTATE_DONE && (status & LPI2C_MSR_SDF) != 0) - { - imxrt_lpi2c_traceevent(priv, I2CEVENT_STOP, 0); - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MSR_OFFSET, LPI2C_MSR_SDF); - } - - /* Check if there is more bytes to send */ - - else if (((priv->flags & I2C_M_READ) == 0) && - (status & LPI2C_MSR_TDF) != 0) - { - if (priv->dcnt > 0) - { - imxrt_lpi2c_traceevent(priv, I2CEVENT_SENDBYTE, priv->dcnt); - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MTDR_OFFSET, - LPI2C_MTDR_CMD_TXD | - LPI2C_MTDR_DATA(*priv->ptr++)); - priv->dcnt--; - - if ((priv->msgc <= 0) && (priv->dcnt == 0)) - { - imxrt_lpi2c_sendstop(priv); - } - } - } - - /* Check if there is more bytes to read */ - - else if (((priv->flags & I2C_M_READ) != 0) && - (status & LPI2C_MSR_RDF) != 0) - { - /* Read a byte, if dcnt goes < 0, then read dummy bytes to ack ISRs */ - - if (priv->dcnt > 0) - { - imxrt_lpi2c_traceevent(priv, I2CEVENT_RCVBYTE, priv->dcnt); - - /* No interrupts or context switches should occur in the following - * sequence. Otherwise, additional bytes may be sent by the device. - */ - -#ifdef CONFIG_I2C_POLLED - irqstate_t flags = enter_critical_section(); -#endif - - /* Receive a byte */ - - *priv->ptr++ = imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MRDR_OFFSET) & - LPI2C_MRDR_DATA_MASK; - priv->dcnt--; - -#ifdef CONFIG_I2C_POLLED - leave_critical_section(flags); -#endif - if ((priv->msgc <= 0) && (priv->dcnt == 0)) - { - imxrt_lpi2c_sendstop(priv); - } - } - else - { - imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MRDR_OFFSET); - } - } - - if (priv->dcnt <= 0) - { - if (priv->msgc > 0 && priv->msgv != NULL) - { - priv->ptr = priv->msgv->buffer; - priv->dcnt = priv->msgv->length; - priv->flags = priv->msgv->flags; - - if ((priv->msgv->flags & I2C_M_NOSTART) == 0) - { - imxrt_lpi2c_traceevent(priv, I2CEVENT_STARTRESTART, - priv->msgc); - imxrt_lpi2c_sendstart(priv, priv->msgv->addr); - } - else - { - imxrt_lpi2c_traceevent(priv, I2CEVENT_NOSTART, priv->msgc); - } - - priv->msgv++; - priv->msgc--; - - if ((priv->flags & I2C_M_READ) != 0) - { -#ifndef CONFIG_I2C_POLLED - /* Stop TX interrupt */ - - imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MIER_OFFSET, - LPI2C_MIER_TDIE, LPI2C_MIER_RDIE); -#endif - /* Set LPI2C in read mode */ - - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MTDR_OFFSET, - LPI2C_MTDR_CMD_RXD | - LPI2C_MTDR_DATA((priv->dcnt - 1))); - } - else - { - /* Send the first byte from tx buffer */ - - imxrt_lpi2c_traceevent(priv, I2CEVENT_SENDBYTE, priv->dcnt); - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MTDR_OFFSET, - LPI2C_MTDR_CMD_TXD | - LPI2C_MTDR_DATA(*priv->ptr++)); - priv->dcnt--; - if ((priv->msgc <= 0) && (priv->dcnt == 0)) - { - imxrt_lpi2c_sendstop(priv); - } - } - } - else if (priv->msgv && ((status & LPI2C_MSR_SDF) != 0)) - { - imxrt_lpi2c_traceevent(priv, I2CEVENT_STOP, 0); - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MSR_OFFSET, LPI2C_MSR_SDF); - - /* Check is there thread waiting for this event (there should be) */ - -#ifndef CONFIG_I2C_POLLED - if (priv->intstate == INTSTATE_WAITING) - { - /* Update Status once at the end */ - - priv->status = status; - - /* inform the thread that transfer is complete - * and wake it up - */ - - nxsem_post(&priv->sem_isr); - priv->intstate = INTSTATE_DONE; - } -#else - priv->status = status; - priv->intstate = INTSTATE_DONE; -#endif - /* Mark that this transaction stopped */ - - priv->msgv = NULL; - } -#ifndef CONFIG_I2C_POLLED - else - { - imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MIER_OFFSET, - LPI2C_MIER_TDIE | LPI2C_MIER_RDIE, 0); - } -#endif - } - - /* Check for errors */ - - if ((status & LPI2C_MSR_EPF) != 0) - { - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MSR_OFFSET, LPI2C_MSR_EPF); - } - - if ((status & LPI2C_MSR_ERROR_MASK) != 0) - { - imxrt_lpi2c_traceevent(priv, I2CEVENT_ERROR, 0); - - /* Clear the TX and RX FIFOs */ - - imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCR_OFFSET, 0, - LPI2C_MCR_RTF | LPI2C_MCR_RRF); - - /* Clear the error */ - - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MSR_OFFSET, - (status & (LPI2C_MSR_NDF | LPI2C_MSR_ALF | - LPI2C_MSR_FEF))); - -#ifndef CONFIG_I2C_POLLED - if (priv->intstate == INTSTATE_WAITING) - { - /* Update Status once at the end */ - - priv->status = status; - - /* inform the thread that transfer is complete - * and wake it up - */ - - nxsem_post(&priv->sem_isr); - priv->intstate = INTSTATE_DONE; - } -#else - priv->status = status; - priv->intstate = INTSTATE_DONE; -#endif - } - - return OK; -} - -/**************************************************************************** - * Name: imxrt_lpi2c_isr - * - * Description: - * Common I2C interrupt service routine - * - ****************************************************************************/ - -#ifndef CONFIG_I2C_POLLED -static int imxrt_lpi2c_isr(int irq, void *context, FAR void *arg) -{ - struct imxrt_lpi2c_priv_s *priv = (struct imxrt_lpi2c_priv_s *)arg; - - DEBUGASSERT(priv != NULL); - return imxrt_lpi2c_isr_process(priv); -} -#endif - -/**************************************************************************** - * Name: imxrt_lpi2c_clock_enable - * - * Description: - * Ungate LPI2C clock - * - ****************************************************************************/ - -void imxrt_lpi2c_clock_enable (uint32_t base) -{ - if (base == IMXRT_LPI2C1_BASE) - { - imxrt_clockall_lpi2c1(); - } - else if (base == IMXRT_LPI2C2_BASE) - { - imxrt_clockall_lpi2c2(); - } - else if (base == IMXRT_LPI2C3_BASE) - { - imxrt_clockall_lpi2c3(); - } - else if (base == IMXRT_LPI2C4_BASE) - { - imxrt_clockall_lpi2c4_serial(); - } -} - -/**************************************************************************** - * Name: imxrt_lpi2c_clock_disable - * - * Description: - * Gate LPI2C clock - * - ****************************************************************************/ - -void imxrt_lpi2c_clock_disable (uint32_t base) -{ - if (base == IMXRT_LPI2C1_BASE) - { - imxrt_clockoff_lpi2c1(); - } - else if (base == IMXRT_LPI2C2_BASE) - { - imxrt_clockoff_lpi2c2(); - } - else if (base == IMXRT_LPI2C3_BASE) - { - imxrt_clockoff_lpi2c3(); - } - else if (base == IMXRT_LPI2C4_BASE) - { - imxrt_clockoff_lpi2c4_serial(); - } -} - -/**************************************************************************** - * Name: imxrt_lpi2c_init - * - * Description: - * Setup the I2C hardware, ready for operation with defaults - * - ****************************************************************************/ - -static int imxrt_lpi2c_init(FAR struct imxrt_lpi2c_priv_s *priv) -{ - /* Power-up and configure GPIOs */ - - /* Configure pins */ - - imxrt_config_gpio(priv->config->scl_pin); - imxrt_config_gpio(priv->config->sda_pin); - - /* Enable power and reset the peripheral */ - - imxrt_lpi2c_clock_enable(priv->config->base); - - /* Reset LPI2C before configuring it */ - - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MCR_OFFSET, LPI2C_MCR_RST); - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MCR_OFFSET, 0); - - /* Disable doze mode (Set DOZEN bit in 1 to disable) */ - - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MCR_OFFSET, LPI2C_MCR_DOZEN); - - /* Disable host request */ - - imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCFGR0_OFFSET, - LPI2C_MCFG0_HREN | LPI2C_MCFG0_HRSEL, - LPI2C_MCFG0_HRPOL); - - /* Pin config and ignore NACK disable */ - - imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCFGR1_OFFSET, - LPI2C_MCFGR1_IGNACK | LPI2C_MCFGR1_PINCFG_MASK, 0); - - /* Set tx and rx watermarks */ - - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MFCR_OFFSET, - LPI2C_MFCR_TXWATER(0) | LPI2C_MFCR_RXWATER(0)); - - /* Force a frequency update */ - - priv->frequency = 0; - imxrt_lpi2c_setclock(priv, 100000); - - /* Set scl, sda glitch filters and busy idle */ - - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MCFGR2_OFFSET, - LPI2C_MCFG2_BUSIDLE(priv->config->busy_idle) | - LPI2C_MCFG2_FILTSCL_CYCLES(priv->config->filtscl) | - LPI2C_MCFG2_FILTSDA_CYCLES(priv->config->filtsda)); - - /* Set pin low cycles to 0 (disable) */ - - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MCFGR3_OFFSET, - LPI2C_MCFG3_PINLOW_CYCLES(0)); - - /* Attach ISRs */ - -#ifndef CONFIG_I2C_POLLED - irq_attach(priv->config->irq, imxrt_lpi2c_isr, priv); - up_enable_irq(priv->config->irq); -#endif - - /* Enable I2C */ - - imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCR_OFFSET, 0, LPI2C_MCR_MEN); - return OK; -} - -/**************************************************************************** - * Name: imxrt_lpi2c_deinit - * - * Description: - * Shutdown the I2C hardware - * - ****************************************************************************/ - -static int imxrt_lpi2c_deinit(FAR struct imxrt_lpi2c_priv_s *priv) -{ - /* Disable I2C */ - - imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCR_OFFSET, LPI2C_MCR_MEN, 0); - - /* Reset LPI2C */ - - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MCR_OFFSET, LPI2C_MCR_RST); - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MCR_OFFSET, 0); - - /* Disable and detach interrupts */ - -#ifndef CONFIG_I2C_POLLED - up_disable_irq(priv->config->irq); - irq_detach(priv->config->irq); -#endif - - /* Disable clocking */ - - imxrt_lpi2c_clock_disable(priv->config->base); - - return OK; -} - -/**************************************************************************** - * Device Driver Operations - ****************************************************************************/ - -/**************************************************************************** - * Name: imxrt_lpi2c_transfer - * - * Description: - * Generic I2C transfer function - * - ****************************************************************************/ - -static int imxrt_lpi2c_transfer(FAR struct i2c_master_s *dev, - FAR struct i2c_msg_s *msgs, int count) -{ - FAR struct imxrt_lpi2c_priv_s *priv = (struct imxrt_lpi2c_priv_s *)dev; - int ret; - - DEBUGASSERT(count > 0); - - /* Ensure that address or flags don't change meanwhile */ - - ret = imxrt_lpi2c_sem_wait(priv); - if (ret < 0) - { - return ret; - } - - /* Clear any pending error interrupts */ - - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MSR_OFFSET, 0xffffffff); - - /* Old transfers are done */ - - /* Reset ptr and dcnt to ensure an unexpected data interrupt doesn't - * overwrite stale data. - */ - - priv->dcnt = 0; - priv->ptr = NULL; - - priv->msgv = msgs; - priv->msgc = count; - priv->flags = msgs->flags; - - i2cinfo("Flags %x, len %d \n", msgs->flags, msgs->length); - - /* Reset I2C trace logic */ - - imxrt_lpi2c_tracereset(priv); - - /* Set I2C clock frequency */ - - imxrt_lpi2c_setclock(priv, msgs->frequency); - - priv->status = 0; - - /* Wait for an ISR, if there was a timeout, fetch latest status to get - * the BUSY flag. - */ - - if (imxrt_lpi2c_sem_waitdone(priv) < 0) - { - ret = -ETIMEDOUT; - - i2cerr("ERROR: Timed out: MCR: status: 0x%" PRIx32 "\n", priv->status); - } - - /* Check for error status conditions */ - - else if ((priv->status & LPI2C_MSR_ERROR_MASK) != 0) - { - /* I2C_SR1_ERRORMASK is the 'OR' of the following individual bits: */ - - if (priv->status & LPI2C_MSR_ALF) - { - /* Arbitration Lost (master mode) */ - - i2cerr("Arbitration lost\n"); - ret = -EAGAIN; - } - else if (priv->status & LPI2C_MSR_NDF) - { - /* Acknowledge Failure */ - - i2cerr("Ack failure\n"); - ret = -ENXIO; - } - else - { - /* FIFO Error */ - - i2cerr("Transfer without start condition\n"); - ret = -EINVAL; - } - } - - /* Dump the trace result */ - - imxrt_lpi2c_tracedump(priv); - - /* Ensure that any ISR happening after we finish can't overwrite any user - * data. - */ - - priv->dcnt = 0; - priv->ptr = NULL; - - imxrt_lpi2c_sem_post(priv); - return ret; -} - -/**************************************************************************** - * Name: imxrt_lpi2c_reset - * - * Description: - * Perform an I2C bus reset in an attempt to break loose stuck I2C devices. - * - * Input Parameters: - * dev - Device-specific state data - * - * Returned Value: - * Zero (OK) on success; a negated errno value on failure. - * - ****************************************************************************/ - -#ifdef CONFIG_I2C_RESET -static int imxrt_lpi2c_reset(FAR struct i2c_master_s *dev) -{ - FAR struct imxrt_lpi2c_priv_s *priv = (FAR struct imxrt_lpi2c_priv_s *)dev; - unsigned int clock_count; - unsigned int stretch_count; - uint32_t scl_gpio; - uint32_t sda_gpio; - uint32_t frequency; - int ret; - - DEBUGASSERT(dev); - - /* Our caller must own a ref */ - - DEBUGASSERT(priv->refs > 0); - - /* Lock out other clients */ - - ret = imxrt_lpi2c_sem_wait_noncancelable(priv); - if (ret < 0) - { - return ret; - } - - ret = -EIO; - - /* Save the current frequency */ - - frequency = priv->frequency; - - /* De-init the port */ - - imxrt_lpi2c_deinit(priv); - - /* Use GPIO configuration to un-wedge the bus */ - - scl_gpio = priv->config->reset_scl_pin | GPIO_SION_ENABLE; - sda_gpio = priv->config->reset_sda_pin | GPIO_SION_ENABLE; - - imxrt_config_gpio(scl_gpio); - imxrt_config_gpio(sda_gpio); - - /* Let SDA go high */ - - imxrt_gpio_write(sda_gpio, 1); - - /* Clock the bus until any slaves currently driving it let it go. */ - - clock_count = 0; - while (!imxrt_gpio_read(sda_gpio)) - { - /* Give up if we have tried too hard */ - - if (clock_count++ > 10) - { - goto out; - } - - /* Sniff to make sure that clock stretching has finished. - * - * If the bus never relaxes, the reset has failed. - */ - - stretch_count = 0; - while (!imxrt_gpio_read(scl_gpio)) - { - /* Give up if we have tried too hard */ - - if (stretch_count++ > 10) - { - goto out; - } - - up_udelay(10); - } - - /* Drive SCL low */ - - imxrt_gpio_write(scl_gpio, 0); - up_udelay(10); - - /* Drive SCL high again */ - - imxrt_gpio_write(scl_gpio, 1); - up_udelay(10); - } - - /* Generate a start followed by a stop to reset slave - * state machines. - */ - - imxrt_gpio_write(sda_gpio, 0); - up_udelay(10); - imxrt_gpio_write(scl_gpio, 0); - up_udelay(10); - imxrt_gpio_write(scl_gpio, 1); - up_udelay(10); - imxrt_gpio_write(sda_gpio, 1); - up_udelay(10); - - imxrt_config_gpio(sda_gpio); - imxrt_config_gpio(scl_gpio); - - /* Re-init the port */ - - imxrt_lpi2c_init(priv); - - /* Restore the frequency */ - - imxrt_lpi2c_setclock(priv, frequency); - ret = OK; - -out: - - /* Release the port for re-use by other clients */ - - imxrt_lpi2c_sem_post(priv); - return ret; -} -#endif /* CONFIG_I2C_RESET */ - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: imxrt_i2cbus_initialize - * - * Description: - * Initialize one I2C bus - * - ****************************************************************************/ - -FAR struct i2c_master_s *imxrt_i2cbus_initialize(int port) -{ - struct imxrt_lpi2c_priv_s * priv = NULL; - irqstate_t flags; - - /* Get I2C private structure */ - - switch (port) - { -#ifdef CONFIG_IMXRT_LPI2C1 - case 1: - priv = (struct imxrt_lpi2c_priv_s *)&imxrt_lpi2c1_priv; - break; -#endif -#ifdef CONFIG_IMXRT_LPI2C2 - case 2: - priv = (struct imxrt_lpi2c_priv_s *)&imxrt_lpi2c2_priv; - break; -#endif -#ifdef CONFIG_IMXRT_LPI2C3 - case 3: - priv = (struct imxrt_lpi2c_priv_s *)&imxrt_lpi2c3_priv; - break; -#endif -#ifdef CONFIG_IMXRT_LPI2C4 - case 4: - priv = (struct imxrt_lpi2c_priv_s *)&imxrt_lpi2c4_priv; - break; -#endif - default: - return NULL; - } - - /* Initialize private data for the first time, increment reference count, - * power-up hardware and configure GPIOs. - */ - - flags = enter_critical_section(); - - if ((volatile int)priv->refs++ == 0) - { - imxrt_lpi2c_sem_init(priv); - imxrt_lpi2c_init(priv); - } - - leave_critical_section(flags); - - return (struct i2c_master_s *)priv; -} - -/**************************************************************************** - * Name: imxrt_i2cbus_uninitialize - * - * Description: - * Uninitialize an I2C bus - * - ****************************************************************************/ - -int imxrt_i2cbus_uninitialize(FAR struct i2c_master_s *dev) -{ - FAR struct imxrt_lpi2c_priv_s *priv = (struct imxrt_lpi2c_priv_s *)dev; - irqstate_t flags; - - DEBUGASSERT(dev); - - /* Decrement reference count and check for underflow */ - - if (priv->refs == 0) - { - return ERROR; - } - - flags = enter_critical_section(); - - if (--priv->refs > 0) - { - leave_critical_section(flags); - return OK; - } - - leave_critical_section(flags); - - /* Disable power and other HW resource (GPIO's) */ - - imxrt_lpi2c_deinit(priv); - - /* Release unused resources */ - - imxrt_lpi2c_sem_destroy(priv); - return OK; -} - -#endif /* CONFIG_IMXRT_LPI2C1 || CONFIG_IMXRT_LPI2C2 || \ - * CONFIG_IMXRT_LPI2C3 || CONFIG_IMXRT_LPI2C4 */ diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpspi.c b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpspi.c deleted file mode 100644 index 3abe7d9d2..000000000 --- a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpspi.c +++ /dev/null @@ -1,1706 +0,0 @@ -/**************************************************************************** - * arch/arm/src/imxrt/imxrt_lpspi.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. - * - ****************************************************************************/ - -/**************************************************************************** - * The external functions, imxrt_lpspi1/2/3/4select and - * imxrt_lpspi1/2/3/4status must be provided by board-specific logic. - * They are implementations of the select and status methods of the SPI - * interface defined by struct imxrt_lpspi_ops_s (see - * include/nuttx/spi/spi.h). All other methods (including - * imxrt_lpspibus_initialize()) are provided by common IMXRT logic. - * To use this common SPI logic on your board: - * - * 1. Provide logic in imxrt_boardinitialize() to configure SPI chip - * select pins. - * 2. Provide imxrt_lpspi1/2/3/4select() and imxrt_lpspi1/2/3/4status() - * functions in your board-specific logic. These functions will - * perform chip selection and status operations using GPIOs in the way - * your board is configured. - * 3. Add a calls to imxrt_lpspibus_initialize() in your low level - * application initialization logic - * 4. The handle returned by imxrt_lpspibus_initialize() may then be - * used to bind the SPI driver to higher level logic (e.g., calling - * mmcsd_lpspislotinitialize(), for example, will bind the SPI - * driver to the SPI MMC/SD driver). - * - ****************************************************************************/ - -/**************************************************************************** - * Included Files - ****************************************************************************/ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include "arm_internal.h" -#include "arm_arch.h" - -#include "chip.h" - -#include "imxrt_lpspi.h" -#include "imxrt_gpio.h" -#include "hardware/imxrt_pinmux.h" -#include "hardware/imxrt_lpspi.h" -#include "hardware/imxrt_ccm.h" -#include "imxrt_periphclks.h" - -#if defined(CONFIG_IMXRT_LPSPI1) || defined(CONFIG_IMXRT_LPSPI2) || \ - defined(CONFIG_IMXRT_LPSPI3) || defined(CONFIG_IMXRT_LPSPI4) - -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -/* Configuration ************************************************************/ - -/* SPI interrupts */ - -#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS -# error "Interrupt driven SPI not yet supported" -#endif - -#if defined(CONFIG_IMXRT_LPSPI_DMA) -# error "DMA mode is not yet supported" -#endif - -/* Can't have both interrupt driven SPI and SPI DMA */ - -#if defined(CONFIG_IMXRT_LPSPI_INTERRUPTS) && defined(CONFIG_IMXRT_LPSPI_DMA) -# error "Cannot enable both interrupt mode and DMA mode for SPI" -#endif - -/**************************************************************************** - * Private Types - ****************************************************************************/ - -struct imxrt_lpspidev_s -{ - struct spi_dev_s spidev; /* Externally visible part of the SPI interface */ - uint32_t spibase; /* SPIn base address */ -#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS - uint8_t spiirq; /* SPI IRQ number */ -#endif - sem_t exclsem; /* Held while chip is selected for mutual exclusion */ - uint32_t frequency; /* Requested clock frequency */ - uint32_t actual; /* Actual clock frequency */ - int8_t nbits; /* Width of word in bits */ - uint8_t mode; /* Mode 0,1,2,3 */ -}; - -enum imxrt_delay_e -{ - LPSPI_PCS_TO_SCK = 1, /* PCS-to-SCK delay. */ - LPSPI_LAST_SCK_TO_PCS, /* Last SCK edge to PCS delay. */ - LPSPI_BETWEEN_TRANSFER /* Delay between transfers. */ -}; - -/**************************************************************************** - * Private Function Prototypes - ****************************************************************************/ - -/* Helpers */ - -static inline uint32_t -imxrt_lpspi_getreg32(FAR struct imxrt_lpspidev_s *priv, - uint8_t offset); -static inline void imxrt_lpspi_putreg32(FAR struct imxrt_lpspidev_s *priv, - uint8_t offset, uint32_t value); -static inline uint32_t imxrt_lpspi_readword( - FAR struct imxrt_lpspidev_s *priv); -static inline void imxrt_lpspi_writeword(FAR struct imxrt_lpspidev_s *priv, - uint16_t byte); -static inline bool imxrt_lpspi_9to16bitmode( - FAR struct imxrt_lpspidev_s *priv); -static inline void imxrt_lpspi_master_set_delays(FAR struct imxrt_lpspidev_s - *priv, uint32_t delay_ns, - enum imxrt_delay_e type); -static inline void imxrt_lpspi_master_set_delay_scaler( - FAR struct imxrt_lpspidev_s *priv, - uint32_t scaler, - enum imxrt_delay_e type); - -/* SPI methods */ - -static int imxrt_lpspi_lock(FAR struct spi_dev_s *dev, bool lock); -static uint32_t imxrt_lpspi_setfrequency(FAR struct spi_dev_s *dev, - uint32_t frequency); -static void imxrt_lpspi_setmode(FAR struct spi_dev_s *dev, - enum spi_mode_e mode); -static void imxrt_lpspi_setbits(FAR struct spi_dev_s *dev, int nbits); -#ifdef CONFIG_SPI_HWFEATURES -static int imxrt_lpspi_hwfeatures(FAR struct spi_dev_s *dev, - imxrt_lpspi_hwfeatures_t features); -#endif -static uint32_t imxrt_lpspi_send(FAR struct spi_dev_s *dev, uint32_t wd); -static void imxrt_lpspi_exchange(FAR struct spi_dev_s *dev, - FAR const void *txbuffer, - FAR void *rxbuffer, - size_t nwords); -#ifndef CONFIG_SPI_EXCHANGE -static void imxrt_lpspi_sndblock(FAR struct spi_dev_s *dev, - FAR const void *txbuffer, size_t nwords); -static void imxrt_lpspi_recvblock(FAR struct spi_dev_s *dev, - FAR void *rxbuffer, - size_t nwords); -#endif - -/* Initialization */ - -static void imxrt_lpspi_bus_initialize(FAR struct imxrt_lpspidev_s *priv); - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -#ifdef CONFIG_IMXRT_LPSPI1 -static const struct spi_ops_s g_spi1ops = -{ - .lock = imxrt_lpspi_lock, - .select = imxrt_lpspi1select, - .setfrequency = imxrt_lpspi_setfrequency, - .setmode = imxrt_lpspi_setmode, - .setbits = imxrt_lpspi_setbits, -#ifdef CONFIG_SPI_HWFEATURES - .hwfeatures = imxrt_lpspi_hwfeatures, -#endif - .status = imxrt_lpspi1status, -#ifdef CONFIG_SPI_CMDDATA - .cmddata = imxrt_lpspi1cmddata, -#endif - .send = imxrt_lpspi_send, -#ifdef CONFIG_SPI_EXCHANGE - .exchange = imxrt_lpspi_exchange, -#else - .sndblock = imxrt_lpspi_sndblock, - .recvblock = imxrt_lpspi_recvblock, -#endif -#ifdef CONFIG_SPI_CALLBACK - .registercallback = imxrt_lpspi1register, /* Provided externally */ -#else - .registercallback = 0, /* Not implemented */ -#endif -}; - -static struct imxrt_lpspidev_s g_lpspi1dev = -{ - .spidev = - { - &g_spi1ops - }, - .spibase = IMXRT_LPSPI1_BASE, -#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS - .spiirq = IMXRT_IRQ_LPSPI1, -#endif -#ifdef CONFIG_IMXRT_LPSPI_DMA - .rxch = DMAMAP_LPSPI1_RX, - .txch = DMAMAP_LPSPI1_TX, -#endif -}; -#endif - -#ifdef CONFIG_IMXRT_LPSPI2 -static const struct spi_ops_s g_spi2ops = -{ - .lock = imxrt_lpspi_lock, - .select = imxrt_lpspi2select, - .setfrequency = imxrt_lpspi_setfrequency, - .setmode = imxrt_lpspi_setmode, - .setbits = imxrt_lpspi_setbits, -#ifdef CONFIG_SPI_HWFEATURES - .hwfeatures = imxrt_lpspi_hwfeatures, -#endif - .status = imxrt_lpspi2status, -#ifdef CONFIG_SPI_CMDDATA - .cmddata = imxrt_lpspi2cmddata, -#endif - .send = imxrt_lpspi_send, -#ifdef CONFIG_SPI_EXCHANGE - .exchange = imxrt_lpspi_exchange, -#else - .sndblock = imxrt_lpspi_sndblock, - .recvblock = imxrt_lpspi_recvblock, -#endif -#ifdef CONFIG_SPI_CALLBACK - .registercallback = imxrt_lpspi2register, /* Provided externally */ -#else - .registercallback = 0, /* Not implemented */ -#endif -}; - -static struct imxrt_lpspidev_s g_lpspi2dev = -{ - .spidev = - { - &g_spi2ops - }, - .spibase = IMXRT_LPSPI2_BASE, -#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS - .spiirq = IMXRT_IRQ_LPSPI2, -#endif -#ifdef CONFIG_IMXRT_LPSPI_DMA - .rxch = DMAMAP_LPSPI2_RX, - .txch = DMAMAP_LPSPI2_TX, -#endif -}; -#endif - -#ifdef CONFIG_IMXRT_LPSPI3 -static const struct spi_ops_s g_spi3ops = -{ - .lock = imxrt_lpspi_lock, - .select = imxrt_lpspi3select, - .setfrequency = imxrt_lpspi_setfrequency, - .setmode = imxrt_lpspi_setmode, - .setbits = imxrt_lpspi_setbits, -#ifdef CONFIG_SPI_HWFEATURES - .hwfeatures = imxrt_lpspi_hwfeatures, -#endif - .status = imxrt_lpspi3status, -#ifdef CONFIG_SPI_CMDDATA - .cmddata = imxrt_lpspi3cmddata, -#endif - .send = imxrt_lpspi_send, -#ifdef CONFIG_SPI_EXCHANGE - .exchange = imxrt_lpspi_exchange, -#else - .sndblock = imxrt_lpspi_sndblock, - .recvblock = imxrt_lpspi_recvblock, -#endif -#ifdef CONFIG_SPI_CALLBACK - .registercallback = imxrt_lpspi3register, /* Provided externally */ -#else - .registercallback = 0, /* Not implemented */ -#endif -}; - -static struct imxrt_lpspidev_s g_lpspi3dev = -{ - .spidev = - { - &g_spi3ops - }, - .spibase = IMXRT_LPSPI3_BASE, -#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS - .spiirq = IMXRT_IRQ_LPSPI3, -#endif -#ifdef CONFIG_IMXRT_LPSPI_DMA - .rxch = DMAMAP_LPSPI3_RX, - .txch = DMAMAP_LPSPI3_TX, -#endif -}; -#endif - -#ifdef CONFIG_IMXRT_LPSPI4 -static const struct spi_ops_s g_spi4ops = -{ - .lock = imxrt_lpspi_lock, - .select = imxrt_lpspi4select, - .setfrequency = imxrt_lpspi_setfrequency, - .setmode = imxrt_lpspi_setmode, - .setbits = imxrt_lpspi_setbits, -#ifdef CONFIG_SPI_HWFEATURES - .hwfeatures = imxrt_lpspi_hwfeatures, -#endif - .status = imxrt_lpspi4status, -#ifdef CONFIG_SPI_CMDDATA - .cmddata = imxrt_lpspi4cmddata, -#endif - .send = imxrt_lpspi_send, -#ifdef CONFIG_SPI_EXCHANGE - .exchange = imxrt_lpspi_exchange, -#else - .sndblock = imxrt_lpspi_sndblock, - .recvblock = imxrt_lpspi_recvblock, -#endif -#ifdef CONFIG_SPI_CALLBACK - .registercallback = imxrt_lpspi4register, /* Provided externally */ -#else - .registercallback = 0, /* Not implemented */ -#endif -}; - -static struct imxrt_lpspidev_s g_lpspi4dev = -{ - .spidev = - { - &g_spi4ops - }, - .spibase = IMXRT_LPSPI4_BASE, -#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS - .spiirq = IMXRT_IRQ_LPSPI4, -#endif -#ifdef CONFIG_IMXRT_LPSPI_DMA - .rxch = DMAMAP_LPSPI4_RX, - .txch = DMAMAP_LPSPI4_TX, -#endif -}; -#endif - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: imxrt_lpspi_getreg8 - * - * Description: - * Get the contents of the SPI register at offset - * - * Input Parameters: - * priv - private SPI device structure - * offset - offset to the register of interest - * - * Returned Value: - * The contents of the 8-bit register - * - ****************************************************************************/ - -static inline uint8_t imxrt_lpspi_getreg8(FAR struct imxrt_lpspidev_s *priv, - uint8_t offset) -{ - return getreg8(priv->spibase + offset); -} - -/**************************************************************************** - * Name: imxrt_lpspi_putreg8 - * - * Description: - * Write a 8-bit value to the SPI register at offset - * - * Input Parameters: - * priv - private SPI device structure - * offset - offset to the register of interest - * value - the 8-bit value to be written - * - ****************************************************************************/ - -static inline void imxrt_lpspi_putreg8(FAR struct imxrt_lpspidev_s *priv, - uint8_t offset, uint8_t value) -{ - putreg8(value, priv->spibase + offset); -} - -/**************************************************************************** - * Name: imxrt_lpspi_getreg - * - * Description: - * Get the contents of the SPI register at offset - * - * Input Parameters: - * priv - private SPI device structure - * offset - offset to the register of interest - * - * Returned Value: - * The contents of the 32-bit register - * - ****************************************************************************/ - -static inline uint32_t -imxrt_lpspi_getreg32(FAR struct imxrt_lpspidev_s *priv, - uint8_t offset) -{ - return getreg32(priv->spibase + offset); -} - -/**************************************************************************** - * Name: imxrt_lpspi_putreg - * - * Description: - * Write a 16-bit value to the SPI register at offset - * - * Input Parameters: - * priv - private SPI device structure - * offset - offset to the register of interest - * value - the 32-bit value to be written - * - * Returned Value: - * The contents of the 32-bit register - * - ****************************************************************************/ - -static inline void imxrt_lpspi_putreg32(FAR struct imxrt_lpspidev_s *priv, - uint8_t offset, uint32_t value) -{ - putreg32(value, priv->spibase + offset); -} - -/**************************************************************************** - * Name: imxrt_lpspi_readword - * - * Description: - * Read one word from SPI - * - * Input Parameters: - * priv - Device-specific state data - * - * Returned Value: - * word as read - * - ****************************************************************************/ - -static inline uint32_t -imxrt_lpspi_readword(FAR struct imxrt_lpspidev_s *priv) -{ - /* Wait until the receive buffer is not empty */ - - while ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_SR_OFFSET) - & LPSPI_SR_RDF) == 0); - - /* Then return the received byte */ - - return imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_RDR_OFFSET); -} - -/**************************************************************************** - * Name: imxrt_lpspi_writeword - * - * Description: - * Write one word to SPI - * - * Input Parameters: - * priv - Device-specific state data - * word - word to send - * - * Returned Value: - * None - * - ****************************************************************************/ - -static inline void imxrt_lpspi_writeword(FAR struct imxrt_lpspidev_s *priv, - uint16_t word) -{ - /* Wait until the transmit buffer is empty */ - - while ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_SR_OFFSET) - & LPSPI_SR_TDF) == 0); - - /* Then send the word */ - - imxrt_lpspi_putreg32(priv, IMXRT_LPSPI_TDR_OFFSET, word); -} - -/**************************************************************************** - * Name: imxrt_lpspi_readbyte - * - * Description: - * Read one byte from SPI - * - * Input Parameters: - * priv - Device-specific state data - * - * Returned Value: - * Byte as read - * - ****************************************************************************/ - -static inline uint8_t imxrt_lpspi_readbyte(FAR struct imxrt_lpspidev_s *priv) -{ - /* Wait until the receive buffer is not empty */ - - while ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_SR_OFFSET) - & LPSPI_SR_RDF) == 0); - - /* Then return the received byte */ - - return imxrt_lpspi_getreg8(priv, IMXRT_LPSPI_RDR_OFFSET); -} - -/**************************************************************************** - * Name: imxrt_lpspi_writebyte - * - * Description: - * Write one 8-bit frame to the SPI FIFO - * - * Input Parameters: - * priv - Device-specific state data - * byte - Byte to send - * - * Returned Value: - * None - * - ****************************************************************************/ - -static inline void imxrt_lpspi_writebyte(FAR struct imxrt_lpspidev_s *priv, - uint8_t byte) -{ - /* Wait until the transmit buffer is empty */ - - while ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_SR_OFFSET) - & LPSPI_SR_TDF) == 0); - - /* Then send the byte */ - - imxrt_lpspi_putreg8(priv, IMXRT_LPSPI_TDR_OFFSET, byte); -} - -/**************************************************************************** - * Name: imxrt_lpspi_9to16bitmode - * - * Description: - * Check if the SPI is operating in more then 8 bit mode - * - * Input Parameters: - * priv - Device-specific state data - * - * Returned Value: - * true: >8 bit mode-bit mode, false: <= 8-bit mode - * - ****************************************************************************/ - -static inline bool -imxrt_lpspi_9to16bitmode(FAR struct imxrt_lpspidev_s *priv) -{ - bool ret; - - if (((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_TCR_OFFSET) & - LPSPI_TCR_FRAMESZ_MASK) + 1) < 9) - { - ret = false; - } - else - { - ret = true; - } - - return ret; -} - -/**************************************************************************** - * Name: imxrt_lpspi_modifyreg - * - * Description: - * Clear and set bits in register - * - * Input Parameters: - * priv - Device-specific state data - * offset - Register offset - * clrbits - The bits to clear - * setbits - The bits to set - * - * Returned Value: - * None - * - ****************************************************************************/ - -static void imxrt_lpspi_modifyreg32(FAR struct imxrt_lpspidev_s *priv, - uint8_t offset, uint32_t clrbits, - uint32_t setbits) -{ - modifyreg32(priv->spibase + offset, clrbits, setbits); -} - -/**************************************************************************** - * Name: imxrt_lpspi_master_set_delays - * - * Description: - * SET LPSPI Delay times - * - * Input Parameters: - * priv - Device-specific state data - * scaler - scaler value - * type - delay time type - * - * Returned Value: - * None - * - ****************************************************************************/ - -static inline void imxrt_lpspi_master_set_delay_scaler( - FAR struct imxrt_lpspidev_s *priv, - uint32_t scaler, - enum imxrt_delay_e type) -{ - switch (type) - { - case LPSPI_PCS_TO_SCK: - imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CCR_OFFSET, - LPSPI_CCR_PCSSCK_MASK, 0); - imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CCR_OFFSET, 0, - LPSPI_CCR_PCSSCK(scaler)); - break; - - case LPSPI_LAST_SCK_TO_PCS: - imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CCR_OFFSET, - LPSPI_CCR_SCKPCS_MASK, 0); - imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CCR_OFFSET, 0, - LPSPI_CCR_SCKPCS(scaler)); - break; - - case LPSPI_BETWEEN_TRANSFER: - imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CCR_OFFSET, - LPSPI_CCR_DBT_MASK, 0); - imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CCR_OFFSET, 0, - LPSPI_CCR_DBT(scaler)); - break; - } -} - -/**************************************************************************** - * Name: imxrt_lpspi_master_set_delays - * - * Description: - * SET LPSPI Delay times - * - * Input Parameters: - * priv - Device-specific state data - * delay_ns - delay time in nano seconds - * type - delay time type - * - * Returned Value: - * None - * - ****************************************************************************/ - -static inline void imxrt_lpspi_master_set_delays( - FAR struct imxrt_lpspidev_s *priv, - uint32_t delay_ns, - enum imxrt_delay_e type) -{ - uint32_t pll3_div; - uint32_t pll_freq; - uint32_t src_freq; - uint64_t real_delay; - uint64_t best_delay; - uint32_t scaler; - uint32_t best_scaler; - uint32_t diff; - uint32_t min_diff; - uint64_t initial_delay_ns; - uint32_t clock_div_prescaler; - uint32_t additional_scaler; - - if ((getreg32(IMXRT_CCM_ANALOG_PLL_USB2) & - CCM_ANALOG_PLL_USB2_DIV_SELECT_MASK) != 0) - { - pll3_div = 22; - } - else - { - pll3_div = 20; - } - - pll_freq = BOARD_XTAL_FREQUENCY * pll3_div; - - /* Assumption this formula will work only if the LPSPI Clock Source is PLL3 - * PFD0 so check if LPSPI clock source is set to 1 (PLL3 PFD0) in CCM_CBCMR - * register bits 4-5 - */ - - src_freq = pll_freq / - ((getreg32(IMXRT_CCM_ANALOG_PFD_480) - & CCM_ANALOG_PFD_480_PFD0_FRAC_MASK) >> - CCM_ANALOG_PFD_480_PFD0_FRAC_SHIFT); - src_freq *= 18; - src_freq /= ((getreg32(IMXRT_CCM_CBCMR) & CCM_CBCMR_LPSPI_PODF_MASK) >> - CCM_CBCMR_LPSPI_PODF_SHIFT) + 1; - - clock_div_prescaler = src_freq / - (1 << ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_TCR_OFFSET) & - LPSPI_TCR_PRESCALE_MASK) >> LPSPI_TCR_PRESCALE_SHIFT)); - - min_diff = 0xffffffff; - - /* Initialize scaler to max value to generate the max delay */ - - best_scaler = 0xff; - - if (type == LPSPI_BETWEEN_TRANSFER) - { - /* First calculate the initial, default delay, note min delay is 2 - * clock cycles. Due to large size of * calculated values (uint64_t), - * we need to break up the calculation into several steps to ensure - * accurate calculated results - */ - - initial_delay_ns = 1000000000U; - initial_delay_ns *= 2; - initial_delay_ns /= clock_div_prescaler; - - /* Calculate the maximum delay */ - - best_delay = 1000000000U; - - /* based on DBT+2, or 255 + 2 */ - - best_delay *= 257; - best_delay /= clock_div_prescaler; - - additional_scaler = 1U; - } - else - { - /* First calculate the initial, default delay, min delay is 1 clock - * cycle. Due to large size of calculated values (uint64_t), we need to - * break up the calculation into several steps to ensure accurate - * calculated * results. - */ - - initial_delay_ns = 1000000000U; - initial_delay_ns /= clock_div_prescaler; - - /* Calculate the maximum delay */ - - best_delay = 1000000000U; - - /* Based on SCKPCS+1 or PCSSCK+1, or 255 + 1 */ - - best_delay *= 256; - best_delay /= clock_div_prescaler; - - additional_scaler = 0; - } - - /* If the initial, default delay is already greater than the desired delay, - * then * set the delay to their initial value (0) and return the delay. In - * other words, * there is no way to decrease the delay value further. - */ - - if (initial_delay_ns >= delay_ns) - { - imxrt_lpspi_master_set_delay_scaler(priv, 0, type); - } - else - { - /* If min_diff = 0, the exit for loop */ - - for (scaler = 0; (scaler < 256) && min_diff; scaler++) - { - /* Calculate the real delay value as we cycle through the scaler - * values. Due to large size of calculated values (uint64_t), - * we need to break up the calculation into several steps to - * ensure accurate calculated results - */ - - real_delay = 1000000000U; - real_delay *= (scaler + 1 + additional_scaler); - real_delay /= clock_div_prescaler; - - /* calculate the delay difference based on the conditional - * statement that states that the calculated delay must not be - * less then the desired delay - */ - - if (real_delay >= delay_ns) - { - diff = real_delay - delay_ns; - if (min_diff > diff) - { - /* A better match found */ - - min_diff = diff; - best_scaler = scaler; - best_delay = real_delay; - } - } - } - - imxrt_lpspi_master_set_delay_scaler(priv, best_scaler, type); - } -} - -/**************************************************************************** - * Name: imxrt_lpspi_lock - * - * Description: - * On SPI buses where there are multiple devices, it will be necessary to - * lock SPI to have exclusive access to the buses for a sequence of - * transfers. The bus should be locked before the chip is selected. After - * locking the SPI bus, the caller should then also call the setfrequency, - * setbits, and setmode methods to make sure that the SPI is properly - * configured for the device. If the SPI bus is being shared, then it - * may have been left in an incompatible state. - * - * Input Parameters: - * dev - Device-specific state data - * lock - true: Lock spi bus, false: unlock SPI bus - * - * Returned Value: - * None - * - ****************************************************************************/ - -static int imxrt_lpspi_lock(FAR struct spi_dev_s *dev, bool lock) -{ - FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)dev; - int ret; - - if (lock) - { - ret = nxsem_wait_uninterruptible(&priv->exclsem); - } - else - { - ret = nxsem_post(&priv->exclsem); - } - - return ret; -} - -/**************************************************************************** - * Name: imxrt_lpspi_setfrequency - * - * Description: - * Set the SPI frequency. - * - * Input Parameters: - * dev - Device-specific state data - * frequency - The SPI frequency requested - * - * Returned Value: - * Returns the actual frequency selected - * - ****************************************************************************/ - -static uint32_t imxrt_lpspi_setfrequency(FAR struct spi_dev_s *dev, - uint32_t frequency) -{ - FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)dev; - - uint32_t men; - uint32_t pll_freq; - uint32_t pll3_div; - uint32_t src_freq = 0; - uint32_t prescaler; - uint32_t best_prescaler; - uint32_t scaler; - uint32_t best_scaler; - uint32_t real_frequency; - uint32_t best_frequency; - uint32_t diff; - uint32_t min_diff; - - /* Has the LPSPI bus frequency changed? */ - - if (frequency != priv->frequency) - { - /* Disable LPSPI if it is enabled */ - - men = imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CR_OFFSET) & LPSPI_CR_MEN; - if (men) - { - imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, - LPSPI_CR_MEN, 0); - } - - if ((getreg32(IMXRT_CCM_ANALOG_PLL_USB2) & - CCM_ANALOG_PLL_USB2_DIV_SELECT_MASK) != 0) - { - pll3_div = 22; - } - else - { - pll3_div = 20; - } - - pll_freq = BOARD_XTAL_FREQUENCY * pll3_div; - - /* Assumption this formula will work only if the LPSPI Clock Source is - * PLL3 PFD0 * so check if LPSPI clock source is set to 1 (PLL3 PFD0) - * in CCM_CBCMR register bits 4-5 - */ - - src_freq = pll_freq / - ((getreg32(IMXRT_CCM_ANALOG_PFD_480) - & CCM_ANALOG_PFD_480_PFD0_FRAC_MASK) >> - CCM_ANALOG_PFD_480_PFD0_FRAC_SHIFT); - src_freq *= 18; - src_freq /= ((getreg32(IMXRT_CCM_CBCMR) & CCM_CBCMR_LPSPI_PODF_MASK) >> - CCM_CBCMR_LPSPI_PODF_SHIFT) + 1; - - min_diff = 0xffffffff; - - best_prescaler = 7; - best_scaler = 255; - - best_frequency = 0; - - for (prescaler = 0; (prescaler < 8) && min_diff; prescaler++) - { - for (scaler = 0; (scaler < 256) && min_diff; scaler++) - { - real_frequency = src_freq / ((1 << prescaler) * (scaler + 2)); - - /* Calculate the frequency difference based on conditional - * statement that states that the calculated frequency must not - * exceed desired frequency. - */ - - if (frequency >= real_frequency) - { - diff = frequency - real_frequency; - if (min_diff > diff) - { - /* A better match found */ - - min_diff = diff; - best_prescaler = prescaler; - best_scaler = scaler; - best_frequency = real_frequency; - } - } - } - } - - /* Write the best values in the CCR register */ - - imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CCR_OFFSET, - LPSPI_CCR_SCKDIV_MASK, - LPSPI_CCR_SCKDIV(best_scaler)); - - imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_TCR_OFFSET, - LPSPI_TCR_PRESCALE_MASK, - LPSPI_TCR_PRESCALE(best_prescaler)); - - priv->frequency = frequency; - priv->actual = best_frequency; - - imxrt_lpspi_master_set_delays(priv, 1000000000 / best_frequency, - LPSPI_PCS_TO_SCK); - imxrt_lpspi_master_set_delays(priv, 1000000000 / best_frequency, - LPSPI_LAST_SCK_TO_PCS); - imxrt_lpspi_master_set_delays(priv, 1000000000 / best_frequency, - LPSPI_BETWEEN_TRANSFER); - - /* Re-enable LPSPI if it was enabled previously */ - - if (men) - { - imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0, - LPSPI_CR_MEN); - } - } - - return priv->actual; -} - -/**************************************************************************** - * Name: imxrt_lpspi_setmode - * - * Description: - * Set the SPI mode. see enum spi_mode_e mode for mode definitions - * - * Input Parameters: - * dev - Device-specific state data - * mode - The SPI mode requested - * - * Returned Value: - * Returns the actual frequency selected - * - ****************************************************************************/ - -static void imxrt_lpspi_setmode(FAR struct spi_dev_s *dev, - enum spi_mode_e mode) -{ - FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)dev; - uint32_t setbits; - uint32_t clrbits; - uint32_t men; - - spiinfo("mode=%d\n", mode); - - /* Has the mode changed? */ - - if (mode != priv->mode) - { - /* Disable LPSPI if it is enabled */ - - men = imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CR_OFFSET) & LPSPI_CR_MEN; - if (men) - { - imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, - LPSPI_CR_MEN, 0); - } - - switch (mode) - { - case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */ - setbits = 0; - clrbits = LPSPI_TCR_CPOL | LPSPI_TCR_CPHA; - break; - - case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */ - setbits = LPSPI_TCR_CPHA; - clrbits = LPSPI_TCR_CPOL; - break; - - case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */ - setbits = LPSPI_TCR_CPOL; - clrbits = LPSPI_TCR_CPHA; - break; - - case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */ - setbits = LPSPI_TCR_CPOL | LPSPI_TCR_CPHA; - clrbits = 0; - break; - - default: - return; - } - - imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_TCR_OFFSET, - clrbits, setbits); - - while ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_RSR_OFFSET) & - LPSPI_RSR_RXEMPTY) != LPSPI_RSR_RXEMPTY) - { - /* Flush SPI read FIFO */ - - imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_RSR_OFFSET); - } - - /* Save the mode so that subsequent re-configurations will be faster */ - - priv->mode = mode; - - /* Re-enable LPSPI if it was enabled previously */ - - if (men) - { - imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0, - LPSPI_CR_MEN); - } - } -} - -/**************************************************************************** - * Name: imxrt_lpspi_setbits - * - * Description: - * Set the number of bits per word. - * - * Input Parameters: - * dev - Device-specific state data - * nbits - The number of bits requested - * - * Returned Value: - * None - * - ****************************************************************************/ - -static void imxrt_lpspi_setbits(FAR struct spi_dev_s *dev, int nbits) -{ - FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)dev; - uint32_t men; - - spiinfo("nbits=%d\n", nbits); - - /* Has the number of bits changed? */ - - if (nbits != priv->nbits) - { - if (nbits < 2 || nbits > 4096) - { - return; - } - - /* Disable LPSPI if it is enabled */ - - men = imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CR_OFFSET) & LPSPI_CR_MEN; - if (men) - { - imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, - LPSPI_CR_MEN, 0); - } - - imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_TCR_OFFSET, - LPSPI_TCR_FRAMESZ_MASK, - LPSPI_TCR_FRAMESZ(nbits - 1)); - - /* Save the selection so the subsequent re-configurations - * will be faster. - */ - - priv->nbits = nbits; - - /* Re-enable LPSPI if it was enabled previously */ - - if (men) - { - imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0, - LPSPI_CR_MEN); - } - } -} - -/**************************************************************************** - * Name: imxrt_lpspi_hwfeatures - * - * Description: - * Set hardware-specific feature flags. - * - * Input Parameters: - * dev - Device-specific state data - * features - H/W feature flags - * - * Returned Value: - * Zero (OK) if the selected H/W features are enabled; A negated errno - * value if any H/W feature is not supportable. - * - ****************************************************************************/ - -#ifdef CONFIG_SPI_HWFEATURES -static int imxrt_lpspi_hwfeatures(FAR struct spi_dev_s *dev, - imxrt_lpspi_hwfeatures_t features) -{ -#ifdef CONFIG_SPI_BITORDER - FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)dev; - uint32_t setbits; - uint32_t clrbits; - - spiinfo("features=%08x\n", features); - - /* Transfer data LSB first? */ - - if ((features & HWFEAT_LSBFIRST) != 0) - { - setbits = LPSPI_TCR_LSBF; - clrbits = 0; - } - else - { - setbits = 0; - clrbits = LPSPI_TCR_LSBF; - } - - imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_TCR_OFFSET, clrbits, setbits); - - /* Other H/W features are not supported */ - - return ((features & ~HWFEAT_LSBFIRST) == 0) ? OK : -ENOSYS; -#else - return -ENOSYS; -#endif -} -#endif - -/**************************************************************************** - * Name: imxrt_lpspi_send - * - * Description: - * Exchange one word on SPI - * - * Input Parameters: - * dev - Device-specific state data - * wd - The word to send. the size of the data is determined by the - * number of bits selected for the SPI interface. - * - * Returned Value: - * response - * - ****************************************************************************/ - -static uint32_t imxrt_lpspi_send(FAR struct spi_dev_s *dev, uint32_t wd) -{ - FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)dev; - uint32_t regval; - uint32_t ret; - - DEBUGASSERT(priv && priv->spibase); - - imxrt_lpspi_writeword(priv, wd); - - while ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_SR_OFFSET) & - LPSPI_SR_RDF) != LPSPI_SR_RDF); - - ret = imxrt_lpspi_readword(priv); - - /* Check and clear any error flags (Reading from the SR clears the error - * flags). - */ - - regval = imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_SR_OFFSET); - - spiinfo( - "Sent: %04" PRIx32 " Return: %04" PRIx32 " Status: %02" PRIx32 "\n", - wd, ret, regval); - - UNUSED(regval); - return ret; -} - -/**************************************************************************** - * Name: imxrt_lpspi_exchange (no DMA). aka imxrt_lpspi_exchange_nodma - * - * Description: - * Exchange a block of data on SPI without using DMA - * - * Input Parameters: - * dev - Device-specific state data - * txbuffer - A pointer to the buffer of data to be sent - * rxbuffer - A pointer to a buffer in which to receive data - * nwords - the length of data to be exchanged in units of words. - * The wordsize is determined by the number of bits-per-word - * selected for the SPI interface. If nbits <= 8, the data is - * packed into uint8_t's; if nbits >8, the data is packed - * into uint16_t's - * - * Returned Value: - * None - * - ****************************************************************************/ - -#if !defined(CONFIG_IMXRT_LPSPI_DMA) || defined(CONFIG_IMXRT_DMACAPABLE) -#if !defined(CONFIG_IMXRT_LPSPI_DMA) -static void imxrt_lpspi_exchange(FAR struct spi_dev_s *dev, - FAR const void *txbuffer, - FAR void *rxbuffer, - size_t nwords) -#else -static void imxrt_lpspi_exchange_nodma(FAR struct spi_dev_s *dev, - FAR const void *txbuffer, - FAR void *rxbuffer, size_t nwords) -#endif -{ - FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)dev; - DEBUGASSERT(priv && priv->spibase); - - spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords); - - /* 8- or 16-bit mode? */ - - if (imxrt_lpspi_9to16bitmode(priv)) - { - /* 16-bit mode */ - - const uint16_t *src = (const uint16_t *)txbuffer; - uint16_t *dest = (uint16_t *) rxbuffer; - uint16_t word; - - while (nwords-- > 0) - { - /* Get the next word to write. Is there a source buffer? */ - - if (src) - { - word = *src++; - } - else - { - word = 0xffff; - } - - /* Exchange one word */ - - word = (uint16_t) imxrt_lpspi_send(dev, (uint32_t) word); - - /* Is there a buffer to receive the return value? */ - - if (dest) - { - *dest++ = word; - } - } - } - else - { - /* 8-bit mode */ - - const uint8_t *src = (const uint8_t *)txbuffer; - uint8_t *dest = (uint8_t *) rxbuffer; - uint8_t word; - - while (nwords-- > 0) - { - /* Get the next word to write. Is there a source buffer? */ - - if (src) - { - word = *src++; - } - else - { - word = 0xff; - } - - /* Exchange one word */ - - word = (uint8_t) imxrt_lpspi_send(dev, (uint32_t) word); - - /* Is there a buffer to receive the return value? */ - - if (dest) - { - *dest++ = word; - } - } - } -} -#endif /* !CONFIG_IMXRT_LPSPI_DMA || CONFIG_IMXRT_DMACAPABLE */ - -/**************************************************************************** - * Name: imxrt_lpspi_sndblock - * - * Description: - * Send a block of data on SPI - * - * Input Parameters: - * dev - Device-specific state data - * txbuffer - A pointer to the buffer of data to be sent - * nwords - the length of data to send from the buffer in number of - * words. The wordsize is determined by the number of - * bits-per-word selected for the SPI interface. If nbits <= 8, - * the data is packed into uint8_t's; if nbits >8, the data is - * packed into uint16_t's - * - * Returned Value: - * None - * - ****************************************************************************/ - -#ifndef CONFIG_SPI_EXCHANGE -static void imxrt_lpspi_sndblock(FAR struct spi_dev_s *dev, - FAR const void *txbuffer, size_t nwords) -{ - spiinfo("txbuffer=%p nwords=%d\n", txbuffer, nwords); - return imxrt_lpspi_exchange(dev, txbuffer, NULL, nwords); -} -#endif - -/**************************************************************************** - * Name: imxrt_lpspi_recvblock - * - * Description: - * Receive a block of data from SPI - * - * Input Parameters: - * dev - Device-specific state data - * rxbuffer - A pointer to the buffer in which to receive data - * nwords - the length of data that can be received in the buffer in - * number of words. The wordsize is determined by the number of - * bits-per-word selected for the SPI interface. If nbits <= 8, - * the data is packed into uint8_t's; if nbits >8, the data is - * packed into uint16_t's - * - * Returned Value: - * None - * - ****************************************************************************/ - -#ifndef CONFIG_SPI_EXCHANGE -static void imxrt_lpspi_recvblock(FAR struct spi_dev_s *dev, - FAR void *rxbuffer, size_t nwords) -{ - spiinfo("rxbuffer=%p nwords=%d\n", rxbuffer, nwords); - return imxrt_lpspi_exchange(dev, NULL, rxbuffer, nwords); -} -#endif - -/**************************************************************************** - * Name: imxrt_lpspi_clock_enable - * - * Description: - * Ungate LPSPI clock - * - ****************************************************************************/ - -void imxrt_lpspi_clock_enable(uint32_t base) -{ - if (base == IMXRT_LPSPI1_BASE) - { - imxrt_clockall_lpspi1(); - } - else if (base == IMXRT_LPSPI2_BASE) - { - imxrt_clockall_lpspi2(); - } - else if (base == IMXRT_LPSPI3_BASE) - { - imxrt_clockall_lpspi3(); - } - else if (base == IMXRT_LPSPI4_BASE) - { - imxrt_clockall_lpspi4(); - } -} - -/**************************************************************************** - * Name: imxrt_lpspi_clock_disable - * - * Description: - * Gate LPSPI clock - * - ****************************************************************************/ - -void imxrt_lpspi_clock_disable(uint32_t base) -{ - if (base == IMXRT_LPSPI1_BASE) - { - imxrt_clockoff_lpspi1(); - } - else if (base == IMXRT_LPSPI2_BASE) - { - imxrt_clockoff_lpspi2(); - } - else if (base == IMXRT_LPSPI3_BASE) - { - imxrt_clockoff_lpspi3(); - } - else if (base == IMXRT_LPSPI4_BASE) - { - imxrt_clockoff_lpspi4(); - } -} - -/**************************************************************************** - * Name: imxrt_lpspi_bus_initialize - * - * Description: - * Initialize the selected SPI bus in its default state - * (Master, 8-bit, mode 0, etc.) - * - * Input Parameters: - * priv - private SPI device structure - * - * Returned Value: - * None - * - ****************************************************************************/ - -static void imxrt_lpspi_bus_initialize(struct imxrt_lpspidev_s *priv) -{ - uint32_t reg = 0; - - /* Enable power and reset the peripheral */ - - imxrt_lpspi_clock_enable(priv->spibase); - - /* Reset to known status */ - - imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0, LPSPI_CR_RST); - imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0, - LPSPI_CR_RTF | LPSPI_CR_RRF); - imxrt_lpspi_putreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0x00); - - /* Set LPSPI to master */ - - imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CFGR1_OFFSET, 0, - LPSPI_CFGR1_MASTER); - - /* Set specific PCS to active high or low - * TODO: Not needed for now - */ - - /* Set Configuration Register 1 related setting. */ - - reg = imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CFGR1_OFFSET); - reg &= ~(LPSPI_CFGR1_OUTCFG | LPSPI_CFGR1_PINCFG_MASK - | LPSPI_CFGR1_NOSTALL); - reg |= LPSPI_CFGR1_OUTCFG_RETAIN | LPSPI_CFGR1_PINCFG_SIN_SOUT; - imxrt_lpspi_putreg32(priv, IMXRT_LPSPI_CFGR1_OFFSET, reg); - - /* Set frequency and delay times */ - - imxrt_lpspi_setfrequency((FAR struct spi_dev_s *)priv, 400000); - - /* Set default watermarks */ - - imxrt_lpspi_putreg32(priv, IMXRT_LPSPI_FCR_OFFSET, - LPSPI_FCR_TXWATER(0) | LPSPI_FCR_RXWATER(0)); - - /* Set Transmit Command Register */ - - imxrt_lpspi_setbits((FAR struct spi_dev_s *)priv, 8); - - imxrt_lpspi_setmode((FAR struct spi_dev_s *)priv, SPIDEV_MODE0); - - /* Initialize the SPI semaphore that enforces mutually exclusive access */ - - nxsem_init(&priv->exclsem, 0, 1); - - /* Enable LPSPI */ - - imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0, LPSPI_CR_MEN); -} - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: imxrt_lpspibus_initialize - * - * Description: - * Initialize the selected SPI bus - * - * Input Parameters: - * Port number (for hardware that has multiple SPI interfaces) - * - * Returned Value: - * Valid SPI device structure reference on success; a NULL on failure - * - ****************************************************************************/ - -FAR struct spi_dev_s *imxrt_lpspibus_initialize(int bus) -{ - FAR struct imxrt_lpspidev_s *priv = NULL; - - irqstate_t flags = enter_critical_section(); - -#ifdef CONFIG_IMXRT_LPSPI1 - if (bus == 1) - { - /* Select SPI1 */ - - priv = &g_lpspi1dev; - - /* Only configure if the bus is not already configured */ - - if ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CR_OFFSET) - & LPSPI_CR_MEN) == 0) - { - /* Configure SPI1 pins: SCK, MISO, and MOSI */ - - imxrt_config_gpio(GPIO_LPSPI1_SCK); - imxrt_config_gpio(GPIO_LPSPI1_MISO); - imxrt_config_gpio(GPIO_LPSPI1_MOSI); -#ifdef GPIO_LPSPI1_CS - imxrt_config_gpio(GPIO_LPSPI1_CS); -#endif -#if defined(GPIO_LPSPI1_DC) && defined(CONFIG_SPI_CMDDATA) - imxrt_config_gpio(GPIO_LPSPI1_DC); -#endif - - /* Set up default configuration: Master, 8-bit, etc. */ - - imxrt_lpspi_bus_initialize(priv); - } - } - else -#endif -#ifdef CONFIG_IMXRT_LPSPI2 - if (bus == 2) - { - /* Select SPI2 */ - - priv = &g_lpspi2dev; - - /* Only configure if the bus is not already configured */ - - if ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CR_OFFSET) - & LPSPI_CR_MEN) == 0) - { - /* Configure SPI2 pins: SCK, MISO, and MOSI */ - - imxrt_config_gpio(GPIO_LPSPI2_SCK); - imxrt_config_gpio(GPIO_LPSPI2_MISO); - imxrt_config_gpio(GPIO_LPSPI2_MOSI); -#ifdef GPIO_LPSPI2_CS - imxrt_config_gpio(GPIO_LPSPI2_CS); -#endif -#if defined(GPIO_LPSPI2_DC) && defined(CONFIG_SPI_CMDDATA) - imxrt_config_gpio(GPIO_LPSPI2_DC); -#endif - - /* Set up default configuration: Master, 8-bit, etc. */ - - imxrt_lpspi_bus_initialize(priv); - } - } - else -#endif -#ifdef CONFIG_IMXRT_LPSPI3 - if (bus == 3) - { - /* Select SPI3 */ - - priv = &g_lpspi3dev; - - /* Only configure if the bus is not already configured */ - - if ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CR_OFFSET) - & LPSPI_CR_MEN) == 0) - { - /* Configure SPI3 pins: SCK, MISO, and MOSI */ - - imxrt_config_gpio(GPIO_LPSPI3_SCK); - imxrt_config_gpio(GPIO_LPSPI3_MISO); - imxrt_config_gpio(GPIO_LPSPI3_MOSI); -#ifdef GPIO_LPSPI3_CS - imxrt_config_gpio(GPIO_LPSPI3_CS); -#endif -#if defined(GPIO_LPSPI3_DC) && defined(CONFIG_SPI_CMDDATA) - imxrt_config_gpio(GPIO_LPSPI3_DC); -#endif - - /* Set up default configuration: Master, 8-bit, etc. */ - - imxrt_lpspi_bus_initialize(priv); - } - } - else -#endif -#ifdef CONFIG_IMXRT_LPSPI4 - if (bus == 4) - { - /* Select SPI4 */ - - priv = &g_lpspi4dev; - - /* Only configure if the bus is not already configured */ - - if ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CR_OFFSET) - & LPSPI_CR_MEN) == 0) - { - /* Configure SPI4 pins: SCK, MISO, and MOSI */ - - imxrt_config_gpio(GPIO_LPSPI4_SCK); - imxrt_config_gpio(GPIO_LPSPI4_MISO); - imxrt_config_gpio(GPIO_LPSPI4_MOSI); -#ifdef GPIO_LPSPI4_CS - imxrt_config_gpio(GPIO_LPSPI4_CS); -#endif -#if defined(GPIO_LPSPI4_DC) && defined(CONFIG_SPI_CMDDATA) - imxrt_config_gpio(GPIO_LPSPI4_DC); -#endif - - /* Set up default configuration: Master, 8-bit, etc. */ - - imxrt_lpspi_bus_initialize(priv); - } - } - else -#endif - { - spierr("ERROR: Unsupported SPI bus: %d\n", bus); - return NULL; - } - - leave_critical_section(flags); - - return (FAR struct spi_dev_s *)priv; -} - -#endif /* CONFIG_IMXRT_LPSPI1 */ diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_usbdev.c b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_usbdev.c deleted file mode 100644 index 3f0d6ff03..000000000 --- a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_usbdev.c +++ /dev/null @@ -1,3061 +0,0 @@ -/**************************************************************************** - * arch/arm/src/imxrt/imxrt_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 "chip.h" -#include "arm_arch.h" -#include "arm_internal.h" - -#include "hardware/imxrt_usbotg.h" -#include "hardware/imxrt_usbphy.h" -#include "hardware/rt106x/imxrt106x_ccm.h" -#include "imxrt_periphclks.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_IMXRT_USBDEV_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 IMXRT_TRACEERR_ALLOCFAIL 0x0001 -#define IMXRT_TRACEERR_BADCLEARFEATURE 0x0002 -#define IMXRT_TRACEERR_BADDEVGETSTATUS 0x0003 -#define IMXRT_TRACEERR_BADEPNO 0x0004 -#define IMXRT_TRACEERR_BADEPGETSTATUS 0x0005 -#define IMXRT_TRACEERR_BADEPTYPE 0x0006 -#define IMXRT_TRACEERR_BADGETCONFIG 0x0007 -#define IMXRT_TRACEERR_BADGETSETDESC 0x0008 -#define IMXRT_TRACEERR_BADGETSTATUS 0x0009 -#define IMXRT_TRACEERR_BADSETADDRESS 0x000a -#define IMXRT_TRACEERR_BADSETCONFIG 0x000b -#define IMXRT_TRACEERR_BADSETFEATURE 0x000c -#define IMXRT_TRACEERR_BINDFAILED 0x000d -#define IMXRT_TRACEERR_DISPATCHSTALL 0x000e -#define IMXRT_TRACEERR_DRIVER 0x000f -#define IMXRT_TRACEERR_DRIVERREGISTERED 0x0010 -#define IMXRT_TRACEERR_EP0SETUPSTALLED 0x0011 -#define IMXRT_TRACEERR_EPINNULLPACKET 0x0012 -#define IMXRT_TRACEERR_EPOUTNULLPACKET 0x0013 -#define IMXRT_TRACEERR_INVALIDCTRLREQ 0x0014 -#define IMXRT_TRACEERR_INVALIDPARMS 0x0015 -#define IMXRT_TRACEERR_IRQREGISTRATION 0x0016 -#define IMXRT_TRACEERR_NOEP 0x0017 -#define IMXRT_TRACEERR_NOTCONFIGURED 0x0018 -#define IMXRT_TRACEERR_REQABORTED 0x0019 - -/* Trace interrupt codes */ - -#define IMXRT_TRACEINTID_USB 0x0001 -#define IMXRT_TRACEINTID_CLEARFEATURE 0x0002 -#define IMXRT_TRACEINTID_DEVGETSTATUS 0x0003 -#define IMXRT_TRACEINTID_DEVRESET 0x0004 -#define IMXRT_TRACEINTID_DISPATCH 0x0005 -#define IMXRT_TRACEINTID_EP0COMPLETE 0x0006 -#define IMXRT_TRACEINTID_EP0NAK 0x0007 -#define IMXRT_TRACEINTID_EP0SETUP 0x0008 -#define IMXRT_TRACEINTID_EPGETSTATUS 0x0009 -#define IMXRT_TRACEINTID_EPIN 0x000a -#define IMXRT_TRACEINTID_EPINQEMPTY 0x000b -#define IMXRT_TRACEINTID_EP0INSETADDRESS 0x000c -#define IMXRT_TRACEINTID_EPOUT 0x000d -#define IMXRT_TRACEINTID_EPOUTQEMPTY 0x000e -#define IMXRT_TRACEINTID_EP0SETUPSETADDRESS 0x000f -#define IMXRT_TRACEINTID_FRAME 0x0010 -#define IMXRT_TRACEINTID_GETCONFIG 0x0011 -#define IMXRT_TRACEINTID_GETSETDESC 0x0012 -#define IMXRT_TRACEINTID_GETSETIF 0x0013 -#define IMXRT_TRACEINTID_GETSTATUS 0x0014 -#define IMXRT_TRACEINTID_IFGETSTATUS 0x0015 -#define IMXRT_TRACEINTID_SETCONFIG 0x0016 -#define IMXRT_TRACEINTID_SETFEATURE 0x0017 -#define IMXRT_TRACEINTID_SUSPENDED 0x0018 -#define IMXRT_TRACEINTID_RESUMED 0x0019 -#define IMXRT_TRACEINTID_SYNCHFRAME 0x001a - -#ifdef CONFIG_USBDEV_TRACE_STRINGS -const struct trace_msg_t g_usb_trace_strings_deverror[] = -{ - TRACE_STR(IMXRT_TRACEERR_ALLOCFAIL), - TRACE_STR(IMXRT_TRACEERR_BADCLEARFEATURE), - TRACE_STR(IMXRT_TRACEERR_BADDEVGETSTATUS), - TRACE_STR(IMXRT_TRACEERR_BADEPNO), - TRACE_STR(IMXRT_TRACEERR_BADEPGETSTATUS), - TRACE_STR(IMXRT_TRACEERR_BADEPTYPE), - TRACE_STR(IMXRT_TRACEERR_BADGETCONFIG), - TRACE_STR(IMXRT_TRACEERR_BADGETSETDESC), - TRACE_STR(IMXRT_TRACEERR_BADGETSTATUS), - TRACE_STR(IMXRT_TRACEERR_BADSETADDRESS), - TRACE_STR(IMXRT_TRACEERR_BADSETCONFIG), - TRACE_STR(IMXRT_TRACEERR_BADSETFEATURE), - TRACE_STR(IMXRT_TRACEERR_BINDFAILED), - TRACE_STR(IMXRT_TRACEERR_DISPATCHSTALL), - TRACE_STR(IMXRT_TRACEERR_DRIVER), - TRACE_STR(IMXRT_TRACEERR_DRIVERREGISTERED), - TRACE_STR(IMXRT_TRACEERR_EP0SETUPSTALLED), - TRACE_STR(IMXRT_TRACEERR_EPINNULLPACKET), - TRACE_STR(IMXRT_TRACEERR_EPOUTNULLPACKET), - TRACE_STR(IMXRT_TRACEERR_INVALIDCTRLREQ), - TRACE_STR(IMXRT_TRACEERR_INVALIDPARMS), - TRACE_STR(IMXRT_TRACEERR_IRQREGISTRATION), - TRACE_STR(IMXRT_TRACEERR_NOEP), - TRACE_STR(IMXRT_TRACEERR_NOTCONFIGURED), - TRACE_STR(IMXRT_TRACEERR_REQABORTED), - TRACE_STR_END -}; - -const struct trace_msg_t g_usb_trace_strings_intdecode[] = -{ - TRACE_STR(IMXRT_TRACEINTID_USB), - TRACE_STR(IMXRT_TRACEINTID_CLEARFEATURE), - TRACE_STR(IMXRT_TRACEINTID_DEVGETSTATUS), - TRACE_STR(IMXRT_TRACEINTID_DEVRESET), - TRACE_STR(IMXRT_TRACEINTID_DISPATCH), - TRACE_STR(IMXRT_TRACEINTID_EP0COMPLETE), - TRACE_STR(IMXRT_TRACEINTID_EP0NAK), - TRACE_STR(IMXRT_TRACEINTID_EP0SETUP), - TRACE_STR(IMXRT_TRACEINTID_EPGETSTATUS), - TRACE_STR(IMXRT_TRACEINTID_EPIN), - TRACE_STR(IMXRT_TRACEINTID_EPINQEMPTY), - TRACE_STR(IMXRT_TRACEINTID_EP0INSETADDRESS), - TRACE_STR(IMXRT_TRACEINTID_EPOUT), - TRACE_STR(IMXRT_TRACEINTID_EPOUTQEMPTY), - TRACE_STR(IMXRT_TRACEINTID_EP0SETUPSETADDRESS), - TRACE_STR(IMXRT_TRACEINTID_FRAME), - TRACE_STR(IMXRT_TRACEINTID_GETCONFIG), - TRACE_STR(IMXRT_TRACEINTID_GETSETDESC), - TRACE_STR(IMXRT_TRACEINTID_GETSETIF), - TRACE_STR(IMXRT_TRACEINTID_GETSTATUS), - TRACE_STR(IMXRT_TRACEINTID_IFGETSTATUS), - TRACE_STR(IMXRT_TRACEINTID_SETCONFIG), - TRACE_STR(IMXRT_TRACEINTID_SETFEATURE), - TRACE_STR(IMXRT_TRACEINTID_SUSPENDED), - TRACE_STR(IMXRT_TRACEINTID_RESUMED), - TRACE_STR(IMXRT_TRACEINTID_SYNCHFRAME), - TRACE_STR_END -}; -#endif - -/* Hardware interface *******************************************************/ - -/* This represents a Endpoint Transfer Descriptor - note these must be 32 - * byte aligned. - */ - -struct imxrt_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 - not these must be aligned to a 2048 byte - * boundary - */ - -struct imxrt_dqh_s -{ - uint32_t capability; /* Endpoint capability */ - uint32_t currdesc; /* Current dTD pointer */ - struct imxrt_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 IMXRT_NLOGENDPOINTS (8) /* ep0-7 */ -#define IMXRT_NPHYSENDPOINTS (16) /* x2 for IN and OUT */ - -/* Odd physical endpoint numbers are IN; even are OUT */ - -#define IMXRT_EPPHYIN(epphy) (((epphy) & 1) != 0) -#define IMXRT_EPPHYOUT(epphy) (((epphy) & 1) == 0) - -#define IMXRT_EPPHYIN2LOG(epphy) (((uint8_t)(epphy) >> 1) |USB_DIR_IN) -#define IMXRT_EPPHYOUT2LOG(epphy) (((uint8_t)(epphy) >> 1) | USB_DIR_OUT) - -/* Endpoint 0 is special... */ - -#define IMXRT_EP0_OUT (0) -#define IMXRT_EP0_IN (1) - -/* Each endpoint has somewhat different characteristics */ - -#define IMXRT_EPALLSET (0xffff) /* All endpoints */ -#define IMXRT_EPOUTSET (0x5555) /* Even phy endpoint numbers are OUT EPs */ -#define IMXRT_EPINSET (0xaaaa) /* Odd endpoint numbers are IN EPs */ -#define IMXRT_EPCTRLSET (0x0003) /* EP0 IN/OUT are control endpoints */ -#define IMXRT_EPINTRSET (0xfffc) /* Interrupt endpoints */ -#define IMXRT_EPBULKSET (0xfffc) /* Bulk endpoints */ -#define IMXRT_EPISOCSET (0xfffc) /* Isochronous endpoints */ - -/* Maximum packet sizes for endpoints */ - -#define IMXRT_EP0MAXPACKET (64) /* EP0 max packet size (1-64) */ -#define IMXRT_BULKMAXPACKET (512) /* Bulk endpoint max packet (8/16/32/64/512) */ -#define IMXRT_INTRMAXPACKET (1024) /* Interrupt endpoint max packet (1 to 1024) */ -#define IMXRT_ISOCMAXPACKET (512) /* Acutally 1..1023 */ - -/* Endpoint bit position in SETUPSTAT, PRIME, FLUSH, STAT, COMPLETE - * registers - */ - -#define IMXRT_ENDPTSHIFT(epphy) (IMXRT_EPPHYIN(epphy) ? (16 + ((epphy) >> 1)) : ((epphy) >> 1)) -#define IMXRT_ENDPTMASK(epphy) (1 << IMXRT_ENDPTSHIFT(epphy)) -#define IMXRT_ENDPTMASK_ALL 0x00ff00ff - -/* Request queue operations *************************************************/ - -#define imxrt_rqempty(ep) ((ep)->head == NULL) -#define imxrt_rqpeek(ep) ((ep)->head) - -/**************************************************************************** - * Private Types - ****************************************************************************/ - -/* A container for a request so that the request may be retained in a list */ - -struct imxrt_req_s -{ - struct usbdev_req_s req; /* Standard USB request */ - struct imxrt_req_s *flink; /* Supports a singly linked list */ -}; - -/* This is the internal representation of an endpoint */ - -struct imxrt_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 imxrt_ep_s. - */ - - struct usbdev_ep_s ep; /* Standard endpoint structure */ - - /* IMXRTXX-specific fields */ - - struct imxrt_usbdev_s *dev; /* Reference to private driver data */ - struct imxrt_req_s *head; /* Request list for this endpoint */ - struct imxrt_req_s *tail; - uint8_t epphy; /* Physical EP address */ - uint8_t stalled:1; /* 1: Endpoint is stalled */ -}; - -/* This structure retains the state of the USB device controller */ - -struct imxrt_usbdev_s -{ - /* Common device fields. This must be the first thing defined in the - * structure so that it is possible to simply cast from struct usbdev_s - * to struct imxrt_usbdev_s. - */ - - struct usbdev_s usbdev; - - /* The bound device class driver */ - - struct usbdevclass_driver_s *driver; - - /* IMXRTXX-specific fields */ - - uint8_t ep0state; /* State of certain EP0 operations */ - uint8_t ep0buf[64]; /* buffer for EP0 short transfers */ - 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_IMXRT_USBDEV_FRAME_INTERRUPT - uint32_t sof; /* Last start-of-frame */ -#endif - - uint16_t ep0buf_len; - struct usb_ctrlreq_s ep0ctrl; - - /* The endpoint list */ - - struct imxrt_ep_s eplist[IMXRT_NPHYSENDPOINTS]; -}; - -#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_IMXRT_USBDEV_REGDEBUG -static uint32_t imxrt_getreg(uint32_t addr); -static void imxrt_putreg(uint32_t val, uint32_t addr); -#else -# define imxrt_getreg(addr) getreg32(addr) -# define imxrt_putreg(val,addr) putreg32(val,addr) -#endif - -static inline void imxrt_clrbits(uint32_t mask, uint32_t addr); -static inline void imxrt_setbits(uint32_t mask, uint32_t addr); -static inline void imxrt_chgbits(uint32_t mask, uint32_t val, uint32_t addr); - -/* Request queue operations *************************************************/ - -static FAR struct imxrt_req_s *imxrt_rqdequeue( - FAR struct imxrt_ep_s *privep); -static bool imxrt_rqenqueue(FAR struct imxrt_ep_s *privep, - FAR struct imxrt_req_s *req); - -/* Low level data transfers and request operations **************************/ - -static inline void imxrt_writedtd(struct imxrt_dtd_s *dtd, - const uint8_t *data, - uint32_t nbytes); -static inline void imxrt_queuedtd(uint8_t epphy, struct imxrt_dtd_s *dtd); -static inline void imxrt_ep0xfer(uint8_t epphy, uint8_t *data, - uint32_t nbytes); -static void imxrt_readsetup(uint8_t epphy, - struct usb_ctrlreq_s *ctrl); - -static inline void imxrt_set_address(struct imxrt_usbdev_s *priv, - uint16_t address); - -static void imxrt_flushep(struct imxrt_ep_s *privep); - -static int imxrt_progressep(struct imxrt_ep_s *privep); -static void imxrt_reqcomplete(struct imxrt_ep_s *privep, - struct imxrt_req_s *privreq, int16_t result); - -static void imxrt_cancelrequests(struct imxrt_ep_s *privep, - int16_t status); - -/* Interrupt handling *******************************************************/ - -static struct imxrt_ep_s *imxrt_epfindbyaddr(struct imxrt_usbdev_s *priv, - uint16_t eplog); -static void imxrt_dispatchrequest(struct imxrt_usbdev_s *priv, - const struct usb_ctrlreq_s *ctrl); -static void imxrt_ep0configure(struct imxrt_usbdev_s *priv); -static void imxrt_usbreset(struct imxrt_usbdev_s *priv); - -static inline void imxrt_ep0state(struct imxrt_usbdev_s *priv, - uint16_t state); -static void imxrt_ep0setup(struct imxrt_usbdev_s *priv); - -static void imxrt_ep0complete(struct imxrt_usbdev_s *priv, - uint8_t epphy); -static void imxrt_ep0nak(struct imxrt_usbdev_s *priv, uint8_t epphy); -static bool imxrt_epcomplete(struct imxrt_usbdev_s *priv, - uint8_t epphy); - -static int imxrt_usbinterrupt(int irq, FAR void *context, - FAR void *arg); - -/* Endpoint operations ******************************************************/ - -/* USB device controller operations *****************************************/ - -static int imxrt_epconfigure(FAR struct usbdev_ep_s *ep, - const struct usb_epdesc_s *desc, bool last); -static int imxrt_epdisable(FAR struct usbdev_ep_s *ep); -static FAR struct usbdev_req_s *imxrt_epallocreq(FAR struct usbdev_ep_s *ep); -static void imxrt_epfreereq(FAR struct usbdev_ep_s *ep, - FAR struct usbdev_req_s *); -#ifdef CONFIG_USBDEV_DMA -static void *imxrt_epallocbuffer(FAR struct usbdev_ep_s *ep, - uint16_t bytes); -static void imxrt_epfreebuffer(FAR struct usbdev_ep_s *ep, - FAR void *buf); -#endif -static int imxrt_epsubmit(FAR struct usbdev_ep_s *ep, - struct usbdev_req_s *req); -static int imxrt_epcancel(FAR struct usbdev_ep_s *ep, - struct usbdev_req_s *req); -static int imxrt_epstall(FAR struct usbdev_ep_s *ep, bool resume); - -static FAR struct usbdev_ep_s *imxrt_allocep(FAR struct usbdev_s *dev, - uint8_t epno, bool in, uint8_t eptype); -static void imxrt_freeep(FAR struct usbdev_s *dev, - FAR struct usbdev_ep_s *ep); -static int imxrt_getframe(struct usbdev_s *dev); -static int imxrt_wakeup(struct usbdev_s *dev); -static int imxrt_selfpowered(struct usbdev_s *dev, bool selfpowered); -static int imxrt_pullup(struct usbdev_s *dev, bool enable); - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -/* Since there is only a single USB interface, all status information can be - * be simply retained in a single global instance. - */ - -static struct imxrt_usbdev_s g_usbdev; - -static struct imxrt_dqh_s g_qh[IMXRT_NPHYSENDPOINTS] - aligned_data(2048); - -static struct imxrt_dtd_s g_td[IMXRT_NPHYSENDPOINTS] - aligned_data(32); - -static const struct usbdev_epops_s g_epops = -{ - .configure = imxrt_epconfigure, - .disable = imxrt_epdisable, - .allocreq = imxrt_epallocreq, - .freereq = imxrt_epfreereq, -#ifdef CONFIG_USBDEV_DMA - .allocbuffer = imxrt_epallocbuffer, - .freebuffer = imxrt_epfreebuffer, -#endif - .submit = imxrt_epsubmit, - .cancel = imxrt_epcancel, - .stall = imxrt_epstall, -}; - -static const struct usbdev_ops_s g_devops = -{ - .allocep = imxrt_allocep, - .freeep = imxrt_freeep, - .getframe = imxrt_getframe, - .wakeup = imxrt_wakeup, - .selfpowered = imxrt_selfpowered, - .pullup = imxrt_pullup, -}; - -/**************************************************************************** - * Public Data - ****************************************************************************/ - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: imxrt_getreg - * - * Description: - * Get the contents of an IMXRT3x register - * - ****************************************************************************/ - -#ifdef CONFIG_IMXRT_USBDEV_REGDEBUG -static uint32_t imxrt_getreg(uint32_t addr) -{ - static uint32_t prevaddr = 0; - static uint32_t preval = 0; - static uint32_t count = 0; - - /* Read the value from the register */ - - uint32_t val = getreg32(addr); - - /* Is this the same value that we read from the same register last time? - * Are we polling the register? If so, suppress some of the output. - */ - - if (addr == prevaddr && val == preval) - { - if (count == 0xffffffff || ++count > 3) - { - if (count == 4) - { - uinfo("...\n"); - } - - return val; - } - } - - /* No this is a new address or value */ - - else - { - /* Did we print "..." for the previous value? */ - - if (count > 3) - { - /* Yes.. then show how many times the value repeated */ - - uinfo("[repeats %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", addr, val); - return val; -} -#endif - -/**************************************************************************** - * Name: imxrt_putreg - * - * Description: - * Set the contents of an IMXRT3x register to a value - * - ****************************************************************************/ - -#ifdef CONFIG_IMXRT_USBDEV_REGDEBUG -static void imxrt_putreg(uint32_t val, uint32_t addr) -{ - /* Show the register value being written */ - - uinfo("%08x<-%08x\n", addr, val); - - /* Write the value */ - - putreg32(val, addr); -} -#endif - -/**************************************************************************** - * Name: imxrt_clrbits - * - * Description: - * Clear bits in a register - * - ****************************************************************************/ - -static inline void imxrt_clrbits(uint32_t mask, uint32_t addr) -{ - uint32_t reg = imxrt_getreg(addr); - reg &= ~mask; - imxrt_putreg(reg, addr); -} - -/**************************************************************************** - * Name: imxrt_setbits - * - * Description: - * Set bits in a register - * - ****************************************************************************/ - -static inline void imxrt_setbits(uint32_t mask, uint32_t addr) -{ - uint32_t reg = imxrt_getreg(addr); - reg |= mask; - imxrt_putreg(reg, addr); -} - -/**************************************************************************** - * Name: imxrt_chgbits - * - * Description: - * Change bits in a register - * - ****************************************************************************/ - -static inline void imxrt_chgbits(uint32_t mask, uint32_t val, uint32_t addr) -{ - uint32_t reg = imxrt_getreg(addr); - reg &= ~mask; - reg |= val; - imxrt_putreg(reg, addr); -} - -/**************************************************************************** - * Name: imxrt_rqdequeue - * - * Description: - * Remove a request from an endpoint request queue - * - ****************************************************************************/ - -static FAR struct imxrt_req_s *imxrt_rqdequeue(FAR struct imxrt_ep_s *privep) -{ - FAR struct imxrt_req_s *ret = privep->head; - - if (ret) - { - privep->head = ret->flink; - if (!privep->head) - { - privep->tail = NULL; - } - - ret->flink = NULL; - } - - return ret; -} - -/**************************************************************************** - * Name: imxrt_rqenqueue - * - * Description: - * Add a request from an endpoint request queue - * - ****************************************************************************/ - -static bool imxrt_rqenqueue(FAR struct imxrt_ep_s *privep, - FAR struct imxrt_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: imxrt_writedtd - * - * Description: - * Initialise a DTD to transfer the data - * - ****************************************************************************/ - -static inline void imxrt_writedtd(struct imxrt_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) data); - dtd->buffer1 = (((uint32_t) data) + 0x1000) & 0xfffff000; - dtd->buffer2 = (((uint32_t) data) + 0x2000) & 0xfffff000; - dtd->buffer3 = (((uint32_t) data) + 0x3000) & 0xfffff000; - dtd->buffer4 = (((uint32_t) data) + 0x4000) & 0xfffff000; - dtd->xfer_len = nbytes; - - up_flush_dcache((uintptr_t)dtd, - (uintptr_t)dtd + sizeof(struct imxrt_dtd_s)); - up_flush_dcache((uintptr_t)data, - (uintptr_t)data + nbytes); -} - -/**************************************************************************** - * Name: imxrt_queuedtd - * - * Description: - * Add the DTD to the device list - * - * Assumptions: - * DTD is already flushed to RAM. - * - ****************************************************************************/ - -static void imxrt_queuedtd(uint8_t epphy, struct imxrt_dtd_s *dtd) -{ - struct imxrt_dqh_s *dqh = &g_qh[epphy]; - - /* Queue the DTD onto the Endpoint - * NOTE - this only works when no DTD is currently queued - */ - - dqh->overlay.nextdesc = (uint32_t) dtd; - dqh->overlay.config &= ~(DTD_CONFIG_ACTIVE | DTD_CONFIG_HALTED); - - up_flush_dcache((uintptr_t)dqh, - (uintptr_t)dqh + sizeof(struct imxrt_dqh_s)); - - uint32_t bit = IMXRT_ENDPTMASK(epphy); - - imxrt_setbits (bit, IMXRT_USBDEV_ENDPTPRIME); - - while (imxrt_getreg (IMXRT_USBDEV_ENDPTPRIME) & bit) - ; -} - -/**************************************************************************** - * Name: imxrt_ep0xfer - * - * Description: - * Schedule a short transfer for Endpoint 0 (IN or OUT) - * - ****************************************************************************/ - -static inline void imxrt_ep0xfer(uint8_t epphy, uint8_t *buf, - uint32_t nbytes) -{ - struct imxrt_dtd_s *dtd = &g_td[epphy]; - - imxrt_writedtd(dtd, buf, nbytes); - - imxrt_queuedtd(epphy, dtd); -} - -/**************************************************************************** - * Name: imxrt_readsetup - * - * Description: - * Read a Setup packet from the DTD. - * - ****************************************************************************/ - -static void imxrt_readsetup(uint8_t epphy, struct usb_ctrlreq_s *ctrl) -{ - struct imxrt_dqh_s *dqh = &g_qh[epphy]; - int i; - - do - { - /* Set the trip wire */ - - imxrt_setbits(USBDEV_USBCMD_SUTW, IMXRT_USBDEV_USBCMD); - - up_invalidate_dcache((uintptr_t)dqh, - (uintptr_t)dqh + sizeof(struct imxrt_dqh_s)); - - /* Copy the request... */ - - for (i = 0; i < 8; i++) - { - ((uint8_t *) ctrl)[i] = ((uint8_t *) dqh->setup)[i]; - } - } - while (!(imxrt_getreg(IMXRT_USBDEV_USBCMD) & USBDEV_USBCMD_SUTW)); - - /* Clear the trip wire */ - - imxrt_clrbits(USBDEV_USBCMD_SUTW, IMXRT_USBDEV_USBCMD); - - /* Clear the Setup Interrupt */ - - imxrt_putreg (IMXRT_ENDPTMASK(IMXRT_EP0_OUT), IMXRT_USBDEV_ENDPTSETUPSTAT); -} - -/**************************************************************************** - * Name: imxrt_set_address - * - * Description: - * Set the devices USB address - * - ****************************************************************************/ - -static inline void imxrt_set_address(struct imxrt_usbdev_s *priv, - uint16_t address) -{ - priv->paddr = address; - priv->paddrset = address != 0; - - imxrt_chgbits(USBDEV_DEVICEADDR_MASK, - priv->paddr << USBDEV_DEVICEADDR_SHIFT, - IMXRT_USBDEV_DEVICEADDR); -} - -/**************************************************************************** - * Name: imxrt_flushep - * - * Description: - * Flush any primed descriptors from this ep - * - ****************************************************************************/ - -static void imxrt_flushep(struct imxrt_ep_s *privep) -{ - uint32_t mask = IMXRT_ENDPTMASK(privep->epphy); - do - { - imxrt_putreg (mask, IMXRT_USBDEV_ENDPTFLUSH); - while ((imxrt_getreg(IMXRT_USBDEV_ENDPTFLUSH) & mask) != 0) - ; - } - while ((imxrt_getreg(IMXRT_USBDEV_ENDPTSTATUS) & mask) != 0); -} - -/**************************************************************************** - * Name: imxrt_progressep - * - * Description: - * Progress the Endpoint by priming the first request into the queue head - * - ****************************************************************************/ - -static int imxrt_progressep(struct imxrt_ep_s *privep) -{ - struct imxrt_dtd_s *dtd = &g_td[privep->epphy]; - struct imxrt_req_s *privreq; - - /* Check the request from the head of the endpoint request queue */ - - privreq = imxrt_rqpeek(privep); - if (!privreq) - { - usbtrace(TRACE_INTDECODE(IMXRT_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 thr response - */ - - if (privep->epphy == IMXRT_EP0_IN && - privep->dev->ep0state == EP0STATE_SETUP_OUT) - { - imxrt_ep0state (privep->dev, EP0STATE_WAIT_NAK_IN); - } - else - { - if (IMXRT_EPPHYIN(privep->epphy)) - { - usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_EPINNULLPACKET), 0); - } - else - { - usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_EPOUTNULLPACKET), 0); - } - } - - imxrt_reqcomplete(privep, imxrt_rqdequeue(privep), OK); - return OK; - } - - if (privep->epphy == IMXRT_EP0_IN) - { - imxrt_ep0state (privep->dev, EP0STATE_DATA_IN); - } - else if (privep->epphy == IMXRT_EP0_OUT) - { - imxrt_ep0state (privep->dev, EP0STATE_DATA_OUT); - } - - int bytesleft = privreq->req.len - privreq->req.xfrd; - - if (IMXRT_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 */ - - imxrt_writedtd (dtd, privreq->req.buf + privreq->req.xfrd, bytesleft); - - /* Then queue onto the DQH */ - - imxrt_queuedtd(privep->epphy, dtd); - - return OK; -} - -/**************************************************************************** - * Name: imxrt_reqcomplete - * - * Description: - * Handle termination of the request at the head of the endpoint request - * queue. - * - ****************************************************************************/ - -static void imxrt_reqcomplete(struct imxrt_ep_s *privep, - struct imxrt_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 == IMXRT_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: imxrt_cancelrequests - * - * Description: - * Cancel all pending requests for an endpoint - * - ****************************************************************************/ - -static void imxrt_cancelrequests(struct imxrt_ep_s *privep, int16_t status) -{ - if (!imxrt_rqempty(privep)) - imxrt_flushep(privep); - - while (!imxrt_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), - (imxrt_rqpeek(privep))->req.xfrd); - imxrt_reqcomplete(privep, imxrt_rqdequeue(privep), status); - } -} - -/**************************************************************************** - * Name: imxrt_epfindbyaddr - * - * Description: - * Find the physical endpoint structure corresponding to a logic endpoint - * address - * - ****************************************************************************/ - -static struct imxrt_ep_s *imxrt_epfindbyaddr(struct imxrt_usbdev_s *priv, - uint16_t eplog) -{ - struct imxrt_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 < IMXRT_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: imxrt_dispatchrequest - * - * Description: - * Provide unhandled setup actions to the class driver. This is logically - * part of the USB interrupt handler. - * - ****************************************************************************/ - -static void imxrt_dispatchrequest(struct imxrt_usbdev_s *priv, - const struct usb_ctrlreq_s *ctrl) -{ - int ret = -EIO; - - usbtrace(TRACE_INTDECODE(IMXRT_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->ep0buf, - priv->ep0buf_len); - } - - if (ret < 0) - { - /* Stall on failure */ - - usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_DISPATCHSTALL), 0); - priv->stalled = true; - } -} - -/**************************************************************************** - * Name: imxrt_ep0configure - * - * Description: - * Reset Usb engine - * - ****************************************************************************/ - -static void imxrt_ep0configure(struct imxrt_usbdev_s *priv) -{ - /* Enable ep0 IN and ep0 OUT */ - - g_qh[IMXRT_EP0_OUT].capability = - (DQH_CAPABILITY_MAX_PACKET(CONFIG_USBDEV_EP0_MAXSIZE) | - DQH_CAPABILITY_IOS | DQH_CAPABILITY_ZLT); - - g_qh[IMXRT_EP0_IN].capability = - (DQH_CAPABILITY_MAX_PACKET(CONFIG_USBDEV_EP0_MAXSIZE) | - DQH_CAPABILITY_IOS | DQH_CAPABILITY_ZLT); - - g_qh[IMXRT_EP0_OUT].currdesc = DTD_NEXTDESC_INVALID; - g_qh[IMXRT_EP0_IN].currdesc = DTD_NEXTDESC_INVALID; - - up_flush_dcache((uintptr_t)g_qh, - (uintptr_t)g_qh + (sizeof(struct imxrt_dqh_s) * 2)); - - /* Enable EP0 */ - - imxrt_setbits (USBDEV_ENDPTCTRL0_RXE | USBDEV_ENDPTCTRL0_TXE, - IMXRT_USBDEV_ENDPTCTRL0); -} - -/**************************************************************************** - * Name: imxrt_usbreset - * - * Description: - * Reset Usb engine - * - ****************************************************************************/ - -static void imxrt_usbreset(struct imxrt_usbdev_s *priv) -{ - int epphy; - - /* Disable all endpoints. Control endpoint 0 is always enabled */ - - imxrt_clrbits (USBDEV_ENDPTCTRL_RXE | USBDEV_ENDPTCTRL_TXE, - IMXRT_USBDEV_ENDPTCTRL1); - imxrt_clrbits (USBDEV_ENDPTCTRL_RXE | USBDEV_ENDPTCTRL_TXE, - IMXRT_USBDEV_ENDPTCTRL2); - imxrt_clrbits (USBDEV_ENDPTCTRL_RXE | USBDEV_ENDPTCTRL_TXE, - IMXRT_USBDEV_ENDPTCTRL3); - imxrt_clrbits (USBDEV_ENDPTCTRL_RXE | USBDEV_ENDPTCTRL_TXE, - IMXRT_USBDEV_ENDPTCTRL4); - imxrt_clrbits (USBDEV_ENDPTCTRL_RXE | USBDEV_ENDPTCTRL_TXE, - IMXRT_USBDEV_ENDPTCTRL5); - - /* Clear all pending interrupts */ - - imxrt_putreg (imxrt_getreg(IMXRT_USBDEV_ENDPTNAK), - IMXRT_USBDEV_ENDPTNAK); - imxrt_putreg (imxrt_getreg(IMXRT_USBDEV_ENDPTSETUPSTAT), - IMXRT_USBDEV_ENDPTSETUPSTAT); - imxrt_putreg (imxrt_getreg(IMXRT_USBDEV_ENDPTCOMPLETE), - IMXRT_USBDEV_ENDPTCOMPLETE); - - /* Wait for all prime operations to have completed and then flush all - * DTDs - */ - - while (imxrt_getreg (IMXRT_USBDEV_ENDPTPRIME) != 0) - ; - imxrt_putreg (IMXRT_ENDPTMASK_ALL, IMXRT_USBDEV_ENDPTFLUSH); - while (imxrt_getreg (IMXRT_USBDEV_ENDPTFLUSH)) - ; - - /* Reset endpoints */ - - for (epphy = 0; epphy < IMXRT_NPHYSENDPOINTS; epphy++) - { - struct imxrt_ep_s *privep = &priv->eplist[epphy]; - - imxrt_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 */ - - imxrt_chgbits(USBDEV_USBCMD_ITC_MASK, USBDEV_USBCMD_ITCIMME, - IMXRT_USBDEV_USBCMD); - - /* Zero out the Endpoint queue heads */ - - memset ((void *) g_qh, 0, sizeof (g_qh)); - memset ((void *) g_td, 0, sizeof (g_td)); - - up_flush_dcache((uintptr_t)g_qh, (uintptr_t)g_qh + sizeof(g_qh)); - up_flush_dcache((uintptr_t)g_td, (uintptr_t)g_td + sizeof(g_td)); - - /* Set USB address to 0 */ - - imxrt_set_address (priv, 0); - - /* Initialise the Enpoint List Address */ - - imxrt_putreg ((uint32_t)g_qh, IMXRT_USBDEV_ENDPOINTLIST); - - /* EndPoint 0 initialization */ - - imxrt_ep0configure(priv); - - /* Enable Device interrupts */ - - imxrt_putreg(USB_FRAME_INT | USB_ERROR_INT | USBDEV_USBINTR_NAKE | - USBDEV_USBINTR_SLE | USBDEV_USBINTR_URE | USBDEV_USBINTR_PCE | - USBDEV_USBINTR_UE, IMXRT_USBDEV_USBINTR); -} - -/**************************************************************************** - * Name: imxrt_setstate - * - * Description: - * Sets the EP0 state and manages the NAK interrupts - * - ****************************************************************************/ - -static inline void imxrt_ep0state(struct imxrt_usbdev_s *priv, - uint16_t state) -{ - priv->ep0state = state; - - switch (state) - { - case EP0STATE_WAIT_NAK_IN: - imxrt_putreg (IMXRT_ENDPTMASK(IMXRT_EP0_IN), IMXRT_USBDEV_ENDPTNAKEN); - break; - - case EP0STATE_WAIT_NAK_OUT: - imxrt_putreg (IMXRT_ENDPTMASK(IMXRT_EP0_OUT), IMXRT_USBDEV_ENDPTNAKEN); - break; - - default: - imxrt_putreg(0, IMXRT_USBDEV_ENDPTNAKEN); - break; - } -} - -/**************************************************************************** - * Name: imxrt_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 imxrt_ep0setup(struct imxrt_usbdev_s *priv) -{ - struct imxrt_ep_s *privep; - struct usb_ctrlreq_s *ctrl; - uint16_t value; - uint16_t index; - uint16_t len; - - ctrl = &priv->ep0ctrl; - - /* Terminate any pending requests - since all DTDs will have been retired - * because of the setup packet. - */ - - imxrt_cancelrequests(&priv->eplist[IMXRT_EP0_OUT], -EPROTO); - imxrt_cancelrequests(&priv->eplist[IMXRT_EP0_IN], -EPROTO); - - /* Assume NOT stalled */ - - priv->eplist[IMXRT_EP0_OUT].stalled = false; - priv->eplist[IMXRT_EP0_IN].stalled = false; - priv->stalled = false; - - /* Read EP0 setup data */ - - imxrt_readsetup(IMXRT_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->ep0buf_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) - { - imxrt_ep0state (priv, EP0STATE_SETUP_IN); - } - else - { - imxrt_ep0state (priv, EP0STATE_SETUP_OUT); - - if (len > 0) - { - imxrt_ep0state(priv, EP0STATE_SHORTREAD); - imxrt_ep0xfer(IMXRT_EP0_OUT, priv->ep0buf, len); - return; - } - } - - /* Dispatch any non-standard requests */ - - if ((ctrl->type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD) - { - imxrt_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(IMXRT_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(IMXRT_TRACEINTID_EPGETSTATUS), 0); - privep = imxrt_epfindbyaddr(priv, index); - if (!privep) - { - usbtrace( - TRACE_DEVERROR(IMXRT_TRACEERR_BADEPGETSTATUS), - 0); - priv->stalled = true; - } - else - { - if (privep->stalled) - { - priv->ep0buf[0] = 1; /* Stalled */ - } - else - { - priv->ep0buf[0] = 0; /* Not stalled */ - } - - priv->ep0buf[1] = 0; - - imxrt_ep0xfer (IMXRT_EP0_IN, priv->ep0buf, 2); - imxrt_ep0state (priv, EP0STATE_SHORTWRITE); - } - } - break; - - case USB_REQ_RECIPIENT_DEVICE: - { - if (index == 0) - { - usbtrace( - TRACE_INTDECODE(IMXRT_TRACEINTID_DEVGETSTATUS), - 0); - - /* Features: Remote Wakeup=YES; selfpowered=? */ - - priv->ep0buf[0] = - (priv->selfpowered << - USB_FEATURE_SELFPOWERED) | - (1 << USB_FEATURE_REMOTEWAKEUP); - priv->ep0buf[1] = 0; - - imxrt_ep0xfer(IMXRT_EP0_IN, priv->ep0buf, 2); - imxrt_ep0state (priv, EP0STATE_SHORTWRITE); - } - else - { - usbtrace( - TRACE_DEVERROR(IMXRT_TRACEERR_BADDEVGETSTATUS), - 0); - priv->stalled = true; - } - } - break; - - case USB_REQ_RECIPIENT_INTERFACE: - { - usbtrace(TRACE_INTDECODE(IMXRT_TRACEINTID_IFGETSTATUS), - 0); - priv->ep0buf[0] = 0; - priv->ep0buf[1] = 0; - - imxrt_ep0xfer(IMXRT_EP0_IN, priv->ep0buf, 2); - imxrt_ep0state (priv, EP0STATE_SHORTWRITE); - } - break; - - default: - { - usbtrace(TRACE_DEVERROR(IMXRT_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(IMXRT_TRACEINTID_CLEARFEATURE), 0); - if ((ctrl->type & USB_REQ_RECIPIENT_MASK) != - USB_REQ_RECIPIENT_ENDPOINT) - { - imxrt_dispatchrequest(priv, ctrl); - } - else if (priv->paddrset != 0 && - value == USB_FEATURE_ENDPOINTHALT && - len == 0 && (privep = imxrt_epfindbyaddr(priv, index)) != NULL) - { - imxrt_epstall(&privep->ep, true); - imxrt_ep0state (priv, EP0STATE_WAIT_NAK_IN); - } - else - { - usbtrace(TRACE_DEVERROR(IMXRT_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(IMXRT_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) - { - imxrt_dispatchrequest(priv, ctrl); - } - else if (priv->paddrset != 0 && - value == USB_FEATURE_ENDPOINTHALT && - len == 0 && (privep = imxrt_epfindbyaddr(priv, index)) != NULL) - { - imxrt_epstall(&privep->ep, false); - imxrt_ep0state (priv, EP0STATE_WAIT_NAK_IN); - } - else - { - usbtrace(TRACE_DEVERROR(IMXRT_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(IMXRT_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; - imxrt_ep0state (priv, EP0STATE_WAIT_NAK_IN); - } - else - { - usbtrace(TRACE_DEVERROR(IMXRT_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(IMXRT_TRACEINTID_GETSETDESC), 0); - if ((ctrl->type & USB_REQ_RECIPIENT_MASK) == - USB_REQ_RECIPIENT_DEVICE) - { - imxrt_dispatchrequest(priv, ctrl); - } - else - { - usbtrace(TRACE_DEVERROR(IMXRT_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(IMXRT_TRACEINTID_GETCONFIG), 0); - if (priv->paddrset && - ((ctrl->type & USB_REQ_RECIPIENT_MASK) == - USB_REQ_RECIPIENT_DEVICE) && - value == 0 && index == 0 && len == 1) - { - imxrt_dispatchrequest(priv, ctrl); - } - else - { - usbtrace(TRACE_DEVERROR(IMXRT_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(IMXRT_TRACEINTID_SETCONFIG), 0); - if (((ctrl->type & USB_REQ_RECIPIENT_MASK) == - USB_REQ_RECIPIENT_DEVICE) && index == 0 && len == 0) - { - imxrt_dispatchrequest(priv, ctrl); - } - else - { - usbtrace(TRACE_DEVERROR(IMXRT_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(IMXRT_TRACEINTID_GETSETIF), 0); - imxrt_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(IMXRT_TRACEINTID_SYNCHFRAME), 0); - } - break; - - default: - { - usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_INVALIDCTRLREQ), 0); - priv->stalled = true; - } - break; - } - } - - if (priv->stalled) - { - usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_EP0SETUPSTALLED), - priv->ep0state); - imxrt_epstall(&priv->eplist[IMXRT_EP0_IN].ep, false); - imxrt_epstall(&priv->eplist[IMXRT_EP0_OUT].ep, false); - } -} - -/**************************************************************************** - * Name: imxrt_ep0complete - * - * Description: - * Transfer complete handler for Endpoint 0 - * - ****************************************************************************/ - -static void imxrt_ep0complete(struct imxrt_usbdev_s *priv, uint8_t epphy) -{ - struct imxrt_ep_s *privep = &priv->eplist[epphy]; - - usbtrace(TRACE_INTDECODE(IMXRT_TRACEINTID_EP0COMPLETE), - (uint16_t)priv->ep0state); - - switch (priv->ep0state) - { - case EP0STATE_DATA_IN: - if (imxrt_rqempty(privep)) - { - return; - } - - if (imxrt_epcomplete (priv, epphy)) - { - imxrt_ep0state (priv, EP0STATE_WAIT_NAK_OUT); - } - break; - - case EP0STATE_DATA_OUT: - if (imxrt_rqempty(privep)) - { - return; - } - - if (imxrt_epcomplete (priv, epphy)) - { - imxrt_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->ep0buf, - (uintptr_t)priv->ep0buf + sizeof(priv->ep0buf)); - - imxrt_dispatchrequest(priv, &priv->ep0ctrl); - imxrt_ep0state (priv, EP0STATE_WAIT_NAK_IN); - break; - - case EP0STATE_SHORTWRITE: - imxrt_ep0state (priv, EP0STATE_WAIT_NAK_OUT); - break; - - case EP0STATE_WAIT_STATUS_IN: - imxrt_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(IMXRT_TRACEINTID_EP0INSETADDRESS), - (uint16_t)priv->paddr); - imxrt_set_address (priv, priv->paddr); - } - - break; - - case EP0STATE_WAIT_STATUS_OUT: - imxrt_ep0state (priv, EP0STATE_IDLE); - break; - - default: -#ifdef CONFIG_DEBUG_FEATURES - DEBUGASSERT(priv->ep0state != EP0STATE_DATA_IN && - priv->ep0state != EP0STATE_DATA_OUT && - priv->ep0state != EP0STATE_SHORTWRITE && - priv->ep0state != EP0STATE_WAIT_STATUS_IN && - priv->ep0state != EP0STATE_WAIT_STATUS_OUT); -#endif - priv->stalled = true; - break; - } - - if (priv->stalled) - { - usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_EP0SETUPSTALLED), - priv->ep0state); - imxrt_epstall(&priv->eplist[IMXRT_EP0_IN].ep, false); - imxrt_epstall(&priv->eplist[IMXRT_EP0_OUT].ep, false); - } -} - -/**************************************************************************** - * Name: imxrt_ep0nak - * - * Description: - * Handle a NAK interrupt on EP0 - * - ****************************************************************************/ - -static void imxrt_ep0nak(struct imxrt_usbdev_s *priv, uint8_t epphy) -{ - usbtrace(TRACE_INTDECODE(IMXRT_TRACEINTID_EP0NAK), - (uint16_t)priv->ep0state); - - switch (priv->ep0state) - { - case EP0STATE_WAIT_NAK_IN: - imxrt_ep0xfer (IMXRT_EP0_IN, NULL, 0); - imxrt_ep0state (priv, EP0STATE_WAIT_STATUS_IN); - break; - - case EP0STATE_WAIT_NAK_OUT: - imxrt_ep0xfer (IMXRT_EP0_OUT, NULL, 0); - imxrt_ep0state (priv, EP0STATE_WAIT_STATUS_OUT); - break; - - default: -#ifdef CONFIG_DEBUG_FEATURES - DEBUGASSERT(priv->ep0state != EP0STATE_WAIT_NAK_IN && - priv->ep0state != EP0STATE_WAIT_NAK_OUT); -#endif - priv->stalled = true; - break; - } - - if (priv->stalled) - { - usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_EP0SETUPSTALLED), - priv->ep0state); - imxrt_epstall(&priv->eplist[IMXRT_EP0_IN].ep, false); - imxrt_epstall(&priv->eplist[IMXRT_EP0_OUT].ep, false); - } -} - -/**************************************************************************** - * Name: imxrt_epcomplete - * - * Description: - * Transfer complete handler for Endpoints other than 0 - * returns whether the request at the head has completed - * - ****************************************************************************/ - -bool imxrt_epcomplete(struct imxrt_usbdev_s *priv, uint8_t epphy) -{ - struct imxrt_ep_s *privep = &priv->eplist[epphy]; - struct imxrt_req_s *privreq = privep->head; - struct imxrt_dtd_s *dtd = &g_td[epphy]; - - if (privreq == NULL) /* This shouldn't really happen */ - { - if (IMXRT_EPPHYOUT(privep->epphy)) - { - usbtrace(TRACE_INTDECODE(IMXRT_TRACEINTID_EPINQEMPTY), 0); - } - else - { - usbtrace(TRACE_INTDECODE(IMXRT_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 imxrt_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 (IMXRT_EPPHYOUT(privep->epphy)) - { - /* read(OUT) completes when request filled, or a short transfer is - * received - */ - - usbtrace(TRACE_INTDECODE(IMXRT_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(IMXRT_TRACEINTID_EPOUT), complete); - } - - /* If the transfer is complete, then dequeue and progress any further - * queued requests - */ - - if (complete) - { - privreq = imxrt_rqdequeue (privep); - } - - if (!imxrt_rqempty(privep)) - { - imxrt_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); - imxrt_reqcomplete(privep, privreq, OK); - } - - return complete; -} - -/**************************************************************************** - * Name: imxrt_usbinterrupt - * - * Description: - * USB interrupt handler - * - ****************************************************************************/ - -static int imxrt_usbinterrupt(int irq, FAR void *context, FAR void *arg) -{ - struct imxrt_usbdev_s *priv = &g_usbdev; - uint32_t disr; - uint32_t portsc1; - uint32_t n; - - usbtrace(TRACE_INTENTRY(IMXRT_TRACEINTID_USB), 0); - - /* Read the interrupts and then clear them */ - - disr = imxrt_getreg(IMXRT_USBDEV_USBSTS); - imxrt_putreg(disr, IMXRT_USBDEV_USBSTS); - - if (disr & USBDEV_USBSTS_URI) - { - usbtrace(TRACE_INTDECODE(IMXRT_TRACEINTID_DEVRESET), 0); - - imxrt_usbreset(priv); - - usbtrace(TRACE_INTEXIT(IMXRT_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(IMXRT_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(IMXRT_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 = imxrt_getreg(IMXRT_USBDEV_PORTSC1); - - 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_IMXRT_USBDEV_FRAME_INTERRUPT - if (disr & USBDEV_USBSTS_SRI) - { - usbtrace(TRACE_INTDECODE(IMXRT_TRACEINTID_FRAME), 0); - - uint32_t frindex = imxrt_getreg(IMXRT_USBDEV_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 = imxrt_getreg (IMXRT_USBDEV_ENDPTCOMPLETE); - - if (mask) - { - /* Clear any NAK interrupt and completion interrupts */ - - imxrt_putreg (mask, IMXRT_USBDEV_ENDPTNAK); - imxrt_putreg (mask, IMXRT_USBDEV_ENDPTCOMPLETE); - - if (mask & IMXRT_ENDPTMASK(0)) - { - imxrt_ep0complete(priv, 0); - } - - if (mask & IMXRT_ENDPTMASK(1)) - { - imxrt_ep0complete(priv, 1); - } - - for (n = 1; n < IMXRT_NLOGENDPOINTS; n++) - { - if (mask & IMXRT_ENDPTMASK((n << 1))) - { - imxrt_epcomplete (priv, (n << 1)); - } - - if (mask & IMXRT_ENDPTMASK((n << 1)+1)) - { - imxrt_epcomplete(priv, (n << 1)+1); - } - } - } - - /* Handle setup interrupts */ - - uint32_t setupstat = imxrt_getreg(IMXRT_USBDEV_ENDPTSETUPSTAT); - if (setupstat) - { - /* Clear the endpoint complete CTRL OUT and IN when a Setup is - * received - */ - - imxrt_putreg(IMXRT_ENDPTMASK(IMXRT_EP0_IN) | - IMXRT_ENDPTMASK(IMXRT_EP0_OUT), - IMXRT_USBDEV_ENDPTCOMPLETE); - - if (setupstat & IMXRT_ENDPTMASK(IMXRT_EP0_OUT)) - { - usbtrace(TRACE_INTDECODE(IMXRT_TRACEINTID_EP0SETUP), - setupstat); - imxrt_ep0setup(priv); - } - } - } - - if (disr & USBDEV_USBSTS_NAKI) - { - uint32_t pending = imxrt_getreg(IMXRT_USBDEV_ENDPTNAK) & - imxrt_getreg(IMXRT_USBDEV_ENDPTNAKEN); - if (pending) - { - /* We shouldn't see NAK interrupts except on Endpoint 0 */ - - if (pending & IMXRT_ENDPTMASK(0)) - { - imxrt_ep0nak(priv, 0); - } - - if (pending & IMXRT_ENDPTMASK(1)) - { - imxrt_ep0nak(priv, 1); - } - } - - /* Clear the interrupts */ - - imxrt_putreg(pending, IMXRT_USBDEV_ENDPTNAK); - } - - usbtrace(TRACE_INTEXIT(IMXRT_TRACEINTID_USB), 0); - return OK; -} - -/**************************************************************************** - * Endpoint operations - ****************************************************************************/ - -/**************************************************************************** - * Name: imxrt_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 imxrt_epconfigure(FAR struct usbdev_ep_s *ep, - FAR const struct usb_epdesc_s *desc, - bool last) -{ - FAR struct imxrt_ep_s *privep = (FAR struct imxrt_ep_s *)ep; - struct imxrt_dqh_s *dqh = &g_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_flush_dcache((uintptr_t)dqh, - (uintptr_t)dqh + sizeof(struct imxrt_dqh_s)); - - /* Setup Endpoint Control Register */ - - if (IMXRT_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; - } - - imxrt_chgbits (0xffff0000, cfg, - IMXRT_USBDEV_ENDPTCTRL(privep->epphy >> 1)); - } - 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; - } - - imxrt_chgbits (0x0000ffff, cfg, - IMXRT_USBDEV_ENDPTCTRL(privep->epphy >> 1)); - } - - /* Reset endpoint status */ - - privep->stalled = false; - - /* Enable the endpoint */ - - if (IMXRT_EPPHYIN(privep->epphy)) - { - imxrt_setbits (USBDEV_ENDPTCTRL_TXE, - IMXRT_USBDEV_ENDPTCTRL(privep->epphy >> 1)); - } - else - { - imxrt_setbits (USBDEV_ENDPTCTRL_RXE, - IMXRT_USBDEV_ENDPTCTRL(privep->epphy >> 1)); - } - - return OK; -} - -/**************************************************************************** - * Name: imxrt_epdisable - * - * Description: - * The endpoint will no longer be used - * - ****************************************************************************/ - -static int imxrt_epdisable(FAR struct usbdev_ep_s *ep) -{ - FAR struct imxrt_ep_s *privep = (FAR struct imxrt_ep_s *)ep; - irqstate_t flags; - -#ifdef CONFIG_DEBUG_FEATURES - if (!ep) - { - usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_INVALIDPARMS), 0); - return -EINVAL; - } -#endif - - usbtrace(TRACE_EPDISABLE, privep->epphy); - - flags = enter_critical_section(); - - /* Disable Endpoint */ - - if (IMXRT_EPPHYIN(privep->epphy)) - { - imxrt_clrbits (USBDEV_ENDPTCTRL_TXE, - IMXRT_USBDEV_ENDPTCTRL(privep->epphy >> 1)); - } - else - { - imxrt_clrbits (USBDEV_ENDPTCTRL_RXE, - IMXRT_USBDEV_ENDPTCTRL(privep->epphy >> 1)); - } - - privep->stalled = true; - - /* Cancel any ongoing activity */ - - imxrt_cancelrequests(privep, -ESHUTDOWN); - - leave_critical_section(flags); - return OK; -} - -/**************************************************************************** - * Name: imxrt_epallocreq - * - * Description: - * Allocate an I/O request - * - ****************************************************************************/ - -static FAR struct usbdev_req_s *imxrt_epallocreq(FAR struct usbdev_ep_s *ep) -{ - FAR struct imxrt_req_s *privreq; - -#ifdef CONFIG_DEBUG_FEATURES - if (!ep) - { - usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_INVALIDPARMS), 0); - return NULL; - } -#endif - - usbtrace(TRACE_EPALLOCREQ, ((FAR struct imxrt_ep_s *)ep)->epphy); - - privreq = (FAR struct imxrt_req_s *)kmm_malloc(sizeof(struct imxrt_req_s)); - if (!privreq) - { - usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_ALLOCFAIL), 0); - return NULL; - } - - memset(privreq, 0, sizeof(struct imxrt_req_s)); - return &privreq->req; -} - -/**************************************************************************** - * Name: imxrt_epfreereq - * - * Description: - * Free an I/O request - * - ****************************************************************************/ - -static void imxrt_epfreereq(FAR struct usbdev_ep_s *ep, - FAR struct usbdev_req_s *req) -{ - FAR struct imxrt_req_s *privreq = (FAR struct imxrt_req_s *)req; - -#ifdef CONFIG_DEBUG_FEATURES - if (!ep || !req) - { - usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_INVALIDPARMS), 0); - return; - } -#endif - - usbtrace(TRACE_EPFREEREQ, ((FAR struct imxrt_ep_s *)ep)->epphy); - kmm_free(privreq); -} - -/**************************************************************************** - * Name: imxrt_epallocbuffer - * - * Description: - * Allocate an I/O buffer - * - ****************************************************************************/ - -#ifdef CONFIG_USBDEV_DMA -static void *imxrt_epallocbuffer(FAR 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. - */ - - FAR struct imxrt_ep_s *privep = (FAR struct imxrt_ep_s *)ep; - - usbtrace(TRACE_EPALLOCBUFFER, privep->epphy); - -#ifdef CONFIG_USBDEV_DMAMEMORY - return usbdev_dma_alloc(bytes); -#else - return kmm_malloc(bytes); -#endif -} -#endif - -/**************************************************************************** - * Name: imxrt_epfreebuffer - * - * Description: - * Free an I/O buffer - * - ****************************************************************************/ - -#ifdef CONFIG_USBDEV_DMA -static void imxrt_epfreebuffer(FAR struct usbdev_ep_s *ep, FAR void *buf) -{ - FAR struct imxrt_ep_s *privep = (FAR struct imxrt_ep_s *)ep; - - usbtrace(TRACE_EPFREEBUFFER, privep->epphy); - -#ifdef CONFIG_USBDEV_DMAMEMORY - usbdev_dma_free(buf); -#else - kmm_free(buf); -#endif -} -#endif - -/**************************************************************************** - * Name: imxrt_epsubmit - * - * Description: - * Submit an I/O request to the endpoint - * - ****************************************************************************/ - -static int imxrt_epsubmit(FAR struct usbdev_ep_s *ep, - FAR struct usbdev_req_s *req) -{ - FAR struct imxrt_req_s *privreq = (FAR struct imxrt_req_s *)req; - FAR struct imxrt_ep_s *privep = (FAR struct imxrt_ep_s *)ep; - FAR struct imxrt_usbdev_s *priv; - irqstate_t flags; - int ret = OK; - -#ifdef CONFIG_DEBUG_FEATURES - if (!req || !req->callback || !req->buf || !ep) - { - usbtrace(TRACE_DEVERROR(IMXRT_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(IMXRT_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 (IMXRT_EPPHYIN(privep->epphy)) - { - usbtrace(TRACE_INREQQUEUED(privep->epphy), privreq->req.len); - } - else - { - usbtrace(TRACE_OUTREQQUEUED(privep->epphy), privreq->req.len); - } - - if (imxrt_rqenqueue(privep, privreq)) - { - imxrt_progressep(privep); - } - } - - leave_critical_section(flags); - return ret; -} - -/**************************************************************************** - * Name: imxrt_epcancel - * - * Description: - * Cancel an I/O request previously sent to an endpoint - * - ****************************************************************************/ - -static int imxrt_epcancel(FAR struct usbdev_ep_s *ep, - FAR struct usbdev_req_s *req) -{ - FAR struct imxrt_ep_s *privep = (FAR struct imxrt_ep_s *)ep; - irqstate_t flags; - -#ifdef CONFIG_DEBUG_FEATURES - if (!ep || !req) - { - usbtrace(TRACE_DEVERROR(IMXRT_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 ... - */ - - imxrt_cancelrequests(privep, -ESHUTDOWN); - leave_critical_section(flags); - return OK; -} - -/**************************************************************************** - * Name: imxrt_epstall - * - * Description: - * Stall or resume and endpoint - * - ****************************************************************************/ - -static int imxrt_epstall(FAR struct usbdev_ep_s *ep, bool resume) -{ - FAR struct imxrt_ep_s *privep = (FAR struct imxrt_ep_s *)ep; - irqstate_t flags; - - /* STALL or RESUME the endpoint */ - - flags = enter_critical_section(); - usbtrace(resume ? TRACE_EPRESUME : TRACE_EPSTALL, privep->epphy); - - uint32_t addr = IMXRT_USBDEV_ENDPTCTRL(privep->epphy >> 1); - uint32_t ctrl_xs = IMXRT_EPPHYIN(privep->epphy) ? - USBDEV_ENDPTCTRL_TXS : USBDEV_ENDPTCTRL_RXS; - uint32_t ctrl_xr = IMXRT_EPPHYIN(privep->epphy) ? - USBDEV_ENDPTCTRL_TXR : USBDEV_ENDPTCTRL_RXR; - - if (resume) - { - privep->stalled = false; - - /* Clear stall and reset the data toggle */ - - imxrt_chgbits (ctrl_xs | ctrl_xr, ctrl_xr, addr); - } - else - { - privep->stalled = true; - - imxrt_setbits (ctrl_xs, addr); - } - - leave_critical_section(flags); - return OK; -} - -/**************************************************************************** - * Device operations - ****************************************************************************/ - -/**************************************************************************** - * Name: imxrt_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 FAR struct usbdev_ep_s *imxrt_allocep(FAR struct usbdev_s *dev, - uint8_t eplog, - bool in, uint8_t eptype) -{ - FAR struct imxrt_usbdev_s *priv = (FAR struct imxrt_usbdev_s *)dev; - uint32_t epset = IMXRT_EPALLSET & ~IMXRT_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 >= IMXRT_NLOGENDPOINTS) - { - usbtrace(TRACE_DEVERROR(IMXRT_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 &= IMXRT_EPINSET; - } - else - { - epset &= IMXRT_EPOUTSET; - } - - /* Get the subset matching the requested type */ - - switch (eptype) - { - case USB_EP_ATTR_XFER_INT: /* Interrupt endpoint */ - epset &= IMXRT_EPINTRSET; - break; - - case USB_EP_ATTR_XFER_BULK: /* Bulk endpoint */ - epset &= IMXRT_EPBULKSET; - break; - - case USB_EP_ATTR_XFER_ISOC: /* Isochronous endpoint */ - epset &= IMXRT_EPISOCSET; - break; - - case USB_EP_ATTR_XFER_CONTROL: /* Control endpoint -- not a valid choice */ - default: - usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_BADEPTYPE), (uint16_t)eptype); - return NULL; - } - - /* Is the resulting endpoint supported by the IMXRT3x? */ - - 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 < IMXRT_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(IMXRT_TRACEERR_NOEP), (uint16_t)eplog); - return NULL; -} - -/**************************************************************************** - * Name: imxrt_freeep - * - * Description: - * Free the previously allocated endpoint - * - ****************************************************************************/ - -static void imxrt_freeep(FAR struct usbdev_s *dev, - FAR struct usbdev_ep_s *ep) -{ - FAR struct imxrt_usbdev_s *priv = (FAR struct imxrt_usbdev_s *)dev; - FAR struct imxrt_ep_s *privep = (FAR struct imxrt_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: imxrt_getframe - * - * Description: - * Returns the current frame number - * - ****************************************************************************/ - -static int imxrt_getframe(struct usbdev_s *dev) -{ -#ifdef CONFIG_IMXRT_USBDEV_FRAME_INTERRUPT - FAR struct imxrt_usbdev_s *priv = (FAR struct imxrt_usbdev_s *)dev; - - /* 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 = imxrt_getreg(IMXRT_USBDEV_FRINDEX); - 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: imxrt_wakeup - * - * Description: - * Tries to wake up the host connected to this device - * - ****************************************************************************/ - -static int imxrt_wakeup(struct usbdev_s *dev) -{ - irqstate_t flags; - - usbtrace(TRACE_DEVWAKEUP, 0); - - flags = enter_critical_section(); - imxrt_setbits(USBDEV_PRTSC1_FPR, IMXRT_USBDEV_PORTSC1); - leave_critical_section(flags); - return OK; -} - -/**************************************************************************** - * Name: imxrt_selfpowered - * - * Description: - * Sets/clears the device selfpowered feature - * - ****************************************************************************/ - -static int imxrt_selfpowered(struct usbdev_s *dev, bool selfpowered) -{ - FAR struct imxrt_usbdev_s *priv = (FAR struct imxrt_usbdev_s *)dev; - - usbtrace(TRACE_DEVSELFPOWERED, (uint16_t)selfpowered); - -#ifdef CONFIG_DEBUG_FEATURES - if (!dev) - { - usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_INVALIDPARMS), 0); - return -ENODEV; - } -#endif - - priv->selfpowered = selfpowered; - return OK; -} - -/**************************************************************************** - * Name: imxrt_pullup - * - * Description: - * Software-controlled connect to/disconnect from USB host - * - ****************************************************************************/ - -static int imxrt_pullup(struct usbdev_s *dev, bool enable) -{ - usbtrace(TRACE_DEVPULLUP, (uint16_t)enable); - - irqstate_t flags = enter_critical_section(); - if (enable) - { - imxrt_setbits (USBDEV_USBCMD_RS, IMXRT_USBDEV_USBCMD); - -#ifdef CONFIG_IMXRT_USB0DEV_NOVBUS - /* Create a 'false' power event on the USB port so the MAC connects */ - - imxrt_clrbits (USBOTG_OTGSC_VD, IMXRT_USBOTG_OTGSC); - imxrt_setbits (USBOTG_OTGSC_VC, IMXRT_USBOTG_OTGSC); -#endif - } - else - { - imxrt_clrbits (USBDEV_USBCMD_RS, IMXRT_USBDEV_USBCMD); - } - - leave_critical_section(flags); - return OK; -} - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: arm_usbinitialize - * - * Description: - * Initialize USB hardware. - * - * Assumptions: - * - This function is called very early in the initialization sequence - * - PLL initialization is not performed here but should been in - * the low-level boot logic: USB1 PLL must be configured for operation - * at 480MHz - * - ****************************************************************************/ - -void arm_usbinitialize(void) -{ - struct imxrt_usbdev_s *priv = &g_usbdev; - int i; - irqstate_t flags; - - flags = enter_critical_section(); - - /* Initialize the device state structure */ - - memset(priv, 0, sizeof(struct imxrt_usbdev_s)); - priv->usbdev.ops = &g_devops; - priv->usbdev.ep0 = &priv->eplist[IMXRT_EP0_IN].ep; - priv->epavail = IMXRT_EPALLSET & ~IMXRT_EPCTRLSET; - - /* Initialize the endpoint list */ - - for (i = 0; i < IMXRT_NPHYSENDPOINTS; i++) - { - uint32_t bit = 1 << i; - - /* Set endpoint operations, reference to driver structure (not - * really necessary because there is only one controller), and - * the physical endpoint number (which is just the index to the - * endpoint). - */ - - 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 (IMXRT_EPPHYIN(i)) - { - priv->eplist[i].ep.eplog = IMXRT_EPPHYIN2LOG(i); - } - else - { - priv->eplist[i].ep.eplog = IMXRT_EPPHYOUT2LOG(i); - } - - /* The maximum packet size may depend on the type of endpoint */ - - if ((IMXRT_EPCTRLSET & bit) != 0) - { - priv->eplist[i].ep.maxpacket = IMXRT_EP0MAXPACKET; - } - else if ((IMXRT_EPINTRSET & bit) != 0) - { - priv->eplist[i].ep.maxpacket = IMXRT_INTRMAXPACKET; - } - else if ((IMXRT_EPBULKSET & bit) != 0) - { - priv->eplist[i].ep.maxpacket = IMXRT_BULKMAXPACKET; - } - else /* if ((IMXRT_EPISOCSET & bit) != 0) */ - { - priv->eplist[i].ep.maxpacket = IMXRT_ISOCMAXPACKET; - } - } - - /* Clock run */ - - imxrt_clockall_usboh3(); - - /* Disable USB interrupts */ - - imxrt_putreg(0, IMXRT_USBDEV_USBINTR); - - /* Soft reset PHY and enable clock */ - - putreg32(USBPHY_CTRL_SFTRST | USBPHY_CTRL_CLKGATE, IMXRT_USBPHY2_CTRL_CLR); - - /* Disconnect device */ - - imxrt_pullup(&priv->usbdev, false); - - /* Reset the controller */ - - imxrt_setbits (USBDEV_USBCMD_RST, IMXRT_USBDEV_USBCMD); - while (imxrt_getreg (IMXRT_USBDEV_USBCMD) & USBDEV_USBCMD_RST) - ; - - /* Power up the PHY (turn off power disable) - USBPHYx_PWDn - * Manual: The USB PHY Power-Down Register provides overall control of the - * PHY power state. Before programming this register, the PHY clocks must - * be enabled in registers USBPHYx_CTRLn and - * CCM_ANALOG_USBPHYx_PLL_480_CTRLn. - */ - - imxrt_putreg(0, IMXRT_USBPHY2_PWD); - - /* Program the controller to be the USB device controller */ - - imxrt_putreg (USBDEV_USBMODE_SDIS | USBDEV_USBMODE_SLOM | - USBDEV_USBMODE_CM_DEVICE, IMXRT_USBDEV_USBMODE); - - /* Attach USB controller interrupt handler */ - - irq_attach(IMXRT_IRQ_USBOTG2, imxrt_usbinterrupt, NULL); - up_enable_irq(IMXRT_IRQ_USBOTG2); - - leave_critical_section(flags); - - /* Reset/Re-initialize the USB hardware */ - - imxrt_usbreset(priv); - - return; -} - -/**************************************************************************** - * Name: arm_usbuninitialize - ****************************************************************************/ - -void arm_usbuninitialize(void) -{ - struct imxrt_usbdev_s *priv = &g_usbdev; - irqstate_t flags; - - usbtrace(TRACE_DEVUNINIT, 0); - - if (priv->driver) - { - usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_DRIVERREGISTERED), 0); - usbdev_unregister(priv->driver); - } - - flags = enter_critical_section(); - - /* Disconnect device */ - - imxrt_pullup(&priv->usbdev, false); - priv->usbdev.speed = USB_SPEED_UNKNOWN; - - /* Disable and detach IRQs */ - - up_disable_irq(IMXRT_IRQ_USBOTG2); - irq_detach(IMXRT_IRQ_USBOTG2); - - /* Reset the controller */ - - imxrt_setbits (USBDEV_USBCMD_RST, IMXRT_USBDEV_USBCMD); - while (imxrt_getreg (IMXRT_USBDEV_USBCMD) & USBDEV_USBCMD_RST) - ; - - /* Turn off USB power and clocking */ - - /* Power down the PHY */ - - imxrt_putreg(0xffffffff, IMXRT_USBPHY2_PWD); - - /* Stop clock - * NOTE: This will interfere with USB OTG 2 and should probably be removed - * if Device or Host code is expanded to support both OTG Cores. - */ - - imxrt_clockoff_usboh3(); - - 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(IMXRT_TRACEERR_INVALIDPARMS), 0); - return -EINVAL; - } - - if (g_usbdev.driver) - { - usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_DRIVER), 0); - return -EBUSY; - } -#endif - - /* First hook up the driver */ - - g_usbdev.driver = driver; - - /* Then bind the class driver */ - - ret = CLASS_BIND(driver, &g_usbdev.usbdev); - if (ret) - { - usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_BINDFAILED), (uint16_t)-ret); - g_usbdev.driver = NULL; - } - else - { - /* Enable USB controller interrupts */ - - up_enable_irq(IMXRT_IRQ_USBOTG2); - } - - 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) -{ - usbtrace(TRACE_DEVUNREGISTER, 0); - -#ifdef CONFIG_DEBUG_FEATURES - if (driver != g_usbdev.driver) - { - usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_INVALIDPARMS), 0); - return -EINVAL; - } -#endif - - /* Unbind the class driver */ - - CLASS_UNBIND(driver, &g_usbdev.usbdev); - - /* Disable USB controller interrupts */ - - up_disable_irq(IMXRT_IRQ_USBOTG2); - - /* Unhook the driver */ - - g_usbdev.driver = NULL; - return OK; -} - From 0295b7ca38cf7d909057829eab648fc6746b1fea Mon Sep 17 00:00:00 2001 From: wgzAIIT <820906721@qq.com> Date: Fri, 1 Apr 2022 10:10:49 +0800 Subject: [PATCH 4/7] backup 9 files from nuttx/arch/arm/src/imxrt/hardware for usb host --- .../arm/src/imxrt/hardware/imxrt_usbotg.h | 734 +++ .../arm/src/imxrt/hardware/imxrt_usbphy.h | 70 + .../hardware/rt105x/imxrt105x_memorymap.h | 311 + .../arch/arm/src/imxrt/imxrt_clockconfig.c | 698 +++ .../nuttx/arch/arm/src/imxrt/imxrt_ehci.c | 5452 +++++++++++++++++ .../nuttx/arch/arm/src/imxrt/imxrt_lowputc.c | 603 ++ .../nuttx/arch/arm/src/imxrt/imxrt_lpi2c.c | 1997 ++++++ .../nuttx/arch/arm/src/imxrt/imxrt_lpspi.c | 1706 ++++++ .../nuttx/arch/arm/src/imxrt/imxrt_usbdev.c | 3061 +++++++++ 9 files changed, 14632 insertions(+) create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbotg.h create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbphy.h create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/rt105x/imxrt105x_memorymap.h create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_clockconfig.c create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_ehci.c create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lowputc.c create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpi2c.c create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpspi.c create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_usbdev.c diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbotg.h b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbotg.h new file mode 100644 index 000000000..5921081a2 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbotg.h @@ -0,0 +1,734 @@ +/**************************************************************************** + * arch/arm/src/imxrt/hardware/imxrt_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_ARM_SRC_IMXRT_HARDWARE_IMXRT_USBOTG_H +#define __ARCH_ARM_SRC_IMXRT_HARDWARE_IMXRT_USBOTG_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define IMXRT_EHCI_NRHPORT 1 /* There is only a single root hub port */ + +/* USBOTG register offsets (with respect to IMXRT_USB_BASE) *****************/ + + /* 0x000 - 0x0ff: Reserved */ + +/* Device/host capability registers */ + +#define IMXRT_USBOTG_HCCR_OFFSET 0x100 /* Offset to EHCI Host Controller Capabiliy registers */ +#define IMXRT_USBOTG_CAPLENGTH_OFFSET 0x100 /* Capability register length (8-bit) */ +#define IMXRT_USBHOST_HCIVERSION_OFFSET 0x102 /* Host interface version number (16-bit) */ +#define IMXRT_USBHOST_HCSPARAMS_OFFSET 0x104 /* Host controller structural parameters */ +#define IMXRT_USBHOST_HCCPARAMS_OFFSET 0x108 /* Host controller capability parameters */ +#define IMXRT_USBDEV_DCIVERSION_OFFSET 0x120 /* Device interface version number */ +#define IMXRT_USBDEV_DCCPARAMS_OFFSET 0x124 /* Device controller capability parameters */ + +/* Device/host/OTG operational registers */ + +#define IMXRT_USBOTG_HCOR_OFFSET 0x140 /* Offset to EHCI Host Controller Operational Registers */ +#define IMXRT_USBOTG_USBCMD_OFFSET 0x140 /* USB command (both) */ +#define IMXRT_USBOTG_USBSTS_OFFSET 0x144 /* USB status (both) */ +#define IMXRT_USBOTG_USBINTR_OFFSET 0x148 /* USB interrupt enable (both) */ +#define IMXRT_USBOTG_FRINDEX_OFFSET 0x14c /* USB frame index (both) */ + /* EHCI 4G Segment Selector (not supported) */ +#define IMXRT_USBOTG_PERIODICLIST_OFFSET 0x154 /* Frame list base address (host) */ +#define IMXRT_USBOTG_DEVICEADDR_OFFSET 0x154 /* USB device address (device) */ +#define IMXRT_USBOTG_ASYNCLISTADDR_OFFSET 0x158 /* Next asynchronous list address (host) */ +#define IMXRT_USBOTG_ENDPOINTLIST_OFFSET 0x158 /* Address of endpoint list in memory (device) */ +#define IMXRT_USBOTG_TTCTRL_OFFSET 0x15c /* Asynchronous buffer status for embedded TT (host) */ +#define IMXRT_USBOTG_BURSTSIZE_OFFSET 0x160 /* Programmable burst size (both) */ +#define IMXRT_USBOTG_TXFILLTUNING_OFFSET 0x164 /* Host transmit pre-buffer packet tuning (host) */ +#define IMXRT_USBOTG_BINTERVAL_OFFSET 0x174 /* Length of virtual frame (both) */ +#define IMXRT_USBOTG_ENDPTNAK_OFFSET 0x178 /* Endpoint NAK (device) */ +#define IMXRT_USBOTG_ENDPTNAKEN_OFFSET 0x17c /* Endpoint NAK Enable (device) */ +#define IMXRT_USBOTG_CONFIGFLAG_OFFSET 0x180 /* Configured flag register (not used in lpc313x) */ +#define IMXRT_USBOTG_PORTSC1_OFFSET 0x184 /* Port status/control 1 (both) */ +#define IMXRT_USBOTG_OTGSC_OFFSET 0x1a4 /* OTG status and control (otg) */ +#define IMXRT_USBOTG_USBMODE_OFFSET 0x1a8 /* USB device mode (both) */ + +#define IMXRT_USBDEV_USBCMD_OFFSET 0x140 /* USB command (both) */ +#define IMXRT_USBDEV_USBSTS_OFFSET 0x144 /* USB status (both) */ +#define IMXRT_USBDEV_USBINTR_OFFSET 0x148 /* USB interrupt enable (both) */ +#define IMXRT_USBDEV_FRINDEX_OFFSET 0x14c /* USB frame index (both) */ +#define IMXRT_USBDEV_DEVICEADDR_OFFSET 0x154 /* USB device address (device) */ +#define IMXRT_USBDEV_ENDPOINTLIST_OFFSET 0x158 /* Address of endpoint list in memory (device) */ +#define IMXRT_USBDEV_BURSTSIZE_OFFSET 0x160 /* Programmable burst size (both) */ +#define IMXRT_USBDEV_BINTERVAL_OFFSET 0x174 /* Length of virtual frame (both) */ +#define IMXRT_USBDEV_ENDPTNAK_OFFSET 0x178 /* Endpoint NAK (device) */ +#define IMXRT_USBDEV_ENDPTNAKEN_OFFSET 0x17c /* Endpoint NAK Enable (device) */ +#define IMXRT_USBDEV_PORTSC1_OFFSET 0x184 /* Port status/control 1 (both) */ +#define IMXRT_USBDEV_USBMODE_OFFSET 0x1a8 /* USB device mode (both) */ + +#define IMXRT_USBHOST_USBCMD_OFFSET 0x140 /* USB command (both) */ +#define IMXRT_USBHOST_USBSTS_OFFSET 0x144 /* USB status (both) */ +#define IMXRT_USBHOST_USBINTR_OFFSET 0x148 /* USB interrupt enable (both) */ +#define IMXRT_USBHOST_FRINDEX_OFFSET 0x14c /* USB frame index (both) */ +#define IMXRT_USBHOST_PERIODICLIST_OFFSET 0x154 /* Frame list base address (host) */ +#define IMXRT_USBHOST_ASYNCLISTADDR_OFFSET 0x158 /* Next asynchronous list address (host) */ +#define IMXRT_USBHOST_TTCTRL_OFFSET 0x15c /* Asynchronous buffer status for embedded TT (host) */ +#define IMXRT_USBHOST_BURSTSIZE_OFFSET 0x160 /* Programmable burst size (both) */ +#define IMXRT_USBHOST_TXFILLTUNING_OFFSET 0x164 /* Host transmit pre-buffer packet tuning (host) */ +#define IMXRT_USBHOST_BINTERVAL_OFFSET 0x174 /* Length of virtual frame (both) */ +#define IMXRT_USBHOST_PORTSC1_OFFSET 0x184 /* Port status/control 1 (both) */ +#define IMXRT_USBHOST_USBMODE_OFFSET 0x1a8 /* USB device mode (both) */ + +/* Device endpoint registers */ + +#define IMXRT_USBDEV_ENDPTSETUPSTAT_OFFSET 0x1ac /* Endpoint setup status */ +#define IMXRT_USBDEV_ENDPTPRIME_OFFSET 0x1b0 /* Endpoint initialization */ +#define IMXRT_USBDEV_ENDPTFLUSH_OFFSET 0x1b4 /* Endpoint de-initialization */ +#define IMXRT_USBDEV_ENDPTSTATUS_OFFSET 0x1b8 /* Endpoint status */ +#define IMXRT_USBDEV_ENDPTCOMPLETE_OFFSET 0x1bc /* Endpoint complete */ + +#define IMXRT_USBDEV_ENDPTCTRL_OFFSET(n) (IMXRT_USBDEV_ENDPTCTRL0_OFFSET + ((n) * 4)) +#define IMXRT_USBDEV_ENDPTCTRL0_OFFSET 0x1c0 /* Endpoint control 0 */ +#define IMXRT_USBDEV_ENDPTCTRL1_OFFSET 0x1c4 /* Endpoint control 1 */ +#define IMXRT_USBDEV_ENDPTCTRL2_OFFSET 0x1c8 /* Endpoint control 2 */ +#define IMXRT_USBDEV_ENDPTCTRL3_OFFSET 0x1cc /* Endpoint control 3 */ +#define IMXRT_USBDEV_ENDPTCTRL4_OFFSET 0x1d0 /* Endpoint control 4 */ +#define IMXRT_USBDEV_ENDPTCTRL5_OFFSET 0x1d4 /* Endpoint control 5 */ +#define IMXRT_USBDEV_ENDPTCTRL6_OFFSET 0x1d8 /* Endpoint control 6 */ +#define IMXRT_USBDEV_ENDPTCTRL7_OFFSET 0x1dc /* Endpoint control 7 */ + +/* USB Non-core memory map & register definition */ + +#define IMXRT_USBNC_USB_OTG1_CTRL_OFFSET 0x0800 /* OTG1 Control Register */ +#define IMXRT_USBNC_USB_OTG1_PHY_CTRL_0_OFFSET 0x0818 /* OTG1 Phy Control Register */ + +/* USBOTG register (virtual) addresses **************************************/ + +/* Device/host capability registers */ + +#define IMXRT_USBOTG_HCCR_BASE (IMXRT_USB_BASE + IMXRT_USBOTG_HCCR_OFFSET) +#define IMXRT_USBOTG_CAPLENGTH (IMXRT_USB_BASE + IMXRT_USBOTG_CAPLENGTH_OFFSET) +#define IMXRT_USBHOST_HCIVERSION (IMXRT_USB_BASE + IMXRT_USBHOST_HCIVERSION_OFFSET) +#define IMXRT_USBHOST_HCSPARAMS (IMXRT_USB_BASE + IMXRT_USBHOST_HCSPARAMS_OFFSET) +#define IMXRT_USBHOST_HCCPARAMS (IMXRT_USB_BASE + IMXRT_USBHOST_HCCPARAMS_OFFSET) +#define IMXRT_USBDEV_DCIVERSION (IMXRT_USB_BASE + IMXRT_USBDEV_DCIVERSION_OFFSET) +#define IMXRT_USBDEV_DCCPARAMS (IMXRT_USB_BASE + IMXRT_USBDEV_DCCPARAMS_OFFSET) + +/* Device/host operational registers */ + +#define IMXRT_USBOTG_HCOR_BASE (IMXRT_USB_BASE + IMXRT_USBOTG_HCOR_OFFSET) +#define IMXRT_USBOTG_USBCMD (IMXRT_USB_BASE + IMXRT_USBOTG_USBCMD_OFFSET) +#define IMXRT_USBOTG_USBSTS (IMXRT_USB_BASE + IMXRT_USBOTG_USBSTS_OFFSET) +#define IMXRT_USBOTG_USBINTR (IMXRT_USB_BASE + IMXRT_USBOTG_USBINTR_OFFSET) +#define IMXRT_USBOTG_FRINDEX (IMXRT_USB_BASE + IMXRT_USBOTG_FRINDEX_OFFSET) +#define IMXRT_USBOTG_PERIODICLIST (IMXRT_USB_BASE + IMXRT_USBOTG_PERIODICLIST_OFFSET) +#define IMXRT_USBOTG_DEVICEADDR (IMXRT_USB_BASE + IMXRT_USBOTG_DEVICEADDR_OFFSET) +#define IMXRT_USBOTG_ASYNCLISTADDR (IMXRT_USB_BASE + IMXRT_USBOTG_ASYNCLISTADDR_OFFSET) +#define IMXRT_USBOTG_ENDPOINTLIST (IMXRT_USB_BASE + IMXRT_USBOTG_ENDPOINTLIST_OFFSET) +#define IMXRT_USBOTG_TTCTRL (IMXRT_USB_BASE + IMXRT_USBOTG_TTCTRL_OFFSET) +#define IMXRT_USBOTG_BURSTSIZE (IMXRT_USB_BASE + IMXRT_USBOTG_BURSTSIZE_OFFSET) +#define IMXRT_USBOTG_TXFILLTUNING (IMXRT_USB_BASE + IMXRT_USBOTG_TXFILLTUNING_OFFSET) +#define IMXRT_USBOTG_BINTERVAL (IMXRT_USB_BASE + IMXRT_USBOTG_BINTERVAL_OFFSET) +#define IMXRT_USBOTG_ENDPTNAK (IMXRT_USB_BASE + IMXRT_USBOTG_ENDPTNAK_OFFSET) +#define IMXRT_USBOTG_ENDPTNAKEN (IMXRT_USB_BASE + IMXRT_USBOTG_ENDPTNAKEN_OFFSET) +#define IMXRT_USBOTG_PORTSC1 (IMXRT_USB_BASE + IMXRT_USBOTG_PORTSC1_OFFSET) +#define IMXRT_USBOTG_OTGSC (IMXRT_USB_BASE + IMXRT_USBOTG_OTGSC_OFFSET) +#define IMXRT_USBOTG_USBMODE (IMXRT_USB_BASE + IMXRT_USBOTG_USBMODE_OFFSET) + +#define IMXRT_USBDEV_USBCMD (IMXRT_USB_BASE + IMXRT_USBDEV_USBCMD_OFFSET) +#define IMXRT_USBDEV_USBSTS (IMXRT_USB_BASE + IMXRT_USBDEV_USBSTS_OFFSET) +#define IMXRT_USBDEV_USBINTR (IMXRT_USB_BASE + IMXRT_USBDEV_USBINTR_OFFSET) +#define IMXRT_USBDEV_FRINDEX (IMXRT_USB_BASE + IMXRT_USBDEV_FRINDEX_OFFSET) +#define IMXRT_USBDEV_DEVICEADDR (IMXRT_USB_BASE + IMXRT_USBDEV_DEVICEADDR_OFFSET) +#define IMXRT_USBDEV_ENDPOINTLIST (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPOINTLIST_OFFSET) +#define IMXRT_USBDEV_BURSTSIZE (IMXRT_USB_BASE + IMXRT_USBDEV_BURSTSIZE_OFFSET) +#define IMXRT_USBDEV_BINTERVAL (IMXRT_USB_BASE + IMXRT_USBDEV_BINTERVAL_OFFSET) +#define IMXRT_USBDEV_ENDPTNAK (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTNAK_OFFSET) +#define IMXRT_USBDEV_ENDPTNAKEN (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTNAKEN_OFFSET) +#define IMXRT_USBDEV_PORTSC1 (IMXRT_USB_BASE + IMXRT_USBDEV_PORTSC1_OFFSET) +#define IMXRT_USBDEV_USBMODE (IMXRT_USB_BASE + IMXRT_USBDEV_USBMODE_OFFSET) + +#define IMXRT_USBHOST_USBCMD (IMXRT_USB_BASE + IMXRT_USBHOST_USBCMD_OFFSET) +#define IMXRT_USBHOST_USBSTS (IMXRT_USB_BASE + IMXRT_USBHOST_USBSTS_OFFSET) +#define IMXRT_USBHOST_USBINTR (IMXRT_USB_BASE + IMXRT_USBHOST_USBINTR_OFFSET) +#define IMXRT_USBHOST_FRINDEX (IMXRT_USB_BASE + IMXRT_USBHOST_FRINDEX_OFFSET) +#define IMXRT_USBHOST_PERIODICLIST (IMXRT_USB_BASE + IMXRT_USBHOST_PERIODICLIST_OFFSET) +#define IMXRT_USBHOST_ASYNCLISTADDR (IMXRT_USB_BASE + IMXRT_USBHOST_ASYNCLISTADDR_OFFSET) +#define IMXRT_USBHOST_TTCTRL (IMXRT_USB_BASE + IMXRT_USBHOST_TTCTRL_OFFSET) +#define IMXRT_USBHOST_BURSTSIZE (IMXRT_USB_BASE + IMXRT_USBHOST_BURSTSIZE_OFFSET) +#define IMXRT_USBHOST_TXFILLTUNING (IMXRT_USB_BASE + IMXRT_USBHOST_TXFILLTUNING_OFFSET) +#define IMXRT_USBHOST_BINTERVAL (IMXRT_USB_BASE + IMXRT_USBHOST_BINTERVAL_OFFSET) +#define IMXRT_USBHOST_PORTSC1 (IMXRT_USB_BASE + IMXRT_USBHOST_PORTSC1_OFFSET) +#define IMXRT_USBHOST_USBMODE (IMXRT_USB_BASE + IMXRT_USBHOST_USBMODE_OFFSET) + +/* Device endpoint registers */ + +#define IMXRT_USBDEV_ENDPTSETUPSTAT (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTSETUPSTAT_OFFSET) +#define IMXRT_USBDEV_ENDPTPRIME (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTPRIME_OFFSET) +#define IMXRT_USBDEV_ENDPTFLUSH (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTFLUSH_OFFSET) +#define IMXRT_USBDEV_ENDPTSTATUS (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTSTATUS_OFFSET) +#define IMXRT_USBDEV_ENDPTCOMPLETE (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCOMPLETE_OFFSET) + +#define IMXRT_USBDEV_ENDPTCTRL(n) (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCTRL_OFFSET(n)) +#define IMXRT_USBDEV_ENDPTCTRL0 (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCTRL0_OFFSET) +#define IMXRT_USBDEV_ENDPTCTRL1 (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCTRL1_OFFSET) +#define IMXRT_USBDEV_ENDPTCTRL2 (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCTRL2_OFFSET) +#define IMXRT_USBDEV_ENDPTCTRL3 (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCTRL3_OFFSET) +#define IMXRT_USBDEV_ENDPTCTRL4 (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCTRL4_OFFSET) +#define IMXRT_USBDEV_ENDPTCTRL5 (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCTRL5_OFFSET) +#define IMXRT_USBDEV_ENDPTCTRL6 (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCTRL6_OFFSET) +#define IMXRT_USBDEV_ENDPTCTRL7 (IMXRT_USB_BASE + IMXRT_USBDEV_ENDPTCTRL7_OFFSET) + +/* Device non-core registers */ + +#define IMXRT_USBNC_USB_OTG1_CTRL (IMXRT_USB_BASE + IMXRT_USBNC_USB_OTG1_CTRL_OFFSET) +#define IMXRT_USBNC_USB_OTG1_PHY_CTRL_0 (IMXRT_USB_BASE + IMXRT_USBNC_USB_OTG1_PHY_CTRL_0_OFFSET) + +/* 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 (15 << 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 (15) /* Bits 12-15: Number of Companion Controller */ +#define USBHOST_HCSPARAMS_NCC_MASK (15 << USBHOST_HCSPARAMS_NCC_SHIFT) +#define USBHOST_HCSPARAMS_NPCC_SHIFT (8) /* Bits 8-11: Number of Ports per Companion Controller */ +#define USBHOST_HCSPARAMS_NPCC_MASK (15 << 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 (15 << USBHOST_HCSPARAMS_NPORTS_SHIFT) + +/* HCCPARAMS */ + +#define USBHOST_HCCPARAMS_EECP_SHIFT (8) /* Bits 8-15: EHCI Extended Capabilities Pointer */ +#define USBHOST_HCCPARAMS_EECP_MASK (255 << USBHOST_HCCPARAMS_EECP_SHIFT) +#define USBHOST_HCCPARAMS_IST_SHIFT (4) /* Bits 4-7: Isochronous Scheduling Threshold */ +#define USBHOST_HCCPARAMS_IST_MASK (15 << 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 (31 << 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 (255 << 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 (255 << 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 (3 << 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_UPIA (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 (7 << 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 (0x3c << 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_PERBASE_SHIFT (12) /* Bits 12-31: Base Address (Low) */ +#define USBHOST_PERIODICLIST_PERBASE_MASK (0x000fffff << USBHOST_PERIODICLIST_PERBASE_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 (0x001fffff << USBDEV_ENDPOINTLIST_EPBASE_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 TT Control register TTCTRL (address 0x1900015c) -- Host Mode */ + +#define USBHOST_TTCTRL_TTHA_SHIFT (24) /* Bits 24-30: Hub address */ +#define USBHOST_TTCTRL_TTHA_MASK (0x7f << USBHOST_TTCTRL_TTHA_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 (255 << USBDEV_BURSTSIZE_TXPBURST_SHIFT) +#define USBDEV_BURSTSIZE_RXPBURST_SHIFT (0) /* Bits 0-7: RXPBURST Programmable RX burst length */ +#define USBDEV_BURSTSIZE_RXPBURST_MASK (255 << USBDEV_BURSTSIZE_RXPBURST_SHIFT) + +#define USBHOST_BURSTSIZE_TXPBURST_SHIFT (8) /* Bits 8-15: Programmable TX burst length */ +#define USBHOST_BURSTSIZE_TXPBURST_MASK (255 << USBHOST_BURSTSIZE_TXPBURST_SHIFT) +#define USBHOST_BURSTSIZE_RXPBURST_SHIFT (0) /* Bits 0-7: RXPBURST Programmable RX burst length */ +#define USBHOST_BURSTSIZE_RXPBURST_MASK (255 << 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 (0x3c << 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 (0xff << USBHOST_TXFILLTUNING_SCHOH_SHIFT) + +/* USB BINTERVAL register BINTERVAL -- Device/Host Mode */ + +#define USBDEV_BINTERVAL_SHIFT (0) /* Bits 0-3: bInterval value */ +#define USBDEV_BINTERVAL_MASK (15 << USBDEV_BINTERVAL_SHIFT) + +#define USBHOST_BINTERVAL_SHIFT (0) /* Bits 0-3: bInterval value */ +#define USBHOST_BINTERVAL_MASK (15 << USBHOST_BINTERVAL_SHIFT) + +/* USB endpoint NAK register ENDPTNAK -- Device Mode */ + +#define USBDEV_ENDPTNAK_EPTN_SHIFT (16) /* Bits 16-19: Tx endpoint NAK */ +#define USBDEV_ENDPTNAK_EPTN_MASK (15 << USBDEV_ENDPTNAK_EPTN_SHIFT) +#define USBDEV_ENDPTNAK_EPRN_SHIFT (0) /* Bits 0-3: Rx endpoint NAK */ +#define USBDEV_ENDPTNAK_EPRN_MASK (15 << USBDEV_ENDPTNAK_EPRN_SHIFT) + +/* USB Endpoint NAK Enable register ENDPTNAKEN -- Device Mode */ + +#define USBDEV_ENDPTNAK_EPTNE_SHIFT (16) /* Bits 16-19: Tx endpoint NAK enable */ +#define USBDEV_ENDPTNAK_EPTNE_MASK (15 << USBDEV_ENDPTNAK_EPTNE_SHIFT) +#define USBDEV_ENDPTNAK_EPRNE_SHIFT (0) /* Bits 0-3: Rx endpoint NAK enable */ +#define USBDEV_ENDPTNAK_EPRNE_MASK (15 << USBDEV_ENDPTNAK_EPRNE_SHIFT) + +/* Port Status and Control register PRTSC1 -- Device Mode */ + +#define USBDEV_PRTSC1_PSPD_SHIFT (26) /* Bits 26-27: Port speed */ +#define USBDEV_PRTSC1_PSPD_MASK (3 << 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 (15 << 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 (3 << 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 (3 << 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 (15 << 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 (3 << 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 (3 << 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_HABA (1 << 7) /* Bit 7: Hardware assist B-disconnect to A-connect */ +#define USBOTG_OTGSC_HADP (1 << 6) /* Bit 6: Hardware assist data pulse */ +#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_HAAR (1 << 2) /* Bit 2: Hardware assist auto_reset */ +#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 (3 << 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 -- Device Mode */ + +#define USBHOST_USBMODE_VBPS (1 << 5) /* Bit 5: VBUS power select */ +#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 (3 << 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 (3 << 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 (3 << 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 (3 << 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 (3 << 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 */ + +/* Bits 0-6: + * Reserved + */ +#define USBNC_OVER_CUR_DIS (1 << 7) /* Bit 7: Disable Over current detection */ +#define USBNC_OVER_CUR_POL (1 << 8) /* Bit 8: Polarity of over current */ +#define USBNC_PWR_POL (1 << 9) /* Bit 9: Power polarity */ +#define USBNC_WIE (1<< 10) /* Bit 10: Wake up interrupt enable */ + /* Bit 11-13: Reserved */ +#define USBNC_WKUP_SW_EN (1 << 14) /* Bit 14: Software wake up enable */ +#define USBNC_WKUP_SW (1 << 15) /* Bit 15: Software wake up */ +#define USBNC_WKUP_ID_EN (1 << 16) /* Bit 16: Wakeup on ID change enable */ +#define USBNC_WKUP_VBUS_EN (1 << 17) /* Bit 17: Wakeup on VBUS change enable */ + /* Bit 18-28: Reserved */ +#define USBNC_WKUP_DPDM_EN (1 << 29) /* Bit 29: Wakeup on DPDM change enable */ + /* Bit 30: Reserved */ +#define USBNC_WIR (1 << 31) /* Bit 31: Wake up interrupt request */ + +#endif /* __ARCH_ARM_SRC_IMXRT_HARDWARE_IMXRT_USBOTG_H */ diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbphy.h b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbphy.h new file mode 100644 index 000000000..1d4b7ff4c --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbphy.h @@ -0,0 +1,70 @@ +/**************************************************************************** + * arch/arm/src/imxrt/hardware/imxrt_usbphy.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_ARM_SRC_IMXRT_HARDWARE_IMXRT_USB_PHY_H +#define __ARCH_ARM_SRC_IMXRT_HARDWARE_IMXRT_USB_PHY_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include "hardware/imxrt_memorymap.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define IMXRT_USBPHY_BASE_OFFSET 0x1000 /* USB PHY Base */ + +#define IMXRT_USBPHY_BASE (IMXRT_ANATOP_BASE + IMXRT_USBPHY_BASE_OFFSET) /* USB PHY Base */ + +/* Register Offsets *********************************************************/ + +#define IMXRT_USBPHY1_PWD_OFFSET 0x0000 /* USBPHY1 USB PHY Power-Down Register */ +#define IMXRT_USBPHY1_PWD_CLR_OFFSET 0x0008 /* USBPHY1 USB PHY Power-Down Register Clear */ +#define IMXRT_USBPHY1_CTRL_OFFSET 0x0030 /* USBPHY1 USB PHY General Control Register */ +#define IMXRT_USBPHY1_CTRL_CLR_OFFSET 0x0038 /* USBPHY1 USB PHY General Control Register Clear */ + +/* Register addresses *******************************************************/ + +#define IMXRT_USBPHY1_PWD (IMXRT_USBPHY_BASE + IMXRT_USBPHY1_PWD_OFFSET) /* USBPHY1 USB PHY Power-Down Register */ +#define IMXRT_USBPHY1_PWD_CLR (IMXRT_USBPHY_BASE + IMXRT_USBPHY1_PWD_CLR_OFFSET) /* USBPHY1 USB PHY Power-Down Register Clear */ +#define IMXRT_USBPHY1_CTRL (IMXRT_USBPHY_BASE + IMXRT_USBPHY1_CTRL_OFFSET) /* USBPHY1 USB PHY General Control Register */ +#define IMXRT_USBPHY1_CTRL_CLR (IMXRT_USBPHY_BASE + IMXRT_USBPHY1_CTRL_CLR_OFFSET) /* USBPHY1 USB PHY General Control Register Clear */ + +/* Register Bit Definitions *************************************************/ + +/* USB PHY Power-Down Register */ + +#define USBPHY_PWD_RXPWDRX (1 << 20) /* Bit 20: Power-down the entire USB PHY receiver block except for the full-speed differential receiver. */ +#define USBPHY_PWD_RXPWDDIFF (1 << 19) /* Bit 19: Power-down the USB high-speed differential receiver. */ +#define USBPHY_PWD_RXPWD1PT1 (1 << 18) /* Bit 18: Power-down the USB full-speed differential receiver. */ +#define USBPHY_PWD_RXPWDENV (1 << 17) /* Bit 17: Power-down the USB high-speed receiver envelope detector (squelch signal). */ +#define USBPHY_PWD_TXPWDV2I (1 << 12) /* Bit 12: Power-down the USB PHY transmit V-to-I converter and the current mirror. */ +#define USBPHY_PWD_TXPWDIBIAS (1 << 11) /* Bit 11: Power-down the USB PHY current bias block for the transmitter. */ +#define USBPHY_PWD_TXPWDFS (1 << 10) /* Bit 10: Power-down the USB full-speed drivers. */ + +/* USB PHY General Control Register */ + +#define USBPHY_CTRL_SFTRST (1 << 31) /* Bit 31: Soft-reset */ +#define USBPHY_CTRL_CLKGATE (1 << 30) /* Bit 30: Gate UTMI clocks */ + +#endif /* __ARCH_ARM_SRC_IMXRT_HARDWARE_IMXRT_USB_PHY_H */ diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/rt105x/imxrt105x_memorymap.h b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/rt105x/imxrt105x_memorymap.h new file mode 100644 index 000000000..f6afcb4a5 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/rt105x/imxrt105x_memorymap.h @@ -0,0 +1,311 @@ +/**************************************************************************** + * arch/arm/src/imxrt/hardware/rt105x/imxrt105x_memorymap.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_ARM_SRC_IMXRT_HARDWARE_IMXRT105X_MEMORYMAP_H +#define __ARCH_ARM_SRC_IMXRT_HARDWARE_IMXRT105X_MEMORYMAP_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* System memory map */ + +#define IMXRT_ITCM_BASE 0x00000000 /* 512KB ITCM */ + + /* 0x00080000 512KB ITCM Reserved */ + + /* 0x00100000 1MB ITCM Reserved */ +#define IMXRT_ROMCP_BASE 0x00200000 /* 96KB ROMCP */ + + /* 0x00218000 416KB ROMCP Reserved */ + + /* 0x00280000 1536KB Reserved */ + + /* 0x00400000 124MB Reserved */ +#define IMXRT_FLEXSPI_BASE 0x08000000 /* 128MB FlexSPI (Aliased) */ +#define IMXRT_SEMCA_BASE 0x10000000 /* 256MB SEMC (Aliased) */ +#define IMXRT_DTCM_BASE 0x20000000 /* 512KB DTCM */ + + /* 0x20080000 512KB DTCM Reserved */ + + /* 0x20100000 1MB Reserved */ +#define IMXRT_OCRAM_BASE 0x20200000 /* 512KB OCRAM */ + + /* 0x20280000 1536KB OCRAM Reserved */ + + /* 0x20400000 252MB Reserved */ + + /* 0x30000000 256MB Reserved */ +#define IMXRT_AIPS1_BASE 0x40000000 /* 1MB AIPS-1 */ +#define IMXRT_AIPS2_BASE 0x40100000 /* 1MB AIPS-2 */ +#define IMXRT_AIPS3_BASE 0x40200000 /* 1MB AIPS-3 */ +#define IMXRT_AIPS4_BASE 0x40300000 /* 1MB AIPS-4 */ + + /* 40400000 12MB Reserved */ +#define IMXRT_MAINCNF_BASE 0x41000000 /* 1MB "main" configuration port */ +#define IMXRT_MCNF_BASE 0x41100000 /* 1MB "m" configuration port */ + + /* 41200000 1MB Reserved for "per" GPV */ + + /* 41300000 1MB Reserved for "ems" GPV */ +#define IMXRT_CPUCNF_BASE 0x41400000 /* 1MB "cpu" configuration port */ + + /* 0x41500000 1MB GPV Reserved */ + + /* 0x41600000 1MB GPV Reserved */ + + /* 0x41700000 1MB GPV Reserved */ + + /* 0x41800000 8MB Reserved */ + + /* 0x42000000 32MB Reserved */ + + /* 0x44000000 64MB Reserved */ + + /* 0x48000000 384MB Reserved */ +#define IMXRT_FLEXCIPHER_BASE 0x60000000 /* 504MB FlexSPI/ FlexSPI ciphertext */ +#define IMXRT_FLEXSPITX_BASE 0x7f800000 /* 4MB FlexSPI TX FIFO */ +#define IMXRT_FLEXSPIRX_BASE 0x7fc00000 /* 4MB FlexSPI RX FIFO */ +#define IMXRT_EXTMEM_BASE 0x80000000 /* 1.5GB SEMC external memories shared memory space */ +#define IMXRT_CM7_BASE 0xe0000000 /* 1MB CM7 PPB */ + + /* 0xe0100000 511MB Reserved */ + +/* AIPS-1 memory map */ + + /* 0x40000000 256KB Reserved */ + + /* 0x40040000 240KB Reserved */ +#define IMXRT_AIPS1CNF_BASE 0x4007c000 /* 6KB AIPS-1 Configuration */ +#define IMXRT_DCDC_BASE 0x40080000 /* 16KB DCDC */ +#define IMXRT_PIT_BASE 0x40084000 /* 16KB PIT */ + + /* 0x40088000 16KB Reserved */ + + /* 0x4008c000 16KB Reserved */ +#define IMXRT_MTR_BASE 0x40090000 /* 16KB MTR */ +#define IMXRT_ACMP_BASE 0x40094000 /* 16KB ACMP */ + + /* 0x40098000 16KB Reserved */ + + /* 0x4009c000 16KB Reserved */ + + /* 0x400a0000 16KB Reserved */ +#define IMXRT_IOMUXCSNVSGPR_BASE 0x400a4000 /* 16KB IOMUXC_SNVS_GPR */ +#define IMXRT_IOMUXCSNVS_BASE 0x400a8000 /* 16KB IOMUXC_SNVS */ +#define IMXRT_IOMUXCGPR_BASE 0x400ac000 /* 16KB IOMUXC_GPR */ +#define IMXRT_FLEXRAM_BASE 0x400b0000 /* 16KB CM7_MX6RT(FLEXRAM) */ +#define IMXRT_EWM_BASE 0x400b4000 /* 16KB EWM */ +#define IMXRT_WDOG1_BASE 0x400b8000 /* 16KB WDOG1 */ +#define IMXRT_WDOG3_BASE 0x400bc000 /* 16KB WDOG3 */ +#define IMXRT_GPIO5_BASE 0x400c0000 /* 16KB GPIO5 */ +#define IMXRT_ADC1_BASE 0x400c4000 /* 16KB ADC1 */ +#define IMXRT_ADC2_BASE 0x400c8000 /* 16KB ADC2 */ +#define IMXRT_TRNG_BASE 0x400cc000 /* 16KB TRNG */ +#define IMXRT_WDOG2_BASE 0x400d0000 /* 16KB WDOG2 */ +#define IMXRT_SNVSHP_BASE 0x400d4000 /* 16KB SNVS_HP */ +#define IMXRT_ANATOP_BASE 0x400d8000 /* 16KB ANATOP */ +#define IMXRT_CSU_BASE 0x400dc000 /* 16KB CSU */ + + /* 0x400e0000 16KB Reserved */ + + /* 0x400e4000 16KB Reserved */ +#define IMXRT_EDMA_BASE 0x400e8000 /* 16KB EDMA */ +#define IMXRT_DMAMUX_BASE 0x400ec000 /* 16KB DMA_CH_MUX */ + + /* 400f0000 16KB Reserved */ +#define IMXRT_GPC_BASE 0x400f4000 /* 16KB GPC */ +#define IMXRT_SRC_BASE 0x400f8000 /* 16KB SRC */ +#define IMXRT_CCM_BASE 0x400fc000 /* 16KB CCM */ + +/* AIPS-2 memory map */ + + /* 0x40100000 256KB Reserved */ + + /* 0x40140000 240KB Reserved */ +#define IMXRT_AIPS2CNF_BASE 0x4017c000 /* 16KB AIPS-2 Configuration */ +#define IMXRT_ROMCPC_BASE 0x40180000 /* 16KB ROMCP controller*/ +#define IMXRT_LPUART1_BASE 0x40184000 /* 16KB LPUART1 */ +#define IMXRT_LPUART2_BASE 0x40188000 /* 16KB LPUART2 */ +#define IMXRT_LPUART3_BASE 0x4018c000 /* 16KB LPUART3 */ +#define IMXRT_LPUART4_BASE 0x40190000 /* 16KB LPUART4 */ +#define IMXRT_LPUART5_BASE 0x40194000 /* 16KB LPUART5 */ +#define IMXRT_LPUART6_BASE 0x40198000 /* 16KB LPUART6 */ +#define IMXRT_LPUART7_BASE 0x4019c000 /* 16KB LPUART7 */ +#define IMXRT_LPUART8_BASE 0x401a0000 /* 16KB LPUART8 */ + + /* 0x401a4000 16KB Reserved */ + + /* 0x401a8000 16KB Reserved */ +#define IMXRT_FLEXIO1_BASE 0x401ac000 /* 16KB FlexIO1 */ +#define IMXRT_FLEXIO2_BASE 0x401b0000 /* 16KB FlexIO2 */ + + /* 0x401b4000 16KB Reserved */ +#define IMXRT_GPIO1_BASE 0x401b8000 /* 16KB GPIO1 */ +#define IMXRT_GPIO2_BASE 0x401bc000 /* 16KB GPIO2 */ +#define IMXRT_GPIO3_BASE 0x401c0000 /* 16KB GPIO3 */ +#define IMXRT_GPIO4_BASE 0x401c4000 /* 16KB GPIO4 */ + + /* 0x401c8000 16KB Reserved */ + + /* 0x401cc000 16KB Reserved */ +#define IMXRT_CAN1_BASE 0x401d0000 /* 16KB CAN1 */ +#define IMXRT_CAN2_BASE 0x401d4000 /* 16KB CAN2 */ + + /* 0x401d8000 16KB Reserved */ +#define IMXRT_QTIMER1_BASE 0x401dc000 /* 16KB QTimer1 */ +#define IMXRT_QTIMER2_BASE 0x401e0000 /* 16KB QTimer2 */ +#define IMXRT_QTIMER3_BASE 0x401e4000 /* 16KB QTimer3 */ +#define IMXRT_QTIMER4_BASE 0x401e8000 /* 16KB QTimer4 */ +#define IMXRT_GPT1_BASE 0x401ec000 /* 16KB GPT1 */ +#define IMXRT_GPT2_BASE 0x401f0000 /* 16KB GPT2 */ +#define IMXRT_OCOTP_BASE 0x401f4000 /* 16KB OCOTP */ +#define IMXRT_IOMUXC_BASE 0x401f8000 /* 16KB IOMUXC */ +#define IMXRT_KPP_BASE 0x401fc000 /* 16KB KPP */ + +/* AIPS-3 memory map */ + + /* 0x40200000 256KB Reserved */ + + /* 0x40240000 240KB Reserved */ +#define IMXRT_AIOS3CNF_BASE 0x4027c000 /* 16KB AIPS-3 Configuration */ + + /* 0x40280000 16KB Reserved */ + + /* 0x40284000 16KB Reserved */ + + /* 0x40288000 16KB Reserved */ + + /* 0x4028c000 16KB Reserved */ + + /* 0x40290000 16KB Reserved */ + + /* 0x40294000 16KB Reserved */ + + /* 0x40298000 16KB Reserved */ + + /* 0x4029c000 16KB Reserved */ + + /* 0x402a0000 16KB Reserved */ + + /* 0x402a4000 16KB Reserved */ +#define IMXRT_FLEXSPIC_BASE 0x402a8000 /* 16KB FlexSPI controller */ + + /* 0x402ac000 16KB Reserved */ + + /* 0x402b0000 16KB Reserved */ +#define IMXRT_PXP_BASE 0x402b4000 /* 16KB PXP */ +#define IMXRT_LCDIF_BASE 0x402b8000 /* 16KB LCDIF */ +#define IMXRT_CSI_BASE 0x402bc000 /* 16KB CSI */ +#define IMXRT_USDHC1_BASE 0x402c0000 /* 16KB USDHC1 */ +#define IMXRT_USDHC2_BASE 0x402c4000 /* 16KB USDHC2 */ + + /* 0x402c8000 16KB Reserved */ + + /* 0x402cc000 16KB Reserved */ + + /* 0x402d0000 16KB Reserved */ + + /* 0x402d4000 16KB Reserved */ +#define IMXRT_ENET_BASE 0x402d8000 /* 16KB ENET */ +#define IMXRT_USBPL301_BASE 0x402dc000 /* 16KB USB(PL301) */ +#define IMXRT_USB_BASE 0x402e0000 /* 16KB USB(USB) */ + + /* 0x402e4000 16KB Reserved */ + + /* 0x402e8000 16KB Reserved */ + + /* 0x402ec000 16KB Reserved */ +#define IMXRT_SEMC_BASE 0x402f0000 /* 16KB SEMC */ + + /* 0x402f4000 16KB Reserved */ + + /* 0x402f8000 16KB Reserved */ +#define IMXRT_DCP_BASE 0x402fc000 /* 16KB DCP */ + +/* AIPS-4 memory map */ + + /* 0x40300000 256KB Reserved */ + + /* 0x40340000 240KB Reserved */ +#define IMXRT_AIPS4CNF_BASE 0x4037c000 /* 16KB AIPS-4 Configuration */ +#define IMXRT_SPDIF_BASE 0x40380000 /* 16KB SPDIF */ +#define IMXRT_SAI1_BASE 0x40384000 /* 16KB SAI1 */ +#define IMXRT_SAI2_BASE 0x40388000 /* 16KB SAI2 */ +#define IMXRT_SAI3_BASE 0x4038c000 /* 16KB SAI3 */ + + /* 0x40390000 16KB Reserved */ +#define IMXRT_LPSPI1_BASE 0x40394000 /* 16KB LPSPI1 */ +#define IMXRT_LPSPI2_BASE 0x40398000 /* 16KB LPSPI2 */ +#define IMXRT_LPSPI3_BASE 0x4039c000 /* 16KB LPSPI3 */ +#define IMXRT_LPSPI4_BASE 0x403a0000 /* 16KB LPSPI4 */ + + /* 0x403a4000 16KB Reserved */ + + /* 0x403a8000 16KB Reserved */ + + /* 0x403ac000 16KB Reserved */ +#define IMXRT_ADCETC_BASE 0x403b0000 /* 16KB ADC_ETC */ +#define IMXRT_AOI1_BASE 0x403b4000 /* 16KB AOI1 */ +#define IMXRT_AOI2_BASE 0x403b8000 /* 16KB AOI2 */ +#define IMXRT_XBAR1_BASE 0x403bc000 /* 16KB XBAR1 */ +#define IMXRT_XBAR2_BASE 0x403c0000 /* 16KB XBAR2 */ +#define IMXRT_XBAR3_BASE 0x403c4000 /* 16KB XBAR3 */ +#define IMXRT_ENC1_BASE 0x403c8000 /* 16KB ENC1 */ +#define IMXRT_ENC2_BASE 0x403cc000 /* 16KB ENC2 */ +#define IMXRT_ENC3_BASE 0x403d0000 /* 16KB ENC3 */ +#define IMXRT_ENC4_BASE 0x403d4000 /* 16KB ENC4 */ + + /* 0x403d8000 16KB Reserved */ +#define IMXRT_FLEXPWM1_BASE 0x403dc000 /* 16KB FLEXPWM1 */ +#define IMXRT_FLEXPWM2_BASE 0x403e0000 /* 16KB FLEXPWM2 */ +#define IMXRT_FLEXPWM3_BASE 0x403e4000 /* 16KB FLEXPWM3 */ +#define IMXRT_FLEXPWM4_BASE 0x403e8000 /* 16KB FLEXPWM4 */ +#define IMXRT_BEE_BASE 0x403ec000 /* 16KB BEE */ +#define IMXRT_LPI2C1_BASE 0x403f0000 /* 16KB */ +#define IMXRT_LPI2C2_BASE 0x403f4000 /* 16KB LPI2C2 */ +#define IMXRT_LPI2C3_BASE 0x403f8000 /* 16KB LPI2C3 */ +#define IMXRT_LPI2C4_BASE 0x403fc000 /* 16KB LPI2C4 */ + +/* PPB memory map */ + +#define IMXRT_TPIU_BASE 0xe0040000 /* 4KB TPIU */ +#define IMXRT_ETM_BASE 0xe0041000 /* 4KB ETM */ +#define IMXRT_CTI_BASE 0xe0042000 /* 4KB CTI */ +#define IMXRT_TSGEN_BASE 0xe0043000 /* 4KB TSGEN */ +#define IMXRT_PPBRES_BASE 0xe0044000 /* 4KB PPB RES */ + + /* 0xe0045000 236KB PPB Reserved */ +#define IMXRT_MCM_BASE 0xe0080000 /* 4KB MCM */ + + /* 0xe0081000 444KB PPB Reserved */ + + /* 0xe00f0000 52KB PPB Reserved */ +#define IMXRT_SYSROM_BASE 0xe00fd000 /* 4KB SYS ROM */ +#define IMXRT_PROCROM_BASE 0xe00fe000 /* 4KB Processor ROM */ +#define IMXRT_PPBROM_BASE 0xe00ff000 /* 4KB PPB ROM */ + +#endif /* __ARCH_ARM_SRC_IMXRT_HARDWARE_IMXRT105X_MEMORYMAP_H */ diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_clockconfig.c b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_clockconfig.c new file mode 100644 index 000000000..b0aa5d62d --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_clockconfig.c @@ -0,0 +1,698 @@ +/**************************************************************************** + * arch/arm/src/imxrt/imxrt_clockconfig.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 "arm_arch.h" +#include +#include "hardware/imxrt_ccm.h" +#include "hardware/imxrt_dcdc.h" +#include "imxrt_clockconfig.h" +#include "imxrt_lcd.h" +#include "hardware/imxrt_memorymap.h" +#include "hardware/imxrt_iomuxc.h" + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define VIDEO_PLL_MIN_FREQ 650000000 +#define OSC24_FREQ 24000000 + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imxrt_lcd_clockconfig + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_LCD +static void imxrt_lcd_clockconfig(void) +{ + uint32_t reg; + uint32_t reg2; + + int post; + int pre; + + uint32_t numerator; + uint32_t denominator; + uint32_t post_divider; + uint32_t pre_divider; + uint32_t loop_divider; + uint32_t target_freq; + uint32_t freq_error; + + target_freq = (CONFIG_IMXRT_LCD_HWIDTH + + CONFIG_IMXRT_LCD_HPULSE + + CONFIG_IMXRT_LCD_HFRONTPORCH + + CONFIG_IMXRT_LCD_HBACKPORCH) * + (CONFIG_IMXRT_LCD_VHEIGHT + + CONFIG_IMXRT_LCD_VPULSE + + CONFIG_IMXRT_LCD_VFRONTPORCH + + CONFIG_IMXRT_LCD_VBACKPORCH) * + CONFIG_IMXRT_LCD_REFRESH_FREQ; + + for (post_divider = 1; post_divider < 16; post_divider <<= 1) + { + if (IMXRT_LCD_VIDEO_PLL_FREQ * post_divider >= VIDEO_PLL_MIN_FREQ) + { + break; + } + } + + loop_divider = (IMXRT_LCD_VIDEO_PLL_FREQ * post_divider) / OSC24_FREQ; + numerator = (IMXRT_LCD_VIDEO_PLL_FREQ * post_divider) - + (loop_divider * OSC24_FREQ); + denominator = OSC24_FREQ; + + /* Bypass PLL first */ + + modifyreg32(IMXRT_CCM_ANALOG_PLL_VIDEO, + CCM_ANALOG_PLL_VIDEO_BYPASS_CLK_SRC_MASK, + CCM_ANALOG_PLL_VIDEO_BYPASS | + CCM_ANALOG_PLL_VIDEO_BYPASS_CLK_SRC_REF_24M); + + putreg32(CCM_ANALOG_PLL_VIDEO_NUM_A(numerator), + IMXRT_CCM_ANALOG_PLL_VIDEO_NUM); + putreg32(CCM_ANALOG_PLL_VIDEO_DENOM_B(denominator), + IMXRT_CCM_ANALOG_PLL_VIDEO_DENOM); + + /* Set post divider: + * + * ------------------------------------------------------------------------ + * | config->postDivider | PLL_VIDEO[POST_DIV_SELECT] | MISC2[VIDEO_DIV] | + * ------------------------------------------------------------------------ + * | 1 | 2 | 0 | + * ------------------------------------------------------------------------ + * | 2 | 1 | 0 | + * ------------------------------------------------------------------------ + * | 4 | 2 | 3 | + * ------------------------------------------------------------------------ + * | 8 | 1 | 3 | + * ------------------------------------------------------------------------ + * | 16 | 0 | 3 | + * ------------------------------------------------------------------------ + */ + + reg = getreg32(IMXRT_CCM_ANALOG_PLL_VIDEO); + reg &= ~(CCM_ANALOG_PLL_VIDEO_DIV_SELECT_MASK | + CCM_ANALOG_PLL_VIDEO_POWERDOWN); + reg |= CCM_ANALOG_PLL_VIDEO_ENABLE | + CCM_ANALOG_PLL_VIDEO_DIV_SELECT(loop_divider); + + reg2 = getreg32(IMXRT_CCM_ANALOG_MISC2); + reg2 &= ~CCM_ANALOG_MISC2_VIDEO_DIV_MASK; + + switch (post_divider) + { + case 16: + reg |= CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT_DIV4; + reg2 |= CCM_ANALOG_MISC2_VIDEO_DIV(3); + break; + + case 8: + reg |= CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT_DIV2; + reg2 |= CCM_ANALOG_MISC2_VIDEO_DIV(3); + break; + + case 4: + reg |= CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT_DIV1; + reg2 |= CCM_ANALOG_MISC2_VIDEO_DIV(3); + break; + + case 2: + reg |= CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT_DIV2; + reg2 |= 0; + break; + + default: + reg |= CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT_DIV1; + reg2 |= 0; + break; + } + + putreg32(reg, IMXRT_CCM_ANALOG_PLL_VIDEO); + + putreg32(reg2, IMXRT_CCM_ANALOG_MISC2); + + while ((getreg32(IMXRT_CCM_ANALOG_PLL_VIDEO) & + CCM_ANALOG_PLL_VIDEO_LOCK) == 0) + { + } + + /* Disable Bypass */ + + modifyreg32(IMXRT_CCM_ANALOG_PLL_VIDEO, + CCM_ANALOG_PLL_VIDEO_BYPASS, + 0); + + freq_error = IMXRT_LCD_VIDEO_PLL_FREQ; + pre_divider = 0; + post_divider = 0; + + for (post = 0; post < 8; post++) + { + for (pre = 0; pre < 8; pre++) + { + int32_t temp_error; + temp_error = labs((post + 1) * (pre + 1) * target_freq - + IMXRT_LCD_VIDEO_PLL_FREQ); + if (temp_error < freq_error) + { + pre_divider = pre; + post_divider = post; + freq_error = temp_error; + } + } + } + + /* Select PLL5 as LCD Clock and set Pre divider. */ + + modifyreg32(IMXRT_CCM_CSCDR2, + CCM_CSCDR2_LCDIF_PRE_CLK_SEL_MASK | + CCM_CSCDR2_LCDIF_PRED_MASK, + CCM_CSCDR2_LCDIF_PRE_CLK_SEL_PLL5 | + CCM_CSCDR2_LCDIF_PRED(pre_divider)); + + /* Set Post divider. */ + + modifyreg32(IMXRT_CCM_CBCMR, CCM_CBCMR_LCDIF_PODF_MASK, + CCM_CBCMR_LCDIF_PODF(post_divider)); +} + +#endif + +/**************************************************************************** + * Name: imxrt_pllsetup + ****************************************************************************/ + +static void imxrt_pllsetup(void) +{ +#ifdef CONFIG_ARCH_FAMILY_IMXRT102x + uint32_t pll2reg; +#endif + uint32_t pll3reg; + uint32_t reg; + +#if (defined(CONFIG_ARCH_FAMILY_IMXRT105x) || defined (CONFIG_ARCH_FAMILY_IMXRT106x)) + /* Init Arm PLL1 */ + + reg = CCM_ANALOG_PLL_ARM_DIV_SELECT(IMXRT_ARM_PLL_DIV_SELECT) | + CCM_ANALOG_PLL_ARM_ENABLE; + putreg32(reg, IMXRT_CCM_ANALOG_PLL_ARM); + while ((getreg32(IMXRT_CCM_ANALOG_PLL_ARM) & CCM_ANALOG_PLL_ARM_LOCK) == 0) + { + } + + /* Init Sys PLL2 */ + + reg = CCM_ANALOG_PLL_SYS_DIV_SELECT(IMXRT_SYS_PLL_SELECT) | + CCM_ANALOG_PLL_SYS_ENABLE; + putreg32(reg, IMXRT_CCM_ANALOG_PLL_SYS); + while ((getreg32(IMXRT_CCM_ANALOG_PLL_SYS) & CCM_ANALOG_PLL_SYS_LOCK) == 0) + { + } + + /* Init USB PLL3 */ + + /* capture it's original value */ + + pll3reg = getreg32(IMXRT_CCM_ANALOG_PFD_480); + putreg32(pll3reg | + CCM_ANALOG_PFD_480_PFD0_CLKGATE | + CCM_ANALOG_PFD_480_PFD1_CLKGATE | + CCM_ANALOG_PFD_480_PFD2_CLKGATE | + CCM_ANALOG_PFD_480_PFD3_CLKGATE, + IMXRT_CCM_ANALOG_PFD_480); + + reg = IMXRT_USB1_PLL_DIV_SELECT | + CCM_ANALOG_PLL_USB1_ENABLE | + CCM_ANALOG_PLL_USB1_EN_USB_CLKS | + CCM_ANALOG_PLL_USB1_POWER; + putreg32(reg, IMXRT_CCM_ANALOG_PLL_USB1); + + while ((getreg32(IMXRT_CCM_ANALOG_PLL_USB1) & + CCM_ANALOG_PLL_USB1_LOCK) == 0) + { + } + + putreg32(pll3reg, IMXRT_CCM_ANALOG_PFD_480); + +#ifdef CONFIG_IMXRT_LCD + /* Init Video PLL5 */ + + imxrt_lcd_clockconfig(); +#endif + + /* Init ENET PLL6 */ + + reg = CCM_ANALOG_PLL_ENET_ENET0_DIV_SELECT_50MHZ | + CCM_ANALOG_PLL_ENET_ENET1_125M_EN | + CCM_ANALOG_PLL_ENET_ENET_25M_REF_EN | + CCM_ANALOG_PLL_ENET_ENET_500M_REF_EN | + CCM_ANALOG_PLL_ENET_ENET1_DIV_SELECT_50MHZ; + + putreg32(reg, IMXRT_CCM_ANALOG_PLL_ENET); + + while ((getreg32(IMXRT_CCM_ANALOG_PLL_ENET) & + CCM_ANALOG_PLL_ENET_LOCK) == 0) + { + } + +#elif defined(CONFIG_ARCH_FAMILY_IMXRT102x) + /* Init Sys PLL2 */ + + /* First reset its fractional dividers */ + + pll2reg = getreg32(IMXRT_CCM_ANALOG_PFD_528); + putreg32(pll2reg | + CCM_ANALOG_PFD_528_PFD0_CLKGATE | + CCM_ANALOG_PFD_528_PFD1_CLKGATE | + CCM_ANALOG_PFD_528_PFD2_CLKGATE | + CCM_ANALOG_PFD_528_PFD3_CLKGATE, + IMXRT_CCM_ANALOG_PFD_528); + + reg = CCM_ANALOG_PLL_SYS_DIV_SELECT(IMXRT_SYS_PLL_DIV_SELECT) | + CCM_ANALOG_PLL_SYS_ENABLE; + putreg32(reg, IMXRT_CCM_ANALOG_PLL_SYS); + + while ((getreg32(IMXRT_CCM_ANALOG_PLL_SYS) & + CCM_ANALOG_PLL_SYS_LOCK) == 0) + { + } + + putreg32(pll2reg, IMXRT_CCM_ANALOG_PFD_528); + + /* Init USB PLL3 */ + + /* capture it's original value */ + + pll3reg = getreg32(IMXRT_CCM_ANALOG_PFD_480); + putreg32(pll3reg | + CCM_ANALOG_PFD_480_PFD0_CLKGATE | + CCM_ANALOG_PFD_480_PFD1_CLKGATE | + CCM_ANALOG_PFD_480_PFD2_CLKGATE | + CCM_ANALOG_PFD_480_PFD3_CLKGATE, + IMXRT_CCM_ANALOG_PFD_480); + + reg = CCM_ANALOG_PLL_USB1_DIV_SELECT(IMXRT_USB1_PLL_DIV_SELECT) | + CCM_ANALOG_PLL_USB1_ENABLE | CCM_ANALOG_PLL_USB1_EN_USB_CLKS | + CCM_ANALOG_PLL_USB1_POWER; + putreg32(reg, IMXRT_CCM_ANALOG_PLL_USB1); + + while ((getreg32(IMXRT_CCM_ANALOG_PLL_USB1) & + CCM_ANALOG_PLL_USB1_LOCK) == 0) + { + } + + putreg32(pll3reg, IMXRT_CCM_ANALOG_PFD_480); + + /* Init Audio PLL4 */ + + reg = CCM_ANALOG_PLL_AUDIO_DIV_SELECT(IMXRT_AUDIO_PLL_DIV_SELECT) | + CCM_ANALOG_PLL_AUDIO_ENABLE; + putreg32(reg, IMXRT_CCM_ANALOG_PLL_AUDIO); + + while ((getreg32(IMXRT_CCM_ANALOG_PLL_AUDIO) & + CCM_ANALOG_PLL_AUDIO_LOCK) == 0) + { + } + + /* Init ENET PLL6 */ + + reg = CCM_ANALOG_PLL_ENET_ENET0_DIV_SELECT_50MHZ | + CCM_ANALOG_PLL_ENET_ENET1_125M_EN | + CCM_ANALOG_PLL_ENET_ENET_25M_REF_EN | + CCM_ANALOG_PLL_ENET_ENET_500M_REF_EN; + + putreg32(reg, IMXRT_CCM_ANALOG_PLL_ENET); + + while ((getreg32(IMXRT_CCM_ANALOG_PLL_ENET) & + CCM_ANALOG_PLL_ENET_LOCK) == 0) + { + } + +#else +# error Unrecognised IMXRT family member for clock config +#endif +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imxrt_clockconfig + * + * Description: + * Called to initialize the i.MXRT. This does whatever setup is needed to + * put the SoC in a usable state. This includes the initialization of + * clocking using the settings in board.h. + * + ****************************************************************************/ + +void imxrt_clockconfig(void) +{ + /* Don't change the current basic clock configuration if we are running + * from SDRAM. In this case, some bootloader logic has already configured + * clocking and SDRAM. We are pretty much committed to using things the + * way that the bootloader has left them. + * + * Note that although this is safe at boot while nothing is using + * the clocks additional caution is required if at some later date + * we want to manipulate the PODFs while the system is running + * (for power minimisation) because changing those is not glitch free. + */ + +#ifndef CONFIG_IMXRT_BOOT_SDRAM + uint32_t reg; + + /* Set clock mux and dividers */ + + /* Set PERIPH_CLK2 MUX to OSC */ + + reg = getreg32(IMXRT_CCM_CBCMR); + reg &= ~CCM_CBCMR_PERIPH_CLK2_SEL_MASK; + reg |= CCM_CBCMR_PERIPH_CLK2_SEL_OSC_CLK; + putreg32(reg, IMXRT_CCM_CBCMR); + + while ((getreg32(IMXRT_CCM_CDHIPR) & CCM_CDHIPR_PERIPH2_CLK_SEL_BUSY) != 0) + { + } + + /* Set PERIPH_CLK MUX to PERIPH_CLK2 */ + + reg = getreg32(IMXRT_CCM_CBCDR); + reg &= ~CCM_CBCDR_PERIPH_CLK_SEL_MASK; + reg |= CCM_CBCDR_PERIPH_CLK_SEL(CCM_CBCDR_PERIPH_CLK_SEL_PERIPH_CLK2); + putreg32(reg, IMXRT_CCM_CBCDR); + while ((getreg32(IMXRT_CCM_CDHIPR) & CCM_CDHIPR_PERIPH_CLK_SEL_BUSY) != 0) + { + } + + /* Set Soc VDD and wait for it to stablise */ + + reg = getreg32(IMXRT_DCDC_REG3); + reg &= ~(DCDC_REG3_TRG_MASK); + reg |= DCDC_REG3_TRG(IMXRT_VDD_SOC); + putreg32(reg, IMXRT_DCDC_REG3); + while ((getreg32(IMXRT_DCDC_REG0) & DCDC_REG0_STS_DC_OK) == 0) + { + } + + /* OK, now nothing is depending on us, configure the PLLs */ + + imxrt_pllsetup(); + + /* Set Dividers */ + + reg = getreg32(IMXRT_CCM_CACRR); + reg &= ~CCM_CACRR_ARM_PODF_MASK; + reg |= CCM_CACRR_ARM_PODF(CCM_PODF_FROM_DIVISOR(IMXRT_ARM_PODF_DIVIDER)); + putreg32(reg, IMXRT_CCM_CACRR); + while ((getreg32(IMXRT_CCM_CDHIPR) & CCM_CDHIPR_ARM_PODF_BUSY) != 0) + { + } + + reg = getreg32(IMXRT_CCM_CBCDR); + reg &= ~CCM_CBCDR_AHB_PODF_MASK; + reg |= CCM_CBCDR_AHB_PODF(CCM_PODF_FROM_DIVISOR(IMXRT_AHB_PODF_DIVIDER)); + putreg32(reg, IMXRT_CCM_CBCDR); + while ((getreg32(IMXRT_CCM_CDHIPR) & CCM_CDHIPR_AHB_PODF_BUSY) != 0) + { + } + + /* Adjust IPG and PERCLK PODFs. Consumers of these clocks will need to + * be gated if there are any (there aren't at boot). + */ + + reg = getreg32(IMXRT_CCM_CBCDR); + reg &= ~CCM_CBCDR_IPG_PODF_MASK; + reg |= CCM_CBCDR_IPG_PODF(CCM_PODF_FROM_DIVISOR(IMXRT_IPG_PODF_DIVIDER)); + putreg32(reg, IMXRT_CCM_CBCDR); + + reg = getreg32(IMXRT_CCM_CSCMR1); + reg &= ~CCM_CSCMR1_PERCLK_PODF_MASK; + reg |= CCM_CSCMR1_PERCLK_PODF( + CCM_PODF_FROM_DIVISOR(IMXRT_PERCLK_PODF_DIVIDER)); + putreg32(reg, IMXRT_CCM_CSCMR1); + +#ifndef CONFIG_IMXRT_SEMC_INIT_DONE + /* Configure SEMC Clock only if not already done by DCD SDR */ + + reg = getreg32(IMXRT_CCM_CBCDR); + reg &= ~CCM_CBCDR_SEMC_PODF_MASK; + reg |= CCM_CBCDR_SEMC_PODF(CCM_PODF_FROM_DIVISOR(IMXRT_SEMC_PODF_DIVIDER)); + putreg32(reg, IMXRT_CCM_CBCDR); + + while ((getreg32(IMXRT_CCM_CDHIPR) & CCM_CDHIPR_SEMC_PODF_BUSY) != 0) + { + } +#endif + + /* Set PRE_PERIPH_CLK to Board Selection */ + + reg = getreg32(IMXRT_CCM_CBCMR); + reg &= ~CCM_CBCMR_PRE_PERIPH_CLK_SEL_MASK; + reg |= CCM_CBCMR_PRE_PERIPH_CLK_SEL(IMXRT_PRE_PERIPH_CLK_SEL); + putreg32(reg, IMXRT_CCM_CBCMR); + + /* Set PERIPH_CLK MUX to Board Selection */ + + reg = getreg32(IMXRT_CCM_CBCDR); + reg &= ~CCM_CBCDR_PERIPH_CLK_SEL_MASK; + reg |= CCM_CBCDR_PERIPH_CLK_SEL(IMXRT_PERIPH_CLK_SEL); + putreg32(reg, IMXRT_CCM_CBCDR); + + /* Wait handshake */ + + while ((getreg32(IMXRT_CCM_CDHIPR) & CCM_CDHIPR_PERIPH_CLK_SEL_BUSY) != 0) + { + } + + /* Set PERCLK_CLK_SEL to Board Selection */ + + reg = getreg32(IMXRT_CCM_CSCMR1); + reg &= ~CCM_CSCMR1_PERCLK_CLK_SEL_MASK; + reg |= CCM_CSCMR1_PERCLK_CLK_SEL(IMXRT_PERCLK_CLK_SEL); + putreg32(reg, IMXRT_CCM_CSCMR1); + + while ((getreg32(IMXRT_CCM_CDHIPR) & CCM_CDHIPR_PERIPH_CLK_SEL_BUSY) != 0) + { + } + + /* Setup perhiperals. At this point these are not activated so don't + * need to worry too much about switching off the clock feeds. + */ + + /* Set UART source to PLL3 80M */ + + reg = getreg32(IMXRT_CCM_CSCDR1); + reg &= ~CCM_CSCDR1_UART_CLK_SEL; + reg |= CCM_CSCDR1_UART_CLK_SEL_PLL3_80; + putreg32(reg, IMXRT_CCM_CSCDR1); + + /* Set UART divider to 1 */ + + reg = getreg32(IMXRT_CCM_CSCDR1); + reg &= ~CCM_CSCDR1_UART_CLK_PODF_MASK; + reg |= CCM_CSCDR1_UART_CLK_PODF(CCM_PODF_FROM_DIVISOR(1)); + putreg32(reg, IMXRT_CCM_CSCDR1); + +#ifdef CONFIG_IMXRT_FLEXIO1 +#ifdef CONFIG_ARCH_FAMILY_IMXRT102x + /* Set FlEXIO1 source */ + + reg = getreg32(IMXRT_CCM_CSCMR2); + reg &= ~CCM_CSCMR2_FLEXIO1_CLK_SEL_MASK; + reg |= CCM_CSCMR2_FLEXIO1_CLK_SEL(CONFIG_FLEXIO1_CLK); + putreg32(reg, IMXRT_CCM_CSCMR2); + + /* Set FlEXIO1 divider */ + + reg = getreg32(IMXRT_CCM_CS1CDR); + reg &= ~(CCM_CS1CDR_FLEXIO1_CLK_PODF_MASK | \ + CCM_CS1CDR_FLEXIO1_CLK_PRED_MASK); + reg |= CCM_CS1CDR_FLEXIO1_CLK_PODF + (CCM_PODF_FROM_DIVISOR(CONFIG_FLEXIO1_PODF_DIVIDER)); + reg |= CCM_CS1CDR_FLEXIO1_CLK_PRED + (CCM_PRED_FROM_DIVISOR(CONFIG_FLEXIO1_PRED_DIVIDER)); + putreg32(reg, IMXRT_CCM_CS1CDR); + +#elif (defined(CONFIG_ARCH_FAMILY_IMXRT105x) || defined(CONFIG_ARCH_FAMILY_IMXRT106x)) + /* Set FlEXIO1 source & divider */ + + reg = getreg32(IMXRT_CCM_CDCDR); + reg &= ~(CCM_CDCDR_FLEXIO1_CLK_SEL_MASK | + CCM_CDCDR_FLEXIO1_CLK_PODF_MASK | + CCM_CDCDR_FLEXIO1_CLK_PRED_MASK); + reg |= CCM_CDCDR_FLEXIO1_CLK_SEL(CONFIG_FLEXIO1_CLK); + reg |= CCM_CDCDR_FLEXIO1_CLK_PODF + (CCM_PODF_FROM_DIVISOR(CONFIG_FLEXIO1_PODF_DIVIDER)); + reg |= CCM_CDCDR_FLEXIO1_CLK_PRED + (CCM_PRED_FROM_DIVISOR(CONFIG_FLEXIO1_PRED_DIVIDER)); + putreg32(reg, IMXRT_CCM_CDCDR); + +#endif /* CONFIG_ARCH_FAMILY_IMXRT102x */ +#endif /* CONFIG_IMXRT_FLEXIO1 */ + +#if (defined(CONFIG_IMXRT_FLEXIO2) || defined(CONFIG_IMXRT_FLEXIO3)) + /* Set FlEXIO2 source */ + + reg = getreg32(IMXRT_CCM_CSCMR2); + reg &= ~CCM_CSCMR2_FLEXIO2_CLK_SEL_MASK; + reg |= CCM_CSCMR2_FLEXIO2_CLK_SEL(CONFIG_FLEXIO2_CLK); + putreg32(reg, IMXRT_CCM_CSCMR2); + + /* Set FlEXIO2 divider */ + + reg = getreg32(IMXRT_CCM_CS1CDR); + reg &= ~(CCM_CS1CDR_FLEXIO2_CLK_PODF_MASK | \ + CCM_CS1CDR_FLEXIO2_CLK_PRED_MASK); + reg |= CCM_CS1CDR_FLEXIO2_CLK_PODF + (CCM_PODF_FROM_DIVISOR(CONFIG_FLEXIO2_PODF_DIVIDER)); + reg |= CCM_CS1CDR_FLEXIO2_CLK_PRED + (CCM_PRED_FROM_DIVISOR(CONFIG_FLEXIO2_PRED_DIVIDER)); + putreg32(reg, IMXRT_CCM_CS1CDR); + +#endif /* CONFIG_IMXRT_FLEXIO2 */ + +#ifdef CONFIG_IMXRT_LPI2C + /* Set LPI2C source to PLL3 60M */ + + reg = getreg32(IMXRT_CCM_CSCDR2); + reg &= ~CCM_CSCDR2_LPI2C_CLK_SEL; + reg |= IMXRT_LPI2C_CLK_SELECT; + putreg32(reg, IMXRT_CCM_CSCDR2); + + /* Set LPI2C divider to 5 for 12 MHz */ + + reg = getreg32(IMXRT_CCM_CSCDR2); + reg &= ~CCM_CSCDR2_LPI2C_CLK_PODF_MASK; + reg |= CCM_CSCDR2_LPI2C_CLK_PODF( + CCM_PODF_FROM_DIVISOR(IMXRT_LSI2C_PODF_DIVIDER) + ); + putreg32(reg, IMXRT_CCM_CSCDR2); + +#endif + +#ifdef CONFIG_IMXRT_FLEXCAN + /* Set FlexCAN clock source to PLL3 80M */ + + reg = getreg32(IMXRT_CCM_CSCMR2); + reg &= ~CCM_CSCMR2_CAN_CLK_SEL_MASK; + reg |= IMXRT_CAN_CLK_SELECT; + putreg32(reg, IMXRT_CCM_CSCMR2); + + /* Set FlexCAN dividet to 1 for 80 MHz */ + + reg = getreg32(IMXRT_CCM_CSCMR2); + reg &= ~CCM_CSCMR2_CAN_CLK_PODF_MASK; + reg |= CCM_CSCMR2_CAN_CLK_PODF( + CCM_PODF_FROM_DIVISOR(IMXRT_CAN_PODF_DIVIDER) + ); + putreg32(reg, IMXRT_CCM_CSCMR2); + +#endif + +#ifdef CONFIG_IMXRT_LPSPI + /* Set LPSPI clock source to PLL3 PFD0 */ + + reg = getreg32(IMXRT_CCM_CBCMR); + reg &= ~CCM_CBCMR_LPSPI_CLK_SEL_MASK; + reg |= IMXRT_LPSPI_CLK_SELECT; + putreg32(reg, IMXRT_CCM_CBCMR); + + /* Set LPSPI divider to IMXRT_LSPI_PODF_DIVIDER */ + + reg = getreg32(IMXRT_CCM_CBCMR); + reg &= ~CCM_CBCMR_LPSPI_PODF_MASK; + reg |= CCM_CBCMR_LPSPI_PODF( + CCM_PODF_FROM_DIVISOR(IMXRT_LSPI_PODF_DIVIDER) + ); + putreg32(reg, IMXRT_CCM_CBCMR); +#endif + +#ifdef IMXRT_TRACE_PODF_DIVIDER + /* Set TRACE clock source and speed */ + + reg = getreg32(IMXRT_CCM_CBCMR); + reg &= ~CCM_CBCMR_TRACE_CLK_SEL_MASK; + reg |= IMXRT_TRACE_CLK_SELECT; + putreg32(reg, IMXRT_CCM_CBCMR); + + reg = getreg32(IMXRT_CCM_CSCDR1); + reg &= ~CCM_CSCDR1_TRACE_PODF_MASK; + reg |= CCM_CSCDR1_TRACE_PODF( + CCM_PODF_FROM_DIVISOR(IMXRT_TRACE_PODF_DIVIDER)); + putreg32(reg, IMXRT_CCM_CSCDR1); +#endif + +#ifdef CONFIG_IMXRT_USDHC + /* Optionally set USDHC1 & 2 to generate clocks + * from IMXRT_USDHC1_CLK_SELECT + */ + + reg = getreg32(IMXRT_CCM_CSCMR1); + reg &= ~(CCM_CSCMR1_USDHC1_CLK_SEL | CCM_CSCMR1_USDHC2_CLK_SEL); +#if defined(IMXRT_USDHC1_CLK_SELECT) + reg |= IMXRT_USDHC1_CLK_SELECT; +#endif +#if defined(IMXRT_USDHC2_CLK_SELECT) + reg |= IMXRT_USDHC2_CLK_SELECT; +#endif + putreg32(reg, IMXRT_CCM_CSCMR1); + + /* Now divide down clocks by IMXRT_USDHC[1|2]_PODF_DIVIDER */ + + reg = getreg32(IMXRT_CCM_CSCDR1); + reg &= ~(CCM_CSCDR1_USDHC1_PODF_MASK | CCM_CSCDR1_USDHC2_PODF_MASK); +#if defined(IMXRT_USDHC1_PODF_DIVIDER) + reg |= CCM_CSCDR1_USDHC1_PODF( + CCM_PODF_FROM_DIVISOR(IMXRT_USDHC1_PODF_DIVIDER)); +#endif +#if defined(IMXRT_USDHC2_PODF_DIVIDER) + reg |= CCM_CSCDR1_USDHC2_PODF( + CCM_PODF_FROM_DIVISOR(IMXRT_USDHC2_PODF_DIVIDER)); +#endif + putreg32(reg, IMXRT_CCM_CSCDR1); +#endif + + /* Ensure platform memory clocks remain enabled in WFI */ + + reg = getreg32(IMXRT_CCM_CGPR); + reg |= CCM_CGPR_INT_MEM_CLK_LPM; + putreg32(reg, IMXRT_CCM_CGPR); + + /* Remain in run mode */ + + modifyreg32(IMXRT_CCM_CLPCR, + CCM_CLPCR_LPM_MASK, + CCM_CLPCR_LPM_RUN); +#endif +} diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_ehci.c b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_ehci.c new file mode 100644 index 000000000..ff83ee178 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_ehci.c @@ -0,0 +1,5452 @@ +/**************************************************************************** + * arch/arm/src/imxrt/imxrt_ehci.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 + +#include "arm_arch.h" +#include "chip.h" +#include "hardware/imxrt_usbotg.h" +#include "imxrt_periphclks.h" + +#if defined(CONFIG_IMXRT_USBOTG) && defined(CONFIG_USBHOST) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +/* Pre-requisites */ + +#if !defined(CONFIG_SCHED_WORKQUEUE) +# error Work queue support is required (CONFIG_SCHED_WORKQUEUE) +#elif !defined(CONFIG_SCHED_HPWORK) +# error Hi-priority work queue support is required (CONFIG_SCHED_HPWORK) +#endif + +/* Configurable number of Queue Head (QH) structures. The default is one per + * Root hub port plus one for EP0. + */ + +#ifndef CONFIG_IMXRT_EHCI_NQHS +# define CONFIG_IMXRT_EHCI_NQHS (IMXRT_EHCI_NRHPORT + 1) +#endif + +/* Configurable number of Queue Element Transfer Descriptor (qTDs). The + * default is one per root hub plus three from EP0. + */ + +#ifndef CONFIG_IMXRT_EHCI_NQTDS +# define CONFIG_IMXRT_EHCI_NQTDS (IMXRT_EHCI_NRHPORT + 3) +#endif + +/* Buffers must be aligned to the cache line size */ + +#define DCACHE_LINEMASK (ARMV7M_DCACHE_LINESIZE -1) + +/* Configurable size of a request/descriptor buffers */ + +#ifndef CONFIG_IMXRT_EHCI_BUFSIZE +# define CONFIG_IMXRT_EHCI_BUFSIZE 128 +#endif + +#define IMXRT_EHCI_BUFSIZE \ + ((CONFIG_IMXRT_EHCI_BUFSIZE + DCACHE_LINEMASK) & ~DCACHE_LINEMASK) + +/* Debug options */ + +#ifndef CONFIG_DEBUG_USB_INFO +# undef CONFIG_IMXRT_EHCI_REGDEBUG +#endif + +/* Isochronous transfers are not currently supported */ + +#undef CONFIG_USBHOST_ISOC_DISABLE +#define CONFIG_USBHOST_ISOC_DISABLE 1 + +/* Registers **************************************************************** + * Traditionally, NuttX specifies register locations using individual + * register offsets from a base address. That tradition is broken here and, + * instead, register blocks are represented as structures. This is done here + * because, in principle, EHCI operational register address may not be known + * at compile time; the operational registers lie at an offset specified in + * the 'caplength' byte of the Host Controller Capability Registers. + * + * However, for the case of the IMXRT EHCI, we know apriori that locations + * of these register blocks. + */ + +/* Host Controller Capability Registers */ + +#define HCCR ((struct ehci_hccr_s *)IMXRT_USBOTG_HCCR_BASE) + +/* Host Controller Operational Registers */ + +#define HCOR ((volatile struct ehci_hcor_s *)IMXRT_USBOTG_HCOR_BASE) + +/* Interrupts *************************************************************** + * This is the set of interrupts handled by this driver. + */ + +#define EHCI_HANDLED_INTS (EHCI_INT_USBINT | EHCI_INT_USBERRINT | \ + EHCI_INT_PORTSC | EHCI_INT_SYSERROR | \ + EHCI_INT_AAINT) + +/* The periodic frame list is a 4K-page aligned array of Frame List Link + * pointers. The length of the frame list may be programmable. The + * programmability of the periodic frame list is exported to system software + * via the HCCPARAMS register. If non-programmable, the length is 1024 + * elements. If programmable, the length can be selected by system software + * as one of 256, 512, or 1024 elements. + */ + +#define FRAME_LIST_SIZE 1024 + +/* DMA **********************************************************************/ + +/* For now, we are assuming an identity mapping between physical and virtual + * address spaces. + */ + +#define imxrt_physramaddr(a) (a) +#define imxrt_virtramaddr(a) (a) + +/* USB trace ****************************************************************/ + +#ifdef HAVE_USBHOST_TRACE +# define TR_FMT1 false +# define TR_FMT2 true + +# define TRENTRY(id,fmt1,string) {string} + +# define TRACE1_FIRST ((int)__TRACE1_BASEVALUE + 1) +# define TRACE1_INDEX(id) ((int)(id) - TRACE1_FIRST) +# define TRACE1_NSTRINGS TRACE1_INDEX(__TRACE1_NSTRINGS) + +# define TRACE2_FIRST ((int)__TRACE1_NSTRINGS + 1) +# define TRACE2_INDEX(id) ((int)(id) - TRACE2_FIRST) +# define TRACE2_NSTRINGS TRACE2_INDEX(__TRACE2_NSTRINGS) +#endif + +/* Port numbers */ + +#define RHPNDX(rh) ((rh)->hport.hport.port) +#define RHPORT(rh) (RHPNDX(rh)+1) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Internal representation of the EHCI Queue Head (QH) */ + +struct imxrt_epinfo_s; +struct imxrt_qh_s +{ + /* Fields visible to hardware */ + + struct ehci_qh_s hw; /* Hardware representation of the queue head */ + + /* Internal fields used by the EHCI driver */ + + struct imxrt_epinfo_s *epinfo; /* Endpoint used for the transfer */ + uint32_t fqp; /* First qTD in the list (physical address) */ + uint8_t pad[8]; /* Padding to assure 32-byte alignment */ +}; + +/* Internal representation of the EHCI Queue Element Transfer Descriptor + * (qTD) + */ + +struct imxrt_qtd_s +{ + /* Fields visible to hardware */ + + struct ehci_qtd_s hw; /* Hardware representation of the queue head */ + + /* Internal fields used by the EHCI driver */ +}; + +/* The following is used to manage lists of free QHs and qTDs */ + +struct imxrt_list_s +{ + struct imxrt_list_s *flink; /* Link to next entry in the list + * Variable length entry data follows + */ +}; + +/* List traversal call-out functions */ + +typedef int (*foreach_qh_t)(struct imxrt_qh_s *qh, uint32_t **bp, + void *arg); +typedef int (*foreach_qtd_t)(struct imxrt_qtd_s *qtd, uint32_t **bp, + void *arg); + +/* This structure describes one endpoint. */ + +struct imxrt_epinfo_s +{ + uint8_t epno:7; /* Endpoint number */ + uint8_t dirin:1; /* 1:IN endpoint 0:OUT endpoint */ + uint8_t devaddr:7; /* Device address */ + uint8_t toggle:1; /* Next data toggle */ +#ifndef CONFIG_USBHOST_INT_DISABLE + uint8_t interval; /* Polling interval */ +#endif + uint8_t status; /* Retained token status bits (for debug purposes) */ + volatile bool iocwait; /* TRUE: Thread is waiting for transfer completion */ + uint16_t maxpacket:11; /* Maximum packet size */ + uint16_t xfrtype:2; /* See USB_EP_ATTR_XFER_* definitions in usb.h */ + uint16_t speed:2; /* See USB_*_SPEED definitions in ehci.h */ + int result; /* The result of the transfer */ + uint32_t xfrd; /* On completion, will hold the number of bytes transferred */ + sem_t iocsem; /* Semaphore used to wait for transfer completion */ +#ifdef CONFIG_USBHOST_ASYNCH + usbhost_asynch_t callback; /* Transfer complete callback */ + void *arg; /* Argument that accompanies the callback */ +#endif +}; + +/* This structure retains the state of one root hub port */ + +struct imxrt_rhport_s +{ + /* Common device fields. This must be the first thing defined in the + * structure so that it is possible to simply cast from struct usbhost_s + * to struct imxrt_rhport_s. + */ + + struct usbhost_driver_s drvr; + + /* Root hub port status */ + + volatile bool connected; /* Connected to device */ + volatile bool lowspeed; /* Low speed device attached */ + struct imxrt_epinfo_s ep0; /* EP0 endpoint info */ + + /* This is the hub port description understood by class drivers */ + + struct usbhost_roothubport_s hport; +}; + +/* This structure retains the overall state of the USB host controller */ + +struct imxrt_ehci_s +{ + volatile bool pscwait; /* TRUE: Thread is waiting for port status change event */ + + sem_t exclsem; /* Support mutually exclusive access */ + sem_t pscsem; /* Semaphore to wait for port status change events */ + + struct imxrt_epinfo_s ep0; /* Endpoint 0 */ + struct imxrt_list_s *qhfree; /* List of free Queue Head (QH) structures */ + struct imxrt_list_s *qtdfree; /* List of free Queue Element Transfer Descriptor (qTD) */ + struct work_s work; /* Supports interrupt bottom half */ + +#ifdef CONFIG_USBHOST_HUB + /* Used to pass external hub port events */ + + volatile struct usbhost_hubport_s *hport; +#endif + + /* Root hub ports */ + + struct imxrt_rhport_s rhport[IMXRT_EHCI_NRHPORT]; +}; + +#ifdef HAVE_USBHOST_TRACE +/* USB trace codes */ + +enum usbhost_trace1codes_e +{ + __TRACE1_BASEVALUE = 0, /* This will force the first value to be 1 */ + + EHCI_TRACE1_SYSTEMERROR, /* EHCI ERROR: System error */ + EHCI_TRACE1_QTDFOREACH_FAILED, /* EHCI ERROR: imxrt_qtd_foreach failed */ + EHCI_TRACE1_QHALLOC_FAILED, /* EHCI ERROR: Failed to allocate a QH */ + EHCI_TRACE1_BUFTOOBIG, /* EHCI ERROR: Buffer too big */ + EHCI_TRACE1_REQQTDALLOC_FAILED, /* EHCI ERROR: Failed to allocate request qTD */ + EHCI_TRACE1_ADDBPL_FAILED, /* EHCI ERROR: imxrt_qtd_addbpl failed */ + EHCI_TRACE1_DATAQTDALLOC_FAILED, /* EHCI ERROR: Failed to allocate data buffer qTD */ + EHCI_TRACE1_DEVDISCONNECTED, /* EHCI ERROR: Device disconnected */ + EHCI_TRACE1_QHCREATE_FAILED, /* EHCI ERROR: imxrt_qh_create failed */ + EHCI_TRACE1_QTDSETUP_FAILED, /* EHCI ERROR: imxrt_qtd_setupphase failed */ + + EHCI_TRACE1_QTDDATA_FAILED, /* EHCI ERROR: imxrt_qtd_dataphase failed */ + EHCI_TRACE1_QTDSTATUS_FAILED, /* EHCI ERROR: imxrt_qtd_statusphase failed */ + EHCI_TRACE1_TRANSFER_FAILED, /* EHCI ERROR: Transfer failed */ + EHCI_TRACE1_QHFOREACH_FAILED, /* EHCI ERROR: imxrt_qh_foreach failed: */ + EHCI_TRACE1_SYSERR_INTR, /* EHCI: Host System Error Interrupt */ + EHCI_TRACE1_USBERR_INTR, /* EHCI: USB Error Interrupt (USBERRINT) Interrupt */ + EHCI_TRACE1_EPALLOC_FAILED, /* EHCI ERROR: Failed to allocate EP info structure */ + EHCI_TRACE1_BADXFRTYPE, /* EHCI ERROR: Support for transfer type not implemented */ + EHCI_TRACE1_HCHALTED_TIMEOUT, /* EHCI ERROR: Timed out waiting for HCHalted */ + EHCI_TRACE1_QHPOOLALLOC_FAILED, /* EHCI ERROR: Failed to allocate the QH pool */ + + EHCI_TRACE1_QTDPOOLALLOC_FAILED, /* EHCI ERROR: Failed to allocate the qTD pool */ + EHCI_TRACE1_PERFLALLOC_FAILED, /* EHCI ERROR: Failed to allocate the periodic frame list */ + EHCI_TRACE1_RESET_FAILED, /* EHCI ERROR: imxrt_reset failed */ + EHCI_TRACE1_RUN_FAILED, /* EHCI ERROR: EHCI Failed to run */ + EHCI_TRACE1_IRQATTACH_FAILED, /* EHCI ERROR: Failed to attach IRQ */ + +#ifdef HAVE_USBHOST_TRACE_VERBOSE + EHCI_VTRACE1_PORTSC_CSC, /* EHCI Connect Status Change */ + EHCI_VTRACE1_PORTSC_CONNALREADY, /* EHCI Already connected */ + EHCI_VTRACE1_PORTSC_DISCALREADY, /* EHCI Already disconnected */ + EHCI_VTRACE1_TOPHALF, /* EHCI Interrupt top half */ + EHCI_VTRACE1_AAINTR, /* EHCI Async Advance Interrupt */ + + EHCI_VTRACE1_CLASSENUM, /* EHCI Hub port CLASS enumeration */ + EHCI_VTRACE1_USBINTR, /* EHCI USB Interrupt (USBINT) Interrupt */ + EHCI_VTRACE1_ENUM_DISCONN, /* EHCI Enumeration not connected */ + EHCI_VTRACE1_INITIALIZING, /* EHCI Initializing EHCI Stack */ + EHCI_VTRACE1_HCCPARAMS, /* EHCI HCCPARAMS */ + EHCI_VTRACE1_INIITIALIZED, /* EHCI USB EHCI Initialized */ +#endif + + __TRACE1_NSTRINGS, /* Separates the format 1 from the format 2 strings */ + + EHCI_TRACE2_EPSTALLED, /* EHCI EP Stalled */ + EHCI_TRACE2_EPIOERROR, /* EHCI ERROR: EP TOKEN */ + EHCI_TRACE2_CLASSENUM_FAILED, /* EHCI usbhost_enumerate() failed */ + +#ifdef HAVE_USBHOST_TRACE_VERBOSE + EHCI_VTRACE2_ASYNCXFR, /* EHCI Async transfer */ + EHCI_VTRACE2_INTRXFR, /* EHCI Interrupt Transfer */ + EHCI_VTRACE2_IOCCHECK, /* EHCI IOC */ + EHCI_VTRACE2_PORTSC, /* EHCI PORTSC */ + EHCI_VTRACE2_PORTSC_CONNECTED, /* EHCI RHPort connected */ + EHCI_VTRACE2_PORTSC_DISCONND, /* EHCI RHport disconnected */ + EHCI_VTRACE2_MONWAKEUP, /* EHCI RHPort connected wakeup */ + + EHCI_VTRACE2_EPALLOC, /* EHCI EPALLOC */ + EHCI_VTRACE2_CTRLINOUT, /* EHCI CTRLIN/OUT */ + EHCI_VTRACE2_HCIVERSION, /* EHCI HCIVERSION */ + EHCI_VTRACE2_HCSPARAMS, /* EHCI HCSPARAMS */ +#endif + + __TRACE2_NSTRINGS /* Total number of enumeration values */ +}; + +/* USB trace data structure */ + +struct imxrt_ehci_trace_s +{ +#if 0 + uint16_t id; + bool fmt2; +#endif + FAR const char *string; +}; + +#endif /* HAVE_USBHOST_TRACE */ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Register operations ******************************************************/ + +static uint16_t imxrt_read16(const uint8_t *addr); +static uint32_t imxrt_read32(const uint8_t *addr); +#if 0 /* Not used */ +static void imxrt_write16(uint16_t memval, uint8_t *addr); +static void imxrt_write32(uint32_t memval, uint8_t *addr); +#endif + +#ifdef CONFIG_ENDIAN_BIG +static uint16_t imxrt_swap16(uint16_t value); +static uint32_t imxrt_swap32(uint32_t value); +#else +# define imxrt_swap16(value) (value) +# define imxrt_swap32(value) (value) +#endif + +#ifdef CONFIG_IMXRT_EHCI_REGDEBUG +static void imxrt_printreg(volatile uint32_t *regaddr, uint32_t regval, + bool iswrite); +static void imxrt_checkreg(volatile uint32_t *regaddr, uint32_t regval, + bool iswrite); +static uint32_t imxrt_getreg(volatile uint32_t *regaddr); +static void imxrt_putreg(uint32_t regval, volatile uint32_t *regaddr); +#else +static inline uint32_t imxrt_getreg(volatile uint32_t *regaddr); +static inline void imxrt_putreg(uint32_t regval, volatile uint32_t *regaddr); +#endif +static int ehci_wait_usbsts(uint32_t maskbits, uint32_t donebits, + unsigned int delay); + +/* Semaphores ***************************************************************/ + +static int imxrt_takesem(sem_t *sem); +static int imxrt_takesem_noncancelable(sem_t *sem); +#define imxrt_givesem(s) nxsem_post(s); + +/* Allocators ***************************************************************/ + +static struct imxrt_qh_s *imxrt_qh_alloc(void); +static void imxrt_qh_free(struct imxrt_qh_s *qh); +static struct imxrt_qtd_s *imxrt_qtd_alloc(void); +static void imxrt_qtd_free(struct imxrt_qtd_s *qtd); + +/* List Management **********************************************************/ + +static int imxrt_qh_foreach(struct imxrt_qh_s *qh, uint32_t **bp, + foreach_qh_t handler, void *arg); +static int imxrt_qtd_foreach(struct imxrt_qh_s *qh, foreach_qtd_t handler, + void *arg); +static int imxrt_qtd_discard(struct imxrt_qtd_s *qtd, uint32_t **bp, + void *arg); +static int imxrt_qh_discard(struct imxrt_qh_s *qh); + +/* Cache Operations *********************************************************/ + +#if 0 /* Not used */ +static int imxrt_qtd_invalidate(struct imxrt_qtd_s *qtd, uint32_t **bp, + void *arg); +static int imxrt_qh_invalidate(struct imxrt_qh_s *qh); +#endif +static int imxrt_qtd_flush(struct imxrt_qtd_s *qtd, uint32_t **bp, + void *arg); +static int imxrt_qh_flush(struct imxrt_qh_s *qh); + +/* Endpoint Transfer Handling ***********************************************/ + +#ifdef CONFIG_IMXRT_EHCI_REGDEBUG +static void imxrt_qtd_print(struct imxrt_qtd_s *qtd); +static void imxrt_qh_print(struct imxrt_qh_s *qh); +static int imxrt_qtd_dump(struct imxrt_qtd_s *qtd, uint32_t **bp, void *arg); +static int imxrt_qh_dump(struct imxrt_qh_s *qh, uint32_t **bp, void *arg); +#else +# define imxrt_qtd_print(qtd) +# define imxrt_qh_print(qh) +# define imxrt_qtd_dump(qtd, bp, arg) OK +# define imxrt_qh_dump(qh, bp, arg) OK +#endif + +static inline uint8_t imxrt_ehci_speed(uint8_t usbspeed); +static int imxrt_ioc_setup(struct imxrt_rhport_s *rhport, + struct imxrt_epinfo_s *epinfo); +static int imxrt_ioc_wait(struct imxrt_epinfo_s *epinfo); +static void imxrt_qh_enqueue(struct imxrt_qh_s *qhead, + struct imxrt_qh_s *qh); +static struct imxrt_qh_s *imxrt_qh_create(struct imxrt_rhport_s *rhport, + struct imxrt_epinfo_s *epinfo); +static int imxrt_qtd_addbpl(struct imxrt_qtd_s *qtd, const void *buffer, + size_t buflen); +static struct imxrt_qtd_s *imxrt_qtd_setupphase( + struct imxrt_epinfo_s *epinfo, const struct usb_ctrlreq_s *req); +static struct imxrt_qtd_s *imxrt_qtd_dataphase(struct imxrt_epinfo_s *epinfo, + void *buffer, int buflen, uint32_t tokenbits); +static struct imxrt_qtd_s *imxrt_qtd_statusphase(uint32_t tokenbits); +static ssize_t imxrtimxrt_virtramaddr_async_setup( + struct imxrt_rhport_s *rhport, struct imxrt_epinfo_s *epinfo, + const struct usb_ctrlreq_s *req, uint8_t *buffer, size_t buflen); +#ifndef CONFIG_USBHOST_INT_DISABLE +static int imxrt_intr_setup(struct imxrt_rhport_s *rhport, + struct imxrt_epinfo_s *epinfo, uint8_t *buffer, size_t buflen); +#endif +static ssize_t imxrt_transfer_wait(struct imxrt_epinfo_s *epinfo); +#ifdef CONFIG_USBHOST_ASYNCH +static inline int imxrt_ioc_async_setup(struct imxrt_rhport_s *rhport, + struct imxrt_epinfo_s *epinfo, usbhost_asynch_t callback, + FAR void *arg); +static void imxrt_asynch_completion(struct imxrt_epinfo_s *epinfo); +#endif + +/* Interrupt Handling *******************************************************/ + +static int imxrt_qtd_ioccheck(struct imxrt_qtd_s *qtd, uint32_t **bp, + void *arg); +static int imxrt_qh_ioccheck(struct imxrt_qh_s *qh, uint32_t **bp, + void *arg); +#ifdef CONFIG_USBHOST_ASYNCH +static int imxrt_qtd_cancel(struct imxrt_qtd_s *qtd, uint32_t **bp, + void *arg); +static int imxrt_qh_cancel(struct imxrt_qh_s *qh, uint32_t **bp, void *arg); +#endif +static inline void imxrt_ioc_bottomhalf(void); +static inline void imxrt_portsc_bottomhalf(void); +static inline void imxrt_syserr_bottomhalf(void); +static inline void imxrt_async_advance_bottomhalf(void); +static void imxrt_ehci_bottomhalf(FAR void *arg); +static int imxrt_ehci_interrupt(int irq, FAR void *context, FAR void *arg); + +/* USB Host Controller Operations *******************************************/ + +static int imxrt_wait(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s **hport); +static int imxrt_rh_enumerate(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport); +static int imxrt_enumerate(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport); + +static int imxrt_ep0configure(FAR struct usbhost_driver_s *drvr, + usbhost_ep_t ep0, uint8_t funcaddr, uint8_t speed, + uint16_t maxpacketsize); +static int imxrt_epalloc(FAR struct usbhost_driver_s *drvr, + const FAR struct usbhost_epdesc_s *epdesc, usbhost_ep_t *ep); +static int imxrt_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep); +static int imxrt_alloc(FAR struct usbhost_driver_s *drvr, + FAR uint8_t **buffer, FAR size_t *maxlen); +static int imxrt_free(FAR struct usbhost_driver_s *drvr, + FAR uint8_t *buffer); +static int imxrt_ioalloc(FAR struct usbhost_driver_s *drvr, + FAR uint8_t **buffer, size_t buflen); +static int imxrt_iofree(FAR struct usbhost_driver_s *drvr, + FAR uint8_t *buffer); +static int imxrt_ctrlin(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, + FAR const struct usb_ctrlreq_s *req, FAR uint8_t *buffer); +static int imxrt_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, + FAR const struct usb_ctrlreq_s *req, FAR const uint8_t *buffer); +static ssize_t imxrt_transfer(FAR struct usbhost_driver_s *drvr, + usbhost_ep_t ep, FAR uint8_t *buffer, size_t buflen); +#ifdef CONFIG_USBHOST_ASYNCH +static int imxrt_asynch(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, + FAR uint8_t *buffer, size_t buflen, usbhost_asynch_t callback, + FAR void *arg); +#endif +static int imxrt_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep); +#ifdef CONFIG_USBHOST_HUB +static int imxrt_connect(FAR struct usbhost_driver_s *drvr, + FAR struct usbhost_hubport_s *hport, bool connected); +#endif +static void imxrt_disconnect(FAR struct usbhost_driver_s *drvr, + FAR struct usbhost_hubport_s *hport); + +/* Initialization ***********************************************************/ + +static int imxrt_reset(void); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* In this driver implementation, support is provided for only a single a + * single USB device. All status information can be simply retained in a + * single global instance. + */ + +static struct imxrt_ehci_s g_ehci; + +/* This is the connection/enumeration interface */ + +static struct usbhost_connection_s g_ehciconn; + +/* Maps USB chapter 9 speed to EHCI speed */ + +static const uint8_t g_ehci_speed[4] = +{ + 0, EHCI_LOW_SPEED, EHCI_FULL_SPEED, EHCI_HIGH_SPEED +}; + +/* The head of the asynchronous queue */ + +static struct imxrt_qh_s g_asynchead aligned_data(32); + +#ifndef CONFIG_USBHOST_INT_DISABLE +/* The head of the periodic queue */ + +static struct imxrt_qh_s g_intrhead aligned_data(32); + +/* The frame list */ + +#ifdef CONFIG_IMXRT_EHCI_PREALLOCATE +static uint32_t g_framelist[FRAME_LIST_SIZE] aligned_data(4096); +#else +static uint32_t *g_framelist; +#endif +#endif /* CONFIG_USBHOST_INT_DISABLE */ + +#ifdef CONFIG_IMXRT_EHCI_PREALLOCATE +/* Pools of pre-allocated data structures. These will all be linked into the + * free lists within g_ehci. These must all be aligned to 32-byte boundaries + */ + +/* Queue Head (QH) pool */ + +static struct imxrt_qh_s g_qhpool[CONFIG_IMXRT_EHCI_NQHS] + aligned_data(32); + +/* Queue Element Transfer Descriptor (qTD) pool */ + +static struct imxrt_qtd_s g_qtdpool[CONFIG_IMXRT_EHCI_NQTDS] + aligned_data(32); + +#else +/* Pools of dynamically data structures. These will all be linked into the + * free lists within g_ehci. These must all be aligned to 32-byte boundaries + */ + +/* Queue Head (QH) pool */ + +static struct imxrt_qh_s *g_qhpool; + +/* Queue Element Transfer Descriptor (qTD) pool */ + +static struct imxrt_qtd_s *g_qtdpool; + +#endif + +#ifdef HAVE_USBHOST_TRACE +/* USB trace strings */ + +static const struct imxrt_ehci_trace_s g_trace1[TRACE1_NSTRINGS] = +{ + TRENTRY(EHCI_TRACE1_SYSTEMERROR, TR_FMT1, + "EHCI ERROR: System error: %06x\n"), + TRENTRY(EHCI_TRACE1_QTDFOREACH_FAILED, TR_FMT1, + "EHCI ERROR: imxrt_qtd_foreach failed: %d\n"), + TRENTRY(EHCI_TRACE1_QHALLOC_FAILED, TR_FMT1, + "EHCI ERROR: Failed to allocate a QH\n"), + TRENTRY(EHCI_TRACE1_BUFTOOBIG, TR_FMT1, + "EHCI ERROR: Buffer too big. Remaining %d\n"), + TRENTRY(EHCI_TRACE1_REQQTDALLOC_FAILED, TR_FMT1, + "EHCI ERROR: Failed to allocate request qTD"), + TRENTRY(EHCI_TRACE1_ADDBPL_FAILED, TR_FMT1, + "EHCI ERROR: imxrt_qtd_addbpl failed: %d\n"), + TRENTRY(EHCI_TRACE1_DATAQTDALLOC_FAILED, TR_FMT1, + "EHCI ERROR: Failed to allocate data buffer qTD, 0"), + TRENTRY(EHCI_TRACE1_DEVDISCONNECTED, TR_FMT1, + "EHCI ERROR: Device disconnected %d\n"), + TRENTRY(EHCI_TRACE1_QHCREATE_FAILED, TR_FMT1, + "EHCI ERROR: imxrt_qh_create failed\n"), + TRENTRY(EHCI_TRACE1_QTDSETUP_FAILED, TR_FMT1, + "EHCI ERROR: imxrt_qtd_setupphase failed\n"), + + TRENTRY(EHCI_TRACE1_QTDDATA_FAILED, TR_FMT1, + "EHCI ERROR: imxrt_qtd_dataphase failed\n"), + TRENTRY(EHCI_TRACE1_QTDSTATUS_FAILED, TR_FMT1, + "EHCI ERROR: imxrt_qtd_statusphase failed\n"), + TRENTRY(EHCI_TRACE1_TRANSFER_FAILED, TR_FMT1, + "EHCI ERROR: Transfer failed %d\n"), + TRENTRY(EHCI_TRACE1_QHFOREACH_FAILED, TR_FMT1, + "EHCI ERROR: imxrt_qh_foreach failed: %d\n"), + TRENTRY(EHCI_TRACE1_SYSERR_INTR, TR_FMT1, + "EHCI: Host System Error Interrupt\n"), + TRENTRY(EHCI_TRACE1_USBERR_INTR, TR_FMT1, + "EHCI: USB Error Interrupt (USBERRINT) Interrupt: %06x\n"), + TRENTRY(EHCI_TRACE1_EPALLOC_FAILED, TR_FMT1, + "EHCI ERROR: Failed to allocate EP info structure\n"), + TRENTRY(EHCI_TRACE1_BADXFRTYPE, TR_FMT1, + "EHCI ERROR: Support for transfer type %d not implemented\n"), + TRENTRY(EHCI_TRACE1_HCHALTED_TIMEOUT, TR_FMT1, + "EHCI ERROR: Timed out waiting for HCHalted. USBSTS: %06x\n"), + TRENTRY(EHCI_TRACE1_QHPOOLALLOC_FAILED, TR_FMT1, + "EHCI ERROR: Failed to allocate the QH pool\n"), + + TRENTRY(EHCI_TRACE1_QTDPOOLALLOC_FAILED, TR_FMT1, + "EHCI ERROR: Failed to allocate the qTD pool\n"), + TRENTRY(EHCI_TRACE1_PERFLALLOC_FAILED, TR_FMT1, + "EHCI ERROR: Failed to allocate the periodic frame list\n"), + TRENTRY(EHCI_TRACE1_RESET_FAILED, TR_FMT1, + "EHCI ERROR: imxrt_reset failed: %d\n"), + TRENTRY(EHCI_TRACE1_RUN_FAILED, TR_FMT1, + "EHCI ERROR: EHCI Failed to run: USBSTS=%06x\n"), + TRENTRY(EHCI_TRACE1_IRQATTACH_FAILED, TR_FMT1, + "EHCI ERROR: Failed to attach IRQ%d\n"), + +#ifdef HAVE_USBHOST_TRACE_VERBOSE + TRENTRY(EHCI_VTRACE1_PORTSC_CSC, TR_FMT1, + "EHCI Connect Status Change: %06x\n"), + TRENTRY(EHCI_VTRACE1_PORTSC_CONNALREADY, TR_FMT1, + "EHCI Already connected: %06x\n"), + TRENTRY(EHCI_VTRACE1_PORTSC_DISCALREADY, TR_FMT1, + "EHCI Already disconnected: %06x\n"), + TRENTRY(EHCI_VTRACE1_TOPHALF, TR_FMT1, + "EHCI Interrupt: %06x\n"), + TRENTRY(EHCI_VTRACE1_AAINTR, TR_FMT1, + "EHCI Async Advance Interrupt\n"), + + TRENTRY(EHCI_VTRACE1_CLASSENUM, TR_FMT1, + "EHCI Hub port %d: Enumerate the device\n"), + TRENTRY(EHCI_VTRACE1_USBINTR, TR_FMT1, + "EHCI USB Interrupt (USBINT) Interrupt: %06x\n"), + TRENTRY(EHCI_VTRACE1_ENUM_DISCONN, TR_FMT1, + "EHCI Enumeration not connected\n"), + TRENTRY(EHCI_VTRACE1_INITIALIZING, TR_FMT1, + "EHCI Initializing EHCI Stack\n"), + TRENTRY(EHCI_VTRACE1_HCCPARAMS, TR_FMT1, + "EHCI HCCPARAMS=%06x\n"), + TRENTRY(EHCI_VTRACE1_INIITIALIZED, TR_FMT1, + "EHCI USB EHCI Initialized\n"), +#endif +}; + +static const struct imxrt_ehci_trace_s g_trace2[TRACE2_NSTRINGS] = +{ + TRENTRY(EHCI_TRACE2_EPSTALLED, TR_FMT2, + "EHCI EP%d Stalled: TOKEN=%04x\n"), + TRENTRY(EHCI_TRACE2_EPIOERROR, TR_FMT2, + "EHCI ERROR: EP%d TOKEN=%04x\n"), + TRENTRY(EHCI_TRACE2_CLASSENUM_FAILED, TR_FMT2, + "EHCI Hub port %d usbhost_enumerate() failed: %d\n"), + +#ifdef HAVE_USBHOST_TRACE_VERBOSE + TRENTRY(EHCI_VTRACE2_ASYNCXFR, TR_FMT2, + "EHCI Async transfer EP%d buflen=%d\n"), + TRENTRY(EHCI_VTRACE2_INTRXFR, TR_FMT2, + "EHCI Intr Transfer EP%d buflen=%d\n"), + TRENTRY(EHCI_VTRACE2_IOCCHECK, TR_FMT2, + "EHCI IOC EP%d TOKEN=%04x\n"), + TRENTRY(EHCI_VTRACE2_PORTSC, TR_FMT2, + "EHCI PORTSC%d: %04x\n"), + TRENTRY(EHCI_VTRACE2_PORTSC_CONNECTED, TR_FMT2, + "EHCI RHPort%d connected, pscwait: %d\n"), + TRENTRY(EHCI_VTRACE2_PORTSC_DISCONND, TR_FMT2, + "EHCI RHport%d disconnected, pscwait: %d\n"), + TRENTRY(EHCI_VTRACE2_MONWAKEUP, TR_FMT2, + "EHCI RHPort%d connected: %d\n"), + + TRENTRY(EHCI_VTRACE2_EPALLOC, TR_FMT2, + "EHCI EPALLOC: EP%d TYPE=%d\n"), + TRENTRY(EHCI_VTRACE2_CTRLINOUT, TR_FMT2, + "EHCI CTRLIN/OUT: RHPort%d req: %02x\n"), + TRENTRY(EHCI_VTRACE2_HCIVERSION, TR_FMT2, + "EHCI HCIVERSION %x.%02x\n"), + TRENTRY(EHCI_VTRACE2_HCSPARAMS, TR_FMT2, + "EHCI nports=%d, HCSPARAMS=%04x\n"), +#endif +}; +#endif /* HAVE_USBHOST_TRACE */ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imxrt_read16 + * + * Description: + * Read 16-bit little endian data + * + ****************************************************************************/ + +static uint16_t imxrt_read16(const uint8_t *addr) +{ +#ifdef CONFIG_ENDIAN_BIG + return (uint16_t)addr[0] << 8 | (uint16_t)addr[1]; +#else + return (uint16_t)addr[1] << 8 | (uint16_t)addr[0]; +#endif +} + +/**************************************************************************** + * Name: imxrt_read32 + * + * Description: + * Read 32-bit little endian data + * + ****************************************************************************/ + +static inline uint32_t imxrt_read32(const uint8_t *addr) +{ +#ifdef CONFIG_ENDIAN_BIG + return (uint32_t)imxrt_read16(&addr[0]) << 16 | + (uint32_t)imxrt_read16(&addr[2]); +#else + return (uint32_t)imxrt_read16(&addr[2]) << 16 | + (uint32_t)imxrt_read16(&addr[0]); +#endif +} + +/**************************************************************************** + * Name: imxrt_write16 + * + * Description: + * Write 16-bit little endian data + * + ****************************************************************************/ + +#if 0 /* Not used */ +static void imxrt_write16(uint16_t memval, uint8_t *addr) +{ +#ifdef CONFIG_ENDIAN_BIG + addr[0] = memval & 0xff; + addr[1] = memval >> 8; +#else + addr[0] = memval >> 8; + addr[1] = memval & 0xff; +#endif +} +#endif + +/**************************************************************************** + * Name: imxrt_write32 + * + * Description: + * Write 32-bit little endian data + * + ****************************************************************************/ + +#if 0 /* Not used */ +static void imxrt_write32(uint32_t memval, uint8_t *addr) +{ +#ifdef CONFIG_ENDIAN_BIG + imxrt_write16(memval >> 16, &addr[0]); + imxrt_write16(memval & 0xffff, &addr[2]); +#else + imxrt_write16(memval & 0xffff, &addr[0]); + imxrt_write16(memval >> 16, &addr[2]); +#endif +} +#endif + +/**************************************************************************** + * Name: imxrt_swap16 + * + * Description: + * Swap bytes on a 16-bit value + * + ****************************************************************************/ + +#ifdef CONFIG_ENDIAN_BIG +static uint16_t imxrt_swap16(uint16_t value) +{ + return ((value >> 8) & 0xff) | ((value & 0xff) << 8); +} +#endif + +/**************************************************************************** + * Name: imxrt_swap32 + * + * Description: + * Swap bytes on a 32-bit value + * + ****************************************************************************/ + +#ifdef CONFIG_ENDIAN_BIG +static uint32_t imxrt_swap32(uint32_t value) +{ + return (uint32_t)imxrt_swap16((uint16_t)((value >> 16) & 0xffff)) | + (uint32_t)imxrt_swap16((uint16_t)(value & 0xffff)) << 16; +} +#endif + +/**************************************************************************** + * Name: imxrt_printreg + * + * Description: + * Print the contents of a IMXRT EHCI register + * + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_EHCI_REGDEBUG +static void imxrt_printreg(volatile uint32_t *regaddr, uint32_t regval, + bool iswrite) +{ + uinfo("%08x%s%08x\n", (uintptr_t)regaddr, iswrite ? "<-" : "->", regval); +} +#endif + +/**************************************************************************** + * Name: imxrt_checkreg + * + * Description: + * Check if it is time to output debug information for accesses to a IMXRT + * EHCI register + * + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_EHCI_REGDEBUG +static void imxrt_checkreg(volatile uint32_t *regaddr, uint32_t regval, + bool iswrite) +{ + static uint32_t *prevaddr = NULL; + static uint32_t preval = 0; + static uint32_t count = 0; + static bool prevwrite = false; + + /* Is this the same value that we read from/wrote to the same register last + * time? Are we polling the register? If so, suppress the output. + */ + + if (regaddr == prevaddr && regval == preval && prevwrite == iswrite) + { + /* Yes.. Just increment the count */ + + count++; + } + else + { + /* No this is a new address or value or operation. Were there any + * duplicate accesses before this one? + */ + + if (count > 0) + { + /* Yes.. Just one? */ + + if (count == 1) + { + /* Yes.. Just one */ + + imxrt_printreg(prevaddr, preval, prevwrite); + } + else + { + /* No.. More than one. */ + + uinfo("[repeats %d more times]\n", count); + } + } + + /* Save the new address, value, count, and operation for next time */ + + prevaddr = (uint32_t *)regaddr; + preval = regval; + count = 0; + prevwrite = iswrite; + + /* Show the new register access */ + + imxrt_printreg(regaddr, regval, iswrite); + } +} +#endif + +/**************************************************************************** + * Name: imxrt_getreg + * + * Description: + * Get the contents of an IMXRT register + * + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_EHCI_REGDEBUG +static uint32_t imxrt_getreg(volatile uint32_t *regaddr) +{ + /* Read the value from the register */ + + uint32_t regval = *regaddr; + + /* Check if we need to print this value */ + + imxrt_checkreg(regaddr, regval, false); + return regval; +} +#else +static inline uint32_t imxrt_getreg(volatile uint32_t *regaddr) +{ + return *regaddr; +} +#endif + +/**************************************************************************** + * Name: imxrt_putreg + * + * Description: + * Set the contents of an IMXRT register to a value + * + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_EHCI_REGDEBUG +static void imxrt_putreg(uint32_t regval, volatile uint32_t *regaddr) +{ + /* Check if we need to print this value */ + + imxrt_checkreg(regaddr, regval, true); + + /* Write the value */ + + *regaddr = regval; +} +#else +static inline void imxrt_putreg(uint32_t regval, volatile uint32_t *regaddr) +{ + *regaddr = regval; +} +#endif + +/**************************************************************************** + * Name: ehci_wait_usbsts + * + * Description: + * Wait for either (1) a field in the USBSTS register to take a specific + * value, (2) for a timeout to occur, or (3) a error to occur. Return + * a value to indicate which terminated the wait. + * + ****************************************************************************/ + +static int ehci_wait_usbsts(uint32_t maskbits, uint32_t donebits, + unsigned int delay) +{ + uint32_t regval; + unsigned int timeout; + + timeout = 0; + do + { + /* Wait 5usec before trying again */ + + up_udelay(5); + timeout += 5; + + /* Read the USBSTS register and check for a system error */ + + regval = imxrt_getreg(&HCOR->usbsts); + if ((regval & EHCI_INT_SYSERROR) != 0) + { + usbhost_trace1(EHCI_TRACE1_SYSTEMERROR, regval); + return -EIO; + } + + /* Mask out the bits of interest */ + + regval &= maskbits; + + /* Loop until the masked bits take the specified value or until a + * timeout occurs. + */ + } + while (regval != donebits && timeout < delay); + + /* We got here because either the waited for condition or a timeout + * occurred. Return a value to indicate which. + */ + + return (regval == donebits) ? OK : -ETIMEDOUT; +} + +/**************************************************************************** + * Name: imxrt_takesem + * + * Description: + * This is just a wrapper to handle the annoying behavior of semaphore + * waits that return due to the receipt of a signal. + * + ****************************************************************************/ + +static int imxrt_takesem(sem_t *sem) +{ + return nxsem_wait_uninterruptible(sem); +} + +/**************************************************************************** + * Name: imxrt_takesem_noncancelable + * + * Description: + * This is just a wrapper to handle the annoying behavior of semaphore + * waits that return due to the receipt of a signal. This version also + * ignores attempts to cancel the thread. + * + ****************************************************************************/ + +static int imxrt_takesem_noncancelable(sem_t *sem) +{ + int result; + int ret = OK; + + do + { + result = nxsem_wait_uninterruptible(sem); + + /* The only expected error is ECANCELED which would occur if the + * calling thread were canceled. + */ + + DEBUGASSERT(result == OK || result == -ECANCELED); + if (ret == OK && result < 0) + { + ret = result; + } + } + while (result < 0); + + return ret; +} + +/**************************************************************************** + * Name: imxrt_qh_alloc + * + * Description: + * Allocate a Queue Head (QH) structure by removing it from the free list + * + * Assumption: Caller holds the exclsem + * + ****************************************************************************/ + +static struct imxrt_qh_s *imxrt_qh_alloc(void) +{ + struct imxrt_qh_s *qh; + + /* Remove the QH structure from the freelist */ + + qh = (struct imxrt_qh_s *)g_ehci.qhfree; + if (qh) + { + g_ehci.qhfree = ((struct imxrt_list_s *)qh)->flink; + memset(qh, 0, sizeof(struct imxrt_qh_s)); + } + + return qh; +} + +/**************************************************************************** + * Name: imxrt_qh_free + * + * Description: + * Free a Queue Head (QH) structure by returning it to the free list + * + * Assumption: Caller holds the exclsem + * + ****************************************************************************/ + +static void imxrt_qh_free(struct imxrt_qh_s *qh) +{ + struct imxrt_list_s *entry = (struct imxrt_list_s *)qh; + + /* Put the QH structure back into the free list */ + + entry->flink = g_ehci.qhfree; + g_ehci.qhfree = entry; +} + +/**************************************************************************** + * Name: imxrt_qtd_alloc + * + * Description: + * Allocate a Queue Element Transfer Descriptor (qTD) by removing it from + * the free list + * + * Assumption: Caller holds the exclsem + * + ****************************************************************************/ + +static struct imxrt_qtd_s *imxrt_qtd_alloc(void) +{ + struct imxrt_qtd_s *qtd; + + /* Remove the qTD from the freelist */ + + qtd = (struct imxrt_qtd_s *)g_ehci.qtdfree; + if (qtd) + { + g_ehci.qtdfree = ((struct imxrt_list_s *)qtd)->flink; + memset(qtd, 0, sizeof(struct imxrt_qtd_s)); + } + + return qtd; +} + +/**************************************************************************** + * Name: imxrt_qtd_free + * + * Description: + * Free a Queue Element Transfer Descriptor (qTD) by returning it to the + * free list + * + * Assumption: + * Caller holds the exclsem + * + ****************************************************************************/ + +static void imxrt_qtd_free(struct imxrt_qtd_s *qtd) +{ + struct imxrt_list_s *entry = (struct imxrt_list_s *)qtd; + + /* Put the qTD back into the free list */ + + entry->flink = g_ehci.qtdfree; + g_ehci.qtdfree = entry; +} + +/**************************************************************************** + * Name: imxrt_qh_foreach + * + * Description: + * Give the first entry in a list of Queue Head (QH) structures, call the + * handler for each QH structure in the list (including the one at the head + * of the list). + * + ****************************************************************************/ + +static int imxrt_qh_foreach(struct imxrt_qh_s *qh, uint32_t **bp, + foreach_qh_t handler, void *arg) +{ + struct imxrt_qh_s *next; + uintptr_t physaddr; + int ret; + + DEBUGASSERT(qh && handler); + while (qh) + { + /* Is this the end of the list? Check the horizontal link pointer + * (HLP) terminate (T) bit. If T==1, then the HLP address is not + * valid. + */ + + physaddr = imxrt_swap32(qh->hw.hlp); + if ((physaddr & QH_HLP_T) != 0) + { + /* Set the next pointer to NULL. This will terminate the loop. */ + + next = NULL; + } + + /* Is the next QH the asynchronous list head which will always be at + * the end of the asynchronous queue? + */ + + else if (imxrt_virtramaddr(physaddr & QH_HLP_MASK) == + (uintptr_t)&g_asynchead) + { + /* That will also terminate the loop */ + + next = NULL; + } + + /* Otherwise, there is a QH structure after this one that describes + * another transaction. + */ + + else + { + physaddr = imxrt_swap32(qh->hw.hlp) & QH_HLP_MASK; + next = (struct imxrt_qh_s *)imxrt_virtramaddr(physaddr); + } + + /* Perform the user action on this entry. The action might result in + * unlinking the entry! But that is okay because we already have the + * next QH pointer. + * + * Notice that we do not manage the back pointer (bp). If the call- + * out uses it, it must update it as necessary. + */ + + ret = handler(qh, bp, arg); + + /* If the handler returns any non-zero value, then terminate the + * traversal early. + */ + + if (ret != 0) + { + return ret; + } + + /* Set up to visit the next entry */ + + qh = next; + } + + return OK; +} + +/**************************************************************************** + * Name: imxrt_qtd_foreach + * + * Description: + * Give a Queue Head (QH) instance, call the handler for each qTD structure + * in the queue. + * + ****************************************************************************/ + +static int imxrt_qtd_foreach(struct imxrt_qh_s *qh, foreach_qtd_t handler, + void *arg) +{ + struct imxrt_qtd_s *qtd; + struct imxrt_qtd_s *next; + uintptr_t physaddr; + uint32_t *bp; + int ret; + + DEBUGASSERT(qh && handler); + + /* Handle the special case where the queue is empty */ + + bp = &qh->fqp; /* Start of qTDs in original list */ + physaddr = imxrt_swap32(*bp); /* Physical address of first qTD in CPU order */ + + if ((physaddr & QTD_NQP_T) != 0) + { + return 0; + } + + /* Start with the first qTD in the list */ + + qtd = (struct imxrt_qtd_s *)imxrt_virtramaddr(physaddr); + next = NULL; + + /* And loop until we encounter the end of the qTD list */ + + while (qtd) + { + /* Is this the end of the list? Check the next qTD pointer (NQP) + * terminate (T) bit. If T==1, then the NQP address is not valid. + */ + + if ((imxrt_swap32(qtd->hw.nqp) & QTD_NQP_T) != 0) + { + /* Set the next pointer to NULL. This will terminate the loop. */ + + next = NULL; + } + else + { + physaddr = imxrt_swap32(qtd->hw.nqp) & QTD_NQP_NTEP_MASK; + next = (struct imxrt_qtd_s *)imxrt_virtramaddr(physaddr); + } + + /* Perform the user action on this entry. The action might result in + * unlinking the entry! But that is okay because we already have the + * next qTD pointer. + * + * Notice that we do not manage the back pointer (bp). If the call-out + * uses it, it must update it as necessary. + */ + + ret = handler(qtd, &bp, arg); + + /* If the handler returns any non-zero value, then terminate the + * traversal early. + */ + + if (ret != 0) + { + return ret; + } + + /* Set up to visit the next entry */ + + qtd = next; + } + + return OK; +} + +/**************************************************************************** + * Name: imxrt_qtd_discard + * + * Description: + * This is a imxrt_qtd_foreach callback. It simply unlinks the QTD, + * updates the back pointer, and frees the QTD structure. + * + ****************************************************************************/ + +static int imxrt_qtd_discard(struct imxrt_qtd_s *qtd, uint32_t **bp, + void *arg) +{ + DEBUGASSERT(qtd && bp && *bp); + + /* Remove the qTD from the list by updating the forward pointer to skip + * around this qTD. We do not change that pointer because are repeatedly + * removing the aTD at the head of the QH list. + */ + + **bp = qtd->hw.nqp; + + /* Then free the qTD */ + + imxrt_qtd_free(qtd); + return OK; +} + +/**************************************************************************** + * Name: imxrt_qh_discard + * + * Description: + * Free the Queue Head (QH) and all qTD's attached to the QH. + * + * Assumptions: + * The QH structure itself has already been unlinked from whatever list it + * may have been in. + * + ****************************************************************************/ + +static int imxrt_qh_discard(struct imxrt_qh_s *qh) +{ + int ret; + + DEBUGASSERT(qh); + + /* Free all of the qTD's attached to the QH */ + + ret = imxrt_qtd_foreach(qh, imxrt_qtd_discard, NULL); + if (ret < 0) + { + usbhost_trace1(EHCI_TRACE1_QTDFOREACH_FAILED, -ret); + } + + /* Then free the QH itself */ + + imxrt_qh_free(qh); + return ret; +} + +/**************************************************************************** + * Name: imxrt_qtd_invalidate + * + * Description: + * This is a callback from imxrt_qtd_foreach. It simply invalidates D- + * cache for address range of the qTD entry. + * + ****************************************************************************/ + +#if 0 /* Not used */ +static int imxrt_qtd_invalidate(struct imxrt_qtd_s *qtd, uint32_t **bp, + void *arg) +{ + /* Invalidate the D-Cache, i.e., force reloading of the D-Cache from memory + * memory over the specified address range. + */ + + up_invalidate_dcache((uintptr_t)&qtd->hw, + (uintptr_t)&qtd->hw + sizeof(struct ehci_qtd_s)); + return OK; +} +#endif + +/**************************************************************************** + * Name: imxrt_qh_invalidate + * + * Description: + * Invalidate the Queue Head and all qTD entries in the queue. + * + ****************************************************************************/ + +#if 0 /* Not used */ +static int imxrt_qh_invalidate(struct imxrt_qh_s *qh) +{ + /* Invalidate the QH first so that we reload the qTD list head */ + + up_invalidate_dcache((uintptr_t)&qh->hw, + (uintptr_t)&qh->hw + sizeof(struct ehci_qh_s)); + + /* Then invalidate all of the qTD entries in the queue */ + + return imxrt_qtd_foreach(qh, imxrt_qtd_invalidate, NULL); +} +#endif + +/**************************************************************************** + * Name: imxrt_qtd_flush + * + * Description: + * This is a callback from imxrt_qtd_foreach. It simply flushes D-cache + * for address range of the qTD entry. + * + ****************************************************************************/ + +static int imxrt_qtd_flush(struct imxrt_qtd_s *qtd, uint32_t **bp, void *arg) +{ + /* Flush the D-Cache, i.e., make the contents of the memory match the + * contents of the D-Cache in the specified address range and invalidate + * the D-Cache to force re-loading of the data from memory when next + * accessed. + */ + + up_flush_dcache((uintptr_t)&qtd->hw, + (uintptr_t)&qtd->hw + sizeof(struct ehci_qtd_s)); + return OK; +} + +/**************************************************************************** + * Name: imxrt_qh_flush + * + * Description: + * Invalidate the Queue Head and all qTD entries in the queue. + * + ****************************************************************************/ + +static int imxrt_qh_flush(struct imxrt_qh_s *qh) +{ + /* Flush the QH first. This will write the contents of the D-cache to RAM + * and invalidate the contents of the D-cache so that the next access will + * be reloaded from D-Cache. + */ + + up_flush_dcache((uintptr_t)&qh->hw, + (uintptr_t)&qh->hw + sizeof(struct ehci_qh_s)); + + /* Then flush all of the qTD entries in the queue */ + + return imxrt_qtd_foreach(qh, imxrt_qtd_flush, NULL); +} + +/**************************************************************************** + * Name: imxrt_qtd_print + * + * Description: + * Print the context of one qTD + * + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_EHCI_REGDEBUG +static void imxrt_qtd_print(struct imxrt_qtd_s *qtd) +{ + uinfo(" QTD[%p]:\n", qtd); + uinfo(" hw:\n"); + uinfo(" nqp: %08x alt: %08x token: %08x\n", + qtd->hw.nqp, qtd->hw.alt, qtd->hw.token); + uinfo(" bpl: %08x %08x %08x %08x %08x\n", + qtd->hw.bpl[0], qtd->hw.bpl[1], qtd->hw.bpl[2], + qtd->hw.bpl[3], qtd->hw.bpl[4]); +} +#endif + +/**************************************************************************** + * Name: imxrt_qh_print + * + * Description: + * Print the context of one QH + * + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_EHCI_REGDEBUG +static void imxrt_qh_print(struct imxrt_qh_s *qh) +{ + struct imxrt_epinfo_s *epinfo; + struct ehci_overlay_s *overlay; + + uinfo("QH[%p]:\n", qh); + uinfo(" hw:\n"); + uinfo(" hlp: %08x epchar: %08x epcaps: %08x cqp: %08x\n", + qh->hw.hlp, qh->hw.epchar, qh->hw.epcaps, qh->hw.cqp); + + overlay = &qh->hw.overlay; + uinfo(" overlay:\n"); + uinfo(" nqp: %08x alt: %08x token: %08x\n", + overlay->nqp, overlay->alt, overlay->token); + uinfo(" bpl: %08x %08x %08x %08x %08x\n", + overlay->bpl[0], overlay->bpl[1], overlay->bpl[2], + overlay->bpl[3], overlay->bpl[4]); + + uinfo(" fqp:\n", qh->fqp); + + epinfo = qh->epinfo; + uinfo(" epinfo[%p]:\n", epinfo); + if (epinfo) + { + uinfo(" EP%d DIR=%s FA=%08x TYPE=%d MaxPacket=%d\n", + epinfo->epno, epinfo->dirin ? "IN" : "OUT", epinfo->devaddr, + epinfo->xfrtype, epinfo->maxpacket); + uinfo(" Toggle=%d iocwait=%d speed=%d result=%d\n", + epinfo->toggle, epinfo->iocwait, epinfo->speed, epinfo->result); + } +} +#endif + +/**************************************************************************** + * Name: imxrt_qtd_dump + * + * Description: + * This is a imxrt_qtd_foreach callout function. It dumps the context of + * one qTD + * + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_EHCI_REGDEBUG +static int imxrt_qtd_dump(struct imxrt_qtd_s *qtd, uint32_t **bp, void *arg) +{ + imxrt_qtd_print(qtd); + return OK; +} +#endif + +/**************************************************************************** + * Name: imxrt_qh_dump + * + * Description: + * This is a imxrt_qh_foreach call-out function. It dumps a QH structure + * and all of the qTD structures linked to the QH. + * + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_EHCI_REGDEBUG +static int imxrt_qh_dump(struct imxrt_qh_s *qh, uint32_t **bp, void *arg) +{ + imxrt_qh_print(qh); + return imxrt_qtd_foreach(qh, imxrt_qtd_dump, NULL); +} +#endif + +/**************************************************************************** + * Name: imxrt_ehci_speed + * + * Description: + * Map a speed enumeration value per Chapter 9 of the USB specification to + * the speed enumeration required in the EHCI queue head. + * + ****************************************************************************/ + +static inline uint8_t imxrt_ehci_speed(uint8_t usbspeed) +{ + DEBUGASSERT(usbspeed >= USB_SPEED_LOW && usbspeed <= USB_SPEED_HIGH); + return g_ehci_speed[usbspeed]; +} + +/**************************************************************************** + * Name: imxrt_ioc_setup + * + * Description: + * Set the request for the IOC event well BEFORE enabling the transfer (as + * soon as we are absolutely committed to the to avoid transfer). We do + * this to minimize race conditions. This logic would have to be expanded + * if we want to have more than one packet in flight at a time! + * + * Assumption: The caller holds the EHCI exclsem + * + ****************************************************************************/ + +static int imxrt_ioc_setup(struct imxrt_rhport_s *rhport, + struct imxrt_epinfo_s *epinfo) +{ + irqstate_t flags; + int ret = -ENODEV; + + DEBUGASSERT(rhport && epinfo && !epinfo->iocwait); +#ifdef CONFIG_USBHOST_ASYNCH + DEBUGASSERT(epinfo->callback == NULL); +#endif + + /* Is the device still connected? */ + + flags = enter_critical_section(); + if (rhport->connected) + { + /* Then set iocwait to indicate that we expect to be informed when + * either (1) the device is disconnected, or (2) the transfer + * completed. + */ + + epinfo->iocwait = true; /* We want to be awakened by IOC interrupt */ + epinfo->status = 0; /* No status yet */ + epinfo->xfrd = 0; /* Nothing transferred yet */ + epinfo->result = -EBUSY; /* Transfer in progress */ +#ifdef CONFIG_USBHOST_ASYNCH + epinfo->callback = NULL; /* No asynchronous callback */ + epinfo->arg = NULL; +#endif + ret = OK; /* We are good to go */ + } + + leave_critical_section(flags); + return ret; +} + +/**************************************************************************** + * Name: imxrt_ioc_wait + * + * Description: + * Wait for the IOC event. + * + * Assumption: The caller does *NOT* hold the EHCI exclsem. That would + * cause a deadlock when the bottom-half, worker thread needs to take the + * semaphore. + * + ****************************************************************************/ + +static int imxrt_ioc_wait(struct imxrt_epinfo_s *epinfo) +{ + int ret = OK; + + /* Wait for the IOC event. Loop to handle any false alarm semaphore + * counts. Return an error if the task is canceled. + */ + + while (epinfo->iocwait) + { + ret = imxrt_takesem(&epinfo->iocsem); + if (ret < 0) + { + break; + } + } + + return ret < 0 ? ret : epinfo->result; +} + +/**************************************************************************** + * Name: imxrt_qh_enqueue + * + * Description: + * Add a new, ready-to-go QH w/attached qTDs to the asynchronous queue. + * + * Assumptions: The caller holds the EHCI exclsem + * + ****************************************************************************/ + +static void imxrt_qh_enqueue(struct imxrt_qh_s *qhead, struct imxrt_qh_s *qh) +{ + uintptr_t physaddr; + + /* Set the internal fqp field. When we transverse the QH list later, + * we need to know the correct place to start because the overlay may no + * longer point to the first qTD entry. + */ + + qh->fqp = qh->hw.overlay.nqp; + imxrt_qh_dump(qh, NULL, NULL); + + /* Add the new QH to the head of the asynchronous queue list. + * + * First, attach the old head as the new QH HLP and flush the new QH and + * its attached qTDs to RAM. + */ + + qh->hw.hlp = qhead->hw.hlp; + imxrt_qh_flush(qh); + + /* Then set the new QH as the first QH in the asynchronous queue and flush + * the modified head to RAM. + */ + + physaddr = (uintptr_t)imxrt_physramaddr((uintptr_t)qh); + qhead->hw.hlp = imxrt_swap32(physaddr | QH_HLP_TYP_QH); + + up_flush_dcache((uintptr_t)&qhead->hw, + (uintptr_t)&qhead->hw + sizeof(struct ehci_qh_s)); +} + +/**************************************************************************** + * Name: imxrt_qh_create + * + * Description: + * Create a new Queue Head (QH) + * + ****************************************************************************/ + +static struct imxrt_qh_s *imxrt_qh_create(struct imxrt_rhport_s *rhport, + struct imxrt_epinfo_s *epinfo) +{ + struct imxrt_qh_s *qh; + uint32_t rhpndx; + uint32_t regval; + uint8_t hubaddr; + uint8_t hubport; + + /* Allocate a new queue head structure */ + + qh = imxrt_qh_alloc(); + if (qh == NULL) + { + usbhost_trace1(EHCI_TRACE1_QHALLOC_FAILED, 0); + return NULL; + } + + /* Save the endpoint information with the QH itself */ + + qh->epinfo = epinfo; + + /* Write QH endpoint characteristics: + * + * FIELD DESCRIPTION VALUE/SOURCE + * -------- ------------------------------- -------------------- + * DEVADDR Device address Endpoint structure + * I Inactivate on Next Transaction 0 + * ENDPT Endpoint number Endpoint structure + * EPS Endpoint speed Endpoint structure + * DTC Data toggle control 1 + * MAXPKT Max packet size Endpoint structure + * C Control endpoint Calculated + * RL NAK count reloaded 8 + */ + + regval = ((uint32_t)epinfo->devaddr << QH_EPCHAR_DEVADDR_SHIFT) | + ((uint32_t)epinfo->epno << QH_EPCHAR_ENDPT_SHIFT) | + ((uint32_t)imxrt_ehci_speed(epinfo->speed) << + QH_EPCHAR_EPS_SHIFT) | + QH_EPCHAR_DTC | + ((uint32_t)epinfo->maxpacket << QH_EPCHAR_MAXPKT_SHIFT) | + ((uint32_t)8 << QH_EPCHAR_RL_SHIFT); + + /* Paragraph 3.6.3: "Control Endpoint Flag (C). If the QH.EPS field + * indicates the endpoint is not a high-speed device, and the endpoint + * is an control endpoint, then software must set this bit to a one. + * Otherwise it should always set this bit to a zero." + */ + + if (epinfo->speed != USB_SPEED_HIGH && + epinfo->xfrtype == USB_EP_ATTR_XFER_CONTROL) + { + regval |= QH_EPCHAR_C; + } + + /* Save the endpoint characteristics word with the correct byte order */ + + qh->hw.epchar = imxrt_swap32(regval); + + /* Write QH endpoint capabilities + * + * FIELD DESCRIPTION VALUE/SOURCE + * -------- ------------------------------- -------------------- + * SSMASK Interrupt Schedule Mask Depends on epinfo->xfrtype + * SCMASK Split Completion Mask 0 + * HUBADDR Hub Address Always 0 for now + * PORT Port number RH port index + 1 + * MULT High band width multiplier 1 + */ + + rhpndx = RHPNDX(rhport); + +#ifdef CONFIG_USBHOST_HUB + /* REVISIT: Future HUB support will require the HUB port number + * and HUB device address to be included here: + * + * - The HUB device address is the USB device address of the USB 2.0 Hub + * below which a full- or low-speed device is attached. + * - The HUB port number is the port number on the above USB 2.0 Hub + * + * These fields are used in the split-transaction protocol. The kludge + * below should work for hubs connected directly to a root hub port, + * but would not work for devices connected to downstream hubs. + */ + +#warning Missing logic + hubaddr = rhport->ep0.devaddr; + hubport = rhpndx + 1; +#else + hubaddr = rhport->ep0.devaddr; + hubport = rhpndx + 1; +#endif + + regval = ((uint32_t)hubaddr << QH_EPCAPS_HUBADDR_SHIFT) | + ((uint32_t)hubport << QH_EPCAPS_PORT_SHIFT) | + ((uint32_t)1 << QH_EPCAPS_MULT_SHIFT); + +#ifndef CONFIG_USBHOST_INT_DISABLE + if (epinfo->xfrtype == USB_EP_ATTR_XFER_INT) + { + /* Here, the S-Mask field in the queue head is set to 1, indicating + * that the transaction for the endpoint should be executed on the bus + * during micro-frame 0 of the frame. + * + * REVISIT: The polling interval should be controlled by the which + * entry is the framelist holds the QH pointer for a given micro-frame + * and the QH pointer should be replicated for different polling rates. + * This implementation currently just sets all frame_list entry to + * all the same interrupt queue. That should work but will not give + * any control over polling rates. + */ +#warning REVISIT + + regval |= ((uint32_t)1 << QH_EPCAPS_SSMASK_SHIFT); + } +#endif + + qh->hw.epcaps = imxrt_swap32(regval); + + /* Mark this as the end of this list. This will be overwritten if/when the + * next qTD is added to the queue. + */ + + qh->hw.hlp = imxrt_swap32(QH_HLP_T); + qh->hw.overlay.nqp = imxrt_swap32(QH_NQP_T); + qh->hw.overlay.alt = imxrt_swap32(QH_AQP_T); + return qh; +} + +/**************************************************************************** + * Name: imxrt_qtd_addbpl + * + * Description: + * Add a buffer pointer list to a qTD. + * + ****************************************************************************/ + +static int imxrt_qtd_addbpl(struct imxrt_qtd_s *qtd, const void *buffer, + size_t buflen) +{ + uint32_t physaddr; + uint32_t nbytes; + uint32_t next; + int ndx; + + /* Flush the contents of the data buffer to RAM so that the correct + * contents will be accessed for an OUT DMA. + */ + + up_flush_dcache((uintptr_t)buffer, (uintptr_t)buffer + buflen); + + /* Loop, adding the aligned physical addresses of the buffer to the buffer + * page list. Only the first entry need not be aligned (because only the + * first entry has the offset field). The subsequent entries must begin on + * 4KB address boundaries. + */ + + physaddr = (uint32_t)imxrt_physramaddr((uintptr_t)buffer); + + for (ndx = 0; ndx < 5; ndx++) + { + /* Write the physical address of the buffer into the qTD buffer pointer + * list. + */ + + qtd->hw.bpl[ndx] = imxrt_swap32(physaddr); + + /* Get the next buffer pointer (in the case where we will have to + * transfer more then one chunk). This buffer must be aligned to a + * 4KB address boundary. + */ + + next = (physaddr + 4096) & ~4095; + + /* How many bytes were included in the last buffer? Was it the whole + * thing? + */ + + nbytes = next - physaddr; + if (nbytes >= buflen) + { + /* Yes... it was the whole thing. Break out of the loop early. */ + + break; + } + + /* Adjust the buffer length and physical address for the next time + * through the loop. + */ + + buflen -= nbytes; + physaddr = next; + } + + /* Handle the case of a huge buffer > 4*4KB = 16KB */ + + if (ndx >= 5) + { + usbhost_trace1(EHCI_TRACE1_BUFTOOBIG, buflen); + return -EFBIG; + } + + return OK; +} + +/**************************************************************************** + * Name: imxrt_qtd_setupphase + * + * Description: + * Create a SETUP phase request qTD. + * + ****************************************************************************/ + +static struct imxrt_qtd_s * + imxrt_qtd_setupphase(struct imxrt_epinfo_s *epinfo, + const struct usb_ctrlreq_s *req) +{ + struct imxrt_qtd_s *qtd; + uint32_t regval; + int ret; + + /* Allocate a new Queue Element Transfer Descriptor (qTD) */ + + qtd = imxrt_qtd_alloc(); + if (qtd == NULL) + { + usbhost_trace1(EHCI_TRACE1_REQQTDALLOC_FAILED, 0); + return NULL; + } + + /* Mark this as the end of the list (this will be overwritten if another + * qTD is added after this one). + */ + + qtd->hw.nqp = imxrt_swap32(QTD_NQP_T); + qtd->hw.alt = imxrt_swap32(QTD_AQP_T); + + /* Write qTD token: + * + * FIELD DESCRIPTION VALUE/SOURCE + * -------- ------------------------------- -------------------- + * STATUS Status QTD_TOKEN_ACTIVE + * PID PID Code QTD_TOKEN_PID_SETUP + * CERR Error Counter 3 + * CPAGE Current Page 0 + * IOC Interrupt on complete 0 + * NBYTES Total Bytes to Transfer USB_SIZEOF_CTRLREQ + * TOGGLE Data Toggle 0 + */ + + regval = QTD_TOKEN_ACTIVE | QTD_TOKEN_PID_SETUP | + ((uint32_t)3 << QTD_TOKEN_CERR_SHIFT) | + ((uint32_t)USB_SIZEOF_CTRLREQ << QTD_TOKEN_NBYTES_SHIFT); + + qtd->hw.token = imxrt_swap32(regval); + + /* Add the buffer data */ + + ret = imxrt_qtd_addbpl(qtd, req, USB_SIZEOF_CTRLREQ); + if (ret < 0) + { + usbhost_trace1(EHCI_TRACE1_ADDBPL_FAILED, -ret); + imxrt_qtd_free(qtd); + return NULL; + } + + /* Add the data transfer size to the count in the epinfo structure */ + + epinfo->xfrd += USB_SIZEOF_CTRLREQ; + + return qtd; +} + +/**************************************************************************** + * Name: imxrt_qtd_dataphase + * + * Description: + * Create a data transfer or SET data phase qTD. + * + ****************************************************************************/ + +static struct imxrt_qtd_s *imxrt_qtd_dataphase(struct imxrt_epinfo_s *epinfo, + void *buffer, int buflen, + uint32_t tokenbits) +{ + struct imxrt_qtd_s *qtd; + uint32_t regval; + int ret; + + /* Allocate a new Queue Element Transfer Descriptor (qTD) */ + + qtd = imxrt_qtd_alloc(); + if (qtd == NULL) + { + usbhost_trace1(EHCI_TRACE1_DATAQTDALLOC_FAILED, 0); + return NULL; + } + + /* Mark this as the end of the list (this will be overwritten if another + * qTD is added after this one). + */ + + qtd->hw.nqp = imxrt_swap32(QTD_NQP_T); + qtd->hw.alt = imxrt_swap32(QTD_AQP_T); + + /* Write qTD token: + * + * FIELD DESCRIPTION VALUE/SOURCE + * -------- ------------------------------- -------------------- + * STATUS Status QTD_TOKEN_ACTIVE + * PID PID Code Contained in tokenbits + * CERR Error Counter 3 + * CPAGE Current Page 0 + * IOC Interrupt on complete Contained in tokenbits + * NBYTES Total Bytes to Transfer buflen + * TOGGLE Data Toggle Contained in tokenbits + */ + + regval = tokenbits | QTD_TOKEN_ACTIVE | + ((uint32_t)3 << QTD_TOKEN_CERR_SHIFT) | + ((uint32_t)buflen << QTD_TOKEN_NBYTES_SHIFT); + + qtd->hw.token = imxrt_swap32(regval); + + /* Add the buffer information to the buffer pointer list */ + + ret = imxrt_qtd_addbpl(qtd, buffer, buflen); + if (ret < 0) + { + usbhost_trace1(EHCI_TRACE1_ADDBPL_FAILED, -ret); + imxrt_qtd_free(qtd); + return NULL; + } + + /* Add the data transfer size to the count in the epinfo structure */ + + epinfo->xfrd += buflen; + + return qtd; +} + +/**************************************************************************** + * Name: imxrt_qtd_statusphase + * + * Description: + * Create a STATUS phase request qTD. + * + ****************************************************************************/ + +static struct imxrt_qtd_s *imxrt_qtd_statusphase(uint32_t tokenbits) +{ + struct imxrt_qtd_s *qtd; + uint32_t regval; + + /* Allocate a new Queue Element Transfer Descriptor (qTD) */ + + qtd = imxrt_qtd_alloc(); + if (qtd == NULL) + { + usbhost_trace1(EHCI_TRACE1_REQQTDALLOC_FAILED, 0); + return NULL; + } + + /* Mark this as the end of the list (this will be overwritten if another + * qTD is added after this one). + */ + + qtd->hw.nqp = imxrt_swap32(QTD_NQP_T); + qtd->hw.alt = imxrt_swap32(QTD_AQP_T); + + /* Write qTD token: + * + * FIELD DESCRIPTION VALUE/SOURCE + * -------- ------------------------------- -------------------- + * STATUS Status QTD_TOKEN_ACTIVE + * PID PID Code Contained in tokenbits + * CERR Error Counter 3 + * CPAGE Current Page 0 + * IOC Interrupt on complete QTD_TOKEN_IOC + * NBYTES Total Bytes to Transfer 0 + * TOGGLE Data Toggle Contained in tokenbits + */ + + regval = tokenbits | QTD_TOKEN_ACTIVE | QTD_TOKEN_IOC | + ((uint32_t)3 << QTD_TOKEN_CERR_SHIFT); + + qtd->hw.token = imxrt_swap32(regval); + return qtd; +} + +/**************************************************************************** + * Name: imxrt_async_setup + * + * Description: + * Process a IN or OUT request on any asynchronous endpoint (bulk or + * control). This function will enqueue the request and wait for it to + * complete. Bulk data transfers differ in that req == NULL and there are + * not SETUP or STATUS phases. + * + * This is a blocking function; it will not return until the control + * transfer has completed. + * + * Assumption: The caller holds the EHCI exclsem. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is return on + * any failure. + * + ****************************************************************************/ + +static int imxrt_async_setup(struct imxrt_rhport_s *rhport, + struct imxrt_epinfo_s *epinfo, + const struct usb_ctrlreq_s *req, + uint8_t *buffer, size_t buflen) +{ + struct imxrt_qh_s *qh; + struct imxrt_qtd_s *qtd; + uintptr_t physaddr; + uint32_t *flink; + uint32_t *alt; + uint32_t toggle; + bool dirin = false; + int ret; + + /* Terse output only if we are tracing */ + +#ifdef CONFIG_USBHOST_TRACE + usbhost_vtrace2(EHCI_VTRACE2_ASYNCXFR, epinfo->epno, buflen); +#else + uinfo("RHport%d EP%d: buffer=%p, buflen=%d, req=%p\n", + RHPORT(rhport), epinfo->epno, buffer, buflen, req); +#endif + + DEBUGASSERT(rhport && epinfo); + + /* A buffer may or may be supplied with an EP0 SETUP transfer. A buffer + * will always be present for normal endpoint data transfers. + */ + + DEBUGASSERT(req || (buffer && buflen > 0)); + + /* Create and initialize a Queue Head (QH) structure for this transfer */ + + qh = imxrt_qh_create(rhport, epinfo); + if (qh == NULL) + { + usbhost_trace1(EHCI_TRACE1_QHCREATE_FAILED, 0); + return -ENOMEM; + } + + /* Initialize the QH link and get the next data toggle (not used for SETUP + * transfers) + */ + + flink = &qh->hw.overlay.nqp; + toggle = (uint32_t)epinfo->toggle << QTD_TOKEN_TOGGLE_SHIFT; + ret = -EIO; + + /* Is there an EP0 SETUP request? If so, req will be non-NULL and we will + * queue two or three qTDs: + * + * 1) One for the SETUP phase, + * 2) One for the DATA phase (if there is data), and + * 3) One for the STATUS phase. + * + * If this is not an EP0 SETUP request, then only a data transfer will be + * enqueued. + */ + + if (req != NULL) + { + /* Allocate a new Queue Element Transfer Descriptor (qTD) for the SETUP + * phase of the request sequence. + */ + + qtd = imxrt_qtd_setupphase(epinfo, req); + if (qtd == NULL) + { + usbhost_trace1(EHCI_TRACE1_QTDSETUP_FAILED, 0); + goto errout_with_qh; + } + + /* Link the new qTD to the QH head. */ + + physaddr = imxrt_physramaddr((uintptr_t)qtd); + *flink = imxrt_swap32(physaddr); + + /* Get the new forward link pointer and data toggle */ + + flink = &qtd->hw.nqp; + toggle = QTD_TOKEN_TOGGLE; + } + + /* A buffer may or may be supplied with an EP0 SETUP transfer. A buffer + * will always be present for normal endpoint data transfers. + */ + + alt = NULL; + if (buffer != NULL && buflen > 0) + { + uint32_t tokenbits; + + /* Extra TOKEN bits include the data toggle, the data PID, and if + * there is no request, an indication to interrupt at the end of this + * transfer. + */ + + tokenbits = toggle; + + /* Get the data token direction. + * + * If this is a SETUP request, use the direction contained in the + * request. The IOC bit is not set. + */ + + if (req) + { + if ((req->type & USB_REQ_DIR_MASK) == USB_REQ_DIR_IN) + { + tokenbits |= QTD_TOKEN_PID_IN; + dirin = true; + } + else + { + tokenbits |= QTD_TOKEN_PID_OUT; + dirin = false; + } + } + + /* Otherwise, the endpoint is uni-directional. Get the direction from + * the epinfo structure. Since this is not an EP0 SETUP request, + * nothing follows the data and we want the IOC interrupt when the + * data transfer completes. + */ + + else if (epinfo->dirin) + { + tokenbits |= (QTD_TOKEN_PID_IN | QTD_TOKEN_IOC); + dirin = true; + } + else + { + tokenbits |= (QTD_TOKEN_PID_OUT | QTD_TOKEN_IOC); + dirin = false; + } + + /* Allocate a new Queue Element Transfer Descriptor (qTD) for the data + * buffer. + */ + + qtd = imxrt_qtd_dataphase(epinfo, buffer, buflen, tokenbits); + if (qtd == NULL) + { + usbhost_trace1(EHCI_TRACE1_QTDDATA_FAILED, 0); + goto errout_with_qh; + } + + /* Link the new qTD to either QH head of the SETUP qTD. */ + + physaddr = imxrt_physramaddr((uintptr_t)qtd); + *flink = imxrt_swap32(physaddr); + + /* Set the forward link pointer to this new qTD */ + + flink = &qtd->hw.nqp; + + /* If this was an IN transfer, then setup a pointer alternate link. + * The EHCI hardware will use this link if a short packet is received. + */ + + if (dirin) + { + alt = &qtd->hw.alt; + } + } + + /* If this is an EP0 SETUP request, then enqueue one more qTD for the + * STATUS phase transfer. + */ + + if (req != NULL) + { + /* Extra TOKEN bits include the data toggle and the correct data PID. */ + + uint32_t tokenbits = toggle; + + /* The status phase direction is the opposite of the data phase. If + * this is an IN request, then we received the buffer and we will send + * the zero length packet handshake. + */ + + if ((req->type & USB_REQ_DIR_MASK) == USB_REQ_DIR_IN) + { + tokenbits |= QTD_TOKEN_PID_OUT; + } + + /* Otherwise, this in an OUT request. We send the buffer and we expect + * to receive the NULL packet handshake. + */ + + else + { + tokenbits |= QTD_TOKEN_PID_IN; + } + + /* Allocate a new Queue Element Transfer Descriptor (qTD) + * for the status + */ + + qtd = imxrt_qtd_statusphase(tokenbits); + if (qtd == NULL) + { + usbhost_trace1(EHCI_TRACE1_QTDSTATUS_FAILED, 0); + goto errout_with_qh; + } + + /* Link the new qTD to either the SETUP or data qTD. */ + + physaddr = imxrt_physramaddr((uintptr_t)qtd); + *flink = imxrt_swap32(physaddr); + + /* In an IN data qTD was also enqueued, then linked the data qTD's + * alternate pointer to this STATUS phase qTD in order to handle short + * transfers. + */ + + if (alt) + { + *alt = imxrt_swap32(physaddr); + } + } + + /* Add the new QH to the head of the asynchronous queue list */ + + imxrt_qh_enqueue(&g_asynchead, qh); + return OK; + + /* Clean-up after an error */ + +errout_with_qh: + imxrt_qh_discard(qh); + return ret; +} + +/**************************************************************************** + * Name: imxrt_intr_setup + * + * Description: + * Process a IN or OUT request on any interrupt endpoint by inserting a qTD + * into the periodic frame list. + * + * Paragraph 4.10.7 "Adding Interrupt Queue Heads to the Periodic Schedule" + * "The link path(s) from the periodic frame list to a queue head + * establishes in which frames a transaction can be executed for the + * queue head. Queue heads are linked into the periodic schedule so they + * are polled at the appropriate rate. System software sets a bit in a + * queue head's S-Mask to indicate which micro-frame with-in a 1 + * millisecond period a transaction should be executed for the queue + * head. Software must ensure that all queue heads in the periodic + * schedule have S-Mask set to a non-zero value. An S-mask with a zero + * value in the context of the periodic schedule yields undefined + * results. + * + * "If the desired poll rate is greater than one frame, system software + * can use a combination of queue head linking and S-Mask values to + * spread interrupts of equal poll rates through the schedule so that the + * periodic bandwidth is allocated and managed in the most efficient + * manner possible." + * + * Paragraph 4.6 "Periodic Schedule" + * + * "The periodic schedule is used to manage all isochronous and interrupt + * transfer streams. The base of the periodic schedule is the periodic + * frame list. Software links schedule data structures to the periodic + * frame list to produce a graph of scheduled data structures. The graph + * represents an appropriate sequence of transactions on the USB. ... + * isochronous transfers (using iTDs and siTDs) with a period of one are + * linked directly to the periodic frame list. Interrupt transfers (are + * managed with queue heads) and isochronous streams with periods other + * than one are linked following the period-one iTD/siTDs. Interrupt + * queue heads are linked into the frame list ordered by poll rate. + * Longer poll rates are linked first (e.g. closest to the periodic + * frame list), followed by shorter poll rates, with queue heads with a + * poll rate of one, on the very end." + * + * Assumption: The caller holds the EHCI exclsem. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is return on + * any failure. + * + ****************************************************************************/ + +#ifndef CONFIG_USBHOST_INT_DISABLE +static int imxrt_intr_setup(struct imxrt_rhport_s *rhport, + struct imxrt_epinfo_s *epinfo, + uint8_t *buffer, size_t buflen) +{ + struct imxrt_qh_s *qh; + struct imxrt_qtd_s *qtd; + uintptr_t physaddr; + uint32_t tokenbits; + uint32_t regval; + int ret; + + /* Terse output only if we are tracing */ + +#ifdef CONFIG_USBHOST_TRACE + usbhost_vtrace2(EHCI_VTRACE2_INTRXFR, epinfo->epno, buflen); +#else + uinfo("RHport%d EP%d: buffer=%p, buflen=%d\n", + RHPORT(rhport), epinfo->epno, buffer, buflen); +#endif + + DEBUGASSERT(rhport && epinfo && buffer && buflen > 0); + + /* Create and initialize a Queue Head (QH) structure for this transfer */ + + qh = imxrt_qh_create(rhport, epinfo); + if (qh == NULL) + { + usbhost_trace1(EHCI_TRACE1_QHCREATE_FAILED, 0); + return -ENOMEM; + } + + /* Extra TOKEN bits include the data toggle, the data PID, and an + * indication to interrupt at the end of this transfer. + */ + + tokenbits = (uint32_t)epinfo->toggle << QTD_TOKEN_TOGGLE_SHIFT; + + /* Get the data token direction. */ + + if (epinfo->dirin) + { + tokenbits |= (QTD_TOKEN_PID_IN | QTD_TOKEN_IOC); + } + else + { + tokenbits |= (QTD_TOKEN_PID_OUT | QTD_TOKEN_IOC); + } + + /* Allocate a new Queue Element Transfer Descriptor (qTD) for the data + * buffer. + */ + + qtd = imxrt_qtd_dataphase(epinfo, buffer, buflen, tokenbits); + if (qtd == NULL) + { + usbhost_trace1(EHCI_TRACE1_QTDDATA_FAILED, 0); + ret = -ENOMEM; + goto errout_with_qh; + } + + /* Link the new qTD to the QH. */ + + physaddr = imxrt_physramaddr((uintptr_t)qtd); + qh->hw.overlay.nqp = imxrt_swap32(physaddr); + + /* Disable the periodic schedule */ + + regval = imxrt_getreg(&HCOR->usbcmd); + regval &= ~EHCI_USBCMD_PSEN; + imxrt_putreg(regval, &HCOR->usbcmd); + + /* Add the new QH to the head of the interrupt transfer list */ + + imxrt_qh_enqueue(&g_intrhead, qh); + + /* Re-enable the periodic schedule */ + + regval |= EHCI_USBCMD_PSEN; + imxrt_putreg(regval, &HCOR->usbcmd); + return OK; + + /* Clean-up after an error */ + +errout_with_qh: + imxrt_qh_discard(qh); + return ret; +} +#endif /* CONFIG_USBHOST_INT_DISABLE */ + +/**************************************************************************** + * Name: imxrt_transfer_wait + * + * Description: + * Wait for an IN or OUT transfer to complete. + * + * Assumption: The caller holds the EHCI exclsem. The caller must be aware + * that the EHCI exclsem will released while waiting for the transfer to + * complete, but will be re-acquired when before returning. The state of + * EHCI resources could be very different upon return. + * + * Returned Value: + * On success, this function returns the number of bytes actually + * transferred. For control transfers, this size includes the size of the + * control request plus the size of the data (which could be short); for + * bulk transfers, this will be the number of data bytes transfers (which + * could be short). + * + ****************************************************************************/ + +static ssize_t imxrt_transfer_wait(struct imxrt_epinfo_s *epinfo) +{ + int ret; + int ret2; + + /* Release the EHCI semaphore while we wait. Other threads need the + * opportunity to access the EHCI resources while we wait. + * + * REVISIT: Is this safe? NO. This is a bug and needs rethinking. + * We need to lock all of the port-resources (not EHCI common) until + * the transfer is complete. But we can't use the common EHCI exclsem + * or we will deadlock while waiting (because the working thread that + * wakes this thread up needs the exclsem). + */ + + /* REVISIT */ + + imxrt_givesem(&g_ehci.exclsem); + + /* Wait for the IOC completion event */ + + ret = imxrt_ioc_wait(epinfo); + + /* Re-acquire the EHCI semaphore. The caller expects to be holding + * this upon return. + */ + + ret2 = imxrt_takesem_noncancelable(&g_ehci.exclsem); + if (ret >= 0 && ret2 < 0) + { + ret = ret2; + } + +#if 0 /* Does not seem to be needed */ + /* Was there a data buffer? Was this an OUT transfer? */ + + if (buffer != NULL && buflen > 0 && !dirin) + { + /* We have received data from the host -- unless there was an error. + * in any event, we will invalidate the data buffer so that we will + * reload any new data freshly DMAed into the user buffer. + * + * NOTE: This might be un-necessary. We cleaned and invalidated the + * D-Cache prior to starting the DMA so the D-Cache should still be + * invalid in this memory region. + */ + + up_invalidate_dcache((uintptr_t)buffer, (uintptr_t)buffer + buflen); + } +#endif + + /* Did imxrt_ioc_wait() or imxrt_takesem_noncancelable() report an + * error? + */ + + if (ret < 0) + { + usbhost_trace1(EHCI_TRACE1_TRANSFER_FAILED, -ret); + epinfo->iocwait = false; + return (ssize_t)ret; + } + + /* Transfer completed successfully. Return the number of bytes + * transferred. + */ + + return epinfo->xfrd; +} + +/**************************************************************************** + * Name: imxrt_ioc_async_setup + * + * Description: + * Setup to receive an asynchronous notification when a transfer completes. + * + * Input Parameters: + * epinfo - The IN or OUT endpoint descriptor for the device endpoint on + * which the transfer will be performed. + * callback - The function to be called when the completes + * arg - An arbitrary argument that will be provided with the callback. + * + * Returned Value: + * None + * + * Assumptions: + * - Called from the interrupt level + * + ****************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static inline int imxrt_ioc_async_setup(struct imxrt_rhport_s *rhport, + struct imxrt_epinfo_s *epinfo, + usbhost_asynch_t callback, + FAR void *arg) +{ + irqstate_t flags; + int ret = -ENODEV; + + DEBUGASSERT(rhport && epinfo && !epinfo->iocwait && + callback != NULL && epinfo->callback == NULL); + + /* Is the device still connected? */ + + flags = enter_critical_section(); + if (rhport->connected) + { + /* Then save callback information to used when either (1) the + * device is disconnected, or (2) the transfer completes. + */ + + epinfo->iocwait = false; /* No synchronous wakeup */ + epinfo->status = 0; /* No status yet */ + epinfo->xfrd = 0; /* Nothing transferred yet */ + epinfo->result = -EBUSY; /* Transfer in progress */ + epinfo->callback = callback; /* Asynchronous callback */ + epinfo->arg = arg; /* Argument that accompanies the callback */ + ret = OK; /* We are good to go */ + } + + leave_critical_section(flags); + return ret; +} +#endif + +/**************************************************************************** + * Name: imxrt_asynch_completion + * + * Description: + * This function is called at the interrupt level when an asynchronous + * transfer completes. It performs the pending callback. + * + * Input Parameters: + * epinfo - The IN or OUT endpoint descriptor for the device endpoint on + * which the transfer was performed. + * + * Returned Value: + * None + * + * Assumptions: + * - Called from the interrupt level + * + ****************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static void imxrt_asynch_completion(struct imxrt_epinfo_s *epinfo) +{ + usbhost_asynch_t callback; + ssize_t nbytes; + void *arg; + int result; + + DEBUGASSERT(epinfo != NULL && epinfo->iocwait == false && + epinfo->callback != NULL); + + /* Extract and reset the callback info */ + + callback = epinfo->callback; + arg = epinfo->arg; + result = epinfo->result; + nbytes = epinfo->xfrd; + + epinfo->callback = NULL; + epinfo->arg = NULL; + epinfo->result = OK; + epinfo->iocwait = false; + + /* Then perform the callback. Provide the number of bytes successfully + * transferred or the negated errno value in the event of a failure. + */ + + if (result < 0) + { + nbytes = (ssize_t)result; + } + + callback(arg, nbytes); +} +#endif + +/**************************************************************************** + * Name: imxrt_qtd_ioccheck + * + * Description: + * This function is a imxrt_qtd_foreach() callback function. It services + * one qTD in the asynchronous queue. It removes all of the qTD + * structures that are no longer active. + * + ****************************************************************************/ + +static int imxrt_qtd_ioccheck(struct imxrt_qtd_s *qtd, uint32_t **bp, + void *arg) +{ + struct imxrt_epinfo_s *epinfo = (struct imxrt_epinfo_s *)arg; + DEBUGASSERT(qtd && epinfo); + + /* Make sure we reload the QH from memory */ + + up_invalidate_dcache((uintptr_t)&qtd->hw, + (uintptr_t)&qtd->hw + sizeof(struct ehci_qtd_s)); + imxrt_qtd_print(qtd); + + /* Remove the qTD from the list + * + * NOTE that we don't check if the qTD is active nor do we check if there + * are any errors reported in the qTD. If the transfer halted due to + * an error, then qTDs in the list after the error qTD will still appear + * to be active. + */ + + **bp = qtd->hw.nqp; + + /* Subtract the number of bytes left un-transferred. The epinfo->xfrd + * field is initialized to the total number of bytes to be transferred + * (all qTDs in the list). We subtract out the number of un-transferred + * bytes on each transfer and the final result will be the number of bytes + * actually transferred. + */ + + epinfo->xfrd -= (imxrt_swap32(qtd->hw.token) & QTD_TOKEN_NBYTES_MASK) >> + QTD_TOKEN_NBYTES_SHIFT; + + /* Release this QH by returning it to the free list */ + + imxrt_qtd_free(qtd); + return OK; +} + +/**************************************************************************** + * Name: imxrt_qh_ioccheck + * + * Description: + * This function is a imxrt_qh_foreach() callback function. It services + * one QH in the asynchronous queue. It check all attached qTD structures + * and remove all of the structures that are no longer active. if all of + * the qTD structures are removed, then QH itself will also be removed. + * + ****************************************************************************/ + +static int imxrt_qh_ioccheck(struct imxrt_qh_s *qh, uint32_t **bp, void *arg) +{ + struct imxrt_epinfo_s *epinfo; + uint32_t token; + int ret; + + DEBUGASSERT(qh && bp); + + /* Make sure we reload the QH from memory */ + + up_invalidate_dcache((uintptr_t)&qh->hw, + (uintptr_t)&qh->hw + sizeof(struct ehci_qh_s)); + imxrt_qh_print(qh); + + /* Get the endpoint info pointer from the extended QH data. Only the + * g_asynchead QH can have a NULL epinfo field. + */ + + epinfo = qh->epinfo; + DEBUGASSERT(epinfo); + + /* Paragraph 3.6.3: "The nine DWords in [the Transfer Overlay] area + * represent a transaction working space for the host controller. The + * general operational model is that the host controller can detect + * whether the overlay area contains a description of an active transfer. + * If it does not contain an active transfer, then it follows the Queue + * Head Horizontal Link Pointer to the next queue head. The host + * controller will never follow the Next Transfer Queue Element or + * Alternate Queue Element pointers unless it is actively attempting to + * advance the queue ..." + */ + + /* Is the qTD still active? */ + + token = imxrt_swap32(qh->hw.overlay.token); + usbhost_vtrace2(EHCI_VTRACE2_IOCCHECK, epinfo->epno, token); + + if ((token & QH_TOKEN_ACTIVE) != 0) + { + /* Yes... we cannot process the QH while it is still active. Return + * zero to visit the next QH in the list. + */ + + *bp = &qh->hw.hlp; + return OK; + } + + /* Remove all active, attached qTD structures from the inactive QH */ + + ret = imxrt_qtd_foreach(qh, imxrt_qtd_ioccheck, (void *)qh->epinfo); + if (ret < 0) + { + usbhost_trace1(EHCI_TRACE1_QTDFOREACH_FAILED, -ret); + } + + /* If there is no longer anything attached to the QH, then remove it from + * the asynchronous queue. + */ + + if ((imxrt_swap32(qh->fqp) & QTD_NQP_T) != 0) + { + /* Set the forward link of the previous QH to point to the next + * QH in the list. + */ + + **bp = qh->hw.hlp; + up_flush_dcache((uintptr_t)*bp, (uintptr_t)*bp + sizeof(uint32_t)); + + /* Check for errors, update the data toggle */ + + if ((token & QH_TOKEN_ERRORS) == 0) + { + /* No errors.. Save the last data toggle value */ + + epinfo->toggle = (token >> QTD_TOKEN_TOGGLE_SHIFT) & 1; + + /* Report success */ + + epinfo->status = 0; + epinfo->result = OK; + } + else + { + /* An error occurred */ + + epinfo->status = (token & QH_TOKEN_STATUS_MASK) >> + QH_TOKEN_STATUS_SHIFT; + + /* The HALT condition is set on a variety of conditions: babble, + * error counter countdown to zero, or a STALL. If we can rule + * out babble (babble bit not set) and if the error counter is + * non-zero, then we can assume a STALL. In this case, we return + * -PERM to inform the class driver of the stall condition. + */ + + if ((token & (QH_TOKEN_BABBLE | QH_TOKEN_HALTED)) == + QH_TOKEN_HALTED && + (token & QH_TOKEN_CERR_MASK) != 0) + { + /* It is a stall, Note that the data toggle is reset + * after the stall. + */ + + usbhost_trace2(EHCI_TRACE2_EPSTALLED, epinfo->epno, token); + epinfo->result = -EPERM; + epinfo->toggle = 0; + } + else + { + /* Otherwise, it is some kind of data transfer error */ + + usbhost_trace2(EHCI_TRACE2_EPIOERROR, epinfo->epno, token); + epinfo->result = -EIO; + } + } + + /* Is there a thread waiting for this transfer to complete? */ + + if (epinfo->iocwait) + { + /* Yes... wake it up */ + + epinfo->iocwait = false; + imxrt_givesem(&epinfo->iocsem); + } + +#ifdef CONFIG_USBHOST_ASYNCH + /* No.. Is there a pending asynchronous transfer? */ + + else if (epinfo->callback != NULL) + { + /* Yes.. perform the callback */ + + imxrt_asynch_completion(epinfo); + } +#endif + + /* Then release this QH by returning it to the free list */ + + imxrt_qh_free(qh); + } + else + { + /* Otherwise, the horizontal link pointer of this QH will become the + * next back pointer. + */ + + *bp = &qh->hw.hlp; + } + + return OK; +} + +/**************************************************************************** + * Name: imxrt_qtd_cancel + * + * Description: + * This function is a imxrt_qtd_foreach() callback function. It removes + * each qTD attached to a QH. + * + ****************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static int imxrt_qtd_cancel(struct imxrt_qtd_s *qtd, uint32_t **bp, + void *arg) +{ + DEBUGASSERT(qtd != NULL && bp != NULL); + + /* Make sure we reload the QH from memory */ + + up_invalidate_dcache((uintptr_t)&qtd->hw, + (uintptr_t)&qtd->hw + sizeof(struct ehci_qtd_s)); + imxrt_qtd_print(qtd); + + /* Remove the qTD from the list + * + * NOTE that we don't check if the qTD is active nor do we check if there + * are any errors reported in the qTD. If the transfer halted due to + * an error, then qTDs in the list after the error qTD will still appear + * to be active. + * + * REVISIT: There is a race condition here that needs to be resolved. + */ + + **bp = qtd->hw.nqp; + + /* Release this QH by returning it to the free list */ + + imxrt_qtd_free(qtd); + return OK; +} +#endif /* CONFIG_USBHOST_ASYNCH */ + +/**************************************************************************** + * Name: imxrt_qh_cancel + * + * Description: + * This function is a imxrt_qh_foreach() callback function. It cancels + * one QH in the asynchronous queue. It will remove all attached qTD + * structures and remove all of the structures that are no longer active. + * Then QH itself will also be removed. + * + ****************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static int imxrt_qh_cancel(struct imxrt_qh_s *qh, uint32_t **bp, void *arg) +{ + struct imxrt_epinfo_s *epinfo = (struct imxrt_epinfo_s *)arg; + uint32_t regval; + int ret; + + DEBUGASSERT(qh != NULL && bp != NULL && epinfo != NULL); + + /* Make sure we reload the QH from memory */ + + up_invalidate_dcache((uintptr_t)&qh->hw, + (uintptr_t)&qh->hw + sizeof(struct ehci_qh_s)); + imxrt_qh_print(qh); + + /* Check if this is the QH that we are looking for */ + + if (qh->epinfo == epinfo) + { + /* No... keep looking */ + + return OK; + } + + /* Disable both the asynchronous and period schedules */ + + regval = imxrt_getreg(&HCOR->usbcmd); + imxrt_putreg(regval & ~(EHCI_USBCMD_ASEN | EHCI_USBCMD_PSEN), + &HCOR->usbcmd); + + /* Remove the QH from the list + * + * NOTE that we don't check if the qTD is active nor do we check if there + * are any errors reported in the qTD. If the transfer halted due to + * an error, then qTDs in the list after the error qTD will still appear + * to be active. + * + * REVISIT: There is a race condition here that needs to be resolved. + */ + + **bp = qh->hw.hlp; + up_flush_dcache((uintptr_t)*bp, (uintptr_t)*bp + sizeof(uint32_t)); + + /* Re-enable the schedules (if they were enabled before. */ + + imxrt_putreg(regval, &HCOR->usbcmd); + + /* Remove all active, attached qTD structures from the removed QH */ + + ret = imxrt_qtd_foreach(qh, imxrt_qtd_cancel, NULL); + if (ret < 0) + { + usbhost_trace1(EHCI_TRACE1_QTDFOREACH_FAILED, -ret); + } + + /* Then release this QH by returning it to the free list. Return 1 + * to stop the traverse without an error. + */ + + imxrt_qh_free(qh); + return 1; +} +#endif /* CONFIG_USBHOST_ASYNCH */ + +/**************************************************************************** + * Name: imxrt_ioc_bottomhalf + * + * Description: + * EHCI USB Interrupt (USBINT) "Bottom Half" interrupt handler + * + * "The Host Controller sets this bit to 1 on the completion of a USB + * transaction, which results in the retirement of a Transfer Descriptor + * that had its IOC bit set. + * + * "The Host Controller also sets this bit to 1 when a short packet is + * detected (actual number of bytes received was less than the expected + * number of bytes)." + * + * Assumptions: The caller holds the EHCI exclsem + * + ****************************************************************************/ + +static inline void imxrt_ioc_bottomhalf(void) +{ + struct imxrt_qh_s *qh; + uint32_t *bp; + int ret; + + /* Check the Asynchronous Queue + * Make sure that the head of the asynchronous queue is invalidated. + */ + + up_invalidate_dcache((uintptr_t)&g_asynchead.hw, + (uintptr_t)&g_asynchead.hw + + sizeof(struct ehci_qh_s)); + + /* Set the back pointer to the forward QH pointer of the asynchronous + * queue head. + */ + + bp = (uint32_t *)&g_asynchead.hw.hlp; + qh = (struct imxrt_qh_s *) + imxrt_virtramaddr(imxrt_swap32(*bp) & QH_HLP_MASK); + + /* If the asynchronous queue is empty, then the forward point in the + * asynchronous queue head will point back to the queue head. + */ + + if (qh && qh != &g_asynchead) + { + /* Then traverse and operate on every QH and qTD in the asynchronous + * queue + */ + + ret = imxrt_qh_foreach(qh, &bp, imxrt_qh_ioccheck, NULL); + if (ret < 0) + { + usbhost_trace1(EHCI_TRACE1_QHFOREACH_FAILED, -ret); + } + } + +#ifndef CONFIG_USBHOST_INT_DISABLE + + /* Check the Interrupt Queue + * Make sure that the head of the interrupt queue is invalidated. + */ + + up_invalidate_dcache((uintptr_t)&g_intrhead.hw, + (uintptr_t)&g_intrhead.hw + sizeof(struct ehci_qh_s)); + + /* Set the back pointer to the forward qTD pointer of the asynchronous + * queue head. + */ + + bp = (uint32_t *)&g_intrhead.hw.hlp; + qh = (struct imxrt_qh_s *) + imxrt_virtramaddr(imxrt_swap32(*bp) & QH_HLP_MASK); + if (qh) + { + /* Then traverse and operate on every QH and qTD in the asynchronous + * queue. + */ + + ret = imxrt_qh_foreach(qh, &bp, imxrt_qh_ioccheck, NULL); + if (ret < 0) + { + usbhost_trace1(EHCI_TRACE1_QHFOREACH_FAILED, -ret); + } + } +#endif +} + +/**************************************************************************** + * Name: imxrt_portsc_bottomhalf + * + * Description: + * EHCI Port Change Detect "Bottom Half" interrupt handler + * + * "The Host Controller sets this bit to a one when any port for which the + * Port Owner bit is set to zero ... has a change bit transition from a + * zero to a one or a Force Port Resume bit transition from a zero to a + * one as a result of a J-K transition detected on a suspended port. + * This bit will also be set as a result of the Connect Status Change + * being set to a one after system software has relinquished ownership of + * a connected port by writing a one to a port's Port Owner bit... + * + * "This bit is allowed to be maintained in the Auxiliary power well. + * Alternatively, it is also acceptable that on a D3 to D0 transition of + * the EHCI HC device, this bit is loaded with the OR of all of the PORTSC + * change bits (including: Force port resume, over-current change, + * enable/disable change and connect status change)." + * + ****************************************************************************/ + +static inline void imxrt_portsc_bottomhalf(void) +{ + struct imxrt_rhport_s *rhport; + struct usbhost_hubport_s *hport; + uint32_t portsc; + int rhpndx; + + /* Handle root hub status change on each root port */ + + for (rhpndx = 0; rhpndx < IMXRT_EHCI_NRHPORT; rhpndx++) + { + rhport = &g_ehci.rhport[rhpndx]; + portsc = imxrt_getreg(&HCOR->portsc[rhpndx]); + + usbhost_vtrace2(EHCI_VTRACE2_PORTSC, rhpndx + 1, portsc); + + /* Handle port connection status change (CSC) events */ + + if ((portsc & EHCI_PORTSC_CSC) != 0) + { + usbhost_vtrace1(EHCI_VTRACE1_PORTSC_CSC, portsc); + + /* Check current connect status */ + + if ((portsc & EHCI_PORTSC_CCS) != 0) + { + /* Connected ... Did we just become connected? */ + + if (!rhport->connected) + { + /* Yes.. connected. */ + + rhport->connected = true; + + usbhost_vtrace2(EHCI_VTRACE2_PORTSC_CONNECTED, + rhpndx + 1, g_ehci.pscwait); + + /* Notify any waiters */ + + if (g_ehci.pscwait) + { + imxrt_givesem(&g_ehci.pscsem); + g_ehci.pscwait = false; + } + } + else + { + usbhost_vtrace1(EHCI_VTRACE1_PORTSC_CONNALREADY, portsc); + } + } + else + { + /* Disconnected... Did we just become disconnected? */ + + if (rhport->connected) + { + /* Yes.. disconnect the device */ + + usbhost_vtrace2(EHCI_VTRACE2_PORTSC_DISCONND, + rhpndx + 1, g_ehci.pscwait); + + rhport->connected = false; + rhport->lowspeed = false; + + /* Are we bound to a class instance? */ + + hport = &rhport->hport.hport; + if (hport->devclass) + { + /* Yes.. Disconnect the class */ + + CLASS_DISCONNECTED(hport->devclass); + hport->devclass = NULL; + } + + /* Notify any waiters for the Root Hub Status change + * event. + */ + + if (g_ehci.pscwait) + { + imxrt_givesem(&g_ehci.pscsem); + g_ehci.pscwait = false; + } + } + else + { + usbhost_vtrace1(EHCI_VTRACE1_PORTSC_DISCALREADY, portsc); + } + } + } + + /* Clear all pending port interrupt sources by writing a '1' to the + * corresponding bit in the PORTSC register. In addition, we need + * to preserve the values of all R/W bits (RO bits don't matter) + */ + + imxrt_putreg(portsc, &HCOR->portsc[rhpndx]); + } +} + +/**************************************************************************** + * Name: imxrt_syserr_bottomhalf + * + * Description: + * EHCI Host System Error "Bottom Half" interrupt handler + * + * "The Host Controller sets this bit to 1 when a serious error occurs + * during a host system access involving the Host Controller module. ... + * When this error occurs, the Host Controller clears the Run/Stop bit in + * the Command register to prevent further execution of the scheduled TDs." + * + ****************************************************************************/ + +static inline void imxrt_syserr_bottomhalf(void) +{ + usbhost_trace1(EHCI_TRACE1_SYSERR_INTR, 0); + DEBUGPANIC(); +} + +/**************************************************************************** + * Name: imxrt_async_advance_bottomhalf + * + * Description: + * EHCI Async Advance "Bottom Half" interrupt handler + * + * "System software can force the host controller to issue an interrupt the + * next time the host controller advances the asynchronous schedule by + * writing a one to the Interrupt on Async Advance Doorbell bit in the + * USBCMD register. This status bit indicates the assertion of that + * interrupt source." + * + ****************************************************************************/ + +static inline void imxrt_async_advance_bottomhalf(void) +{ + usbhost_vtrace1(EHCI_VTRACE1_AAINTR, 0); + + /* REVISIT: Could remove all tagged QH entries here */ +} + +/**************************************************************************** + * Name: imxrt_ehci_bottomhalf + * + * Description: + * EHCI "Bottom Half" interrupt handler. Runs on a work queue thread. + * + ****************************************************************************/ + +static void imxrt_ehci_bottomhalf(FAR void *arg) +{ + uint32_t pending = (uint32_t)arg; + + /* We need to have exclusive access to the EHCI data structures. Waiting + * here is not a good thing to do on the worker thread, but there is no + * real option (other than to reschedule and delay). + */ + + imxrt_takesem_noncancelable(&g_ehci.exclsem); + + /* Handle all unmasked interrupt sources + * USB Interrupt (USBINT) + * + * "The Host Controller sets this bit to 1 on the completion of a USB + * transaction, which results in the retirement of a Transfer Descriptor + * that had its IOC bit set. + * + * "The Host Controller also sets this bit to 1 when a short packet is + * detected (actual number of bytes received was less than the expected + * number of bytes)." + * + * USB Error Interrupt (USBERRINT) + * + * "The Host Controller sets this bit to 1 when completion of a USB + * transaction results in an error condition (e.g., error counter + * underflow). If the TD on which the error interrupt occurred also + * had its IOC bit set, both this bit and USBINT bit are set. ..." + * + * We do the same thing in either case: Traverse the asynchronous queue + * and remove all of the transfers that are no longer active. + */ + + if ((pending & (EHCI_INT_USBINT | EHCI_INT_USBERRINT)) != 0) + { + if ((pending & EHCI_INT_USBERRINT) != 0) + { + usbhost_trace1(EHCI_TRACE1_USBERR_INTR, pending); + } + else + { + usbhost_vtrace1(EHCI_VTRACE1_USBINTR, pending); + } + + imxrt_ioc_bottomhalf(); + } + + /* Port Change Detect + * + * "The Host Controller sets this bit to a one when any port for which + * the Port Owner bit is set to zero ... has a change bit transition + * from a zero to a one or a Force Port Resume bit transition from a zero + * to a one as a result of a J-K transition detected on a suspended port. + * This bit will also be set as a result of the Connect Status Change + * being set to a one after system software has relinquished ownership + * of a connected port by writing a one to a port's Port Owner bit... + * + * "This bit is allowed to be maintained in the Auxiliary power well. + * Alternatively, it is also acceptable that on a D3 to D0 transition + * of the EHCI HC device, this bit is loaded with the OR of all of the + * PORTSC change bits (including: Force port resume, over-current change, + * enable/disable change and connect status change)." + */ + + if ((pending & EHCI_INT_PORTSC) != 0) + { + imxrt_portsc_bottomhalf(); + } + + /* Frame List Rollover + * + * "The Host Controller sets this bit to a one when the Frame List Index + * ... rolls over from its maximum value to zero. The exact value at + * which the rollover occurs depends on the frame list size. For example, + * if the frame list size (as programmed in the Frame List Size field of + * the USBCMD register) is 1024, the Frame Index Register rolls over + * every time FRINDEX[13] toggles. Similarly, if the size is 512, the + * Host Controller sets this bit to a one every time FRINDEX[12] + * toggles." + */ + +#if 0 /* Not used */ + if ((pending & EHCI_INT_FLROLL) != 0) + { + imxrt_flroll_bottomhalf(); + } +#endif + + /* Host System Error + * + * "The Host Controller sets this bit to 1 when a serious error occurs + * during a host system access involving the Host Controller module. ... + * When this error occurs, the Host Controller clears the Run/Stop bit + * in the Command register to prevent further execution of the scheduled + * TDs." + */ + + if ((pending & EHCI_INT_SYSERROR) != 0) + { + uerr("Syserror\n"); + imxrt_syserr_bottomhalf(); + } + + /* Interrupt on Async Advance + * + * "System software can force the host controller to issue an interrupt + * the next time the host controller advances the asynchronous schedule + * by writing a one to the Interrupt on Async Advance Doorbell bit in + * the USBCMD register. This status bit indicates the assertion of that + * interrupt source." + */ + + if ((pending & EHCI_INT_AAINT) != 0) + { + uerr("Async Advance\n"); + imxrt_async_advance_bottomhalf(); + } + + /* We are done with the EHCI structures */ + + imxrt_givesem(&g_ehci.exclsem); + + /* Re-enable relevant EHCI interrupts. Interrupts should still be enabled + * at the level of the interrupt controller. + */ + + imxrt_putreg(EHCI_HANDLED_INTS, &HCOR->usbintr); +} + +/**************************************************************************** + * Name: imxrt_ehci_interrupt + * + * Description: + * EHCI "Top Half" interrupt handler + * + ****************************************************************************/ + +static int imxrt_ehci_interrupt(int irq, FAR void *context, FAR void *arg) +{ + uint32_t usbsts; + uint32_t pending; + uint32_t regval; + + /* Read Interrupt Status and mask out interrupts that are not enabled. */ + + usbsts = imxrt_getreg(&HCOR->usbsts); + regval = imxrt_getreg(&HCOR->usbintr); + +#ifdef CONFIG_USBHOST_TRACE + usbhost_vtrace1(EHCI_VTRACE1_TOPHALF, usbsts & regval); +#else + uinfo("USBSTS: %08x USBINTR: %08x\n", usbsts, regval); +#endif + + /* Handle all unmasked interrupt sources */ + + pending = usbsts & regval; + if (pending != 0) + { + /* Schedule interrupt handling work for the high priority worker + * thread so that we are not pressed for time and so that we can + * interrupt with other USB threads gracefully. + * + * The worker should be available now because we implement a handshake + * by controlling the EHCI interrupts. + */ + + DEBUGASSERT(work_available(&g_ehci.work)); + DEBUGVERIFY(work_queue(HPWORK, &g_ehci.work, imxrt_ehci_bottomhalf, + (FAR void *)pending, 0)); + + /* Disable further EHCI interrupts so that we do not overrun the work + * queue. + */ + + imxrt_putreg(0, &HCOR->usbintr); + + /* Clear all pending status bits by writing the value of the pending + * interrupt bits back to the status register. + */ + + imxrt_putreg(usbsts & EHCI_INT_ALLINTS, &HCOR->usbsts); + } + + return OK; +} + +/**************************************************************************** + * Name: imxrt_wait + * + * Description: + * Wait for a device to be connected or disconnected to/from a hub port. + * + * Input Parameters: + * conn - The USB host connection instance obtained as a parameter from the + * call to the USB driver initialization logic. + * hport - The location to return the hub port descriptor that detected the + * connection related event. + * + * Returned Value: + * Zero (OK) is returned on success when a device is connected or + * disconnected. This function will not return until either (1) a device is + * connected or disconnect to/from any hub port or until (2) some failure + * occurs. On a failure, a negated errno value is returned indicating the + * nature of the failure + * + * Assumptions: + * - Called from a single thread so no mutual exclusion is required. + * - Never called from an interrupt handler. + * + ****************************************************************************/ + +static int imxrt_wait(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s **hport) +{ + irqstate_t flags; + int rhpndx; + int ret; + + /* Loop until the connection state changes on one of the root hub ports or + * until an error occurs. + */ + + flags = enter_critical_section(); + for (; ; ) + { + /* Check for a change in the connection state on any root hub port */ + + for (rhpndx = 0; rhpndx < IMXRT_EHCI_NRHPORT; rhpndx++) + { + struct imxrt_rhport_s *rhport; + struct usbhost_hubport_s *connport; + + /* Has the connection state changed on the RH port? */ + + rhport = &g_ehci.rhport[rhpndx]; + connport = &rhport->hport.hport; + if (rhport->connected != connport->connected) + { + /* Yes.. Return the RH port to inform the caller which + * port has the connection change. + */ + + connport->connected = rhport->connected; + *hport = connport; + leave_critical_section(flags); + + usbhost_vtrace2(EHCI_VTRACE2_MONWAKEUP, + rhpndx + 1, rhport->connected); + return OK; + } + } + +#ifdef CONFIG_USBHOST_HUB + /* Is a device connected to an external hub? */ + + if (g_ehci.hport) + { + volatile struct usbhost_hubport_s *connport; + + /* Yes.. return the external hub port */ + + connport = g_ehci.hport; + g_ehci.hport = NULL; + + *hport = (struct usbhost_hubport_s *)connport; + leave_critical_section(flags); + + usbhost_vtrace2(EHCI_VTRACE2_MONWAKEUP, + connport->port + 1, connport->connected); + return OK; + } +#endif + + /* No changes on any port. Wait for a connection/disconnection event + * and check again + */ + + g_ehci.pscwait = true; + ret = imxrt_takesem(&g_ehci.pscsem); + if (ret < 0) + { + return ret; + } + } +} + +/**************************************************************************** + * Name: imxrt_enumerate + * + * Description: + * Enumerate the connected device. As part of this enumeration process, + * the driver will (1) get the device's configuration descriptor, (2) + * extract the class ID info from the configuration descriptor, (3) call + * usbhost_findclass() to find the class that supports this device, (4) + * call the create() method on the struct usbhost_registry_s interface + * to get a class instance, and finally (5) call the connect() method + * of the struct usbhost_class_s interface. After that, the class is in + * charge of the sequence of operations. + * + * Input Parameters: + * conn - The USB host connection instance obtained as a parameter from + * the call to the USB driver initialization logic. + * hport - The descriptor of the hub port that has the newly connected + * device. + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ****************************************************************************/ + +static int imxrt_rh_enumerate(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport) +{ + struct imxrt_rhport_s *rhport; + volatile uint32_t *regaddr; + uint32_t regval; + int rhpndx; + + DEBUGASSERT(conn != NULL && hport != NULL); + rhpndx = hport->port; + + DEBUGASSERT(rhpndx >= 0 && rhpndx < IMXRT_EHCI_NRHPORT); + rhport = &g_ehci.rhport[rhpndx]; + + /* Are we connected to a device? The caller should have called the wait() + * method first to be assured that a device is connected. + */ + + while (!rhport->connected) + { + /* No, return an error */ + + usbhost_vtrace1(EHCI_VTRACE1_ENUM_DISCONN, 0); + return -ENODEV; + } + + /* USB 2.0 spec says at least 50ms delay before port reset. + * REVISIT: I think this is wrong. It needs to hold the port in + * reset for 50Msec, not wait 50Msec before resetting. + */ + + nxsig_usleep(100 * 1000); + + /* Paragraph 2.3.9: + * + * "Line Status ... These bits reflect the current logical levels of the + * D+ (bit 11) and D- (bit 10) signal lines. These bits are used for + * detection of low-speed USB devices prior to the port reset and enable + * sequence. This field is valid only when the port enable bit is zero + * and the current connect status bit is set to a one." + * + * Bits[11:10] USB State Interpretation + * ----------- --------- -------------- + * 00b SE0 Not Low-speed device, perform EHCI reset + * 10b J-state Not Low-speed device, perform EHCI reset + * 01b K-state Low-speed device, release ownership of port + * + * NOTE: Low-speed devices could be detected by examining the PORTSC PSPD + * field after resetting the device. The more conventional way here, + * however, also appears to work. + */ + + regval = imxrt_getreg(&HCOR->portsc[rhpndx]); + if ((regval & EHCI_PORTSC_LSTATUS_MASK) == EHCI_PORTSC_LSTATUS_KSTATE) + { + /* EHCI Paragraph 2.3.9: + * + * "Port Owner ... This bit unconditionally goes to a 0b when the + * Configured bit in the CONFIGFLAG register makes a 0b to 1b + * transition. This bit unconditionally goes to 1b whenever the + * Configured bit is zero. + * + * "System software uses this field to release ownership of the + * port to a selected host controller (in the event that the + * attached device is not a high-speed device). Software writes + * a one to this bit when the attached device is not a high-speed + * device. A one in this bit means that a companion host + * controller owns and controls the port. .... + * + * EHCI Paragraph 4.2: + * + * "When a port is routed to a companion HC, it remains under the + * control of the companion HC until the device is disconnected + * from the root por ... When a disconnect occurs, the disconnect + * event is detected by both the companion HC port control and the + * EHCI port ownership control. On the event, the port ownership + * is returned immediately to the EHCI controller. The companion + * HC stack detects the disconnect and acknowledges as it would + * in an ordinary standalone implementation. Subsequent connects + * will be detected by the EHCI port register and the process will + * repeat." + */ + + hport->speed = USB_SPEED_LOW; + } + else + { + /* Assume full-speed for now */ + + hport->speed = USB_SPEED_FULL; + } + + /* Put the root hub port in reset. + * + * EHCI Paragraph 2.3.9: + * + * "The HCHalted bit in the USBSTS register should be a zero before + * software attempts to use [the Port Reset] bit. The host controller + * may hold Port Reset asserted to a one when the HCHalted bit is a one. + */ + + DEBUGASSERT((imxrt_getreg(&HCOR->usbsts) & EHCI_USBSTS_HALTED) == 0); + + /* EHCI paragraph 2.3.9: + * + * "When software writes a one to [the Port Reset] bit (from a zero), the + * bus reset sequence as defined in the USB Specification Revision 2.0 + * is started. Software writes a zero to this bit to terminate the bus + * reset sequence. Software must keep this bit at a one long enough to + * ensure the reset sequence, as specified in the USB Specification + * Revision 2.0, completes. Note: when software writes this bit to a + * one, it must also write a zero to the Port Enable bit." + */ + + regaddr = &HCOR->portsc[RHPNDX(rhport)]; + regval = imxrt_getreg(regaddr); + regval &= ~EHCI_PORTSC_PE; + regval |= EHCI_PORTSC_RESET; + imxrt_putreg(regval, regaddr); + + /* USB 2.0 "Root hubs must provide an aggregate reset period of at least + * 50 ms." + */ + + nxsig_usleep(50 * 1000); + + regval = imxrt_getreg(regaddr); + regval &= ~EHCI_PORTSC_RESET; + imxrt_putreg(regval, regaddr); + + /* Wait for the port reset to complete + * + * EHCI Paragraph 2.3.9: + * + * "Note that when software writes a zero to this bit there may be a + * delay before the bit status changes to a zero. The bit status will + * not read as a zero until after the reset has completed. If the port + * is in high-speed mode after reset is complete, the host controller + * will automatically enable this port (e.g. set the Port Enable bit + * to a one). A host controller must terminate the reset and stabilize + * the state of the port within 2 milliseconds of software transitioning + * this bit from a one to a zero ..." + */ + + while ((imxrt_getreg(regaddr) & EHCI_PORTSC_RESET) != 0); + nxsig_usleep(200 * 1000); + + /* EHCI Paragraph 4.2.2: + * + * "... The reset process is actually complete when software reads a zero + * in the PortReset bit. The EHCI Driver checks the PortEnable bit in + * the PORTSC register. If set to a one, the connected device is a high- + * speed device and EHCI Driver (root hub emulator) issues a change + * report to the hub driver and the hub driver continues to enumerate + * the attached device." + * + * "At the time the EHCI Driver receives the port reset and enable request + * the LineStatus bits might indicate a low-speed device. Additionally, + * when the port reset process is complete, the PortEnable field may + * indicate that a full-speed device is attached. In either case the EHCI + * driver sets the PortOwner bit in the PORTSC register to a one to + * release port ownership to a companion host controller." + * + * LPC31xx User Manual Paragraph 6.1.3: + * + * "In a standard EHCI controller design, the EHCI host controller driver + * detects a Full speed (FS) or Low speed (LS) device by noting if the + * port enable bit is set after the port reset operation. The port enable + * will only be set in a standard EHCI controller implementation after + * the port reset operation and when the host and device negotiate a + * High-Speed connection (i.e. Chirp completes successfully). Since this + * controller has an embedded Transaction Translator, the port enable + * will always be set after the port reset operation regardless of the + * result of the host device chirp result and the resulting port speed + * will be indicated by the PSPD field in PORTSC1. + */ + + regval = imxrt_getreg(&HCOR->portsc[rhpndx]); + + if ((regval & USBDEV_PRTSC1_PSPD_MASK) == USBDEV_PRTSC1_PSPD_HS) + { + /* High speed device */ + + hport->speed = USB_SPEED_HIGH; + } + else if ((regval & USBDEV_PRTSC1_PSPD_MASK) == USBDEV_PRTSC1_PSPD_FS) + { + /* Low- or Full- speed device. Set the port ownership bit. + * + * EHCI Paragraph 4.2: + * + * "When a port is routed to a companion HC, it remains under the + * control of the companion HC until the device is disconnected + * from the root por ... When a disconnect occurs, the disconnect + * event is detected by both the companion HC port control and the + * EHCI port ownership control. On the event, the port ownership + * is returned immediately to the EHCI controller. The companion + * HC stack detects the disconnect and acknowledges as it would + * in an ordinary standalone implementation. Subsequent connects + * will be detected by the EHCI port register and the process will + * repeat." + */ + + DEBUGASSERT(hport->speed == USB_SPEED_FULL); + } + + /* Otherwise it must be a low speed device */ + + else + { + DEBUGASSERT(hport->speed == USB_SPEED_LOW); + DEBUGASSERT((regval & USBDEV_PRTSC1_PSPD_MASK) == + USBDEV_PRTSC1_PSPD_LS); + } + + return OK; +} + +static int imxrt_enumerate(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport) +{ + int ret; + + /* If this is a connection on the root hub, then we need to go to + * little more effort to get the device speed. If it is a connection + * on an external hub, then we already have that information. + */ + + DEBUGASSERT(hport); +#ifdef CONFIG_USBHOST_HUB + if (ROOTHUB(hport)) +#endif + { + ret = imxrt_rh_enumerate(conn, hport); + if (ret < 0) + { + return ret; + } + } + + /* Then let the common usbhost_enumerate do the real enumeration. */ + + usbhost_vtrace1(EHCI_VTRACE1_CLASSENUM, hport->port); + ret = usbhost_enumerate(hport, &hport->devclass); + if (ret < 0) + { + /* Failed to enumerate */ + + usbhost_trace2(EHCI_TRACE2_CLASSENUM_FAILED, hport->port + 1, -ret); + + /* If this is a root hub port, then marking the hub port not connected + * will cause imxrt_wait() to return and we will try the connection + * again. + */ + + hport->connected = false; + } + + return ret; +} + +/**************************************************************************** + * Name: imxrt_ep0configure + * + * Description: + * Configure endpoint 0. This method is normally used internally by the + * enumerate() method but is made available at the interface to support + * an external implementation of the enumeration logic. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the + * call to the class create() method. + * funcaddr - The USB address of the function containing the endpoint that + * EP0 controls. A funcaddr of zero will be received if no address is + * yet assigned to the device. + * speed - The speed of the port USB_SPEED_LOW, _FULL, or _HIGH + * maxpacketsize - The maximum number of bytes that can be sent to or + * received from the endpoint in a single data packet + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure. + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ****************************************************************************/ + +static int imxrt_ep0configure(FAR struct usbhost_driver_s *drvr, + usbhost_ep_t ep0, uint8_t funcaddr, + uint8_t speed, uint16_t maxpacketsize) +{ + struct imxrt_epinfo_s *epinfo = (struct imxrt_epinfo_s *)ep0; + int ret; + + DEBUGASSERT(drvr != NULL && epinfo != NULL && maxpacketsize < 2048); + + /* We must have exclusive access to the EHCI data structures. */ + + ret = imxrt_takesem(&g_ehci.exclsem); + if (ret >= 0) + { + /* Remember the new device address and max packet size */ + + epinfo->devaddr = funcaddr; + epinfo->speed = speed; + epinfo->maxpacket = maxpacketsize; + + imxrt_givesem(&g_ehci.exclsem); + } + + return ret; +} + +/**************************************************************************** + * Name: imxrt_epalloc + * + * Description: + * Allocate and configure one endpoint. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the + * call to the class create() method. + * epdesc - Describes the endpoint to be allocated. + * ep - A memory location provided by the caller in which to receive the + * allocated endpoint descriptor. + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure. + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ****************************************************************************/ + +static int imxrt_epalloc(FAR struct usbhost_driver_s *drvr, + const FAR struct usbhost_epdesc_s *epdesc, + usbhost_ep_t *ep) +{ + struct imxrt_epinfo_s *epinfo; + struct usbhost_hubport_s *hport; + + /* Sanity check. NOTE that this method should only be called if a device + * is connected (because we need a valid low speed indication). + */ + + DEBUGASSERT(drvr != 0 && epdesc != NULL && epdesc->hport != NULL && + ep != NULL); + hport = epdesc->hport; + + /* Terse output only if we are tracing */ + +#ifdef CONFIG_USBHOST_TRACE + usbhost_vtrace2(EHCI_VTRACE2_EPALLOC, epdesc->addr, epdesc->xfrtype); +#else + uinfo("EP%d DIR=%s FA=%08x TYPE=%d Interval=%d MaxPacket=%d\n", + epdesc->addr, epdesc->in ? "IN" : "OUT", hport->funcaddr, + epdesc->xfrtype, epdesc->interval, epdesc->mxpacketsize); +#endif + + /* Allocate a endpoint information structure */ + + epinfo = (struct imxrt_epinfo_s *) + kmm_zalloc(sizeof(struct imxrt_epinfo_s)); + if (!epinfo) + { + usbhost_trace1(EHCI_TRACE1_EPALLOC_FAILED, 0); + return -ENOMEM; + } + + /* Initialize the endpoint container (which is really just another form of + * 'struct usbhost_epdesc_s', packed differently and with additional + * information. A cleaner design might just embed struct usbhost_epdesc_s + * inside of struct imxrt_epinfo_s and just memcpy() here. + */ + + epinfo->epno = epdesc->addr; + epinfo->dirin = epdesc->in; + epinfo->devaddr = hport->funcaddr; +#ifndef CONFIG_USBHOST_INT_DISABLE + epinfo->interval = epdesc->interval; +#endif + epinfo->maxpacket = epdesc->mxpacketsize; + epinfo->xfrtype = epdesc->xfrtype; + epinfo->speed = hport->speed; + + /* The iocsem semaphore is used for signaling and, hence, should not have + * priority inheritance enabled. + */ + + nxsem_init(&epinfo->iocsem, 0, 0); + nxsem_set_protocol(&epinfo->iocsem, SEM_PRIO_NONE); + + /* Success.. return an opaque reference to the endpoint information + * structure instance + */ + + *ep = (usbhost_ep_t)epinfo; + return OK; +} + +/**************************************************************************** + * Name: imxrt_epfree + * + * Description: + * Free and endpoint previously allocated by DRVR_EPALLOC. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the + * call to the class create() method. + * ep - The endpint to be freed. + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ****************************************************************************/ + +static int imxrt_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep) +{ + struct imxrt_epinfo_s *epinfo = (struct imxrt_epinfo_s *)ep; + + /* There should not be any pending, transfers */ + + DEBUGASSERT(drvr && epinfo && epinfo->iocwait == 0); + + /* Free the container */ + + kmm_free(epinfo); + return OK; +} + +/**************************************************************************** + * Name: imxrt_alloc + * + * Description: + * Some hardware supports special memory in which request and descriptor + * data can be accessed more efficiently. This method provides a + * mechanism to allocate the request/descriptor memory. If the underlying + * hardware does not support such "special" memory, this functions may + * simply map to kmm_malloc(). + * + * This interface was optimized under a particular assumption. It was + * assumed that the driver maintains a pool of small, pre-allocated buffers + * for descriptor traffic. NOTE that size is not an input, but an output: + * The size of the pre-allocated buffer is returned. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the + * call to the class create() method. + * buffer - The address of a memory location provided by the caller in + * which to return the allocated buffer memory address. + * maxlen - The address of a memory location provided by the caller in + * which to return the maximum size of the allocated buffer memory. + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure + * + * Assumptions: + * - Called from a single thread so no mutual exclusion is required. + * - Never called from an interrupt handler. + * + ****************************************************************************/ + +static int imxrt_alloc(FAR struct usbhost_driver_s *drvr, + FAR uint8_t **buffer, FAR size_t *maxlen) +{ + int ret = -ENOMEM; + DEBUGASSERT(drvr && buffer && maxlen); + + /* The only special requirements for transfer/descriptor buffers are that + * (1) they be aligned to a cache line boundary and (2) they are a + * multiple of the cache line size in length. + */ + + *buffer = (FAR uint8_t *)kmm_memalign(ARMV7M_DCACHE_LINESIZE, + IMXRT_EHCI_BUFSIZE); + if (*buffer) + { + *maxlen = IMXRT_EHCI_BUFSIZE; + ret = OK; + } + + return ret; +} + +/**************************************************************************** + * Name: imxrt_free + * + * Description: + * Some hardware supports special memory in which request and descriptor + * data can be accessed more efficiently. This method provides a + * mechanism to free that request/descriptor memory. If the underlying + * hardware does not support such "special" memory, this functions may + * simply map to kmm_free(). + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the + * call to the class create() method. + * buffer - The address of the allocated buffer memory to be freed. + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure + * + * Assumptions: + * - Never called from an interrupt handler. + * + ****************************************************************************/ + +static int imxrt_free(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer) +{ + DEBUGASSERT(drvr && buffer); + + /* No special action is require to free the transfer/descriptor buffer + * memory + */ + + kmm_free(buffer); + return OK; +} + +/**************************************************************************** + * Name: imxrt_ioalloc + * + * Description: + * Some hardware supports special memory in which larger IO buffers can + * be accessed more efficiently. This method provides a mechanism to + * allocate the request/descriptor memory. If the underlying hardware + * does not support such "special" memory, this functions may simply map + * to kumm_malloc. + * + * This interface differs from DRVR_ALLOC in that the buffers are variable- + * sized. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the + * call to the class create() method. + * buffer - The address of a memory location provided by the caller in + * which to return the allocated buffer memory address. + * buflen - The size of the buffer required. + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ****************************************************************************/ + +static int imxrt_ioalloc(FAR struct usbhost_driver_s *drvr, + FAR uint8_t **buffer, size_t buflen) +{ + DEBUGASSERT(drvr && buffer && buflen > 0); + + /* The only special requirements for I/O buffers are that (1) they be + * aligned to a cache line boundary, (2) they are a multiple of the cache + * line size in length, and (3) they might need to be user accessible + * (depending on how the class driver implements its buffering). + */ + + buflen = (buflen + DCACHE_LINEMASK) & ~DCACHE_LINEMASK; + *buffer = (FAR uint8_t *)kumm_memalign(ARMV7M_DCACHE_LINESIZE, buflen); + return *buffer ? OK : -ENOMEM; +} + +/**************************************************************************** + * Name: imxrt_iofree + * + * Description: + * Some hardware supports special memory in which IO data can be accessed + * more efficiently. This method provides a mechanism to free that IO + * buffer memory. If the underlying hardware does not support such + * "special" memory, this functions may simply map to kumm_free(). + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the + * call to the class create() method. + * buffer - The address of the allocated buffer memory to be freed. + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ****************************************************************************/ + +static int imxrt_iofree(FAR struct usbhost_driver_s *drvr, + FAR uint8_t *buffer) +{ + DEBUGASSERT(drvr && buffer); + + /* No special action is require to free the I/O buffer memory */ + + kumm_free(buffer); + return OK; +} + +/**************************************************************************** + * Name: imxrt_ctrlin and imxrt_ctrlout + * + * Description: + * Process a IN or OUT request on the control endpoint. These methods + * will enqueue the request and wait for it to complete. Only one + * transfer may be queued; Neither these methods nor the transfer() method + * can be called again until the control transfer functions returns. + * + * These are blocking methods; these functions will not return until the + * control transfer has completed. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the + * call to the class create() method. + * ep0 - The control endpoint to send/receive the control request. + * req - Describes the request to be sent. This request must lie in + * memory created by DRVR_ALLOC. + * buffer - A buffer used for sending the request and for returning any + * responses. This buffer must be large enough to hold the + * length value in the request description. buffer must have been + * allocated using DRVR_ALLOC. + * + * NOTE: On an IN transaction, req and buffer may refer to the same + * allocated memory. + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure + * + * Assumptions: + * - Called from a single thread so no mutual exclusion is required. + * - Never called from an interrupt handler. + * + ****************************************************************************/ + +static int imxrt_ctrlin(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, + FAR const struct usb_ctrlreq_s *req, + FAR uint8_t *buffer) +{ + struct imxrt_rhport_s *rhport = (struct imxrt_rhport_s *)drvr; + struct imxrt_epinfo_s *ep0info = (struct imxrt_epinfo_s *)ep0; + uint16_t len; + ssize_t nbytes; + int ret; + + DEBUGASSERT(rhport != NULL && ep0info != NULL && req != NULL); + + len = imxrt_read16(req->len); + + /* Terse output only if we are tracing */ + +#ifdef CONFIG_USBHOST_TRACE + usbhost_vtrace2(EHCI_VTRACE2_CTRLINOUT, RHPORT(rhport), req->req); +#else + uinfo("RHPort%d type: %02x req: %02x value: %02x%02x index: %02x%02x " + "len: %04x\n", + RHPORT(rhport), req->type, req->req, req->value[1], req->value[0], + req->index[1], req->index[0], len); +#endif + + /* We must have exclusive access to the EHCI hardware and data + * structures. + */ + + ret = imxrt_takesem(&g_ehci.exclsem); + if (ret < 0) + { + return ret; + } + + /* Set the request for the IOC event well BEFORE initiating the transfer. */ + + ret = imxrt_ioc_setup(rhport, ep0info); + if (ret != OK) + { + usbhost_trace1(EHCI_TRACE1_DEVDISCONNECTED, -ret); + goto errout_with_sem; + } + + /* Now initiate the transfer */ + + ret = imxrt_async_setup(rhport, ep0info, req, buffer, len); + if (ret < 0) + { + uerr("ERROR: imxrt_async_setup failed: %d\n", ret); + goto errout_with_iocwait; + } + + /* And wait for the transfer to complete */ + + nbytes = imxrt_transfer_wait(ep0info); + imxrt_givesem(&g_ehci.exclsem); + return nbytes >= 0 ? OK : (int)nbytes; + +errout_with_iocwait: + ep0info->iocwait = false; +errout_with_sem: + imxrt_givesem(&g_ehci.exclsem); + return ret; +} + +static int imxrt_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, + FAR const struct usb_ctrlreq_s *req, + FAR const uint8_t *buffer) +{ + /* imxrt_ctrlin can handle both directions. We just need to work around + * the differences in the function signatures. + */ + + return imxrt_ctrlin(drvr, ep0, req, (uint8_t *)buffer); +} + +/**************************************************************************** + * Name: imxrt_transfer + * + * Description: + * Process a request to handle a transfer descriptor. This method will + * enqueue the transfer request, blocking until the transfer completes. + * Only one transfer may be queued; Neither this method nor the ctrlin or + * ctrlout methods can be called again until this function returns. + * + * This is a blocking method; this functions will not return until the + * transfer has completed. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the + * call to the class create() method. + * ep - The IN or OUT endpoint descriptor for the device endpoint on + * which to perform the transfer. + * buffer - A buffer containing the data to be sent (OUT endpoint) or + * received (IN endpoint). buffer must have been allocated using + * DRVR_ALLOC + * buflen - The length of the data to be sent or received. + * + * Returned Value: + * On success, a non-negative value is returned that indicates the number + * of bytes successfully transferred. On a failure, a negated errno value + * is returned that indicates the nature of the failure: + * + * EAGAIN - If devices NAKs the transfer (or NYET or other error where + * it may be appropriate to restart the entire transaction). + * EPERM - If the endpoint stalls + * EIO - On a TX or data toggle error + * EPIPE - Overrun errors + * + * Assumptions: + * - Called from a single thread so no mutual exclusion is required. + * - Never called from an interrupt handler. + * + ****************************************************************************/ + +static ssize_t imxrt_transfer(FAR struct usbhost_driver_s *drvr, + usbhost_ep_t ep, FAR uint8_t *buffer, + size_t buflen) +{ + struct imxrt_rhport_s *rhport = (struct imxrt_rhport_s *)drvr; + struct imxrt_epinfo_s *epinfo = (struct imxrt_epinfo_s *)ep; + ssize_t nbytes; + int ret; + + DEBUGASSERT(rhport && epinfo && buffer && buflen > 0); + + /* We must have exclusive access to the EHCI hardware and data + * structures. + */ + + ret = imxrt_takesem(&g_ehci.exclsem); + if (ret < 0) + { + return (ssize_t)ret; + } + + /* Set the request for the IOC event well BEFORE initiating the + * transfer. + */ + + ret = imxrt_ioc_setup(rhport, epinfo); + if (ret != OK) + { + usbhost_trace1(EHCI_TRACE1_DEVDISCONNECTED, -ret); + goto errout_with_sem; + } + + /* Initiate the transfer */ + + switch (epinfo->xfrtype) + { + case USB_EP_ATTR_XFER_BULK: + ret = imxrt_async_setup(rhport, epinfo, NULL, buffer, buflen); + break; + +#ifndef CONFIG_USBHOST_INT_DISABLE + case USB_EP_ATTR_XFER_INT: + ret = imxrt_intr_setup(rhport, epinfo, buffer, buflen); + break; +#endif + +#ifndef CONFIG_USBHOST_ISOC_DISABLE + case USB_EP_ATTR_XFER_ISOC: +# warning "Isochronous endpoint support not emplemented" +#endif + case USB_EP_ATTR_XFER_CONTROL: + default: + usbhost_trace1(EHCI_TRACE1_BADXFRTYPE, epinfo->xfrtype); + ret = -ENOSYS; + break; + } + + /* Check for errors in the setup of the transfer */ + + if (ret < 0) + { + goto errout_with_iocwait; + } + + /* Then wait for the transfer to complete */ + + nbytes = imxrt_transfer_wait(epinfo); + imxrt_givesem(&g_ehci.exclsem); + return nbytes; + +errout_with_iocwait: + epinfo->iocwait = false; +errout_with_sem: + uerr("!!!\n"); + imxrt_givesem(&g_ehci.exclsem); + return (ssize_t)ret; +} + +/**************************************************************************** + * Name: imxrt_asynch + * + * Description: + * Process a request to handle a transfer descriptor. This method will + * enqueue the transfer request and return immediately. When the transfer + * completes, the callback will be invoked with the provided transfer. + * This method is useful for receiving interrupt transfers which may come + * infrequently. + * + * Only one transfer may be queued; Neither this method nor the ctrlin or + * ctrlout methods can be called again until the transfer completes. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from + * the call to the class create() method. + * ep - The IN or OUT endpoint descriptor for the device endpoint on + * which to perform the transfer. + * buffer - A buffer containing the data to be sent (OUT endpoint) or + * received (IN endpoint). buffer must have been allocated + * using DRVR_ALLOC + * buflen - The length of the data to be sent or received. + * callback - This function will be called when the transfer completes. + * arg - The arbitrary parameter that will be passed to the callback + * function when the transfer completes. + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure + * + * Assumptions: + * - Called from a single thread so no mutual exclusion is required. + * - Never called from an interrupt handler. + * + ****************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static int imxrt_asynch(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, + FAR uint8_t *buffer, size_t buflen, + usbhost_asynch_t callback, FAR void *arg) +{ + struct imxrt_rhport_s *rhport = (struct imxrt_rhport_s *)drvr; + struct imxrt_epinfo_s *epinfo = (struct imxrt_epinfo_s *)ep; + int ret; + + DEBUGASSERT(rhport && epinfo && buffer && buflen > 0); + + /* We must have exclusive access to the EHCI hardware and data + * structures. + */ + + ret = imxrt_takesem(&g_ehci.exclsem); + if (ret < 0) + { + return ret; + } + + /* Set the request for the callback well BEFORE initiating the transfer. */ + + ret = imxrt_ioc_async_setup(rhport, epinfo, callback, arg); + if (ret != OK) + { + usbhost_trace1(EHCI_TRACE1_DEVDISCONNECTED, -ret); + goto errout_with_sem; + } + + /* Initiate the transfer */ + + switch (epinfo->xfrtype) + { + case USB_EP_ATTR_XFER_BULK: + ret = imxrt_async_setup(rhport, epinfo, NULL, buffer, buflen); + break; + +#ifndef CONFIG_USBHOST_INT_DISABLE + case USB_EP_ATTR_XFER_INT: + ret = imxrt_intr_setup(rhport, epinfo, buffer, buflen); + break; +#endif + +#ifndef CONFIG_USBHOST_ISOC_DISABLE + case USB_EP_ATTR_XFER_ISOC: +# warning "Isochronous endpoint support not emplemented" +#endif + case USB_EP_ATTR_XFER_CONTROL: + default: + usbhost_trace1(EHCI_TRACE1_BADXFRTYPE, epinfo->xfrtype); + ret = -ENOSYS; + break; + } + + /* Check for errors in the setup of the transfer */ + + if (ret < 0) + { + goto errout_with_callback; + } + + /* The transfer is in progress */ + + imxrt_givesem(&g_ehci.exclsem); + return OK; + +errout_with_callback: + epinfo->callback = NULL; + epinfo->arg = NULL; +errout_with_sem: + imxrt_givesem(&g_ehci.exclsem); + return ret; +} +#endif /* CONFIG_USBHOST_ASYNCH */ + +/**************************************************************************** + * Name: imxrt_cancel + * + * Description: + * Cancel a pending transfer on an endpoint. Canceled synchronous or + * asynchronous transfer will complete normally with the error -ESHUTDOWN. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the + * call to the class create() method. + * ep - The IN or OUT endpoint descriptor for the device endpoint on + * which an asynchronous transfer should be transferred. + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure + * + ****************************************************************************/ + +static int imxrt_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep) +{ + struct imxrt_epinfo_s *epinfo = (struct imxrt_epinfo_s *)ep; + struct imxrt_qh_s *qh; +#ifdef CONFIG_USBHOST_ASYNCH + usbhost_asynch_t callback; + void *arg; +#endif + uint32_t *bp; + irqstate_t flags; + bool iocwait; + int ret; + + DEBUGASSERT(epinfo); + + /* We must have exclusive access to the EHCI hardware and data structures. + * This will prevent servicing any transfer completion events while we + * perform the the cancellation, but will not prevent DMA-related race + * conditions. + * + * REVISIT: This won't work. This function must be callable from the + * interrupt level. + */ + + ret = imxrt_takesem(&g_ehci.exclsem); + if (ret < 0) + { + return ret; + } + + /* Sample and reset all transfer termination information. This will + * prevent any callbacks from occurring while are performing the + * cancellation. The transfer may still be in progress, however, so this + * does not eliminate other DMA-related race conditions. + */ + + flags = enter_critical_section(); +#ifdef CONFIG_USBHOST_ASYNCH + callback = epinfo->callback; + arg = epinfo->arg; +#endif + iocwait = epinfo->iocwait; + +#ifdef CONFIG_USBHOST_ASYNCH + epinfo->callback = NULL; + epinfo->arg = NULL; +#endif + epinfo->iocwait = false; + + /* This will prevent any callbacks from occurring while are performing + * the cancellation. The transfer may still be in progress, however, so + * this does not eliminate other DMA-related race conditions. + */ + + epinfo->callback = NULL; + epinfo->arg = NULL; + leave_critical_section(flags); + + /* Bail if there is no transfer in progress for this endpoint */ + +#ifdef CONFIG_USBHOST_ASYNCH + if (callback == NULL && !iocwait) +#else + if (!iocwait) +#endif + { + ret = OK; + goto errout_with_sem; + } + + /* Handle the cancellation according to the type of the transfer */ + + switch (epinfo->xfrtype) + { + case USB_EP_ATTR_XFER_CONTROL: + case USB_EP_ATTR_XFER_BULK: + { + /* Get the horizontal pointer from the head of the asynchronous + * queue. + */ + + bp = (uint32_t *)&g_asynchead.hw.hlp; + qh = (struct imxrt_qh_s *) + imxrt_virtramaddr(imxrt_swap32(*bp) & QH_HLP_MASK); + + /* If the asynchronous queue is empty, then the forward point in + * the asynchronous queue head will point back to the queue + * head. + */ + + if (qh && qh != &g_asynchead) + { + /* Claim that we successfully cancelled the transfer */ + + ret = OK; + goto exit_terminate; + } + } + break; + +#ifndef CONFIG_USBHOST_INT_DISABLE + case USB_EP_ATTR_XFER_INT: + { + /* Get the horizontal pointer from the head of the interrupt + * queue. + */ + + bp = (uint32_t *)&g_intrhead.hw.hlp; + qh = (struct imxrt_qh_s *) + imxrt_virtramaddr(imxrt_swap32(*bp) & QH_HLP_MASK); + if (qh) + { + /* if the queue is empty, then just claim that we successfully + * canceled the transfer. + */ + + ret = OK; + goto exit_terminate; + } + } + break; +#endif + +#ifndef CONFIG_USBHOST_ISOC_DISABLE + case USB_EP_ATTR_XFER_ISOC: +# warning "Isochronous endpoint support not emplemented" +#endif + default: + usbhost_trace1(EHCI_TRACE1_BADXFRTYPE, epinfo->xfrtype); + ret = -ENOSYS; + goto errout_with_sem; + } + + /* Find and remove the QH. There are four possibilities: + * + * 1) The transfer has already completed and the QH is no longer in the + * list. In this case, sam_hq_foreach will return zero + * 2a) The transfer is not active and still pending. It was removed from + * the list and sam_hq_foreach will return one. + * 2b) The is active but not yet complete. This is currently handled the + * same as 2a). REVISIT: This needs to be fixed. + * 3) Some bad happened and sam_hq_foreach returned an error code < 0. + */ + + ret = imxrt_qh_foreach(qh, &bp, imxrt_qh_cancel, epinfo); + if (ret < 0) + { + usbhost_trace1(EHCI_TRACE1_QTDFOREACH_FAILED, -ret); + } + + /* Was there a pending synchronous transfer? */ + +exit_terminate: + epinfo->result = -ESHUTDOWN; +#ifdef CONFIG_USBHOST_ASYNCH + if (iocwait) + { + /* Yes... wake it up */ + + DEBUGASSERT(callback == NULL); + imxrt_givesem(&epinfo->iocsem); + } + + /* No.. Is there a pending asynchronous transfer? */ + + else /* if (callback != NULL) */ + { + /* Yes.. perform the callback */ + + callback(arg, -ESHUTDOWN); + } + +#else + /* Wake up the waiting thread */ + + imxrt_givesem(&epinfo->iocsem); +#endif + +errout_with_sem: + imxrt_givesem(&g_ehci.exclsem); + return ret; +} + +/**************************************************************************** + * Name: imxrt_connect + * + * Description: + * New connections may be detected by an attached hub. This method is the + * mechanism that is used by the hub class to introduce a new connection + * and port description to the system. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the + * call to the class create() method. + * hport - The descriptor of the hub port that detected the connection + * related event + * connected - True: device connected; false: device disconnected + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure + * + ****************************************************************************/ + +#ifdef CONFIG_USBHOST_HUB +static int imxrt_connect(FAR struct usbhost_driver_s *drvr, + FAR struct usbhost_hubport_s *hport, + bool connected) +{ + irqstate_t flags; + + /* Set the connected/disconnected flag */ + + hport->connected = connected; + uinfo("Hub port %d connected: %s\n", + hport->port, connected ? "YES" : "NO"); + + /* Report the connection event */ + + flags = enter_critical_section(); + DEBUGASSERT(g_ehci.hport == NULL); /* REVISIT */ + + g_ehci.hport = hport; + if (g_ehci.pscwait) + { + g_ehci.pscwait = false; + imxrt_givesem(&g_ehci.pscsem); + } + + leave_critical_section(flags); + return OK; +} +#endif + +/**************************************************************************** + * Name: imxrt_disconnect + * + * Description: + * Called by the class when an error occurs and driver has been + * disconnected. The USB host driver should discard the handle to the + * class instance (it is stale) and not attempt any further interaction + * with the class driver instance (until a new instance is received from + * the create() method). The driver should not called the class' + * disconnected() method. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the + * call to the class create() method. + * hport - The port from which the device is being disconnected. Might be + * a port on a hub. + * + * Returned Value: + * None + * + * Assumptions: + * - Only a single class bound to a single device is supported. + * - Never called from an interrupt handler. + * + ****************************************************************************/ + +static void imxrt_disconnect(FAR struct usbhost_driver_s *drvr, + FAR struct usbhost_hubport_s *hport) +{ + DEBUGASSERT(hport != NULL); + hport->devclass = NULL; +} + +/**************************************************************************** + * Name: imxrt_reset + * + * Description: + * Set the HCRESET bit in the USBCMD register to reset the EHCI hardware. + * + * Table 2-9. USBCMD - USB Command Register Bit Definitions + * + * "Host Controller Reset (HCRESET) ... This control bit is used by + * software to reset the host controller. The effects of this on Root + * Hub registers are similar to a Chip Hardware Reset. + * + * "When software writes a one to this bit, the Host Controller resets its + * internal pipelines, timers, counters, state machines, etc. to their + * initial value. Any transaction currently in progress on USB is + * immediately terminated. A USB reset is not driven on downstream + * ports. + * + * "PCI Configuration registers are not affected by this reset. All + * operational registers, including port registers and port state + * machines are set to their initial values. Port ownership reverts + * to the companion host controller(s)... Software must reinitialize + * the host controller ... in order to return the host controller to + * an operational state. + * + * "This bit is set to zero by the Host Controller when the reset process + * is complete. Software cannot terminate the reset process early by + * writing a zero to this register. Software should not set this bit to + * a one when the HCHalted bit in the USBSTS register is a zero. + * Attempting to reset an actively running host controller will result + * in undefined behavior." + * + * Input Parameters: + * None. + * + * Returned Value: + * Zero (OK) is returned on success; A negated errno value is returned + * on failure. + * + * Assumptions: + * - Called during the initialization of the EHCI. + * + ****************************************************************************/ + +static int imxrt_reset(void) +{ + uint32_t regval; + unsigned int timeout; + + /* Make sure that the EHCI is halted: "When [the Run/Stop] bit is set to + * 0, the Host Controller completes the current transaction on the USB and + * then halts. The HC Halted bit in the status register indicates when the + * Host Controller has finished the transaction and has entered the + * stopped state..." + */ + + imxrt_putreg(0, &HCOR->usbcmd); + + /* "... Software should not set [HCRESET] to a one when the HCHalted bit in + * the USBSTS register is a zero. Attempting to reset an actively running + * host controller will result in undefined behavior." + */ + + timeout = 0; + do + { + /* Wait one microsecond and update the timeout counter */ + + up_udelay(1); + timeout++; + + /* Get the current value of the USBSTS register. This loop will + * terminate when either the timeout exceeds one millisecond or when + * the HCHalted bit is no longer set in the USBSTS register. + */ + + regval = imxrt_getreg(&HCOR->usbsts); + } + while (((regval & EHCI_USBSTS_HALTED) == 0) && (timeout < 1000)); + + /* Is the EHCI still running? Did we timeout? */ + + if ((regval & EHCI_USBSTS_HALTED) == 0) + { + usbhost_trace1(EHCI_TRACE1_HCHALTED_TIMEOUT, regval); + return -ETIMEDOUT; + } + + /* Now we can set the HCReset bit in the USBCMD register to initiate the + * reset + */ + + regval = imxrt_getreg(&HCOR->usbcmd); + regval |= EHCI_USBCMD_HCRESET; + imxrt_putreg(regval, &HCOR->usbcmd); + + /* Wait for the HCReset bit to become clear */ + + do + { + /* Wait five microsecondw and update the timeout counter */ + + up_udelay(5); + timeout += 5; + + /* Get the current value of the USBCMD register. This loop will + * terminate when either the timeout exceeds one second or when the + * HCReset bit is no longer set in the USBSTS register. + */ + + regval = imxrt_getreg(&HCOR->usbcmd); + } + while (((regval & EHCI_USBCMD_HCRESET) != 0) && (timeout < 1000000)); + + /* Return either success or a timeout */ + + return (regval & EHCI_USBCMD_HCRESET) != 0 ? -ETIMEDOUT : OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imxrt_ehci_initialize + * + * Description: + * Initialize USB EHCI host controller hardware. + * + * Input Parameters: + * controller -- If the device supports more than one EHCI interface, then + * this identifies which controller is being initialized. Normally, this + * is just zero. + * + * Returned Value: + * And instance of the USB host interface. The controlling task should + * use this interface to (1) call the wait() method to wait for a device + * to be connected, and (2) call the enumerate() method to bind the device + * to a class driver. + * + * Assumptions: + * - This function should called in the initialization sequence in order + * to initialize the USB device functionality. + * - Class drivers should be initialized prior to calling this function. + * Otherwise, there is a race condition if the device is already connected. + * + ****************************************************************************/ + +FAR struct usbhost_connection_s *imxrt_ehci_initialize(int controller) +{ + FAR struct usbhost_hubport_s *hport; + uint32_t regval; +# if defined(CONFIG_DEBUG_USB) && defined(CONFIG_DEBUG_INFO) + uint16_t regval16; + unsigned int nports; +# endif + uintptr_t physaddr; + int ret; + int i; + + /* Sanity checks */ + + DEBUGASSERT(controller == 0); + DEBUGASSERT(((uintptr_t) & g_asynchead & 0x1f) == 0); + DEBUGASSERT((sizeof(struct imxrt_qh_s) & 0x1f) == 0); + DEBUGASSERT((sizeof(struct imxrt_qtd_s) & 0x1f) == 0); + +# ifdef CONFIG_IMXRT_EHCI_PREALLOCATE + DEBUGASSERT(((uintptr_t) & g_qhpool & 0x1f) == 0); + DEBUGASSERT(((uintptr_t) & g_qtdpool & 0x1f) == 0); +# endif + +# ifndef CONFIG_USBHOST_INT_DISABLE + DEBUGASSERT(((uintptr_t) & g_intrhead & 0x1f) == 0); +# ifdef CONFIG_IMXRT_EHCI_PREALLOCATE + DEBUGASSERT(((uintptr_t) g_framelist & 0xfff) == 0); +# endif +# endif /* CONFIG_USBHOST_INT_DISABLE */ + + /* Software Configuration *************************************************/ + + usbhost_vtrace1(EHCI_VTRACE1_INITIALIZING, 0); + + /* Initialize the EHCI state data structure */ + + nxsem_init(&g_ehci.exclsem, 0, 1); + nxsem_init(&g_ehci.pscsem, 0, 0); + + /* The pscsem semaphore is used for signaling and, hence, should not have + * priority inheritance enabled. + */ + + nxsem_set_protocol(&g_ehci.pscsem, SEM_PRIO_NONE); + + /* Initialize EP0 */ + + nxsem_init(&g_ehci.ep0.iocsem, 0, 1); + + /* Initialize the root hub port structures */ + + for (i = 0; i < IMXRT_EHCI_NRHPORT; i++) + { + struct imxrt_rhport_s *rhport = &g_ehci.rhport[i]; + + /* Initialize the device operations */ + + rhport->drvr.ep0configure = imxrt_ep0configure; + rhport->drvr.epalloc = imxrt_epalloc; + rhport->drvr.epfree = imxrt_epfree; + rhport->drvr.alloc = imxrt_alloc; + rhport->drvr.free = imxrt_free; + rhport->drvr.ioalloc = imxrt_ioalloc; + rhport->drvr.iofree = imxrt_iofree; + rhport->drvr.ctrlin = imxrt_ctrlin; + rhport->drvr.ctrlout = imxrt_ctrlout; + rhport->drvr.transfer = imxrt_transfer; +# ifdef CONFIG_USBHOST_ASYNCH + rhport->drvr.asynch = imxrt_asynch; +# endif + rhport->drvr.cancel = imxrt_cancel; +# ifdef CONFIG_USBHOST_HUB + rhport->drvr.connect = imxrt_connect; +# endif + rhport->drvr.disconnect = imxrt_disconnect; + + /* Initialize EP0 */ + + rhport->ep0.xfrtype = USB_EP_ATTR_XFER_CONTROL; + rhport->ep0.speed = USB_SPEED_FULL; + rhport->ep0.maxpacket = 8; + + /* The EP0 iocsem semaphore is used for signaling and, hence, should + * not have priority inheritance enabled. + */ + + nxsem_init(&rhport->ep0.iocsem, 0, 0); + nxsem_set_protocol(&rhport->ep0.iocsem, SEM_PRIO_NONE); + + /* Initialize the public port representation */ + + hport = &rhport->hport.hport; + hport->drvr = &rhport->drvr; +# ifdef CONFIG_USBHOST_HUB + hport->parent = NULL; +# endif + hport->ep0 = &rhport->ep0; + hport->port = i; + hport->speed = USB_SPEED_FULL; + + /* Initialize function address generation logic */ + + usbhost_devaddr_initialize(&rhport->hport); + } + +# ifndef CONFIG_IMXRT_EHCI_PREALLOCATE + /* Allocate a pool of free Queue Head (QH) structures */ + + g_qhpool = + (struct imxrt_qh_s *)kmm_memalign(32, + CONFIG_IMXRT_EHCI_NQHS * + sizeof(struct imxrt_qh_s)); + if (!g_qhpool) + { + usbhost_trace1(EHCI_TRACE1_QHPOOLALLOC_FAILED, 0); + return NULL; + } +# endif + + /* Initialize the list of free Queue Head (QH) structures */ + + for (i = 0; i < CONFIG_IMXRT_EHCI_NQHS; i++) + { + /* Put the QH structure in a free list */ + + imxrt_qh_free(&g_qhpool[i]); + } + +# ifndef CONFIG_IMXRT_EHCI_PREALLOCATE + /* Allocate a pool of free Transfer Descriptor (qTD) structures */ + + g_qtdpool = + (struct imxrt_qtd_s *)kmm_memalign(32, + CONFIG_IMXRT_EHCI_NQTDS * + sizeof(struct imxrt_qtd_s)); + if (!g_qtdpool) + { + usbhost_trace1(EHCI_TRACE1_QTDPOOLALLOC_FAILED, 0); + kmm_free(g_qhpool); + return NULL; + } +# endif + +# if !defined(CONFIG_IMXRT_EHCI_PREALLOCATE) && !defined(CONFIG_USBHOST_INT_DISABLE) + /* Allocate the periodic framelist */ + + g_framelist = (uint32_t *) + kmm_memalign(4096, FRAME_LIST_SIZE * sizeof(uint32_t)); + if (!g_framelist) + { + usbhost_trace1(EHCI_TRACE1_PERFLALLOC_FAILED, 0); + kmm_free(g_qhpool); + kmm_free(g_qtdpool); + return NULL; + } +# endif + + /* Initialize the list of free Transfer Descriptor (qTD) structures */ + + for (i = 0; i < CONFIG_IMXRT_EHCI_NQTDS; i++) + { + /* Put the TD in a free list */ + + imxrt_qtd_free(&g_qtdpool[i]); + } + + /* EHCI Hardware Configuration ********************************************/ + + imxrt_clockall_usboh3(); + + /* Reset the controller from the OTG peripheral */ + + putreg32(USBDEV_USBCMD_RST, IMXRT_USBDEV_USBCMD); + while ((getreg32(IMXRT_USBDEV_USBCMD) & USBDEV_USBCMD_RST) != 0); + + /* Program the controller to be the USB host controller Fixed selections: + * CM = Host mode ES = 0, Little endian mode. SLOM Not used in host mode. + * VBPS = 1, off-chip power source Configurable selections: SDIS = 1, + * Stream disable mode. Eliminates overruns/underruns at the expense of + * some performance. + */ + +# ifdef CONFIG_IMXRT_EHCI_SDIS + putreg32(USBHOST_USBMODE_CM_HOST | USBHOST_USBMODE_SDIS | + USBHOST_USBMODE_VBPS, IMXRT_USBDEV_USBMODE); +# else + putreg32(USBHOST_USBMODE_CM_HOST | USBHOST_USBMODE_VBPS, + IMXRT_USBDEV_USBMODE); +# endif + + /* Reset the EHCI hardware */ + + ret = imxrt_reset(); + if (ret < 0) + { + usbhost_trace1(EHCI_TRACE1_RESET_FAILED, -ret); + return NULL; + } + + /* Re-program the USB host controller. As implemented, imxrt_reset() + * requires the host mode setup in order to work. However, we lose the + * host configuration in the reset. + */ + +# ifdef CONFIG_IMXRT_EHCI_SDIS + putreg32(USBHOST_USBMODE_CM_HOST | USBHOST_USBMODE_SDIS | + USBHOST_USBMODE_VBPS, IMXRT_USBDEV_USBMODE); +# else + putreg32(USBHOST_USBMODE_CM_HOST | USBHOST_USBMODE_VBPS, + IMXRT_USBDEV_USBMODE); +# endif + + /* Disable all interrupts */ + + imxrt_putreg(0, &HCOR->usbintr); + + /* Clear pending interrupts. Bits in the USBSTS register are cleared by + * writing a '1' to the corresponding bit. + */ + + imxrt_putreg(EHCI_INT_ALLINTS, &HCOR->usbsts); + +# if defined(CONFIG_DEBUG_USB) && defined(CONFIG_DEBUG_INFO) + /* Show the EHCI version */ + + regval16 = imxrt_swap16(HCCR->hciversion); + usbhost_vtrace2(EHCI_VTRACE2_HCIVERSION, regval16 >> 8, regval16 & 0xff); + + /* Verify that the correct number of ports is reported */ + + regval = imxrt_getreg(&HCCR->hcsparams); + nports = (regval & EHCI_HCSPARAMS_NPORTS_MASK) >> + EHCI_HCSPARAMS_NPORTS_SHIFT; + + usbhost_vtrace2(EHCI_VTRACE2_HCSPARAMS, nports, regval); + DEBUGASSERT(nports == IMXRT_EHCI_NRHPORT); + + /* Show the HCCPARAMS register */ + + regval = imxrt_getreg(&HCCR->hccparams); + usbhost_vtrace1(EHCI_VTRACE1_HCCPARAMS, regval); +# endif + + /* Initialize the head of the asynchronous queue/reclamation list. "In + * order to communicate with devices via the asynchronous schedule, system + * software must write the ASYNDLISTADDR register with the address of a + * control or bulk queue head. Software must then enable the asynchronous + * schedule by writing a one to the Asynchronous Schedule Enable bit in + * the USBCMD register. In order to communicate with devices via the + * periodic schedule, system software must enable the periodic schedule by + * writing a one to the Periodic Schedule Enable bit in the USBCMD + * register. Note that the schedules can be turned on before the first + * port is reset (and enabled)." + */ + + memset(&g_asynchead, 0, sizeof(struct imxrt_qh_s)); + physaddr = imxrt_physramaddr((uintptr_t) & g_asynchead); + g_asynchead.hw.hlp = imxrt_swap32(physaddr | QH_HLP_TYP_QH); + g_asynchead.hw.epchar = imxrt_swap32(QH_EPCHAR_H | QH_EPCHAR_EPS_FULL); + g_asynchead.hw.overlay.nqp = imxrt_swap32(QH_NQP_T); + g_asynchead.hw.overlay.alt = imxrt_swap32(QH_NQP_T); + g_asynchead.hw.overlay.token = imxrt_swap32(QH_TOKEN_HALTED); + g_asynchead.fqp = imxrt_swap32(QTD_NQP_T); + + /* Set the Current Asynchronous List Address. */ + + up_flush_dcache((uintptr_t)&g_asynchead.hw, + (uintptr_t)&g_asynchead.hw + sizeof(struct ehci_qh_s)); + + imxrt_putreg(imxrt_swap32(physaddr), &HCOR->asynclistaddr); + +# ifndef CONFIG_USBHOST_INT_DISABLE + + /* Initialize the head of the periodic list. Since Isochronous endpoints + * are not not yet supported, each element of the frame list is initialized + * to point to the Interrupt Queue Head (g_intrhead). + */ + + memset(&g_intrhead, 0, sizeof(struct imxrt_qh_s)); + g_intrhead.hw.hlp = imxrt_swap32(QH_HLP_T); + g_intrhead.hw.overlay.nqp = imxrt_swap32(QH_NQP_T); + g_intrhead.hw.overlay.alt = imxrt_swap32(QH_NQP_T); + g_intrhead.hw.overlay.token = imxrt_swap32(QH_TOKEN_HALTED); + g_intrhead.hw.epcaps = imxrt_swap32(QH_EPCAPS_SSMASK(1)); + + /* Attach the periodic QH to Period Frame List */ + + physaddr = imxrt_physramaddr((uintptr_t) & g_intrhead); + for (i = 0; i < FRAME_LIST_SIZE; i++) + { + g_framelist[i] = imxrt_swap32(physaddr) | PFL_TYP_QH; + } + + /* Set the Periodic Frame List Base Address. */ + + physaddr = imxrt_physramaddr((uintptr_t) g_framelist); + imxrt_putreg(imxrt_swap32(physaddr), &HCOR->periodiclistbase); +# endif + + /* Enable the asynchronous schedule and, possibly enable the periodic + * schedule and set the frame list size. + */ + + regval = imxrt_getreg(&HCOR->usbcmd); + regval &= ~(EHCI_USBCMD_HCRESET | EHCI_USBCMD_FLSIZE_MASK | + EHCI_USBCMD_FLSIZE_MASK | EHCI_USBCMD_PSEN | + EHCI_USBCMD_IAADB | EHCI_USBCMD_LRESET); + regval |= EHCI_USBCMD_ASEN; + +# ifndef CONFIG_USBHOST_INT_DISABLE + regval |= EHCI_USBCMD_PSEN; +# if FRAME_LIST_SIZE == 1024 + regval |= EHCI_USBCMD_FLSIZE_1024; +# elif FRAME_LIST_SIZE == 512 + regval |= EHCI_USBCMD_FLSIZE_512; +# elif FRAME_LIST_SIZE == 512 + regval |= EHCI_USBCMD_FLSIZE_256; +# else +# error Unsupported frame size list size +# endif +# endif + + imxrt_putreg(regval, &HCOR->usbcmd); + + /* Start the host controller by setting the RUN bit in the USBCMD + * register. + */ + + regval = imxrt_getreg(&HCOR->usbcmd); + regval |= EHCI_USBCMD_RUN; + imxrt_putreg(regval, &HCOR->usbcmd); + + /* Route all ports to this host controller by setting the CONFIG flag. */ + + regval = imxrt_getreg(&HCOR->configflag); + regval |= EHCI_CONFIGFLAG; + imxrt_putreg(regval, &HCOR->configflag); + + /* Wait for the EHCI to run (i.e., no longer report halted) */ + + ret = ehci_wait_usbsts(EHCI_USBSTS_HALTED, 0, 100 * 1000); + if (ret < 0) + { + usbhost_trace1(EHCI_TRACE1_RUN_FAILED, imxrt_getreg(&HCOR->usbsts)); + return NULL; + } + + /* Interrupt Configuration ************************************************/ + + ret = irq_attach(IMXRT_IRQ_USBOTG1, imxrt_ehci_interrupt, NULL); + if (ret != 0) + { + usbhost_trace1(EHCI_TRACE1_IRQATTACH_FAILED, IMXRT_IRQ_USBOTG1); + return NULL; + } + + /* Enable EHCI interrupts. Interrupts are still disabled at the level of + * the interrupt controller. + */ + + imxrt_putreg(EHCI_HANDLED_INTS, &HCOR->usbintr); + + /* Enable interrupts at the interrupt controller */ + + up_enable_irq(IMXRT_IRQ_USBOTG1); + + /* Drive Vbus +5V (the smoke test) */ + + for (i = 0; i < IMXRT_EHCI_NRHPORT; i++) + { + /* Enable VBUS power for the port */ + + imxrt_usbhost_vbusdrive(i, true); + up_mdelay(25); + } + + /* If there is a USB device in the slot at power up, then we will not get + * the status change interrupt to signal us that the device is connected. + * We need to set the initial connected state accordingly. + */ + + for (i = 0; i < IMXRT_EHCI_NRHPORT; i++) + { + g_ehci.rhport[i].connected = + ((imxrt_getreg(&HCOR->portsc[i]) & EHCI_PORTSC_CCS) != 0); + } + + usbhost_vtrace1(EHCI_VTRACE1_INIITIALIZED, 0); + + /* Initialize and return the connection interface */ + + g_ehciconn.wait = imxrt_wait; + g_ehciconn.enumerate = imxrt_enumerate; + return &g_ehciconn; +} + +/**************************************************************************** + * Name: usbhost_trformat1 and usbhost_trformat2 + * + * Description: + * This interface must be provided by platform specific logic that knows + * the HCDs encoding of USB trace data. + * + * Given an 9-bit index, return a format string suitable for use with, say, + * printf. The returned format is expected to handle two unsigned integer + * values. + * + ****************************************************************************/ + +#ifdef HAVE_USBHOST_TRACE +FAR const char *usbhost_trformat1(uint16_t id) +{ + int ndx = TRACE1_INDEX(id); + + if (ndx < TRACE1_NSTRINGS) + { + return g_trace1[ndx].string; + } + + return NULL; +} + +FAR const char *usbhost_trformat2(uint16_t id) +{ + int ndx = TRACE2_INDEX(id); + + if (ndx < TRACE2_NSTRINGS) + { + return g_trace2[ndx].string; + } + + return NULL; +} +#endif /* HAVE_USBHOST_TRACE */ + +#endif /* CONFIG_IMXRT_USBOTG && CONFIG_USBHOST */ diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lowputc.c b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lowputc.c new file mode 100644 index 000000000..682d1fec9 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lowputc.c @@ -0,0 +1,603 @@ +/**************************************************************************** + * arch/arm/src/imxrt/imxrt_lowputc.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 "arm_arch.h" + +#include "hardware/imxrt_iomuxc.h" +#include "hardware/imxrt_pinmux.h" +#include "hardware/imxrt_ccm.h" +#include "hardware/imxrt_lpuart.h" +#include "imxrt_config.h" +#include "imxrt_periphclks.h" +#include "imxrt_iomuxc.h" +#include "imxrt_gpio.h" +#include "imxrt_lowputc.h" + +#include "arm_internal.h" + +#include /* Include last: has dependencies */ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +#ifdef HAVE_LPUART_CONSOLE +# if defined(CONFIG_LPUART1_SERIAL_CONSOLE) +# define IMXRT_CONSOLE_BASE IMXRT_LPUART1_BASE +# define IMXRT_CONSOLE_BAUD CONFIG_LPUART1_BAUD +# define IMXRT_CONSOLE_BITS CONFIG_LPUART1_BITS +# define IMXRT_CONSOLE_PARITY CONFIG_LPUART1_PARITY +# define IMXRT_CONSOLE_2STOP CONFIG_LPUART1_2STOP +# elif defined(CONFIG_LPUART2_SERIAL_CONSOLE) +# define IMXRT_CONSOLE_BASE IMXRT_LPUART2_BASE +# define IMXRT_CONSOLE_BAUD CONFIG_LPUART2_BAUD +# define IMXRT_CONSOLE_BITS CONFIG_LPUART2_BITS +# define IMXRT_CONSOLE_PARITY CONFIG_LPUART2_PARITY +# define IMXRT_CONSOLE_2STOP CONFIG_LPUART2_2STOP +# elif defined(CONFIG_LPUART3_SERIAL_CONSOLE) +# define IMXRT_CONSOLE_BASE IMXRT_LPUART3_BASE +# define IMXRT_CONSOLE_BAUD CONFIG_LPUART3_BAUD +# define IMXRT_CONSOLE_BITS CONFIG_LPUART3_BITS +# define IMXRT_CONSOLE_PARITY CONFIG_LPUART3_PARITY +# define IMXRT_CONSOLE_2STOP CONFIG_LPUART3_2STOP +# elif defined(CONFIG_LPUART4_SERIAL_CONSOLE) +# define IMXRT_CONSOLE_BASE IMXRT_LPUART4_BASE +# define IMXRT_CONSOLE_BAUD CONFIG_LPUART4_BAUD +# define IMXRT_CONSOLE_BITS CONFIG_LPUART4_BITS +# define IMXRT_CONSOLE_PARITY CONFIG_LPUART4_PARITY +# define IMXRT_CONSOLE_2STOP CONFIG_LPUART4_2STOP +# elif defined(CONFIG_LPUART5_SERIAL_CONSOLE) +# define IMXRT_CONSOLE_BASE IMXRT_LPUART5_BASE +# define IMXRT_CONSOLE_BAUD CONFIG_LPUART5_BAUD +# define IMXRT_CONSOLE_BITS CONFIG_LPUART5_BITS +# define IMXRT_CONSOLE_PARITY CONFIG_LPUART5_PARITY +# define IMXRT_CONSOLE_2STOP CONFIG_LPUART5_2STOP +# elif defined(CONFIG_LPUART6_SERIAL_CONSOLE) +# define IMXRT_CONSOLE_BASE IMXRT_LPUART6_BASE +# define IMXRT_CONSOLE_BAUD CONFIG_LPUART6_BAUD +# define IMXRT_CONSOLE_BITS CONFIG_LPUART6_BITS +# define IMXRT_CONSOLE_PARITY CONFIG_LPUART6_PARITY +# define IMXRT_CONSOLE_2STOP CONFIG_LPUART6_2STOP +# elif defined(CONFIG_LPUART7_SERIAL_CONSOLE) +# define IMXRT_CONSOLE_BASE IMXRT_LPUART7_BASE +# define IMXRT_CONSOLE_BAUD CONFIG_LPUART7_BAUD +# define IMXRT_CONSOLE_BITS CONFIG_LPUART7_BITS +# define IMXRT_CONSOLE_PARITY CONFIG_LPUART7_PARITY +# define IMXRT_CONSOLE_2STOP CONFIG_LPUART7_2STOP +# elif defined(CONFIG_LPUART8_SERIAL_CONSOLE) +# define IMXRT_CONSOLE_BASE IMXRT_LPUART8_BASE +# define IMXRT_CONSOLE_BAUD CONFIG_LPUART8_BAUD +# define IMXRT_CONSOLE_BITS CONFIG_LPUART8_BITS +# define IMXRT_CONSOLE_PARITY CONFIG_LPUART8_PARITY +# define IMXRT_CONSOLE_2STOP CONFIG_LPUART8_2STOP +# endif +#endif + +/* Clocking *****************************************************************/ + +/* The UART module receives two clocks, a peripheral_clock (ipg_clk) and the + * module_clock (ipg_perclk). The peripheral_clock is used as write clock + * of the TxFIFO, read clock of the RxFIFO and synchronization of the modem + * control input pins. It must always be running when UART is enabled. + * + * The default lpuart1 ipg_clk is 66MHz (max 66.5MHz). ipg_clk is shared + * among many modules and should not be controlled by the UART logic. + * + * The module_clock is for all the state machines, writing RxFIFO, reading + * TxFIFO, etc. It must always be running when UART is sending or receiving + * characters. This clock is used in order to allow frequency scaling on + * peripheral_clock without changing configuration of baud rate. + * + * The default ipg_perclk is 80MHz (max 80MHz). ipg_perclk is gated by + * CCGR5[CG12], lpuart1_clk_enable. The clock generation sequence is: + * + * pll3_sw_clk (480M) -> CCGR5[CG12] -> 3 bit divider cg podf=6 -> + * PLL3_80M (80Mhz) -> CDCDR1: lpuart1_clk_podf -> + * 6 bit divider default=1 -> LPUART1_CLK_ROOT + * + * REVISIT: This logic assumes that all dividers are at the default value + * and that the value of the ipg_perclk is 80MHz. + */ + +#define IPG_PERCLK_FREQUENCY 80000000 + +/* The BRM sub-block receives ref_clk (module_clock clock after divider). + * From this clock, and with integer and non-integer division, BRM generates + * a 16x baud rate clock. + */ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifdef HAVE_LPUART_CONSOLE +static const struct uart_config_s g_console_config = +{ + .baud = IMXRT_CONSOLE_BAUD, /* Configured baud */ + .parity = IMXRT_CONSOLE_PARITY, /* 0=none, 1=odd, 2=even */ + .bits = IMXRT_CONSOLE_BITS, /* Number of bits (5-9) */ + .stopbits2 = IMXRT_CONSOLE_2STOP, /* true: Configure with 2 stop bits instead of 1 */ +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +void imxrt_lpuart_clock_enable (uint32_t base) +{ + if (base == IMXRT_LPUART1_BASE) + { + imxrt_clockall_lpuart1(); + } + else if (base == IMXRT_LPUART2_BASE) + { + imxrt_clockall_lpuart2(); + } + else if (base == IMXRT_LPUART3_BASE) + { + imxrt_clockall_lpuart3(); + } + else if (base == IMXRT_LPUART4_BASE) + { + imxrt_clockall_lpuart4(); + } + else if (base == IMXRT_LPUART5_BASE) + { + imxrt_clockall_lpuart5(); + } + else if (base == IMXRT_LPUART6_BASE) + { + imxrt_clockall_lpuart6(); + } + else if (base == IMXRT_LPUART7_BASE) + { + imxrt_clockall_lpuart7(); + } + else if (base == IMXRT_LPUART8_BASE) + { + imxrt_clockall_lpuart8(); + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imxrt_lowsetup + * + * Description: + * Called at the very beginning of _start. Performs low level + * initialization including setup of the console UART. This UART done + * early so that the serial console is available for debugging very early + * in the boot sequence. + * + ****************************************************************************/ + +void imxrt_lowsetup(void) +{ +#ifndef CONFIG_SUPPRESS_LPUART_CONFIG +#ifdef HAVE_LPUART_DEVICE + +#ifdef CONFIG_IMXRT_LPUART1 + + /* Configure LPUART1 pins: RXD and TXD. Also configure RTS and CTS if flow + * control is enabled. + */ + + imxrt_config_gpio(GPIO_LPUART1_RX); + imxrt_config_gpio(GPIO_LPUART1_TX); +#ifdef CONFIG_LPUART1_OFLOWCONTROL + imxrt_config_gpio(GPIO_LPUART1_CTS); +#endif +#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART1_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART1_IFLOWCONTROL))) + imxrt_config_gpio(GPIO_LPUART1_RTS); +#endif +#endif + +#ifdef CONFIG_IMXRT_LPUART2 + + /* Configure LPUART2 pins: RXD and TXD. Also configure RTS and CTS if flow + * control is enabled. + */ + + imxrt_config_gpio(GPIO_LPUART2_RX); + imxrt_config_gpio(GPIO_LPUART2_TX); +#ifdef CONFIG_LPUART2_OFLOWCONTROL + imxrt_config_gpio(GPIO_LPUART2_CTS); +#endif +#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART2_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART2_IFLOWCONTROL))) + imxrt_config_gpio(GPIO_LPUART2_RTS); +#endif +#endif + +#ifdef CONFIG_IMXRT_LPUART3 + + /* Configure LPUART3 pins: RXD and TXD. Also configure RTS and CTS if flow + * control is enabled. + */ + + imxrt_config_gpio(GPIO_LPUART3_RX); + imxrt_config_gpio(GPIO_LPUART3_TX); +#ifdef CONFIG_LPUART3_OFLOWCONTROL + imxrt_config_gpio(GPIO_LPUART3_CTS); +#endif +#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART3_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART3_IFLOWCONTROL))) + imxrt_config_gpio(GPIO_LPUART3_RTS); +#endif +#endif + +#ifdef CONFIG_IMXRT_LPUART4 + + /* Configure LPUART4 pins: RXD and TXD. Also configure RTS and CTS if flow + * control is enabled. + */ + + imxrt_config_gpio(GPIO_LPUART4_RX); + imxrt_config_gpio(GPIO_LPUART4_TX); +#ifdef CONFIG_LPUART4_OFLOWCONTROL + imxrt_config_gpio(GPIO_LPUART4_CTS); +#endif +#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART4_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART4_IFLOWCONTROL))) + imxrt_config_gpio(GPIO_LPUART4_RTS); +#endif +#endif + +#ifdef CONFIG_IMXRT_LPUART5 + + /* Configure LPUART5 pins: RXD and TXD. Also configure RTS and CTS if flow + * control is enabled. + */ + + imxrt_config_gpio(GPIO_LPUART5_RX); + imxrt_config_gpio(GPIO_LPUART5_TX); +#ifdef CONFIG_LPUART5_OFLOWCONTROL + imxrt_config_gpio(GPIO_LPUART5_CTS); +#endif +#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART5_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART5_IFLOWCONTROL))) + imxrt_config_gpio(GPIO_LPUART5_RTS); +#endif +#endif + +#ifdef CONFIG_IMXRT_LPUART6 + + /* Configure LPUART6 pins: RXD and TXD. Also configure RTS and CTS if flow + * control is enabled. + */ + + imxrt_config_gpio(GPIO_LPUART6_RX); + imxrt_config_gpio(GPIO_LPUART6_TX); +#ifdef CONFIG_LPUART6_OFLOWCONTROL + imxrt_config_gpio(GPIO_LPUART6_CTS); +#endif +#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART6_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART6_IFLOWCONTROL))) + imxrt_config_gpio(GPIO_LPUART6_RTS); +#endif +#endif + +#ifdef CONFIG_IMXRT_LPUART7 + + /* Configure LPUART7 pins: RXD and TXD. Also configure RTS and CTS if flow + * control is enabled. + */ + + imxrt_config_gpio(GPIO_LPUART7_RX); + imxrt_config_gpio(GPIO_LPUART7_TX); +#ifdef CONFIG_LPUART7_OFLOWCONTROL + imxrt_config_gpio(GPIO_LPUART7_CTS); +#endif +#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART7_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART7_IFLOWCONTROL))) + imxrt_config_gpio(GPIO_LPUART7_RTS); +#endif +#endif + +#ifdef CONFIG_IMXRT_LPUART8 + + /* Configure LPUART8 pins: RXD and TXD. Also configure RTS and CTS if flow + * control is enabled. + */ + + imxrt_config_gpio(GPIO_LPUART8_RX); + imxrt_config_gpio(GPIO_LPUART8_TX); +#ifdef CONFIG_LPUART8_OFLOWCONTROL + imxrt_config_gpio(GPIO_LPUART8_CTS); +#endif +#if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART8_RS485RTSCONTROL)) || \ + (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART8_IFLOWCONTROL))) + imxrt_config_gpio(GPIO_LPUART8_RTS); +#endif +#endif + +#ifdef HAVE_LPUART_CONSOLE + /* Configure the serial console for initial, non-interrupt driver mode */ + + imxrt_lpuart_configure(IMXRT_CONSOLE_BASE, &g_console_config); +#endif +#endif /* HAVE_LPUART_DEVICE */ +#endif /* CONFIG_SUPPRESS_LPUART_CONFIG */ +} + +/**************************************************************************** + * Name: imxrt_lpuart_configure + * + * Description: + * Configure a UART for non-interrupt driven operation + * + ****************************************************************************/ + +#ifdef HAVE_LPUART_DEVICE +int imxrt_lpuart_configure(uint32_t base, + FAR const struct uart_config_s *config) +{ + uint32_t src_freq = 0; + uint32_t pll3_div = 0; + uint32_t uart_div = 0; + uint32_t lpuart_freq = 0; + uint16_t sbr; + uint16_t temp_sbr; + uint32_t osr; + uint32_t temp_osr; + uint32_t temp_diff; + uint32_t calculated_baud; + uint32_t baud_diff; + uint32_t regval; + uint32_t regval2; + + if ((getreg32(IMXRT_CCM_CSCDR1) & CCM_CSCDR1_UART_CLK_SEL) != 0) + { + src_freq = BOARD_XTAL_FREQUENCY; + } + else + { + if ((getreg32(IMXRT_CCM_ANALOG_PLL_USB1) & + CCM_ANALOG_PLL_USB1_DIV_SELECT_MASK) != 0) + { + pll3_div = 22; + } + else + { + pll3_div = 20; + } + + src_freq = (BOARD_XTAL_FREQUENCY * pll3_div) / 6; + } + + uart_div = (getreg32(IMXRT_CCM_CSCDR1) & + CCM_CSCDR1_UART_CLK_PODF_MASK) + 1; + lpuart_freq = src_freq / uart_div; + + /* This LPUART instantiation uses a slightly different baud rate + * calculation. The idea is to use the best OSR (over-sampling rate) + * possible. + * + * NOTE: OSR is typically hard-set to 16 in other LPUART instantiations + * loop to find the best OSR value possible, one that generates minimum + * baud_diff iterate through the rest of the supported values of OSR + */ + + baud_diff = config->baud; + osr = 0; + sbr = 0; + + for (temp_osr = 4; temp_osr <= 32; temp_osr++) + { + /* Calculate the temporary sbr value */ + + temp_sbr = (lpuart_freq / (config->baud * temp_osr)); + + /* Set temp_sbr to 1 if the sourceClockInHz can not satisfy the + * desired baud rate. + */ + + if (temp_sbr == 0) + { + temp_sbr = 1; + } + + /* Calculate the baud rate based on the temporary OSR and SBR values */ + + calculated_baud = (lpuart_freq / (temp_osr * temp_sbr)); + temp_diff = calculated_baud - config->baud; + + /* Select the better value between srb and (sbr + 1) */ + + if (temp_diff > (config->baud - + (lpuart_freq / (temp_osr * (temp_sbr + 1))))) + { + temp_diff = config->baud - + (lpuart_freq / (temp_osr * (temp_sbr + 1))); + temp_sbr++; + } + + if (temp_diff <= baud_diff) + { + baud_diff = temp_diff; + osr = temp_osr; + sbr = temp_sbr; + } + } + + if (baud_diff > ((config->baud / 100) * 3)) + { + /* Unacceptable baud rate difference of more than 3% */ + + return ERROR; + } + + /* Enable lpuart clock */ + + imxrt_lpuart_clock_enable(base); + + /* Reset all internal logic and registers, except the Global Register */ + + regval = getreg32(base + IMXRT_LPUART_GLOBAL_OFFSET); + regval |= LPUART_GLOBAL_RST; + putreg32(regval, base + IMXRT_LPUART_GLOBAL_OFFSET); + + regval &= ~LPUART_GLOBAL_RST; + putreg32(regval, base + IMXRT_LPUART_GLOBAL_OFFSET); + + /* Construct MODIR register */ + + regval = 0; + + if (config->userts) + { + regval |= LPUART_MODIR_RXRTSE; + } + else if (config->users485) + { + /* Both TX and RX side can't control RTS, so this gives + * the RX side precedence. This should have been filtered + * in layers above anyway, but it's just a precaution. + */ + + regval |= LPUART_MODIR_TXRTSE; + } + + if (config->usects) + { + regval |= LPUART_MODIR_TXCTSE; + } + + if (config->invrts) + { + regval |= LPUART_MODIR_TXRTSPOL; + } + + putreg32(regval, base + IMXRT_LPUART_MODIR_OFFSET); + + regval = 0; + + if ((osr > 3) && (osr < 8)) + { + regval |= LPUART_BAUD_BOTHEDGE; + } + + if (config->stopbits2) + { + regval |= LPUART_BAUD_SBNS; + } + + regval |= LPUART_BAUD_OSR(osr) | LPUART_BAUD_SBR(sbr); + putreg32(regval, base + IMXRT_LPUART_BAUD_OFFSET); + + regval = 0; + if (config->parity == 1) + { + regval |= LPUART_CTRL_PE | LPUART_CTRL_PT_ODD; + } + else if (config->parity == 2) + { + regval |= LPUART_CTRL_PE | LPUART_CTRL_PT_EVEN; + } + + if (config->bits == 9 || (config->bits == 8 && config->parity != 0)) + { + regval |= LPUART_CTRL_M; + } + else if ((config->bits == 8)) + { + regval &= ~LPUART_CTRL_M; + } + else + { + /* Here should be added support of other bit modes. */ + +#warning missing logic + return ERROR; + } + + regval2 = getreg32(base + IMXRT_LPUART_FIFO_OFFSET); + regval2 |= LPUART_FIFO_RXFLUSH | LPUART_FIFO_TXFLUSH | + LPUART_FIFO_RXFE | LPUART_FIFO_RXIDEN_1 | LPUART_FIFO_TXFE; + putreg32(regval2 , base + IMXRT_LPUART_FIFO_OFFSET); + + regval |= LPUART_CTRL_RE | LPUART_CTRL_TE; + putreg32(regval, base + IMXRT_LPUART_CTRL_OFFSET); + + return OK; +} +#endif /* HAVE_LPUART_DEVICE */ + +/**************************************************************************** + * Name: imxrt_lowputc + * + * Description: + * Output a byte with as few system dependencies as possible. This will + * even work BEFORE the console is initialized if we are booting from U- + * Boot (and the same UART is used for the console, of course.) + * + ****************************************************************************/ + +#if defined(HAVE_LPUART_DEVICE) && defined(CONFIG_DEBUG_FEATURES) +void imxrt_lowputc(int ch) +{ + while ((getreg32(IMXRT_CONSOLE_BASE + IMXRT_LPUART_STAT_OFFSET) & + LPUART_STAT_TDRE) == 0) + { + } + + /* If the character to output is a newline, then pre-pend a carriage + * return + */ + + if (ch == '\n') + { + /* Send the carriage return by writing it into the UART_TXD register. */ + + putreg32((uint32_t)'\r', IMXRT_CONSOLE_BASE + + IMXRT_LPUART_DATA_OFFSET); + + /* Wait for the transmit register to be emptied. When the TXFE bit is + * non-zero, the TX Buffer FIFO is empty. + */ + + while ((getreg32(IMXRT_CONSOLE_BASE + IMXRT_LPUART_STAT_OFFSET) & + LPUART_STAT_TDRE) == 0) + { + } + } + + /* Send the character by writing it into the UART_TXD register. */ + + putreg32((uint32_t)ch, IMXRT_CONSOLE_BASE + IMXRT_LPUART_DATA_OFFSET); +} +#endif diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpi2c.c b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpi2c.c new file mode 100644 index 000000000..76fa24cc4 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpi2c.c @@ -0,0 +1,1997 @@ +/**************************************************************************** + * arch/arm/src/imxrt/imxrt_lpi2c.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 "arm_arch.h" + +#include "imxrt_lpi2c.h" +#include "imxrt_gpio.h" + +#include "hardware/imxrt_pinmux.h" +#include "hardware/imxrt_ccm.h" +#include "imxrt_periphclks.h" + +/* At least one I2C peripheral must be enabled */ + +#if defined(CONFIG_IMXRT_LPI2C1) || defined(CONFIG_IMXRT_LPI2C2) || \ + defined(CONFIG_IMXRT_LPI2C3) || defined(CONFIG_IMXRT_LPI2C4) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +/* CONFIG_I2C_POLLED may be set so that I2C interrupts will not be used. + * Instead, CPU-intensive polling will be used. + */ + +/* Interrupt wait timeout in seconds and milliseconds */ + +#if !defined(CONFIG_IMXRT_LPI2C_TIMEOSEC) && \ + !defined(CONFIG_IMXRT_LPI2C_TIMEOMS) +# define CONFIG_IMXRT_LPI2C_TIMEOSEC 0 +# define CONFIG_IMXRT_LPI2C_TIMEOMS 500 /* Default is 500 milliseconds */ +#elif !defined(CONFIG_IMXRT_LPI2C_TIMEOSEC) +# define CONFIG_IMXRT_LPI2C_TIMEOSEC 0 /* User provided milliseconds */ +#elif !defined(CONFIG_IMXRT_LPI2C_TIMEOMS) +# define CONFIG_IMXRT_LPI2C_TIMEOMS 0 /* User provided seconds */ +#endif + +/* Interrupt wait time timeout in system timer ticks */ + +#ifndef CONFIG_IMXRT_LPI2C_TIMEOTICKS +# define CONFIG_IMXRT_LPI2C_TIMEOTICKS \ + (SEC2TICK(CONFIG_IMXRT_LPI2C_TIMEOSEC) + \ + MSEC2TICK(CONFIG_IMXRT_LPI2C_TIMEOMS)) +#endif + +#ifndef CONFIG_IMXRT_LPI2C_DYNTIMEO_STARTSTOP +# define CONFIG_IMXRT_LPI2C_DYNTIMEO_STARTSTOP \ + TICK2USEC(CONFIG_IMXRT_LPI2C_TIMEOTICKS) +#endif + +/* Debug ********************************************************************/ + +/* I2C event trace logic. NOTE: trace uses the internal, non-standard, + * low-level debug interface syslog() but does not require that any other + * debug is enabled. + */ + +#ifndef CONFIG_I2C_TRACE +# define imxrt_lpi2c_tracereset(p) +# define imxrt_lpi2c_tracenew(p,s) +# define imxrt_lpi2c_traceevent(p,e,a) +# define imxrt_lpi2c_tracedump(p) +#endif + +#ifndef CONFIG_I2C_NTRACE +# define CONFIG_I2C_NTRACE 32 +#endif + +#ifdef CONFIG_I2C_SLAVE +# error I2C slave logic is not supported yet for IMXRT +#endif + +#define LPI2C_MASTER 1 +#define LPI2C_SLAVE 2 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Interrupt state */ + +enum imxrt_intstate_e +{ + INTSTATE_IDLE = 0, /* No I2C activity */ + INTSTATE_WAITING, /* Waiting for completion of interrupt activity */ + INTSTATE_DONE, /* Interrupt activity complete */ +}; + +/* Trace events */ + +enum imxrt_trace_e +{ + I2CEVENT_NONE = 0, /* No events have occurred with this status */ + I2CEVENT_SENDADDR, /* Start/Master bit set and address sent, param = msgc */ + I2CEVENT_SENDBYTE, /* Send byte, param = dcnt */ + I2CEVENT_RCVBYTE, /* Read more dta, param = dcnt */ + I2CEVENT_NOSTART, /* BTF on last byte with no restart, param = msgc */ + I2CEVENT_STARTRESTART, /* Last byte sent, re-starting, param = msgc */ + I2CEVENT_STOP, /* Last byte sten, send stop, param = 0 */ + I2CEVENT_ERROR /* Error occurred, param = 0 */ +}; + +/* Trace data */ + +struct imxrt_trace_s +{ + uint32_t status; /* I2C 32-bit SR2|SR1 status */ + uint32_t count; /* Interrupt count when status change */ + enum imxrt_intstate_e event; /* Last event that occurred with this status */ + uint32_t parm; /* Parameter associated with the event */ + clock_t time; /* First of event or first status */ +}; + +/* I2C Device hardware configuration */ + +struct imxrt_lpi2c_config_s +{ + uint32_t base; /* LPI2C base address */ + uint16_t busy_idle; /* LPI2C Bus Idle Timeout */ + uint8_t filtscl; /* Glitch Filter for SCL pin */ + uint8_t filtsda; /* Glitch Filter for SDA pin */ + uint32_t scl_pin; /* Peripheral configuration for SCL as SCL */ + uint32_t sda_pin; /* Peripheral configuration for SDA as SDA */ +#if defined(CONFIG_I2C_RESET) + uint32_t reset_scl_pin; /* GPIO configuration for SCL as SCL */ + uint32_t reset_sda_pin; /* GPIO configuration for SDA as SDA */ +#endif + uint8_t mode; /* Master or Slave mode */ +#ifndef CONFIG_I2C_POLLED + uint32_t irq; /* Event IRQ */ +#endif +}; + +/* I2C Device Private Data */ + +struct imxrt_lpi2c_priv_s +{ + /* Standard I2C operations */ + + const struct i2c_ops_s *ops; + + /* Port configuration */ + + const struct imxrt_lpi2c_config_s *config; + + int refs; /* Reference count */ + sem_t sem_excl; /* Mutual exclusion semaphore */ +#ifndef CONFIG_I2C_POLLED + sem_t sem_isr; /* Interrupt wait semaphore */ +#endif + volatile uint8_t intstate; /* Interrupt handshake (see enum imxrt_intstate_e) */ + + uint8_t msgc; /* Message count */ + struct i2c_msg_s *msgv; /* Message list */ + uint8_t *ptr; /* Current message buffer */ + uint32_t frequency; /* Current I2C frequency */ + int dcnt; /* Current message length */ + uint16_t flags; /* Current message flags */ + + /* I2C trace support */ + +#ifdef CONFIG_I2C_TRACE + int tndx; /* Trace array index */ + clock_t start_time; /* Time when the trace was started */ + + /* The actual trace data */ + + struct imxrt_trace_s trace[CONFIG_I2C_NTRACE]; +#endif + + uint32_t status; /* End of transfer SR2|SR1 status */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static inline uint32_t + imxrt_lpi2c_getreg(FAR struct imxrt_lpi2c_priv_s *priv, uint16_t offset); +static inline void imxrt_lpi2c_putreg(FAR struct imxrt_lpi2c_priv_s *priv, + uint16_t offset, uint32_t value); +static inline void imxrt_lpi2c_modifyreg(FAR struct imxrt_lpi2c_priv_s *priv, + uint16_t offset, uint32_t clearbits, + uint32_t setbits); +static inline int imxrt_lpi2c_sem_wait(FAR struct imxrt_lpi2c_priv_s *priv); +#ifdef CONFIG_I2C_RESET +static int + imxrt_lpi2c_sem_wait_noncancelable(FAR struct imxrt_lpi2c_priv_s *priv); +#endif + +#ifdef CONFIG_IMXRT_LPI2C_DYNTIMEO +static useconds_t imxrt_lpi2c_tousecs(int msgc, FAR struct i2c_msg_s *msgs); +#endif /* CONFIG_IMXRT_LPI2C_DYNTIMEO */ + +static inline int + imxrt_lpi2c_sem_waitdone(FAR struct imxrt_lpi2c_priv_s *priv); +static inline void + imxrt_lpi2c_sem_waitstop(FAR struct imxrt_lpi2c_priv_s *priv); +static inline void + imxrt_lpi2c_sem_post(FAR struct imxrt_lpi2c_priv_s *priv); +static inline void + imxrt_lpi2c_sem_init(FAR struct imxrt_lpi2c_priv_s *priv); +static inline void + imxrt_lpi2c_sem_destroy(FAR struct imxrt_lpi2c_priv_s *priv); + +#ifdef CONFIG_I2C_TRACE +static void imxrt_lpi2c_tracereset(FAR struct imxrt_lpi2c_priv_s *priv); +static void imxrt_lpi2c_tracenew(FAR struct imxrt_lpi2c_priv_s *priv, + uint32_t status); +static void imxrt_lpi2c_traceevent(FAR struct imxrt_lpi2c_priv_s *priv, + enum imxrt_trace_e event, uint32_t parm); +static void imxrt_lpi2c_tracedump(FAR struct imxrt_lpi2c_priv_s *priv); +#endif /* CONFIG_I2C_TRACE */ + +static void imxrt_lpi2c_setclock(FAR struct imxrt_lpi2c_priv_s *priv, + uint32_t frequency); +static inline void imxrt_lpi2c_sendstart(FAR struct imxrt_lpi2c_priv_s *priv, + uint8_t address); +static inline void imxrt_lpi2c_sendstop(FAR struct imxrt_lpi2c_priv_s *priv); +static inline uint32_t + imxrt_lpi2c_getstatus(FAR struct imxrt_lpi2c_priv_s *priv); + +static int imxrt_lpi2c_isr_process(struct imxrt_lpi2c_priv_s * priv); + +#ifndef CONFIG_I2C_POLLED +static int imxrt_lpi2c_isr(int irq, void *context, FAR void *arg); +#endif /* !CONFIG_I2C_POLLED */ + +void imxrt_lpi2c_clock_enable (uint32_t base); +void imxrt_lpi2c_clock_disable (uint32_t base); +static int imxrt_lpi2c_init(FAR struct imxrt_lpi2c_priv_s *priv); +static int imxrt_lpi2c_deinit(FAR struct imxrt_lpi2c_priv_s *priv); +static int imxrt_lpi2c_transfer(FAR struct i2c_master_s *dev, + FAR struct i2c_msg_s *msgs, int count); +#ifdef CONFIG_I2C_RESET +static int imxrt_lpi2c_reset(FAR struct i2c_master_s *dev); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Trace events strings */ + +#ifdef CONFIG_I2C_TRACE +static const char *g_trace_names[] = +{ + "NONE ", + "SENDADDR ", + "SENDBYTE ", + "RCVBYTE ", + "NOSTART ", + "START/RESTART ", + "STOP ", + "ERROR " +}; +#endif + +/* I2C interface */ + +static const struct i2c_ops_s imxrt_lpi2c_ops = +{ + .transfer = imxrt_lpi2c_transfer +#ifdef CONFIG_I2C_RESET + , .reset = imxrt_lpi2c_reset +#endif +}; + +/* I2C device structures */ + +#ifdef CONFIG_IMXRT_LPI2C1 +static const struct imxrt_lpi2c_config_s imxrt_lpi2c1_config = +{ + .base = IMXRT_LPI2C1_BASE, + .busy_idle = CONFIG_LPI2C1_BUSYIDLE, + .filtscl = CONFIG_LPI2C1_FILTSCL, + .filtsda = CONFIG_LPI2C1_FILTSDA, + .scl_pin = GPIO_LPI2C1_SCL, + .sda_pin = GPIO_LPI2C1_SDA, +#if defined(CONFIG_I2C_RESET) + .reset_scl_pin = GPIO_LPI2C1_SCL_RESET, + .reset_sda_pin = GPIO_LPI2C1_SDA_RESET, +#endif +#ifndef CONFIG_I2C_SLAVE + .mode = LPI2C_MASTER, +#else + .mode = LPI2C_SLAVE, +#endif +#ifndef CONFIG_I2C_POLLED + .irq = IMXRT_IRQ_LPI2C1, +#endif +}; + +static struct imxrt_lpi2c_priv_s imxrt_lpi2c1_priv = +{ + .ops = &imxrt_lpi2c_ops, + .config = &imxrt_lpi2c1_config, + .refs = 0, + .intstate = INTSTATE_IDLE, + .msgc = 0, + .msgv = NULL, + .ptr = NULL, + .dcnt = 0, + .flags = 0, + .status = 0 +}; +#endif + +#ifdef CONFIG_IMXRT_LPI2C2 +static const struct imxrt_lpi2c_config_s imxrt_lpi2c2_config = +{ + .base = IMXRT_LPI2C2_BASE, + .busy_idle = CONFIG_LPI2C2_BUSYIDLE, + .filtscl = CONFIG_LPI2C2_FILTSCL, + .filtsda = CONFIG_LPI2C2_FILTSDA, + .scl_pin = GPIO_LPI2C2_SCL, + .sda_pin = GPIO_LPI2C2_SDA, +#if defined(CONFIG_I2C_RESET) + .reset_scl_pin = GPIO_LPI2C2_SCL_RESET, + .reset_sda_pin = GPIO_LPI2C2_SDA_RESET, +#endif +#ifndef CONFIG_I2C_SLAVE + .mode = LPI2C_MASTER, +#else + .mode = LPI2C_SLAVE, +#endif +#ifndef CONFIG_I2C_POLLED + .irq = IMXRT_IRQ_LPI2C2, +#endif +}; + +static struct imxrt_lpi2c_priv_s imxrt_lpi2c2_priv = +{ + .ops = &imxrt_lpi2c_ops, + .config = &imxrt_lpi2c2_config, + .refs = 0, + .intstate = INTSTATE_IDLE, + .msgc = 0, + .msgv = NULL, + .ptr = NULL, + .dcnt = 0, + .flags = 0, + .status = 0 +}; +#endif + +#ifdef CONFIG_IMXRT_LPI2C3 +static const struct imxrt_lpi2c_config_s imxrt_lpi2c3_config = +{ + .base = IMXRT_LPI2C3_BASE, + .busy_idle = CONFIG_LPI2C3_BUSYIDLE, + .filtscl = CONFIG_LPI2C3_FILTSCL, + .filtsda = CONFIG_LPI2C3_FILTSDA, + .scl_pin = GPIO_LPI2C3_SCL, + .sda_pin = GPIO_LPI2C3_SDA, +#if defined(CONFIG_I2C_RESET) + .reset_scl_pin = GPIO_LPI2C3_SCL_RESET, + .reset_sda_pin = GPIO_LPI2C3_SDA_RESET, +#endif +#ifndef CONFIG_I2C_SLAVE + .mode = LPI2C_MASTER, +#else + .mode = LPI2C_SLAVE, +#endif +#ifndef CONFIG_I2C_POLLED + .irq = IMXRT_IRQ_LPI2C3, +#endif +}; + +static struct imxrt_lpi2c_priv_s imxrt_lpi2c3_priv = +{ + .ops = &imxrt_lpi2c_ops, + .config = &imxrt_lpi2c3_config, + .refs = 0, + .intstate = INTSTATE_IDLE, + .msgc = 0, + .msgv = NULL, + .ptr = NULL, + .dcnt = 0, + .flags = 0, + .status = 0 +}; +#endif + +#ifdef CONFIG_IMXRT_LPI2C4 +static const struct imxrt_lpi2c_config_s imxrt_lpi2c4_config = +{ + .base = IMXRT_LPI2C4_BASE, + .busy_idle = CONFIG_LPI2C4_BUSYIDLE, + .filtscl = CONFIG_LPI2C4_FILTSCL, + .filtsda = CONFIG_LPI2C4_FILTSDA, + .scl_pin = GPIO_LPI2C4_SCL, + .sda_pin = GPIO_LPI2C4_SDA, +#if defined(CONFIG_I2C_RESET) + .reset_scl_pin = GPIO_LPI2C4_SCL_RESET, + .reset_sda_pin = GPIO_LPI2C4_SDA_RESET, +#endif +#ifndef CONFIG_I2C_SLAVE + .mode = LPI2C_MASTER, +#else + .mode = LPI2C_SLAVE, +#endif +#ifndef CONFIG_I2C_POLLED + .irq = IMXRT_IRQ_LPI2C4, +#endif +}; + +static struct imxrt_lpi2c_priv_s imxrt_lpi2c4_priv = +{ + .ops = &imxrt_lpi2c_ops, + .config = &imxrt_lpi2c4_config, + .refs = 0, + .intstate = INTSTATE_IDLE, + .msgc = 0, + .msgv = NULL, + .ptr = NULL, + .dcnt = 0, + .flags = 0, + .status = 0 +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imxrt_lpi2c_getreg + * + * Description: + * Get a 32-bit register value by offset + * + ****************************************************************************/ + +static inline uint32_t + imxrt_lpi2c_getreg(FAR struct imxrt_lpi2c_priv_s *priv, uint16_t offset) +{ + return getreg32(priv->config->base + offset); +} + +/**************************************************************************** + * Name: imxrt_lpi2c_putreg + * + * Description: + * Put a 32-bit register value by offset + * + ****************************************************************************/ + +static inline void imxrt_lpi2c_putreg(FAR struct imxrt_lpi2c_priv_s *priv, + uint16_t offset, uint32_t value) +{ + putreg32(value, priv->config->base + offset); +} + +/**************************************************************************** + * Name: imxrt_lpi2c_modifyreg + * + * Description: + * Modify a 32-bit register value by offset + * + ****************************************************************************/ + +static inline void imxrt_lpi2c_modifyreg(FAR struct imxrt_lpi2c_priv_s *priv, + uint16_t offset, uint32_t clearbits, + uint32_t setbits) +{ + modifyreg32(priv->config->base + offset, clearbits, setbits); +} + +/**************************************************************************** + * Name: imxrt_lpi2c_sem_wait + * + * Description: + * Take the exclusive access, waiting as necessary. May be interrupted by + * a signal. + * + ****************************************************************************/ + +static inline int imxrt_lpi2c_sem_wait(FAR struct imxrt_lpi2c_priv_s *priv) +{ + return nxsem_wait(&priv->sem_excl); +} + +/**************************************************************************** + * Name: imxrt_lpi2c_sem_wait_noncancelable + * + * Description: + * Take the exclusive access, waiting as necessary. + * + ****************************************************************************/ + +#ifdef CONFIG_I2C_RESET +static int + imxrt_lpi2c_sem_wait_noncancelable(FAR struct imxrt_lpi2c_priv_s *priv) +{ + return nxsem_wait_uninterruptible(&priv->sem_excl); +} +#endif + +/**************************************************************************** + * Name: imxrt_lpi2c_tousecs + * + * Description: + * Return a micro-second delay based on the number of bytes left to be + * processed. + * + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_LPI2C_DYNTIMEO +static useconds_t imxrt_lpi2c_tousecs(int msgc, FAR struct i2c_msg_s *msgs) +{ + size_t bytecount = 0; + int i; + + /* Count the number of bytes left to process */ + + for (i = 0; i < msgc; i++) + { + bytecount += msgs[i].length; + } + + /* Then return a number of microseconds based on a user provided scaling + * factor. + */ + + return (useconds_t)(CONFIG_IMXRT_LPI2C_DYNTIMEO_USECPERBYTE * bytecount); +} +#endif + +/**************************************************************************** + * Name: imxrt_lpi2c_sem_waitdone + * + * Description: + * Wait for a transfer to complete + * + ****************************************************************************/ + +#ifndef CONFIG_I2C_POLLED +static inline int + imxrt_lpi2c_sem_waitdone(FAR struct imxrt_lpi2c_priv_s *priv) +{ + struct timespec abstime; + irqstate_t flags; + uint32_t regval; + int ret; + + flags = enter_critical_section(); + + /* Enable Interrupts when master mode */ + + if (priv->config->mode == LPI2C_MASTER) + { + if ((priv->flags & I2C_M_READ) != 0) + { + regval = LPI2C_MIER_TDIE | LPI2C_MIER_RDIE | LPI2C_MIER_NDIE | \ + LPI2C_MIER_ALIE | LPI2C_MIER_SDIE; + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MIER_OFFSET, regval); + } + else + { + regval = LPI2C_MIER_TDIE | LPI2C_MIER_NDIE | \ + LPI2C_MIER_ALIE | LPI2C_MIER_SDIE; + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MIER_OFFSET, regval); + } + } + + /* Enable Interrupts when slave mode */ + + else + { +#warning Missing logic for I2C Slave mode + } + + /* Signal the interrupt handler that we are waiting. NOTE: Interrupts + * are currently disabled but will be temporarily re-enabled below when + * nxsem_timedwait() sleeps. + */ + + priv->intstate = INTSTATE_WAITING; + do + { + /* Get the current time */ + + clock_gettime(CLOCK_REALTIME, &abstime); + + /* Calculate a time in the future */ + +#if CONFIG_IMXRT_LPI2C_TIMEOSEC > 0 + abstime.tv_sec += CONFIG_IMXRT_LPI2C_TIMEOSEC; +#endif + + /* Add a value proportional to the number of bytes in the transfer */ + +#ifdef CONFIG_IMXRT_LPI2C_DYNTIMEO + abstime.tv_nsec += 1000 * imxrt_lpi2c_tousecs(priv->msgc, priv->msgv); + if (abstime.tv_nsec >= 1000 * 1000 * 1000) + { + abstime.tv_sec++; + abstime.tv_nsec -= 1000 * 1000 * 1000; + } + +#elif CONFIG_IMXRT_LPI2C_TIMEOMS > 0 + abstime.tv_nsec += CONFIG_IMXRT_LPI2C_TIMEOMS * 1000 * 1000; + if (abstime.tv_nsec >= 1000 * 1000 * 1000) + { + abstime.tv_sec++; + abstime.tv_nsec -= 1000 * 1000 * 1000; + } +#endif + + /* Wait until either the transfer is complete or the timeout expires */ + + ret = nxsem_timedwait_uninterruptible(&priv->sem_isr, &abstime); + if (ret < 0) + { + /* Break out of the loop on irrecoverable errors. This would + * include timeouts and mystery errors reported by nxsem_timedwait. + */ + + break; + } + } + + /* Loop until the interrupt level transfer is complete. */ + + while (priv->intstate != INTSTATE_DONE); + + /* Set the interrupt state back to IDLE */ + + priv->intstate = INTSTATE_IDLE; + + /* Disable I2C interrupts */ + + /* Enable Interrupts when master mode */ + + if (priv->config->mode == LPI2C_MASTER) + { + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MIER_OFFSET, 0); + } + + /* Enable Interrupts when slave mode */ + + else + { + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_SIER_OFFSET, 0); + } + + leave_critical_section(flags); + return ret; +} +#else +static inline int + imxrt_lpi2c_sem_waitdone(FAR struct imxrt_lpi2c_priv_s *priv) +{ + clock_t timeout; + clock_t start; + clock_t elapsed; + int ret; + + /* Get the timeout value */ + +#ifdef CONFIG_IMXRT_LPI2C_DYNTIMEO + timeout = USEC2TICK(imxrt_lpi2c_tousecs(priv->msgc, priv->msgv)); +#else + timeout = CONFIG_IMXRT_LPI2C_TIMEOTICKS; +#endif + + /* Signal the interrupt handler that we are waiting. NOTE: Interrupts + * are currently disabled but will be temporarily re-enabled below when + * nxsem_timedwait() sleeps. + */ + + priv->intstate = INTSTATE_WAITING; + start = clock_systime_ticks(); + + do + { + /* Calculate the elapsed time */ + + elapsed = clock_systime_ticks() - start; + + /* Poll by simply calling the timer interrupt handler until it + * reports that it is done. + */ + + imxrt_lpi2c_isr_process(priv); + } + + /* Loop until the transfer is complete. */ + + while (priv->intstate != INTSTATE_DONE && elapsed < timeout); + + i2cinfo("intstate: %d elapsed: %ld threshold: %ld status: %08x\n", + priv->intstate, (long)elapsed, (long)timeout, priv->status); + + /* Set the interrupt state back to IDLE */ + + ret = priv->intstate == INTSTATE_DONE ? OK : -ETIMEDOUT; + priv->intstate = INTSTATE_IDLE; + return ret; +} +#endif + +/**************************************************************************** + * Name: imxrt_lpi2c_sem_waitstop + * + * Description: + * Wait for a STOP to complete + * + ****************************************************************************/ + +static inline void + imxrt_lpi2c_sem_waitstop(FAR struct imxrt_lpi2c_priv_s *priv) +{ + clock_t start; + clock_t elapsed; + clock_t timeout; + uint32_t regval; + + /* Select a timeout */ + +#ifdef CONFIG_IMXRT_LPI2C_DYNTIMEO + timeout = USEC2TICK(CONFIG_IMXRT_LPI2C_DYNTIMEO_STARTSTOP); +#else + timeout = CONFIG_IMXRT_LPI2C_TIMEOTICKS; +#endif + + /* Wait as stop might still be in progress; but stop might also + * be set because of a timeout error: "The [STOP] bit is set and + * cleared by software, cleared by hardware when a Stop condition is + * detected, set by hardware when a timeout error is detected." + */ + + start = clock_systime_ticks(); + do + { + /* Calculate the elapsed time */ + + elapsed = clock_systime_ticks() - start; + + /* Check for STOP condition */ + + if (priv->config->mode == LPI2C_MASTER) + { + regval = imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MSR_OFFSET); + if ((regval & LPI2C_MSR_SDF) == LPI2C_MSR_SDF) + { + return; + } + } + + /* Enable Interrupts when slave mode */ + + else + { + regval = imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_SSR_OFFSET); + if ((regval & LPI2C_SSR_SDF) == LPI2C_SSR_SDF) + { + return; + } + } + + /* Check for NACK error */ + + if (priv->config->mode == LPI2C_MASTER) + { + regval = imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MSR_OFFSET); + if ((regval & LPI2C_MSR_NDF) == LPI2C_MSR_NDF) + { + return; + } + } + + /* Enable Interrupts when slave mode */ + + else + { +#warning Missing logic for I2C Slave + } + } + + /* Loop until the stop is complete or a timeout occurs. */ + + while (elapsed < timeout); + + /* If we get here then a timeout occurred with the STOP condition + * still pending. + */ + + i2cinfo("Timeout with Status Register: %" PRIx32 "\n", regval); +} + +/**************************************************************************** + * Name: imxrt_lpi2c_sem_post + * + * Description: + * Release the mutual exclusion semaphore + * + ****************************************************************************/ + +static inline void imxrt_lpi2c_sem_post(struct imxrt_lpi2c_priv_s *priv) +{ + nxsem_post(&priv->sem_excl); +} + +/**************************************************************************** + * Name: imxrt_lpi2c_sem_init + * + * Description: + * Initialize semaphores + * + ****************************************************************************/ + +static inline void imxrt_lpi2c_sem_init(FAR struct imxrt_lpi2c_priv_s *priv) +{ + nxsem_init(&priv->sem_excl, 0, 1); + +#ifndef CONFIG_I2C_POLLED + /* This semaphore is used for signaling and, hence, should not have + * priority inheritance enabled. + */ + + nxsem_init(&priv->sem_isr, 0, 0); + nxsem_set_protocol(&priv->sem_isr, SEM_PRIO_NONE); +#endif +} + +/**************************************************************************** + * Name: imxrt_lpi2c_sem_destroy + * + * Description: + * Destroy semaphores. + * + ****************************************************************************/ + +static inline void + imxrt_lpi2c_sem_destroy(FAR struct imxrt_lpi2c_priv_s *priv) +{ + nxsem_destroy(&priv->sem_excl); +#ifndef CONFIG_I2C_POLLED + nxsem_destroy(&priv->sem_isr); +#endif +} + +/**************************************************************************** + * Name: imxrt_lpi2c_trace* + * + * Description: + * I2C trace instrumentation + * + ****************************************************************************/ + +#ifdef CONFIG_I2C_TRACE +static void imxrt_lpi2c_traceclear(FAR struct imxrt_lpi2c_priv_s *priv) +{ + struct imxrt_trace_s *trace = &priv->trace[priv->tndx]; + + trace->status = 0; /* I2C 32-bit SR2|SR1 status */ + trace->count = 0; /* Interrupt count when status change */ + trace->event = I2CEVENT_NONE; /* Last event that occurred with this status */ + trace->parm = 0; /* Parameter associated with the event */ + trace->time = 0; /* Time of first status or event */ +} + +static void imxrt_lpi2c_tracereset(FAR struct imxrt_lpi2c_priv_s *priv) +{ + /* Reset the trace info for a new data collection */ + + priv->tndx = 0; + priv->start_time = clock_systime_ticks(); + imxrt_lpi2c_traceclear(priv); +} + +static void imxrt_lpi2c_tracenew(FAR struct imxrt_lpi2c_priv_s *priv, + uint32_t status) +{ + struct imxrt_trace_s *trace = &priv->trace[priv->tndx]; + + /* Is the current entry uninitialized? Has the status changed? */ + + if (trace->count == 0 || status != trace->status) + { + /* Yes.. Was it the status changed? */ + + if (trace->count != 0) + { + /* Yes.. bump up the trace index (unless out of trace entries) */ + + if (priv->tndx >= (CONFIG_I2C_NTRACE - 1)) + { + i2cerr("ERROR: Trace table overflow\n"); + return; + } + + priv->tndx++; + trace = &priv->trace[priv->tndx]; + } + + /* Initialize the new trace entry */ + + imxrt_lpi2c_traceclear(priv); + trace->status = status; + trace->count = 1; + trace->time = clock_systime_ticks(); + } + else + { + /* Just increment the count of times that we have seen this status */ + + trace->count++; + } +} + +static void imxrt_lpi2c_traceevent(FAR struct imxrt_lpi2c_priv_s *priv, + enum imxrt_trace_e event, uint32_t parm) +{ + struct imxrt_trace_s *trace; + + if (event != I2CEVENT_NONE) + { + trace = &priv->trace[priv->tndx]; + + /* Initialize the new trace entry */ + + trace->event = event; + trace->parm = parm; + + /* Bump up the trace index (unless we are out of trace entries) */ + + if (priv->tndx >= (CONFIG_I2C_NTRACE - 1)) + { + i2cerr("ERROR: Trace table overflow\n"); + return; + } + + priv->tndx++; + imxrt_lpi2c_traceclear(priv); + } +} + +static void imxrt_lpi2c_tracedump(FAR struct imxrt_lpi2c_priv_s *priv) +{ + struct imxrt_trace_s *trace; + int i; + + syslog(LOG_DEBUG, "Elapsed time: %ld\n", + (long)(clock_systime_ticks() - priv->start_time)); + + for (i = 0; i < priv->tndx; i++) + { + trace = &priv->trace[i]; + syslog(LOG_DEBUG, + "%2d. STATUS: %08x COUNT: %3d EVENT: %s(%2d) PARM: %08x " + "TIME: %d\n", + i + 1, trace->status, trace->count, + g_trace_names[trace->event], + trace->event, trace->parm, trace->time - priv->start_time); + } +} +#endif /* CONFIG_I2C_TRACE */ + +/**************************************************************************** + * Name: imxrt_lpi2c_setclock + * + * Description: + * Set the I2C clock + * + ****************************************************************************/ + +static void imxrt_lpi2c_setclock(FAR struct imxrt_lpi2c_priv_s *priv, + uint32_t frequency) +{ + uint32_t src_freq = 0; + uint32_t pll3_div = 0; + uint32_t lpi2c_clk_div; + uint32_t regval; + uint32_t men; + uint32_t prescale = 0; + uint32_t best_prescale = 0; + uint32_t best_clk_hi = 0; + uint32_t abs_error = 0; + uint32_t best_error = 0xffffffff; + uint32_t clk_hi_cycle; + uint32_t computed_rate; + uint32_t count; + + /* Has the I2C bus frequency changed? */ + + if (priv->config->mode == LPI2C_MASTER) + { + if (frequency != priv->frequency) + { + /* Disable the selected LPI2C peripheral to configure the new + * clock if it is enabled. + */ + + men = imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MCR_OFFSET) & + LPI2C_MCR_MEN; + if (men) + { + imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCR_OFFSET, + LPI2C_MCR_MEN, 0); + } + + /* Get the LPI2C clock source frequency */ + + if ((getreg32(IMXRT_CCM_CSCDR2) & CCM_CSCDR2_LPI2C_CLK_SEL) == + CCM_CSCDR2_LPI2C_CLK_SEL_OSC_CLK) + { + src_freq = BOARD_XTAL_FREQUENCY; + } + else + { + if ((getreg32(IMXRT_CCM_ANALOG_PLL_USB1) & + CCM_ANALOG_PLL_USB1_DIV_SELECT_MASK) != 0) + { + pll3_div = 22; + } + else + { + pll3_div = 20; + } + + lpi2c_clk_div = (getreg32(IMXRT_CCM_CSCDR2) & + CCM_CSCDR2_LPI2C_CLK_PODF_MASK) >> + CCM_CSCDR2_LPI2C_CLK_PODF_SHIFT; + lpi2c_clk_div = lpi2c_clk_div + 1; + src_freq = (BOARD_XTAL_FREQUENCY * pll3_div) / + (8 * lpi2c_clk_div) ; + } + + /* LPI2C output frequency = (Source Clock (Hz)/ 2^prescale) / + * (CLKLO + 1 + CLKHI + 1 + ROUNDDOWN((2 + FILTSCL) / 2^prescale) + * + * Assume CLKLO = 2 * CLKHI, SETHOLD = CLKHI, DATAVD = CLKHI / 2 + */ + + for (prescale = 1; + (prescale <= 128) && (best_error != 0); + prescale *= 2) + { + for (clk_hi_cycle = 1; clk_hi_cycle < 32; clk_hi_cycle++) + { + if (clk_hi_cycle == 1) + { + computed_rate = (src_freq / prescale) / + (6 + (2 / prescale)); + } + else + { + computed_rate = (src_freq / prescale) / + ((3 * clk_hi_cycle + 2) + + (2 / prescale)); + } + + if (frequency > computed_rate) + { + abs_error = frequency - computed_rate; + } + else + { + abs_error = computed_rate - frequency; + } + + if (abs_error < best_error) + { + best_prescale = prescale; + best_clk_hi = clk_hi_cycle; + best_error = abs_error; + + if (abs_error == 0) + { + break; + } + } + } + } + + regval = LPI2C_MCCR0_CLKHI(best_clk_hi); + + if (best_clk_hi < 2) + { + regval |= LPI2C_MCCR0_CLKLO(3) | LPI2C_MCCR0_SETHOLD(2) | + LPI2C_MCCR0_DATAVD(1); + } + else + { + regval |= LPI2C_MCCR0_CLKLO(2 * best_clk_hi) | + LPI2C_MCCR0_SETHOLD(best_clk_hi) | + LPI2C_MCCR0_DATAVD(best_clk_hi / 2); + } + + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MCCR0_OFFSET, regval); + + for (count = 0; count < 8; count++) + { + if (best_prescale == (1 << count)) + { + best_prescale = count; + break; + } + } + + imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCFGR1_OFFSET, + LPI2C_MCFGR1_PRESCALE_MASK, + LPI2C_MCFGR1_PRESCALE(best_prescale)); + + /* Re-enable LPI2C if it was enabled previously */ + + if (men) + { + imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCR_OFFSET, 0, + LPI2C_MCR_MEN); + } + + /* Save the new LPI2C frequency */ + + priv->frequency = frequency; + } + } +} + +/**************************************************************************** + * Name: imxrt_lpi2c_sendstart + * + * Description: + * Send the START conditions/force Master mode + * + ****************************************************************************/ + +static inline void imxrt_lpi2c_sendstart(FAR struct imxrt_lpi2c_priv_s *priv, + uint8_t address) +{ + uint32_t txcount = 0; + uint32_t status = 0; + uint8_t addr; + + /* Generate START condition and send the address */ + + /* Turn off auto_stop option */ + + imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCFGR1_OFFSET, + LPI2C_MCFGR1_IGNACK, 0); + + do + { + txcount = (imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MFSR_OFFSET) & + LPI2C_MFSR_TXCOUNT_MASK) >> LPI2C_MFSR_TXCOUNT_SHIFT; + txcount = 4 - txcount; + + status = imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MSR_OFFSET); + + if (status & LPI2C_MSR_ERROR_MASK) + { + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MSR_OFFSET, + status & LPI2C_MSR_ERROR_MASK); + } + } + while (txcount == 0); + + if ((priv->flags & I2C_M_READ) != 0) + { + addr = I2C_READADDR8(address); + } + else + { + addr = I2C_WRITEADDR8(address); + } + + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MTDR_OFFSET, + (LPI2C_MTDR_CMD_START | LPI2C_MTDR_DATA(addr))); +} + +/**************************************************************************** + * Name: imxrt_lpi2c_sendstop + * + * Description: + * Send the STOP conditions + * + ****************************************************************************/ + +static inline void imxrt_lpi2c_sendstop(FAR struct imxrt_lpi2c_priv_s *priv) +{ + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MTDR_OFFSET, LPI2C_MTDR_CMD_STOP); +} + +/**************************************************************************** + * Name: imxrt_lpi2c_getstatus + * + * Description: + * Get 32-bit status + * + ****************************************************************************/ + +static inline uint32_t + imxrt_lpi2c_getstatus(FAR struct imxrt_lpi2c_priv_s *priv) +{ + return imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MSR_OFFSET); +} + +/**************************************************************************** + * Name: imxrt_lpi2c_isr_process + * + * Description: + * Common Interrupt Service Routine + * + ****************************************************************************/ + +static int imxrt_lpi2c_isr_process(struct imxrt_lpi2c_priv_s *priv) +{ + uint32_t status = imxrt_lpi2c_getstatus(priv); + + /* Check for new trace setup */ + + imxrt_lpi2c_tracenew(priv, status); + + /* After an error we can get an SDF */ + + if (priv->intstate == INTSTATE_DONE && (status & LPI2C_MSR_SDF) != 0) + { + imxrt_lpi2c_traceevent(priv, I2CEVENT_STOP, 0); + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MSR_OFFSET, LPI2C_MSR_SDF); + } + + /* Check if there is more bytes to send */ + + else if (((priv->flags & I2C_M_READ) == 0) && + (status & LPI2C_MSR_TDF) != 0) + { + if (priv->dcnt > 0) + { + imxrt_lpi2c_traceevent(priv, I2CEVENT_SENDBYTE, priv->dcnt); + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MTDR_OFFSET, + LPI2C_MTDR_CMD_TXD | + LPI2C_MTDR_DATA(*priv->ptr++)); + priv->dcnt--; + + if ((priv->msgc <= 0) && (priv->dcnt == 0)) + { + imxrt_lpi2c_sendstop(priv); + } + } + } + + /* Check if there is more bytes to read */ + + else if (((priv->flags & I2C_M_READ) != 0) && + (status & LPI2C_MSR_RDF) != 0) + { + /* Read a byte, if dcnt goes < 0, then read dummy bytes to ack ISRs */ + + if (priv->dcnt > 0) + { + imxrt_lpi2c_traceevent(priv, I2CEVENT_RCVBYTE, priv->dcnt); + + /* No interrupts or context switches should occur in the following + * sequence. Otherwise, additional bytes may be sent by the device. + */ + +#ifdef CONFIG_I2C_POLLED + irqstate_t flags = enter_critical_section(); +#endif + + /* Receive a byte */ + + *priv->ptr++ = imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MRDR_OFFSET) & + LPI2C_MRDR_DATA_MASK; + priv->dcnt--; + +#ifdef CONFIG_I2C_POLLED + leave_critical_section(flags); +#endif + if ((priv->msgc <= 0) && (priv->dcnt == 0)) + { + imxrt_lpi2c_sendstop(priv); + } + } + else + { + imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MRDR_OFFSET); + } + } + + if (priv->dcnt <= 0) + { + if (priv->msgc > 0 && priv->msgv != NULL) + { + priv->ptr = priv->msgv->buffer; + priv->dcnt = priv->msgv->length; + priv->flags = priv->msgv->flags; + + if ((priv->msgv->flags & I2C_M_NOSTART) == 0) + { + imxrt_lpi2c_traceevent(priv, I2CEVENT_STARTRESTART, + priv->msgc); + imxrt_lpi2c_sendstart(priv, priv->msgv->addr); + } + else + { + imxrt_lpi2c_traceevent(priv, I2CEVENT_NOSTART, priv->msgc); + } + + priv->msgv++; + priv->msgc--; + + if ((priv->flags & I2C_M_READ) != 0) + { +#ifndef CONFIG_I2C_POLLED + /* Stop TX interrupt */ + + imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MIER_OFFSET, + LPI2C_MIER_TDIE, LPI2C_MIER_RDIE); +#endif + /* Set LPI2C in read mode */ + + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MTDR_OFFSET, + LPI2C_MTDR_CMD_RXD | + LPI2C_MTDR_DATA((priv->dcnt - 1))); + } + else + { + /* Send the first byte from tx buffer */ + + imxrt_lpi2c_traceevent(priv, I2CEVENT_SENDBYTE, priv->dcnt); + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MTDR_OFFSET, + LPI2C_MTDR_CMD_TXD | + LPI2C_MTDR_DATA(*priv->ptr++)); + priv->dcnt--; + if ((priv->msgc <= 0) && (priv->dcnt == 0)) + { + imxrt_lpi2c_sendstop(priv); + } + } + } + else if (priv->msgv && ((status & LPI2C_MSR_SDF) != 0)) + { + imxrt_lpi2c_traceevent(priv, I2CEVENT_STOP, 0); + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MSR_OFFSET, LPI2C_MSR_SDF); + + /* Check is there thread waiting for this event (there should be) */ + +#ifndef CONFIG_I2C_POLLED + if (priv->intstate == INTSTATE_WAITING) + { + /* Update Status once at the end */ + + priv->status = status; + + /* inform the thread that transfer is complete + * and wake it up + */ + + nxsem_post(&priv->sem_isr); + priv->intstate = INTSTATE_DONE; + } +#else + priv->status = status; + priv->intstate = INTSTATE_DONE; +#endif + /* Mark that this transaction stopped */ + + priv->msgv = NULL; + } +#ifndef CONFIG_I2C_POLLED + else + { + imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MIER_OFFSET, + LPI2C_MIER_TDIE | LPI2C_MIER_RDIE, 0); + } +#endif + } + + /* Check for errors */ + + if ((status & LPI2C_MSR_EPF) != 0) + { + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MSR_OFFSET, LPI2C_MSR_EPF); + } + + if ((status & LPI2C_MSR_ERROR_MASK) != 0) + { + imxrt_lpi2c_traceevent(priv, I2CEVENT_ERROR, 0); + + /* Clear the TX and RX FIFOs */ + + imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCR_OFFSET, 0, + LPI2C_MCR_RTF | LPI2C_MCR_RRF); + + /* Clear the error */ + + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MSR_OFFSET, + (status & (LPI2C_MSR_NDF | LPI2C_MSR_ALF | + LPI2C_MSR_FEF))); + +#ifndef CONFIG_I2C_POLLED + if (priv->intstate == INTSTATE_WAITING) + { + /* Update Status once at the end */ + + priv->status = status; + + /* inform the thread that transfer is complete + * and wake it up + */ + + nxsem_post(&priv->sem_isr); + priv->intstate = INTSTATE_DONE; + } +#else + priv->status = status; + priv->intstate = INTSTATE_DONE; +#endif + } + + return OK; +} + +/**************************************************************************** + * Name: imxrt_lpi2c_isr + * + * Description: + * Common I2C interrupt service routine + * + ****************************************************************************/ + +#ifndef CONFIG_I2C_POLLED +static int imxrt_lpi2c_isr(int irq, void *context, FAR void *arg) +{ + struct imxrt_lpi2c_priv_s *priv = (struct imxrt_lpi2c_priv_s *)arg; + + DEBUGASSERT(priv != NULL); + return imxrt_lpi2c_isr_process(priv); +} +#endif + +/**************************************************************************** + * Name: imxrt_lpi2c_clock_enable + * + * Description: + * Ungate LPI2C clock + * + ****************************************************************************/ + +void imxrt_lpi2c_clock_enable (uint32_t base) +{ + if (base == IMXRT_LPI2C1_BASE) + { + imxrt_clockall_lpi2c1(); + } + else if (base == IMXRT_LPI2C2_BASE) + { + imxrt_clockall_lpi2c2(); + } + else if (base == IMXRT_LPI2C3_BASE) + { + imxrt_clockall_lpi2c3(); + } + else if (base == IMXRT_LPI2C4_BASE) + { + imxrt_clockall_lpi2c4_serial(); + } +} + +/**************************************************************************** + * Name: imxrt_lpi2c_clock_disable + * + * Description: + * Gate LPI2C clock + * + ****************************************************************************/ + +void imxrt_lpi2c_clock_disable (uint32_t base) +{ + if (base == IMXRT_LPI2C1_BASE) + { + imxrt_clockoff_lpi2c1(); + } + else if (base == IMXRT_LPI2C2_BASE) + { + imxrt_clockoff_lpi2c2(); + } + else if (base == IMXRT_LPI2C3_BASE) + { + imxrt_clockoff_lpi2c3(); + } + else if (base == IMXRT_LPI2C4_BASE) + { + imxrt_clockoff_lpi2c4_serial(); + } +} + +/**************************************************************************** + * Name: imxrt_lpi2c_init + * + * Description: + * Setup the I2C hardware, ready for operation with defaults + * + ****************************************************************************/ + +static int imxrt_lpi2c_init(FAR struct imxrt_lpi2c_priv_s *priv) +{ + /* Power-up and configure GPIOs */ + + /* Configure pins */ + + imxrt_config_gpio(priv->config->scl_pin); + imxrt_config_gpio(priv->config->sda_pin); + + /* Enable power and reset the peripheral */ + + imxrt_lpi2c_clock_enable(priv->config->base); + + /* Reset LPI2C before configuring it */ + + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MCR_OFFSET, LPI2C_MCR_RST); + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MCR_OFFSET, 0); + + /* Disable doze mode (Set DOZEN bit in 1 to disable) */ + + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MCR_OFFSET, LPI2C_MCR_DOZEN); + + /* Disable host request */ + + imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCFGR0_OFFSET, + LPI2C_MCFG0_HREN | LPI2C_MCFG0_HRSEL, + LPI2C_MCFG0_HRPOL); + + /* Pin config and ignore NACK disable */ + + imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCFGR1_OFFSET, + LPI2C_MCFGR1_IGNACK | LPI2C_MCFGR1_PINCFG_MASK, 0); + + /* Set tx and rx watermarks */ + + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MFCR_OFFSET, + LPI2C_MFCR_TXWATER(0) | LPI2C_MFCR_RXWATER(0)); + + /* Force a frequency update */ + + priv->frequency = 0; + imxrt_lpi2c_setclock(priv, 100000); + + /* Set scl, sda glitch filters and busy idle */ + + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MCFGR2_OFFSET, + LPI2C_MCFG2_BUSIDLE(priv->config->busy_idle) | + LPI2C_MCFG2_FILTSCL_CYCLES(priv->config->filtscl) | + LPI2C_MCFG2_FILTSDA_CYCLES(priv->config->filtsda)); + + /* Set pin low cycles to 0 (disable) */ + + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MCFGR3_OFFSET, + LPI2C_MCFG3_PINLOW_CYCLES(0)); + + /* Attach ISRs */ + +#ifndef CONFIG_I2C_POLLED + irq_attach(priv->config->irq, imxrt_lpi2c_isr, priv); + up_enable_irq(priv->config->irq); +#endif + + /* Enable I2C */ + + imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCR_OFFSET, 0, LPI2C_MCR_MEN); + return OK; +} + +/**************************************************************************** + * Name: imxrt_lpi2c_deinit + * + * Description: + * Shutdown the I2C hardware + * + ****************************************************************************/ + +static int imxrt_lpi2c_deinit(FAR struct imxrt_lpi2c_priv_s *priv) +{ + /* Disable I2C */ + + imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCR_OFFSET, LPI2C_MCR_MEN, 0); + + /* Reset LPI2C */ + + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MCR_OFFSET, LPI2C_MCR_RST); + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MCR_OFFSET, 0); + + /* Disable and detach interrupts */ + +#ifndef CONFIG_I2C_POLLED + up_disable_irq(priv->config->irq); + irq_detach(priv->config->irq); +#endif + + /* Disable clocking */ + + imxrt_lpi2c_clock_disable(priv->config->base); + + return OK; +} + +/**************************************************************************** + * Device Driver Operations + ****************************************************************************/ + +/**************************************************************************** + * Name: imxrt_lpi2c_transfer + * + * Description: + * Generic I2C transfer function + * + ****************************************************************************/ + +static int imxrt_lpi2c_transfer(FAR struct i2c_master_s *dev, + FAR struct i2c_msg_s *msgs, int count) +{ + FAR struct imxrt_lpi2c_priv_s *priv = (struct imxrt_lpi2c_priv_s *)dev; + int ret; + + DEBUGASSERT(count > 0); + + /* Ensure that address or flags don't change meanwhile */ + + ret = imxrt_lpi2c_sem_wait(priv); + if (ret < 0) + { + return ret; + } + + /* Clear any pending error interrupts */ + + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MSR_OFFSET, 0xffffffff); + + /* Old transfers are done */ + + /* Reset ptr and dcnt to ensure an unexpected data interrupt doesn't + * overwrite stale data. + */ + + priv->dcnt = 0; + priv->ptr = NULL; + + priv->msgv = msgs; + priv->msgc = count; + priv->flags = msgs->flags; + + i2cinfo("Flags %x, len %d \n", msgs->flags, msgs->length); + + /* Reset I2C trace logic */ + + imxrt_lpi2c_tracereset(priv); + + /* Set I2C clock frequency */ + + imxrt_lpi2c_setclock(priv, msgs->frequency); + + priv->status = 0; + + /* Wait for an ISR, if there was a timeout, fetch latest status to get + * the BUSY flag. + */ + + if (imxrt_lpi2c_sem_waitdone(priv) < 0) + { + ret = -ETIMEDOUT; + + i2cerr("ERROR: Timed out: MCR: status: 0x%" PRIx32 "\n", priv->status); + } + + /* Check for error status conditions */ + + else if ((priv->status & LPI2C_MSR_ERROR_MASK) != 0) + { + /* I2C_SR1_ERRORMASK is the 'OR' of the following individual bits: */ + + if (priv->status & LPI2C_MSR_ALF) + { + /* Arbitration Lost (master mode) */ + + i2cerr("Arbitration lost\n"); + ret = -EAGAIN; + } + else if (priv->status & LPI2C_MSR_NDF) + { + /* Acknowledge Failure */ + + i2cerr("Ack failure\n"); + ret = -ENXIO; + } + else + { + /* FIFO Error */ + + i2cerr("Transfer without start condition\n"); + ret = -EINVAL; + } + } + + /* Dump the trace result */ + + imxrt_lpi2c_tracedump(priv); + + /* Ensure that any ISR happening after we finish can't overwrite any user + * data. + */ + + priv->dcnt = 0; + priv->ptr = NULL; + + imxrt_lpi2c_sem_post(priv); + return ret; +} + +/**************************************************************************** + * Name: imxrt_lpi2c_reset + * + * Description: + * Perform an I2C bus reset in an attempt to break loose stuck I2C devices. + * + * Input Parameters: + * dev - Device-specific state data + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_I2C_RESET +static int imxrt_lpi2c_reset(FAR struct i2c_master_s *dev) +{ + FAR struct imxrt_lpi2c_priv_s *priv = (FAR struct imxrt_lpi2c_priv_s *)dev; + unsigned int clock_count; + unsigned int stretch_count; + uint32_t scl_gpio; + uint32_t sda_gpio; + uint32_t frequency; + int ret; + + DEBUGASSERT(dev); + + /* Our caller must own a ref */ + + DEBUGASSERT(priv->refs > 0); + + /* Lock out other clients */ + + ret = imxrt_lpi2c_sem_wait_noncancelable(priv); + if (ret < 0) + { + return ret; + } + + ret = -EIO; + + /* Save the current frequency */ + + frequency = priv->frequency; + + /* De-init the port */ + + imxrt_lpi2c_deinit(priv); + + /* Use GPIO configuration to un-wedge the bus */ + + scl_gpio = priv->config->reset_scl_pin | GPIO_SION_ENABLE; + sda_gpio = priv->config->reset_sda_pin | GPIO_SION_ENABLE; + + imxrt_config_gpio(scl_gpio); + imxrt_config_gpio(sda_gpio); + + /* Let SDA go high */ + + imxrt_gpio_write(sda_gpio, 1); + + /* Clock the bus until any slaves currently driving it let it go. */ + + clock_count = 0; + while (!imxrt_gpio_read(sda_gpio)) + { + /* Give up if we have tried too hard */ + + if (clock_count++ > 10) + { + goto out; + } + + /* Sniff to make sure that clock stretching has finished. + * + * If the bus never relaxes, the reset has failed. + */ + + stretch_count = 0; + while (!imxrt_gpio_read(scl_gpio)) + { + /* Give up if we have tried too hard */ + + if (stretch_count++ > 10) + { + goto out; + } + + up_udelay(10); + } + + /* Drive SCL low */ + + imxrt_gpio_write(scl_gpio, 0); + up_udelay(10); + + /* Drive SCL high again */ + + imxrt_gpio_write(scl_gpio, 1); + up_udelay(10); + } + + /* Generate a start followed by a stop to reset slave + * state machines. + */ + + imxrt_gpio_write(sda_gpio, 0); + up_udelay(10); + imxrt_gpio_write(scl_gpio, 0); + up_udelay(10); + imxrt_gpio_write(scl_gpio, 1); + up_udelay(10); + imxrt_gpio_write(sda_gpio, 1); + up_udelay(10); + + imxrt_config_gpio(sda_gpio); + imxrt_config_gpio(scl_gpio); + + /* Re-init the port */ + + imxrt_lpi2c_init(priv); + + /* Restore the frequency */ + + imxrt_lpi2c_setclock(priv, frequency); + ret = OK; + +out: + + /* Release the port for re-use by other clients */ + + imxrt_lpi2c_sem_post(priv); + return ret; +} +#endif /* CONFIG_I2C_RESET */ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imxrt_i2cbus_initialize + * + * Description: + * Initialize one I2C bus + * + ****************************************************************************/ + +FAR struct i2c_master_s *imxrt_i2cbus_initialize(int port) +{ + struct imxrt_lpi2c_priv_s * priv = NULL; + irqstate_t flags; + + /* Get I2C private structure */ + + switch (port) + { +#ifdef CONFIG_IMXRT_LPI2C1 + case 1: + priv = (struct imxrt_lpi2c_priv_s *)&imxrt_lpi2c1_priv; + break; +#endif +#ifdef CONFIG_IMXRT_LPI2C2 + case 2: + priv = (struct imxrt_lpi2c_priv_s *)&imxrt_lpi2c2_priv; + break; +#endif +#ifdef CONFIG_IMXRT_LPI2C3 + case 3: + priv = (struct imxrt_lpi2c_priv_s *)&imxrt_lpi2c3_priv; + break; +#endif +#ifdef CONFIG_IMXRT_LPI2C4 + case 4: + priv = (struct imxrt_lpi2c_priv_s *)&imxrt_lpi2c4_priv; + break; +#endif + default: + return NULL; + } + + /* Initialize private data for the first time, increment reference count, + * power-up hardware and configure GPIOs. + */ + + flags = enter_critical_section(); + + if ((volatile int)priv->refs++ == 0) + { + imxrt_lpi2c_sem_init(priv); + imxrt_lpi2c_init(priv); + } + + leave_critical_section(flags); + + return (struct i2c_master_s *)priv; +} + +/**************************************************************************** + * Name: imxrt_i2cbus_uninitialize + * + * Description: + * Uninitialize an I2C bus + * + ****************************************************************************/ + +int imxrt_i2cbus_uninitialize(FAR struct i2c_master_s *dev) +{ + FAR struct imxrt_lpi2c_priv_s *priv = (struct imxrt_lpi2c_priv_s *)dev; + irqstate_t flags; + + DEBUGASSERT(dev); + + /* Decrement reference count and check for underflow */ + + if (priv->refs == 0) + { + return ERROR; + } + + flags = enter_critical_section(); + + if (--priv->refs > 0) + { + leave_critical_section(flags); + return OK; + } + + leave_critical_section(flags); + + /* Disable power and other HW resource (GPIO's) */ + + imxrt_lpi2c_deinit(priv); + + /* Release unused resources */ + + imxrt_lpi2c_sem_destroy(priv); + return OK; +} + +#endif /* CONFIG_IMXRT_LPI2C1 || CONFIG_IMXRT_LPI2C2 || \ + * CONFIG_IMXRT_LPI2C3 || CONFIG_IMXRT_LPI2C4 */ diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpspi.c b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpspi.c new file mode 100644 index 000000000..ccf50c13f --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpspi.c @@ -0,0 +1,1706 @@ +/**************************************************************************** + * arch/arm/src/imxrt/imxrt_lpspi.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. + * + ****************************************************************************/ + +/**************************************************************************** + * The external functions, imxrt_lpspi1/2/3/4select and + * imxrt_lpspi1/2/3/4status must be provided by board-specific logic. + * They are implementations of the select and status methods of the SPI + * interface defined by struct imxrt_lpspi_ops_s (see + * include/nuttx/spi/spi.h). All other methods (including + * imxrt_lpspibus_initialize()) are provided by common IMXRT logic. + * To use this common SPI logic on your board: + * + * 1. Provide logic in imxrt_boardinitialize() to configure SPI chip + * select pins. + * 2. Provide imxrt_lpspi1/2/3/4select() and imxrt_lpspi1/2/3/4status() + * functions in your board-specific logic. These functions will + * perform chip selection and status operations using GPIOs in the way + * your board is configured. + * 3. Add a calls to imxrt_lpspibus_initialize() in your low level + * application initialization logic + * 4. The handle returned by imxrt_lpspibus_initialize() may then be + * used to bind the SPI driver to higher level logic (e.g., calling + * mmcsd_lpspislotinitialize(), for example, will bind the SPI + * driver to the SPI MMC/SD driver). + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "arm_internal.h" +#include "arm_arch.h" + +#include "chip.h" + +#include "imxrt_lpspi.h" +#include "imxrt_gpio.h" +#include "hardware/imxrt_pinmux.h" +#include "hardware/imxrt_lpspi.h" +#include "hardware/imxrt_ccm.h" +#include "imxrt_periphclks.h" + +#if defined(CONFIG_IMXRT_LPSPI1) || defined(CONFIG_IMXRT_LPSPI2) || \ + defined(CONFIG_IMXRT_LPSPI3) || defined(CONFIG_IMXRT_LPSPI4) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +/* SPI interrupts */ + +#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS +# error "Interrupt driven SPI not yet supported" +#endif + +#if defined(CONFIG_IMXRT_LPSPI_DMA) +# error "DMA mode is not yet supported" +#endif + +/* Can't have both interrupt driven SPI and SPI DMA */ + +#if defined(CONFIG_IMXRT_LPSPI_INTERRUPTS) && defined(CONFIG_IMXRT_LPSPI_DMA) +# error "Cannot enable both interrupt mode and DMA mode for SPI" +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct imxrt_lpspidev_s +{ + struct spi_dev_s spidev; /* Externally visible part of the SPI interface */ + uint32_t spibase; /* SPIn base address */ +#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS + uint8_t spiirq; /* SPI IRQ number */ +#endif + sem_t exclsem; /* Held while chip is selected for mutual exclusion */ + uint32_t frequency; /* Requested clock frequency */ + uint32_t actual; /* Actual clock frequency */ + int8_t nbits; /* Width of word in bits */ + uint8_t mode; /* Mode 0,1,2,3 */ +}; + +enum imxrt_delay_e +{ + LPSPI_PCS_TO_SCK = 1, /* PCS-to-SCK delay. */ + LPSPI_LAST_SCK_TO_PCS, /* Last SCK edge to PCS delay. */ + LPSPI_BETWEEN_TRANSFER /* Delay between transfers. */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Helpers */ + +static inline uint32_t +imxrt_lpspi_getreg32(FAR struct imxrt_lpspidev_s *priv, + uint8_t offset); +static inline void imxrt_lpspi_putreg32(FAR struct imxrt_lpspidev_s *priv, + uint8_t offset, uint32_t value); +static inline uint32_t imxrt_lpspi_readword( + FAR struct imxrt_lpspidev_s *priv); +static inline void imxrt_lpspi_writeword(FAR struct imxrt_lpspidev_s *priv, + uint16_t byte); +static inline bool imxrt_lpspi_9to16bitmode( + FAR struct imxrt_lpspidev_s *priv); +static inline void imxrt_lpspi_master_set_delays(FAR struct imxrt_lpspidev_s + *priv, uint32_t delay_ns, + enum imxrt_delay_e type); +static inline void imxrt_lpspi_master_set_delay_scaler( + FAR struct imxrt_lpspidev_s *priv, + uint32_t scaler, + enum imxrt_delay_e type); + +/* SPI methods */ + +static int imxrt_lpspi_lock(FAR struct spi_dev_s *dev, bool lock); +static uint32_t imxrt_lpspi_setfrequency(FAR struct spi_dev_s *dev, + uint32_t frequency); +static void imxrt_lpspi_setmode(FAR struct spi_dev_s *dev, + enum spi_mode_e mode); +static void imxrt_lpspi_setbits(FAR struct spi_dev_s *dev, int nbits); +#ifdef CONFIG_SPI_HWFEATURES +static int imxrt_lpspi_hwfeatures(FAR struct spi_dev_s *dev, + imxrt_lpspi_hwfeatures_t features); +#endif +static uint32_t imxrt_lpspi_send(FAR struct spi_dev_s *dev, uint32_t wd); +static void imxrt_lpspi_exchange(FAR struct spi_dev_s *dev, + FAR const void *txbuffer, + FAR void *rxbuffer, + size_t nwords); +#ifndef CONFIG_SPI_EXCHANGE +static void imxrt_lpspi_sndblock(FAR struct spi_dev_s *dev, + FAR const void *txbuffer, size_t nwords); +static void imxrt_lpspi_recvblock(FAR struct spi_dev_s *dev, + FAR void *rxbuffer, + size_t nwords); +#endif + +/* Initialization */ + +static void imxrt_lpspi_bus_initialize(FAR struct imxrt_lpspidev_s *priv); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_LPSPI1 +static const struct spi_ops_s g_spi1ops = +{ + .lock = imxrt_lpspi_lock, + .select = imxrt_lpspi1select, + .setfrequency = imxrt_lpspi_setfrequency, + .setmode = imxrt_lpspi_setmode, + .setbits = imxrt_lpspi_setbits, +#ifdef CONFIG_SPI_HWFEATURES + .hwfeatures = imxrt_lpspi_hwfeatures, +#endif + .status = imxrt_lpspi1status, +#ifdef CONFIG_SPI_CMDDATA + .cmddata = imxrt_lpspi1cmddata, +#endif + .send = imxrt_lpspi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = imxrt_lpspi_exchange, +#else + .sndblock = imxrt_lpspi_sndblock, + .recvblock = imxrt_lpspi_recvblock, +#endif +#ifdef CONFIG_SPI_CALLBACK + .registercallback = imxrt_lpspi1register, /* Provided externally */ +#else + .registercallback = 0, /* Not implemented */ +#endif +}; + +static struct imxrt_lpspidev_s g_lpspi1dev = +{ + .spidev = + { + &g_spi1ops + }, + .spibase = IMXRT_LPSPI1_BASE, +#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS + .spiirq = IMXRT_IRQ_LPSPI1, +#endif +#ifdef CONFIG_IMXRT_LPSPI_DMA + .rxch = DMAMAP_LPSPI1_RX, + .txch = DMAMAP_LPSPI1_TX, +#endif +}; +#endif + +#ifdef CONFIG_IMXRT_LPSPI2 +static const struct spi_ops_s g_spi2ops = +{ + .lock = imxrt_lpspi_lock, + .select = imxrt_lpspi2select, + .setfrequency = imxrt_lpspi_setfrequency, + .setmode = imxrt_lpspi_setmode, + .setbits = imxrt_lpspi_setbits, +#ifdef CONFIG_SPI_HWFEATURES + .hwfeatures = imxrt_lpspi_hwfeatures, +#endif + .status = imxrt_lpspi2status, +#ifdef CONFIG_SPI_CMDDATA + .cmddata = imxrt_lpspi2cmddata, +#endif + .send = imxrt_lpspi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = imxrt_lpspi_exchange, +#else + .sndblock = imxrt_lpspi_sndblock, + .recvblock = imxrt_lpspi_recvblock, +#endif +#ifdef CONFIG_SPI_CALLBACK + .registercallback = imxrt_lpspi2register, /* Provided externally */ +#else + .registercallback = 0, /* Not implemented */ +#endif +}; + +static struct imxrt_lpspidev_s g_lpspi2dev = +{ + .spidev = + { + &g_spi2ops + }, + .spibase = IMXRT_LPSPI2_BASE, +#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS + .spiirq = IMXRT_IRQ_LPSPI2, +#endif +#ifdef CONFIG_IMXRT_LPSPI_DMA + .rxch = DMAMAP_LPSPI2_RX, + .txch = DMAMAP_LPSPI2_TX, +#endif +}; +#endif + +#ifdef CONFIG_IMXRT_LPSPI3 +static const struct spi_ops_s g_spi3ops = +{ + .lock = imxrt_lpspi_lock, + .select = imxrt_lpspi3select, + .setfrequency = imxrt_lpspi_setfrequency, + .setmode = imxrt_lpspi_setmode, + .setbits = imxrt_lpspi_setbits, +#ifdef CONFIG_SPI_HWFEATURES + .hwfeatures = imxrt_lpspi_hwfeatures, +#endif + .status = imxrt_lpspi3status, +#ifdef CONFIG_SPI_CMDDATA + .cmddata = imxrt_lpspi3cmddata, +#endif + .send = imxrt_lpspi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = imxrt_lpspi_exchange, +#else + .sndblock = imxrt_lpspi_sndblock, + .recvblock = imxrt_lpspi_recvblock, +#endif +#ifdef CONFIG_SPI_CALLBACK + .registercallback = imxrt_lpspi3register, /* Provided externally */ +#else + .registercallback = 0, /* Not implemented */ +#endif +}; + +static struct imxrt_lpspidev_s g_lpspi3dev = +{ + .spidev = + { + &g_spi3ops + }, + .spibase = IMXRT_LPSPI3_BASE, +#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS + .spiirq = IMXRT_IRQ_LPSPI3, +#endif +#ifdef CONFIG_IMXRT_LPSPI_DMA + .rxch = DMAMAP_LPSPI3_RX, + .txch = DMAMAP_LPSPI3_TX, +#endif +}; +#endif + +#ifdef CONFIG_IMXRT_LPSPI4 +static const struct spi_ops_s g_spi4ops = +{ + .lock = imxrt_lpspi_lock, + .select = imxrt_lpspi4select, + .setfrequency = imxrt_lpspi_setfrequency, + .setmode = imxrt_lpspi_setmode, + .setbits = imxrt_lpspi_setbits, +#ifdef CONFIG_SPI_HWFEATURES + .hwfeatures = imxrt_lpspi_hwfeatures, +#endif + .status = imxrt_lpspi4status, +#ifdef CONFIG_SPI_CMDDATA + .cmddata = imxrt_lpspi4cmddata, +#endif + .send = imxrt_lpspi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = imxrt_lpspi_exchange, +#else + .sndblock = imxrt_lpspi_sndblock, + .recvblock = imxrt_lpspi_recvblock, +#endif +#ifdef CONFIG_SPI_CALLBACK + .registercallback = imxrt_lpspi4register, /* Provided externally */ +#else + .registercallback = 0, /* Not implemented */ +#endif +}; + +static struct imxrt_lpspidev_s g_lpspi4dev = +{ + .spidev = + { + &g_spi4ops + }, + .spibase = IMXRT_LPSPI4_BASE, +#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS + .spiirq = IMXRT_IRQ_LPSPI4, +#endif +#ifdef CONFIG_IMXRT_LPSPI_DMA + .rxch = DMAMAP_LPSPI4_RX, + .txch = DMAMAP_LPSPI4_TX, +#endif +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imxrt_lpspi_getreg8 + * + * Description: + * Get the contents of the SPI register at offset + * + * Input Parameters: + * priv - private SPI device structure + * offset - offset to the register of interest + * + * Returned Value: + * The contents of the 8-bit register + * + ****************************************************************************/ + +static inline uint8_t imxrt_lpspi_getreg8(FAR struct imxrt_lpspidev_s *priv, + uint8_t offset) +{ + return getreg8(priv->spibase + offset); +} + +/**************************************************************************** + * Name: imxrt_lpspi_putreg8 + * + * Description: + * Write a 8-bit value to the SPI register at offset + * + * Input Parameters: + * priv - private SPI device structure + * offset - offset to the register of interest + * value - the 8-bit value to be written + * + ****************************************************************************/ + +static inline void imxrt_lpspi_putreg8(FAR struct imxrt_lpspidev_s *priv, + uint8_t offset, uint8_t value) +{ + putreg8(value, priv->spibase + offset); +} + +/**************************************************************************** + * Name: imxrt_lpspi_getreg + * + * Description: + * Get the contents of the SPI register at offset + * + * Input Parameters: + * priv - private SPI device structure + * offset - offset to the register of interest + * + * Returned Value: + * The contents of the 32-bit register + * + ****************************************************************************/ + +static inline uint32_t +imxrt_lpspi_getreg32(FAR struct imxrt_lpspidev_s *priv, + uint8_t offset) +{ + return getreg32(priv->spibase + offset); +} + +/**************************************************************************** + * Name: imxrt_lpspi_putreg + * + * Description: + * Write a 16-bit value to the SPI register at offset + * + * Input Parameters: + * priv - private SPI device structure + * offset - offset to the register of interest + * value - the 32-bit value to be written + * + * Returned Value: + * The contents of the 32-bit register + * + ****************************************************************************/ + +static inline void imxrt_lpspi_putreg32(FAR struct imxrt_lpspidev_s *priv, + uint8_t offset, uint32_t value) +{ + putreg32(value, priv->spibase + offset); +} + +/**************************************************************************** + * Name: imxrt_lpspi_readword + * + * Description: + * Read one word from SPI + * + * Input Parameters: + * priv - Device-specific state data + * + * Returned Value: + * word as read + * + ****************************************************************************/ + +static inline uint32_t +imxrt_lpspi_readword(FAR struct imxrt_lpspidev_s *priv) +{ + /* Wait until the receive buffer is not empty */ + + while ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_SR_OFFSET) + & LPSPI_SR_RDF) == 0); + + /* Then return the received byte */ + + return imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_RDR_OFFSET); +} + +/**************************************************************************** + * Name: imxrt_lpspi_writeword + * + * Description: + * Write one word to SPI + * + * Input Parameters: + * priv - Device-specific state data + * word - word to send + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void imxrt_lpspi_writeword(FAR struct imxrt_lpspidev_s *priv, + uint16_t word) +{ + /* Wait until the transmit buffer is empty */ + + while ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_SR_OFFSET) + & LPSPI_SR_TDF) == 0); + + /* Then send the word */ + + imxrt_lpspi_putreg32(priv, IMXRT_LPSPI_TDR_OFFSET, word); +} + +/**************************************************************************** + * Name: imxrt_lpspi_readbyte + * + * Description: + * Read one byte from SPI + * + * Input Parameters: + * priv - Device-specific state data + * + * Returned Value: + * Byte as read + * + ****************************************************************************/ + +static inline uint8_t imxrt_lpspi_readbyte(FAR struct imxrt_lpspidev_s *priv) +{ + /* Wait until the receive buffer is not empty */ + + while ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_SR_OFFSET) + & LPSPI_SR_RDF) == 0); + + /* Then return the received byte */ + + return imxrt_lpspi_getreg8(priv, IMXRT_LPSPI_RDR_OFFSET); +} + +/**************************************************************************** + * Name: imxrt_lpspi_writebyte + * + * Description: + * Write one 8-bit frame to the SPI FIFO + * + * Input Parameters: + * priv - Device-specific state data + * byte - Byte to send + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void imxrt_lpspi_writebyte(FAR struct imxrt_lpspidev_s *priv, + uint8_t byte) +{ + /* Wait until the transmit buffer is empty */ + + while ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_SR_OFFSET) + & LPSPI_SR_TDF) == 0); + + /* Then send the byte */ + + imxrt_lpspi_putreg8(priv, IMXRT_LPSPI_TDR_OFFSET, byte); +} + +/**************************************************************************** + * Name: imxrt_lpspi_9to16bitmode + * + * Description: + * Check if the SPI is operating in more then 8 bit mode + * + * Input Parameters: + * priv - Device-specific state data + * + * Returned Value: + * true: >8 bit mode-bit mode, false: <= 8-bit mode + * + ****************************************************************************/ + +static inline bool +imxrt_lpspi_9to16bitmode(FAR struct imxrt_lpspidev_s *priv) +{ + bool ret; + + if (((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_TCR_OFFSET) & + LPSPI_TCR_FRAMESZ_MASK) + 1) < 9) + { + ret = false; + } + else + { + ret = true; + } + + return ret; +} + +/**************************************************************************** + * Name: imxrt_lpspi_modifyreg + * + * Description: + * Clear and set bits in register + * + * Input Parameters: + * priv - Device-specific state data + * offset - Register offset + * clrbits - The bits to clear + * setbits - The bits to set + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void imxrt_lpspi_modifyreg32(FAR struct imxrt_lpspidev_s *priv, + uint8_t offset, uint32_t clrbits, + uint32_t setbits) +{ + modifyreg32(priv->spibase + offset, clrbits, setbits); +} + +/**************************************************************************** + * Name: imxrt_lpspi_master_set_delays + * + * Description: + * SET LPSPI Delay times + * + * Input Parameters: + * priv - Device-specific state data + * scaler - scaler value + * type - delay time type + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void imxrt_lpspi_master_set_delay_scaler( + FAR struct imxrt_lpspidev_s *priv, + uint32_t scaler, + enum imxrt_delay_e type) +{ + switch (type) + { + case LPSPI_PCS_TO_SCK: + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CCR_OFFSET, + LPSPI_CCR_PCSSCK_MASK, 0); + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CCR_OFFSET, 0, + LPSPI_CCR_PCSSCK(scaler)); + break; + + case LPSPI_LAST_SCK_TO_PCS: + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CCR_OFFSET, + LPSPI_CCR_SCKPCS_MASK, 0); + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CCR_OFFSET, 0, + LPSPI_CCR_SCKPCS(scaler)); + break; + + case LPSPI_BETWEEN_TRANSFER: + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CCR_OFFSET, + LPSPI_CCR_DBT_MASK, 0); + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CCR_OFFSET, 0, + LPSPI_CCR_DBT(scaler)); + break; + } +} + +/**************************************************************************** + * Name: imxrt_lpspi_master_set_delays + * + * Description: + * SET LPSPI Delay times + * + * Input Parameters: + * priv - Device-specific state data + * delay_ns - delay time in nano seconds + * type - delay time type + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void imxrt_lpspi_master_set_delays( + FAR struct imxrt_lpspidev_s *priv, + uint32_t delay_ns, + enum imxrt_delay_e type) +{ + uint32_t pll3_div; + uint32_t pll_freq; + uint32_t src_freq; + uint64_t real_delay; + uint64_t best_delay; + uint32_t scaler; + uint32_t best_scaler; + uint32_t diff; + uint32_t min_diff; + uint64_t initial_delay_ns; + uint32_t clock_div_prescaler; + uint32_t additional_scaler; + + if ((getreg32(IMXRT_CCM_ANALOG_PLL_USB1) & + CCM_ANALOG_PLL_USB1_DIV_SELECT_MASK) != 0) + { + pll3_div = 22; + } + else + { + pll3_div = 20; + } + + pll_freq = BOARD_XTAL_FREQUENCY * pll3_div; + + /* Assumption this formula will work only if the LPSPI Clock Source is PLL3 + * PFD0 so check if LPSPI clock source is set to 1 (PLL3 PFD0) in CCM_CBCMR + * register bits 4-5 + */ + + src_freq = pll_freq / + ((getreg32(IMXRT_CCM_ANALOG_PFD_480) + & CCM_ANALOG_PFD_480_PFD0_FRAC_MASK) >> + CCM_ANALOG_PFD_480_PFD0_FRAC_SHIFT); + src_freq *= 18; + src_freq /= ((getreg32(IMXRT_CCM_CBCMR) & CCM_CBCMR_LPSPI_PODF_MASK) >> + CCM_CBCMR_LPSPI_PODF_SHIFT) + 1; + + clock_div_prescaler = src_freq / + (1 << ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_TCR_OFFSET) & + LPSPI_TCR_PRESCALE_MASK) >> LPSPI_TCR_PRESCALE_SHIFT)); + + min_diff = 0xffffffff; + + /* Initialize scaler to max value to generate the max delay */ + + best_scaler = 0xff; + + if (type == LPSPI_BETWEEN_TRANSFER) + { + /* First calculate the initial, default delay, note min delay is 2 + * clock cycles. Due to large size of * calculated values (uint64_t), + * we need to break up the calculation into several steps to ensure + * accurate calculated results + */ + + initial_delay_ns = 1000000000U; + initial_delay_ns *= 2; + initial_delay_ns /= clock_div_prescaler; + + /* Calculate the maximum delay */ + + best_delay = 1000000000U; + + /* based on DBT+2, or 255 + 2 */ + + best_delay *= 257; + best_delay /= clock_div_prescaler; + + additional_scaler = 1U; + } + else + { + /* First calculate the initial, default delay, min delay is 1 clock + * cycle. Due to large size of calculated values (uint64_t), we need to + * break up the calculation into several steps to ensure accurate + * calculated * results. + */ + + initial_delay_ns = 1000000000U; + initial_delay_ns /= clock_div_prescaler; + + /* Calculate the maximum delay */ + + best_delay = 1000000000U; + + /* Based on SCKPCS+1 or PCSSCK+1, or 255 + 1 */ + + best_delay *= 256; + best_delay /= clock_div_prescaler; + + additional_scaler = 0; + } + + /* If the initial, default delay is already greater than the desired delay, + * then * set the delay to their initial value (0) and return the delay. In + * other words, * there is no way to decrease the delay value further. + */ + + if (initial_delay_ns >= delay_ns) + { + imxrt_lpspi_master_set_delay_scaler(priv, 0, type); + } + else + { + /* If min_diff = 0, the exit for loop */ + + for (scaler = 0; (scaler < 256) && min_diff; scaler++) + { + /* Calculate the real delay value as we cycle through the scaler + * values. Due to large size of calculated values (uint64_t), + * we need to break up the calculation into several steps to + * ensure accurate calculated results + */ + + real_delay = 1000000000U; + real_delay *= (scaler + 1 + additional_scaler); + real_delay /= clock_div_prescaler; + + /* calculate the delay difference based on the conditional + * statement that states that the calculated delay must not be + * less then the desired delay + */ + + if (real_delay >= delay_ns) + { + diff = real_delay - delay_ns; + if (min_diff > diff) + { + /* A better match found */ + + min_diff = diff; + best_scaler = scaler; + best_delay = real_delay; + } + } + } + + imxrt_lpspi_master_set_delay_scaler(priv, best_scaler, type); + } +} + +/**************************************************************************** + * Name: imxrt_lpspi_lock + * + * Description: + * On SPI buses where there are multiple devices, it will be necessary to + * lock SPI to have exclusive access to the buses for a sequence of + * transfers. The bus should be locked before the chip is selected. After + * locking the SPI bus, the caller should then also call the setfrequency, + * setbits, and setmode methods to make sure that the SPI is properly + * configured for the device. If the SPI bus is being shared, then it + * may have been left in an incompatible state. + * + * Input Parameters: + * dev - Device-specific state data + * lock - true: Lock spi bus, false: unlock SPI bus + * + * Returned Value: + * None + * + ****************************************************************************/ + +static int imxrt_lpspi_lock(FAR struct spi_dev_s *dev, bool lock) +{ + FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)dev; + int ret; + + if (lock) + { + ret = nxsem_wait_uninterruptible(&priv->exclsem); + } + else + { + ret = nxsem_post(&priv->exclsem); + } + + return ret; +} + +/**************************************************************************** + * Name: imxrt_lpspi_setfrequency + * + * Description: + * Set the SPI frequency. + * + * Input Parameters: + * dev - Device-specific state data + * frequency - The SPI frequency requested + * + * Returned Value: + * Returns the actual frequency selected + * + ****************************************************************************/ + +static uint32_t imxrt_lpspi_setfrequency(FAR struct spi_dev_s *dev, + uint32_t frequency) +{ + FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)dev; + + uint32_t men; + uint32_t pll_freq; + uint32_t pll3_div; + uint32_t src_freq = 0; + uint32_t prescaler; + uint32_t best_prescaler; + uint32_t scaler; + uint32_t best_scaler; + uint32_t real_frequency; + uint32_t best_frequency; + uint32_t diff; + uint32_t min_diff; + + /* Has the LPSPI bus frequency changed? */ + + if (frequency != priv->frequency) + { + /* Disable LPSPI if it is enabled */ + + men = imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CR_OFFSET) & LPSPI_CR_MEN; + if (men) + { + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, + LPSPI_CR_MEN, 0); + } + + if ((getreg32(IMXRT_CCM_ANALOG_PLL_USB1) & + CCM_ANALOG_PLL_USB1_DIV_SELECT_MASK) != 0) + { + pll3_div = 22; + } + else + { + pll3_div = 20; + } + + pll_freq = BOARD_XTAL_FREQUENCY * pll3_div; + + /* Assumption this formula will work only if the LPSPI Clock Source is + * PLL3 PFD0 * so check if LPSPI clock source is set to 1 (PLL3 PFD0) + * in CCM_CBCMR register bits 4-5 + */ + + src_freq = pll_freq / + ((getreg32(IMXRT_CCM_ANALOG_PFD_480) + & CCM_ANALOG_PFD_480_PFD0_FRAC_MASK) >> + CCM_ANALOG_PFD_480_PFD0_FRAC_SHIFT); + src_freq *= 18; + src_freq /= ((getreg32(IMXRT_CCM_CBCMR) & CCM_CBCMR_LPSPI_PODF_MASK) >> + CCM_CBCMR_LPSPI_PODF_SHIFT) + 1; + + min_diff = 0xffffffff; + + best_prescaler = 7; + best_scaler = 255; + + best_frequency = 0; + + for (prescaler = 0; (prescaler < 8) && min_diff; prescaler++) + { + for (scaler = 0; (scaler < 256) && min_diff; scaler++) + { + real_frequency = src_freq / ((1 << prescaler) * (scaler + 2)); + + /* Calculate the frequency difference based on conditional + * statement that states that the calculated frequency must not + * exceed desired frequency. + */ + + if (frequency >= real_frequency) + { + diff = frequency - real_frequency; + if (min_diff > diff) + { + /* A better match found */ + + min_diff = diff; + best_prescaler = prescaler; + best_scaler = scaler; + best_frequency = real_frequency; + } + } + } + } + + /* Write the best values in the CCR register */ + + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CCR_OFFSET, + LPSPI_CCR_SCKDIV_MASK, + LPSPI_CCR_SCKDIV(best_scaler)); + + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_TCR_OFFSET, + LPSPI_TCR_PRESCALE_MASK, + LPSPI_TCR_PRESCALE(best_prescaler)); + + priv->frequency = frequency; + priv->actual = best_frequency; + + imxrt_lpspi_master_set_delays(priv, 1000000000 / best_frequency, + LPSPI_PCS_TO_SCK); + imxrt_lpspi_master_set_delays(priv, 1000000000 / best_frequency, + LPSPI_LAST_SCK_TO_PCS); + imxrt_lpspi_master_set_delays(priv, 1000000000 / best_frequency, + LPSPI_BETWEEN_TRANSFER); + + /* Re-enable LPSPI if it was enabled previously */ + + if (men) + { + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0, + LPSPI_CR_MEN); + } + } + + return priv->actual; +} + +/**************************************************************************** + * Name: imxrt_lpspi_setmode + * + * Description: + * Set the SPI mode. see enum spi_mode_e mode for mode definitions + * + * Input Parameters: + * dev - Device-specific state data + * mode - The SPI mode requested + * + * Returned Value: + * Returns the actual frequency selected + * + ****************************************************************************/ + +static void imxrt_lpspi_setmode(FAR struct spi_dev_s *dev, + enum spi_mode_e mode) +{ + FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)dev; + uint32_t setbits; + uint32_t clrbits; + uint32_t men; + + spiinfo("mode=%d\n", mode); + + /* Has the mode changed? */ + + if (mode != priv->mode) + { + /* Disable LPSPI if it is enabled */ + + men = imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CR_OFFSET) & LPSPI_CR_MEN; + if (men) + { + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, + LPSPI_CR_MEN, 0); + } + + switch (mode) + { + case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */ + setbits = 0; + clrbits = LPSPI_TCR_CPOL | LPSPI_TCR_CPHA; + break; + + case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */ + setbits = LPSPI_TCR_CPHA; + clrbits = LPSPI_TCR_CPOL; + break; + + case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */ + setbits = LPSPI_TCR_CPOL; + clrbits = LPSPI_TCR_CPHA; + break; + + case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */ + setbits = LPSPI_TCR_CPOL | LPSPI_TCR_CPHA; + clrbits = 0; + break; + + default: + return; + } + + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_TCR_OFFSET, + clrbits, setbits); + + while ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_RSR_OFFSET) & + LPSPI_RSR_RXEMPTY) != LPSPI_RSR_RXEMPTY) + { + /* Flush SPI read FIFO */ + + imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_RSR_OFFSET); + } + + /* Save the mode so that subsequent re-configurations will be faster */ + + priv->mode = mode; + + /* Re-enable LPSPI if it was enabled previously */ + + if (men) + { + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0, + LPSPI_CR_MEN); + } + } +} + +/**************************************************************************** + * Name: imxrt_lpspi_setbits + * + * Description: + * Set the number of bits per word. + * + * Input Parameters: + * dev - Device-specific state data + * nbits - The number of bits requested + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void imxrt_lpspi_setbits(FAR struct spi_dev_s *dev, int nbits) +{ + FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)dev; + uint32_t men; + + spiinfo("nbits=%d\n", nbits); + + /* Has the number of bits changed? */ + + if (nbits != priv->nbits) + { + if (nbits < 2 || nbits > 4096) + { + return; + } + + /* Disable LPSPI if it is enabled */ + + men = imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CR_OFFSET) & LPSPI_CR_MEN; + if (men) + { + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, + LPSPI_CR_MEN, 0); + } + + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_TCR_OFFSET, + LPSPI_TCR_FRAMESZ_MASK, + LPSPI_TCR_FRAMESZ(nbits - 1)); + + /* Save the selection so the subsequent re-configurations + * will be faster. + */ + + priv->nbits = nbits; + + /* Re-enable LPSPI if it was enabled previously */ + + if (men) + { + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0, + LPSPI_CR_MEN); + } + } +} + +/**************************************************************************** + * Name: imxrt_lpspi_hwfeatures + * + * Description: + * Set hardware-specific feature flags. + * + * Input Parameters: + * dev - Device-specific state data + * features - H/W feature flags + * + * Returned Value: + * Zero (OK) if the selected H/W features are enabled; A negated errno + * value if any H/W feature is not supportable. + * + ****************************************************************************/ + +#ifdef CONFIG_SPI_HWFEATURES +static int imxrt_lpspi_hwfeatures(FAR struct spi_dev_s *dev, + imxrt_lpspi_hwfeatures_t features) +{ +#ifdef CONFIG_SPI_BITORDER + FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)dev; + uint32_t setbits; + uint32_t clrbits; + + spiinfo("features=%08x\n", features); + + /* Transfer data LSB first? */ + + if ((features & HWFEAT_LSBFIRST) != 0) + { + setbits = LPSPI_TCR_LSBF; + clrbits = 0; + } + else + { + setbits = 0; + clrbits = LPSPI_TCR_LSBF; + } + + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_TCR_OFFSET, clrbits, setbits); + + /* Other H/W features are not supported */ + + return ((features & ~HWFEAT_LSBFIRST) == 0) ? OK : -ENOSYS; +#else + return -ENOSYS; +#endif +} +#endif + +/**************************************************************************** + * Name: imxrt_lpspi_send + * + * Description: + * Exchange one word on SPI + * + * Input Parameters: + * dev - Device-specific state data + * wd - The word to send. the size of the data is determined by the + * number of bits selected for the SPI interface. + * + * Returned Value: + * response + * + ****************************************************************************/ + +static uint32_t imxrt_lpspi_send(FAR struct spi_dev_s *dev, uint32_t wd) +{ + FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)dev; + uint32_t regval; + uint32_t ret; + + DEBUGASSERT(priv && priv->spibase); + + imxrt_lpspi_writeword(priv, wd); + + while ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_SR_OFFSET) & + LPSPI_SR_RDF) != LPSPI_SR_RDF); + + ret = imxrt_lpspi_readword(priv); + + /* Check and clear any error flags (Reading from the SR clears the error + * flags). + */ + + regval = imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_SR_OFFSET); + + spiinfo( + "Sent: %04" PRIx32 " Return: %04" PRIx32 " Status: %02" PRIx32 "\n", + wd, ret, regval); + + UNUSED(regval); + return ret; +} + +/**************************************************************************** + * Name: imxrt_lpspi_exchange (no DMA). aka imxrt_lpspi_exchange_nodma + * + * Description: + * Exchange a block of data on SPI without using DMA + * + * Input Parameters: + * dev - Device-specific state data + * txbuffer - A pointer to the buffer of data to be sent + * rxbuffer - A pointer to a buffer in which to receive data + * nwords - the length of data to be exchanged in units of words. + * The wordsize is determined by the number of bits-per-word + * selected for the SPI interface. If nbits <= 8, the data is + * packed into uint8_t's; if nbits >8, the data is packed + * into uint16_t's + * + * Returned Value: + * None + * + ****************************************************************************/ + +#if !defined(CONFIG_IMXRT_LPSPI_DMA) || defined(CONFIG_IMXRT_DMACAPABLE) +#if !defined(CONFIG_IMXRT_LPSPI_DMA) +static void imxrt_lpspi_exchange(FAR struct spi_dev_s *dev, + FAR const void *txbuffer, + FAR void *rxbuffer, + size_t nwords) +#else +static void imxrt_lpspi_exchange_nodma(FAR struct spi_dev_s *dev, + FAR const void *txbuffer, + FAR void *rxbuffer, size_t nwords) +#endif +{ + FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)dev; + DEBUGASSERT(priv && priv->spibase); + + spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords); + + /* 8- or 16-bit mode? */ + + if (imxrt_lpspi_9to16bitmode(priv)) + { + /* 16-bit mode */ + + const uint16_t *src = (const uint16_t *)txbuffer; + uint16_t *dest = (uint16_t *) rxbuffer; + uint16_t word; + + while (nwords-- > 0) + { + /* Get the next word to write. Is there a source buffer? */ + + if (src) + { + word = *src++; + } + else + { + word = 0xffff; + } + + /* Exchange one word */ + + word = (uint16_t) imxrt_lpspi_send(dev, (uint32_t) word); + + /* Is there a buffer to receive the return value? */ + + if (dest) + { + *dest++ = word; + } + } + } + else + { + /* 8-bit mode */ + + const uint8_t *src = (const uint8_t *)txbuffer; + uint8_t *dest = (uint8_t *) rxbuffer; + uint8_t word; + + while (nwords-- > 0) + { + /* Get the next word to write. Is there a source buffer? */ + + if (src) + { + word = *src++; + } + else + { + word = 0xff; + } + + /* Exchange one word */ + + word = (uint8_t) imxrt_lpspi_send(dev, (uint32_t) word); + + /* Is there a buffer to receive the return value? */ + + if (dest) + { + *dest++ = word; + } + } + } +} +#endif /* !CONFIG_IMXRT_LPSPI_DMA || CONFIG_IMXRT_DMACAPABLE */ + +/**************************************************************************** + * Name: imxrt_lpspi_sndblock + * + * Description: + * Send a block of data on SPI + * + * Input Parameters: + * dev - Device-specific state data + * txbuffer - A pointer to the buffer of data to be sent + * nwords - the length of data to send from the buffer in number of + * words. The wordsize is determined by the number of + * bits-per-word selected for the SPI interface. If nbits <= 8, + * the data is packed into uint8_t's; if nbits >8, the data is + * packed into uint16_t's + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifndef CONFIG_SPI_EXCHANGE +static void imxrt_lpspi_sndblock(FAR struct spi_dev_s *dev, + FAR const void *txbuffer, size_t nwords) +{ + spiinfo("txbuffer=%p nwords=%d\n", txbuffer, nwords); + return imxrt_lpspi_exchange(dev, txbuffer, NULL, nwords); +} +#endif + +/**************************************************************************** + * Name: imxrt_lpspi_recvblock + * + * Description: + * Receive a block of data from SPI + * + * Input Parameters: + * dev - Device-specific state data + * rxbuffer - A pointer to the buffer in which to receive data + * nwords - the length of data that can be received in the buffer in + * number of words. The wordsize is determined by the number of + * bits-per-word selected for the SPI interface. If nbits <= 8, + * the data is packed into uint8_t's; if nbits >8, the data is + * packed into uint16_t's + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifndef CONFIG_SPI_EXCHANGE +static void imxrt_lpspi_recvblock(FAR struct spi_dev_s *dev, + FAR void *rxbuffer, size_t nwords) +{ + spiinfo("rxbuffer=%p nwords=%d\n", rxbuffer, nwords); + return imxrt_lpspi_exchange(dev, NULL, rxbuffer, nwords); +} +#endif + +/**************************************************************************** + * Name: imxrt_lpspi_clock_enable + * + * Description: + * Ungate LPSPI clock + * + ****************************************************************************/ + +void imxrt_lpspi_clock_enable(uint32_t base) +{ + if (base == IMXRT_LPSPI1_BASE) + { + imxrt_clockall_lpspi1(); + } + else if (base == IMXRT_LPSPI2_BASE) + { + imxrt_clockall_lpspi2(); + } + else if (base == IMXRT_LPSPI3_BASE) + { + imxrt_clockall_lpspi3(); + } + else if (base == IMXRT_LPSPI4_BASE) + { + imxrt_clockall_lpspi4(); + } +} + +/**************************************************************************** + * Name: imxrt_lpspi_clock_disable + * + * Description: + * Gate LPSPI clock + * + ****************************************************************************/ + +void imxrt_lpspi_clock_disable(uint32_t base) +{ + if (base == IMXRT_LPSPI1_BASE) + { + imxrt_clockoff_lpspi1(); + } + else if (base == IMXRT_LPSPI2_BASE) + { + imxrt_clockoff_lpspi2(); + } + else if (base == IMXRT_LPSPI3_BASE) + { + imxrt_clockoff_lpspi3(); + } + else if (base == IMXRT_LPSPI4_BASE) + { + imxrt_clockoff_lpspi4(); + } +} + +/**************************************************************************** + * Name: imxrt_lpspi_bus_initialize + * + * Description: + * Initialize the selected SPI bus in its default state + * (Master, 8-bit, mode 0, etc.) + * + * Input Parameters: + * priv - private SPI device structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void imxrt_lpspi_bus_initialize(struct imxrt_lpspidev_s *priv) +{ + uint32_t reg = 0; + + /* Enable power and reset the peripheral */ + + imxrt_lpspi_clock_enable(priv->spibase); + + /* Reset to known status */ + + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0, LPSPI_CR_RST); + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0, + LPSPI_CR_RTF | LPSPI_CR_RRF); + imxrt_lpspi_putreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0x00); + + /* Set LPSPI to master */ + + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CFGR1_OFFSET, 0, + LPSPI_CFGR1_MASTER); + + /* Set specific PCS to active high or low + * TODO: Not needed for now + */ + + /* Set Configuration Register 1 related setting. */ + + reg = imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CFGR1_OFFSET); + reg &= ~(LPSPI_CFGR1_OUTCFG | LPSPI_CFGR1_PINCFG_MASK + | LPSPI_CFGR1_NOSTALL); + reg |= LPSPI_CFGR1_OUTCFG_RETAIN | LPSPI_CFGR1_PINCFG_SIN_SOUT; + imxrt_lpspi_putreg32(priv, IMXRT_LPSPI_CFGR1_OFFSET, reg); + + /* Set frequency and delay times */ + + imxrt_lpspi_setfrequency((FAR struct spi_dev_s *)priv, 400000); + + /* Set default watermarks */ + + imxrt_lpspi_putreg32(priv, IMXRT_LPSPI_FCR_OFFSET, + LPSPI_FCR_TXWATER(0) | LPSPI_FCR_RXWATER(0)); + + /* Set Transmit Command Register */ + + imxrt_lpspi_setbits((FAR struct spi_dev_s *)priv, 8); + + imxrt_lpspi_setmode((FAR struct spi_dev_s *)priv, SPIDEV_MODE0); + + /* Initialize the SPI semaphore that enforces mutually exclusive access */ + + nxsem_init(&priv->exclsem, 0, 1); + + /* Enable LPSPI */ + + imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0, LPSPI_CR_MEN); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imxrt_lpspibus_initialize + * + * Description: + * Initialize the selected SPI bus + * + * Input Parameters: + * Port number (for hardware that has multiple SPI interfaces) + * + * Returned Value: + * Valid SPI device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +FAR struct spi_dev_s *imxrt_lpspibus_initialize(int bus) +{ + FAR struct imxrt_lpspidev_s *priv = NULL; + + irqstate_t flags = enter_critical_section(); + +#ifdef CONFIG_IMXRT_LPSPI1 + if (bus == 1) + { + /* Select SPI1 */ + + priv = &g_lpspi1dev; + + /* Only configure if the bus is not already configured */ + + if ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CR_OFFSET) + & LPSPI_CR_MEN) == 0) + { + /* Configure SPI1 pins: SCK, MISO, and MOSI */ + + imxrt_config_gpio(GPIO_LPSPI1_SCK); + imxrt_config_gpio(GPIO_LPSPI1_MISO); + imxrt_config_gpio(GPIO_LPSPI1_MOSI); +#ifdef GPIO_LPSPI1_CS + imxrt_config_gpio(GPIO_LPSPI1_CS); +#endif +#if defined(GPIO_LPSPI1_DC) && defined(CONFIG_SPI_CMDDATA) + imxrt_config_gpio(GPIO_LPSPI1_DC); +#endif + + /* Set up default configuration: Master, 8-bit, etc. */ + + imxrt_lpspi_bus_initialize(priv); + } + } + else +#endif +#ifdef CONFIG_IMXRT_LPSPI2 + if (bus == 2) + { + /* Select SPI2 */ + + priv = &g_lpspi2dev; + + /* Only configure if the bus is not already configured */ + + if ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CR_OFFSET) + & LPSPI_CR_MEN) == 0) + { + /* Configure SPI2 pins: SCK, MISO, and MOSI */ + + imxrt_config_gpio(GPIO_LPSPI2_SCK); + imxrt_config_gpio(GPIO_LPSPI2_MISO); + imxrt_config_gpio(GPIO_LPSPI2_MOSI); +#ifdef GPIO_LPSPI2_CS + imxrt_config_gpio(GPIO_LPSPI2_CS); +#endif +#if defined(GPIO_LPSPI2_DC) && defined(CONFIG_SPI_CMDDATA) + imxrt_config_gpio(GPIO_LPSPI2_DC); +#endif + + /* Set up default configuration: Master, 8-bit, etc. */ + + imxrt_lpspi_bus_initialize(priv); + } + } + else +#endif +#ifdef CONFIG_IMXRT_LPSPI3 + if (bus == 3) + { + /* Select SPI3 */ + + priv = &g_lpspi3dev; + + /* Only configure if the bus is not already configured */ + + if ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CR_OFFSET) + & LPSPI_CR_MEN) == 0) + { + /* Configure SPI3 pins: SCK, MISO, and MOSI */ + + imxrt_config_gpio(GPIO_LPSPI3_SCK); + imxrt_config_gpio(GPIO_LPSPI3_MISO); + imxrt_config_gpio(GPIO_LPSPI3_MOSI); +#ifdef GPIO_LPSPI3_CS + imxrt_config_gpio(GPIO_LPSPI3_CS); +#endif +#if defined(GPIO_LPSPI3_DC) && defined(CONFIG_SPI_CMDDATA) + imxrt_config_gpio(GPIO_LPSPI3_DC); +#endif + + /* Set up default configuration: Master, 8-bit, etc. */ + + imxrt_lpspi_bus_initialize(priv); + } + } + else +#endif +#ifdef CONFIG_IMXRT_LPSPI4 + if (bus == 4) + { + /* Select SPI4 */ + + priv = &g_lpspi4dev; + + /* Only configure if the bus is not already configured */ + + if ((imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CR_OFFSET) + & LPSPI_CR_MEN) == 0) + { + /* Configure SPI4 pins: SCK, MISO, and MOSI */ + + imxrt_config_gpio(GPIO_LPSPI4_SCK); + imxrt_config_gpio(GPIO_LPSPI4_MISO); + imxrt_config_gpio(GPIO_LPSPI4_MOSI); +#ifdef GPIO_LPSPI4_CS + imxrt_config_gpio(GPIO_LPSPI4_CS); +#endif +#if defined(GPIO_LPSPI4_DC) && defined(CONFIG_SPI_CMDDATA) + imxrt_config_gpio(GPIO_LPSPI4_DC); +#endif + + /* Set up default configuration: Master, 8-bit, etc. */ + + imxrt_lpspi_bus_initialize(priv); + } + } + else +#endif + { + spierr("ERROR: Unsupported SPI bus: %d\n", bus); + return NULL; + } + + leave_critical_section(flags); + + return (FAR struct spi_dev_s *)priv; +} + +#endif /* CONFIG_IMXRT_LPSPI1 */ diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_usbdev.c b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_usbdev.c new file mode 100644 index 000000000..69f6f000b --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_usbdev.c @@ -0,0 +1,3061 @@ +/**************************************************************************** + * arch/arm/src/imxrt/imxrt_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 "chip.h" +#include "arm_arch.h" +#include "arm_internal.h" + +#include "hardware/imxrt_usbotg.h" +#include "hardware/imxrt_usbphy.h" +#include "hardware/rt106x/imxrt106x_ccm.h" +#include "imxrt_periphclks.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_IMXRT_USBDEV_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 IMXRT_TRACEERR_ALLOCFAIL 0x0001 +#define IMXRT_TRACEERR_BADCLEARFEATURE 0x0002 +#define IMXRT_TRACEERR_BADDEVGETSTATUS 0x0003 +#define IMXRT_TRACEERR_BADEPNO 0x0004 +#define IMXRT_TRACEERR_BADEPGETSTATUS 0x0005 +#define IMXRT_TRACEERR_BADEPTYPE 0x0006 +#define IMXRT_TRACEERR_BADGETCONFIG 0x0007 +#define IMXRT_TRACEERR_BADGETSETDESC 0x0008 +#define IMXRT_TRACEERR_BADGETSTATUS 0x0009 +#define IMXRT_TRACEERR_BADSETADDRESS 0x000a +#define IMXRT_TRACEERR_BADSETCONFIG 0x000b +#define IMXRT_TRACEERR_BADSETFEATURE 0x000c +#define IMXRT_TRACEERR_BINDFAILED 0x000d +#define IMXRT_TRACEERR_DISPATCHSTALL 0x000e +#define IMXRT_TRACEERR_DRIVER 0x000f +#define IMXRT_TRACEERR_DRIVERREGISTERED 0x0010 +#define IMXRT_TRACEERR_EP0SETUPSTALLED 0x0011 +#define IMXRT_TRACEERR_EPINNULLPACKET 0x0012 +#define IMXRT_TRACEERR_EPOUTNULLPACKET 0x0013 +#define IMXRT_TRACEERR_INVALIDCTRLREQ 0x0014 +#define IMXRT_TRACEERR_INVALIDPARMS 0x0015 +#define IMXRT_TRACEERR_IRQREGISTRATION 0x0016 +#define IMXRT_TRACEERR_NOEP 0x0017 +#define IMXRT_TRACEERR_NOTCONFIGURED 0x0018 +#define IMXRT_TRACEERR_REQABORTED 0x0019 + +/* Trace interrupt codes */ + +#define IMXRT_TRACEINTID_USB 0x0001 +#define IMXRT_TRACEINTID_CLEARFEATURE 0x0002 +#define IMXRT_TRACEINTID_DEVGETSTATUS 0x0003 +#define IMXRT_TRACEINTID_DEVRESET 0x0004 +#define IMXRT_TRACEINTID_DISPATCH 0x0005 +#define IMXRT_TRACEINTID_EP0COMPLETE 0x0006 +#define IMXRT_TRACEINTID_EP0NAK 0x0007 +#define IMXRT_TRACEINTID_EP0SETUP 0x0008 +#define IMXRT_TRACEINTID_EPGETSTATUS 0x0009 +#define IMXRT_TRACEINTID_EPIN 0x000a +#define IMXRT_TRACEINTID_EPINQEMPTY 0x000b +#define IMXRT_TRACEINTID_EP0INSETADDRESS 0x000c +#define IMXRT_TRACEINTID_EPOUT 0x000d +#define IMXRT_TRACEINTID_EPOUTQEMPTY 0x000e +#define IMXRT_TRACEINTID_EP0SETUPSETADDRESS 0x000f +#define IMXRT_TRACEINTID_FRAME 0x0010 +#define IMXRT_TRACEINTID_GETCONFIG 0x0011 +#define IMXRT_TRACEINTID_GETSETDESC 0x0012 +#define IMXRT_TRACEINTID_GETSETIF 0x0013 +#define IMXRT_TRACEINTID_GETSTATUS 0x0014 +#define IMXRT_TRACEINTID_IFGETSTATUS 0x0015 +#define IMXRT_TRACEINTID_SETCONFIG 0x0016 +#define IMXRT_TRACEINTID_SETFEATURE 0x0017 +#define IMXRT_TRACEINTID_SUSPENDED 0x0018 +#define IMXRT_TRACEINTID_RESUMED 0x0019 +#define IMXRT_TRACEINTID_SYNCHFRAME 0x001a + +#ifdef CONFIG_USBDEV_TRACE_STRINGS +const struct trace_msg_t g_usb_trace_strings_deverror[] = +{ + TRACE_STR(IMXRT_TRACEERR_ALLOCFAIL), + TRACE_STR(IMXRT_TRACEERR_BADCLEARFEATURE), + TRACE_STR(IMXRT_TRACEERR_BADDEVGETSTATUS), + TRACE_STR(IMXRT_TRACEERR_BADEPNO), + TRACE_STR(IMXRT_TRACEERR_BADEPGETSTATUS), + TRACE_STR(IMXRT_TRACEERR_BADEPTYPE), + TRACE_STR(IMXRT_TRACEERR_BADGETCONFIG), + TRACE_STR(IMXRT_TRACEERR_BADGETSETDESC), + TRACE_STR(IMXRT_TRACEERR_BADGETSTATUS), + TRACE_STR(IMXRT_TRACEERR_BADSETADDRESS), + TRACE_STR(IMXRT_TRACEERR_BADSETCONFIG), + TRACE_STR(IMXRT_TRACEERR_BADSETFEATURE), + TRACE_STR(IMXRT_TRACEERR_BINDFAILED), + TRACE_STR(IMXRT_TRACEERR_DISPATCHSTALL), + TRACE_STR(IMXRT_TRACEERR_DRIVER), + TRACE_STR(IMXRT_TRACEERR_DRIVERREGISTERED), + TRACE_STR(IMXRT_TRACEERR_EP0SETUPSTALLED), + TRACE_STR(IMXRT_TRACEERR_EPINNULLPACKET), + TRACE_STR(IMXRT_TRACEERR_EPOUTNULLPACKET), + TRACE_STR(IMXRT_TRACEERR_INVALIDCTRLREQ), + TRACE_STR(IMXRT_TRACEERR_INVALIDPARMS), + TRACE_STR(IMXRT_TRACEERR_IRQREGISTRATION), + TRACE_STR(IMXRT_TRACEERR_NOEP), + TRACE_STR(IMXRT_TRACEERR_NOTCONFIGURED), + TRACE_STR(IMXRT_TRACEERR_REQABORTED), + TRACE_STR_END +}; + +const struct trace_msg_t g_usb_trace_strings_intdecode[] = +{ + TRACE_STR(IMXRT_TRACEINTID_USB), + TRACE_STR(IMXRT_TRACEINTID_CLEARFEATURE), + TRACE_STR(IMXRT_TRACEINTID_DEVGETSTATUS), + TRACE_STR(IMXRT_TRACEINTID_DEVRESET), + TRACE_STR(IMXRT_TRACEINTID_DISPATCH), + TRACE_STR(IMXRT_TRACEINTID_EP0COMPLETE), + TRACE_STR(IMXRT_TRACEINTID_EP0NAK), + TRACE_STR(IMXRT_TRACEINTID_EP0SETUP), + TRACE_STR(IMXRT_TRACEINTID_EPGETSTATUS), + TRACE_STR(IMXRT_TRACEINTID_EPIN), + TRACE_STR(IMXRT_TRACEINTID_EPINQEMPTY), + TRACE_STR(IMXRT_TRACEINTID_EP0INSETADDRESS), + TRACE_STR(IMXRT_TRACEINTID_EPOUT), + TRACE_STR(IMXRT_TRACEINTID_EPOUTQEMPTY), + TRACE_STR(IMXRT_TRACEINTID_EP0SETUPSETADDRESS), + TRACE_STR(IMXRT_TRACEINTID_FRAME), + TRACE_STR(IMXRT_TRACEINTID_GETCONFIG), + TRACE_STR(IMXRT_TRACEINTID_GETSETDESC), + TRACE_STR(IMXRT_TRACEINTID_GETSETIF), + TRACE_STR(IMXRT_TRACEINTID_GETSTATUS), + TRACE_STR(IMXRT_TRACEINTID_IFGETSTATUS), + TRACE_STR(IMXRT_TRACEINTID_SETCONFIG), + TRACE_STR(IMXRT_TRACEINTID_SETFEATURE), + TRACE_STR(IMXRT_TRACEINTID_SUSPENDED), + TRACE_STR(IMXRT_TRACEINTID_RESUMED), + TRACE_STR(IMXRT_TRACEINTID_SYNCHFRAME), + TRACE_STR_END +}; +#endif + +/* Hardware interface *******************************************************/ + +/* This represents a Endpoint Transfer Descriptor - note these must be 32 + * byte aligned. + */ + +struct imxrt_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 - not these must be aligned to a 2048 byte + * boundary + */ + +struct imxrt_dqh_s +{ + uint32_t capability; /* Endpoint capability */ + uint32_t currdesc; /* Current dTD pointer */ + struct imxrt_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 IMXRT_NLOGENDPOINTS (8) /* ep0-7 */ +#define IMXRT_NPHYSENDPOINTS (16) /* x2 for IN and OUT */ + +/* Odd physical endpoint numbers are IN; even are OUT */ + +#define IMXRT_EPPHYIN(epphy) (((epphy) & 1) != 0) +#define IMXRT_EPPHYOUT(epphy) (((epphy) & 1) == 0) + +#define IMXRT_EPPHYIN2LOG(epphy) (((uint8_t)(epphy) >> 1) |USB_DIR_IN) +#define IMXRT_EPPHYOUT2LOG(epphy) (((uint8_t)(epphy) >> 1) | USB_DIR_OUT) + +/* Endpoint 0 is special... */ + +#define IMXRT_EP0_OUT (0) +#define IMXRT_EP0_IN (1) + +/* Each endpoint has somewhat different characteristics */ + +#define IMXRT_EPALLSET (0xffff) /* All endpoints */ +#define IMXRT_EPOUTSET (0x5555) /* Even phy endpoint numbers are OUT EPs */ +#define IMXRT_EPINSET (0xaaaa) /* Odd endpoint numbers are IN EPs */ +#define IMXRT_EPCTRLSET (0x0003) /* EP0 IN/OUT are control endpoints */ +#define IMXRT_EPINTRSET (0xfffc) /* Interrupt endpoints */ +#define IMXRT_EPBULKSET (0xfffc) /* Bulk endpoints */ +#define IMXRT_EPISOCSET (0xfffc) /* Isochronous endpoints */ + +/* Maximum packet sizes for endpoints */ + +#define IMXRT_EP0MAXPACKET (64) /* EP0 max packet size (1-64) */ +#define IMXRT_BULKMAXPACKET (512) /* Bulk endpoint max packet (8/16/32/64/512) */ +#define IMXRT_INTRMAXPACKET (1024) /* Interrupt endpoint max packet (1 to 1024) */ +#define IMXRT_ISOCMAXPACKET (512) /* Acutally 1..1023 */ + +/* Endpoint bit position in SETUPSTAT, PRIME, FLUSH, STAT, COMPLETE + * registers + */ + +#define IMXRT_ENDPTSHIFT(epphy) (IMXRT_EPPHYIN(epphy) ? (16 + ((epphy) >> 1)) : ((epphy) >> 1)) +#define IMXRT_ENDPTMASK(epphy) (1 << IMXRT_ENDPTSHIFT(epphy)) +#define IMXRT_ENDPTMASK_ALL 0x00ff00ff + +/* Request queue operations *************************************************/ + +#define imxrt_rqempty(ep) ((ep)->head == NULL) +#define imxrt_rqpeek(ep) ((ep)->head) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* A container for a request so that the request may be retained in a list */ + +struct imxrt_req_s +{ + struct usbdev_req_s req; /* Standard USB request */ + struct imxrt_req_s *flink; /* Supports a singly linked list */ +}; + +/* This is the internal representation of an endpoint */ + +struct imxrt_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 imxrt_ep_s. + */ + + struct usbdev_ep_s ep; /* Standard endpoint structure */ + + /* IMXRTXX-specific fields */ + + struct imxrt_usbdev_s *dev; /* Reference to private driver data */ + struct imxrt_req_s *head; /* Request list for this endpoint */ + struct imxrt_req_s *tail; + uint8_t epphy; /* Physical EP address */ + uint8_t stalled:1; /* 1: Endpoint is stalled */ +}; + +/* This structure retains the state of the USB device controller */ + +struct imxrt_usbdev_s +{ + /* Common device fields. This must be the first thing defined in the + * structure so that it is possible to simply cast from struct usbdev_s + * to struct imxrt_usbdev_s. + */ + + struct usbdev_s usbdev; + + /* The bound device class driver */ + + struct usbdevclass_driver_s *driver; + + /* IMXRTXX-specific fields */ + + uint8_t ep0state; /* State of certain EP0 operations */ + uint8_t ep0buf[64]; /* buffer for EP0 short transfers */ + 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_IMXRT_USBDEV_FRAME_INTERRUPT + uint32_t sof; /* Last start-of-frame */ +#endif + + uint16_t ep0buf_len; + struct usb_ctrlreq_s ep0ctrl; + + /* The endpoint list */ + + struct imxrt_ep_s eplist[IMXRT_NPHYSENDPOINTS]; +}; + +#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_IMXRT_USBDEV_REGDEBUG +static uint32_t imxrt_getreg(uint32_t addr); +static void imxrt_putreg(uint32_t val, uint32_t addr); +#else +# define imxrt_getreg(addr) getreg32(addr) +# define imxrt_putreg(val,addr) putreg32(val,addr) +#endif + +static inline void imxrt_clrbits(uint32_t mask, uint32_t addr); +static inline void imxrt_setbits(uint32_t mask, uint32_t addr); +static inline void imxrt_chgbits(uint32_t mask, uint32_t val, uint32_t addr); + +/* Request queue operations *************************************************/ + +static FAR struct imxrt_req_s *imxrt_rqdequeue( + FAR struct imxrt_ep_s *privep); +static bool imxrt_rqenqueue(FAR struct imxrt_ep_s *privep, + FAR struct imxrt_req_s *req); + +/* Low level data transfers and request operations **************************/ + +static inline void imxrt_writedtd(struct imxrt_dtd_s *dtd, + const uint8_t *data, + uint32_t nbytes); +static inline void imxrt_queuedtd(uint8_t epphy, struct imxrt_dtd_s *dtd); +static inline void imxrt_ep0xfer(uint8_t epphy, uint8_t *data, + uint32_t nbytes); +static void imxrt_readsetup(uint8_t epphy, + struct usb_ctrlreq_s *ctrl); + +static inline void imxrt_set_address(struct imxrt_usbdev_s *priv, + uint16_t address); + +static void imxrt_flushep(struct imxrt_ep_s *privep); + +static int imxrt_progressep(struct imxrt_ep_s *privep); +static void imxrt_reqcomplete(struct imxrt_ep_s *privep, + struct imxrt_req_s *privreq, int16_t result); + +static void imxrt_cancelrequests(struct imxrt_ep_s *privep, + int16_t status); + +/* Interrupt handling *******************************************************/ + +static struct imxrt_ep_s *imxrt_epfindbyaddr(struct imxrt_usbdev_s *priv, + uint16_t eplog); +static void imxrt_dispatchrequest(struct imxrt_usbdev_s *priv, + const struct usb_ctrlreq_s *ctrl); +static void imxrt_ep0configure(struct imxrt_usbdev_s *priv); +static void imxrt_usbreset(struct imxrt_usbdev_s *priv); + +static inline void imxrt_ep0state(struct imxrt_usbdev_s *priv, + uint16_t state); +static void imxrt_ep0setup(struct imxrt_usbdev_s *priv); + +static void imxrt_ep0complete(struct imxrt_usbdev_s *priv, + uint8_t epphy); +static void imxrt_ep0nak(struct imxrt_usbdev_s *priv, uint8_t epphy); +static bool imxrt_epcomplete(struct imxrt_usbdev_s *priv, + uint8_t epphy); + +static int imxrt_usbinterrupt(int irq, FAR void *context, + FAR void *arg); + +/* Endpoint operations ******************************************************/ + +/* USB device controller operations *****************************************/ + +static int imxrt_epconfigure(FAR struct usbdev_ep_s *ep, + const struct usb_epdesc_s *desc, bool last); +static int imxrt_epdisable(FAR struct usbdev_ep_s *ep); +static FAR struct usbdev_req_s *imxrt_epallocreq(FAR struct usbdev_ep_s *ep); +static void imxrt_epfreereq(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *); +#ifdef CONFIG_USBDEV_DMA +static void *imxrt_epallocbuffer(FAR struct usbdev_ep_s *ep, + uint16_t bytes); +static void imxrt_epfreebuffer(FAR struct usbdev_ep_s *ep, + FAR void *buf); +#endif +static int imxrt_epsubmit(FAR struct usbdev_ep_s *ep, + struct usbdev_req_s *req); +static int imxrt_epcancel(FAR struct usbdev_ep_s *ep, + struct usbdev_req_s *req); +static int imxrt_epstall(FAR struct usbdev_ep_s *ep, bool resume); + +static FAR struct usbdev_ep_s *imxrt_allocep(FAR struct usbdev_s *dev, + uint8_t epno, bool in, uint8_t eptype); +static void imxrt_freeep(FAR struct usbdev_s *dev, + FAR struct usbdev_ep_s *ep); +static int imxrt_getframe(struct usbdev_s *dev); +static int imxrt_wakeup(struct usbdev_s *dev); +static int imxrt_selfpowered(struct usbdev_s *dev, bool selfpowered); +static int imxrt_pullup(struct usbdev_s *dev, bool enable); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Since there is only a single USB interface, all status information can be + * be simply retained in a single global instance. + */ + +static struct imxrt_usbdev_s g_usbdev; + +static struct imxrt_dqh_s g_qh[IMXRT_NPHYSENDPOINTS] + aligned_data(2048); + +static struct imxrt_dtd_s g_td[IMXRT_NPHYSENDPOINTS] + aligned_data(32); + +static const struct usbdev_epops_s g_epops = +{ + .configure = imxrt_epconfigure, + .disable = imxrt_epdisable, + .allocreq = imxrt_epallocreq, + .freereq = imxrt_epfreereq, +#ifdef CONFIG_USBDEV_DMA + .allocbuffer = imxrt_epallocbuffer, + .freebuffer = imxrt_epfreebuffer, +#endif + .submit = imxrt_epsubmit, + .cancel = imxrt_epcancel, + .stall = imxrt_epstall, +}; + +static const struct usbdev_ops_s g_devops = +{ + .allocep = imxrt_allocep, + .freeep = imxrt_freeep, + .getframe = imxrt_getframe, + .wakeup = imxrt_wakeup, + .selfpowered = imxrt_selfpowered, + .pullup = imxrt_pullup, +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imxrt_getreg + * + * Description: + * Get the contents of an IMXRT3x register + * + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_USBDEV_REGDEBUG +static uint32_t imxrt_getreg(uint32_t addr) +{ + static uint32_t prevaddr = 0; + static uint32_t preval = 0; + static uint32_t count = 0; + + /* Read the value from the register */ + + uint32_t val = getreg32(addr); + + /* Is this the same value that we read from the same register last time? + * Are we polling the register? If so, suppress some of the output. + */ + + if (addr == prevaddr && val == preval) + { + if (count == 0xffffffff || ++count > 3) + { + if (count == 4) + { + uinfo("...\n"); + } + + return val; + } + } + + /* No this is a new address or value */ + + else + { + /* Did we print "..." for the previous value? */ + + if (count > 3) + { + /* Yes.. then show how many times the value repeated */ + + uinfo("[repeats %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", addr, val); + return val; +} +#endif + +/**************************************************************************** + * Name: imxrt_putreg + * + * Description: + * Set the contents of an IMXRT3x register to a value + * + ****************************************************************************/ + +#ifdef CONFIG_IMXRT_USBDEV_REGDEBUG +static void imxrt_putreg(uint32_t val, uint32_t addr) +{ + /* Show the register value being written */ + + uinfo("%08x<-%08x\n", addr, val); + + /* Write the value */ + + putreg32(val, addr); +} +#endif + +/**************************************************************************** + * Name: imxrt_clrbits + * + * Description: + * Clear bits in a register + * + ****************************************************************************/ + +static inline void imxrt_clrbits(uint32_t mask, uint32_t addr) +{ + uint32_t reg = imxrt_getreg(addr); + reg &= ~mask; + imxrt_putreg(reg, addr); +} + +/**************************************************************************** + * Name: imxrt_setbits + * + * Description: + * Set bits in a register + * + ****************************************************************************/ + +static inline void imxrt_setbits(uint32_t mask, uint32_t addr) +{ + uint32_t reg = imxrt_getreg(addr); + reg |= mask; + imxrt_putreg(reg, addr); +} + +/**************************************************************************** + * Name: imxrt_chgbits + * + * Description: + * Change bits in a register + * + ****************************************************************************/ + +static inline void imxrt_chgbits(uint32_t mask, uint32_t val, uint32_t addr) +{ + uint32_t reg = imxrt_getreg(addr); + reg &= ~mask; + reg |= val; + imxrt_putreg(reg, addr); +} + +/**************************************************************************** + * Name: imxrt_rqdequeue + * + * Description: + * Remove a request from an endpoint request queue + * + ****************************************************************************/ + +static FAR struct imxrt_req_s *imxrt_rqdequeue(FAR struct imxrt_ep_s *privep) +{ + FAR struct imxrt_req_s *ret = privep->head; + + if (ret) + { + privep->head = ret->flink; + if (!privep->head) + { + privep->tail = NULL; + } + + ret->flink = NULL; + } + + return ret; +} + +/**************************************************************************** + * Name: imxrt_rqenqueue + * + * Description: + * Add a request from an endpoint request queue + * + ****************************************************************************/ + +static bool imxrt_rqenqueue(FAR struct imxrt_ep_s *privep, + FAR struct imxrt_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: imxrt_writedtd + * + * Description: + * Initialise a DTD to transfer the data + * + ****************************************************************************/ + +static inline void imxrt_writedtd(struct imxrt_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) data); + dtd->buffer1 = (((uint32_t) data) + 0x1000) & 0xfffff000; + dtd->buffer2 = (((uint32_t) data) + 0x2000) & 0xfffff000; + dtd->buffer3 = (((uint32_t) data) + 0x3000) & 0xfffff000; + dtd->buffer4 = (((uint32_t) data) + 0x4000) & 0xfffff000; + dtd->xfer_len = nbytes; + + up_flush_dcache((uintptr_t)dtd, + (uintptr_t)dtd + sizeof(struct imxrt_dtd_s)); + up_flush_dcache((uintptr_t)data, + (uintptr_t)data + nbytes); +} + +/**************************************************************************** + * Name: imxrt_queuedtd + * + * Description: + * Add the DTD to the device list + * + * Assumptions: + * DTD is already flushed to RAM. + * + ****************************************************************************/ + +static void imxrt_queuedtd(uint8_t epphy, struct imxrt_dtd_s *dtd) +{ + struct imxrt_dqh_s *dqh = &g_qh[epphy]; + + /* Queue the DTD onto the Endpoint + * NOTE - this only works when no DTD is currently queued + */ + + dqh->overlay.nextdesc = (uint32_t) dtd; + dqh->overlay.config &= ~(DTD_CONFIG_ACTIVE | DTD_CONFIG_HALTED); + + up_flush_dcache((uintptr_t)dqh, + (uintptr_t)dqh + sizeof(struct imxrt_dqh_s)); + + uint32_t bit = IMXRT_ENDPTMASK(epphy); + + imxrt_setbits (bit, IMXRT_USBDEV_ENDPTPRIME); + + while (imxrt_getreg (IMXRT_USBDEV_ENDPTPRIME) & bit) + ; +} + +/**************************************************************************** + * Name: imxrt_ep0xfer + * + * Description: + * Schedule a short transfer for Endpoint 0 (IN or OUT) + * + ****************************************************************************/ + +static inline void imxrt_ep0xfer(uint8_t epphy, uint8_t *buf, + uint32_t nbytes) +{ + struct imxrt_dtd_s *dtd = &g_td[epphy]; + + imxrt_writedtd(dtd, buf, nbytes); + + imxrt_queuedtd(epphy, dtd); +} + +/**************************************************************************** + * Name: imxrt_readsetup + * + * Description: + * Read a Setup packet from the DTD. + * + ****************************************************************************/ + +static void imxrt_readsetup(uint8_t epphy, struct usb_ctrlreq_s *ctrl) +{ + struct imxrt_dqh_s *dqh = &g_qh[epphy]; + int i; + + do + { + /* Set the trip wire */ + + imxrt_setbits(USBDEV_USBCMD_SUTW, IMXRT_USBDEV_USBCMD); + + up_invalidate_dcache((uintptr_t)dqh, + (uintptr_t)dqh + sizeof(struct imxrt_dqh_s)); + + /* Copy the request... */ + + for (i = 0; i < 8; i++) + { + ((uint8_t *) ctrl)[i] = ((uint8_t *) dqh->setup)[i]; + } + } + while (!(imxrt_getreg(IMXRT_USBDEV_USBCMD) & USBDEV_USBCMD_SUTW)); + + /* Clear the trip wire */ + + imxrt_clrbits(USBDEV_USBCMD_SUTW, IMXRT_USBDEV_USBCMD); + + /* Clear the Setup Interrupt */ + + imxrt_putreg (IMXRT_ENDPTMASK(IMXRT_EP0_OUT), IMXRT_USBDEV_ENDPTSETUPSTAT); +} + +/**************************************************************************** + * Name: imxrt_set_address + * + * Description: + * Set the devices USB address + * + ****************************************************************************/ + +static inline void imxrt_set_address(struct imxrt_usbdev_s *priv, + uint16_t address) +{ + priv->paddr = address; + priv->paddrset = address != 0; + + imxrt_chgbits(USBDEV_DEVICEADDR_MASK, + priv->paddr << USBDEV_DEVICEADDR_SHIFT, + IMXRT_USBDEV_DEVICEADDR); +} + +/**************************************************************************** + * Name: imxrt_flushep + * + * Description: + * Flush any primed descriptors from this ep + * + ****************************************************************************/ + +static void imxrt_flushep(struct imxrt_ep_s *privep) +{ + uint32_t mask = IMXRT_ENDPTMASK(privep->epphy); + do + { + imxrt_putreg (mask, IMXRT_USBDEV_ENDPTFLUSH); + while ((imxrt_getreg(IMXRT_USBDEV_ENDPTFLUSH) & mask) != 0) + ; + } + while ((imxrt_getreg(IMXRT_USBDEV_ENDPTSTATUS) & mask) != 0); +} + +/**************************************************************************** + * Name: imxrt_progressep + * + * Description: + * Progress the Endpoint by priming the first request into the queue head + * + ****************************************************************************/ + +static int imxrt_progressep(struct imxrt_ep_s *privep) +{ + struct imxrt_dtd_s *dtd = &g_td[privep->epphy]; + struct imxrt_req_s *privreq; + + /* Check the request from the head of the endpoint request queue */ + + privreq = imxrt_rqpeek(privep); + if (!privreq) + { + usbtrace(TRACE_INTDECODE(IMXRT_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 thr response + */ + + if (privep->epphy == IMXRT_EP0_IN && + privep->dev->ep0state == EP0STATE_SETUP_OUT) + { + imxrt_ep0state (privep->dev, EP0STATE_WAIT_NAK_IN); + } + else + { + if (IMXRT_EPPHYIN(privep->epphy)) + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_EPINNULLPACKET), 0); + } + else + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_EPOUTNULLPACKET), 0); + } + } + + imxrt_reqcomplete(privep, imxrt_rqdequeue(privep), OK); + return OK; + } + + if (privep->epphy == IMXRT_EP0_IN) + { + imxrt_ep0state (privep->dev, EP0STATE_DATA_IN); + } + else if (privep->epphy == IMXRT_EP0_OUT) + { + imxrt_ep0state (privep->dev, EP0STATE_DATA_OUT); + } + + int bytesleft = privreq->req.len - privreq->req.xfrd; + + if (IMXRT_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 */ + + imxrt_writedtd (dtd, privreq->req.buf + privreq->req.xfrd, bytesleft); + + /* Then queue onto the DQH */ + + imxrt_queuedtd(privep->epphy, dtd); + + return OK; +} + +/**************************************************************************** + * Name: imxrt_reqcomplete + * + * Description: + * Handle termination of the request at the head of the endpoint request + * queue. + * + ****************************************************************************/ + +static void imxrt_reqcomplete(struct imxrt_ep_s *privep, + struct imxrt_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 == IMXRT_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: imxrt_cancelrequests + * + * Description: + * Cancel all pending requests for an endpoint + * + ****************************************************************************/ + +static void imxrt_cancelrequests(struct imxrt_ep_s *privep, int16_t status) +{ + if (!imxrt_rqempty(privep)) + imxrt_flushep(privep); + + while (!imxrt_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), + (imxrt_rqpeek(privep))->req.xfrd); + imxrt_reqcomplete(privep, imxrt_rqdequeue(privep), status); + } +} + +/**************************************************************************** + * Name: imxrt_epfindbyaddr + * + * Description: + * Find the physical endpoint structure corresponding to a logic endpoint + * address + * + ****************************************************************************/ + +static struct imxrt_ep_s *imxrt_epfindbyaddr(struct imxrt_usbdev_s *priv, + uint16_t eplog) +{ + struct imxrt_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 < IMXRT_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: imxrt_dispatchrequest + * + * Description: + * Provide unhandled setup actions to the class driver. This is logically + * part of the USB interrupt handler. + * + ****************************************************************************/ + +static void imxrt_dispatchrequest(struct imxrt_usbdev_s *priv, + const struct usb_ctrlreq_s *ctrl) +{ + int ret = -EIO; + + usbtrace(TRACE_INTDECODE(IMXRT_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->ep0buf, + priv->ep0buf_len); + } + + if (ret < 0) + { + /* Stall on failure */ + + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_DISPATCHSTALL), 0); + priv->stalled = true; + } +} + +/**************************************************************************** + * Name: imxrt_ep0configure + * + * Description: + * Reset Usb engine + * + ****************************************************************************/ + +static void imxrt_ep0configure(struct imxrt_usbdev_s *priv) +{ + /* Enable ep0 IN and ep0 OUT */ + + g_qh[IMXRT_EP0_OUT].capability = + (DQH_CAPABILITY_MAX_PACKET(CONFIG_USBDEV_EP0_MAXSIZE) | + DQH_CAPABILITY_IOS | DQH_CAPABILITY_ZLT); + + g_qh[IMXRT_EP0_IN].capability = + (DQH_CAPABILITY_MAX_PACKET(CONFIG_USBDEV_EP0_MAXSIZE) | + DQH_CAPABILITY_IOS | DQH_CAPABILITY_ZLT); + + g_qh[IMXRT_EP0_OUT].currdesc = DTD_NEXTDESC_INVALID; + g_qh[IMXRT_EP0_IN].currdesc = DTD_NEXTDESC_INVALID; + + up_flush_dcache((uintptr_t)g_qh, + (uintptr_t)g_qh + (sizeof(struct imxrt_dqh_s) * 2)); + + /* Enable EP0 */ + + imxrt_setbits (USBDEV_ENDPTCTRL0_RXE | USBDEV_ENDPTCTRL0_TXE, + IMXRT_USBDEV_ENDPTCTRL0); +} + +/**************************************************************************** + * Name: imxrt_usbreset + * + * Description: + * Reset Usb engine + * + ****************************************************************************/ + +static void imxrt_usbreset(struct imxrt_usbdev_s *priv) +{ + int epphy; + + /* Disable all endpoints. Control endpoint 0 is always enabled */ + + imxrt_clrbits (USBDEV_ENDPTCTRL_RXE | USBDEV_ENDPTCTRL_TXE, + IMXRT_USBDEV_ENDPTCTRL1); + imxrt_clrbits (USBDEV_ENDPTCTRL_RXE | USBDEV_ENDPTCTRL_TXE, + IMXRT_USBDEV_ENDPTCTRL2); + imxrt_clrbits (USBDEV_ENDPTCTRL_RXE | USBDEV_ENDPTCTRL_TXE, + IMXRT_USBDEV_ENDPTCTRL3); + imxrt_clrbits (USBDEV_ENDPTCTRL_RXE | USBDEV_ENDPTCTRL_TXE, + IMXRT_USBDEV_ENDPTCTRL4); + imxrt_clrbits (USBDEV_ENDPTCTRL_RXE | USBDEV_ENDPTCTRL_TXE, + IMXRT_USBDEV_ENDPTCTRL5); + + /* Clear all pending interrupts */ + + imxrt_putreg (imxrt_getreg(IMXRT_USBDEV_ENDPTNAK), + IMXRT_USBDEV_ENDPTNAK); + imxrt_putreg (imxrt_getreg(IMXRT_USBDEV_ENDPTSETUPSTAT), + IMXRT_USBDEV_ENDPTSETUPSTAT); + imxrt_putreg (imxrt_getreg(IMXRT_USBDEV_ENDPTCOMPLETE), + IMXRT_USBDEV_ENDPTCOMPLETE); + + /* Wait for all prime operations to have completed and then flush all + * DTDs + */ + + while (imxrt_getreg (IMXRT_USBDEV_ENDPTPRIME) != 0) + ; + imxrt_putreg (IMXRT_ENDPTMASK_ALL, IMXRT_USBDEV_ENDPTFLUSH); + while (imxrt_getreg (IMXRT_USBDEV_ENDPTFLUSH)) + ; + + /* Reset endpoints */ + + for (epphy = 0; epphy < IMXRT_NPHYSENDPOINTS; epphy++) + { + struct imxrt_ep_s *privep = &priv->eplist[epphy]; + + imxrt_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 */ + + imxrt_chgbits(USBDEV_USBCMD_ITC_MASK, USBDEV_USBCMD_ITCIMME, + IMXRT_USBDEV_USBCMD); + + /* Zero out the Endpoint queue heads */ + + memset ((void *) g_qh, 0, sizeof (g_qh)); + memset ((void *) g_td, 0, sizeof (g_td)); + + up_flush_dcache((uintptr_t)g_qh, (uintptr_t)g_qh + sizeof(g_qh)); + up_flush_dcache((uintptr_t)g_td, (uintptr_t)g_td + sizeof(g_td)); + + /* Set USB address to 0 */ + + imxrt_set_address (priv, 0); + + /* Initialise the Enpoint List Address */ + + imxrt_putreg ((uint32_t)g_qh, IMXRT_USBDEV_ENDPOINTLIST); + + /* EndPoint 0 initialization */ + + imxrt_ep0configure(priv); + + /* Enable Device interrupts */ + + imxrt_putreg(USB_FRAME_INT | USB_ERROR_INT | USBDEV_USBINTR_NAKE | + USBDEV_USBINTR_SLE | USBDEV_USBINTR_URE | USBDEV_USBINTR_PCE | + USBDEV_USBINTR_UE, IMXRT_USBDEV_USBINTR); +} + +/**************************************************************************** + * Name: imxrt_setstate + * + * Description: + * Sets the EP0 state and manages the NAK interrupts + * + ****************************************************************************/ + +static inline void imxrt_ep0state(struct imxrt_usbdev_s *priv, + uint16_t state) +{ + priv->ep0state = state; + + switch (state) + { + case EP0STATE_WAIT_NAK_IN: + imxrt_putreg (IMXRT_ENDPTMASK(IMXRT_EP0_IN), IMXRT_USBDEV_ENDPTNAKEN); + break; + + case EP0STATE_WAIT_NAK_OUT: + imxrt_putreg (IMXRT_ENDPTMASK(IMXRT_EP0_OUT), IMXRT_USBDEV_ENDPTNAKEN); + break; + + default: + imxrt_putreg(0, IMXRT_USBDEV_ENDPTNAKEN); + break; + } +} + +/**************************************************************************** + * Name: imxrt_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 imxrt_ep0setup(struct imxrt_usbdev_s *priv) +{ + struct imxrt_ep_s *privep; + struct usb_ctrlreq_s *ctrl; + uint16_t value; + uint16_t index; + uint16_t len; + + ctrl = &priv->ep0ctrl; + + /* Terminate any pending requests - since all DTDs will have been retired + * because of the setup packet. + */ + + imxrt_cancelrequests(&priv->eplist[IMXRT_EP0_OUT], -EPROTO); + imxrt_cancelrequests(&priv->eplist[IMXRT_EP0_IN], -EPROTO); + + /* Assume NOT stalled */ + + priv->eplist[IMXRT_EP0_OUT].stalled = false; + priv->eplist[IMXRT_EP0_IN].stalled = false; + priv->stalled = false; + + /* Read EP0 setup data */ + + imxrt_readsetup(IMXRT_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->ep0buf_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) + { + imxrt_ep0state (priv, EP0STATE_SETUP_IN); + } + else + { + imxrt_ep0state (priv, EP0STATE_SETUP_OUT); + + if (len > 0) + { + imxrt_ep0state(priv, EP0STATE_SHORTREAD); + imxrt_ep0xfer(IMXRT_EP0_OUT, priv->ep0buf, len); + return; + } + } + + /* Dispatch any non-standard requests */ + + if ((ctrl->type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD) + { + imxrt_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(IMXRT_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(IMXRT_TRACEINTID_EPGETSTATUS), 0); + privep = imxrt_epfindbyaddr(priv, index); + if (!privep) + { + usbtrace( + TRACE_DEVERROR(IMXRT_TRACEERR_BADEPGETSTATUS), + 0); + priv->stalled = true; + } + else + { + if (privep->stalled) + { + priv->ep0buf[0] = 1; /* Stalled */ + } + else + { + priv->ep0buf[0] = 0; /* Not stalled */ + } + + priv->ep0buf[1] = 0; + + imxrt_ep0xfer (IMXRT_EP0_IN, priv->ep0buf, 2); + imxrt_ep0state (priv, EP0STATE_SHORTWRITE); + } + } + break; + + case USB_REQ_RECIPIENT_DEVICE: + { + if (index == 0) + { + usbtrace( + TRACE_INTDECODE(IMXRT_TRACEINTID_DEVGETSTATUS), + 0); + + /* Features: Remote Wakeup=YES; selfpowered=? */ + + priv->ep0buf[0] = + (priv->selfpowered << + USB_FEATURE_SELFPOWERED) | + (1 << USB_FEATURE_REMOTEWAKEUP); + priv->ep0buf[1] = 0; + + imxrt_ep0xfer(IMXRT_EP0_IN, priv->ep0buf, 2); + imxrt_ep0state (priv, EP0STATE_SHORTWRITE); + } + else + { + usbtrace( + TRACE_DEVERROR(IMXRT_TRACEERR_BADDEVGETSTATUS), + 0); + priv->stalled = true; + } + } + break; + + case USB_REQ_RECIPIENT_INTERFACE: + { + usbtrace(TRACE_INTDECODE(IMXRT_TRACEINTID_IFGETSTATUS), + 0); + priv->ep0buf[0] = 0; + priv->ep0buf[1] = 0; + + imxrt_ep0xfer(IMXRT_EP0_IN, priv->ep0buf, 2); + imxrt_ep0state (priv, EP0STATE_SHORTWRITE); + } + break; + + default: + { + usbtrace(TRACE_DEVERROR(IMXRT_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(IMXRT_TRACEINTID_CLEARFEATURE), 0); + if ((ctrl->type & USB_REQ_RECIPIENT_MASK) != + USB_REQ_RECIPIENT_ENDPOINT) + { + imxrt_dispatchrequest(priv, ctrl); + } + else if (priv->paddrset != 0 && + value == USB_FEATURE_ENDPOINTHALT && + len == 0 && (privep = imxrt_epfindbyaddr(priv, index)) != NULL) + { + imxrt_epstall(&privep->ep, true); + imxrt_ep0state (priv, EP0STATE_WAIT_NAK_IN); + } + else + { + usbtrace(TRACE_DEVERROR(IMXRT_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(IMXRT_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) + { + imxrt_dispatchrequest(priv, ctrl); + } + else if (priv->paddrset != 0 && + value == USB_FEATURE_ENDPOINTHALT && + len == 0 && (privep = imxrt_epfindbyaddr(priv, index)) != NULL) + { + imxrt_epstall(&privep->ep, false); + imxrt_ep0state (priv, EP0STATE_WAIT_NAK_IN); + } + else + { + usbtrace(TRACE_DEVERROR(IMXRT_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(IMXRT_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; + imxrt_ep0state (priv, EP0STATE_WAIT_NAK_IN); + } + else + { + usbtrace(TRACE_DEVERROR(IMXRT_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(IMXRT_TRACEINTID_GETSETDESC), 0); + if ((ctrl->type & USB_REQ_RECIPIENT_MASK) == + USB_REQ_RECIPIENT_DEVICE) + { + imxrt_dispatchrequest(priv, ctrl); + } + else + { + usbtrace(TRACE_DEVERROR(IMXRT_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(IMXRT_TRACEINTID_GETCONFIG), 0); + if (priv->paddrset && + ((ctrl->type & USB_REQ_RECIPIENT_MASK) == + USB_REQ_RECIPIENT_DEVICE) && + value == 0 && index == 0 && len == 1) + { + imxrt_dispatchrequest(priv, ctrl); + } + else + { + usbtrace(TRACE_DEVERROR(IMXRT_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(IMXRT_TRACEINTID_SETCONFIG), 0); + if (((ctrl->type & USB_REQ_RECIPIENT_MASK) == + USB_REQ_RECIPIENT_DEVICE) && index == 0 && len == 0) + { + imxrt_dispatchrequest(priv, ctrl); + } + else + { + usbtrace(TRACE_DEVERROR(IMXRT_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(IMXRT_TRACEINTID_GETSETIF), 0); + imxrt_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(IMXRT_TRACEINTID_SYNCHFRAME), 0); + } + break; + + default: + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_INVALIDCTRLREQ), 0); + priv->stalled = true; + } + break; + } + } + + if (priv->stalled) + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_EP0SETUPSTALLED), + priv->ep0state); + imxrt_epstall(&priv->eplist[IMXRT_EP0_IN].ep, false); + imxrt_epstall(&priv->eplist[IMXRT_EP0_OUT].ep, false); + } +} + +/**************************************************************************** + * Name: imxrt_ep0complete + * + * Description: + * Transfer complete handler for Endpoint 0 + * + ****************************************************************************/ + +static void imxrt_ep0complete(struct imxrt_usbdev_s *priv, uint8_t epphy) +{ + struct imxrt_ep_s *privep = &priv->eplist[epphy]; + + usbtrace(TRACE_INTDECODE(IMXRT_TRACEINTID_EP0COMPLETE), + (uint16_t)priv->ep0state); + + switch (priv->ep0state) + { + case EP0STATE_DATA_IN: + if (imxrt_rqempty(privep)) + { + return; + } + + if (imxrt_epcomplete (priv, epphy)) + { + imxrt_ep0state (priv, EP0STATE_WAIT_NAK_OUT); + } + break; + + case EP0STATE_DATA_OUT: + if (imxrt_rqempty(privep)) + { + return; + } + + if (imxrt_epcomplete (priv, epphy)) + { + imxrt_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->ep0buf, + (uintptr_t)priv->ep0buf + sizeof(priv->ep0buf)); + + imxrt_dispatchrequest(priv, &priv->ep0ctrl); + imxrt_ep0state (priv, EP0STATE_WAIT_NAK_IN); + break; + + case EP0STATE_SHORTWRITE: + imxrt_ep0state (priv, EP0STATE_WAIT_NAK_OUT); + break; + + case EP0STATE_WAIT_STATUS_IN: + imxrt_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(IMXRT_TRACEINTID_EP0INSETADDRESS), + (uint16_t)priv->paddr); + imxrt_set_address (priv, priv->paddr); + } + + break; + + case EP0STATE_WAIT_STATUS_OUT: + imxrt_ep0state (priv, EP0STATE_IDLE); + break; + + default: +#ifdef CONFIG_DEBUG_FEATURES + DEBUGASSERT(priv->ep0state != EP0STATE_DATA_IN && + priv->ep0state != EP0STATE_DATA_OUT && + priv->ep0state != EP0STATE_SHORTWRITE && + priv->ep0state != EP0STATE_WAIT_STATUS_IN && + priv->ep0state != EP0STATE_WAIT_STATUS_OUT); +#endif + priv->stalled = true; + break; + } + + if (priv->stalled) + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_EP0SETUPSTALLED), + priv->ep0state); + imxrt_epstall(&priv->eplist[IMXRT_EP0_IN].ep, false); + imxrt_epstall(&priv->eplist[IMXRT_EP0_OUT].ep, false); + } +} + +/**************************************************************************** + * Name: imxrt_ep0nak + * + * Description: + * Handle a NAK interrupt on EP0 + * + ****************************************************************************/ + +static void imxrt_ep0nak(struct imxrt_usbdev_s *priv, uint8_t epphy) +{ + usbtrace(TRACE_INTDECODE(IMXRT_TRACEINTID_EP0NAK), + (uint16_t)priv->ep0state); + + switch (priv->ep0state) + { + case EP0STATE_WAIT_NAK_IN: + imxrt_ep0xfer (IMXRT_EP0_IN, NULL, 0); + imxrt_ep0state (priv, EP0STATE_WAIT_STATUS_IN); + break; + + case EP0STATE_WAIT_NAK_OUT: + imxrt_ep0xfer (IMXRT_EP0_OUT, NULL, 0); + imxrt_ep0state (priv, EP0STATE_WAIT_STATUS_OUT); + break; + + default: +#ifdef CONFIG_DEBUG_FEATURES + DEBUGASSERT(priv->ep0state != EP0STATE_WAIT_NAK_IN && + priv->ep0state != EP0STATE_WAIT_NAK_OUT); +#endif + priv->stalled = true; + break; + } + + if (priv->stalled) + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_EP0SETUPSTALLED), + priv->ep0state); + imxrt_epstall(&priv->eplist[IMXRT_EP0_IN].ep, false); + imxrt_epstall(&priv->eplist[IMXRT_EP0_OUT].ep, false); + } +} + +/**************************************************************************** + * Name: imxrt_epcomplete + * + * Description: + * Transfer complete handler for Endpoints other than 0 + * returns whether the request at the head has completed + * + ****************************************************************************/ + +bool imxrt_epcomplete(struct imxrt_usbdev_s *priv, uint8_t epphy) +{ + struct imxrt_ep_s *privep = &priv->eplist[epphy]; + struct imxrt_req_s *privreq = privep->head; + struct imxrt_dtd_s *dtd = &g_td[epphy]; + + if (privreq == NULL) /* This shouldn't really happen */ + { + if (IMXRT_EPPHYOUT(privep->epphy)) + { + usbtrace(TRACE_INTDECODE(IMXRT_TRACEINTID_EPINQEMPTY), 0); + } + else + { + usbtrace(TRACE_INTDECODE(IMXRT_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 imxrt_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 (IMXRT_EPPHYOUT(privep->epphy)) + { + /* read(OUT) completes when request filled, or a short transfer is + * received + */ + + usbtrace(TRACE_INTDECODE(IMXRT_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(IMXRT_TRACEINTID_EPOUT), complete); + } + + /* If the transfer is complete, then dequeue and progress any further + * queued requests + */ + + if (complete) + { + privreq = imxrt_rqdequeue (privep); + } + + if (!imxrt_rqempty(privep)) + { + imxrt_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); + imxrt_reqcomplete(privep, privreq, OK); + } + + return complete; +} + +/**************************************************************************** + * Name: imxrt_usbinterrupt + * + * Description: + * USB interrupt handler + * + ****************************************************************************/ + +static int imxrt_usbinterrupt(int irq, FAR void *context, FAR void *arg) +{ + struct imxrt_usbdev_s *priv = &g_usbdev; + uint32_t disr; + uint32_t portsc1; + uint32_t n; + + usbtrace(TRACE_INTENTRY(IMXRT_TRACEINTID_USB), 0); + + /* Read the interrupts and then clear them */ + + disr = imxrt_getreg(IMXRT_USBDEV_USBSTS); + imxrt_putreg(disr, IMXRT_USBDEV_USBSTS); + + if (disr & USBDEV_USBSTS_URI) + { + usbtrace(TRACE_INTDECODE(IMXRT_TRACEINTID_DEVRESET), 0); + + imxrt_usbreset(priv); + + usbtrace(TRACE_INTEXIT(IMXRT_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(IMXRT_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(IMXRT_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 = imxrt_getreg(IMXRT_USBDEV_PORTSC1); + + 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_IMXRT_USBDEV_FRAME_INTERRUPT + if (disr & USBDEV_USBSTS_SRI) + { + usbtrace(TRACE_INTDECODE(IMXRT_TRACEINTID_FRAME), 0); + + uint32_t frindex = imxrt_getreg(IMXRT_USBDEV_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 = imxrt_getreg (IMXRT_USBDEV_ENDPTCOMPLETE); + + if (mask) + { + /* Clear any NAK interrupt and completion interrupts */ + + imxrt_putreg (mask, IMXRT_USBDEV_ENDPTNAK); + imxrt_putreg (mask, IMXRT_USBDEV_ENDPTCOMPLETE); + + if (mask & IMXRT_ENDPTMASK(0)) + { + imxrt_ep0complete(priv, 0); + } + + if (mask & IMXRT_ENDPTMASK(1)) + { + imxrt_ep0complete(priv, 1); + } + + for (n = 1; n < IMXRT_NLOGENDPOINTS; n++) + { + if (mask & IMXRT_ENDPTMASK((n << 1))) + { + imxrt_epcomplete (priv, (n << 1)); + } + + if (mask & IMXRT_ENDPTMASK((n << 1)+1)) + { + imxrt_epcomplete(priv, (n << 1)+1); + } + } + } + + /* Handle setup interrupts */ + + uint32_t setupstat = imxrt_getreg(IMXRT_USBDEV_ENDPTSETUPSTAT); + if (setupstat) + { + /* Clear the endpoint complete CTRL OUT and IN when a Setup is + * received + */ + + imxrt_putreg(IMXRT_ENDPTMASK(IMXRT_EP0_IN) | + IMXRT_ENDPTMASK(IMXRT_EP0_OUT), + IMXRT_USBDEV_ENDPTCOMPLETE); + + if (setupstat & IMXRT_ENDPTMASK(IMXRT_EP0_OUT)) + { + usbtrace(TRACE_INTDECODE(IMXRT_TRACEINTID_EP0SETUP), + setupstat); + imxrt_ep0setup(priv); + } + } + } + + if (disr & USBDEV_USBSTS_NAKI) + { + uint32_t pending = imxrt_getreg(IMXRT_USBDEV_ENDPTNAK) & + imxrt_getreg(IMXRT_USBDEV_ENDPTNAKEN); + if (pending) + { + /* We shouldn't see NAK interrupts except on Endpoint 0 */ + + if (pending & IMXRT_ENDPTMASK(0)) + { + imxrt_ep0nak(priv, 0); + } + + if (pending & IMXRT_ENDPTMASK(1)) + { + imxrt_ep0nak(priv, 1); + } + } + + /* Clear the interrupts */ + + imxrt_putreg(pending, IMXRT_USBDEV_ENDPTNAK); + } + + usbtrace(TRACE_INTEXIT(IMXRT_TRACEINTID_USB), 0); + return OK; +} + +/**************************************************************************** + * Endpoint operations + ****************************************************************************/ + +/**************************************************************************** + * Name: imxrt_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 imxrt_epconfigure(FAR struct usbdev_ep_s *ep, + FAR const struct usb_epdesc_s *desc, + bool last) +{ + FAR struct imxrt_ep_s *privep = (FAR struct imxrt_ep_s *)ep; + struct imxrt_dqh_s *dqh = &g_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_flush_dcache((uintptr_t)dqh, + (uintptr_t)dqh + sizeof(struct imxrt_dqh_s)); + + /* Setup Endpoint Control Register */ + + if (IMXRT_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; + } + + imxrt_chgbits (0xffff0000, cfg, + IMXRT_USBDEV_ENDPTCTRL(privep->epphy >> 1)); + } + 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; + } + + imxrt_chgbits (0x0000ffff, cfg, + IMXRT_USBDEV_ENDPTCTRL(privep->epphy >> 1)); + } + + /* Reset endpoint status */ + + privep->stalled = false; + + /* Enable the endpoint */ + + if (IMXRT_EPPHYIN(privep->epphy)) + { + imxrt_setbits (USBDEV_ENDPTCTRL_TXE, + IMXRT_USBDEV_ENDPTCTRL(privep->epphy >> 1)); + } + else + { + imxrt_setbits (USBDEV_ENDPTCTRL_RXE, + IMXRT_USBDEV_ENDPTCTRL(privep->epphy >> 1)); + } + + return OK; +} + +/**************************************************************************** + * Name: imxrt_epdisable + * + * Description: + * The endpoint will no longer be used + * + ****************************************************************************/ + +static int imxrt_epdisable(FAR struct usbdev_ep_s *ep) +{ + FAR struct imxrt_ep_s *privep = (FAR struct imxrt_ep_s *)ep; + irqstate_t flags; + +#ifdef CONFIG_DEBUG_FEATURES + if (!ep) + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + + usbtrace(TRACE_EPDISABLE, privep->epphy); + + flags = enter_critical_section(); + + /* Disable Endpoint */ + + if (IMXRT_EPPHYIN(privep->epphy)) + { + imxrt_clrbits (USBDEV_ENDPTCTRL_TXE, + IMXRT_USBDEV_ENDPTCTRL(privep->epphy >> 1)); + } + else + { + imxrt_clrbits (USBDEV_ENDPTCTRL_RXE, + IMXRT_USBDEV_ENDPTCTRL(privep->epphy >> 1)); + } + + privep->stalled = true; + + /* Cancel any ongoing activity */ + + imxrt_cancelrequests(privep, -ESHUTDOWN); + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: imxrt_epallocreq + * + * Description: + * Allocate an I/O request + * + ****************************************************************************/ + +static FAR struct usbdev_req_s *imxrt_epallocreq(FAR struct usbdev_ep_s *ep) +{ + FAR struct imxrt_req_s *privreq; + +#ifdef CONFIG_DEBUG_FEATURES + if (!ep) + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_INVALIDPARMS), 0); + return NULL; + } +#endif + + usbtrace(TRACE_EPALLOCREQ, ((FAR struct imxrt_ep_s *)ep)->epphy); + + privreq = (FAR struct imxrt_req_s *)kmm_malloc(sizeof(struct imxrt_req_s)); + if (!privreq) + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_ALLOCFAIL), 0); + return NULL; + } + + memset(privreq, 0, sizeof(struct imxrt_req_s)); + return &privreq->req; +} + +/**************************************************************************** + * Name: imxrt_epfreereq + * + * Description: + * Free an I/O request + * + ****************************************************************************/ + +static void imxrt_epfreereq(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req) +{ + FAR struct imxrt_req_s *privreq = (FAR struct imxrt_req_s *)req; + +#ifdef CONFIG_DEBUG_FEATURES + if (!ep || !req) + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_INVALIDPARMS), 0); + return; + } +#endif + + usbtrace(TRACE_EPFREEREQ, ((FAR struct imxrt_ep_s *)ep)->epphy); + kmm_free(privreq); +} + +/**************************************************************************** + * Name: imxrt_epallocbuffer + * + * Description: + * Allocate an I/O buffer + * + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_DMA +static void *imxrt_epallocbuffer(FAR 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. + */ + + FAR struct imxrt_ep_s *privep = (FAR struct imxrt_ep_s *)ep; + + usbtrace(TRACE_EPALLOCBUFFER, privep->epphy); + +#ifdef CONFIG_USBDEV_DMAMEMORY + return usbdev_dma_alloc(bytes); +#else + return kmm_malloc(bytes); +#endif +} +#endif + +/**************************************************************************** + * Name: imxrt_epfreebuffer + * + * Description: + * Free an I/O buffer + * + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_DMA +static void imxrt_epfreebuffer(FAR struct usbdev_ep_s *ep, FAR void *buf) +{ + FAR struct imxrt_ep_s *privep = (FAR struct imxrt_ep_s *)ep; + + usbtrace(TRACE_EPFREEBUFFER, privep->epphy); + +#ifdef CONFIG_USBDEV_DMAMEMORY + usbdev_dma_free(buf); +#else + kmm_free(buf); +#endif +} +#endif + +/**************************************************************************** + * Name: imxrt_epsubmit + * + * Description: + * Submit an I/O request to the endpoint + * + ****************************************************************************/ + +static int imxrt_epsubmit(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req) +{ + FAR struct imxrt_req_s *privreq = (FAR struct imxrt_req_s *)req; + FAR struct imxrt_ep_s *privep = (FAR struct imxrt_ep_s *)ep; + FAR struct imxrt_usbdev_s *priv; + irqstate_t flags; + int ret = OK; + +#ifdef CONFIG_DEBUG_FEATURES + if (!req || !req->callback || !req->buf || !ep) + { + usbtrace(TRACE_DEVERROR(IMXRT_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(IMXRT_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 (IMXRT_EPPHYIN(privep->epphy)) + { + usbtrace(TRACE_INREQQUEUED(privep->epphy), privreq->req.len); + } + else + { + usbtrace(TRACE_OUTREQQUEUED(privep->epphy), privreq->req.len); + } + + if (imxrt_rqenqueue(privep, privreq)) + { + imxrt_progressep(privep); + } + } + + leave_critical_section(flags); + return ret; +} + +/**************************************************************************** + * Name: imxrt_epcancel + * + * Description: + * Cancel an I/O request previously sent to an endpoint + * + ****************************************************************************/ + +static int imxrt_epcancel(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req) +{ + FAR struct imxrt_ep_s *privep = (FAR struct imxrt_ep_s *)ep; + irqstate_t flags; + +#ifdef CONFIG_DEBUG_FEATURES + if (!ep || !req) + { + usbtrace(TRACE_DEVERROR(IMXRT_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 ... + */ + + imxrt_cancelrequests(privep, -ESHUTDOWN); + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: imxrt_epstall + * + * Description: + * Stall or resume and endpoint + * + ****************************************************************************/ + +static int imxrt_epstall(FAR struct usbdev_ep_s *ep, bool resume) +{ + FAR struct imxrt_ep_s *privep = (FAR struct imxrt_ep_s *)ep; + irqstate_t flags; + + /* STALL or RESUME the endpoint */ + + flags = enter_critical_section(); + usbtrace(resume ? TRACE_EPRESUME : TRACE_EPSTALL, privep->epphy); + + uint32_t addr = IMXRT_USBDEV_ENDPTCTRL(privep->epphy >> 1); + uint32_t ctrl_xs = IMXRT_EPPHYIN(privep->epphy) ? + USBDEV_ENDPTCTRL_TXS : USBDEV_ENDPTCTRL_RXS; + uint32_t ctrl_xr = IMXRT_EPPHYIN(privep->epphy) ? + USBDEV_ENDPTCTRL_TXR : USBDEV_ENDPTCTRL_RXR; + + if (resume) + { + privep->stalled = false; + + /* Clear stall and reset the data toggle */ + + imxrt_chgbits (ctrl_xs | ctrl_xr, ctrl_xr, addr); + } + else + { + privep->stalled = true; + + imxrt_setbits (ctrl_xs, addr); + } + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Device operations + ****************************************************************************/ + +/**************************************************************************** + * Name: imxrt_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 FAR struct usbdev_ep_s *imxrt_allocep(FAR struct usbdev_s *dev, + uint8_t eplog, + bool in, uint8_t eptype) +{ + FAR struct imxrt_usbdev_s *priv = (FAR struct imxrt_usbdev_s *)dev; + uint32_t epset = IMXRT_EPALLSET & ~IMXRT_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 >= IMXRT_NLOGENDPOINTS) + { + usbtrace(TRACE_DEVERROR(IMXRT_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 &= IMXRT_EPINSET; + } + else + { + epset &= IMXRT_EPOUTSET; + } + + /* Get the subset matching the requested type */ + + switch (eptype) + { + case USB_EP_ATTR_XFER_INT: /* Interrupt endpoint */ + epset &= IMXRT_EPINTRSET; + break; + + case USB_EP_ATTR_XFER_BULK: /* Bulk endpoint */ + epset &= IMXRT_EPBULKSET; + break; + + case USB_EP_ATTR_XFER_ISOC: /* Isochronous endpoint */ + epset &= IMXRT_EPISOCSET; + break; + + case USB_EP_ATTR_XFER_CONTROL: /* Control endpoint -- not a valid choice */ + default: + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_BADEPTYPE), (uint16_t)eptype); + return NULL; + } + + /* Is the resulting endpoint supported by the IMXRT3x? */ + + 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 < IMXRT_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(IMXRT_TRACEERR_NOEP), (uint16_t)eplog); + return NULL; +} + +/**************************************************************************** + * Name: imxrt_freeep + * + * Description: + * Free the previously allocated endpoint + * + ****************************************************************************/ + +static void imxrt_freeep(FAR struct usbdev_s *dev, + FAR struct usbdev_ep_s *ep) +{ + FAR struct imxrt_usbdev_s *priv = (FAR struct imxrt_usbdev_s *)dev; + FAR struct imxrt_ep_s *privep = (FAR struct imxrt_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: imxrt_getframe + * + * Description: + * Returns the current frame number + * + ****************************************************************************/ + +static int imxrt_getframe(struct usbdev_s *dev) +{ +#ifdef CONFIG_IMXRT_USBDEV_FRAME_INTERRUPT + FAR struct imxrt_usbdev_s *priv = (FAR struct imxrt_usbdev_s *)dev; + + /* 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 = imxrt_getreg(IMXRT_USBDEV_FRINDEX); + 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: imxrt_wakeup + * + * Description: + * Tries to wake up the host connected to this device + * + ****************************************************************************/ + +static int imxrt_wakeup(struct usbdev_s *dev) +{ + irqstate_t flags; + + usbtrace(TRACE_DEVWAKEUP, 0); + + flags = enter_critical_section(); + imxrt_setbits(USBDEV_PRTSC1_FPR, IMXRT_USBDEV_PORTSC1); + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: imxrt_selfpowered + * + * Description: + * Sets/clears the device selfpowered feature + * + ****************************************************************************/ + +static int imxrt_selfpowered(struct usbdev_s *dev, bool selfpowered) +{ + FAR struct imxrt_usbdev_s *priv = (FAR struct imxrt_usbdev_s *)dev; + + usbtrace(TRACE_DEVSELFPOWERED, (uint16_t)selfpowered); + +#ifdef CONFIG_DEBUG_FEATURES + if (!dev) + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_INVALIDPARMS), 0); + return -ENODEV; + } +#endif + + priv->selfpowered = selfpowered; + return OK; +} + +/**************************************************************************** + * Name: imxrt_pullup + * + * Description: + * Software-controlled connect to/disconnect from USB host + * + ****************************************************************************/ + +static int imxrt_pullup(struct usbdev_s *dev, bool enable) +{ + usbtrace(TRACE_DEVPULLUP, (uint16_t)enable); + + irqstate_t flags = enter_critical_section(); + if (enable) + { + imxrt_setbits (USBDEV_USBCMD_RS, IMXRT_USBDEV_USBCMD); + +#ifdef CONFIG_IMXRT_USB0DEV_NOVBUS + /* Create a 'false' power event on the USB port so the MAC connects */ + + imxrt_clrbits (USBOTG_OTGSC_VD, IMXRT_USBOTG_OTGSC); + imxrt_setbits (USBOTG_OTGSC_VC, IMXRT_USBOTG_OTGSC); +#endif + } + else + { + imxrt_clrbits (USBDEV_USBCMD_RS, IMXRT_USBDEV_USBCMD); + } + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: arm_usbinitialize + * + * Description: + * Initialize USB hardware. + * + * Assumptions: + * - This function is called very early in the initialization sequence + * - PLL initialization is not performed here but should been in + * the low-level boot logic: USB1 PLL must be configured for operation + * at 480MHz + * + ****************************************************************************/ + +void arm_usbinitialize(void) +{ + struct imxrt_usbdev_s *priv = &g_usbdev; + int i; + irqstate_t flags; + + flags = enter_critical_section(); + + /* Initialize the device state structure */ + + memset(priv, 0, sizeof(struct imxrt_usbdev_s)); + priv->usbdev.ops = &g_devops; + priv->usbdev.ep0 = &priv->eplist[IMXRT_EP0_IN].ep; + priv->epavail = IMXRT_EPALLSET & ~IMXRT_EPCTRLSET; + + /* Initialize the endpoint list */ + + for (i = 0; i < IMXRT_NPHYSENDPOINTS; i++) + { + uint32_t bit = 1 << i; + + /* Set endpoint operations, reference to driver structure (not + * really necessary because there is only one controller), and + * the physical endpoint number (which is just the index to the + * endpoint). + */ + + 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 (IMXRT_EPPHYIN(i)) + { + priv->eplist[i].ep.eplog = IMXRT_EPPHYIN2LOG(i); + } + else + { + priv->eplist[i].ep.eplog = IMXRT_EPPHYOUT2LOG(i); + } + + /* The maximum packet size may depend on the type of endpoint */ + + if ((IMXRT_EPCTRLSET & bit) != 0) + { + priv->eplist[i].ep.maxpacket = IMXRT_EP0MAXPACKET; + } + else if ((IMXRT_EPINTRSET & bit) != 0) + { + priv->eplist[i].ep.maxpacket = IMXRT_INTRMAXPACKET; + } + else if ((IMXRT_EPBULKSET & bit) != 0) + { + priv->eplist[i].ep.maxpacket = IMXRT_BULKMAXPACKET; + } + else /* if ((IMXRT_EPISOCSET & bit) != 0) */ + { + priv->eplist[i].ep.maxpacket = IMXRT_ISOCMAXPACKET; + } + } + + /* Clock run */ + + imxrt_clockall_usboh3(); + + /* Disable USB interrupts */ + + imxrt_putreg(0, IMXRT_USBDEV_USBINTR); + + /* Soft reset PHY and enable clock */ + + putreg32(USBPHY_CTRL_SFTRST | USBPHY_CTRL_CLKGATE, IMXRT_USBPHY1_CTRL_CLR); + + /* Disconnect device */ + + imxrt_pullup(&priv->usbdev, false); + + /* Reset the controller */ + + imxrt_setbits (USBDEV_USBCMD_RST, IMXRT_USBDEV_USBCMD); + while (imxrt_getreg (IMXRT_USBDEV_USBCMD) & USBDEV_USBCMD_RST) + ; + + /* Power up the PHY (turn off power disable) - USBPHYx_PWDn + * Manual: The USB PHY Power-Down Register provides overall control of the + * PHY power state. Before programming this register, the PHY clocks must + * be enabled in registers USBPHYx_CTRLn and + * CCM_ANALOG_USBPHYx_PLL_480_CTRLn. + */ + + imxrt_putreg(0, IMXRT_USBPHY1_PWD); + + /* Program the controller to be the USB device controller */ + + imxrt_putreg (USBDEV_USBMODE_SDIS | USBDEV_USBMODE_SLOM | + USBDEV_USBMODE_CM_DEVICE, IMXRT_USBDEV_USBMODE); + + /* Attach USB controller interrupt handler */ + + irq_attach(IMXRT_IRQ_USBOTG1, imxrt_usbinterrupt, NULL); + up_enable_irq(IMXRT_IRQ_USBOTG1); + + leave_critical_section(flags); + + /* Reset/Re-initialize the USB hardware */ + + imxrt_usbreset(priv); + + return; +} + +/**************************************************************************** + * Name: arm_usbuninitialize + ****************************************************************************/ + +void arm_usbuninitialize(void) +{ + struct imxrt_usbdev_s *priv = &g_usbdev; + irqstate_t flags; + + usbtrace(TRACE_DEVUNINIT, 0); + + if (priv->driver) + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_DRIVERREGISTERED), 0); + usbdev_unregister(priv->driver); + } + + flags = enter_critical_section(); + + /* Disconnect device */ + + imxrt_pullup(&priv->usbdev, false); + priv->usbdev.speed = USB_SPEED_UNKNOWN; + + /* Disable and detach IRQs */ + + up_disable_irq(IMXRT_IRQ_USBOTG1); + irq_detach(IMXRT_IRQ_USBOTG1); + + /* Reset the controller */ + + imxrt_setbits (USBDEV_USBCMD_RST, IMXRT_USBDEV_USBCMD); + while (imxrt_getreg (IMXRT_USBDEV_USBCMD) & USBDEV_USBCMD_RST) + ; + + /* Turn off USB power and clocking */ + + /* Power down the PHY */ + + imxrt_putreg(0xffffffff, IMXRT_USBPHY1_PWD); + + /* Stop clock + * NOTE: This will interfere with USB OTG 2 and should probably be removed + * if Device or Host code is expanded to support both OTG Cores. + */ + + imxrt_clockoff_usboh3(); + + 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(IMXRT_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } + + if (g_usbdev.driver) + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_DRIVER), 0); + return -EBUSY; + } +#endif + + /* First hook up the driver */ + + g_usbdev.driver = driver; + + /* Then bind the class driver */ + + ret = CLASS_BIND(driver, &g_usbdev.usbdev); + if (ret) + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_BINDFAILED), (uint16_t)-ret); + g_usbdev.driver = NULL; + } + else + { + /* Enable USB controller interrupts */ + + up_enable_irq(IMXRT_IRQ_USBOTG1); + } + + 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) +{ + usbtrace(TRACE_DEVUNREGISTER, 0); + +#ifdef CONFIG_DEBUG_FEATURES + if (driver != g_usbdev.driver) + { + usbtrace(TRACE_DEVERROR(IMXRT_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + + /* Unbind the class driver */ + + CLASS_UNBIND(driver, &g_usbdev.usbdev); + + /* Disable USB controller interrupts */ + + up_disable_irq(IMXRT_IRQ_USBOTG1); + + /* Unhook the driver */ + + g_usbdev.driver = NULL; + return OK; +} + From a2921db3f6c8f2d8748871a1a0a5c5722edceb73 Mon Sep 17 00:00:00 2001 From: wgzAIIT <820906721@qq.com> Date: Fri, 1 Apr 2022 10:15:39 +0800 Subject: [PATCH 5/7] change support usb1 to usb2 on xidatong --- .../aiit_board/xidatong/include/board.h | 1 + .../aiit_board/xidatong/src/imxrt_usbhost.c | 2 +- .../arm/src/imxrt/hardware/imxrt_usbotg.h | 5 +++ .../arm/src/imxrt/hardware/imxrt_usbphy.h | 32 +++++++++++++------ .../hardware/rt105x/imxrt105x_memorymap.h | 2 +- .../arch/arm/src/imxrt/imxrt_clockconfig.c | 26 +++++++-------- .../nuttx/arch/arm/src/imxrt/imxrt_ehci.c | 6 ++-- .../nuttx/arch/arm/src/imxrt/imxrt_lowputc.c | 4 +-- .../nuttx/arch/arm/src/imxrt/imxrt_lpi2c.c | 4 +-- .../nuttx/arch/arm/src/imxrt/imxrt_lpspi.c | 8 ++--- .../nuttx/arch/arm/src/imxrt/imxrt_usbdev.c | 18 +++++------ 11 files changed, 63 insertions(+), 45 deletions(-) diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/include/board.h b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/include/board.h index e6021288c..49181683a 100644 --- a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/include/board.h +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/include/board.h @@ -128,6 +128,7 @@ #define IMXRT_SYS_PLL_SELECT CCM_ANALOG_PLL_SYS_DIV_SELECT_22 #define IMXRT_USB1_PLL_DIV_SELECT CCM_ANALOG_PLL_USB1_DIV_SELECT_20 +#define IMXRT_USB2_PLL_DIV_SELECT CCM_ANALOG_PLL_USB2_DIV_SELECT_20 // #define BOARD_CPU_FREQUENCY \ // (BOARD_XTAL_FREQUENCY * (IMXRT_ARM_PLL_DIV_SELECT / 2)) / IMXRT_ARM_PODF_DIVIDER diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/src/imxrt_usbhost.c b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/src/imxrt_usbhost.c index 335a3f687..46cc10e63 100644 --- a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/src/imxrt_usbhost.c +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/src/imxrt_usbhost.c @@ -138,7 +138,7 @@ int imxrt_usbhost_initialize(void) /* Make sure we don't accidentally switch on USB bus power */ - *((uint32_t *)IMXRT_USBNC_USB_OTG1_CTRL) = USBNC_PWR_POL; + *((uint32_t *)IMXRT_USBNC_USB_OTG2_CTRL) = USBNC_PWR_POL; *((uint32_t *)0x400d9030) = (1 << 21); *((uint32_t *)0x400d9000) = 0; diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbotg.h b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbotg.h index 5921081a2..3d83733ce 100644 --- a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbotg.h +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbotg.h @@ -117,7 +117,9 @@ /* USB Non-core memory map & register definition */ #define IMXRT_USBNC_USB_OTG1_CTRL_OFFSET 0x0800 /* OTG1 Control Register */ +#define IMXRT_USBNC_USB_OTG2_CTRL_OFFSET 0x0800 /* OTG2 Control Register */ #define IMXRT_USBNC_USB_OTG1_PHY_CTRL_0_OFFSET 0x0818 /* OTG1 Phy Control Register */ +#define IMXRT_USBNC_USB_OTG2_PHY_CTRL_0_OFFSET 0x0818 /* OTG2 Phy Control Register */ /* USBOTG register (virtual) addresses **************************************/ @@ -201,6 +203,9 @@ #define IMXRT_USBNC_USB_OTG1_CTRL (IMXRT_USB_BASE + IMXRT_USBNC_USB_OTG1_CTRL_OFFSET) #define IMXRT_USBNC_USB_OTG1_PHY_CTRL_0 (IMXRT_USB_BASE + IMXRT_USBNC_USB_OTG1_PHY_CTRL_0_OFFSET) +#define IMXRT_USBNC_USB_OTG2_CTRL (IMXRT_USB_BASE + IMXRT_USBNC_USB_OTG2_CTRL_OFFSET) +#define IMXRT_USBNC_USB_OTG2_PHY_CTRL_0 (IMXRT_USB_BASE + IMXRT_USBNC_USB_OTG2_PHY_CTRL_0_OFFSET) + /* USBOTG register bit definitions ******************************************/ /* Device/host capability registers */ diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbphy.h b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbphy.h index 1d4b7ff4c..ec30f0aee 100644 --- a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbphy.h +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/imxrt_usbphy.h @@ -32,23 +32,35 @@ * Pre-processor Definitions ****************************************************************************/ -#define IMXRT_USBPHY_BASE_OFFSET 0x1000 /* USB PHY Base */ +#define IMXRT_USBPHY1_BASE_OFFSET 0x1000 /* USB PHY1 Base */ +#define IMXRT_USBPHY2_BASE_OFFSET 0x2000 /* USB PHY2 Base */ -#define IMXRT_USBPHY_BASE (IMXRT_ANATOP_BASE + IMXRT_USBPHY_BASE_OFFSET) /* USB PHY Base */ +#define IMXRT_USBPHY1_BASE (IMXRT_ANATOP_BASE + IMXRT_USBPHY1_BASE_OFFSET) /* USB PHY1 Base */ +#define IMXRT_USBPHY2_BASE (IMXRT_ANATOP_BASE + IMXRT_USBPHY2_BASE_OFFSET) /* USB PHY2 Base */ /* Register Offsets *********************************************************/ -#define IMXRT_USBPHY1_PWD_OFFSET 0x0000 /* USBPHY1 USB PHY Power-Down Register */ -#define IMXRT_USBPHY1_PWD_CLR_OFFSET 0x0008 /* USBPHY1 USB PHY Power-Down Register Clear */ -#define IMXRT_USBPHY1_CTRL_OFFSET 0x0030 /* USBPHY1 USB PHY General Control Register */ -#define IMXRT_USBPHY1_CTRL_CLR_OFFSET 0x0038 /* USBPHY1 USB PHY General Control Register Clear */ +#define IMXRT_USBPHY1_PWD_OFFSET 0x0000 /* USBPHY1 USB PHY1 Power-Down Register */ +#define IMXRT_USBPHY1_PWD_CLR_OFFSET 0x0008 /* USBPHY1 USB PHY1 Power-Down Register Clear */ +#define IMXRT_USBPHY1_CTRL_OFFSET 0x0030 /* USBPHY1 USB PHY1 General Control Register */ +#define IMXRT_USBPHY1_CTRL_CLR_OFFSET 0x0038 /* USBPHY1 USB PHY1 General Control Register Clear */ + +#define IMXRT_USBPHY2_PWD_OFFSET 0x0000 /* USBPHY2 USB PHY Power-Down Register */ +#define IMXRT_USBPHY2_PWD_CLR_OFFSET 0x0008 /* USBPHY2 USB PHY Power-Down Register Clear */ +#define IMXRT_USBPHY2_CTRL_OFFSET 0x0030 /* USBPHY2 USB PHY General Control Register */ +#define IMXRT_USBPHY2_CTRL_CLR_OFFSET 0x0038 /* USBPHY2 USB PHY General Control Register Clear */ /* Register addresses *******************************************************/ -#define IMXRT_USBPHY1_PWD (IMXRT_USBPHY_BASE + IMXRT_USBPHY1_PWD_OFFSET) /* USBPHY1 USB PHY Power-Down Register */ -#define IMXRT_USBPHY1_PWD_CLR (IMXRT_USBPHY_BASE + IMXRT_USBPHY1_PWD_CLR_OFFSET) /* USBPHY1 USB PHY Power-Down Register Clear */ -#define IMXRT_USBPHY1_CTRL (IMXRT_USBPHY_BASE + IMXRT_USBPHY1_CTRL_OFFSET) /* USBPHY1 USB PHY General Control Register */ -#define IMXRT_USBPHY1_CTRL_CLR (IMXRT_USBPHY_BASE + IMXRT_USBPHY1_CTRL_CLR_OFFSET) /* USBPHY1 USB PHY General Control Register Clear */ +#define IMXRT_USBPHY1_PWD (IMXRT_USBPHY1_BASE + IMXRT_USBPHY1_PWD_OFFSET) /* USBPHY1 USB PHY1 Power-Down Register */ +#define IMXRT_USBPHY1_PWD_CLR (IMXRT_USBPHY1_BASE + IMXRT_USBPHY1_PWD_CLR_OFFSET) /* USBPHY1 USB PHY1 Power-Down Register Clear */ +#define IMXRT_USBPHY1_CTRL (IMXRT_USBPHY1_BASE + IMXRT_USBPHY1_CTRL_OFFSET) /* USBPHY1 USB PHY1 General Control Register */ +#define IMXRT_USBPHY1_CTRL_CLR (IMXRT_USBPHY1_BASE + IMXRT_USBPHY1_CTRL_CLR_OFFSET) /* USBPHY1 USB PHY1 General Control Register Clear */ + +#define IMXRT_USBPHY2_PWD (IMXRT_USBPHY2_BASE + IMXRT_USBPHY2_PWD_OFFSET) /* USBPHY2 USB PHY2 Power-Down Register */ +#define IMXRT_USBPHY2_PWD_CLR (IMXRT_USBPHY2_BASE + IMXRT_USBPHY2_PWD_CLR_OFFSET) /* USBPHY2 USB PHY2 Power-Down Register Clear */ +#define IMXRT_USBPHY2_CTRL (IMXRT_USBPHY2_BASE + IMXRT_USBPHY2_CTRL_OFFSET) /* USBPHY2 USB PHY2 General Control Register */ +#define IMXRT_USBPHY2_CTRL_CLR (IMXRT_USBPHY2_BASE + IMXRT_USBPHY2_CTRL_CLR_OFFSET) /* USBPHY2 USB PHY2 General Control Register Clear */ /* Register Bit Definitions *************************************************/ diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/rt105x/imxrt105x_memorymap.h b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/rt105x/imxrt105x_memorymap.h index f6afcb4a5..dc42ac2e8 100644 --- a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/rt105x/imxrt105x_memorymap.h +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/hardware/rt105x/imxrt105x_memorymap.h @@ -232,7 +232,7 @@ /* 0x402d4000 16KB Reserved */ #define IMXRT_ENET_BASE 0x402d8000 /* 16KB ENET */ #define IMXRT_USBPL301_BASE 0x402dc000 /* 16KB USB(PL301) */ -#define IMXRT_USB_BASE 0x402e0000 /* 16KB USB(USB) */ +#define IMXRT_USB_BASE 0x402e0200 /* 16KB USB(USB) */ /* 0x402e4000 16KB Reserved */ diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_clockconfig.c b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_clockconfig.c index b0aa5d62d..97fef0a96 100644 --- a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_clockconfig.c +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_clockconfig.c @@ -250,14 +250,14 @@ static void imxrt_pllsetup(void) CCM_ANALOG_PFD_480_PFD3_CLKGATE, IMXRT_CCM_ANALOG_PFD_480); - reg = IMXRT_USB1_PLL_DIV_SELECT | - CCM_ANALOG_PLL_USB1_ENABLE | - CCM_ANALOG_PLL_USB1_EN_USB_CLKS | - CCM_ANALOG_PLL_USB1_POWER; - putreg32(reg, IMXRT_CCM_ANALOG_PLL_USB1); + reg = IMXRT_USB2_PLL_DIV_SELECT | + CCM_ANALOG_PLL_USB2_ENABLE | + CCM_ANALOG_PLL_USB2_EN_USB_CLKS | + CCM_ANALOG_PLL_USB2_POWER; + putreg32(reg, IMXRT_CCM_ANALOG_PLL_USB2); - while ((getreg32(IMXRT_CCM_ANALOG_PLL_USB1) & - CCM_ANALOG_PLL_USB1_LOCK) == 0) + while ((getreg32(IMXRT_CCM_ANALOG_PLL_USB2) & + CCM_ANALOG_PLL_USB2_LOCK) == 0) { } @@ -320,13 +320,13 @@ static void imxrt_pllsetup(void) CCM_ANALOG_PFD_480_PFD3_CLKGATE, IMXRT_CCM_ANALOG_PFD_480); - reg = CCM_ANALOG_PLL_USB1_DIV_SELECT(IMXRT_USB1_PLL_DIV_SELECT) | - CCM_ANALOG_PLL_USB1_ENABLE | CCM_ANALOG_PLL_USB1_EN_USB_CLKS | - CCM_ANALOG_PLL_USB1_POWER; - putreg32(reg, IMXRT_CCM_ANALOG_PLL_USB1); + reg = CCM_ANALOG_PLL_USB2_DIV_SELECT(IMXRT_USB2_PLL_DIV_SELECT) | + CCM_ANALOG_PLL_USB2_ENABLE | CCM_ANALOG_PLL_USB2_EN_USB_CLKS | + CCM_ANALOG_PLL_USB2_POWER; + putreg32(reg, IMXRT_CCM_ANALOG_PLL_USB2); - while ((getreg32(IMXRT_CCM_ANALOG_PLL_USB1) & - CCM_ANALOG_PLL_USB1_LOCK) == 0) + while ((getreg32(IMXRT_CCM_ANALOG_PLL_USB2) & + CCM_ANALOG_PLL_USB2_LOCK) == 0) { } diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_ehci.c b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_ehci.c index ff83ee178..e5b28c3f0 100644 --- a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_ehci.c +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_ehci.c @@ -5363,10 +5363,10 @@ FAR struct usbhost_connection_s *imxrt_ehci_initialize(int controller) /* Interrupt Configuration ************************************************/ - ret = irq_attach(IMXRT_IRQ_USBOTG1, imxrt_ehci_interrupt, NULL); + ret = irq_attach(IMXRT_IRQ_USBOTG2, imxrt_ehci_interrupt, NULL); if (ret != 0) { - usbhost_trace1(EHCI_TRACE1_IRQATTACH_FAILED, IMXRT_IRQ_USBOTG1); + usbhost_trace1(EHCI_TRACE1_IRQATTACH_FAILED, IMXRT_IRQ_USBOTG2); return NULL; } @@ -5378,7 +5378,7 @@ FAR struct usbhost_connection_s *imxrt_ehci_initialize(int controller) /* Enable interrupts at the interrupt controller */ - up_enable_irq(IMXRT_IRQ_USBOTG1); + up_enable_irq(IMXRT_IRQ_USBOTG2); /* Drive Vbus +5V (the smoke test) */ diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lowputc.c b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lowputc.c index 682d1fec9..3649ec4f7 100644 --- a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lowputc.c +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lowputc.c @@ -386,8 +386,8 @@ int imxrt_lpuart_configure(uint32_t base, } else { - if ((getreg32(IMXRT_CCM_ANALOG_PLL_USB1) & - CCM_ANALOG_PLL_USB1_DIV_SELECT_MASK) != 0) + if ((getreg32(IMXRT_CCM_ANALOG_PLL_USB2) & + CCM_ANALOG_PLL_USB2_DIV_SELECT_MASK) != 0) { pll3_div = 22; } diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpi2c.c b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpi2c.c index 76fa24cc4..20d8e1914 100644 --- a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpi2c.c +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpi2c.c @@ -1054,8 +1054,8 @@ static void imxrt_lpi2c_setclock(FAR struct imxrt_lpi2c_priv_s *priv, } else { - if ((getreg32(IMXRT_CCM_ANALOG_PLL_USB1) & - CCM_ANALOG_PLL_USB1_DIV_SELECT_MASK) != 0) + if ((getreg32(IMXRT_CCM_ANALOG_PLL_USB2) & + CCM_ANALOG_PLL_USB2_DIV_SELECT_MASK) != 0) { pll3_div = 22; } diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpspi.c b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpspi.c index ccf50c13f..3abe7d9d2 100644 --- a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpspi.c +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_lpspi.c @@ -702,8 +702,8 @@ static inline void imxrt_lpspi_master_set_delays( uint32_t clock_div_prescaler; uint32_t additional_scaler; - if ((getreg32(IMXRT_CCM_ANALOG_PLL_USB1) & - CCM_ANALOG_PLL_USB1_DIV_SELECT_MASK) != 0) + if ((getreg32(IMXRT_CCM_ANALOG_PLL_USB2) & + CCM_ANALOG_PLL_USB2_DIV_SELECT_MASK) != 0) { pll3_div = 22; } @@ -915,8 +915,8 @@ static uint32_t imxrt_lpspi_setfrequency(FAR struct spi_dev_s *dev, LPSPI_CR_MEN, 0); } - if ((getreg32(IMXRT_CCM_ANALOG_PLL_USB1) & - CCM_ANALOG_PLL_USB1_DIV_SELECT_MASK) != 0) + if ((getreg32(IMXRT_CCM_ANALOG_PLL_USB2) & + CCM_ANALOG_PLL_USB2_DIV_SELECT_MASK) != 0) { pll3_div = 22; } diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_usbdev.c b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_usbdev.c index 69f6f000b..3f0d6ff03 100644 --- a/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_usbdev.c +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/app_match_nuttx/nuttx/arch/arm/src/imxrt/imxrt_usbdev.c @@ -2879,7 +2879,7 @@ void arm_usbinitialize(void) /* Soft reset PHY and enable clock */ - putreg32(USBPHY_CTRL_SFTRST | USBPHY_CTRL_CLKGATE, IMXRT_USBPHY1_CTRL_CLR); + putreg32(USBPHY_CTRL_SFTRST | USBPHY_CTRL_CLKGATE, IMXRT_USBPHY2_CTRL_CLR); /* Disconnect device */ @@ -2898,7 +2898,7 @@ void arm_usbinitialize(void) * CCM_ANALOG_USBPHYx_PLL_480_CTRLn. */ - imxrt_putreg(0, IMXRT_USBPHY1_PWD); + imxrt_putreg(0, IMXRT_USBPHY2_PWD); /* Program the controller to be the USB device controller */ @@ -2907,8 +2907,8 @@ void arm_usbinitialize(void) /* Attach USB controller interrupt handler */ - irq_attach(IMXRT_IRQ_USBOTG1, imxrt_usbinterrupt, NULL); - up_enable_irq(IMXRT_IRQ_USBOTG1); + irq_attach(IMXRT_IRQ_USBOTG2, imxrt_usbinterrupt, NULL); + up_enable_irq(IMXRT_IRQ_USBOTG2); leave_critical_section(flags); @@ -2945,8 +2945,8 @@ void arm_usbuninitialize(void) /* Disable and detach IRQs */ - up_disable_irq(IMXRT_IRQ_USBOTG1); - irq_detach(IMXRT_IRQ_USBOTG1); + up_disable_irq(IMXRT_IRQ_USBOTG2); + irq_detach(IMXRT_IRQ_USBOTG2); /* Reset the controller */ @@ -2958,7 +2958,7 @@ void arm_usbuninitialize(void) /* Power down the PHY */ - imxrt_putreg(0xffffffff, IMXRT_USBPHY1_PWD); + imxrt_putreg(0xffffffff, IMXRT_USBPHY2_PWD); /* Stop clock * NOTE: This will interfere with USB OTG 2 and should probably be removed @@ -3016,7 +3016,7 @@ int usbdev_register(struct usbdevclass_driver_s *driver) { /* Enable USB controller interrupts */ - up_enable_irq(IMXRT_IRQ_USBOTG1); + up_enable_irq(IMXRT_IRQ_USBOTG2); } return ret; @@ -3051,7 +3051,7 @@ int usbdev_unregister(struct usbdevclass_driver_s *driver) /* Disable USB controller interrupts */ - up_disable_irq(IMXRT_IRQ_USBOTG1); + up_disable_irq(IMXRT_IRQ_USBOTG2); /* Unhook the driver */ From 59f9671c8cefe08306aac35bdbf942c6b5cab8b1 Mon Sep 17 00:00:00 2001 From: wgzAIIT <820906721@qq.com> Date: Fri, 1 Apr 2022 10:52:03 +0800 Subject: [PATCH 6/7] change sd defconfig --- .../aiit_board/xidatong/configs/usdhc/defconfig | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/configs/usdhc/defconfig b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/configs/usdhc/defconfig index c22671e64..e961fdc9a 100644 --- a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/configs/usdhc/defconfig +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/configs/usdhc/defconfig @@ -20,11 +20,6 @@ CONFIG_ARMV7M_ICACHE=y CONFIG_ARMV7M_USEBASEPRI=y CONFIG_BOARD_LOOPSPERMSEC=104926 CONFIG_BUILTIN=y -CONFIG_DEBUG_FEATURES=y -CONFIG_DEBUG_FULLOPT=y -CONFIG_DEBUG_SYMBOLS=y -CONFIG_DEV_URANDOM=y -CONFIG_DEV_ZERO=y CONFIG_FAT_LCNAMES=y CONFIG_CLOCK_MONOTONIC=y CONFIG_FAT_LFN=y @@ -32,7 +27,6 @@ CONFIG_FS_FAT=y CONFIG_FS_PROCFS=y CONFIG_IDLETHREAD_STACKSIZE=2048 CONFIG_EXAMPLES_HELLO=y -CONFIG_IMXRT1020_EVK_QSPI_FLASH=y CONFIG_IMXRT_GPIO1_0_15_IRQ=y CONFIG_IMXRT_GPIO_IRQ=y CONFIG_IMXRT_LPUART1=y @@ -42,9 +36,7 @@ CONFIG_INTELHEX_BINARY=y CONFIG_IOB_NBUFFERS=24 CONFIG_IOB_NCHAINS=8 CONFIG_LIBC_STRERROR=y -CONFIG_LPUART1_RXBUFSIZE=1024 CONFIG_LPUART1_SERIAL_CONSOLE=y -CONFIG_LPUART1_TXBUFSIZE=1024 CONFIG_MMCSD=y CONFIG_MMCSD_SDIO=y CONFIG_MM_IOB=y From ea4e901650f55725b3e80a56ecb06987e8843ca5 Mon Sep 17 00:00:00 2001 From: wgzAIIT <820906721@qq.com> Date: Fri, 1 Apr 2022 11:07:57 +0800 Subject: [PATCH 7/7] add usbnsh/defconfig --- .../configs/{usdhc => sdionsh}/defconfig | 0 .../xidatong/configs/usbnsh/defconfig | 66 +++++++++++++++++++ 2 files changed, 66 insertions(+) rename Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/configs/{usdhc => sdionsh}/defconfig (100%) create mode 100644 Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/configs/usbnsh/defconfig diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/configs/usdhc/defconfig b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/configs/sdionsh/defconfig similarity index 100% rename from Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/configs/usdhc/defconfig rename to Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/configs/sdionsh/defconfig diff --git a/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/configs/usbnsh/defconfig b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/configs/usbnsh/defconfig new file mode 100644 index 000000000..854891e53 --- /dev/null +++ b/Ubiquitous/Nuttx_Fusion_XiUOS/aiit_board/xidatong/configs/usbnsh/defconfig @@ -0,0 +1,66 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +CONFIG_ADD_NUTTX_FETURES=y +CONFIG_ARCH="arm" +CONFIG_ARCH_BOARD="xidatong" +CONFIG_ARCH_BOARD_XIDATONG=y +CONFIG_ARCH_CHIP="imxrt" +CONFIG_ARCH_CHIP_IMXRT=y +CONFIG_ARCH_CHIP_MIMXRT1052CVL5B=y +CONFIG_ARCH_INTERRUPTSTACK=10240 +CONFIG_ARCH_STACKDUMP=y +CONFIG_ARMV7M_DCACHE=y +CONFIG_ARMV7M_DCACHE_WRITETHROUGH=y +CONFIG_ARMV7M_ICACHE=y +CONFIG_ARMV7M_USEBASEPRI=y +CONFIG_BOARD_LOOPSPERMSEC=104926 +CONFIG_BUILTIN=y +CONFIG_FAT_LCNAMES=y +CONFIG_CLOCK_MONOTONIC=y +CONFIG_FAT_LFN=y +CONFIG_FS_FAT=y +CONFIG_FS_PROCFS=y +CONFIG_IDLETHREAD_STACKSIZE=2048 +CONFIG_EXAMPLES_HELLO=y +CONFIG_IMXRT_GPIO1_0_15_IRQ=y +CONFIG_IMXRT_GPIO_IRQ=y +CONFIG_IMXRT_LPUART1=y +CONFIG_INTELHEX_BINARY=y +CONFIG_IOB_NBUFFERS=24 +CONFIG_IOB_NCHAINS=8 +CONFIG_LIBC_STRERROR=y +CONFIG_LPUART1_SERIAL_CONSOLE=y +CONFIG_MM_IOB=y +CONFIG_NSH_ARCHINIT=y +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_CMDOPT_DD_STATS=y +CONFIG_NSH_DISABLE_IFUPDOWN=y +CONFIG_NSH_FILEIOSIZE=512 +CONFIG_NSH_LINELEN=64 +CONFIG_NSH_READLINE=y +CONFIG_RAM_SIZE=524288 +CONFIG_RAM_START=0x20200000 +CONFIG_RAW_BINARY=y +CONFIG_SCHED_CHILD_STATUS=y +CONFIG_SCHED_HAVE_PARENT=y +CONFIG_SCHED_HPWORK=y +CONFIG_SCHED_LPWORK=y +CONFIG_SCHED_WAITPID=y +CONFIG_SERIAL_TERMIOS=y +CONFIG_START_DAY=14 +CONFIG_START_MONTH=3 +CONFIG_SYSTEM_CLE_CMD_HISTORY=y +CONFIG_SYSTEM_COLOR_CLE=y +CONFIG_SYSTEM_NSH=y +CONFIG_IMXRT_USBOTG=y +CONFIG_IMXRT_USBDEV=y +CONFIG_USBDEV=y +CONFIG_USBHOST=y +CONFIG_USBHOST_MSC=y +CONFIG_USBHOST_MSC_NOTIFIER=y +CONFIG_USER_ENTRYPOINT="nsh_main"