From 8708cddde16863c86d15b8781db40739d64ff4bb Mon Sep 17 00:00:00 2001 From: Lok Tep Date: Thu, 16 Jun 2016 13:44:14 +0200 Subject: [PATCH 01/24] mmc1 copy + dcache --- arch/arm/src/stm32f7/Make.defs | 4 + arch/arm/src/stm32f7/chip/stm32_sdmmc.h | 52 + .../src/stm32f7/chip/stm32f74xx75xx_sdmmc.h | 268 ++ arch/arm/src/stm32f7/stm32_sdmmc.c | 2977 +++++++++++++++++ arch/arm/src/stm32f7/stm32_sdmmc.h | 129 + configs/stm32f746-ws/include/board.h | 42 + configs/stm32f746-ws/nsh/defconfig | 41 +- configs/stm32f746-ws/src/stm32_spi.c | 12 +- 8 files changed, 3507 insertions(+), 18 deletions(-) create mode 100644 arch/arm/src/stm32f7/chip/stm32_sdmmc.h create mode 100644 arch/arm/src/stm32f7/chip/stm32f74xx75xx_sdmmc.h create mode 100644 arch/arm/src/stm32f7/stm32_sdmmc.c create mode 100644 arch/arm/src/stm32f7/stm32_sdmmc.h diff --git a/arch/arm/src/stm32f7/Make.defs b/arch/arm/src/stm32f7/Make.defs index 07aa380ebde..123c70ffc2f 100644 --- a/arch/arm/src/stm32f7/Make.defs +++ b/arch/arm/src/stm32f7/Make.defs @@ -147,6 +147,10 @@ ifeq ($(CONFIG_STM32F7_SPI),y) CHIP_CSRCS += stm32_spi.c endif +ifeq ($(CONFIG_STM32F7_SDMMC1),y) +CHIP_CSRCS += stm32_sdmmc.c +endif + ifeq ($(CONFIG_STM32F7_TIM),y) CHIP_CSRCS += stm32_tim.c endif diff --git a/arch/arm/src/stm32f7/chip/stm32_sdmmc.h b/arch/arm/src/stm32f7/chip/stm32_sdmmc.h new file mode 100644 index 00000000000..e04bdddc1e6 --- /dev/null +++ b/arch/arm/src/stm32f7/chip/stm32_sdmmc.h @@ -0,0 +1,52 @@ +/************************************************************************************ + * arch/arm/src/stm32f7/chip/stm32_i2c.h + * + * Copyright (C) 2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32F7_CHIP_STM32_SDMMC_H +#define __ARCH_ARM_SRC_STM32F7_CHIP_STM32_SDMMC_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include +#include "chip.h" + +#if defined(CONFIG_STM32F7_STM32F74XX) || defined(CONFIG_STM32F7_STM32F75XX) +# include "stm32f74xx75xx_sdmmc.h" +#else +# error "Unsupported STM32 F7 part" +#endif + +#endif /* __ARCH_ARM_SRC_STM32F7_CHIP_STM32_SDMMC_H */ diff --git a/arch/arm/src/stm32f7/chip/stm32f74xx75xx_sdmmc.h b/arch/arm/src/stm32f7/chip/stm32f74xx75xx_sdmmc.h new file mode 100644 index 00000000000..e40990ec16a --- /dev/null +++ b/arch/arm/src/stm32f7/chip/stm32f74xx75xx_sdmmc.h @@ -0,0 +1,268 @@ +/************************************************************************************ + * arch/arm/src/stm32/chip/stm32_sdio.h + * + * Copyright (C) 2009, 2011-2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32F7_CHIP_STM32F74XX75XX_SDMMC_H +#define __ARCH_ARM_SRC_STM32F7_CHIP_STM32F74XX75XX_SDMMC_H + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +/* Register Offsets *****************************************************************/ + +#define STM32_SDMMC1_POWER_OFFSET 0x0000 /* SDIO power control register */ +#define STM32_SDMMC1_CLKCR_OFFSET 0x0004 /* SDI clock control register */ +#define STM32_SDMMC1_ARG_OFFSET 0x0008 /* SDIO argument register */ +#define STM32_SDMMC1_CMD_OFFSET 0x000c /* SDIO command register */ +#define STM32_SDMMC1_RESPCMD_OFFSET 0x0010 /* SDIO command response register */ +#define STM32_SDMMC1_RESP_OFFSET(n) (0x0010+4*(n)) +#define STM32_SDMMC1_RESP1_OFFSET 0x0014 /* SDIO response 1 register */ +#define STM32_SDMMC1_RESP2_OFFSET 0x0018 /* SDIO response 2 register */ +#define STM32_SDMMC1_RESP3_OFFSET 0x001c /* SDIO response 3 register */ +#define STM32_SDMMC1_RESP4_OFFSET 0x0020 /* SDIO response 4 register */ +#define STM32_SDMMC1_DTIMER_OFFSET 0x0024 /* SDIO data timer register */ +#define STM32_SDMMC1_DLEN_OFFSET 0x0028 /* SDIO data length register */ +#define STM32_SDMMC1_DCTRL_OFFSET 0x002c /* SDIO data control register */ +#define STM32_SDMMC1_DCOUNT_OFFSET 0x0030 /* SDIO data counter register */ +#define STM32_SDMMC1_STA_OFFSET 0x0034 /* SDIO status register */ +#define STM32_SDMMC1_ICR_OFFSET 0x0038 /* SDIO interrupt clear register */ +#define STM32_SDMMC1_MASK_OFFSET 0x003c /* SDIO mask register */ +#define STM32_SDMMC1_FIFOCNT_OFFSET 0x0048 /* SDIO FIFO counter register */ +#define STM32_SDMMC1_FIFO_OFFSET 0x0080 /* SDIO data FIFO register */ + +/* Register Addresses ***************************************************************/ + +#define STM32_SDMMC1_POWER (STM32_SDMMC1_BASE+STM32_SDMMC1_POWER_OFFSET) +#define STM32_SDMMC1_CLKCR (STM32_SDMMC1_BASE+STM32_SDMMC1_CLKCR_OFFSET) +#define STM32_SDMMC1_ARG (STM32_SDMMC1_BASE+STM32_SDMMC1_ARG_OFFSET) +#define STM32_SDMMC1_CMD (STM32_SDMMC1_BASE+STM32_SDMMC1_CMD_OFFSET) +#define STM32_SDMMC1_RESPCMD (STM32_SDMMC1_BASE+STM32_SDMMC1_RESPCMD_OFFSET) +#define STM32_SDMMC1_RESP(n) (STM32_SDMMC1_BASE+STM32_SDMMC1_RESP_OFFSET(n)) +#define STM32_SDMMC1_RESP1 (STM32_SDMMC1_BASE+STM32_SDMMC1_RESP1_OFFSET) +#define STM32_SDMMC1_RESP2 (STM32_SDMMC1_BASE+STM32_SDMMC1_RESP2_OFFSET) +#define STM32_SDMMC1_RESP3 (STM32_SDMMC1_BASE+STM32_SDMMC1_RESP3_OFFSET) +#define STM32_SDMMC1_RESP4 (STM32_SDMMC1_BASE+STM32_SDMMC1_RESP4_OFFSET) +#define STM32_SDMMC1_DTIMER (STM32_SDMMC1_BASE+STM32_SDMMC1_DTIMER_OFFSET) +#define STM32_SDMMC1_DLEN (STM32_SDMMC1_BASE+STM32_SDMMC1_DLEN_OFFSET) +#define STM32_SDMMC1_DCTRL (STM32_SDMMC1_BASE+STM32_SDMMC1_DCTRL_OFFSET) +#define STM32_SDMMC1_DCOUNT (STM32_SDMMC1_BASE+STM32_SDMMC1_DCOUNT_OFFSET) +#define STM32_SDMMC1_STA (STM32_SDMMC1_BASE+STM32_SDMMC1_STA_OFFSET) +#define STM32_SDMMC1_ICR (STM32_SDMMC1_BASE+STM32_SDMMC1_ICR_OFFSET) +#define STM32_SDMMC1_MASK (STM32_SDMMC1_BASE+STM32_SDMMC1_MASK_OFFSET) +#define STM32_SDMMC1_FIFOCNT (STM32_SDMMC1_BASE+STM32_SDMMC1_FIFOCNT_OFFSET) +#define STM32_SDMMC1_FIFO (STM32_SDMMC1_BASE+STM32_SDMMC1_FIFO_OFFSET) + + +/* Register Bitfield Definitions ****************************************************/ + +#define SDIO_POWER_PWRCTRL_SHIFT (0) /* Bits 0-1: Power supply control bits */ +#define SDIO_POWER_PWRCTRL_MASK (3 << SDIO_POWER_PWRCTRL_SHIFT) +# define SDIO_POWER_PWRCTRL_OFF (0 << SDIO_POWER_PWRCTRL_SHIFT) /* 00: Power-off: card clock stopped */ +# define SDIO_POWER_PWRCTRL_PWRUP (2 << SDIO_POWER_PWRCTRL_SHIFT) /* 10: Reserved power-up */ +# define SDIO_POWER_PWRCTRL_ON (3 << SDIO_POWER_PWRCTRL_SHIFT) /* 11: Power-on: card is clocked */ + +#define SDIO_POWER_RESET (0) /* Reset value */ + +#define SDIO_CLKCR_CLKDIV_SHIFT (0) /* Bits 7-0: Clock divide factor */ +#define SDIO_CLKCR_CLKDIV_MASK (0xff << SDIO_CLKCR_CLKDIV_SHIFT) +#define SDIO_CLKCR_CLKEN (1 << 8) /* Bit 8: Clock enable bit */ +#define SDIO_CLKCR_PWRSAV (1 << 9) /* Bit 9: Power saving configuration bit */ +#define SDIO_CLKCR_BYPASS (1 << 10) /* Bit 10: Clock divider bypass enable bit */ +#define SDIO_CLKCR_WIDBUS_SHIFT (11) /* Bits 12-11: Wide bus mode enable bits */ +#define SDIO_CLKCR_WIDBUS_MASK (3 << SDIO_CLKCR_WIDBUS_SHIFT) +# define SDIO_CLKCR_WIDBUS_D1 (0 << SDIO_CLKCR_WIDBUS_SHIFT) /* 00: Default (SDIO_D0) */ +# define SDIO_CLKCR_WIDBUS_D4 (1 << SDIO_CLKCR_WIDBUS_SHIFT) /* 01: 4-wide (SDIO_D[3:0]) */ +# define SDIO_CLKCR_WIDBUS_D8 (2 << SDIO_CLKCR_WIDBUS_SHIFT) /* 10: 8-wide (SDIO_D[7:0]) */ +#define SDIO_CLKCR_NEGEDGE (1 << 13) /* Bit 13: SDIO_CK dephasing selection bit */ +#define SDIO_CLKCR_HWFC_EN (1 << 14) /* Bit 14: HW Flow Control enable */ + +#define SDIO_CLKCR_RESET (0) /* Reset value */ +#define SDIO_ARG_RESET (0) /* Reset value */ + +#define SDIO_CLKCR_CLKEN_BB (STM32_SDMMC1_CLKCR_BB + (8 * 4)) +#define SDIO_CLKCR_PWRSAV_BB (STM32_SDMMC1_CLKCR_BB + (9 * 4)) +#define SDIO_CLKCR_BYPASS_BB (STM32_SDMMC1_CLKCR_BB + (10 * 4)) +#define SDIO_CLKCR_NEGEDGE_BB (STM32_SDMMC1_CLKCR_BB + (13 * 4)) +#define SDIO_CLKCR_HWFC_EN_BB (STM32_SDMMC1_CLKCR_BB + (14 * 4)) + +#define SDIO_CMD_CMDINDEX_SHIFT (0) +#define SDIO_CMD_CMDINDEX_MASK (0x3f << SDIO_CMD_CMDINDEX_SHIFT) +#define SDIO_CMD_WAITRESP_SHIFT (6) /* Bits 7-6: Wait for response bits */ +#define SDIO_CMD_WAITRESP_MASK (3 << SDIO_CMD_WAITRESP_SHIFT) +# define SDIO_CMD_NORESPONSE (0 << SDIO_CMD_WAITRESP_SHIFT) /* 00/10: No response */ +# define SDIO_CMD_SHORTRESPONSE (1 << SDIO_CMD_WAITRESP_SHIFT) /* 01: Short response */ +# define SDIO_CMD_LONGRESPONSE (3 << SDIO_CMD_WAITRESP_SHIFT) /* 11: Long response */ +#define SDIO_CMD_WAITINT (1 << 8) /* Bit 8: CPSM waits for interrupt request */ +#define SDIO_CMD_WAITPEND (1 << 9) /* Bit 9: CPSM Waits for ends of data transfer */ +#define SDIO_CMD_CPSMEN (1 << 10) /* Bit 10: Command path state machine enable */ +#define SDIO_CMD_SUSPEND (1 << 11) /* Bit 11: SD I/O suspend command */ +#define SDIO_CMD_ENDCMD (1 << 12) /* Bit 12: Enable CMD completion */ +#define SDIO_CMD_NIEN (1 << 13) /* Bit 13: not Interrupt Enable */ +#define SDIO_CMD_ATACMD (1 << 14) /* Bit 14: CE-ATA command */ + +#define SDIO_CMD_RESET (0) /* Reset value */ + +#define SDIO_CMD_WAITINT_BB (STM32_SDMMC1_CMD_BB + (8 * 4)) +#define SDIO_CMD_WAITPEND_BB (STM32_SDMMC1_CMD_BB + (9 * 4)) +#define SDIO_CMD_CPSMEN_BB (STM32_SDMMC1_CMD_BB + (10 * 4)) +#define SDIO_CMD_SUSPEND_BB (STM32_SDMMC1_CMD_BB + (11 * 4)) +#define SDIO_CMD_ENCMD_BB (STM32_SDMMC1_CMD_BB + (12 * 4)) +#define SDIO_CMD_NIEN_BB (STM32_SDMMC1_CMD_BB + (13 * 4)) +#define SDIO_CMD_ATACMD_BB (STM32_SDMMC1_CMD_BB + (14 * 4)) + +#define SDIO_RESPCMD_SHIFT (0) +#define SDIO_RESPCMD_MASK (0x3f << SDIO_RESPCMD_SHIFT) + +#define SDIO_DTIMER_RESET (0) /* Reset value */ + +#define SDIO_DLEN_SHIFT (0) +#define SDIO_DLEN_MASK (0x01ffffff << SDIO_DLEN_SHIFT) + +#define SDIO_DLEN_RESET (0) /* Reset value */ + +#define SDIO_DCTRL_DTEN (1 << 0) /* Bit 0: Data transfer enabled bit */ +#define SDIO_DCTRL_DTDIR (1 << 1) /* Bit 1: Data transfer direction */ +#define SDIO_DCTRL_DTMODE (1 << 2) /* Bit 2: Data transfer mode */ +#define SDIO_DCTRL_DMAEN (1 << 3) /* Bit 3: DMA enable bit */ +#define SDIO_DCTRL_DBLOCKSIZE_SHIFT (4) /* Bits 7-4: Data block size */ +#define SDIO_DCTRL_DBLOCKSIZE_MASK (15 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) +# define SDIO_DCTRL_1BYTE (0 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) +# define SDIO_DCTRL_2BYTES (1 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) +# define SDIO_DCTRL_4BYTES (2 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) +# define SDIO_DCTRL_8BYTES (3 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) +# define SDIO_DCTRL_16BYTES (4 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) +# define SDIO_DCTRL_32BYTES (5 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) +# define SDIO_DCTRL_64BYTES (6 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) +# define SDIO_DCTRL_128BYTES (7 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) +# define SDIO_DCTRL_256BYTES (8 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) +# define SDIO_DCTRL_512BYTES (9 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) +# define SDIO_DCTRL_1KBYTE (10 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) +# define SDIO_DCTRL_2KBYTES (11 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) +# define SDIO_DCTRL_4KBYTES (12 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) +# define SDIO_DCTRL_8KBYTES (13 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) +# define SDIO_DCTRL_16KBYTES (14 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) +#define SDIO_DCTRL_RWSTART (1 << 8) /* Bit 8: Read wait start */ +#define SDIO_DCTRL_RWSTOP (1 << 9) /* Bit 9: Read wait stop */ +#define SDIO_DCTRL_RWMOD (1 << 10) /* Bit 10: Read wait mode */ +#define SDIO_DCTRL_SDIOEN (1 << 11) /* Bit 11: SD I/O enable functions */ + +#define SDIO_DCTRL_RESET (0) /* Reset value */ + +#define SDIO_DCTRL_DTEN_BB (STM32_SDMMC1_DCTRL_BB + (0 * 4)) +#define SDIO_DCTRL_DTDIR_BB (STM32_SDMMC1_DCTRL_BB + (1 * 4)) +#define SDIO_DCTRL_DTMODE_BB (STM32_SDMMC1_DCTRL_BB + (2 * 4)) +#define SDIO_DCTRL_DMAEN_BB (STM32_SDMMC1_DCTRL_BB + (3 * 4)) +#define SDIO_DCTRL_RWSTART_BB (STM32_SDMMC1_DCTRL_BB + (8 * 4)) +#define SDIO_DCTRL_RWSTOP_BB (STM32_SDMMC1_DCTRL_BB + (9 * 4)) +#define SDIO_DCTRL_RWMOD_BB (STM32_SDMMC1_DCTRL_BB + (10 * 4)) +#define SDIO_DCTRL_SDIOEN_BB (STM32_SDMMC1_DCTRL_BB + (11 * 4)) + +#define SDIO_DATACOUNT_SHIFT (0) +#define SDIO_DATACOUNT_MASK (0x01ffffff << SDIO_DATACOUNT_SHIFT) + +#define SDIO_STA_CCRCFAIL (1 << 0) /* Bit 0: Command response CRC fail */ +#define SDIO_STA_DCRCFAIL (1 << 1) /* Bit 1: Data block CRC fail */ +#define SDIO_STA_CTIMEOUT (1 << 2) /* Bit 2: Command response timeout */ +#define SDIO_STA_DTIMEOUT (1 << 3) /* Bit 3: Data timeout */ +#define SDIO_STA_TXUNDERR (1 << 4) /* Bit 4: Transmit FIFO underrun error */ +#define SDIO_STA_RXOVERR (1 << 5) /* Bit 5: Received FIFO overrun error */ +#define SDIO_STA_CMDREND (1 << 6) /* Bit 6: Command response received */ +#define SDIO_STA_CMDSENT (1 << 7) /* Bit 7: Command sent */ +#define SDIO_STA_DATAEND (1 << 8) /* Bit 8: Data end */ +#define SDIO_STA_STBITERR (1 << 9) /* Bit 9: Start bit not detected */ +#define SDIO_STA_DBCKEND (1 << 10) /* Bit 10: Data block sent/received */ +#define SDIO_STA_CMDACT (1 << 11) /* Bit 11: Command transfer in progress */ +#define SDIO_STA_TXACT (1 << 12) /* Bit 12: Data transmit in progress */ +#define SDIO_STA_RXACT (1 << 13) /* Bit 13: Data receive in progress */ +#define SDIO_STA_TXFIFOHE (1 << 14) /* Bit 14: Transmit FIFO half empty */ +#define SDIO_STA_RXFIFOHF (1 << 15) /* Bit 15: Receive FIFO half full */ +#define SDIO_STA_TXFIFOF (1 << 16) /* Bit 16: Transmit FIFO full */ +#define SDIO_STA_RXFIFOF (1 << 17) /* Bit 17: Receive FIFO full */ +#define SDIO_STA_TXFIFOE (1 << 18) /* Bit 18: Transmit FIFO empty */ +#define SDIO_STA_RXFIFOE (1 << 19) /* Bit 19: Receive FIFO empty */ +#define SDIO_STA_TXDAVL (1 << 20) /* Bit 20: Data available in transmit FIFO */ +#define SDIO_STA_RXDAVL (1 << 21) /* Bit 21: Data available in receive FIFO */ +#define SDIO_STA_SDIOIT (1 << 22) /* Bit 22: SDIO interrupt received */ +#define SDIO_STA_CEATAEND (1 << 23) /* Bit 23: CMD6 CE-ATA command completion */ + +#define SDIO_ICR_CCRCFAILC (1 << 0) /* Bit 0: CCRCFAIL flag clear bit */ +#define SDIO_ICR_DCRCFAILC (1 << 1) /* Bit 1: DCRCFAIL flag clear bit */ +#define SDIO_ICR_CTIMEOUTC (1 << 2) /* Bit 2: CTIMEOUT flag clear bit */ +#define SDIO_ICR_DTIMEOUTC (1 << 3) /* Bit 3: DTIMEOUT flag clear bit */ +#define SDIO_ICR_TXUNDERRC (1 << 4) /* Bit 4: TXUNDERR flag clear bit */ +#define SDIO_ICR_RXOVERRC (1 << 5) /* Bit 5: RXOVERR flag clear bit */ +#define SDIO_ICR_CMDRENDC (1 << 6) /* Bit 6: CMDREND flag clear bit */ +#define SDIO_ICR_CMDSENTC (1 << 7) /* Bit 7: CMDSENT flag clear bit */ +#define SDIO_ICR_DATAENDC (1 << 8) /* Bit 8: DATAEND flag clear bit */ +#define SDIO_ICR_STBITERRC (1 << 9) /* Bit 9: STBITERR flag clear bit */ +#define SDIO_ICR_DBCKENDC (1 << 10) /* Bit 10: DBCKEND flag clear bit */ +#define SDIO_ICR_SDIOITC (1 << 22) /* Bit 22: SDIOIT flag clear bit */ +#define SDIO_ICR_CEATAENDC (1 << 23) /* Bit 23: CEATAEND flag clear bit */ + +#define SDIO_ICR_RESET 0x00c007ff +#define SDIO_ICR_STATICFLAGS 0x000005ff + +#define SDIO_MASK_CCRCFAILIE (1 << 0) /* Bit 0: Command CRC fail interrupt enable */ +#define SDIO_MASK_DCRCFAILIE (1 << 1) /* Bit 1: Data CRC fail interrupt enable */ +#define SDIO_MASK_CTIMEOUTIE (1 << 2) /* Bit 2: Command timeout interrupt enable */ +#define SDIO_MASK_DTIMEOUTIE (1 << 3) /* Bit 3: Data timeout interrupt enable */ +#define SDIO_MASK_TXUNDERRIE (1 << 4) /* Bit 4: Tx FIFO underrun error interrupt enable */ +#define SDIO_MASK_RXOVERRIE (1 << 5) /* Bit 5: Rx FIFO overrun error interrupt enable */ +#define SDIO_MASK_CMDRENDIE (1 << 6) /* Bit 6: Command response received interrupt enable */ +#define SDIO_MASK_CMDSENTIE (1 << 7) /* Bit 7: Command sent interrupt enable */ +#define SDIO_MASK_DATAENDIE (1 << 8) /* Bit 8: Data end interrupt enable */ +#define SDIO_MASK_STBITERRIE (1 << 9) /* Bit 9: Start bit error interrupt enable */ +#define SDIO_MASK_DBCKENDIE (1 << 10) /* Bit 10: Data block end interrupt enable */ +#define SDIO_MASK_CMDACTIE (1 << 11) /* Bit 11: Command acting interrupt enable */ +#define SDIO_MASK_TXACTIE (1 << 12) /* Bit 12: Data transmit acting interrupt enable */ +#define SDIO_MASK_RXACTIE (1 << 13) /* Bit 13: Data receive acting interrupt enable */ +#define SDIO_MASK_TXFIFOHEIE (1 << 14) /* Bit 14: Tx FIFO half empty interrupt enable */ +#define SDIO_MASK_RXFIFOHFIE (1 << 15) /* Bit 15: Rx FIFO half full interrupt enable */ +#define SDIO_MASK_TXFIFOFIE (1 << 16) /* Bit 16: Tx FIFO full interrupt enable */ +#define SDIO_MASK_RXFIFOFIE (1 << 17) /* Bit 17: Rx FIFO full interrupt enable */ +#define SDIO_MASK_TXFIFOEIE (1 << 18) /* Bit 18: Tx FIFO empty interrupt enable */ +#define SDIO_MASK_RXFIFOEIE (1 << 19) /* Bit 19: Rx FIFO empty interrupt enable */ +#define SDIO_MASK_TXDAVLIE (1 << 20) /* Bit 20: Data available in Tx FIFO interrupt enable */ +#define SDIO_MASK_RXDAVLIE (1 << 21) /* Bit 21: Data available in Rx FIFO interrupt enable */ +#define SDIO_MASK_SDIOITIE (1 << 22) /* Bit 22: SDIO mode interrupt received interrupt enable */ +#define SDIO_MASK_CEATAENDIE (1 << 23) /* Bit 23: CE-ATA command completion interrupt enable */ + +#define SDIO_MASK_RESET (0) + +#define SDIO_FIFOCNT_SHIFT (0) +#define SDIO_FIFOCNT_MASK (0x01ffffff << SDIO_FIFOCNT_SHIFT) + +#endif /* __ARCH_ARM_SRC_STM32F7_CHIP_STM32F74XX75XX_SDMMC_H */ + diff --git a/arch/arm/src/stm32f7/stm32_sdmmc.c b/arch/arm/src/stm32f7/stm32_sdmmc.c new file mode 100644 index 00000000000..58f7b7318df --- /dev/null +++ b/arch/arm/src/stm32f7/stm32_sdmmc.c @@ -0,0 +1,2977 @@ +/**************************************************************************** + * arch/arm/src/stm32f7/stm32_sdio.c + * + * Copyright (C) 2009, 2011-2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "stm32_sdmmc.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "cache.h" +#include "chip.h" +#include "up_arch.h" + +#include "stm32_dma.h" +#include "stm32_gpio.h" + + +/* TODO STM32F7_SDMMC2 + */ + +#ifdef CONFIG_STM32F7_SDMMC1 + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ +/* Required system configuration options: + * + * CONFIG_ARCH_DMA - Enable architecture-specific DMA subsystem + * initialization. Required if CONFIG_SDIO_DMA is enabled. + * CONFIG_STM32F7_DMA2 - Enable STM32 DMA2 support. Required if + * CONFIG_SDIO_DMA is enabled + * CONFIG_SCHED_WORKQUEUE -- Callback support requires work queue support. + * + * Driver-specific configuration options: + * + * CONFIG_SDIO_MUXBUS - Setting this configuration enables some locking + * APIs to manage concurrent accesses on the SDIO bus. This is not + * needed for the simple case of a single SD card, for example. + * CONFIG_SDIO_DMA - Enable SDIO. This is a marginally optional. For + * most usages, SDIO will cause data overruns if used without DMA. + * NOTE the above system DMA configuration options. + * CONFIG_SDIO_WIDTH_D1_ONLY - This may be selected to force the driver + * operate with only a single data line (the default is to use all + * 4 SD data lines). + * CONFIG_SDIO_PRI - SDIO interrupt priority. This setting is not very + * important since interrupt nesting is not currently supported. + * CONFIG_SDM_DMAPRIO - SDIO DMA priority. This can be selecte if + * CONFIG_SDIO_DMA is enabled. + * CONFIG_SDIO_XFRDEBUG - Enables some very low-level debug output + * This also requires CONFIG_DEBUG_FS and CONFIG_DEBUG_INFO + */ + +#if defined(CONFIG_SDIO_DMA) && !defined(CONFIG_STM32F7_DMA2) +# warning "CONFIG_SDIO_DMA support requires CONFIG_STM32F7_DMA2" +#endif + +#ifndef CONFIG_SDIO_DMA +# warning "Large Non-DMA transfer may result in RX overrun failures" +#endif + +#ifndef CONFIG_SCHED_WORKQUEUE +# error "Callback support requires CONFIG_SCHED_WORKQUEUE" +#endif + +#ifndef CONFIG_SDIO_PRI +# define CONFIG_SDIO_PRI NVIC_SYSH_PRIORITY_DEFAULT +#endif + +#ifdef CONFIG_SDIO_DMA +# ifndef CONFIG_SDIO_DMAPRIO +# define CONFIG_SDIO_DMAPRIO DMA_SCR_PRIVERYHI +# endif +# if (CONFIG_SDIO_DMAPRIO & ~DMA_SCR_PL_MASK) != 0 +# error "Illegal value for CONFIG_SDIO_DMAPRIO" +# endif +#else +# undef CONFIG_SDIO_DMAPRIO +#endif + +#if !defined(CONFIG_DEBUG_FS) || !defined(CONFIG_DEBUG_FEATURES) +# undef CONFIG_SDIO_XFRDEBUG +#endif + +/* Friendly CLKCR bit re-definitions ****************************************/ + +#define SDIO_CLKCR_RISINGEDGE (0) +#define SDIO_CLKCR_FALLINGEDGE SDIO_CLKCR_NEGEDGE + +/* Mode dependent settings. These depend on clock devisor settings that must + * be defined in the board-specific board.h header file: SDIO_INIT_CLKDIV, + * SDIO_MMCXFR_CLKDIV, and SDIO_SDXFR_CLKDIV. + */ + +#define STM32_CLCKCR_INIT (SDMMC1_INIT_CLKDIV | SDIO_CLKCR_RISINGEDGE | \ + SDIO_CLKCR_WIDBUS_D1) +#define SDIO_CLKCR_MMCXFR (SDMMC1_MMCXFR_CLKDIV | SDIO_CLKCR_RISINGEDGE | \ + SDIO_CLKCR_WIDBUS_D1) +#define SDIO_CLCKR_SDXFR (SDMMC1_SDXFR_CLKDIV | SDIO_CLKCR_RISINGEDGE | \ + SDIO_CLKCR_WIDBUS_D1) +#define SDIO_CLCKR_SDWIDEXFR (SDMMC1_SDXFR_CLKDIV | SDIO_CLKCR_RISINGEDGE | \ + SDIO_CLKCR_WIDBUS_D4) + +/* Timing */ + +#define SDIO_CMDTIMEOUT (100000) +#define SDIO_LONGTIMEOUT (0x7fffffff) + +/* Big DTIMER setting */ + +#define SDIO_DTIMER_DATATIMEOUT (0x000fffff) + +/* DMA channel/stream configuration register settings. The following + * must be selected. The DMA driver will select the remaining fields. + * + * - 32-bit DMA + * - Memory increment + * - Direction (memory-to-peripheral, peripheral-to-memory) + * - Memory burst size (F4 only) + */ + + +/* STM32 stream configuration register (SCR) settings. */ + +#define SDIO_RXDMA32_CONFIG (DMA_SCR_PFCTRL | DMA_SCR_DIR_P2M|DMA_SCR_MINC | \ + DMA_SCR_PSIZE_32BITS | DMA_SCR_MSIZE_32BITS | \ + CONFIG_SDIO_DMAPRIO | DMA_SCR_PBURST_INCR4 | \ + DMA_SCR_MBURST_INCR4) +#define SDIO_TXDMA32_CONFIG (DMA_SCR_PFCTRL | DMA_SCR_DIR_M2P | DMA_SCR_MINC | \ + DMA_SCR_PSIZE_32BITS | DMA_SCR_MSIZE_32BITS | \ + CONFIG_SDIO_DMAPRIO | DMA_SCR_PBURST_INCR4 | \ + DMA_SCR_MBURST_INCR4) + +/* SDIO DMA Channel/Stream selection. There + * are multiple DMA stream options that must be dis-ambiguated in the board.h + * file. + */ + +#define SDIO_DMACHAN DMAMAP_SDMMC1 + +/* FIFO sizes */ + +#define SDIO_HALFFIFO_WORDS (8) +#define SDIO_HALFFIFO_BYTES (8*4) + +/* Data transfer interrupt mask bits */ + +#define SDIO_RECV_MASK (SDIO_MASK_DCRCFAILIE | SDIO_MASK_DTIMEOUTIE | \ + SDIO_MASK_DATAENDIE | SDIO_MASK_RXOVERRIE | \ + SDIO_MASK_RXFIFOHFIE | SDIO_MASK_STBITERRIE) +#define SDIO_SEND_MASK (SDIO_MASK_DCRCFAILIE | SDIO_MASK_DTIMEOUTIE | \ + SDIO_MASK_DATAENDIE | SDIO_MASK_TXUNDERRIE | \ + SDIO_MASK_TXFIFOHEIE | SDIO_MASK_STBITERRIE) +#define SDIO_DMARECV_MASK (SDIO_MASK_DCRCFAILIE | SDIO_MASK_DTIMEOUTIE | \ + SDIO_MASK_DATAENDIE | SDIO_MASK_RXOVERRIE | \ + SDIO_MASK_STBITERRIE) +#define SDIO_DMASEND_MASK (SDIO_MASK_DCRCFAILIE | SDIO_MASK_DTIMEOUTIE | \ + SDIO_MASK_DATAENDIE | SDIO_MASK_TXUNDERRIE | \ + SDIO_MASK_STBITERRIE) + +/* Event waiting interrupt mask bits */ + +#define SDIO_CMDDONE_STA (SDIO_STA_CMDSENT) +#define SDIO_RESPDONE_STA (SDIO_STA_CTIMEOUT | SDIO_STA_CCRCFAIL | \ + SDIO_STA_CMDREND) +#define SDIO_XFRDONE_STA (0) + +#define SDIO_CMDDONE_MASK (SDIO_MASK_CMDSENTIE) +#define SDIO_RESPDONE_MASK (SDIO_MASK_CCRCFAILIE | SDIO_MASK_CTIMEOUTIE | \ + SDIO_MASK_CMDRENDIE) +#define SDIO_XFRDONE_MASK (0) + +#define SDIO_CMDDONE_ICR (SDIO_ICR_CMDSENTC | SDIO_ICR_DBCKENDC) +#define SDIO_RESPDONE_ICR (SDIO_ICR_CTIMEOUTC | SDIO_ICR_CCRCFAILC | \ + SDIO_ICR_CMDRENDC | SDIO_ICR_DBCKENDC) +#define SDIO_XFRDONE_ICR (SDIO_ICR_DATAENDC | SDIO_ICR_DCRCFAILC | \ + SDIO_ICR_DTIMEOUTC | SDIO_ICR_RXOVERRC | \ + SDIO_ICR_TXUNDERRC | SDIO_ICR_STBITERRC | \ + SDIO_ICR_DBCKENDC) + +#define SDIO_WAITALL_ICR (SDIO_CMDDONE_ICR | SDIO_RESPDONE_ICR | \ + SDIO_XFRDONE_ICR | SDIO_ICR_DBCKENDC) + +/* Let's wait until we have both SDIO transfer complete and DMA complete. */ + +#define SDIO_XFRDONE_FLAG (1) +#define SDIO_DMADONE_FLAG (2) +#define SDIO_ALLDONE (3) + +/* Register logging support */ + +#ifdef CONFIG_SDIO_XFRDEBUG +# ifdef CONFIG_SDIO_DMA +# define SAMPLENDX_BEFORE_SETUP 0 +# define SAMPLENDX_BEFORE_ENABLE 1 +# define SAMPLENDX_AFTER_SETUP 2 +# define SAMPLENDX_END_TRANSFER 3 +# define SAMPLENDX_DMA_CALLBACK 4 +# define DEBUG_NSAMPLES 5 +# else +# define SAMPLENDX_BEFORE_SETUP 0 +# define SAMPLENDX_AFTER_SETUP 1 +# define SAMPLENDX_END_TRANSFER 2 +# define DEBUG_NSAMPLES 3 +# endif +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure defines the state of the STM32 SDIO interface */ + +struct stm32_dev_s +{ + struct sdio_dev_s dev; /* Standard, base SDIO interface */ + + /* STM32-specific extensions */ + /* Event support */ + + sem_t waitsem; /* Implements event waiting */ + sdio_eventset_t waitevents; /* Set of events to be waited for */ + uint32_t waitmask; /* Interrupt enables for event waiting */ + volatile sdio_eventset_t wkupevent; /* The event that caused the wakeup */ + WDOG_ID waitwdog; /* Watchdog that handles event timeouts */ + + /* Callback support */ + + uint8_t cdstatus; /* Card status */ + sdio_eventset_t cbevents; /* Set of events to be cause callbacks */ + worker_t callback; /* Registered callback function */ + void *cbarg; /* Registered callback argument */ + struct work_s cbwork; /* Callback work queue structure */ + + /* Interrupt mode data transfer support */ + + uint32_t *buffer; /* Address of current R/W buffer */ + size_t remaining; /* Number of bytes remaining in the transfer */ + uint32_t xfrmask; /* Interrupt enables for data transfer */ + + /* DMA data transfer support */ + + bool widebus; /* Required for DMA support */ +#ifdef CONFIG_SDIO_DMA + volatile uint8_t xfrflags; /* Used to synchronize SDIO and DMA completion events */ + bool dmamode; /* true: DMA mode transfer */ + DMA_HANDLE dma; /* Handle for DMA channel */ +#endif +}; + +/* Register logging support */ + +#ifdef CONFIG_SDIO_XFRDEBUG +struct stm32_sdioregs_s +{ + uint8_t power; + uint16_t clkcr; + uint16_t dctrl; + uint32_t dtimer; + uint32_t dlen; + uint32_t dcount; + uint32_t sta; + uint32_t mask; + uint32_t fifocnt; +}; + +struct stm32_sampleregs_s +{ + struct stm32_sdioregs_s sdio; +#if defined(CONFIG_DEBUG_DMA_INFO) && defined(CONFIG_SDIO_DMA) + struct stm32_dmaregs_s dma; +#endif +}; +#endif + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Low-level helpers ********************************************************/ + +static void stm32_takesem(struct stm32_dev_s *priv); +#define stm32_givesem(priv) (sem_post(&priv->waitsem)) +static inline void stm32_setclkcr(uint32_t clkcr); +static void stm32_configwaitints(struct stm32_dev_s *priv, uint32_t waitmask, + sdio_eventset_t waitevents, sdio_eventset_t wkupevents); +static void stm32_configxfrints(struct stm32_dev_s *priv, uint32_t xfrmask); +static void stm32_setpwrctrl(uint32_t pwrctrl); +static inline uint32_t stm32_getpwrctrl(void); + +/* DMA Helpers **************************************************************/ + +#ifdef CONFIG_SDIO_XFRDEBUG +static void stm32_sampleinit(void); +static void stm32_sdiosample(struct stm32_sdioregs_s *regs); +static void stm32_sample(struct stm32_dev_s *priv, int index); +static void stm32_sdiodump(struct stm32_sdioregs_s *regs, const char *msg); +static void stm32_dumpsample(struct stm32_dev_s *priv, + struct stm32_sampleregs_s *regs, const char *msg); +static void stm32_dumpsamples(struct stm32_dev_s *priv); +#else +# define stm32_sampleinit() +# define stm32_sample(priv,index) +# define stm32_dumpsamples(priv) +#endif + +#ifdef CONFIG_SDIO_DMA +static void stm32_dmacallback(DMA_HANDLE handle, uint8_t status, void *arg); +#endif + +/* Data Transfer Helpers ****************************************************/ + +static uint8_t stm32_log2(uint16_t value); +static void stm32_dataconfig(uint32_t timeout, uint32_t dlen, uint32_t dctrl); +static void stm32_datadisable(void); +static void stm32_sendfifo(struct stm32_dev_s *priv); +static void stm32_recvfifo(struct stm32_dev_s *priv); +static void stm32_eventtimeout(int argc, uint32_t arg); +static void stm32_endwait(struct stm32_dev_s *priv, sdio_eventset_t wkupevent); +static void stm32_endtransfer(struct stm32_dev_s *priv, sdio_eventset_t wkupevent); + +/* Interrupt Handling *******************************************************/ + +static int stm32_interrupt(int irq, void *context); +#ifdef CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE +static int stm32_rdyinterrupt(int irq, void *context); +#endif + +/* SDIO interface methods ***************************************************/ + +/* Mutual exclusion */ + +#ifdef CONFIG_SDIO_MUXBUS +static int stm32_lock(FAR struct sdio_dev_s *dev, bool lock); +#endif + +/* Initialization/setup */ + +static void stm32_reset(FAR struct sdio_dev_s *dev); +static uint8_t stm32_status(FAR struct sdio_dev_s *dev); +static void stm32_widebus(FAR struct sdio_dev_s *dev, bool enable); +static void stm32_clock(FAR struct sdio_dev_s *dev, + enum sdio_clock_e rate); +static int stm32_attach(FAR struct sdio_dev_s *dev); + +/* Command/Status/Data Transfer */ + +static int stm32_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t arg); +static int stm32_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer, + size_t nbytes); +static int stm32_sendsetup(FAR struct sdio_dev_s *dev, + FAR const uint8_t *buffer, uint32_t nbytes); +static int stm32_cancel(FAR struct sdio_dev_s *dev); + +static int stm32_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd); +static int stm32_recvshortcrc(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t *rshort); +static int stm32_recvlong(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t rlong[4]); +static int stm32_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t *rshort); +static int stm32_recvnotimpl(FAR struct sdio_dev_s *dev, uint32_t cmd, + uint32_t *rnotimpl); + +/* EVENT handler */ + +static void stm32_waitenable(FAR struct sdio_dev_s *dev, + sdio_eventset_t eventset); +static sdio_eventset_t + stm32_eventwait(FAR struct sdio_dev_s *dev, uint32_t timeout); +static void stm32_callbackenable(FAR struct sdio_dev_s *dev, + sdio_eventset_t eventset); +static int stm32_registercallback(FAR struct sdio_dev_s *dev, + worker_t callback, void *arg); + +/* DMA */ + +#ifdef CONFIG_SDIO_DMA +static bool stm32_dmasupported(FAR struct sdio_dev_s *dev); +#ifdef CONFIG_SDIO_PREFLIGHT +static int stm32_dmapreflight(FAR struct sdio_dev_s *dev, + FAR const uint8_t *buffer, size_t buflen); +#endif +static int stm32_dmarecvsetup(FAR struct sdio_dev_s *dev, + FAR uint8_t *buffer, size_t buflen); +static int stm32_dmasendsetup(FAR struct sdio_dev_s *dev, + FAR const uint8_t *buffer, size_t buflen); +#endif + +/* Initialization/uninitialization/reset ************************************/ + +static void stm32_callback(void *arg); +static void stm32_default(void); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +struct stm32_dev_s g_sdiodev = +{ + .dev = + { +#ifdef CONFIG_SDIO_MUXBUS + .lock = stm32_lock, +#endif + .reset = stm32_reset, + .status = stm32_status, + .widebus = stm32_widebus, + .clock = stm32_clock, + .attach = stm32_attach, + .sendcmd = stm32_sendcmd, +#ifdef CONFIG_SDIO_BLOCKSETUP + .blocksetup = stm32_blocksetup, /* Not implemented yet */ +#endif + .recvsetup = stm32_recvsetup, + .sendsetup = stm32_sendsetup, + .cancel = stm32_cancel, + .waitresponse = stm32_waitresponse, + .recvR1 = stm32_recvshortcrc, + .recvR2 = stm32_recvlong, + .recvR3 = stm32_recvshort, + .recvR4 = stm32_recvnotimpl, + .recvR5 = stm32_recvnotimpl, + .recvR6 = stm32_recvshortcrc, + .recvR7 = stm32_recvshort, + .waitenable = stm32_waitenable, + .eventwait = stm32_eventwait, + .callbackenable = stm32_callbackenable, + .registercallback = stm32_registercallback, +#ifdef CONFIG_SDIO_DMA + .dmasupported = stm32_dmasupported, +#ifdef CONFIG_SDIO_PREFLIGHT + .dmapreflight = stm32_dmapreflight, +#endif + .dmarecvsetup = stm32_dmarecvsetup, + .dmasendsetup = stm32_dmasendsetup, +#endif + }, +}; + +/* Register logging support */ + +#ifdef CONFIG_SDIO_XFRDEBUG +static struct stm32_sampleregs_s g_sampleregs[DEBUG_NSAMPLES]; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Low-level Helpers + ****************************************************************************/ +/**************************************************************************** + * Name: stm32_takesem + * + * Description: + * Take the wait semaphore (handling false alarm wakeups due to the receipt + * of signals). + * + * Input Parameters: + * dev - Instance of the SDIO device driver state structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32_takesem(struct stm32_dev_s *priv) +{ + /* Take the semaphore (perhaps waiting) */ + + while (sem_wait(&priv->waitsem) != 0) + { + /* The only case that an error should occr here is if the wait was + * awakened by a signal. + */ + + ASSERT(errno == EINTR); + } +} + +/**************************************************************************** + * Name: stm32_setclkcr + * + * Description: + * Modify oft-changed bits in the CLKCR register. Only the following bit- + * fields are changed: + * + * CLKDIV, PWRSAV, BYPASS, WIDBUS, NEGEDGE, and HWFC_EN + * + * Input Parameters: + * clkcr - A new CLKCR setting for the above mentions bits (other bits + * are ignored. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void stm32_setclkcr(uint32_t clkcr) +{ + uint32_t regval = getreg32(STM32_SDMMC1_CLKCR); + + /* Clear CLKDIV, PWRSAV, BYPASS, WIDBUS, NEGEDGE, HWFC_EN bits */ + + regval &= ~(SDIO_CLKCR_CLKDIV_MASK | SDIO_CLKCR_PWRSAV | SDIO_CLKCR_BYPASS | + SDIO_CLKCR_WIDBUS_MASK | SDIO_CLKCR_NEGEDGE | SDIO_CLKCR_HWFC_EN | + SDIO_CLKCR_CLKEN); + + /* Replace with user provided settings */ + + clkcr &= (SDIO_CLKCR_CLKDIV_MASK | SDIO_CLKCR_PWRSAV | SDIO_CLKCR_BYPASS | + SDIO_CLKCR_WIDBUS_MASK | SDIO_CLKCR_NEGEDGE | SDIO_CLKCR_HWFC_EN | + SDIO_CLKCR_CLKEN); + + regval |= clkcr; + putreg32(regval, STM32_SDMMC1_CLKCR); + + finfo("CLKCR: %08x PWR: %08x\n", + getreg32(STM32_SDMMC1_CLKCR), getreg32(STM32_SDMMC1_POWER)); +} + +/**************************************************************************** + * Name: stm32_configwaitints + * + * Description: + * Enable/disable SDIO interrupts needed to suport the wait function + * + * Input Parameters: + * priv - A reference to the SDIO device state structure + * waitmask - The set of bits in the SDIO MASK register to set + * waitevents - Waited for events + * wkupevent - Wake-up events + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32_configwaitints(struct stm32_dev_s *priv, uint32_t waitmask, + sdio_eventset_t waitevents, + sdio_eventset_t wkupevent) +{ + irqstate_t flags; +#ifdef CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE + int pinset; +#endif + + /* Save all of the data and set the new interrupt mask in one, atomic + * operation. + */ + flags = enter_critical_section(); + +#ifdef CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE + if ((waitmask & SDIOWAIT_WRCOMPLETE) != 0) + { + /* Do not use this in STM32_SDMMC1_MASK register */ + + waitmask &= !SDIOWAIT_WRCOMPLETE; + + pinset = GPIO_SDMMC1_D0 & (GPIO_PORT_MASK | GPIO_PIN_MASK); + pinset |= (GPIO_INPUT | GPIO_FLOAT | GPIO_EXTI); + + /* Arm the SDIO_D Ready and install Isr */ + + stm32_gpiosetevent(pinset, true, false, false, stm32_rdyinterrupt); + } + + /* Disarm SDIO_D ready */ + + if ((wkupevent & SDIOWAIT_WRCOMPLETE) != 0) + { + stm32_gpiosetevent(GPIO_SDMMC1_D0, false, false, false , NULL); + stm32_configgpio(GPIO_SDMMC1_D0); + } +#endif + + priv->waitevents = waitevents; + priv->wkupevent = wkupevent; + priv->waitmask = waitmask; +#ifdef CONFIG_SDIO_DMA + priv->xfrflags = 0; +#endif + putreg32(priv->xfrmask | priv->waitmask, STM32_SDMMC1_MASK); + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: stm32_configxfrints + * + * Description: + * Enable SDIO interrupts needed to support the data transfer event + * + * Input Parameters: + * priv - A reference to the SDIO device state structure + * xfrmask - The set of bits in the SDIO MASK register to set + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32_configxfrints(struct stm32_dev_s *priv, uint32_t xfrmask) +{ + irqstate_t flags; + flags = enter_critical_section(); + priv->xfrmask = xfrmask; + putreg32(priv->xfrmask | priv->waitmask, STM32_SDMMC1_MASK); + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: stm32_setpwrctrl + * + * Description: + * Change the PWRCTRL field of the SDIO POWER register to turn the SDIO + * ON or OFF + * + * Input Parameters: + * clkcr - A new PWRCTRL setting + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32_setpwrctrl(uint32_t pwrctrl) +{ + uint32_t regval; + + regval = getreg32(STM32_SDMMC1_POWER); + regval &= ~SDIO_POWER_PWRCTRL_MASK; + regval |= pwrctrl; + putreg32(regval, STM32_SDMMC1_POWER); +} + +/**************************************************************************** + * Name: stm32_getpwrctrl + * + * Description: + * Return the current value of the the PWRCTRL field of the SDIO POWER + * register. This function can be used to see if the SDIO is powered ON + * or OFF + * + * Input Parameters: + * None + * + * Returned Value: + * The current value of the the PWRCTRL field of the SDIO POWER register. + * + ****************************************************************************/ + +static inline uint32_t stm32_getpwrctrl(void) +{ + return getreg32(STM32_SDMMC1_POWER) & SDIO_POWER_PWRCTRL_MASK; +} + +/**************************************************************************** + * DMA Helpers + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_sampleinit + * + * Description: + * Setup prior to collecting DMA samples + * + ****************************************************************************/ + +#ifdef CONFIG_SDIO_XFRDEBUG +static void stm32_sampleinit(void) +{ + memset(g_sampleregs, 0xff, DEBUG_NSAMPLES * sizeof(struct stm32_sampleregs_s)); +} +#endif + +/**************************************************************************** + * Name: stm32_sdiosample + * + * Description: + * Sample SDIO registers + * + ****************************************************************************/ + +#ifdef CONFIG_SDIO_XFRDEBUG +static void stm32_sdiosample(struct stm32_sdioregs_s *regs) +{ + regs->power = (uint8_t)getreg32(STM32_SDMMC1_POWER); + regs->clkcr = (uint16_t)getreg32(STM32_SDMMC1_CLKCR); + regs->dctrl = (uint16_t)getreg32(STM32_SDMMC1_DCTRL); + regs->dtimer = getreg32(STM32_SDMMC1_DTIMER); + regs->dlen = getreg32(STM32_SDMMC1_DLEN); + regs->dcount = getreg32(STM32_SDMMC1_DCOUNT); + regs->sta = getreg32(STM32_SDMMC1_STA); + regs->mask = getreg32(STM32_SDMMC1_MASK); + regs->fifocnt = getreg32(STM32_SDMMC1_FIFOCNT); +} +#endif + +/**************************************************************************** + * Name: stm32_sample + * + * Description: + * Sample SDIO/DMA registers + * + ****************************************************************************/ + +#ifdef CONFIG_SDIO_XFRDEBUG +static void stm32_sample(struct stm32_dev_s *priv, int index) +{ + struct stm32_sampleregs_s *regs = &g_sampleregs[index]; + +#if defined(CONFIG_DEBUG_DMA_INFO) && defined(CONFIG_SDIO_DMA) + if (priv->dmamode) + { + stm32_dmasample(priv->dma, ®s->dma); + } +#endif + + stm32_sdiosample(®s->sdio); +} +#endif + +/**************************************************************************** + * Name: stm32_sdiodump + * + * Description: + * Dump one register sample + * + ****************************************************************************/ + +#ifdef CONFIG_SDIO_XFRDEBUG +static void stm32_sdiodump(struct stm32_sdioregs_s *regs, const char *msg) +{ + ferr("SDIO Registers: %s\n", msg); + ferr(" POWER[%08x]: %08x\n", STM32_SDMMC1_POWER, regs->power); + ferr(" CLKCR[%08x]: %08x\n", STM32_SDMMC1_CLKCR, regs->clkcr); + ferr(" DCTRL[%08x]: %08x\n", STM32_SDMMC1_DCTRL, regs->dctrl); + ferr(" DTIMER[%08x]: %08x\n", STM32_SDMMC1_DTIMER, regs->dtimer); + ferr(" DLEN[%08x]: %08x\n", STM32_SDMMC1_DLEN, regs->dlen); + ferr(" DCOUNT[%08x]: %08x\n", STM32_SDMMC1_DCOUNT, regs->dcount); + ferr(" STA[%08x]: %08x\n", STM32_SDMMC1_STA, regs->sta); + ferr(" MASK[%08x]: %08x\n", STM32_SDMMC1_MASK, regs->mask); + ferr("FIFOCNT[%08x]: %08x\n", STM32_SDMMC1_FIFOCNT, regs->fifocnt); +} +#endif + +/**************************************************************************** + * Name: stm32_dumpsample + * + * Description: + * Dump one register sample + * + ****************************************************************************/ + +#ifdef CONFIG_SDIO_XFRDEBUG +static void stm32_dumpsample(struct stm32_dev_s *priv, + struct stm32_sampleregs_s *regs, const char *msg) +{ +#if defined(CONFIG_DEBUG_DMA_INFO) && defined(CONFIG_SDIO_DMA) + if (priv->dmamode) + { + stm32_dmadump(priv->dma, ®s->dma, msg); + } +#endif + + stm32_sdiodump(®s->sdio, msg); +} +#endif + +/**************************************************************************** + * Name: stm32_dumpsamples + * + * Description: + * Dump all sampled register data + * + ****************************************************************************/ + +#ifdef CONFIG_SDIO_XFRDEBUG +static void stm32_dumpsamples(struct stm32_dev_s *priv) +{ + stm32_dumpsample(priv, &g_sampleregs[SAMPLENDX_BEFORE_SETUP], "Before setup"); + +#if defined(CONFIG_DEBUG_DMA_INFO) && defined(CONFIG_SDIO_DMA) + if (priv->dmamode) + { + stm32_dumpsample(priv, &g_sampleregs[SAMPLENDX_BEFORE_ENABLE], "Before DMA enable"); + } +#endif + + stm32_dumpsample(priv, &g_sampleregs[SAMPLENDX_AFTER_SETUP], "After setup"); + stm32_dumpsample(priv, &g_sampleregs[SAMPLENDX_END_TRANSFER], "End of transfer"); + +#if defined(CONFIG_DEBUG_DMA_INFO) && defined(CONFIG_SDIO_DMA) + if (priv->dmamode) + { + stm32_dumpsample(priv, &g_sampleregs[SAMPLENDX_DMA_CALLBACK], "DMA Callback"); + } +#endif +} +#endif + +/**************************************************************************** + * Name: stm32_dmacallback + * + * Description: + * Called when SDIO DMA completes + * + ****************************************************************************/ + +#ifdef CONFIG_SDIO_DMA +static void stm32_dmacallback(DMA_HANDLE handle, uint8_t status, void *arg) +{ + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)arg; + DEBUGASSERT(priv->dmamode); + sdio_eventset_t result; + + /* In the normal case, SDIO appears to handle the End-Of-Transfer interrupt + * first with the End-Of-DMA event occurring significantly later. On + * transfer errors, however, the DMA error will occur before the End-of- + * Transfer. + */ + + stm32_sample((struct stm32_dev_s *)arg, SAMPLENDX_DMA_CALLBACK); + + /* Get the result of the DMA transfer */ + + if ((status & DMA_STATUS_ERROR) != 0) + { + fllerr("DMA error %02x, remaining: %d\n", status, priv->remaining); + result = SDIOWAIT_ERROR; + } + else + { + result = SDIOWAIT_TRANSFERDONE; + } + + /* Then terminate the transfer if this completes all of the steps in the + * transfer OR if a DMA error occurred. In the non-error case, we should + * already have the SDIO transfer done interrupt. If not, the transfer + * will appropriately time out. + */ + + priv->xfrflags |= SDIO_DMADONE_FLAG; + if (priv->xfrflags == SDIO_ALLDONE || result == SDIOWAIT_ERROR) + { + stm32_endtransfer(priv, result); + } +} +#endif + +/**************************************************************************** + * Data Transfer Helpers + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_log2 + * + * Description: + * Take (approximate) log base 2 of the provided number (Only works if the + * provided number is a power of 2). + * + ****************************************************************************/ + +static uint8_t stm32_log2(uint16_t value) +{ + uint8_t log2 = 0; + + /* 0000 0000 0000 0001 -> return 0, + * 0000 0000 0000 001x -> return 1, + * 0000 0000 0000 01xx -> return 2, + * 0000 0000 0000 1xxx -> return 3, + * ... + * 1xxx xxxx xxxx xxxx -> return 15, + */ + + DEBUGASSERT(value > 0); + while (value != 1) + { + value >>= 1; + log2++; + } + + return log2; +} + +/**************************************************************************** + * Name: stm32_dataconfig + * + * Description: + * Configure the SDIO data path for the next data transfer + * + ****************************************************************************/ + +static void stm32_dataconfig(uint32_t timeout, uint32_t dlen, uint32_t dctrl) +{ + uint32_t regval = 0; + + /* Enable data path */ + + putreg32(timeout, STM32_SDMMC1_DTIMER); /* Set DTIMER */ + putreg32(dlen, STM32_SDMMC1_DLEN); /* Set DLEN */ + + /* Configure DCTRL DTDIR, DTMODE, and DBLOCKSIZE fields and set the DTEN + * field + */ + + regval = getreg32(STM32_SDMMC1_DCTRL); + regval &= ~(SDIO_DCTRL_DTDIR | SDIO_DCTRL_DTMODE | SDIO_DCTRL_DBLOCKSIZE_MASK); + dctrl &= (SDIO_DCTRL_DTDIR | SDIO_DCTRL_DTMODE | SDIO_DCTRL_DBLOCKSIZE_MASK); + regval |= (dctrl | SDIO_DCTRL_DTEN); + putreg32(regval, STM32_SDMMC1_DCTRL); +} + +/**************************************************************************** + * Name: stm32_datadisable + * + * Description: + * Disable the SDIO data path setup by stm32_dataconfig() and + * disable DMA. + * + ****************************************************************************/ + +static void stm32_datadisable(void) +{ + uint32_t regval; + + /* Disable the data path */ + + putreg32(SDIO_DTIMER_DATATIMEOUT, STM32_SDMMC1_DTIMER); /* Reset DTIMER */ + putreg32(0, STM32_SDMMC1_DLEN); /* Reset DLEN */ + + /* Reset DCTRL DTEN, DTDIR, DTMODE, DMAEN, and DBLOCKSIZE fields */ + + regval = getreg32(STM32_SDMMC1_DCTRL); + regval &= ~(SDIO_DCTRL_DTEN | SDIO_DCTRL_DTDIR | SDIO_DCTRL_DTMODE | + SDIO_DCTRL_DMAEN | SDIO_DCTRL_DBLOCKSIZE_MASK); + putreg32(regval, STM32_SDMMC1_DCTRL); +} + +/**************************************************************************** + * Name: stm32_sendfifo + * + * Description: + * Send SDIO data in interrupt mode + * + * Input Parameters: + * priv - An instance of the SDIO device interface + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32_sendfifo(struct stm32_dev_s *priv) +{ + union + { + uint32_t w; + uint8_t b[4]; + } data; + + /* Loop while there is more data to be sent and the RX FIFO is not full */ + + while (priv->remaining > 0 && + (getreg32(STM32_SDMMC1_STA) & SDIO_STA_TXFIFOF) == 0) + { + /* Is there a full word remaining in the user buffer? */ + + if (priv->remaining >= sizeof(uint32_t)) + { + /* Yes, transfer the word to the TX FIFO */ + + data.w = *priv->buffer++; + priv->remaining -= sizeof(uint32_t); + } + else + { + /* No.. transfer just the bytes remaining in the user buffer, + * padding with zero as necessary to extend to a full word. + */ + + uint8_t *ptr = (uint8_t *)priv->remaining; + int i; + + data.w = 0; + for (i = 0; i < (int)priv->remaining; i++) + { + data.b[i] = *ptr++; + } + + /* Now the transfer is finished */ + + priv->remaining = 0; + } + + /* Put the word in the FIFO */ + + putreg32(data.w, STM32_SDMMC1_FIFO); + } +} + +/**************************************************************************** + * Name: stm32_recvfifo + * + * Description: + * Receive SDIO data in interrupt mode + * + * Input Parameters: + * priv - An instance of the SDIO device interface + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32_recvfifo(struct stm32_dev_s *priv) +{ + union + { + uint32_t w; + uint8_t b[4]; + } data; + + /* Loop while there is space to store the data and there is more + * data available in the RX FIFO. + */ + + while (priv->remaining > 0 && + (getreg32(STM32_SDMMC1_STA) & SDIO_STA_RXDAVL) != 0) + { + /* Read the next word from the RX FIFO */ + + data.w = getreg32(STM32_SDMMC1_FIFO); + if (priv->remaining >= sizeof(uint32_t)) + { + /* Transfer the whole word to the user buffer */ + + *priv->buffer++ = data.w; + priv->remaining -= sizeof(uint32_t); + } + else + { + /* Transfer any trailing fractional word */ + + uint8_t *ptr = (uint8_t *)priv->buffer; + int i; + + for (i = 0; i < (int)priv->remaining; i++) + { + *ptr++ = data.b[i]; + } + + /* Now the transfer is finished */ + + priv->remaining = 0; + } + } +} + +/**************************************************************************** + * Name: stm32_eventtimeout + * + * Description: + * The watchdog timeout setup when the event wait start has expired without + * any other waited-for event occurring. + * + * Input Parameters: + * argc - The number of arguments (should be 1) + * arg - The argument (state structure reference cast to uint32_t) + * + * Returned Value: + * None + * + * Assumptions: + * Always called from the interrupt level with interrupts disabled. + * + ****************************************************************************/ + +static void stm32_eventtimeout(int argc, uint32_t arg) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)arg; + + /* There is always race conditions with timer expirations. */ + + DEBUGASSERT((priv->waitevents & SDIOWAIT_TIMEOUT) != 0 || priv->wkupevent != 0); + + /* Is a data transfer complete event expected? */ + + if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0) + { + /* Yes.. wake up any waiting threads */ + + stm32_endwait(priv, SDIOWAIT_TIMEOUT); + fllerr("Timeout: remaining: %d\n", priv->remaining); + } +} + +/**************************************************************************** + * Name: stm32_endwait + * + * Description: + * Wake up a waiting thread if the waited-for event has occurred. + * + * Input Parameters: + * priv - An instance of the SDIO device interface + * wkupevent - The event that caused the wait to end + * + * Returned Value: + * None + * + * Assumptions: + * Always called from the interrupt level with interrupts disabled. + * + ****************************************************************************/ + +static void stm32_endwait(struct stm32_dev_s *priv, sdio_eventset_t wkupevent) +{ + /* Cancel the watchdog timeout */ + + (void)wd_cancel(priv->waitwdog); + + /* Disable event-related interrupts */ + + stm32_configwaitints(priv, 0, 0, wkupevent); + + /* Wake up the waiting thread */ + + stm32_givesem(priv); +} + +/**************************************************************************** + * Name: stm32_endtransfer + * + * Description: + * Terminate a transfer with the provided status. This function is called + * only from the SDIO interrupt handler when end-of-transfer conditions + * are detected. + * + * Input Parameters: + * priv - An instance of the SDIO device interface + * wkupevent - The event that caused the transfer to end + * + * Returned Value: + * None + * + * Assumptions: + * Always called from the interrupt level with interrupts disabled. + * + ****************************************************************************/ + +static void stm32_endtransfer(struct stm32_dev_s *priv, sdio_eventset_t wkupevent) +{ + /* Disable all transfer related interrupts */ + + stm32_configxfrints(priv, 0); + + /* Clearing pending interrupt status on all transfer related interrupts */ + + putreg32(SDIO_XFRDONE_ICR, STM32_SDMMC1_ICR); + + /* If this was a DMA transfer, make sure that DMA is stopped */ + +#ifdef CONFIG_SDIO_DMA + if (priv->dmamode) + { + /* DMA debug instrumentation */ + + stm32_sample(priv, SAMPLENDX_END_TRANSFER); + + /* Make sure that the DMA is stopped (it will be stopped automatically + * on normal transfers, but not necessarily when the transfer terminates + * on an error condition). + */ + + stm32_dmastop(priv->dma); + } +#endif + + /* Mark the transfer finished */ + + priv->remaining = 0; + + /* Is a thread wait for these data transfer complete events? */ + + if ((priv->waitevents & wkupevent) != 0) + { + /* Yes.. wake up any waiting threads */ + + stm32_endwait(priv, wkupevent); + } +} + +/**************************************************************************** + * Interrupt Handling + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_rdyinterrupt + * + * Description: + * SDIO ready interrupt handler + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE +static int stm32_rdyinterrupt(int irq, void *context) +{ + struct stm32_dev_s *priv = &g_sdiodev; + stm32_endwait(priv, SDIOWAIT_WRCOMPLETE); + return OK; +} +#endif + +/**************************************************************************** + * Name: stm32_interrupt + * + * Description: + * SDIO interrupt handler + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * + * Returned Value: + * None + * + ****************************************************************************/ + +static int stm32_interrupt(int irq, void *context) +{ + struct stm32_dev_s *priv = &g_sdiodev; + uint32_t enabled; + uint32_t pending; + + /* Loop while there are pending interrupts. Check the SDIO status + * register. Mask out all bits that don't correspond to enabled + * interrupts. (This depends on the fact that bits are ordered + * the same in both the STA and MASK register). If there are non-zero + * bits remaining, then we have work to do here. + */ + + while ((enabled = getreg32(STM32_SDMMC1_STA) & getreg32(STM32_SDMMC1_MASK)) != 0) + { + /* Handle in progress, interrupt driven data transfers ****************/ + + pending = enabled & priv->xfrmask; + if (pending != 0) + { +#ifdef CONFIG_SDIO_DMA + if (!priv->dmamode) +#endif + { + /* Is the RX FIFO half full or more? Is so then we must be + * processing a receive transaction. + */ + + if ((pending & SDIO_STA_RXFIFOHF) != 0) + { + /* Receive data from the RX FIFO */ + + stm32_recvfifo(priv); + } + + /* Otherwise, Is the transmit FIFO half empty or less? If so we must + * be processing a send transaction. NOTE: We can't be processing + * both! + */ + + else if ((pending & SDIO_STA_TXFIFOHE) != 0) + { + /* Send data via the TX FIFO */ + + stm32_sendfifo(priv); + } + } + + /* Handle data end events */ + + if ((pending & SDIO_STA_DATAEND) != 0) + { + /* Handle any data remaining the RX FIFO. If the RX FIFO is + * less than half full at the end of the transfer, then no + * half-full interrupt will be received. + */ + + /* Was this transfer performed in DMA mode? */ + +#ifdef CONFIG_SDIO_DMA + if (priv->dmamode) + { + /* Yes.. Terminate the transfers only if the DMA has also + * finished. + */ + + priv->xfrflags |= SDIO_XFRDONE_FLAG; + if (priv->xfrflags == SDIO_ALLDONE) + { + stm32_endtransfer(priv, SDIOWAIT_TRANSFERDONE); + } + + /* Otherwise, just disable futher transfer interrupts and + * wait for the DMA complete event. + */ + + else + { + stm32_configxfrints(priv, 0); + } + } + else +#endif + { + /* Receive data from the RX FIFO */ + + stm32_recvfifo(priv); + + /* Then terminate the transfer */ + + stm32_endtransfer(priv, SDIOWAIT_TRANSFERDONE); + } + } + + /* Handle data block send/receive CRC failure */ + + else if ((pending & SDIO_STA_DCRCFAIL) != 0) + { + /* Terminate the transfer with an error */ + + fllerr("ERROR: Data block CRC failure, remaining: %d\n", priv->remaining); + stm32_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR); + } + + /* Handle data timeout error */ + + else if ((pending & SDIO_STA_DTIMEOUT) != 0) + { + /* Terminate the transfer with an error */ + + fllerr("ERROR: Data timeout, remaining: %d\n", priv->remaining); + stm32_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_TIMEOUT); + } + + /* Handle RX FIFO overrun error */ + + else if ((pending & SDIO_STA_RXOVERR) != 0) + { + /* Terminate the transfer with an error */ + + fllerr("ERROR: RX FIFO overrun, remaining: %d\n", priv->remaining); + stm32_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR); + } + + /* Handle TX FIFO underrun error */ + + else if ((pending & SDIO_STA_TXUNDERR) != 0) + { + /* Terminate the transfer with an error */ + + fllerr("ERROR: TX FIFO underrun, remaining: %d\n", priv->remaining); + stm32_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR); + } + + /* Handle start bit error */ + + else if ((pending & SDIO_STA_STBITERR) != 0) + { + /* Terminate the transfer with an error */ + + fllerr("ERROR: Start bit, remaining: %d\n", priv->remaining); + stm32_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR); + } + } + + /* Handle wait events *************************************************/ + + pending = enabled & priv->waitmask; + if (pending != 0) + { + /* Is this a response completion event? */ + + if ((pending & SDIO_RESPDONE_STA) != 0) + { + /* Yes.. Is their a thread waiting for response done? */ + + if ((priv->waitevents & SDIOWAIT_RESPONSEDONE) != 0) + { + /* Yes.. wake the thread up */ + + putreg32(SDIO_RESPDONE_ICR | SDIO_CMDDONE_ICR, STM32_SDMMC1_ICR); + stm32_endwait(priv, SDIOWAIT_RESPONSEDONE); + } + } + + /* Is this a command completion event? */ + + if ((pending & SDIO_CMDDONE_STA) != 0) + { + /* Yes.. Is their a thread waiting for command done? */ + + if ((priv->waitevents & SDIOWAIT_RESPONSEDONE) != 0) + { + /* Yes.. wake the thread up */ + + putreg32(SDIO_CMDDONE_ICR, STM32_SDMMC1_ICR); + stm32_endwait(priv, SDIOWAIT_CMDDONE); + } + } + } + } + + return OK; +} + +/**************************************************************************** + * SDIO Interface Methods + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_lock + * + * Description: + * Locks the bus. Function calls low-level multiplexed bus routines to + * resolve bus requests and acknowledgment issues. + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * lock - TRUE to lock, FALSE to unlock. + * + * Returned Value: + * OK on success; a negated errno on failure + * + ****************************************************************************/ + +#ifdef CONFIG_SDIO_MUXBUS +static int stm32_lock(FAR struct sdio_dev_s *dev, bool lock) +{ + /* Single SDIO instance so there is only one possibility. The multiplex + * bus is part of board support package. + */ + + stm32_muxbus_sdio_lock(lock); + return OK; +} +#endif + +/**************************************************************************** + * Name: stm32_reset + * + * Description: + * Reset the SDIO controller. Undo all setup and initialization. + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32_reset(FAR struct sdio_dev_s *dev) +{ + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev; + irqstate_t flags; + + /* Disable clocking */ + + flags = enter_critical_section(); + stm32_setpwrctrl(SDIO_POWER_PWRCTRL_OFF); + + /* Put SDIO registers in their default, reset state */ + + stm32_default(); + + /* Reset data */ + + priv->waitevents = 0; /* Set of events to be waited for */ + priv->waitmask = 0; /* Interrupt enables for event waiting */ + priv->wkupevent = 0; /* The event that caused the wakeup */ +#ifdef CONFIG_SDIO_DMA + priv->xfrflags = 0; /* Used to synchronize SDIO and DMA completion events */ +#endif + + wd_cancel(priv->waitwdog); /* Cancel any timeouts */ + + /* Interrupt mode data transfer support */ + + priv->buffer = 0; /* Address of current R/W buffer */ + priv->remaining = 0; /* Number of bytes remaining in the transfer */ + priv->xfrmask = 0; /* Interrupt enables for data transfer */ + + /* DMA data transfer support */ + + priv->widebus = false; /* Required for DMA support */ +#ifdef CONFIG_SDIO_DMA + priv->dmamode = false; /* true: DMA mode transfer */ +#endif + + /* Configure the SDIO peripheral */ + + stm32_setclkcr(STM32_CLCKCR_INIT | SDIO_CLKCR_CLKEN); + stm32_setpwrctrl(SDIO_POWER_PWRCTRL_ON); + leave_critical_section(flags); + + finfo("CLCKR: %08x POWER: %08x\n", + getreg32(STM32_SDMMC1_CLKCR), getreg32(STM32_SDMMC1_POWER)); +} + +/**************************************************************************** + * Name: stm32_status + * + * Description: + * Get SDIO status. + * + * Input Parameters: + * dev - Device-specific state data + * + * Returned Value: + * Returns a bitset of status values (see stm32_status_* defines) + * + ****************************************************************************/ + +static uint8_t stm32_status(FAR struct sdio_dev_s *dev) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + return priv->cdstatus; +} + +/**************************************************************************** + * Name: stm32_widebus + * + * Description: + * Called after change in Bus width has been selected (via ACMD6). Most + * controllers will need to perform some special operations to work + * correctly in the new bus mode. + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * wide - true: wide bus (4-bit) bus mode enabled + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32_widebus(FAR struct sdio_dev_s *dev, bool wide) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + priv->widebus = wide; +} + +/**************************************************************************** + * Name: stm32_clock + * + * Description: + * Enable/disable SDIO clocking + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * rate - Specifies the clocking to use (see enum sdio_clock_e) + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate) +{ + uint32_t clckr; + + switch (rate) + { + /* Disable clocking (with default ID mode divisor) */ + + default: + case CLOCK_SDIO_DISABLED: + clckr = STM32_CLCKCR_INIT; + return; + + /* Enable in initial ID mode clocking (<400KHz) */ + + case CLOCK_IDMODE: + clckr = (STM32_CLCKCR_INIT | SDIO_CLKCR_CLKEN); + break; + + /* Enable in MMC normal operation clocking */ + + case CLOCK_MMC_TRANSFER: + clckr = (SDIO_CLKCR_MMCXFR | SDIO_CLKCR_CLKEN); + break; + + /* SD normal operation clocking (wide 4-bit mode) */ + + case CLOCK_SD_TRANSFER_4BIT: +#ifndef CONFIG_SDIO_WIDTH_D1_ONLY + clckr = (SDIO_CLCKR_SDWIDEXFR | SDIO_CLKCR_CLKEN); + break; +#endif + + /* SD normal operation clocking (narrow 1-bit mode) */ + + case CLOCK_SD_TRANSFER_1BIT: + clckr = (SDIO_CLCKR_SDXFR | SDIO_CLKCR_CLKEN); + break; + } + + /* Set the new clock frequency along with the clock enable/disable bit */ + + stm32_setclkcr(clckr); +} + +/**************************************************************************** + * Name: stm32_attach + * + * Description: + * Attach and prepare interrupts + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * + * Returned Value: + * OK on success; A negated errno on failure. + * + ****************************************************************************/ + +static int stm32_attach(FAR struct sdio_dev_s *dev) +{ + int ret; + + /* Attach the SDIO interrupt handler */ + + ret = irq_attach(STM32_IRQ_SDMMC1, stm32_interrupt); + if (ret == OK) + { + + /* Disable all interrupts at the SDIO controller and clear static + * interrupt flags + */ + + putreg32(SDIO_MASK_RESET, STM32_SDMMC1_MASK); + putreg32(SDIO_ICR_STATICFLAGS, STM32_SDMMC1_ICR); + + /* Enable SDIO interrupts at the NVIC. They can now be enabled at + * the SDIO controller as needed. + */ + + up_enable_irq(STM32_IRQ_SDMMC1); + +#ifdef CONFIG_ARCH_IRQPRIO + /* Set the interrupt priority */ + + up_prioritize_irq(STM32_IRQ_SDIO, CONFIG_SDIO_PRI); +#endif + } + + return ret; +} + +/**************************************************************************** + * Name: stm32_sendcmd + * + * Description: + * Send the SDIO command + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * cmd - The command to send (32-bits, encoded) + * arg - 32-bit argument required with some commands + * + * Returned Value: + * None + * + ****************************************************************************/ + +static int stm32_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd, uint32_t arg) +{ + uint32_t regval; + uint32_t cmdidx; + + /* Set the SDIO Argument value */ + + putreg32(arg, STM32_SDMMC1_ARG); + + /* Clear CMDINDEX, WAITRESP, WAITINT, WAITPEND, and CPSMEN bits */ + + regval = getreg32(STM32_SDMMC1_CMD); + regval &= ~(SDIO_CMD_CMDINDEX_MASK | SDIO_CMD_WAITRESP_MASK | + SDIO_CMD_WAITINT | SDIO_CMD_WAITPEND | SDIO_CMD_CPSMEN); + + /* Set WAITRESP bits */ + + switch (cmd & MMCSD_RESPONSE_MASK) + { + case MMCSD_NO_RESPONSE: + regval |= SDIO_CMD_NORESPONSE; + break; + + case MMCSD_R1_RESPONSE: + case MMCSD_R1B_RESPONSE: + case MMCSD_R3_RESPONSE: + case MMCSD_R4_RESPONSE: + case MMCSD_R5_RESPONSE: + case MMCSD_R6_RESPONSE: + case MMCSD_R7_RESPONSE: + regval |= SDIO_CMD_SHORTRESPONSE; + break; + + case MMCSD_R2_RESPONSE: + regval |= SDIO_CMD_LONGRESPONSE; + break; + } + + /* Set CPSMEN and the command index */ + + cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT; + regval |= cmdidx | SDIO_CMD_CPSMEN; + + finfo("cmd: %08x arg: %08x regval: %08x\n", cmd, arg, regval); + + /* Write the SDIO CMD */ + + putreg32(SDIO_RESPDONE_ICR | SDIO_CMDDONE_ICR, STM32_SDMMC1_ICR); + putreg32(regval, STM32_SDMMC1_CMD); + return OK; +} + +/**************************************************************************** + * Name: stm32_recvsetup + * + * Description: + * Setup hardware in preparation for data transfer from the card in non-DMA + * (interrupt driven mode). This method will do whatever controller setup + * is necessary. This would be called for SD memory just BEFORE sending + * CMD13 (SEND_STATUS), CMD17 (READ_SINGLE_BLOCK), CMD18 + * (READ_MULTIPLE_BLOCKS), ACMD51 (SEND_SCR), etc. Normally, SDIO_WAITEVENT + * will be called to receive the indication that the transfer is complete. + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * buffer - Address of the buffer in which to receive the data + * nbytes - The number of bytes in the transfer + * + * Returned Value: + * Number of bytes sent on success; a negated errno on failure + * + ****************************************************************************/ + +static int stm32_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer, + size_t nbytes) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + uint32_t dblocksize; + + DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0); + DEBUGASSERT(((uint32_t)buffer & 3) == 0); + + /* Reset the DPSM configuration */ + + stm32_datadisable(); + stm32_sampleinit(); + stm32_sample(priv, SAMPLENDX_BEFORE_SETUP); + + /* Save the destination buffer information for use by the interrupt handler */ + + priv->buffer = (uint32_t *)buffer; + priv->remaining = nbytes; +#ifdef CONFIG_SDIO_DMA + priv->dmamode = false; +#endif + + /* Then set up the SDIO data path */ + + dblocksize = stm32_log2(nbytes) << SDIO_DCTRL_DBLOCKSIZE_SHIFT; + stm32_dataconfig(SDIO_DTIMER_DATATIMEOUT, nbytes, dblocksize | SDIO_DCTRL_DTDIR); + + /* And enable interrupts */ + + stm32_configxfrints(priv, SDIO_RECV_MASK); + stm32_sample(priv, SAMPLENDX_AFTER_SETUP); + return OK; +} + +/**************************************************************************** + * Name: stm32_sendsetup + * + * Description: + * Setup hardware in preparation for data transfer from the card. This method + * will do whatever controller setup is necessary. This would be called + * for SD memory just AFTER sending CMD24 (WRITE_BLOCK), CMD25 + * (WRITE_MULTIPLE_BLOCK), ... and before SDIO_SENDDATA is called. + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * buffer - Address of the buffer containing the data to send + * nbytes - The number of bytes in the transfer + * + * Returned Value: + * Number of bytes sent on success; a negated errno on failure + * + ****************************************************************************/ + +static int stm32_sendsetup(FAR struct sdio_dev_s *dev, FAR const uint8_t *buffer, + size_t nbytes) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + uint32_t dblocksize; + + DEBUGASSERT(priv != NULL && buffer != NULL && nbytes > 0); + DEBUGASSERT(((uint32_t)buffer & 3) == 0); + + /* Reset the DPSM configuration */ + + stm32_datadisable(); + stm32_sampleinit(); + stm32_sample(priv, SAMPLENDX_BEFORE_SETUP); + + /* Save the source buffer information for use by the interrupt handler */ + + priv->buffer = (uint32_t *)buffer; + priv->remaining = nbytes; +#ifdef CONFIG_SDIO_DMA + priv->dmamode = false; +#endif + + /* Then set up the SDIO data path */ + + dblocksize = stm32_log2(nbytes) << SDIO_DCTRL_DBLOCKSIZE_SHIFT; + stm32_dataconfig(SDIO_DTIMER_DATATIMEOUT, nbytes, dblocksize); + + /* Enable TX interrupts */ + + stm32_configxfrints(priv, SDIO_SEND_MASK); + stm32_sample(priv, SAMPLENDX_AFTER_SETUP); + return OK; +} + +/**************************************************************************** + * Name: stm32_cancel + * + * Description: + * Cancel the data transfer setup of SDIO_RECVSETUP, SDIO_SENDSETUP, + * SDIO_DMARECVSETUP or SDIO_DMASENDSETUP. This must be called to cancel + * the data transfer setup if, for some reason, you cannot perform the + * transfer. + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * + * Returned Value: + * OK is success; a negated errno on failure + * + ****************************************************************************/ + +static int stm32_cancel(FAR struct sdio_dev_s *dev) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + + /* Disable all transfer- and event- related interrupts */ + + stm32_configxfrints(priv, 0); + stm32_configwaitints(priv, 0, 0, 0); + + /* Clearing pending interrupt status on all transfer- and event- related + * interrupts + */ + + putreg32(SDIO_WAITALL_ICR, STM32_SDMMC1_ICR); + + /* Cancel any watchdog timeout */ + + (void)wd_cancel(priv->waitwdog); + + /* If this was a DMA transfer, make sure that DMA is stopped */ + +#ifdef CONFIG_SDIO_DMA + if (priv->dmamode) + { + /* Make sure that the DMA is stopped (it will be stopped automatically + * on normal transfers, but not necessarily when the transfer terminates + * on an error condition. + */ + + stm32_dmastop(priv->dma); + } +#endif + + /* Mark no transfer in progress */ + + priv->remaining = 0; + return OK; +} + +/**************************************************************************** + * Name: stm32_waitresponse + * + * Description: + * Poll-wait for the response to the last command to be ready. + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * cmd - The command that was sent. See 32-bit command definitions above. + * + * Returned Value: + * OK is success; a negated errno on failure + * + ****************************************************************************/ + +static int stm32_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd) +{ + int32_t timeout; + uint32_t events; + + switch (cmd & MMCSD_RESPONSE_MASK) + { + case MMCSD_NO_RESPONSE: + events = SDIO_CMDDONE_STA; + timeout = SDIO_CMDTIMEOUT; + break; + + case MMCSD_R1_RESPONSE: + case MMCSD_R1B_RESPONSE: + case MMCSD_R2_RESPONSE: + case MMCSD_R6_RESPONSE: + events = SDIO_RESPDONE_STA; + timeout = SDIO_LONGTIMEOUT; + break; + + case MMCSD_R4_RESPONSE: + case MMCSD_R5_RESPONSE: + return -ENOSYS; + + case MMCSD_R3_RESPONSE: + case MMCSD_R7_RESPONSE: + events = SDIO_RESPDONE_STA; + timeout = SDIO_CMDTIMEOUT; + break; + + default: + return -EINVAL; + } + + /* Then wait for the response (or timeout) */ + + while ((getreg32(STM32_SDMMC1_STA) & events) == 0) + { + if (--timeout <= 0) + { + ferr("ERROR: Timeout cmd: %08x events: %08x STA: %08x\n", + cmd, events, getreg32(STM32_SDMMC1_STA)); + + return -ETIMEDOUT; + } + } + + putreg32(SDIO_CMDDONE_ICR, STM32_SDMMC1_ICR); + return OK; +} + +/**************************************************************************** + * Name: stm32_recvRx + * + * Description: + * Receive response to SDIO command. Only the critical payload is + * returned -- that is 32 bits for 48 bit status and 128 bits for 136 bit + * status. The driver implementation should verify the correctness of + * the remaining, non-returned bits (CRCs, CMD index, etc.). + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * Rx - Buffer in which to receive the response + * + * Returned Value: + * Number of bytes sent on success; a negated errno on failure. Here a + * failure means only a faiure to obtain the requested reponse (due to + * transport problem -- timeout, CRC, etc.). The implementation only + * assures that the response is returned intacta and does not check errors + * within the response itself. + * + ****************************************************************************/ + +static int stm32_recvshortcrc(FAR struct sdio_dev_s *dev, uint32_t cmd, uint32_t *rshort) +{ +#ifdef CONFIG_DEBUG_FEATURES + uint32_t respcmd; +#endif + uint32_t regval; + int ret = OK; + + /* R1 Command response (48-bit) + * 47 0 Start bit + * 46 0 Transmission bit (0=from card) + * 45:40 bit5 - bit0 Command index (0-63) + * 39:8 bit31 - bit0 32-bit card status + * 7:1 bit6 - bit0 CRC7 + * 0 1 End bit + * + * R1b Identical to R1 with the additional busy signaling via the data + * line. + * + * R6 Published RCA Response (48-bit, SD card only) + * 47 0 Start bit + * 46 0 Transmission bit (0=from card) + * 45:40 bit5 - bit0 Command index (0-63) + * 39:8 bit31 - bit0 32-bit Argument Field, consisting of: + * [31:16] New published RCA of card + * [15:0] Card status bits {23,22,19,12:0} + * 7:1 bit6 - bit0 CRC7 + * 0 1 End bit + */ + + +#ifdef CONFIG_DEBUG_FEATURES + if (!rshort) + { + ferr("ERROR: rshort=NULL\n"); + ret = -EINVAL; + } + + /* Check that this is the correct response to this command */ + + else if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1_RESPONSE && + (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R1B_RESPONSE && + (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R6_RESPONSE) + { + ferr("ERROR: Wrong response CMD=%08x\n", cmd); + ret = -EINVAL; + } + else +#endif + { + /* Check if a timeout or CRC error occurred */ + + regval = getreg32(STM32_SDMMC1_STA); + if ((regval & SDIO_STA_CTIMEOUT) != 0) + { + ferr("ERROR: Command timeout: %08x\n", regval); + ret = -ETIMEDOUT; + } + else if ((regval & SDIO_STA_CCRCFAIL) != 0) + { + ferr("ERROR: CRC failure: %08x\n", regval); + ret = -EIO; + } +#ifdef CONFIG_DEBUG_FEATURES + else + { + /* Check response received is of desired command */ + + respcmd = getreg32(STM32_SDMMC1_RESPCMD); + if ((uint8_t)(respcmd & SDIO_RESPCMD_MASK) != (cmd & MMCSD_CMDIDX_MASK)) + { + ferr("ERROR: RESCMD=%02x CMD=%08x\n", respcmd, cmd); + ret = -EINVAL; + } + } +#endif + } + + /* Clear all pending message completion events and return the R1/R6 response */ + + putreg32(SDIO_RESPDONE_ICR | SDIO_CMDDONE_ICR, STM32_SDMMC1_ICR); + *rshort = getreg32(STM32_SDMMC1_RESP1); + return ret; +} + +static int stm32_recvlong(FAR struct sdio_dev_s *dev, uint32_t cmd, uint32_t rlong[4]) +{ + uint32_t regval; + int ret = OK; + + /* R2 CID, CSD register (136-bit) + * 135 0 Start bit + * 134 0 Transmission bit (0=from card) + * 133:128 bit5 - bit0 Reserved + * 127:1 bit127 - bit1 127-bit CID or CSD register + * (including internal CRC) + * 0 1 End bit + */ + +#ifdef CONFIG_DEBUG_FEATURES + /* Check that R1 is the correct response to this command */ + + if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R2_RESPONSE) + { + ferr("ERROR: Wrong response CMD=%08x\n", cmd); + ret = -EINVAL; + } + else +#endif + { + /* Check if a timeout or CRC error occurred */ + + regval = getreg32(STM32_SDMMC1_STA); + if (regval & SDIO_STA_CTIMEOUT) + { + ferr("ERROR: Timeout STA: %08x\n", regval); + ret = -ETIMEDOUT; + } + else if (regval & SDIO_STA_CCRCFAIL) + { + ferr("ERROR: CRC fail STA: %08x\n", regval); + ret = -EIO; + } + } + + /* Return the long response */ + + putreg32(SDIO_RESPDONE_ICR | SDIO_CMDDONE_ICR, STM32_SDMMC1_ICR); + if (rlong) + { + rlong[0] = getreg32(STM32_SDMMC1_RESP1); + rlong[1] = getreg32(STM32_SDMMC1_RESP2); + rlong[2] = getreg32(STM32_SDMMC1_RESP3); + rlong[3] = getreg32(STM32_SDMMC1_RESP4); + } + return ret; +} + +static int stm32_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd, uint32_t *rshort) +{ + uint32_t regval; + int ret = OK; + + /* R3 OCR (48-bit) + * 47 0 Start bit + * 46 0 Transmission bit (0=from card) + * 45:40 bit5 - bit0 Reserved + * 39:8 bit31 - bit0 32-bit OCR register + * 7:1 bit6 - bit0 Reserved + * 0 1 End bit + */ + + /* Check that this is the correct response to this command */ + +#ifdef CONFIG_DEBUG_FEATURES + if ((cmd & MMCSD_RESPONSE_MASK) != MMCSD_R3_RESPONSE && + (cmd & MMCSD_RESPONSE_MASK) != MMCSD_R7_RESPONSE) + { + ferr("ERROR: Wrong response CMD=%08x\n", cmd); + ret = -EINVAL; + } + else +#endif + { + /* Check if a timeout occurred (Apparently a CRC error can terminate + * a good response) + */ + + regval = getreg32(STM32_SDMMC1_STA); + if (regval & SDIO_STA_CTIMEOUT) + { + ferr("ERROR: Timeout STA: %08x\n", regval); + ret = -ETIMEDOUT; + } + } + + putreg32(SDIO_RESPDONE_ICR | SDIO_CMDDONE_ICR, STM32_SDMMC1_ICR); + if (rshort) + { + *rshort = getreg32(STM32_SDMMC1_RESP1); + } + return ret; +} + +/* MMC responses not supported */ + +static int stm32_recvnotimpl(FAR struct sdio_dev_s *dev, uint32_t cmd, uint32_t *rnotimpl) +{ + putreg32(SDIO_RESPDONE_ICR | SDIO_CMDDONE_ICR, STM32_SDMMC1_ICR); + return -ENOSYS; +} + +/**************************************************************************** + * Name: stm32_waitenable + * + * Description: + * Enable/disable of a set of SDIO wait events. This is part of the + * the SDIO_WAITEVENT sequence. The set of to-be-waited-for events is + * configured before calling stm32_eventwait. This is done in this way + * to help the driver to eliminate race conditions between the command + * setup and the subsequent events. + * + * The enabled events persist until either (1) SDIO_WAITENABLE is called + * again specifying a different set of wait events, or (2) SDIO_EVENTWAIT + * returns. + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * eventset - A bitset of events to enable or disable (see SDIOWAIT_* + * definitions). 0=disable; 1=enable. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32_waitenable(FAR struct sdio_dev_s *dev, + sdio_eventset_t eventset) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + uint32_t waitmask; + + DEBUGASSERT(priv != NULL); + + /* Disable event-related interrupts */ + + stm32_configwaitints(priv, 0, 0, 0); + + /* Select the interrupt mask that will give us the appropriate wakeup + * interrupts. + */ + +#if defined(CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE) + if ((eventset & SDIOWAIT_WRCOMPLETE) != 0) + { + waitmask = SDIOWAIT_WRCOMPLETE; + } + else +#endif + { + waitmask = 0; + if ((eventset & SDIOWAIT_CMDDONE) != 0) + { + waitmask |= SDIO_CMDDONE_MASK; + } + + if ((eventset & SDIOWAIT_RESPONSEDONE) != 0) + { + waitmask |= SDIO_RESPDONE_MASK; + } + + if ((eventset & SDIOWAIT_TRANSFERDONE) != 0) + { + waitmask |= SDIO_XFRDONE_MASK; + } + + /* Enable event-related interrupts */ + + putreg32(SDIO_WAITALL_ICR, STM32_SDMMC1_ICR); + } + + stm32_configwaitints(priv, waitmask, eventset, 0); +} + +/**************************************************************************** + * Name: stm32_eventwait + * + * Description: + * Wait for one of the enabled events to occur (or a timeout). Note that + * all events enabled by SDIO_WAITEVENTS are disabled when stm32_eventwait + * returns. SDIO_WAITEVENTS must be called again before stm32_eventwait + * can be used again. + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * timeout - Maximum time in milliseconds to wait. Zero means immediate + * timeout with no wait. The timeout value is ignored if + * SDIOWAIT_TIMEOUT is not included in the waited-for eventset. + * + * Returned Value: + * Event set containing the event(s) that ended the wait. Should always + * be non-zero. All events are disabled after the wait concludes. + * + ****************************************************************************/ + +static sdio_eventset_t stm32_eventwait(FAR struct sdio_dev_s *dev, + uint32_t timeout) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + sdio_eventset_t wkupevent = 0; + irqstate_t flags; + int ret; + + /* There is a race condition here... the event may have completed before + * we get here. In this case waitevents will be zero, but wkupevents will + * be non-zero (and, hopefully, the semaphore count will also be non-zero. + */ + + flags = enter_critical_section(); + DEBUGASSERT(priv->waitevents != 0 || priv->wkupevent != 0); + + /* Check if the timeout event is specified in the event set */ + + if ((priv->waitevents & SDIOWAIT_TIMEOUT) != 0) + { + int delay; + + /* Yes.. Handle a cornercase: The user request a timeout event but + * with timeout == 0? + */ + + if (!timeout) + { + /* Then just tell the caller that we already timed out */ + + wkupevent = SDIOWAIT_TIMEOUT; + goto errout; + } + + /* Start the watchdog timer */ + + delay = MSEC2TICK(timeout); + ret = wd_start(priv->waitwdog, delay, (wdentry_t)stm32_eventtimeout, + 1, (uint32_t)priv); + if (ret != OK) + { + ferr("ERROR: wd_start failed: %d\n", ret); + } + } + +#if defined(CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE) + if ((priv->waitevents & SDIOWAIT_WRCOMPLETE) != 0) + { + /* Atomically read pin to see if ready (true) and determine if ISR fired + * If Pin is ready and if ISR did NOT fire end the wait here + */ + + if (stm32_gpioread(GPIO_SDMMC1_D0) && + (priv->wkupevent & SDIOWAIT_WRCOMPLETE) == 0) + { + stm32_endwait(priv, SDIOWAIT_WRCOMPLETE); + } + } +#endif + + /* Loop until the event (or the timeout occurs). Race conditions are avoided + * by calling stm32_waitenable prior to triggering the logic that will cause + * the wait to terminate. Under certain race conditions, the waited-for + * may have already occurred before this function was called! + */ + + for (; ; ) + { + /* Wait for an event in event set to occur. If this the event has already + * occurred, then the semaphore will already have been incremented and + * there will be no wait. + */ + + stm32_takesem(priv); + wkupevent = priv->wkupevent; + + /* Check if the event has occurred. When the event has occurred, then + * evenset will be set to 0 and wkupevent will be set to a nonzero value. + */ + + if (wkupevent != 0) + { + /* Yes... break out of the loop with wkupevent non-zero */ + + break; + } + } + + /* Disable event-related interrupts */ + + stm32_configwaitints(priv, 0, 0, 0); +#ifdef CONFIG_SDIO_DMA + priv->xfrflags = 0; +#endif + +errout: + leave_critical_section(flags); + stm32_dumpsamples(priv); + return wkupevent; +} + +/**************************************************************************** + * Name: stm32_callbackenable + * + * Description: + * Enable/disable of a set of SDIO callback events. This is part of the + * the SDIO callback sequence. The set of events is configured to enabled + * callbacks to the function provided in stm32_registercallback. + * + * Events are automatically disabled once the callback is performed and no + * further callback events will occur until they are again enabled by + * calling this methos. + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * eventset - A bitset of events to enable or disable (see SDIOMEDIA_* + * definitions). 0=disable; 1=enable. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32_callbackenable(FAR struct sdio_dev_s *dev, + sdio_eventset_t eventset) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + + finfo("eventset: %02x\n", eventset); + DEBUGASSERT(priv != NULL); + + priv->cbevents = eventset; + stm32_callback(priv); +} + +/**************************************************************************** + * Name: stm32_registercallback + * + * Description: + * Register a callback that that will be invoked on any media status + * change. Callbacks should not be made from interrupt handlers, rather + * interrupt level events should be handled by calling back on the work + * thread. + * + * When this method is called, all callbacks should be disabled until they + * are enabled via a call to SDIO_CALLBACKENABLE + * + * Input Parameters: + * dev - Device-specific state data + * callback - The funtion to call on the media change + * arg - A caller provided value to return with the callback + * + * Returned Value: + * 0 on success; negated errno on failure. + * + ****************************************************************************/ + +static int stm32_registercallback(FAR struct sdio_dev_s *dev, + worker_t callback, void *arg) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + + /* Disable callbacks and register this callback and is argument */ + + finfo("Register %p(%p)\n", callback, arg); + DEBUGASSERT(priv != NULL); + + priv->cbevents = 0; + priv->cbarg = arg; + priv->callback = callback; + return OK; +} + +/**************************************************************************** + * Name: stm32_dmasupported + * + * Description: + * Return true if the hardware can support DMA + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * + * Returned Value: + * true if DMA is supported. + * + ****************************************************************************/ + +#ifdef CONFIG_SDIO_DMA +static bool stm32_dmasupported(FAR struct sdio_dev_s *dev) +{ + return true; +} +#endif + +/**************************************************************************** + * Name: stm32_dmapreflight + * + * Description: + * Preflight an SDIO DMA operation. If the buffer is not well-formed for + * SDIO DMA transfer (alignment, size, etc.) returns an error. + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * buffer - The memory to DMA to/from + * buflen - The size of the DMA transfer in bytes + * + * Returned Value: + * OK on success; a negated errno on failure + ****************************************************************************/ + +#if defined(CONFIG_SDIO_DMA) && defined(CONFIG_SDIO_PREFLIGHT) +static int stm32_dmapreflight(FAR struct sdio_dev_s *dev, + FAR const uint8_t *buffer, size_t buflen) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + + DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0); + + /* Wide bus operation is required for DMA */ + + if (!priv->widebus) + { + return -EINVAL; + } + + /* DMA must be possible to the buffer */ + + if (!stm32_dmacapable((uintptr_t)buffer, (buflen + 3) >> 2, SDIO_RXDMA32_CONFIG)) + { + return -EFAULT; + } + + return 0; +} +#endif + +/**************************************************************************** + * Name: stm32_dmarecvsetup + * + * Description: + * Setup to perform a read DMA. If the processor supports a data cache, + * then this method will also make sure that the contents of the DMA memory + * and the data cache are coherent. For read transfers this may mean + * invalidating the data cache. + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * buffer - The memory to DMA from + * buflen - The size of the DMA transfer in bytes + * + * Returned Value: + * OK on success; a negated errno on failure + * + ****************************************************************************/ + +#ifdef CONFIG_SDIO_DMA +static int stm32_dmarecvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer, + size_t buflen) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + uint32_t dblocksize; + + DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0); +#ifdef CONFIG_SDIO_PREFLIGHT + DEBUGASSERT(stm32_dmapreflight(dev, buffer, buflen) == 0); +#endif + +#ifdef CONFIG_ARMV7M_DCACHE + /* buffer alignment is required for DMA transfers with dcache */ + + if (((uintptr_t)buffer & (ARMV7M_DCACHE_LINESIZE-1)) != 0 || (buflen & (ARMV7M_DCACHE_LINESIZE-1)) != 0) + { + return -EFAULT; + } +#endif + + /* Reset the DPSM configuration */ + + stm32_datadisable(); + + /* Initialize register sampling */ + + stm32_sampleinit(); + stm32_sample(priv, SAMPLENDX_BEFORE_SETUP); + + /* Save the destination buffer information for use by the interrupt handler */ + + priv->buffer = (uint32_t *)buffer; + priv->remaining = buflen; + priv->dmamode = true; + + /* Then set up the SDIO data path */ + + dblocksize = stm32_log2(buflen) << SDIO_DCTRL_DBLOCKSIZE_SHIFT; + stm32_dataconfig(SDIO_DTIMER_DATATIMEOUT, buflen, dblocksize | SDIO_DCTRL_DTDIR); + + /* Configure the RX DMA */ + + stm32_configxfrints(priv, SDIO_DMARECV_MASK); + + stm32_dmasetup(priv->dma, STM32_SDMMC1_FIFO, (uint32_t)buffer, + (buflen + 3) >> 2, SDIO_RXDMA32_CONFIG); + + /* Force RAM reread */ + + arch_invalidate_dcache((uintptr_t)buffer,(uintptr_t)buffer + buflen); + + /* Start the DMA */ + + stm32_sample(priv, SAMPLENDX_BEFORE_ENABLE); + stm32_dmastart(priv->dma, stm32_dmacallback, priv, false); + stm32_sample(priv, SAMPLENDX_AFTER_SETUP); + + return OK; +} +#endif + +/**************************************************************************** + * Name: stm32_dmasendsetup + * + * Description: + * Setup to perform a write DMA. If the processor supports a data cache, + * then this method will also make sure that the contents of the DMA memory + * and the data cache are coherent. For write transfers, this may mean + * flushing the data cache. + * + * Input Parameters: + * dev - An instance of the SDIO device interface + * buffer - The memory to DMA into + * buflen - The size of the DMA transfer in bytes + * + * Returned Value: + * OK on success; a negated errno on failure + * + ****************************************************************************/ + +#ifdef CONFIG_SDIO_DMA +static int stm32_dmasendsetup(FAR struct sdio_dev_s *dev, + FAR const uint8_t *buffer, size_t buflen) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + uint32_t dblocksize; + + DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0); +#ifdef CONFIG_SDIO_PREFLIGHT + DEBUGASSERT(stm32_dmapreflight(dev, buffer, buflen) == 0); +#endif + +#ifdef CONFIG_ARMV7M_DCACHE + /* buffer alignment is required for DMA transfers with dcache */ + + if (((uintptr_t)buffer & (ARMV7M_DCACHE_LINESIZE-1)) != 0 || (buflen & (ARMV7M_DCACHE_LINESIZE-1)) != 0) + { + return -EFAULT; + } +#endif + + /* Reset the DPSM configuration */ + + stm32_datadisable(); + + /* Initialize register sampling */ + + stm32_sampleinit(); + stm32_sample(priv, SAMPLENDX_BEFORE_SETUP); + + /* Save the source buffer information for use by the interrupt handler */ + + priv->buffer = (uint32_t *)buffer; + priv->remaining = buflen; + priv->dmamode = true; + + /* Then set up the SDIO data path */ + + dblocksize = stm32_log2(buflen) << SDIO_DCTRL_DBLOCKSIZE_SHIFT; + stm32_dataconfig(SDIO_DTIMER_DATATIMEOUT, buflen, dblocksize); + + /* Configure the TX DMA */ + + stm32_dmasetup(priv->dma, STM32_SDMMC1_FIFO, (uint32_t)buffer, + (buflen + 3) >> 2, SDIO_TXDMA32_CONFIG); + + stm32_sample(priv, SAMPLENDX_BEFORE_ENABLE); + + /* Flush cache to physical memory */ + + arch_flush_dcache((uintptr_t)buffer, (uintptr_t)buffer + buflen); + + /* Start the DMA */ + + stm32_dmastart(priv->dma, stm32_dmacallback, priv, false); + stm32_sample(priv, SAMPLENDX_AFTER_SETUP); + + /* Enable TX interrupts */ + + stm32_configxfrints(priv, SDIO_DMASEND_MASK); + + return OK; +} +#endif + +/**************************************************************************** + * Initialization/uninitialization/reset + ****************************************************************************/ +/**************************************************************************** + * Name: stm32_callback + * + * Description: + * Perform callback. + * + * Assumptions: + * This function does not execute in the context of an interrupt handler. + * It may be invoked on any user thread or scheduled on the work thread + * from an interrupt handler. + * + ****************************************************************************/ + +static void stm32_callback(void *arg) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)arg; + + /* Is a callback registered? */ + + DEBUGASSERT(priv != NULL); + finfo("Callback %p(%p) cbevents: %02x cdstatus: %02x\n", + priv->callback, priv->cbarg, priv->cbevents, priv->cdstatus); + + if (priv->callback) + { + /* Yes.. Check for enabled callback events */ + + if ((priv->cdstatus & SDIO_STATUS_PRESENT) != 0) + { + /* Media is present. Is the media inserted event enabled? */ + + if ((priv->cbevents & SDIOMEDIA_INSERTED) == 0) + { + /* No... return without performing the callback */ + + return; + } + } + else + { + /* Media is not present. Is the media eject event enabled? */ + + if ((priv->cbevents & SDIOMEDIA_EJECTED) == 0) + { + /* No... return without performing the callback */ + + return; + } + } + + /* Perform the callback, disabling further callbacks. Of course, the + * the callback can (and probably should) re-enable callbacks. + */ + + priv->cbevents = 0; + + /* Callbacks cannot be performed in the context of an interrupt handler. + * If we are in an interrupt handler, then queue the callback to be + * performed later on the work thread. + */ + + if (up_interrupt_context()) + { + /* Yes.. queue it */ + + finfo("Queuing callback to %p(%p)\n", priv->callback, priv->cbarg); + (void)work_queue(HPWORK, &priv->cbwork, (worker_t)priv->callback, priv->cbarg, 0); + } + else + { + /* No.. then just call the callback here */ + + finfo("Callback to %p(%p)\n", priv->callback, priv->cbarg); + priv->callback(priv->cbarg); + } + } +} + +/**************************************************************************** + * Name: stm32_default + * + * Description: + * Restore SDIO registers to their default, reset values + * + ****************************************************************************/ + +static void stm32_default(void) +{ + putreg32(SDIO_POWER_RESET, STM32_SDMMC1_POWER); + putreg32(SDIO_CLKCR_RESET, STM32_SDMMC1_CLKCR); + putreg32(SDIO_ARG_RESET, STM32_SDMMC1_ARG); + putreg32(SDIO_CMD_RESET, STM32_SDMMC1_CMD); + putreg32(SDIO_DTIMER_RESET, STM32_SDMMC1_DTIMER); + putreg32(SDIO_DLEN_RESET, STM32_SDMMC1_DLEN); + putreg32(SDIO_DCTRL_RESET, STM32_SDMMC1_DCTRL); + putreg32(SDIO_ICR_RESET, STM32_SDMMC1_ICR); + putreg32(SDIO_MASK_RESET, STM32_SDMMC1_MASK); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sdio_initialize + * + * Description: + * Initialize SDIO for operation. + * + * Input Parameters: + * slotno - Not used. + * + * Returned Values: + * A reference to an SDIO interface structure. NULL is returned on failures. + * + ****************************************************************************/ + +FAR struct sdio_dev_s *sdio_initialize(int slotno) +{ + /* There is only one slot */ + + struct stm32_dev_s *priv = &g_sdiodev; + + /* Initialize the SDIO slot structure */ + + sem_init(&priv->waitsem, 0, 0); + priv->waitwdog = wd_create(); + DEBUGASSERT(priv->waitwdog); + + /* Allocate a DMA channel */ + +#ifdef CONFIG_SDIO_DMA + priv->dma = stm32_dmachannel(SDIO_DMACHAN); + DEBUGASSERT(priv->dma); +#endif + + /* Configure GPIOs for 4-bit, wide-bus operation (the chip is capable of + * 8-bit wide bus operation but D4-D7 are not configured). + * + * If bus is multiplexed then there is a custom bus configuration utility + * in the scope of the board support package. + */ + +#ifndef CONFIG_SDIO_MUXBUS + stm32_configgpio(GPIO_SDMMC1_D0); +#ifndef CONFIG_SDIO_WIDTH_D1_ONLY + stm32_configgpio(GPIO_SDMMC1_D1); + stm32_configgpio(GPIO_SDMMC1_D2); + stm32_configgpio(GPIO_SDMMC1_D3); +#endif + stm32_configgpio(GPIO_SDMMC1_CK); + stm32_configgpio(GPIO_SDMMC1_CMD); +#endif + + /* Reset the card and assure that it is in the initial, unconfigured + * state. + */ + + stm32_reset(&priv->dev); + return &g_sdiodev.dev; +} + +/**************************************************************************** + * Name: sdio_mediachange + * + * Description: + * Called by board-specific logic -- posssible from an interrupt handler -- + * in order to signal to the driver that a card has been inserted or + * removed from the slot + * + * Input Parameters: + * dev - An instance of the SDIO driver device state structure. + * cardinslot - true is a card has been detected in the slot; false if a + * card has been removed from the slot. Only transitions + * (inserted->removed or removed->inserted should be reported) + * + * Returned Values: + * None + * + ****************************************************************************/ + +void sdio_mediachange(FAR struct sdio_dev_s *dev, bool cardinslot) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + uint8_t cdstatus; + irqstate_t flags; + + /* Update card status */ + + flags = enter_critical_section(); + cdstatus = priv->cdstatus; + if (cardinslot) + { + priv->cdstatus |= SDIO_STATUS_PRESENT; + } + else + { + priv->cdstatus &= ~SDIO_STATUS_PRESENT; + } + + leave_critical_section(flags); + + finfo("cdstatus OLD: %02x NEW: %02x\n", cdstatus, priv->cdstatus); + + /* Perform any requested callback if the status has changed */ + + if (cdstatus != priv->cdstatus) + { + stm32_callback(priv); + } +} + +/**************************************************************************** + * Name: sdio_wrprotect + * + * Description: + * Called by board-specific logic to report if the card in the slot is + * mechanically write protected. + * + * Input Parameters: + * dev - An instance of the SDIO driver device state structure. + * wrprotect - true is a card is writeprotected. + * + * Returned Values: + * None + * + ****************************************************************************/ + +void sdio_wrprotect(FAR struct sdio_dev_s *dev, bool wrprotect) +{ + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev; + irqstate_t flags; + + /* Update card status */ + + flags = enter_critical_section(); + if (wrprotect) + { + priv->cdstatus |= SDIO_STATUS_WRPROTECTED; + } + else + { + priv->cdstatus &= ~SDIO_STATUS_WRPROTECTED; + } + finfo("cdstatus: %02x\n", priv->cdstatus); + leave_critical_section(flags); +} +#endif /* CONFIG_STM32F7_SDMMC1 */ diff --git a/arch/arm/src/stm32f7/stm32_sdmmc.h b/arch/arm/src/stm32f7/stm32_sdmmc.h new file mode 100644 index 00000000000..29e14683cea --- /dev/null +++ b/arch/arm/src/stm32f7/stm32_sdmmc.h @@ -0,0 +1,129 @@ +/************************************************************************************ + * arch/arm/src/stm32/stm32_sdio.h + * + * Copyright (C) 2009, 2011, 2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32F7_STM32_SDMMC_H +#define __ARCH_ARM_SRC_STM32F7_STM32_SDMMC_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include "chip/stm32_sdmmc.h" + +#include +#include +#include + +#include "chip.h" + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: sdio_initialize + * + * Description: + * Initialize SDIO for operation. + * + * Input Parameters: + * slotno - Not used. + * + * Returned Values: + * A reference to an SDIO interface structure. NULL is returned on failures. + * + ****************************************************************************/ + +struct sdio_dev_s; /* See include/nuttx/sdio.h */ +FAR struct sdio_dev_s *sdio_initialize(int slotno); + +/**************************************************************************** + * Name: sdio_mediachange + * + * Description: + * Called by board-specific logic -- posssible from an interrupt handler -- + * in order to signal to the driver that a card has been inserted or + * removed from the slot + * + * Input Parameters: + * dev - An instance of the SDIO driver device state structure. + * cardinslot - true is a card has been detected in the slot; false if a + * card has been removed from the slot. Only transitions + * (inserted->removed or removed->inserted should be reported) + * + * Returned Values: + * None + * + ****************************************************************************/ + +void sdio_mediachange(FAR struct sdio_dev_s *dev, bool cardinslot); + +/**************************************************************************** + * Name: sdio_wrprotect + * + * Description: + * Called by board-specific logic to report if the card in the slot is + * mechanically write protected. + * + * Input Parameters: + * dev - An instance of the SDIO driver device state structure. + * wrprotect - true is a card is writeprotected. + * + * Returned Values: + * None + * + ****************************************************************************/ + +void sdio_wrprotect(FAR struct sdio_dev_s *dev, bool wrprotect); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM_SRC_STM32F7_STM32_SDMMC_H */ + diff --git a/configs/stm32f746-ws/include/board.h b/configs/stm32f746-ws/include/board.h index 1f5e3f030cf..df3474497ae 100644 --- a/configs/stm32f746-ws/include/board.h +++ b/configs/stm32f746-ws/include/board.h @@ -213,6 +213,48 @@ #define GPIO_I2C1_SCL GPIO_I2C1_SCL_1 #define GPIO_I2C1_SDA GPIO_I2C1_SDA_1 +/* SDMMC */ + +/* Stream selections are arbitrary for now but might become important in the future + * if we set aside more DMA channels/streams. + * + * SDIO DMA + * DMAMAP_SDMMC1_1 = Channel 4, Stream 3 + * DMAMAP_SDMMC1_2 = Channel 4, Stream 6 + */ + +#define DMAMAP_SDMMC1 DMAMAP_SDMMC1_1 + +/* SDIO dividers. Note that slower clocking is required when DMA is disabled + * in order to avoid RX overrun/TX underrun errors due to delayed responses + * to service FIFOs in interrupt driven mode. These values have not been + * tuned!!! + * + * SDIOCLK=48MHz, SDIO_CK=SDIOCLK/(118+2)=400 KHz + */ + +#define SDMMC1_INIT_CLKDIV (118 << SDIO_CLKCR_CLKDIV_SHIFT) + +/* DMA ON: SDIOCLK=48MHz, SDIO_CK=SDIOCLK/(1+2)=16 MHz + * DMA OFF: SDIOCLK=48MHz, SDIO_CK=SDIOCLK/(2+2)=12 MHz + */ + +#ifdef CONFIG_SDIO_DMA +# define SDMMC1_MMCXFR_CLKDIV (1 << SDIO_CLKCR_CLKDIV_SHIFT) +#else +# define SDMMC1_MMCXFR_CLKDIV (2 << SDIO_CLKCR_CLKDIV_SHIFT) +#endif + +/* DMA ON: SDIOCLK=48MHz, SDIO_CK=SDIOCLK/(1+2)=16 MHz + * DMA OFF: SDIOCLK=48MHz, SDIO_CK=SDIOCLK/(2+2)=12 MHz + */ + +#ifdef CONFIG_SDIO_DMA +# define SDMMC1_SDXFR_CLKDIV (1 << SDIO_CLKCR_CLKDIV_SHIFT) +#else +# define SDMMC1_SDXFR_CLKDIV (2 << SDIO_CLKCR_CLKDIV_SHIFT) +#endif + /************************************************************************************ * Public Data ************************************************************************************/ diff --git a/configs/stm32f746-ws/nsh/defconfig b/configs/stm32f746-ws/nsh/defconfig index df3654343f0..c282d9ee49d 100644 --- a/configs/stm32f746-ws/nsh/defconfig +++ b/configs/stm32f746-ws/nsh/defconfig @@ -170,7 +170,7 @@ CONFIG_STM32F7_HAVE_LTDC=y # CONFIG_STM32F7_ADC is not set # CONFIG_STM32F7_CAN is not set # CONFIG_STM32F7_DAC is not set -# CONFIG_STM32F7_DMA is not set +CONFIG_STM32F7_DMA=y CONFIG_STM32F7_I2C=y # CONFIG_STM32F7_SAI is not set CONFIG_STM32F7_SPI=y @@ -185,7 +185,7 @@ CONFIG_STM32F7_ADC1=y # CONFIG_STM32F7_CRC is not set # CONFIG_STM32F7_CRYP is not set # CONFIG_STM32F7_DMA1 is not set -# CONFIG_STM32F7_DMA2 is not set +CONFIG_STM32F7_DMA2=y # CONFIG_STM32F7_DAC1 is not set # CONFIG_STM32F7_DAC2 is not set # CONFIG_STM32F7_DCMI is not set @@ -203,7 +203,7 @@ CONFIG_STM32F7_I2C1=y # CONFIG_STM32F7_SAI1 is not set # CONFIG_STM32F7_RNG is not set # CONFIG_STM32F7_SAI2 is not set -# CONFIG_STM32F7_SDMMC1 is not set +CONFIG_STM32F7_SDMMC1=y # CONFIG_STM32F7_SPDIFRX is not set CONFIG_STM32F7_SPI1=y # CONFIG_STM32F7_SPI2 is not set @@ -243,7 +243,7 @@ CONFIG_STM32F7_USART6=y # # CONFIG_ARCH_NOINTC is not set # CONFIG_ARCH_VECNOTIRQ is not set -# CONFIG_ARCH_DMA is not set +CONFIG_ARCH_DMA=y CONFIG_ARCH_HAVE_IRQPRIO=y # CONFIG_ARCH_L2CACHE is not set # CONFIG_ARCH_HAVE_COHERENT_DCACHE is not set @@ -464,6 +464,7 @@ CONFIG_SPI_EXCHANGE=y # CONFIG_SPI_CRCGENERATION is not set # CONFIG_I2S is not set CONFIG_ADC=y +CONFIG_SDIO_DMA=y # # Timer Driver Support @@ -485,7 +486,15 @@ CONFIG_WATCHDOG_DEVPATH="/dev/watchdog0" # CONFIG_USERLED is not set # CONFIG_RGBLED is not set # CONFIG_PCA9635PW is not set -# CONFIG_MMCSD is not set + +CONFIG_MMCSD=y +CONFIG_MMCSD_NSLOTS=1 +CONFIG_MMCSD_MULTIBLOCK_DISABLE=y +CONFIG_ARCH_HAVE_SDIO=y +CONFIG_ARCH_HAVE_SDIOWAIT_WRCOMPLETE=y +CONFIG_MMCSD_SDIO=y +CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE=y + # CONFIG_MODEM is not set # CONFIG_MTD is not set # CONFIG_EEPROM is not set @@ -578,19 +587,27 @@ CONFIG_USART6_2STOP=0 # CONFIG_DISABLE_MOUNTPOINT is not set # CONFIG_FS_AUTOMOUNTER is not set # CONFIG_DISABLE_PSEUDOFS_OPERATIONS is not set -# CONFIG_FS_READABLE is not set -# CONFIG_FS_WRITABLE is not set +CONFIG_FS_READABLE=y +CONFIG_FS_WRITABLE=y # CONFIG_FS_NAMED_SEMAPHORES is not set CONFIG_FS_MQUEUE_MPATH="/var/mqueue" # CONFIG_FS_RAMMAP is not set -# CONFIG_FS_FAT is not set +CONFIG_FS_FAT=y +CONFIG_FAT_LCNAMES=y +CONFIG_FAT_LFN=y +CONFIG_FAT_MAXFNAME=32 # CONFIG_FS_NXFFS is not set -# CONFIG_FS_ROMFS is not set -# CONFIG_FS_TMPFS is not set +CONFIG_FS_ROMFS=y +CONFIG_FS_TMPFS=y +CONFIG_FS_TMPFS_BLOCKSIZE=512 +CONFIG_FS_TMPFS_DIRECTORY_ALLOCGUARD=64 +CONFIG_FS_TMPFS_DIRECTORY_FREEGUARD=128 +CONFIG_FS_TMPFS_FILE_ALLOCGUARD=512 +CONFIG_FS_TMPFS_FILE_FREEGUARD=1024 # CONFIG_FS_SMARTFS is not set # CONFIG_FS_BINFS is not set -# CONFIG_FS_PROCFS is not set -# CONFIG_FS_UNIONFS is not set +CONFIG_FS_PROCFS=y +CONFIG_FS_PROCFS_REGISTER=y # # System Logging diff --git a/configs/stm32f746-ws/src/stm32_spi.c b/configs/stm32f746-ws/src/stm32_spi.c index e973ca9b82b..517ea3761b2 100644 --- a/configs/stm32f746-ws/src/stm32_spi.c +++ b/configs/stm32f746-ws/src/stm32_spi.c @@ -101,7 +101,7 @@ void weak_function stm32_spidev_initialize(void) #ifdef CONFIG_STM32F7_SPI1 void stm32_spi1select(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool selected) { - spidbg("devid: %d CS: %s\n", (int)devid, selected ? "assert" : "de-assert"); + spiinfo("devid: %d CS: %s\n", (int)devid, selected ? "assert" : "de-assert"); } uint8_t stm32_spi1status(FAR struct spi_dev_s *dev, enum spi_dev_e devid) @@ -113,7 +113,7 @@ uint8_t stm32_spi1status(FAR struct spi_dev_s *dev, enum spi_dev_e devid) #ifdef CONFIG_STM32F7_SPI2 void stm32_spi2select(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool selected) { - spidbg("devid: %d CS: %s\n", (int)devid, selected ? "assert" : "de-assert"); + spiinfo("devid: %d CS: %s\n", (int)devid, selected ? "assert" : "de-assert"); } uint8_t stm32_spi2status(FAR struct spi_dev_s *dev, enum spi_dev_e devid) @@ -125,7 +125,7 @@ uint8_t stm32_spi2status(FAR struct spi_dev_s *dev, enum spi_dev_e devid) #ifdef CONFIG_STM32F7_SPI3 void stm32_spi3select(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool selected) { - spidbg("devid: %d CS: %s\n", (int)devid, selected ? "assert" : "de-assert"); + spiinfo("devid: %d CS: %s\n", (int)devid, selected ? "assert" : "de-assert"); } uint8_t stm32_spi3status(FAR struct spi_dev_s *dev, enum spi_dev_e devid) @@ -137,7 +137,7 @@ uint8_t stm32_spi3status(FAR struct spi_dev_s *dev, enum spi_dev_e devid) #ifdef CONFIG_STM32F7_SPI4 void stm32_spi4select(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool selected) { - spidbg("devid: %d CS: %s\n", (int)devid, selected ? "assert" : "de-assert"); + spiinfo("devid: %d CS: %s\n", (int)devid, selected ? "assert" : "de-assert"); } uint8_t stm32_spi4status(FAR struct spi_dev_s *dev, enum spi_dev_e devid) @@ -149,7 +149,7 @@ uint8_t stm32_spi4status(FAR struct spi_dev_s *dev, enum spi_dev_e devid) #ifdef CONFIG_STM32F7_SPI5 void stm32_spi5select(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool selected) { - spidbg("devid: %d CS: %s\n", (int)devid, selected ? "assert" : "de-assert"); + spiinfo("devid: %d CS: %s\n", (int)devid, selected ? "assert" : "de-assert"); } uint8_t stm32_spi5status(FAR struct spi_dev_s *dev, enum spi_dev_e devid) @@ -161,7 +161,7 @@ uint8_t stm32_spi5status(FAR struct spi_dev_s *dev, enum spi_dev_e devid) #ifdef CONFIG_STM32F7_SPI6 void stm32_spi6select(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool selected) { - spidbg("devid: %d CS: %s\n", (int)devid, selected ? "assert" : "de-assert"); + spiinfo("devid: %d CS: %s\n", (int)devid, selected ? "assert" : "de-assert"); } uint8_t stm32_spi6status(FAR struct spi_dev_s *dev, enum spi_dev_e devid) From 98e7e5c402aa79ca07a6a8f8766e6aed3ff9eb04 Mon Sep 17 00:00:00 2001 From: Lok Tep Date: Fri, 17 Jun 2016 17:47:23 +0200 Subject: [PATCH 02/24] usb copy --- arch/arm/src/stm32f7/Make.defs | 8 + arch/arm/src/stm32f7/chip/stm32_otg.h | 1022 +++ .../src/stm32f7/chip/stm32f74xx75xx_sdmmc.h | 2 +- arch/arm/src/stm32f7/stm32_i2c.c | 2 +- arch/arm/src/stm32f7/stm32_otg.h | 139 + arch/arm/src/stm32f7/stm32_otgdev.c | 5666 +++++++++++++++++ arch/arm/src/stm32f7/stm32_otghost.c | 5306 +++++++++++++++ arch/arm/src/stm32f7/stm32_sdmmc.h | 2 +- 8 files changed, 12144 insertions(+), 3 deletions(-) create mode 100644 arch/arm/src/stm32f7/chip/stm32_otg.h create mode 100644 arch/arm/src/stm32f7/stm32_otg.h create mode 100644 arch/arm/src/stm32f7/stm32_otgdev.c create mode 100644 arch/arm/src/stm32f7/stm32_otghost.c diff --git a/arch/arm/src/stm32f7/Make.defs b/arch/arm/src/stm32f7/Make.defs index 123c70ffc2f..1474c0b57d1 100644 --- a/arch/arm/src/stm32f7/Make.defs +++ b/arch/arm/src/stm32f7/Make.defs @@ -151,6 +151,14 @@ ifeq ($(CONFIG_STM32F7_SDMMC1),y) CHIP_CSRCS += stm32_sdmmc.c endif +ifeq ($(CONFIG_USBDEV),y) +CHIP_CSRCS += stm32_otgdev.c +endif + +ifeq ($(CONFIG_USBHOST),y) +CHIP_CSRCS += stm32_otghost.c +endif + ifeq ($(CONFIG_STM32F7_TIM),y) CHIP_CSRCS += stm32_tim.c endif diff --git a/arch/arm/src/stm32f7/chip/stm32_otg.h b/arch/arm/src/stm32f7/chip/stm32_otg.h new file mode 100644 index 00000000000..ee064971025 --- /dev/null +++ b/arch/arm/src/stm32f7/chip/stm32_otg.h @@ -0,0 +1,1022 @@ +/**************************************************************************************************** + * arch/arm/src/stm32f7/chip/stm32f_otgfs.h + * + * Copyright (C) 2012, 2014-2015 Gregory Nutt. All rights reserved. + * Copyright (C) 2016 Omni Hoverboards Inc. All rights reserved. + * Author: Gregory Nutt + * Paul Alexander Patience + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32F7_CHIP_STM32_OTG_H +#define __ARCH_ARM_SRC_STM32F7_CHIP_STM32_OTG_H + +/**************************************************************************************************** + * Included Files + ****************************************************************************************************/ +/**************************************************************************************************** + * Pre-processor Definitions + ****************************************************************************************************/ +/* General definitions */ + +#define OTG_EPTYPE_CTRL (0) /* Control */ +#define OTG_EPTYPE_ISOC (1) /* Isochronous */ +#define OTG_EPTYPE_BULK (2) /* Bulk */ +#define OTG_EPTYPE_INTR (3) /* Interrupt */ + +#define OTG_PID_DATA0 (0) +#define OTG_PID_DATA2 (1) +#define OTG_PID_DATA1 (2) +#define OTG_PID_MDATA (3) /* Non-control */ +#define OTG_PID_SETUP (3) /* Control */ + +/* Register Offsets *********************************************************************************/ +/* Core global control and status registers */ + +#define STM32_OTG_GOTGCTL_OFFSET 0x0000 /* Control and status register */ +#define STM32_OTG_GOTGINT_OFFSET 0x0004 /* Interrupt register */ +#define STM32_OTG_GAHBCFG_OFFSET 0x0008 /* AHB configuration register */ +#define STM32_OTG_GUSBCFG_OFFSET 0x000c /* USB configuration register */ +#define STM32_OTG_GRSTCTL_OFFSET 0x0010 /* Reset register */ +#define STM32_OTG_GINTSTS_OFFSET 0x0014 /* Core interrupt register */ +#define STM32_OTG_GINTMSK_OFFSET 0x0018 /* Interrupt mask register */ +#define STM32_OTG_GRXSTSR_OFFSET 0x001c /* Receive status debug read/OTG status read register */ +#define STM32_OTG_GRXSTSP_OFFSET 0x0020 /* Receive status debug read/OTG status pop register */ +#define STM32_OTG_GRXFSIZ_OFFSET 0x0024 /* Receive FIFO size register */ +#define STM32_OTG_HNPTXFSIZ_OFFSET 0x0028 /* Host non-periodic transmit FIFO size register */ +#define STM32_OTG_DIEPTXF0_OFFSET 0x0028 /* Endpoint 0 Transmit FIFO size */ +#define STM32_OTG_HNPTXSTS_OFFSET 0x002c /* Non-periodic transmit FIFO/queue status register */ +#define STM32_OTG_GCCFG_OFFSET 0x0038 /* General core configuration register */ +#define STM32_OTG_CID_OFFSET 0x003c /* Core ID register */ +#define STM32_OTG_HPTXFSIZ_OFFSET 0x0100 /* Host periodic transmit FIFO size register */ + +#define STM32_OTG_DIEPTXF_OFFSET(n) (104+(((n)-1) << 2)) +#define STM32_OTG_DIEPTXF1_OFFSET 0x0104 /* Device IN endpoint transmit FIFO1 size register */ +#define STM32_OTG_DIEPTXF2_OFFSET 0x0108 /* Device IN endpoint transmit FIFO2 size register */ +#define STM32_OTG_DIEPTXF3_OFFSET 0x010c /* Device IN endpoint transmit FIFO3 size register */ + +/* Host-mode control and status registers */ + +#define STM32_OTG_HCFG_OFFSET 0x0400 /* Host configuration register */ +#define STM32_OTG_HFIR_OFFSET 0x0404 /* Host frame interval register */ +#define STM32_OTG_HFNUM_OFFSET 0x0408 /* Host frame number/frame time remaining register */ +#define STM32_OTG_HPTXSTS_OFFSET 0x0410 /* Host periodic transmit FIFO/queue status register */ +#define STM32_OTG_HAINT_OFFSET 0x0414 /* Host all channels interrupt register */ +#define STM32_OTG_HAINTMSK_OFFSET 0x0418 /* Host all channels interrupt mask register */ +#define STM32_OTG_HPRT_OFFSET 0x0440 /* Host port control and status register */ + +#define STM32_OTG_CHAN_OFFSET(n) (0x500 + ((n) << 5) +#define STM32_OTG_HCCHAR_CHOFFSET 0x0000 /* Host channel characteristics register */ +#define STM32_OTG_HCINT_CHOFFSET 0x0008 /* Host channel interrupt register */ +#define STM32_OTG_HCINTMSK_CHOFFSET 0x000c /* Host channel interrupt mask register */ +#define STM32_OTG_HCTSIZ_CHOFFSET 0x0010 /* Host channel interrupt register */ + +#define STM32_OTG_HCCHAR_OFFSET(n) (0x500 + ((n) << 5)) +#define STM32_OTG_HCCHAR0_OFFSET 0x0500 /* Host channel-0 characteristics register */ +#define STM32_OTG_HCCHAR1_OFFSET 0x0520 /* Host channel-1 characteristics register */ +#define STM32_OTG_HCCHAR2_OFFSET 0x0540 /* Host channel-2 characteristics register */ +#define STM32_OTG_HCCHAR3_OFFSET 0x0560 /* Host channel-3 characteristics register */ +#define STM32_OTG_HCCHAR4_OFFSET 0x0580 /* Host channel-4 characteristics register */ +#define STM32_OTG_HCCHAR5_OFFSET 0x05a0 /* Host channel-5 characteristics register */ +#define STM32_OTG_HCCHAR6_OFFSET 0x05c0 /* Host channel-6 characteristics register */ +#define STM32_OTG_HCCHAR7_OFFSET 0x05e0 /* Host channel-7 characteristics register */ + +#define STM32_OTG_HCINT_OFFSET(n) (0x508 + ((n) << 5)) +#define STM32_OTG_HCINT0_OFFSET 0x0508 /* Host channel-0 interrupt register */ +#define STM32_OTG_HCINT1_OFFSET 0x0528 /* Host channel-1 interrupt register */ +#define STM32_OTG_HCINT2_OFFSET 0x0548 /* Host channel-2 interrupt register */ +#define STM32_OTG_HCINT3_OFFSET 0x0568 /* Host channel-3 interrupt register */ +#define STM32_OTG_HCINT4_OFFSET 0x0588 /* Host channel-4 interrupt register */ +#define STM32_OTG_HCINT5_OFFSET 0x05a8 /* Host channel-5 interrupt register */ +#define STM32_OTG_HCINT6_OFFSET 0x05c8 /* Host channel-6 interrupt register */ +#define STM32_OTG_HCINT7_OFFSET 0x05e8 /* Host channel-7 interrupt register */ + +#define STM32_OTG_HCINTMSK_OFFSET(n) (0x50c + ((n) << 5)) +#define STM32_OTG_HCINTMSK0_OFFSET 0x050c /* Host channel-0 interrupt mask register */ +#define STM32_OTG_HCINTMSK1_OFFSET 0x052c /* Host channel-1 interrupt mask register */ +#define STM32_OTG_HCINTMSK2_OFFSET 0x054c /* Host channel-2 interrupt mask register */ +#define STM32_OTG_HCINTMSK3_OFFSET 0x056c /* Host channel-3 interrupt mask register */ +#define STM32_OTG_HCINTMSK4_OFFSET 0x058c /* Host channel-4 interrupt mask register */ +#define STM32_OTG_HCINTMSK5_OFFSET 0x05ac /* Host channel-5 interrupt mask register */ +#define STM32_OTG_HCINTMSK6_OFFSET 0x05cc /* Host channel-6 interrupt mask register */ +#define STM32_OTG_HCINTMSK7_OFFSET 0x05ec /* Host channel-7 interrupt mask register */ + +#define STM32_OTG_HCTSIZ_OFFSET(n) (0x510 + ((n) << 5)) +#define STM32_OTG_HCTSIZ0_OFFSET 0x0510 /* Host channel-0 interrupt register */ +#define STM32_OTG_HCTSIZ1_OFFSET 0x0530 /* Host channel-1 interrupt register */ +#define STM32_OTG_HCTSIZ2_OFFSET 0x0550 /* Host channel-2 interrupt register */ +#define STM32_OTG_HCTSIZ3_OFFSET 0x0570 /* Host channel-3 interrupt register */ +#define STM32_OTG_HCTSIZ4_OFFSET 0x0590 /* Host channel-4 interrupt register */ +#define STM32_OTG_HCTSIZ5_OFFSET 0x05b0 /* Host channel-5 interrupt register */ +#define STM32_OTG_HCTSIZ6_OFFSET 0x05d0 /* Host channel-6 interrupt register */ +#define STM32_OTG_HCTSIZ7_OFFSET 0x05f0 /* Host channel-7 interrupt register */ + +/* Device-mode control and status registers */ + +#define STM32_OTG_DCFG_OFFSET 0x0800 /* Device configuration register */ +#define STM32_OTG_DCTL_OFFSET 0x0804 /* Device control register */ +#define STM32_OTG_DSTS_OFFSET 0x0808 /* Device status register */ +#define STM32_OTG_DIEPMSK_OFFSET 0x0810 /* Device IN endpoint common interrupt mask register */ +#define STM32_OTG_DOEPMSK_OFFSET 0x0814 /* Device OUT endpoint common interrupt mask register */ +#define STM32_OTG_DAINT_OFFSET 0x0818 /* Device all endpoints interrupt register */ +#define STM32_OTG_DAINTMSK_OFFSET 0x081c /* All endpoints interrupt mask register */ +#define STM32_OTG_DVBUSDIS_OFFSET 0x0828 /* Device VBUS discharge time register */ +#define STM32_OTG_DVBUSPULSE_OFFSET 0x082c /* Device VBUS pulsing time register */ +#define STM32_OTG_DIEPEMPMSK_OFFSET 0x0834 /* Device IN endpoint FIFO empty interrupt mask register */ + +#define STM32_OTG_DIEP_OFFSET(n) (0x0900 + ((n) << 5)) +#define STM32_OTG_DIEPCTL_EPOFFSET 0x0000 /* Device endpoint control register */ +#define STM32_OTG_DIEPINT_EPOFFSET 0x0008 /* Device endpoint interrupt register */ +#define STM32_OTG_DIEPTSIZ_EPOFFSET 0x0010 /* Device IN endpoint transfer size register */ +#define STM32_OTG_DTXFSTS_EPOFFSET 0x0018 /* Device IN endpoint transmit FIFO status register */ + +#define STM32_OTG_DIEPCTL_OFFSET(n) (0x0900 + ((n) << 5)) +#define STM32_OTG_DIEPCTL0_OFFSET 0x0900 /* Device control IN endpoint 0 control register */ +#define STM32_OTG_DIEPCTL1_OFFSET 0x0920 /* Device control IN endpoint 2 control register */ +#define STM32_OTG_DIEPCTL2_OFFSET 0x0940 /* Device control IN endpoint 3 control register */ +#define STM32_OTG_DIEPCTL3_OFFSET 0x0960 /* Device control IN endpoint 4 control register */ + +#define STM32_OTG_DIEPINT_OFFSET(n) (0x0908 + ((n) << 5)) +#define STM32_OTG_DIEPINT0_OFFSET 0x0908 /* Device endpoint-0 interrupt register */ +#define STM32_OTG_DIEPINT1_OFFSET 0x0928 /* Device endpoint-1 interrupt register */ +#define STM32_OTG_DIEPINT2_OFFSET 0x0948 /* Device endpoint-2 interrupt register */ +#define STM32_OTG_DIEPINT3_OFFSET 0x0968 /* Device endpoint-3 interrupt register */ + +#define STM32_OTG_DIEPTSIZ_OFFSET(n) (0x910 + ((n) << 5)) +#define STM32_OTG_DIEPTSIZ0_OFFSET 0x0910 /* Device IN endpoint 0 transfer size register */ +#define STM32_OTG_DIEPTSIZ1_OFFSET 0x0930 /* Device IN endpoint 1 transfer size register */ +#define STM32_OTG_DIEPTSIZ2_OFFSET 0x0950 /* Device IN endpoint 2 transfer size register */ +#define STM32_OTG_DIEPTSIZ3_OFFSET 0x0970 /* Device IN endpoint 3 transfer size register */ + +#define STM32_OTG_DTXFSTS_OFFSET(n) (0x0918 + ((n) << 5)) +#define STM32_OTG_DTXFSTS0_OFFSET 0x0918 /* Device OUT endpoint-0 TxFIFO status register */ +#define STM32_OTG_DTXFSTS1_OFFSET 0x0938 /* Device OUT endpoint-1 TxFIFO status register */ +#define STM32_OTG_DTXFSTS2_OFFSET 0x0958 /* Device OUT endpoint-2 TxFIFO status register */ +#define STM32_OTG_DTXFSTS3_OFFSET 0x0978 /* Device OUT endpoint-3 TxFIFO status register */ + +#define STM32_OTG_DOEP_OFFSET(n) (0x0b00 + ((n) << 5)) +#define STM32_OTG_DOEPCTL_EPOFFSET 0x0000 /* Device control OUT endpoint 0 control register */ +#define STM32_OTG_DOEPINT_EPOFFSET 0x0008 /* Device endpoint-x interrupt register */ + +#define STM32_OTG_DOEPCTL_OFFSET(n) (0x0b00 + ((n) << 5)) +#define STM32_OTG_DOEPCTL0_OFFSET 0x00b00 /* Device OUT endpoint 0 control register */ +#define STM32_OTG_DOEPCTL1_OFFSET 0x00b20 /* Device OUT endpoint 1 control register */ +#define STM32_OTG_DOEPCTL2_OFFSET 0x00b40 /* Device OUT endpoint 2 control register */ +#define STM32_OTG_DOEPCTL3_OFFSET 0x00b60 /* Device OUT endpoint 3 control register */ + +#define STM32_OTG_DOEPINT_OFFSET(n) (0x0b08 + ((n) << 5)) +#define STM32_OTG_DOEPINT0_OFFSET 0x00b08 /* Device endpoint-0 interrupt register */ +#define STM32_OTG_DOEPINT1_OFFSET 0x00b28 /* Device endpoint-1 interrupt register */ +#define STM32_OTG_DOEPINT2_OFFSET 0x00b48 /* Device endpoint-2 interrupt register */ +#define STM32_OTG_DOEPINT3_OFFSET 0x00b68 /* Device endpoint-3 interrupt register */ + +#define STM32_OTG_DOEPTSIZ_OFFSET(n) (0x0b10 + ((n) << 5)) +#define STM32_OTG_DOEPTSIZ0_OFFSET 0x00b10 /* Device OUT endpoint-0 transfer size register */ +#define STM32_OTG_DOEPTSIZ1_OFFSET 0x00b30 /* Device OUT endpoint-1 transfer size register */ +#define STM32_OTG_DOEPTSIZ2_OFFSET 0x00b50 /* Device OUT endpoint-2 transfer size register */ +#define STM32_OTG_DOEPTSIZ3_OFFSET 0x00b70 /* Device OUT endpoint-3 transfer size register */ + +/* Power and clock gating registers */ + +#define STM32_OTG_PCGCCTL_OFFSET 0x0e00 /* Power and clock gating control register */ + +/* Data FIFO (DFIFO) access registers */ + +#define STM32_OTG_DFIFO_DEP_OFFSET(n) (0x1000 + ((n) << 12)) +#define STM32_OTG_DFIFO_HCH_OFFSET(n) (0x1000 + ((n) << 12)) + +#define STM32_OTG_DFIFO_DEP0_OFFSET 0x1000 /* 0x1000-0x1ffc Device IN/OUT Endpoint 0 DFIFO Write/Read Access */ +#define STM32_OTG_DFIFO_HCH0_OFFSET 0x1000 /* 0x1000-0x1ffc Host OUT/IN Channel 0 DFIFO Read/Write Access */ + +#define STM32_OTG_DFIFO_DEP1_OFFSET 0x2000 /* 0x2000-0x2ffc Device IN/OUT Endpoint 1 DFIFO Write/Read Access */ +#define STM32_OTG_DFIFO_HCH1_OFFSET 0x2000 /* 0x2000-0x2ffc Host OUT/IN Channel 1 DFIFO Read/Write Access */ + +#define STM32_OTG_DFIFO_DEP2_OFFSET 0x3000 /* 0x3000-0x3ffc Device IN/OUT Endpoint 2 DFIFO Write/Read Access */ +#define STM32_OTG_DFIFO_HCH2_OFFSET 0x3000 /* 0x3000-0x3ffc Host OUT/IN Channel 2 DFIFO Read/Write Access */ + +#define STM32_OTG_DFIFO_DEP3_OFFSET 0x4000 /* 0x4000-0x4ffc Device IN/OUT Endpoint 3 DFIFO Write/Read Access */ +#define STM32_OTG_DFIFO_HCH3_OFFSET 0x4000 /* 0x4000-0x4ffc Host OUT/IN Channel 3 DFIFO Read/Write Access */ + +/* Register Addresses *******************************************************************************/ + +#define STM32_OTG_GOTGCTL (STM32_OTG_BASE+STM32_OTG_GOTGCTL_OFFSET) +#define STM32_OTG_GOTGINT (STM32_OTG_BASE+STM32_OTG_GOTGINT_OFFSET) +#define STM32_OTG_GAHBCFG (STM32_OTG_BASE+STM32_OTG_GAHBCFG_OFFSET) +#define STM32_OTG_GUSBCFG (STM32_OTG_BASE+STM32_OTG_GUSBCFG_OFFSET) +#define STM32_OTG_GRSTCTL (STM32_OTG_BASE+STM32_OTG_GRSTCTL_OFFSET) +#define STM32_OTG_GINTSTS (STM32_OTG_BASE+STM32_OTG_GINTSTS_OFFSET) +#define STM32_OTG_GINTMSK (STM32_OTG_BASE+STM32_OTG_GINTMSK_OFFSET) +#define STM32_OTG_GRXSTSR (STM32_OTG_BASE+STM32_OTG_GRXSTSR_OFFSET) +#define STM32_OTG_GRXSTSP (STM32_OTG_BASE+STM32_OTG_GRXSTSP_OFFSET) +#define STM32_OTG_GRXFSIZ (STM32_OTG_BASE+STM32_OTG_GRXFSIZ_OFFSET) +#define STM32_OTG_HNPTXFSIZ (STM32_OTG_BASE+STM32_OTG_HNPTXFSIZ_OFFSET) +#define STM32_OTG_DIEPTXF0 (STM32_OTG_BASE+STM32_OTG_DIEPTXF0_OFFSET) +#define STM32_OTG_HNPTXSTS (STM32_OTG_BASE+STM32_OTG_HNPTXSTS_OFFSET) +#define STM32_OTG_GCCFG (STM32_OTG_BASE+STM32_OTG_GCCFG_OFFSET) +#define STM32_OTG_CID (STM32_OTG_BASE+STM32_OTG_CID_OFFSET) +#define STM32_OTG_HPTXFSIZ (STM32_OTG_BASE+STM32_OTG_HPTXFSIZ_OFFSET) + +#define STM32_OTG_DIEPTXF(n) (STM32_OTG_BASE+STM32_OTG_DIEPTXF_OFFSET(n)) +#define STM32_OTG_DIEPTXF1 (STM32_OTG_BASE+STM32_OTG_DIEPTXF1_OFFSET) +#define STM32_OTG_DIEPTXF2 (STM32_OTG_BASE+STM32_OTG_DIEPTXF2_OFFSET) +#define STM32_OTG_DIEPTXF3 (STM32_OTG_BASE+STM32_OTG_DIEPTXF3_OFFSET) + +/* Host-mode control and status registers */ + +#define STM32_OTG_HCFG (STM32_OTG_BASE+STM32_OTG_HCFG_OFFSET) +#define STM32_OTG_HFIR (STM32_OTG_BASE+STM32_OTG_HFIR_OFFSET) +#define STM32_OTG_HFNUM (STM32_OTG_BASE+STM32_OTG_HFNUM_OFFSET) +#define STM32_OTG_HPTXSTS (STM32_OTG_BASE+STM32_OTG_HPTXSTS_OFFSET) +#define STM32_OTG_HAINT (STM32_OTG_BASE+STM32_OTG_HAINT_OFFSET) +#define STM32_OTG_HAINTMSK (STM32_OTG_BASE+STM32_OTG_HAINTMSK_OFFSET) +#define STM32_OTG_HPRT (STM32_OTG_BASE+STM32_OTG_HPRT_OFFSET) + +#define STM32_OTG_CHAN(n) (STM32_OTG_BASE+STM32_OTG_CHAN_OFFSET(n)) + +#define STM32_OTG_HCCHAR(n) (STM32_OTG_BASE+STM32_OTG_HCCHAR_OFFSET(n)) +#define STM32_OTG_HCCHAR0 (STM32_OTG_BASE+STM32_OTG_HCCHAR0_OFFSET) +#define STM32_OTG_HCCHAR1 (STM32_OTG_BASE+STM32_OTG_HCCHAR1_OFFSET) +#define STM32_OTG_HCCHAR2 (STM32_OTG_BASE+STM32_OTG_HCCHAR2_OFFSET) +#define STM32_OTG_HCCHAR3 (STM32_OTG_BASE+STM32_OTG_HCCHAR3_OFFSET) +#define STM32_OTG_HCCHAR4 (STM32_OTG_BASE+STM32_OTG_HCCHAR4_OFFSET) +#define STM32_OTG_HCCHAR5 (STM32_OTG_BASE+STM32_OTG_HCCHAR5_OFFSET) +#define STM32_OTG_HCCHAR6 (STM32_OTG_BASE+STM32_OTG_HCCHAR6_OFFSET) +#define STM32_OTG_HCCHAR7 (STM32_OTG_BASE+STM32_OTG_HCCHAR7_OFFSET) + +#define STM32_OTG_HCINT(n) (STM32_OTG_BASE+STM32_OTG_HCINT_OFFSET(n)) +#define STM32_OTG_HCINT0 (STM32_OTG_BASE+STM32_OTG_HCINT0_OFFSET) +#define STM32_OTG_HCINT1 (STM32_OTG_BASE+STM32_OTG_HCINT1_OFFSET) +#define STM32_OTG_HCINT2 (STM32_OTG_BASE+STM32_OTG_HCINT2_OFFSET) +#define STM32_OTG_HCINT3 (STM32_OTG_BASE+STM32_OTG_HCINT3_OFFSET) +#define STM32_OTG_HCINT4 (STM32_OTG_BASE+STM32_OTG_HCINT4_OFFSET) +#define STM32_OTG_HCINT5 (STM32_OTG_BASE+STM32_OTG_HCINT5_OFFSET) +#define STM32_OTG_HCINT6 (STM32_OTG_BASE+STM32_OTG_HCINT6_OFFSET) +#define STM32_OTG_HCINT7 (STM32_OTG_BASE+STM32_OTG_HCINT7_OFFSET) + +#define STM32_OTG_HCINTMSK(n) (STM32_OTG_BASE+STM32_OTG_HCINTMSK_OFFSET(n)) +#define STM32_OTG_HCINTMSK0 (STM32_OTG_BASE+STM32_OTG_HCINTMSK0_OFFSET) +#define STM32_OTG_HCINTMSK1 (STM32_OTG_BASE+STM32_OTG_HCINTMSK1_OFFSET) +#define STM32_OTG_HCINTMSK2 (STM32_OTG_BASE+STM32_OTG_HCINTMSK2_OFFSET) +#define STM32_OTG_HCINTMSK3 (STM32_OTG_BASE+STM32_OTG_HCINTMSK3_OFFSET) +#define STM32_OTG_HCINTMSK4 (STM32_OTG_BASE+STM32_OTG_HCINTMSK4_OFFSET) +#define STM32_OTG_HCINTMSK5 (STM32_OTG_BASE+STM32_OTG_HCINTMSK5_OFFSET) +#define STM32_OTG_HCINTMSK6 (STM32_OTG_BASE+STM32_OTG_HCINTMSK6_OFFSET) +#define STM32_OTG_HCINTMSK7 (STM32_OTG_BASE+STM32_OTG_HCINTMSK7_OFFSET)_ + +#define STM32_OTG_HCTSIZ(n) (STM32_OTG_BASE+STM32_OTG_HCTSIZ_OFFSET(n)) +#define STM32_OTG_HCTSIZ0 (STM32_OTG_BASE+STM32_OTG_HCTSIZ0_OFFSET) +#define STM32_OTG_HCTSIZ1 (STM32_OTG_BASE+STM32_OTG_HCTSIZ1_OFFSET) +#define STM32_OTG_HCTSIZ2 (STM32_OTG_BASE+STM32_OTG_HCTSIZ2_OFFSET) +#define STM32_OTG_HCTSIZ3 (STM32_OTG_BASE+STM32_OTG_HCTSIZ3_OFFSET) +#define STM32_OTG_HCTSIZ4 (STM32_OTG_BASE+STM32_OTG_HCTSIZ4_OFFSET) +#define STM32_OTG_HCTSIZ5 (STM32_OTG_BASE+STM32_OTG_HCTSIZ5_OFFSET) +#define STM32_OTG_HCTSIZ6 (STM32_OTG_BASE+STM32_OTG_HCTSIZ6_OFFSET) +#define STM32_OTG_HCTSIZ7 (STM32_OTG_BASE+STM32_OTG_HCTSIZ7_OFFSET) + +/* Device-mode control and status registers */ + +#define STM32_OTG_DCFG (STM32_OTG_BASE+STM32_OTG_DCFG_OFFSET) +#define STM32_OTG_DCTL (STM32_OTG_BASE+STM32_OTG_DCTL_OFFSET) +#define STM32_OTG_DSTS (STM32_OTG_BASE+STM32_OTG_DSTS_OFFSET) +#define STM32_OTG_DIEPMSK (STM32_OTG_BASE+STM32_OTG_DIEPMSK_OFFSET) +#define STM32_OTG_DOEPMSK (STM32_OTG_BASE+STM32_OTG_DOEPMSK_OFFSET) +#define STM32_OTG_DAINT (STM32_OTG_BASE+STM32_OTG_DAINT_OFFSET) +#define STM32_OTG_DAINTMSK (STM32_OTG_BASE+STM32_OTG_DAINTMSK_OFFSET) +#define STM32_OTG_DVBUSDIS (STM32_OTG_BASE+STM32_OTG_DVBUSDIS_OFFSET) +#define STM32_OTG_DVBUSPULSE (STM32_OTG_BASE+STM32_OTG_DVBUSPULSE_OFFSET) +#define STM32_OTG_DIEPEMPMSK (STM32_OTG_BASE+STM32_OTG_DIEPEMPMSK_OFFSET) + +#define STM32_OTG_DIEP(n) (STM32_OTG_BASE+STM32_OTG_DIEP_OFFSET(n)) + +#define STM32_OTG_DIEPCTL(n) (STM32_OTG_BASE+STM32_OTG_DIEPCTL_OFFSET(n)) +#define STM32_OTG_DIEPCTL0 (STM32_OTG_BASE+STM32_OTG_DIEPCTL0_OFFSET) +#define STM32_OTG_DIEPCTL1 (STM32_OTG_BASE+STM32_OTG_DIEPCTL1_OFFSET) +#define STM32_OTG_DIEPCTL2 (STM32_OTG_BASE+STM32_OTG_DIEPCTL2_OFFSET) +#define STM32_OTG_DIEPCTL3 (STM32_OTG_BASE+STM32_OTG_DIEPCTL3_OFFSET) + +#define STM32_OTG_DIEPINT(n) (STM32_OTG_BASE+STM32_OTG_DIEPINT_OFFSET(n)) +#define STM32_OTG_DIEPINT0 (STM32_OTG_BASE+STM32_OTG_DIEPINT0_OFFSET) +#define STM32_OTG_DIEPINT1 (STM32_OTG_BASE+STM32_OTG_DIEPINT1_OFFSET) +#define STM32_OTG_DIEPINT2 (STM32_OTG_BASE+STM32_OTG_DIEPINT2_OFFSET) +#define STM32_OTG_DIEPINT3 (STM32_OTG_BASE+STM32_OTG_DIEPINT3_OFFSET) + +#define STM32_OTG_DIEPTSIZ(n) (STM32_OTG_BASE+STM32_OTG_DIEPTSIZ_OFFSET(n)) +#define STM32_OTG_DIEPTSIZ0 (STM32_OTG_BASE+STM32_OTG_DIEPTSIZ0_OFFSET) +#define STM32_OTG_DIEPTSIZ1 (STM32_OTG_BASE+STM32_OTG_DIEPTSIZ1_OFFSET) +#define STM32_OTG_DIEPTSIZ2 (STM32_OTG_BASE+STM32_OTG_DIEPTSIZ2_OFFSET) +#define STM32_OTG_DIEPTSIZ3 (STM32_OTG_BASE+STM32_OTG_DIEPTSIZ3_OFFSET) + +#define STM32_OTG_DTXFSTS(n) (STM32_OTG_BASE+STM32_OTG_DTXFSTS_OFFSET(n)) +#define STM32_OTG_DTXFSTS0 (STM32_OTG_BASE+STM32_OTG_DTXFSTS0_OFFSET) +#define STM32_OTG_DTXFSTS1 (STM32_OTG_BASE+STM32_OTG_DTXFSTS1_OFFSET) +#define STM32_OTG_DTXFSTS2 (STM32_OTG_BASE+STM32_OTG_DTXFSTS2_OFFSET) +#define STM32_OTG_DTXFSTS3 (STM32_OTG_BASE+STM32_OTG_DTXFSTS3_OFFSET) + +#define STM32_OTG_DOEP(n) (STM32_OTG_BASE+STM32_OTG_DOEP_OFFSET(n)) + +#define STM32_OTG_DOEPCTL(n) (STM32_OTG_BASE+STM32_OTG_DOEPCTL_OFFSET(n)) +#define STM32_OTG_DOEPCTL0 (STM32_OTG_BASE+STM32_OTG_DOEPCTL0_OFFSET) +#define STM32_OTG_DOEPCTL1 (STM32_OTG_BASE+STM32_OTG_DOEPCTL1_OFFSET) +#define STM32_OTG_DOEPCTL2 (STM32_OTG_BASE+STM32_OTG_DOEPCTL2_OFFSET) +#define STM32_OTG_DOEPCTL3 (STM32_OTG_BASE+STM32_OTG_DOEPCTL3_OFFSET) + +#define STM32_OTG_DOEPINT(n) (STM32_OTG_BASE+STM32_OTG_DOEPINT_OFFSET(n)) +#define STM32_OTG_DOEPINT0 (STM32_OTG_BASE+STM32_OTG_DOEPINT0_OFFSET) +#define STM32_OTG_DOEPINT1 (STM32_OTG_BASE+STM32_OTG_DOEPINT1_OFFSET) +#define STM32_OTG_DOEPINT2 (STM32_OTG_BASE+STM32_OTG_DOEPINT2_OFFSET) +#define STM32_OTG_DOEPINT3 (STM32_OTG_BASE+STM32_OTG_DOEPINT3_OFFSET) + +#define STM32_OTG_DOEPTSIZ(n) (STM32_OTG_BASE+STM32_OTG_DOEPTSIZ_OFFSET(n)) +#define STM32_OTG_DOEPTSIZ0 (STM32_OTG_BASE+STM32_OTG_DOEPTSIZ0_OFFSET) +#define STM32_OTG_DOEPTSIZ1 (STM32_OTG_BASE+STM32_OTG_DOEPTSIZ1_OFFSET) +#define STM32_OTG_DOEPTSIZ2 (STM32_OTG_BASE+STM32_OTG_DOEPTSIZ2_OFFSET) +#define STM32_OTG_DOEPTSIZ3 (STM32_OTG_BASE+STM32_OTG_DOEPTSIZ3_OFFSET) + +/* Power and clock gating registers */ + +#define STM32_OTG_PCGCCTL (STM32_OTG_BASE+STM32_OTG_PCGCCTL_OFFSET) + +/* Data FIFO (DFIFO) access registers */ + +#define STM32_OTG_DFIFO_DEP(n) (STM32_OTG_BASE+STM32_OTG_DFIFO_DEP_OFFSET(n)) +#define STM32_OTG_DFIFO_HCH(n) (STM32_OTG_BASE+STM32_OTG_DFIFO_HCH_OFFSET(n)) + +#define STM32_OTG_DFIFO_DEP0 (STM32_OTG_BASE+STM32_OTG_DFIFO_DEP0_OFFSET) +#define STM32_OTG_DFIFO_HCH0 (STM32_OTG_BASE+STM32_OTG_DFIFO_HCH0_OFFSET) + +#define STM32_OTG_DFIFO_DEP1 (STM32_OTG_BASE+STM32_OTG_DFIFO_DEP1_OFFSET) +#define STM32_OTG_DFIFO_HCH1 (STM32_OTG_BASE+STM32_OTG_DFIFO_HCH1_OFFSET) + +#define STM32_OTG_DFIFO_DEP2 (STM32_OTG_BASE+STM32_OTG_DFIFO_DEP2_OFFSET) +#define STM32_OTG_DFIFO_HCH2 (STM32_OTG_BASE+STM32_OTG_DFIFO_HCH2_OFFSET) + +#define STM32_OTG_DFIFO_DEP3 (STM32_OTG_BASE+STM32_OTG_DFIFO_DEP3_OFFSET) +#define STM32_OTG_DFIFO_HCH3 (STM32_OTG_BASE+STM32_OTG_DFIFO_HCH3_OFFSET) + +/* Register Bitfield Definitions ********************************************************************/ +/* Core global control and status registers */ + +/* Control and status register */ + +#define OTG_GOTGCTL_SRQSCS (1 << 0) /* Bit 0: Session request success */ +#define OTG_GOTGCTL_SRQ (1 << 1) /* Bit 1: Session request */ +#define OTG_GOTGCTL_VBVALOEN (1 << 2) /* Bit 2: VBUS valid override enable */ +#define OTG_GOTGCTL_VBVALOVAL (1 << 3) /* Bit 3: VBUS valid override value */ +#define OTG_GOTGCTL_AVALOEN (1 << 4) /* Bit 4: A-peripheral session valid override enable */ +#define OTG_GOTGCTL_AVALOVAL (1 << 5) /* Bit 5: A-peripheral session valid override value */ +#define OTG_GOTGCTL_BVALOEN (1 << 6) /* Bit 6: B-peripheral session valid override enable */ +#define OTG_GOTGCTL_BVALOVAL (1 << 7) /* Bit 7: B-peripheral session valid override value */ +#define OTG_GOTGCTL_HNGSCS (1 << 8) /* Bit 8: Host negotiation success */ +#define OTG_GOTGCTL_HNPRQ (1 << 9) /* Bit 9: HNP request */ +#define OTG_GOTGCTL_HSHNPEN (1 << 10) /* Bit 10: host set HNP enable */ +#define OTG_GOTGCTL_DHNPEN (1 << 11) /* Bit 11: Device HNP enabled */ +#define OTG_GOTGCTL_EHEN (1 << 12) /* Bit 12: Embedded host enable */ + /* Bits 13-15: Reserved, must be kept at reset value */ +#define OTG_GOTGCTL_CIDSTS (1 << 16) /* Bit 16: Connector ID status */ +#define OTG_GOTGCTL_DBCT (1 << 17) /* Bit 17: Long/short debounce time */ +#define OTG_GOTGCTL_ASVLD (1 << 18) /* Bit 18: A-session valid */ +#define OTG_GOTGCTL_BSVLD (1 << 19) /* Bit 19: B-session valid */ +#define OTG_GOTGCTL_OTGVER (1 << 20) /* Bit 20: OTG version */ + /* Bits 21-31: Reserved, must be kept at reset value */ +/* Interrupt register */ + /* Bits 1:0 Reserved, must be kept at reset value */ +#define OTG_GOTGINT_SEDET (1 << 2) /* Bit 2: Session end detected */ + /* Bits 3-7: Reserved, must be kept at reset value */ +#define OTG_GOTGINT_SRSSCHG (1 << 8) /* Bit 8: Session request success status change */ +#define OTG_GOTGINT_HNSSCHG (1 << 9) /* Bit 9: Host negotiation success status change */ + /* Bits 16:10 Reserved, must be kept at reset value */ +#define OTG_GOTGINT_HNGDET (1 << 17) /* Bit 17: Host negotiation detected */ +#define OTG_GOTGINT_ADTOCHG (1 << 18) /* Bit 18: A-device timeout change */ +#define OTG_GOTGINT_DBCDNE (1 << 19) /* Bit 19: Debounce done */ +#define OTG_GOTGINT_IDCHNG (1 << 20) /* Bit 20: Change in ID pin input value */ + /* Bits 21-31: Reserved, must be kept at reset value */ + +/* AHB configuration register */ + +#define OTG_GAHBCFG_GINTMSK (1 << 0) /* Bit 0: Global interrupt mask */ + /* Bits 1-6: Reserved, must be kept at reset value */ +#define OTG_GAHBCFG_TXFELVL (1 << 7) /* Bit 7: TxFIFO empty level */ +#define OTG_GAHBCFG_PTXFELVL (1 << 8) /* Bit 8: Periodic TxFIFO empty level */ + /* Bits 20-31: Reserved, must be kept at reset value */ +/* USB configuration register */ + +#define OTG_GUSBCFG_TOCAL_SHIFT (0) /* Bits 0-2: FS timeout calibration */ +#define OTG_GUSBCFG_TOCAL_MASK (7 << OTG_GUSBCFG_TOCAL_SHIFT) + /* Bits 3-5: Reserved, must be kept at reset value */ +#define OTG_GUSBCFG_PHYSEL (1 << 6) /* Bit 6: Full Speed serial transceiver select */ + /* Bit 7: Reserved, must be kept at reset value */ +#define OTG_GUSBCFG_SRPCAP (1 << 8) /* Bit 8: SRP-capable */ +#define OTG_GUSBCFG_HNPCAP (1 << 9) /* Bit 9: HNP-capable */ +#define OTG_GUSBCFG_TRDT_SHIFT (10) /* Bits 10-13: USB turnaround time */ +#define OTG_GUSBCFG_TRDT_MASK (15 << OTG_GUSBCFG_TRDT_SHIFT) +# define OTG_GUSBCFG_TRDT(n) ((n) << OTG_GUSBCFG_TRDT_SHIFT) + /* Bits 14-28: Reserved, must be kept at reset value */ +#define OTG_GUSBCFG_FHMOD (1 << 29) /* Bit 29: Force host mode */ +#define OTG_GUSBCFG_FDMOD (1 << 30) /* Bit 30: Force device mode */ +#define OTG_GUSBCFG_CTXPKT (1 << 31) /* Bit 31: Corrupt Tx packet */ + /* Bits 20-31: Reserved, must be kept at reset value */ +/* Reset register */ + +#define OTG_GRSTCTL_CSRST (1 << 0) /* Bit 0: Core soft reset */ +#define OTG_GRSTCTL_HSRST (1 << 1) /* Bit 1: HCLK soft reset */ +#define OTG_GRSTCTL_FCRST (1 << 2) /* Bit 2: Host frame counter reset */ + /* Bit 3 Reserved, must be kept at reset value */ +#define OTG_GRSTCTL_RXFFLSH (1 << 4) /* Bit 4: RxFIFO flush */ +#define OTG_GRSTCTL_TXFFLSH (1 << 5) /* Bit 5: TxFIFO flush */ +#define OTG_GRSTCTL_TXFNUM_SHIFT (6) /* Bits 6-10: TxFIFO number */ +#define OTG_GRSTCTL_TXFNUM_MASK (31 << OTG_GRSTCTL_TXFNUM_SHIFT) +# define OTG_GRSTCTL_TXFNUM_HNONPER (0 << OTG_GRSTCTL_TXFNUM_SHIFT) /* Non-periodic TxFIFO flush in host mode */ +# define OTG_GRSTCTL_TXFNUM_HPER (1 << OTG_GRSTCTL_TXFNUM_SHIFT) /* Periodic TxFIFO flush in host mode */ +# define OTG_GRSTCTL_TXFNUM_HALL (16 << OTG_GRSTCTL_TXFNUM_SHIFT) /* Flush all the transmit FIFOs in host mode.*/ +# define OTG_GRSTCTL_TXFNUM_D(n) ((n) << OTG_GRSTCTL_TXFNUM_SHIFT) /* TXFIFO n flush in device mode, n=0-15 */ +# define OTG_GRSTCTL_TXFNUM_DALL (16 << OTG_GRSTCTL_TXFNUM_SHIFT) /* Flush all the transmit FIFOs in device mode.*/ + /* Bits 11-31: Reserved, must be kept at reset value */ +#define OTG_GRSTCTL_AHBIDL (1 << 31) /* Bit 31: AHB master idle */ + +/* Core interrupt and Interrupt mask registers */ + +#define OTG_GINTSTS_CMOD (1 << 0) /* Bit 0: Current mode of operation */ +# define OTG_GINTSTS_DEVMODE (0) +# define OTG_GINTSTS_HOSTMODE (OTG_GINTSTS_CMOD) +#define OTG_GINT_MMIS (1 << 1) /* Bit 1: Mode mismatch interrupt */ +#define OTG_GINT_OTG (1 << 2) /* Bit 2: OTG interrupt */ +#define OTG_GINT_SOF (1 << 3) /* Bit 3: Start of frame */ +#define OTG_GINT_RXFLVL (1 << 4) /* Bit 4: RxFIFO non-empty */ +#define OTG_GINT_NPTXFE (1 << 5) /* Bit 5: Non-periodic TxFIFO empty */ +#define OTG_GINT_GINAKEFF (1 << 6) /* Bit 6: Global IN non-periodic NAK effective */ +#define OTG_GINT_GONAKEFF (1 << 7) /* Bit 7: Global OUT NAK effective */ + /* Bits 8-9: Reserved, must be kept at reset value */ +#define OTG_GINT_ESUSP (1 << 10) /* Bit 10: Early suspend */ +#define OTG_GINT_USBSUSP (1 << 11) /* Bit 11: USB suspend */ +#define OTG_GINT_USBRST (1 << 12) /* Bit 12: USB reset */ +#define OTG_GINT_ENUMDNE (1 << 13) /* Bit 13: Enumeration done */ +#define OTG_GINT_ISOODRP (1 << 14) /* Bit 14: Isochronous OUT packet dropped interrupt */ +#define OTG_GINT_EOPF (1 << 15) /* Bit 15: End of periodic frame interrupt */ + /* Bits 16 Reserved, must be kept at reset value */ +#define OTG_GINTMSK_EPMISM (1 << 17) /* Bit 17: Endpoint mismatch interrupt mask */ +#define OTG_GINT_IEP (1 << 18) /* Bit 18: IN endpoint interrupt */ +#define OTG_GINT_OEP (1 << 19) /* Bit 19: OUT endpoint interrupt */ +#define OTG_GINT_IISOIXFR (1 << 20) /* Bit 20: Incomplete isochronous IN transfer */ +#define OTG_GINT_IISOOXFR (1 << 21) /* Bit 21: Incomplete isochronous OUT transfer (device) */ +#define OTG_GINT_IPXFR (1 << 21) /* Bit 21: Incomplete periodic transfer (host) */ + /* Bit 22: Reserved, must be kept at reset value */ +#define OTG_GINT_RSTDET (1 << 23) /* Bit 23: Reset detected interrupt */ +#define OTG_GINT_HPRT (1 << 24) /* Bit 24: Host port interrupt */ +#define OTG_GINT_HC (1 << 25) /* Bit 25: Host channels interrupt */ +#define OTG_GINT_PTXFE (1 << 26) /* Bit 26: Periodic TxFIFO empty */ +#define OTG_GINT_LPMINT (1 << 27) /* Bit 27: LPM interrupt */ +#define OTG_GINT_CIDSCHG (1 << 28) /* Bit 28: Connector ID status change */ +#define OTG_GINT_DISC (1 << 29) /* Bit 29: Disconnect detected interrupt */ +#define OTG_GINT_SRQ (1 << 30) /* Bit 30: Session request/new session detected interrupt */ +#define OTG_GINT_WKUP (1 << 31) /* Bit 31: Resume/remote wakeup detected interrupt */ + +/* Receive status debug read/OTG status read and pop registers (host mode) */ + +#define OTG_GRXSTSH_CHNUM_SHIFT (0) /* Bits 0-3: Channel number */ +#define OTG_GRXSTSH_CHNUM_MASK (15 << OTG_GRXSTSH_CHNUM_SHIFT) +#define OTG_GRXSTSH_BCNT_SHIFT (4) /* Bits 4-14: Byte count */ +#define OTG_GRXSTSH_BCNT_MASK (0x7ff << OTG_GRXSTSH_BCNT_SHIFT) +#define OTG_GRXSTSH_DPID_SHIFT (15) /* Bits 15-16: Data PID */ +#define OTG_GRXSTSH_DPID_MASK (3 << OTG_GRXSTSH_DPID_SHIFT) +# define OTG_GRXSTSH_DPID_DATA0 (0 << OTG_GRXSTSH_DPID_SHIFT) +# define OTG_GRXSTSH_DPID_DATA2 (1 << OTG_GRXSTSH_DPID_SHIFT) +# define OTG_GRXSTSH_DPID_DATA1 (2 << OTG_GRXSTSH_DPID_SHIFT) +# define OTG_GRXSTSH_DPID_MDATA (3 << OTG_GRXSTSH_DPID_SHIFT) +#define OTG_GRXSTSH_PKTSTS_SHIFT (17) /* Bits 17-20: Packet status */ +#define OTG_GRXSTSH_PKTSTS_MASK (15 << OTG_GRXSTSH_PKTSTS_SHIFT) +# define OTG_GRXSTSH_PKTSTS_INRECVD (2 << OTG_GRXSTSH_PKTSTS_SHIFT) /* IN data packet received */ +# define OTG_GRXSTSH_PKTSTS_INDONE (3 << OTG_GRXSTSH_PKTSTS_SHIFT) /* IN transfer completed */ +# define OTG_GRXSTSH_PKTSTS_DTOGERR (5 << OTG_GRXSTSH_PKTSTS_SHIFT) /* Data toggle error */ +# define OTG_GRXSTSH_PKTSTS_HALTED (7 << OTG_GRXSTSH_PKTSTS_SHIFT) /* Channel halted */ + /* Bits 21-31: Reserved, must be kept at reset value */ +/* Receive status debug read/OTG status read and pop registers (device mode) */ + +#define OTG_GRXSTSD_EPNUM_SHIFT (0) /* Bits 0-3: Endpoint number */ +#define OTG_GRXSTSD_EPNUM_MASK (15 << OTG_GRXSTSD_EPNUM_SHIFT) +#define OTG_GRXSTSD_BCNT_SHIFT (4) /* Bits 4-14: Byte count */ +#define OTG_GRXSTSD_BCNT_MASK (0x7ff << OTG_GRXSTSD_BCNT_SHIFT) +#define OTG_GRXSTSD_DPID_SHIFT (15) /* Bits 15-16: Data PID */ +#define OTG_GRXSTSD_DPID_MASK (3 << OTG_GRXSTSD_DPID_SHIFT) +# define OTG_GRXSTSD_DPID_DATA0 (0 << OTG_GRXSTSD_DPID_SHIFT) +# define OTG_GRXSTSD_DPID_DATA2 (1 << OTG_GRXSTSD_DPID_SHIFT) +# define OTG_GRXSTSD_DPID_DATA1 (2 << OTG_GRXSTSD_DPID_SHIFT) +# define OTG_GRXSTSD_DPID_MDATA (3 << OTG_GRXSTSD_DPID_SHIFT) +#define OTG_GRXSTSD_PKTSTS_SHIFT (17) /* Bits 17-20: Packet status */ +#define OTG_GRXSTSD_PKTSTS_MASK (15 << OTG_GRXSTSD_PKTSTS_SHIFT) +# define OTG_GRXSTSD_PKTSTS_OUTNAK (1 << OTG_GRXSTSD_PKTSTS_SHIFT) /* Global OUT NAK */ +# define OTG_GRXSTSD_PKTSTS_OUTRECVD (2 << OTG_GRXSTSD_PKTSTS_SHIFT) /* OUT data packet received */ +# define OTG_GRXSTSD_PKTSTS_OUTDONE (3 << OTG_GRXSTSD_PKTSTS_SHIFT) /* OUT transfer completed */ +# define OTG_GRXSTSD_PKTSTS_SETUPDONE (4 << OTG_GRXSTSD_PKTSTS_SHIFT) /* SETUP transaction completed */ +# define OTG_GRXSTSD_PKTSTS_SETUPRECVD (6 << OTG_GRXSTSD_PKTSTS_SHIFT) /* SETUP data packet received */ +#define OTG_GRXSTSD_FRMNUM_SHIFT (21) /* Bits 21-24: Frame number */ +#define OTG_GRXSTSD_FRMNUM_MASK (15 << OTG_GRXSTSD_FRMNUM_SHIFT) + /* Bits 25-31: Reserved, must be kept at reset value */ +/* Receive FIFO size register */ + +#define OTG_GRXFSIZ_MASK (0xffff) + +/* Host non-periodic transmit FIFO size register */ + +#define OTG_HNPTXFSIZ_NPTXFSA_SHIFT (0) /* Bits 0-15: Non-periodic transmit RAM start address */ +#define OTG_HNPTXFSIZ_NPTXFSA_MASK (0xffff << OTG_HNPTXFSIZ_NPTXFSA_SHIFT) +#define OTG_HNPTXFSIZ_NPTXFD_SHIFT (16) /* Bits 16-31: Non-periodic TxFIFO depth */ +#define OTG_HNPTXFSIZ_NPTXFD_MASK (0xffff << OTG_HNPTXFSIZ_NPTXFD_SHIFT) +# define OTG_HNPTXFSIZ_NPTXFD_MIN (16 << OTG_HNPTXFSIZ_NPTXFD_SHIFT) +# define OTG_HNPTXFSIZ_NPTXFD_MAX (256 << OTG_HNPTXFSIZ_NPTXFD_SHIFT) + +/* Endpoint 0 Transmit FIFO size */ + +#define OTG_DIEPTXF0_TX0FD_SHIFT (0) /* Bits 0-15: Endpoint 0 transmit RAM start address */ +#define OTG_DIEPTXF0_TX0FD_MASK (0xffff << OTG_DIEPTXF0_TX0FD_SHIFT) +#define OTG_DIEPTXF0_TX0FSA_SHIFT (16) /* Bits 16-31: Endpoint 0 TxFIFO depth */ +#define OTG_DIEPTXF0_TX0FSA_MASK (0xffff << OTG_DIEPTXF0_TX0FSA_SHIFT) +# define OTG_DIEPTXF0_TX0FSA_MIN (16 << OTG_DIEPTXF0_TX0FSA_SHIFT) +# define OTG_DIEPTXF0_TX0FSA_MAX (256 << OTG_DIEPTXF0_TX0FSA_SHIFT) + +/* Non-periodic transmit FIFO/queue status register */ + +#define OTG_HNPTXSTS_NPTXFSAV_SHIFT (0) /* Bits 0-15: Non-periodic TxFIFO space available */ +#define OTG_HNPTXSTS_NPTXFSAV_MASK (0xffff << OTG_HNPTXSTS_NPTXFSAV_SHIFT) +# define OTG_HNPTXSTS_NPTXFSAV_FULL (0 << OTG_HNPTXSTS_NPTXFSAV_SHIFT) +#define OTG_HNPTXSTS_NPTQXSAV_SHIFT (16) /* Bits 16-23: Non-periodic transmit request queue space available */ +#define OTG_HNPTXSTS_NPTQXSAV_MASK (0xff << OTG_HNPTXSTS_NPTQXSAV_SHIFT) +# define OTG_HNPTXSTS_NPTQXSAV_FULL (0 << OTG_HNPTXSTS_NPTQXSAV_SHIFT) +#define OTG_HNPTXSTS_NPTXQTOP_SHIFT (24) /* Bits 24-30: Top of the non-periodic transmit request queue */ +#define OTG_HNPTXSTS_NPTXQTOP_MASK (0x7f << OTG_HNPTXSTS_NPTXQTOP_SHIFT) +# define OTG_HNPTXSTS_TERMINATE (1 << 24) /* Bit 24: Terminate (last entry for selected channel/endpoint) */ +# define OTG_HNPTXSTS_TYPE_SHIFT (25) /* Bits 25-26: Status */ +# define OTG_HNPTXSTS_TYPE_MASK (3 << OTG_HNPTXSTS_TYPE_SHIFT) +# define OTG_HNPTXSTS_TYPE_INOUT (0 << OTG_HNPTXSTS_TYPE_SHIFT) /* IN/OUT token */ +# define OTG_HNPTXSTS_TYPE_ZLP (1 << OTG_HNPTXSTS_TYPE_SHIFT) /* Zero-length transmit packet (device IN/host OUT) */ +# define OTG_HNPTXSTS_TYPE_HALT (3 << OTG_HNPTXSTS_TYPE_SHIFT) /* Channel halt command */ +# define OTG_HNPTXSTS_CHNUM_SHIFT (27) /* Bits 27-30: Channel number */ +# define OTG_HNPTXSTS_CHNUM_MASK (15 << OTG_HNPTXSTS_CHNUM_SHIFT) +# define OTG_HNPTXSTS_EPNUM_SHIFT (27) /* Bits 27-30: Endpoint number */ +# define OTG_HNPTXSTS_EPNUM_MASK (15 << OTG_HNPTXSTS_EPNUM_SHIFT) + /* Bit 31 Reserved, must be kept at reset value */ +/* General core configuration register */ + /* Bits 0-15: Reserved, must be kept at reset value */ +#define OTG_GCCFG_PWRDWN (1 << 16) /* Bit 16: Power down */ + /* Bit 17 Reserved, must be kept at reset value */ +#define OTG_GCCFG_VBDEN (1 << 21) /* Bit 21: USB VBUS detection enable */ + /* Bits 22-31: Reserved, must be kept at reset value */ +/* Core ID register (32-bit product ID) */ + +/* Host periodic transmit FIFO size register */ + +#define OTG_HPTXFSIZ_PTXSA_SHIFT (0) /* Bits 0-15: Host periodic TxFIFO start address */ +#define OTG_HPTXFSIZ_PTXSA_MASK (0xffff << OTG_HPTXFSIZ_PTXSA_SHIFT) +#define OTG_HPTXFSIZ_PTXFD_SHIFT (16) /* Bits 16-31: Host periodic TxFIFO depth */ +#define OTG_HPTXFSIZ_PTXFD_MASK (0xffff << OTG_HPTXFSIZ_PTXFD_SHIFT) + +/* Device IN endpoint transmit FIFOn size register */ + +#define OTG_DIEPTXF_INEPTXSA_SHIFT (0) /* Bits 0-15: IN endpoint FIFOx transmit RAM start address */ +#define OTG_DIEPTXF_INEPTXSA_MASK (0xffff << OTG_DIEPTXF_INEPTXSA_SHIFT) +#define OTG_DIEPTXF_INEPTXFD_SHIFT (16) /* Bits 16-31: IN endpoint TxFIFO depth */ +#define OTG_DIEPTXF_INEPTXFD_MASK (0xffff << OTG_DIEPTXF_INEPTXFD_SHIFT) +# define OTG_DIEPTXF_INEPTXFD_MIN (16 << OTG_DIEPTXF_INEPTXFD_MASK) + +/* Host-mode control and status registers */ + +/* Host configuration register */ + +#define OTG_HCFG_FSLSPCS_SHIFT (0) /* Bits 0-1: FS/LS PHY clock select */ +#define OTG_HCFG_FSLSPCS_MASK (3 << OTG_HCFG_FSLSPCS_SHIFT) +# define OTG_HCFG_FSLSPCS_FS48MHz (1 << OTG_HCFG_FSLSPCS_SHIFT) /* FS host mode, PHY clock is running at 48 MHz */ +# define OTG_HCFG_FSLSPCS_LS48MHz (1 << OTG_HCFG_FSLSPCS_SHIFT) /* LS host mode, Select 48 MHz PHY clock frequency */ +# define OTG_HCFG_FSLSPCS_LS6MHz (2 << OTG_HCFG_FSLSPCS_SHIFT) /* LS host mode, Select 6 MHz PHY clock frequency */ +#define OTG_HCFG_FSLSS (1 << 2) /* Bit 2: FS- and LS-only support */ + /* Bits 31:3 Reserved, must be kept at reset value */ +/* Host frame interval register */ + +#define OTG_HFIR_MASK (0xffff) + +/* Host frame number/frame time remaining register */ + +#define OTG_HFNUM_FRNUM_SHIFT (0) /* Bits 0-15: Frame number */ +#define OTG_HFNUM_FRNUM_MASK (0xffff << OTG_HFNUM_FRNUM_SHIFT) +#define OTG_HFNUM_FTREM_SHIFT (16) /* Bits 16-31: Frame time remaining */ +#define OTG_HFNUM_FTREM_MASK (0xffff << OTG_HFNUM_FTREM_SHIFT) + +/* Host periodic transmit FIFO/queue status register */ + +#define OTG_HPTXSTS_PTXFSAVL_SHIFT (0) /* Bits 0-15: Periodic transmit data FIFO space available */ +#define OTG_HPTXSTS_PTXFSAVL_MASK (0xffff << OTG_HPTXSTS_PTXFSAVL_SHIFT) +# define OTG_HPTXSTS_PTXFSAVL_FULL (0 << OTG_HPTXSTS_PTXFSAVL_SHIFT) +#define OTG_HPTXSTS_PTXQSAV_SHIFT (16) /* Bits 16-23: Periodic transmit request queue space available */ +#define OTG_HPTXSTS_PTXQSAV_MASK (0xff << OTG_HPTXSTS_PTXQSAV_SHIFT) +# define OTG_HPTXSTS_PTXQSAV_FULL (0 << OTG_HPTXSTS_PTXQSAV_SHIFT) +#define OTG_HPTXSTS_PTXQTOP_SHIFT (24) /* Bits 24-31: Top of the periodic transmit request queue */ +#define OTG_HPTXSTS_PTXQTOP_MASK (0x7f << OTG_HPTXSTS_PTXQTOP_SHIFT) +# define OTG_HPTXSTS_TERMINATE (1 << 24) /* Bit 24: Terminate (last entry for selected channel/endpoint) */ +# define OTG_HPTXSTS_TYPE_SHIFT (25) /* Bits 25-26: Type */ +# define OTG_HPTXSTS_TYPE_MASK (3 << OTG_HPTXSTS_TYPE_SHIFT) +# define OTG_HPTXSTS_TYPE_INOUT (0 << OTG_HPTXSTS_TYPE_SHIFT) /* IN/OUT token */ +# define OTG_HPTXSTS_TYPE_ZLP (1 << OTG_HPTXSTS_TYPE_SHIFT) /* Zero-length transmit packet */ +# define OTG_HPTXSTS_TYPE_HALT (3 << OTG_HPTXSTS_TYPE_SHIFT) /* Disable channel command */ +# define OTG_HPTXSTS_EPNUM_SHIFT (27) /* Bits 27-30: Endpoint number */ +# define OTG_HPTXSTS_EPNUM_MASK (15 << OTG_HPTXSTS_EPNUM_SHIFT) +# define OTG_HPTXSTS_CHNUM_SHIFT (27) /* Bits 27-30: Channel number */ +# define OTG_HPTXSTS_CHNUM_MASK (15 << OTG_HPTXSTS_CHNUM_SHIFT) +# define OTG_HPTXSTS_ODD (1 << 24) /* Bit 31: Send in odd (vs even) frame */ + +/* Host all channels interrupt and all channels interrupt mask registers */ + +#define OTG_HAINT(n) (1 << (n)) /* Bits 15:0 HAINTM: Channel interrupt */ + +/* Host port control and status register */ + +#define OTG_HPRT_PCSTS (1 << 0) /* Bit 0: Port connect status */ +#define OTG_HPRT_PCDET (1 << 1) /* Bit 1: Port connect detected */ +#define OTG_HPRT_PENA (1 << 2) /* Bit 2: Port enable */ +#define OTG_HPRT_PENCHNG (1 << 3) /* Bit 3: Port enable/disable change */ +#define OTG_HPRT_POCA (1 << 4) /* Bit 4: Port overcurrent active */ +#define OTG_HPRT_POCCHNG (1 << 5) /* Bit 5: Port overcurrent change */ +#define OTG_HPRT_PRES (1 << 6) /* Bit 6: Port resume */ +#define OTG_HPRT_PSUSP (1 << 7) /* Bit 7: Port suspend */ +#define OTG_HPRT_PRST (1 << 8) /* Bit 8: Port reset */ + /* Bit 9: Reserved, must be kept at reset value */ +#define OTG_HPRT_PLSTS_SHIFT (10) /* Bits 10-11: Port line status */ +#define OTG_HPRT_PLSTS_MASK (3 << OTG_HPRT_PLSTS_SHIFT) +# define OTG_HPRT_PLSTS_DP (1 << 10) /* Bit 10: Logic level of OTG_FS_FS_DP */ +# define OTG_HPRT_PLSTS_DM (1 << 11) /* Bit 11: Logic level of OTG_FS_FS_DM */ +#define OTG_HPRT_PPWR (1 << 12) /* Bit 12: Port power */ +#define OTG_HPRT_PTCTL_SHIFT (13) /* Bits 13-16: Port test control */ +#define OTG_HPRT_PTCTL_MASK (15 << OTG_HPRT_PTCTL_SHIFT) +# define OTG_HPRT_PTCTL_DISABLED (0 << OTG_HPRT_PTCTL_SHIFT) /* Test mode disabled */ +# define OTG_HPRT_PTCTL_J (1 << OTG_HPRT_PTCTL_SHIFT) /* Test_J mode */ +# define OTG_HPRT_PTCTL_L (2 << OTG_HPRT_PTCTL_SHIFT) /* Test_K mode */ +# define OTG_HPRT_PTCTL_SE0_NAK (3 << OTG_HPRT_PTCTL_SHIFT) /* Test_SE0_NAK mode */ +# define OTG_HPRT_PTCTL_PACKET (4 << OTG_HPRT_PTCTL_SHIFT) /* Test_Packet mode */ +# define OTG_HPRT_PTCTL_FORCE (5 << OTG_HPRT_PTCTL_SHIFT) /* Test_Force_Enable */ +#define OTG_HPRT_PSPD_SHIFT (17) /* Bits 17-18: Port speed */ +#define OTG_HPRT_PSPD_MASK (3 << OTG_HPRT_PSPD_SHIFT) +# define OTG_HPRT_PSPD_FS (1 << OTG_HPRT_PSPD_SHIFT) /* Full speed */ +# define OTG_HPRT_PSPD_LS (2 << OTG_HPRT_PSPD_SHIFT) /* Low speed */ + /* Bits 19-31: Reserved, must be kept at reset value */ + +/* Host channel-n characteristics register */ + +#define OTG_HCCHAR_MPSIZ_SHIFT (0) /* Bits 0-10: Maximum packet size */ +#define OTG_HCCHAR_MPSIZ_MASK (0x7ff << OTG_HCCHAR_MPSIZ_SHIFT) +#define OTG_HCCHAR_EPNUM_SHIFT (11) /* Bits 11-14: Endpoint number */ +#define OTG_HCCHAR_EPNUM_MASK (15 << OTG_HCCHAR_EPNUM_SHIFT) +#define OTG_HCCHAR_EPDIR (1 << 15) /* Bit 15: Endpoint direction */ +# define OTG_HCCHAR_EPDIR_OUT (0) +# define OTG_HCCHAR_EPDIR_IN OTG_HCCHAR_EPDIR + /* Bit 16 Reserved, must be kept at reset value */ +#define OTG_HCCHAR_LSDEV (1 << 17) /* Bit 17: Low-speed device */ +#define OTG_HCCHAR_EPTYP_SHIFT (18) /* Bits 18-19: Endpoint type */ +#define OTG_HCCHAR_EPTYP_MASK (3 << OTG_HCCHAR_EPTYP_SHIFT) +# define OTG_HCCHAR_EPTYP_CTRL (0 << OTG_HCCHAR_EPTYP_SHIFT) /* Control */ +# define OTG_HCCHAR_EPTYP_ISOC (1 << OTG_HCCHAR_EPTYP_SHIFT) /* Isochronous */ +# define OTG_HCCHAR_EPTYP_BULK (2 << OTG_HCCHAR_EPTYP_SHIFT) /* Bulk */ +# define OTG_HCCHAR_EPTYP_INTR (3 << OTG_HCCHAR_EPTYP_SHIFT) /* Interrupt */ +#define OTG_HCCHAR_MCNT_SHIFT (20) /* Bits 20-21: Multicount */ +#define OTG_HCCHAR_MCNT_MASK (3 << OTG_HCCHAR_MCNT_SHIFT) +#define OTG_HCCHAR_DAD_SHIFT (22) /* Bits 22-28: Device address */ +#define OTG_HCCHAR_DAD_MASK (0x7f << OTG_HCCHAR_DAD_SHIFT) +#define OTG_HCCHAR_ODDFRM (1 << 29) /* Bit 29: Odd frame */ +#define OTG_HCCHAR_CHDIS (1 << 30) /* Bit 30: Channel disable */ +#define OTG_HCCHAR_CHENA (1 << 31) /* Bit 31: Channel enable */ + +/* Host channel-n interrupt and Host channel-0 interrupt mask registers */ + +#define OTG_HCINT_XFRC (1 << 0) /* Bit 0: Transfer completed */ +#define OTG_HCINT_CHH (1 << 1) /* Bit 1: Channel halted */ + /* Bit 2: Reserved, must be kept at reset value */ +#define OTG_HCINT_STALL (1 << 3) /* Bit 3: STALL response received interrupt */ +#define OTG_HCINT_NAK (1 << 4) /* Bit 4: NAK response received interrupt */ +#define OTG_HCINT_ACK (1 << 5) /* Bit 5: ACK response received/transmitted interrupt */ +#define OTG_HCINT_NYET (1 << 6) /* Bit 6: Response received interrupt */ +#define OTG_HCINT_TXERR (1 << 7) /* Bit 7: Transaction error */ +#define OTG_HCINT_BBERR (1 << 8) /* Bit 8: Babble error */ +#define OTG_HCINT_FRMOR (1 << 9) /* Bit 9: Frame overrun */ +#define OTG_HCINT_DTERR (1 << 10) /* Bit 10: Data toggle error */ + /* Bits 11-31 Reserved, must be kept at reset value */ +/* Host channel-n interrupt register */ + +#define OTG_HCTSIZ_XFRSIZ_SHIFT (0) /* Bits 0-18: Transfer size */ +#define OTG_HCTSIZ_XFRSIZ_MASK (0x7ffff << OTG_HCTSIZ_XFRSIZ_SHIFT) +#define OTG_HCTSIZ_PKTCNT_SHIFT (19) /* Bits 19-28: Packet count */ +#define OTG_HCTSIZ_PKTCNT_MASK (0x3ff << OTG_HCTSIZ_PKTCNT_SHIFT) +#define OTG_HCTSIZ_DPID_SHIFT (29) /* Bits 29-30: Data PID */ +#define OTG_HCTSIZ_DPID_MASK (3 << OTG_HCTSIZ_DPID_SHIFT) +# define OTG_HCTSIZ_DPID_DATA0 (0 << OTG_HCTSIZ_DPID_SHIFT) +# define OTG_HCTSIZ_DPID_DATA2 (1 << OTG_HCTSIZ_DPID_SHIFT) +# define OTG_HCTSIZ_DPID_DATA1 (2 << OTG_HCTSIZ_DPID_SHIFT) +# define OTG_HCTSIZ_DPID_MDATA (3 << OTG_HCTSIZ_DPID_SHIFT) /* Non-control */ +# define OTG_HCTSIZ_PID_SETUP (3 << OTG_HCTSIZ_DPID_SHIFT) /* Control */ + /* Bit 31 Reserved, must be kept at reset value */ +/* Device-mode control and status registers */ + +/* Device configuration register */ + +#define OTG_DCFG_DSPD_SHIFT (0) /* Bits 0-1: Device speed */ +#define OTG_DCFG_DSPD_MASK (3 << OTG_DCFG_DSPD_SHIFT) +# define OTG_DCFG_DSPD_FS (3 << OTG_DCFG_DSPD_SHIFT) /* Full speed */ +#define OTG_DCFG_NZLSOHSK (1 << 2) /* Bit 2: Non-zero-length status OUT handshake */ + /* Bit 3: Reserved, must be kept at reset value */ +#define OTG_DCFG_DAD_SHIFT (4) /* Bits 4-10: Device address */ +#define OTG_DCFG_DAD_MASK (0x7f << OTG_DCFG_DAD_SHIFT) +#define OTG_DCFG_PFIVL_SHIFT (11) /* Bits 11-12: Periodic frame interval */ +#define OTG_DCFG_PFIVL_MASK (3 << OTG_DCFG_PFIVL_SHIFT) +# define OTG_DCFG_PFIVL_80PCT (0 << OTG_DCFG_PFIVL_SHIFT) /* 80% of the frame interval */ +# define OTG_DCFG_PFIVL_85PCT (1 << OTG_DCFG_PFIVL_SHIFT) /* 85% of the frame interval */ +# define OTG_DCFG_PFIVL_90PCT (2 << OTG_DCFG_PFIVL_SHIFT) /* 90% of the frame interval */ +# define OTG_DCFG_PFIVL_95PCT (3 << OTG_DCFG_PFIVL_SHIFT) /* 95% of the frame interval */ + /* Bits 13-31 Reserved, must be kept at reset value */ +/* Device control register */ + +#define OTG_TESTMODE_DISABLED (0) /* Test mode disabled */ +#define OTG_TESTMODE_J (1) /* Test_J mode */ +#define OTG_TESTMODE_K (2) /* Test_K mode */ +#define OTG_TESTMODE_SE0_NAK (3) /* Test_SE0_NAK mode */ +#define OTG_TESTMODE_PACKET (4) /* Test_Packet mode */ +#define OTG_TESTMODE_FORCE (5) /* Test_Force_Enable */ + +#define OTG_DCTL_RWUSIG (1 << 0) /* Bit 0: Remote wakeup signaling */ +#define OTG_DCTL_SDIS (1 << 1) /* Bit 1: Soft disconnect */ +#define OTG_DCTL_GINSTS (1 << 2) /* Bit 2: Global IN NAK status */ +#define OTG_DCTL_GONSTS (1 << 3) /* Bit 3: Global OUT NAK status */ +#define OTG_DCTL_TCTL_SHIFT (4) /* Bits 4-6: Test control */ +#define OTG_DCTL_TCTL_MASK (7 << OTG_DCTL_TCTL_SHIFT) +# define OTG_DCTL_TCTL_DISABLED (0 << OTG_DCTL_TCTL_SHIFT) /* Test mode disabled */ +# define OTG_DCTL_TCTL_J (1 << OTG_DCTL_TCTL_SHIFT) /* Test_J mode */ +# define OTG_DCTL_TCTL_K (2 << OTG_DCTL_TCTL_SHIFT) /* Test_K mode */ +# define OTG_DCTL_TCTL_SE0_NAK (3 << OTG_DCTL_TCTL_SHIFT) /* Test_SE0_NAK mode */ +# define OTG_DCTL_TCTL_PACKET (4 << OTG_DCTL_TCTL_SHIFT) /* Test_Packet mode */ +# define OTG_DCTL_TCTL_FORCE (5 << OTG_DCTL_TCTL_SHIFT) /* Test_Force_Enable */ +#define OTG_DCTL_SGINAK (1 << 7) /* Bit 7: Set global IN NAK */ +#define OTG_DCTL_CGINAK (1 << 8) /* Bit 8: Clear global IN NAK */ +#define OTG_DCTL_SGONAK (1 << 9) /* Bit 9: Set global OUT NAK */ +#define OTG_DCTL_CGONAK (1 << 10) /* Bit 10: Clear global OUT NAK */ +#define OTG_DCTL_POPRGDNE (1 << 11) /* Bit 11: Power-on programming done */ + /* Bits 12-31: Reserved, must be kept at reset value */ +/* Device status register */ + +#define OTG_DSTS_SUSPSTS (1 << 0) /* Bit 0: Suspend status */ +#define OTG_DSTS_ENUMSPD_SHIFT (1) /* Bits 1-2: Enumerated speed */ +#define OTG_DSTS_ENUMSPD_MASK (3 << OTG_DSTS_ENUMSPD_SHIFT) +# define OTG_DSTS_ENUMSPD_FS (3 << OTG_DSTS_ENUMSPD_MASK) /* Full speed */ + /* Bits 4-7: Reserved, must be kept at reset value */ +#define OTG_DSTS_EERR (1 << 3) /* Bit 3: Erratic error */ +#define OTG_DSTS_SOFFN_SHIFT (8) /* Bits 8-21: Frame number of the received SOF */ +#define OTG_DSTS_SOFFN_MASK (0x3fff << OTG_DSTS_SOFFN_SHIFT) +#define OTG_DSTS_SOFFN0 (1 << 8) /* Bits 8: Frame number even/odd bit */ +#define OTG_DSTS_SOFFN_EVEN 0 +#define OTG_DSTS_SOFFN_ODD OTG_DSTS_SOFFN0 + /* Bits 22-31: Reserved, must be kept at reset value */ +/* Device IN endpoint common interrupt mask register */ + +#define OTG_DIEPMSK_XFRCM (1 << 0) /* Bit 0: Transfer completed interrupt mask */ +#define OTG_DIEPMSK_EPDM (1 << 1) /* Bit 1: Endpoint disabled interrupt mask */ + /* Bit 2: Reserved, must be kept at reset value */ +#define OTG_DIEPMSK_TOM (1 << 3) /* Bit 3: Timeout condition mask (Non-isochronous endpoints) */ +#define OTG_DIEPMSK_ITTXFEMSK (1 << 4) /* Bit 4: IN token received when TxFIFO empty mask */ +#define OTG_DIEPMSK_INEPNMM (1 << 5) /* Bit 5: IN token received with EP mismatch mask */ +#define OTG_DIEPMSK_INEPNEM (1 << 6) /* Bit 6: IN endpoint NAK effective mask */ + /* Bits 7-31: Reserved, must be kept at reset value */ +/* Device OUT endpoint common interrupt mask register */ + +#define OTG_DOEPMSK_XFRCM (1 << 0) /* Bit 0: Transfer completed interrupt mask */ +#define OTG_DOEPMSK_EPDM (1 << 1) /* Bit 1: Endpoint disabled interrupt mask */ + /* Bit 2: Reserved, must be kept at reset value */ +#define OTG_DOEPMSK_STUPM (1 << 3) /* Bit 3: SETUP phase done mask */ +#define OTG_DOEPMSK_OTEPDM (1 << 4) /* Bit 4: OUT token received when endpoint disabled mask */ + /* Bits 5-31: Reserved, must be kept at reset value */ +/* Device all endpoints interrupt and All endpoints interrupt mask registers */ + +#define OTG_DAINT_IEP_SHIFT (0) /* Bits 0-15: IN endpoint interrupt bits */ +#define OTG_DAINT_IEP_MASK (0xffff << OTG_DAINT_IEP_SHIFT) +# define OTG_DAINT_IEP(n) (1 << (n)) +#define OTG_DAINT_OEP_SHIFT (16) /* Bits 16-31: OUT endpoint interrupt bits */ +#define OTG_DAINT_OEP_MASK (0xffff << OTG_DAINT_OEP_SHIFT) +# define OTG_DAINT_OEP(n) (1 << ((n)+16)) + +/* Device VBUS discharge time register */ + +#define OTG_DVBUSDIS_MASK (0xffff) + +/* Device VBUS pulsing time register */ + +#define OTG_DVBUSPULSE_MASK (0xfff) + +/* Device IN endpoint FIFO empty interrupt mask register */ + +#define OTG_DIEPEMPMSK(n) (1 << (n)) + +/* Device control IN endpoint 0 control register */ + +#define OTG_DIEPCTL0_MPSIZ_SHIFT (0) /* Bits 0-1: Maximum packet size */ +#define OTG_DIEPCTL0_MPSIZ_MASK (3 << OTG_DIEPCTL0_MPSIZ_SHIFT) +# define OTG_DIEPCTL0_MPSIZ_64 (0 << OTG_DIEPCTL0_MPSIZ_SHIFT) /* 64 bytes */ +# define OTG_DIEPCTL0_MPSIZ_32 (1 << OTG_DIEPCTL0_MPSIZ_SHIFT) /* 32 bytes */ +# define OTG_DIEPCTL0_MPSIZ_16 (2 << OTG_DIEPCTL0_MPSIZ_SHIFT) /* 16 bytes */ +# define OTG_DIEPCTL0_MPSIZ_8 (3 << OTG_DIEPCTL0_MPSIZ_SHIFT) /* 8 bytes */ + /* Bits 2-14: Reserved, must be kept at reset value */ +#define OTG_DIEPCTL0_USBAEP (1 << 15) /* Bit 15: USB active endpoint */ + /* Bit 16: Reserved, must be kept at reset value */ +#define OTG_DIEPCTL0_NAKSTS (1 << 17) /* Bit 17: NAK status */ +#define OTG_DIEPCTL0_EPTYP_SHIFT (18) /* Bits 18-19: Endpoint type */ +#define OTG_DIEPCTL0_EPTYP_MASK (3 << OTG_DIEPCTL0_EPTYP_SHIFT) +# define OTG_DIEPCTL0_EPTYP_CTRL (0 << OTG_DIEPCTL0_EPTYP_SHIFT) /* Control (hard-coded) */ + /* Bit 20: Reserved, must be kept at reset value */ +#define OTG_DIEPCTL0_STALL (1 << 21) /* Bit 21: STALL handshake */ +#define OTG_DIEPCTL0_TXFNUM_SHIFT (22) /* Bits 22-25: TxFIFO number */ +#define OTG_DIEPCTL0_TXFNUM_MASK (15 << OTG_DIEPCTL0_TXFNUM_SHIFT) +#define OTG_DIEPCTL0_CNAK (1 << 26) /* Bit 26: Clear NAK */ +#define OTG_DIEPCTL0_SNAK (1 << 27) /* Bit 27: Set NAK */ + /* Bits 28-29: Reserved, must be kept at reset value */ +#define OTG_DIEPCTL0_EPDIS (1 << 30) /* Bit 30: Endpoint disable */ +#define OTG_DIEPCTL0_EPENA (1 << 31) /* Bit 31: Endpoint enable */ + +/* Device control IN endpoint n control register */ + +#define OTG_DIEPCTL_MPSIZ_SHIFT (0) /* Bits 0-10: Maximum packet size */ +#define OTG_DIEPCTL_MPSIZ_MASK (0x7ff << OTG_DIEPCTL_MPSIZ_SHIFT) + /* Bits 11-14: Reserved, must be kept at reset value */ +#define OTG_DIEPCTL_USBAEP (1 << 15) /* Bit 15: USB active endpoint */ +#define OTG_DIEPCTL_EONUM (1 << 16) /* Bit 16: Even/odd frame */ +# define OTG_DIEPCTL_EVEN (0) +# define OTG_DIEPCTL_ODD OTG_DIEPCTL_EONUM +# define OTG_DIEPCTL_DATA0 (0) +# define OTG_DIEPCTL_DATA1 OTG_DIEPCTL_EONUM +#define OTG_DIEPCTL_NAKSTS (1 << 17) /* Bit 17: NAK status */ +#define OTG_DIEPCTL_EPTYP_SHIFT (18) /* Bits 18-19: Endpoint type */ +#define OTG_DIEPCTL_EPTYP_MASK (3 << OTG_DIEPCTL_EPTYP_SHIFT) +# define OTG_DIEPCTL_EPTYP_CTRL (0 << OTG_DIEPCTL_EPTYP_SHIFT) /* Control */ +# define OTG_DIEPCTL_EPTYP_ISOC (1 << OTG_DIEPCTL_EPTYP_SHIFT) /* Isochronous */ +# define OTG_DIEPCTL_EPTYP_BULK (2 << OTG_DIEPCTL_EPTYP_SHIFT) /* Bulk */ +# define OTG_DIEPCTL_EPTYP_INTR (3 << OTG_DIEPCTL_EPTYP_SHIFT) /* Interrupt */ + /* Bit 20: Reserved, must be kept at reset value */ +#define OTG_DIEPCTL_STALL (1 << 21) /* Bit 21: STALL handshake */ +#define OTG_DIEPCTL_TXFNUM_SHIFT (22) /* Bits 22-25: TxFIFO number */ +#define OTG_DIEPCTL_TXFNUM_MASK (15 << OTG_DIEPCTL_TXFNUM_SHIFT) +#define OTG_DIEPCTL_CNAK (1 << 26) /* Bit 26: Clear NAK */ +#define OTG_DIEPCTL_SNAK (1 << 27) /* Bit 27: Set NAK */ +#define OTG_DIEPCTL_SD0PID (1 << 28) /* Bit 28: Set DATA0 PID (interrupt/bulk) */ +#define OTG_DIEPCTL_SEVNFRM (1 << 28) /* Bit 28: Set even frame (isochronous)) */ +#define OTG_DIEPCTL_SODDFRM (1 << 29) /* Bit 29: Set odd frame (isochronous) */ +#define OTG_DIEPCTL_EPDIS (1 << 30) /* Bit 30: Endpoint disable */ +#define OTG_DIEPCTL_EPENA (1 << 31) /* Bit 31: Endpoint enable */ + +/* Device endpoint-n interrupt register */ + +#define OTG_DIEPINT_XFRC (1 << 0) /* Bit 0: Transfer completed interrupt */ +#define OTG_DIEPINT_EPDISD (1 << 1) /* Bit 1: Endpoint disabled interrupt */ + /* Bit 2: Reserved, must be kept at reset value */ +#define OTG_DIEPINT_TOC (1 << 3) /* Bit 3: Timeout condition */ +#define OTG_DIEPINT_ITTXFE (1 << 4) /* Bit 4: IN token received when TxFIFO is empty */ + /* Bit 5: Reserved, must be kept at reset value */ +#define OTG_DIEPINT_INEPNE (1 << 6) /* Bit 6: IN endpoint NAK effective */ +#define OTG_DIEPINT_TXFE (1 << 7) /* Bit 7: Transmit FIFO empty */ + /* Bits 8-31: Reserved, must be kept at reset value */ +/* Device IN endpoint 0 transfer size register */ + +#define OTG_DIEPTSIZ0_XFRSIZ_SHIFT (0) /* Bits 0-6: Transfer size */ +#define OTG_DIEPTSIZ0_XFRSIZ_MASK (0x7f << OTG_DIEPTSIZ0_XFRSIZ_SHIFT) + /* Bits 7-18: Reserved, must be kept at reset value */ +#define OTG_DIEPTSIZ0_PKTCNT_SHIFT (19) /* Bits 19-20: Packet count */ +#define OTG_DIEPTSIZ0_PKTCNT_MASK (3 << OTG_DIEPTSIZ0_PKTCNT_SHIFT) + /* Bits 21-31: Reserved, must be kept at reset value */ +/* Device IN endpoint n transfer size register */ + +#define OTG_DIEPTSIZ_XFRSIZ_SHIFT (0) /* Bits 0-18: Transfer size */ +#define OTG_DIEPTSIZ_XFRSIZ_MASK (0x7ffff << OTG_DIEPTSIZ_XFRSIZ_SHIFT) +#define OTG_DIEPTSIZ_PKTCNT_SHIFT (19) /* Bit 19-28: Packet count */ +#define OTG_DIEPTSIZ_PKTCNT_MASK (0x3ff << OTG_DIEPTSIZ_PKTCNT_SHIFT) +#define OTG_DIEPTSIZ_MCNT_SHIFT (29) /* Bits 29-30: Multi count */ +#define OTG_DIEPTSIZ_MCNT_MASK (3 << OTG_DIEPTSIZ_MCNT_SHIFT) + /* Bit 31: Reserved, must be kept at reset value */ +/* Device OUT endpoint TxFIFO status register */ + +#define OTG_DTXFSTS_MASK (0xffff) + +/* Device OUT endpoint 0 control register */ + +#define OTG_DOEPCTL0_MPSIZ_SHIFT (0) /* Bits 0-1: Maximum packet size */ +#define OTG_DOEPCTL0_MPSIZ_MASK (3 << OTG_DOEPCTL0_MPSIZ_SHIFT) +# define OTG_DOEPCTL0_MPSIZ_64 (0 << OTG_DOEPCTL0_MPSIZ_SHIFT) /* 64 bytes */ +# define OTG_DOEPCTL0_MPSIZ_32 (1 << OTG_DOEPCTL0_MPSIZ_SHIFT) /* 32 bytes */ +# define OTG_DOEPCTL0_MPSIZ_16 (2 << OTG_DOEPCTL0_MPSIZ_SHIFT) /* 16 bytes */ +# define OTG_DOEPCTL0_MPSIZ_8 (3 << OTG_DOEPCTL0_MPSIZ_SHIFT) /* 8 bytes */ + /* Bits 2-14: Reserved, must be kept at reset value */ +#define OTG_DOEPCTL0_USBAEP (1 << 15) /* Bit 15: USB active endpoint */ + /* Bit 16: Reserved, must be kept at reset value */ +#define OTG_DOEPCTL0_NAKSTS (1 << 17) /* Bit 17: NAK status */ +#define OTG_DOEPCTL0_EPTYP_SHIFT (18) /* Bits 18-19: Endpoint type */ +#define OTG_DOEPCTL0_EPTYP_MASK (3 << OTG_DOEPCTL0_EPTYP_SHIFT) +# define OTG_DOEPCTL0_EPTYP_CTRL (0 << OTG_DOEPCTL0_EPTYP_SHIFT) /* Control (hard-coded) */ +#define OTG_DOEPCTL0_SNPM (1 << 20) /* Bit 20: Snoop mode */ +#define OTG_DOEPCTL0_STALL (1 << 21) /* Bit 21: STALL handshake */ + /* Bits 22-25: Reserved, must be kept at reset value */ +#define OTG_DOEPCTL0_CNAK (1 << 26) /* Bit 26: Clear NAK */ +#define OTG_DOEPCTL0_SNAK (1 << 27) /* Bit 27: Set NAK */ + /* Bits 28-29: Reserved, must be kept at reset value */ +#define OTG_DOEPCTL0_EPDIS (1 << 30) /* Bit 30: Endpoint disable */ +#define OTG_DOEPCTL0_EPENA (1 << 31) /* Bit 31: Endpoint enable */ + +/* Device OUT endpoint n control register */ + +#define OTG_DOEPCTL_MPSIZ_SHIFT (0) /* Bits 0-10: Maximum packet size */ +#define OTG_DOEPCTL_MPSIZ_MASK (0x7ff << OTG_DOEPCTL_MPSIZ_SHIFT) + /* Bits 11-14: Reserved, must be kept at reset value */ +#define OTG_DOEPCTL_USBAEP (1 << 15) /* Bit 15: USB active endpoint */ +#define OTG_DOEPCTL_DPID (1 << 16) /* Bit 16: Endpoint data PID (interrupt/buld) */ +# define OTG_DOEPCTL_DATA0 (0) +# define OTG_DOEPCTL_DATA1 OTG_DOEPCTL_DPID +#define OTG_DOEPCTL_EONUM (1 << 16) /* Bit 16: Even/odd frame (isochronous) */ +# define OTG_DOEPCTL_EVEN (0) +# define OTG_DOEPCTL_ODD OTG_DOEPCTL_EONUM +#define OTG_DOEPCTL_NAKSTS (1 << 17) /* Bit 17: NAK status */ +#define OTG_DOEPCTL_EPTYP_SHIFT (18) /* Bits 18-19: Endpoint type */ +#define OTG_DOEPCTL_EPTYP_MASK (3 << OTG_DOEPCTL_EPTYP_SHIFT) +# define OTG_DOEPCTL_EPTYP_CTRL (0 << OTG_DOEPCTL_EPTYP_SHIFT) /* Control */ +# define OTG_DOEPCTL_EPTYP_ISOC (1 << OTG_DOEPCTL_EPTYP_SHIFT) /* Isochronous */ +# define OTG_DOEPCTL_EPTYP_BULK (2 << OTG_DOEPCTL_EPTYP_SHIFT) /* Bulk */ +# define OTG_DOEPCTL_EPTYP_INTR (3 << OTG_DOEPCTL_EPTYP_SHIFT) /* Interrupt */ +#define OTG_DOEPCTL_SNPM (1 << 20) /* Bit 20: Snoop mode */ +#define OTG_DOEPCTL_STALL (1 << 21) /* Bit 21: STALL handshake */ + /* Bits 22-25: Reserved, must be kept at reset value */ +#define OTG_DOEPCTL_CNAK (1 << 26) /* Bit 26: Clear NAK */ +#define OTG_DOEPCTL_SNAK (1 << 27) /* Bit 27: Set NAK */ +#define OTG_DOEPCTL_SD0PID (1 << 28) /* Bit 28: Set DATA0 PID (interrupt/bulk) */ +#define OTG_DOEPCTL_SEVNFRM (1 << 28) /* Bit 28: Set even frame (isochronous) */ +#define OTG_DOEPCTL_SD1PID (1 << 29) /* Bit 29: Set DATA1 PID (interrupt/bulk) */ +#define OTG_DOEPCTL_SODDFRM (1 << 29) /* Bit 29: Set odd frame (isochronous */ +#define OTG_DOEPCTL_EPDIS (1 << 30) /* Bit 30: Endpoint disable */ +#define OTG_DOEPCTL_EPENA (1 << 31) /* Bit 31: Endpoint enable */ + +/* Device endpoint-n interrupt register */ + +#define OTG_DOEPINT_XFRC (1 << 0) /* Bit 0: Transfer completed interrupt */ +#define OTG_DOEPINT_EPDISD (1 << 1) /* Bit 1: Endpoint disabled interrupt */ + /* Bit 2: Reserved, must be kept at reset value */ +#define OTG_DOEPINT_SETUP (1 << 3) /* Bit 3: SETUP phase done */ +#define OTG_DOEPINT_OTEPDIS (1 << 4) /* Bit 4: OUT token received when endpoint disabled */ + /* Bit 5: Reserved, must be kept at reset value */ +#define OTG_DOEPINT_B2BSTUP (1 << 6) /* Bit 6: Back-to-back SETUP packets received */ + /* Bits 7-31: Reserved, must be kept at reset value */ +/* Device OUT endpoint-0 transfer size register */ + +#define OTG_DOEPTSIZ0_XFRSIZ_SHIFT (0) /* Bits 0-6: Transfer size */ +#define OTG_DOEPTSIZ0_XFRSIZ_MASK (0x7f << OTG_DOEPTSIZ0_XFRSIZ_SHIFT) + /* Bits 7-18: Reserved, must be kept at reset value */ +#define OTG_DOEPTSIZ0_PKTCNT (1 << 19) /* Bit 19 PKTCNT: Packet count */ + /* Bits 20-28: Reserved, must be kept at reset value */ +#define OTG_DOEPTSIZ0_STUPCNT_SHIFT (29) /* Bits 29-30: SETUP packet count */ +#define OTG_DOEPTSIZ0_STUPCNT_MASK (3 << OTG_DOEPTSIZ0_STUPCNT_SHIFT) + /* Bit 31: Reserved, must be kept at reset value */ +/* Device OUT endpoint-n transfer size register */ + +#define OTG_DOEPTSIZ_XFRSIZ_SHIFT (0) /* Bits 0-18: Transfer size */ +#define OTG_DOEPTSIZ_XFRSIZ_MASK (0x7ffff << OTG_DOEPTSIZ_XFRSIZ_SHIFT) +#define OTG_DOEPTSIZ_PKTCNT_SHIFT (19) /* Bit 19-28: Packet count */ +#define OTG_DOEPTSIZ_PKTCNT_MASK (0x3ff << OTG_DOEPTSIZ_PKTCNT_SHIFT) +#define OTG_DOEPTSIZ_STUPCNT_SHIFT (29) /* Bits 29-30: SETUP packet count */ +#define OTG_DOEPTSIZ_STUPCNT_MASK (3 << OTG_DOEPTSIZ_STUPCNT_SHIFT) +#define OTG_DOEPTSIZ_RXDPID_SHIFT (29) /* Bits 29-30: Received data PID */ +#define OTG_DOEPTSIZ_RXDPID_MASK (3 << OTG_DOEPTSIZ_RXDPID_SHIFT) +# define OTG_DOEPTSIZ_RXDPID_DATA0 (0 << OTG_DOEPTSIZ_RXDPID_SHIFT) +# define OTG_DOEPTSIZ_RXDPID_DATA2 (1 << OTG_DOEPTSIZ_RXDPID_SHIFT) +# define OTG_DOEPTSIZ_RXDPID_DATA1 (2 << OTG_DOEPTSIZ_RXDPID_SHIFT) +# define OTG_DOEPTSIZ_RXDPID_MDATA (3 << OTG_DOEPTSIZ_RXDPID_SHIFT) + /* Bit 31: Reserved, must be kept at reset value */ +/* Power and clock gating control register */ + +#define OTG_PCGCCTL_STPPCLK (1 << 0) /* Bit 0: Stop PHY clock */ +#define OTG_PCGCCTL_GATEHCLK (1 << 1) /* Bit 1: Gate HCLK */ + /* Bits 2-3: Reserved, must be kept at reset value */ +#define OTG_PCGCCTL_PHYSUSP (1 << 4) /* Bit 4: PHY Suspended */ + /* Bits 5-31: Reserved, must be kept at reset value */ + +#endif /* __ARCH_ARM_SRC_STM32F7_CHIP_STM32_OTG_H */ diff --git a/arch/arm/src/stm32f7/chip/stm32f74xx75xx_sdmmc.h b/arch/arm/src/stm32f7/chip/stm32f74xx75xx_sdmmc.h index e40990ec16a..f710aed6d1b 100644 --- a/arch/arm/src/stm32f7/chip/stm32f74xx75xx_sdmmc.h +++ b/arch/arm/src/stm32f7/chip/stm32f74xx75xx_sdmmc.h @@ -1,5 +1,5 @@ /************************************************************************************ - * arch/arm/src/stm32/chip/stm32_sdio.h + * arch/arm/src/stm32f7/chip/stm32_sdio.h * * Copyright (C) 2009, 2011-2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt diff --git a/arch/arm/src/stm32f7/stm32_i2c.c b/arch/arm/src/stm32f7/stm32_i2c.c index bc34aa25aaa..6bd842263d3 100644 --- a/arch/arm/src/stm32f7/stm32_i2c.c +++ b/arch/arm/src/stm32f7/stm32_i2c.c @@ -1,5 +1,5 @@ /************************************************************************************ - * arch/arm/src/stm32/stm32f3xx_i2c.c + * arch/arm/src/stm32f7/stm32f3xx_i2c.c * STM32 F3 I2C Hardware Layer - Device Driver * * Copyright (C) 2011 Uros Platise. All rights reserved. diff --git a/arch/arm/src/stm32f7/stm32_otg.h b/arch/arm/src/stm32f7/stm32_otg.h new file mode 100644 index 00000000000..69def146083 --- /dev/null +++ b/arch/arm/src/stm32f7/stm32_otg.h @@ -0,0 +1,139 @@ +/************************************************************************************ + * arch/arm/src/stm32f7/stm32_otg.h + * + * Copyright (C) 2012-2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32F7_STM32_OTG_H +#define __ARCH_ARM_SRC_STM32F7_STM32_OTG_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +#include + + +#include "chip/stm32_otg.h" + +#if defined(CONFIG_STM32_OTGFS) || defined(CONFIG_STM32_OTGHS) + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ +/* Configuration ********************************************************************/ + +#ifndef CONFIG_OTG_PRI +# define CONFIG_OTG_PRI NVIC_SYSH_PRIORITY_DEFAULT +#endif + +#if defined(CONFIG_STM32_OTGFS) +# define STM32_IRQ_OTG STM32_IRQ_OTGFS +# define STM32_OTG_BASE STM32_USBOTGFS_BASE +# define STM32_NENDPOINTS (6) /* ep0-5 x 2 for IN and OUT */ +#endif + +#if defined(CONFIG_STM32_OTGHS) +# define STM32_IRQ_OTG STM32_IRQ_OTGHS +# define STM32_OTG_BASE STM32_USBOTGHS_BASE +# define STM32_NENDPOINTS (8) /* ep0-7 x 2 for IN and OUT */ +#endif + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: stm32_otghost_initialize + * + * Description: + * Initialize USB host device controller hardware. + * + * Input Parameters: + * controller -- If the device supports more than USB host controller, then + * this identifies which controller is being initializeed. Normally, this + * is just zero. + * + * Returned Value: + * And instance of the USB host interface. The controlling task should + * use this interface to (1) call the wait() method to wait for a device + * to be connected, and (2) call the enumerate() method to bind the device + * to a class driver. + * + * Assumptions: + * - This function should called in the initialization sequence in order + * to initialize the USB device functionality. + * - Class drivers should be initialized prior to calling this function. + * Otherwise, there is a race condition if the device is already connected. + * + ****************************************************************************/ + +#ifdef CONFIG_USBHOST +struct usbhost_connection_s; +FAR struct usbhost_connection_s *stm32_otghost_initialize(int controller); +#endif + +/************************************************************************************ + * Name: stm32_usbsuspend + * + * Description: + * Board logic must provide the stm32_usbsuspend logic if the OTG FS device driver + * is used. This function is called whenever the USB enters or leaves suspend + * mode. This is an opportunity for the board logic to shutdown clocks, power, + * etc. while the USB is suspended. + * + ************************************************************************************/ + +void stm32_usbsuspend(FAR struct usbdev_s *dev, bool resume); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_STM32_OTGFS */ +#endif /* __ARCH_ARM_SRC_STM32F7_STM32_OTG_H */ + diff --git a/arch/arm/src/stm32f7/stm32_otgdev.c b/arch/arm/src/stm32f7/stm32_otgdev.c new file mode 100644 index 00000000000..e362ef2b450 --- /dev/null +++ b/arch/arm/src/stm32f7/stm32_otgdev.c @@ -0,0 +1,5666 @@ +/**************************************************************************** + * arch/arm/src/stm32f7/stm32_otgdev.c + * + * Copyright (C) 2012-2014 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "chip.h" +#include "stm32_otg.h" +#include "up_arch.h" +#include "up_internal.h" + + +#if defined(CONFIG_USBDEV) && (defined(CONFIG_STM32_OTGFS) || defined(CONFIG_STM32_OTGHS)) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* Configuration ***************************************************************/ + +#ifndef CONFIG_USBDEV_EP0_MAXSIZE +# define CONFIG_USBDEV_EP0_MAXSIZE 64 +#endif + +#ifndef CONFIG_USBDEV_SETUP_MAXDATASIZE +# define CONFIG_USBDEV_SETUP_MAXDATASIZE CONFIG_USBDEV_EP0_MAXSIZE +#endif + +#ifndef CONFIG_USBDEV_MAXPOWER +# define CONFIG_USBDEV_MAXPOWER 100 /* mA */ +#endif + +/* There is 1.25Kb of FIFO memory. The default partitions this memory + * so that there is a TxFIFO allocated for each endpoint and with more + * memory provided for the common RxFIFO. A more knowledge-able + * configuration would not allocate any TxFIFO space to OUT endpoints. + */ + +#ifndef CONFIG_USBDEV_RXFIFO_SIZE +# define CONFIG_USBDEV_RXFIFO_SIZE 512 +#endif + +#ifndef CONFIG_USBDEV_EP0_TXFIFO_SIZE +# define CONFIG_USBDEV_EP0_TXFIFO_SIZE 192 +#endif + +#ifndef CONFIG_USBDEV_EP1_TXFIFO_SIZE +# define CONFIG_USBDEV_EP1_TXFIFO_SIZE 192 +#endif + +#ifndef CONFIG_USBDEV_EP2_TXFIFO_SIZE +# define CONFIG_USBDEV_EP2_TXFIFO_SIZE 192 +#endif + +#ifndef CONFIG_USBDEV_EP3_TXFIFO_SIZE +# define CONFIG_USBDEV_EP3_TXFIFO_SIZE 192 +#endif + +#if (CONFIG_USBDEV_RXFIFO_SIZE + CONFIG_USBDEV_EP0_TXFIFO_SIZE + \ + CONFIG_USBDEV_EP2_TXFIFO_SIZE + CONFIG_USBDEV_EP3_TXFIFO_SIZE) > 1280 +# error "FIFO allocations exceed FIFO memory size" +#endif + +/* The actual FIFO addresses that we use must be aligned to 4-byte boundaries; + * FIFO sizes must be provided in units of 32-bit words. + */ + +#define STM32_RXFIFO_BYTES ((CONFIG_USBDEV_RXFIFO_SIZE + 3) & ~3) +#define STM32_RXFIFO_WORDS ((CONFIG_USBDEV_RXFIFO_SIZE + 3) >> 2) + +#define STM32_EP0_TXFIFO_BYTES ((CONFIG_USBDEV_EP0_TXFIFO_SIZE + 3) & ~3) +#define STM32_EP0_TXFIFO_WORDS ((CONFIG_USBDEV_EP0_TXFIFO_SIZE + 3) >> 2) + +#if STM32_EP0_TXFIFO_WORDS < 16 || STM32_EP0_TXFIFO_WORDS > 256 +# error "CONFIG_USBDEV_EP0_TXFIFO_SIZE is out of range" +#endif + +#define STM32_EP1_TXFIFO_BYTES ((CONFIG_USBDEV_EP1_TXFIFO_SIZE + 3) & ~3) +#define STM32_EP1_TXFIFO_WORDS ((CONFIG_USBDEV_EP1_TXFIFO_SIZE + 3) >> 2) + +#if STM32_EP1_TXFIFO_WORDS < 16 +# error "CONFIG_USBDEV_EP1_TXFIFO_SIZE is out of range" +#endif + +#define STM32_EP2_TXFIFO_BYTES ((CONFIG_USBDEV_EP2_TXFIFO_SIZE + 3) & ~3) +#define STM32_EP2_TXFIFO_WORDS ((CONFIG_USBDEV_EP2_TXFIFO_SIZE + 3) >> 2) + +#if STM32_EP2_TXFIFO_WORDS < 16 +# error "CONFIG_USBDEV_EP2_TXFIFO_SIZE is out of range" +#endif + +#define STM32_EP3_TXFIFO_BYTES ((CONFIG_USBDEV_EP3_TXFIFO_SIZE + 3) & ~3) +#define STM32_EP3_TXFIFO_WORDS ((CONFIG_USBDEV_EP3_TXFIFO_SIZE + 3) >> 2) + +#if STM32_EP3_TXFIFO_WORDS < 16 +# error "CONFIG_USBDEV_EP3_TXFIFO_SIZE is out of range" +#endif + +/* Debug ***********************************************************************/ +/* Trace error codes */ + +#define STM32_TRACEERR_ALLOCFAIL 0x01 +#define STM32_TRACEERR_BADCLEARFEATURE 0x02 +#define STM32_TRACEERR_BADDEVGETSTATUS 0x03 +#define STM32_TRACEERR_BADEPNO 0x04 +#define STM32_TRACEERR_BADEPGETSTATUS 0x05 +#define STM32_TRACEERR_BADGETCONFIG 0x06 +#define STM32_TRACEERR_BADGETSETDESC 0x07 +#define STM32_TRACEERR_BADGETSTATUS 0x08 +#define STM32_TRACEERR_BADSETADDRESS 0x09 +#define STM32_TRACEERR_BADSETCONFIG 0x0a +#define STM32_TRACEERR_BADSETFEATURE 0x0b +#define STM32_TRACEERR_BADTESTMODE 0x0c +#define STM32_TRACEERR_BINDFAILED 0x0d +#define STM32_TRACEERR_DISPATCHSTALL 0x0e +#define STM32_TRACEERR_DRIVER 0x0f +#define STM32_TRACEERR_DRIVERREGISTERED 0x10 +#define STM32_TRACEERR_EP0NOSETUP 0x11 +#define STM32_TRACEERR_EP0SETUPSTALLED 0x12 +#define STM32_TRACEERR_EPINNULLPACKET 0x13 +#define STM32_TRACEERR_EPINUNEXPECTED 0x14 +#define STM32_TRACEERR_EPOUTNULLPACKET 0x15 +#define STM32_TRACEERR_EPOUTUNEXPECTED 0x16 +#define STM32_TRACEERR_INVALIDCTRLREQ 0x17 +#define STM32_TRACEERR_INVALIDPARMS 0x18 +#define STM32_TRACEERR_IRQREGISTRATION 0x19 +#define STM32_TRACEERR_NOEP 0x1a +#define STM32_TRACEERR_NOTCONFIGURED 0x1b +#define STM32_TRACEERR_EPOUTQEMPTY 0x1c +#define STM32_TRACEERR_EPINREQEMPTY 0x1d +#define STM32_TRACEERR_NOOUTSETUP 0x1e +#define STM32_TRACEERR_POLLTIMEOUT 0x1f + +/* Trace interrupt codes */ + +#define STM32_TRACEINTID_USB 1 /* USB Interrupt entry/exit */ +#define STM32_TRACEINTID_INTPENDING 2 /* On each pass through the loop */ + +#define STM32_TRACEINTID_EPOUT (10 + 0) /* First level interrupt decode */ +#define STM32_TRACEINTID_EPIN (10 + 1) +#define STM32_TRACEINTID_MISMATCH (10 + 2) +#define STM32_TRACEINTID_WAKEUP (10 + 3) +#define STM32_TRACEINTID_SUSPEND (10 + 4) +#define STM32_TRACEINTID_SOF (10 + 5) +#define STM32_TRACEINTID_RXFIFO (10 + 6) +#define STM32_TRACEINTID_DEVRESET (10 + 7) +#define STM32_TRACEINTID_ENUMDNE (10 + 8) +#define STM32_TRACEINTID_IISOIXFR (10 + 9) +#define STM32_TRACEINTID_IISOOXFR (10 + 10) +#define STM32_TRACEINTID_SRQ (10 + 11) +#define STM32_TRACEINTID_OTG (10 + 12) + +#define STM32_TRACEINTID_EPOUT_XFRC (40 + 0) /* EPOUT second level decode */ +#define STM32_TRACEINTID_EPOUT_EPDISD (40 + 1) +#define STM32_TRACEINTID_EPOUT_SETUP (40 + 2) +#define STM32_TRACEINTID_DISPATCH (40 + 3) + +#define STM32_TRACEINTID_GETSTATUS (50 + 0) /* EPOUT third level decode */ +#define STM32_TRACEINTID_EPGETSTATUS (50 + 1) +#define STM32_TRACEINTID_DEVGETSTATUS (50 + 2) +#define STM32_TRACEINTID_IFGETSTATUS (50 + 3) +#define STM32_TRACEINTID_CLEARFEATURE (50 + 4) +#define STM32_TRACEINTID_SETFEATURE (50 + 5) +#define STM32_TRACEINTID_SETADDRESS (50 + 6) +#define STM32_TRACEINTID_GETSETDESC (50 + 7) +#define STM32_TRACEINTID_GETCONFIG (50 + 8) +#define STM32_TRACEINTID_SETCONFIG (50 + 9) +#define STM32_TRACEINTID_GETSETIF (50 + 10) +#define STM32_TRACEINTID_SYNCHFRAME (50 + 11) + +#define STM32_TRACEINTID_EPIN_XFRC (70 + 0) /* EPIN second level decode */ +#define STM32_TRACEINTID_EPIN_TOC (70 + 1) +#define STM32_TRACEINTID_EPIN_ITTXFE (70 + 2) +#define STM32_TRACEINTID_EPIN_EPDISD (70 + 3) +#define STM32_TRACEINTID_EPIN_TXFE (70 + 4) + +#define STM32_TRACEINTID_EPIN_EMPWAIT (80 + 0) /* EPIN second level decode */ + +#define STM32_TRACEINTID_OUTNAK (90 + 0) /* RXFLVL second level decode */ +#define STM32_TRACEINTID_OUTRECVD (90 + 1) +#define STM32_TRACEINTID_OUTDONE (90 + 2) +#define STM32_TRACEINTID_SETUPDONE (90 + 3) +#define STM32_TRACEINTID_SETUPRECVD (90 + 4) + +/* Endpoints ******************************************************************/ + + +/* Odd physical endpoint numbers are IN; even are OUT */ + +#define STM32_EPPHYIN2LOG(epphy) ((uint8_t)(epphy)|USB_DIR_IN) +#define STM32_EPPHYOUT2LOG(epphy) ((uint8_t)(epphy)|USB_DIR_OUT) + +/* Endpoint 0 */ + +#define EP0 (0) + +/* The set of all enpoints available to the class implementation (1-3) */ + +#define STM32_EP_AVAILABLE (0x0e) /* All available endpoints */ + +/* Maximum packet sizes for full speed endpoints */ + +#define STM32_MAXPACKET (64) /* Max packet size (1-64) */ + +/* Delays **********************************************************************/ + +#define STM32_READY_DELAY 200000 +#define STM32_FLUSH_DELAY 200000 + +/* Request queue operations ****************************************************/ + +#define stm32_rqempty(ep) ((ep)->head == NULL) +#define stm32_rqpeek(ep) ((ep)->head) + +/* Standard stuff **************************************************************/ + +#ifndef MIN +# define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef MAX +# define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Overall device state */ + +enum stm32_devstate_e +{ + DEVSTATE_DEFAULT = 0, /* Power-up, unconfigured state. This state simply + * means that the device is not yet been given an + * address. + * SET: At initialization, uninitialization, + * reset, and whenever the device address + * is set to zero + * TESTED: Never + */ + DEVSTATE_ADDRESSED, /* Device address has been assigned, not no + * configuration has yet been selected. + * SET: When either a non-zero device address + * is first assigned or when the device + * is unconfigured (with configuration == 0) + * TESTED: never + */ + DEVSTATE_CONFIGURED, /* Address assigned and configured: + * SET: When the device has been addressed and + * an non-zero configuration has been selected. + * TESTED: In many places to assure that the USB device + * has been properly configured by the host. + */ +}; + +/* Endpoint 0 states */ + +enum stm32_ep0state_e +{ + EP0STATE_IDLE = 0, /* Idle State, leave on receiving a SETUP packet or + * epsubmit: + * SET: In stm32_epin() and stm32_epout() when + * we revert from request processing to + * SETUP processing. + * TESTED: Never + */ + EP0STATE_SETUP_OUT, /* OUT SETUP packet received. Waiting for the DATA + * OUT phase of SETUP Packet to complete before + * processing a SETUP command (without a USB request): + * SET: Set in stm32_rxinterrupt() when SETUP OUT + * packet is received. + * TESTED: In stm32_ep0out_receive() + */ + EP0STATE_SETUP_READY, /* IN SETUP packet received -OR- OUT SETUP packet and + * accompanying data have been received. Processing + * of SETUP command will happen soon. + * SET: (1) stm32_ep0out_receive() when the OUT + * SETUP data phase completes, or (2) + * stm32_rxinterrupt() when an IN SETUP is + * packet received. + * TESTED: Tested in stm32_epout_interrupt() when + * SETUP phase is done to see if the SETUP + * command is ready to be processed. Also + * tested in stm32_ep0out_setup() just to + * double-check that we have a SETUP request + * and any accompanying data. + */ + EP0STATE_SETUP_PROCESS, /* SETUP Packet is being processed by stm32_ep0out_setup(): + * SET: When SETUP packet received in EP0 OUT + * TESTED: Never + */ + EP0STATE_SETUPRESPONSE, /* Short SETUP response write (without a USB request): + * SET: When SETUP response is sent by + * stm32_ep0in_setupresponse() + * TESTED: Never + */ + EP0STATE_DATA_IN, /* Waiting for data out stage (with a USB request): + * SET: In stm32_epin_request() when a write + * request is processed on EP0. + * TESTED: In stm32_epin() to see if we should + * revert to SETUP processing. + */ + EP0STATE_DATA_OUT /* Waiting for data in phase to complete ( with a + * USB request) + * SET: In stm32_epout_request() when a read + * request is processed on EP0. + * TESTED: In stm32_epout() to see if we should + * revert to SETUP processing + */ +}; + +/* Parsed control request */ + +struct stm32_ctrlreq_s +{ + uint8_t type; + uint8_t req; + uint16_t value; + uint16_t index; + uint16_t len; +}; + +/* A container for a request so that the request may be retained in a list */ + +struct stm32_req_s +{ + struct usbdev_req_s req; /* Standard USB request */ + struct stm32_req_s *flink; /* Supports a singly linked list */ +}; + +/* This is the internal representation of an endpoint */ + +struct stm32_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 stm32_ep_s. + */ + + struct usbdev_ep_s ep; /* Standard endpoint structure */ + + /* STM32-specific fields */ + + struct stm32_usbdev_s *dev; /* Reference to private driver data */ + struct stm32_req_s *head; /* Request list for this endpoint */ + struct stm32_req_s *tail; + uint8_t epphy; /* Physical EP address */ + uint8_t eptype:2; /* Endpoint type */ + uint8_t active:1; /* 1: A request is being processed */ + uint8_t stalled:1; /* 1: Endpoint is stalled */ + uint8_t isin:1; /* 1: IN Endpoint */ + uint8_t odd:1; /* 1: Odd frame */ + uint8_t zlp:1; /* 1: Transmit a zero-length-packet (IN EPs only) */ +}; + +/* This structure retains the state of the USB device controller */ + +struct stm32_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 stm32_usbdev_s. + */ + + struct usbdev_s usbdev; + + /* The bound device class driver */ + + struct usbdevclass_driver_s *driver; + + /* STM32-specific fields */ + + uint8_t stalled:1; /* 1: Protocol stalled */ + uint8_t selfpowered:1; /* 1: Device is self powered */ + uint8_t addressed:1; /* 1: Peripheral address has been set */ + uint8_t configured:1; /* 1: Class driver has been configured */ + uint8_t wakeup:1; /* 1: Device remote wake-up */ + uint8_t dotest:1; /* 1: Test mode selected */ + + uint8_t devstate:4; /* See enum stm32_devstate_e */ + uint8_t ep0state:4; /* See enum stm32_ep0state_e */ + uint8_t testmode:4; /* Selected test mode */ + uint8_t epavail[2]; /* Bitset of available OUT/IN endpoints */ + + /* E0 SETUP data buffering. + * + * ctrlreq: + * The 8-byte SETUP request is received on the EP0 OUT endpoint and is + * saved. + * + * ep0data + * For OUT SETUP requests, the SETUP data phase must also complete before + * the SETUP command can be processed. The pack receipt logic will save + * the accompanying EP0 IN data in ep0data[] before the SETUP command is + * processed. + * + * For IN SETUP requests, the DATA phase will occur AFTER the SETUP + * control request is processed. In that case, ep0data[] may be used as + * the response buffer. + * + * ep0datlen + * Length of OUT DATA received in ep0data[] (Not used with OUT data) + */ + + struct usb_ctrlreq_s ctrlreq; + uint8_t ep0data[CONFIG_USBDEV_SETUP_MAXDATASIZE]; + uint16_t ep0datlen; + + /* The endpoint lists */ + + struct stm32_ep_s epin[STM32_NENDPOINTS]; + struct stm32_ep_s epout[STM32_NENDPOINTS]; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Register operations ********************************************************/ + +#if defined(CONFIG_STM32_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG_FEATURES) +static uint32_t stm32_getreg(uint32_t addr); +static void stm32_putreg(uint32_t val, uint32_t addr); +#else +# define stm32_getreg(addr) getreg32(addr) +# define stm32_putreg(val,addr) putreg32(val,addr) +#endif + +/* Request queue operations ****************************************************/ + +static FAR struct stm32_req_s *stm32_req_remfirst(FAR struct stm32_ep_s *privep); +static bool stm32_req_addlast(FAR struct stm32_ep_s *privep, + FAR struct stm32_req_s *req); + +/* Low level data transfers and request operations *****************************/ +/* Special endpoint 0 data transfer logic */ + +static void stm32_ep0in_setupresponse(FAR struct stm32_usbdev_s *priv, + FAR uint8_t *data, uint32_t nbytes); +static inline void stm32_ep0in_transmitzlp(FAR struct stm32_usbdev_s *priv); +static void stm32_ep0in_activate(void); + +static void stm32_ep0out_ctrlsetup(FAR struct stm32_usbdev_s *priv); + +/* IN request and TxFIFO handling */ + +static void stm32_txfifo_write(FAR struct stm32_ep_s *privep, + FAR uint8_t *buf, int nbytes); +static void stm32_epin_transfer(FAR struct stm32_ep_s *privep, + FAR uint8_t *buf, int nbytes); +static void stm32_epin_request(FAR struct stm32_usbdev_s *priv, + FAR struct stm32_ep_s *privep); + +/* OUT request and RxFIFO handling */ + +static void stm32_rxfifo_read(FAR struct stm32_ep_s *privep, + FAR uint8_t *dest, uint16_t len); +static void stm32_rxfifo_discard(FAR struct stm32_ep_s *privep, int len); +static void stm32_epout_complete(FAR struct stm32_usbdev_s *priv, + FAR struct stm32_ep_s *privep); +static inline void stm32_ep0out_receive(FAR struct stm32_ep_s *privep, int bcnt); +static inline void stm32_epout_receive(FAR struct stm32_ep_s *privep, int bcnt); +static void stm32_epout_request(FAR struct stm32_usbdev_s *priv, + FAR struct stm32_ep_s *privep); + +/* General request handling */ + +static void stm32_ep_flush(FAR struct stm32_ep_s *privep); +static void stm32_req_complete(FAR struct stm32_ep_s *privep, + int16_t result); +static void stm32_req_cancel(FAR struct stm32_ep_s *privep, + int16_t status); + +/* Interrupt handling **********************************************************/ + +static struct stm32_ep_s *stm32_ep_findbyaddr(struct stm32_usbdev_s *priv, + uint16_t eplog); +static int stm32_req_dispatch(FAR struct stm32_usbdev_s *priv, + FAR const struct usb_ctrlreq_s *ctrl); +static void stm32_usbreset(FAR struct stm32_usbdev_s *priv); + +/* Second level OUT endpoint interrupt processing */ + +static inline void stm32_ep0out_testmode(FAR struct stm32_usbdev_s *priv, + uint16_t index); +static inline void stm32_ep0out_stdrequest(struct stm32_usbdev_s *priv, + FAR struct stm32_ctrlreq_s *ctrlreq); +static inline void stm32_ep0out_setup(struct stm32_usbdev_s *priv); +static inline void stm32_epout(FAR struct stm32_usbdev_s *priv, + uint8_t epno); +static inline void stm32_epout_interrupt(FAR struct stm32_usbdev_s *priv); + +/* Second level IN endpoint interrupt processing */ + +static inline void stm32_epin_runtestmode(FAR struct stm32_usbdev_s *priv); +static inline void stm32_epin(FAR struct stm32_usbdev_s *priv, uint8_t epno); +static inline void stm32_epin_txfifoempty(FAR struct stm32_usbdev_s *priv, int epno); +static inline void stm32_epin_interrupt(FAR struct stm32_usbdev_s *priv); + +/* Other second level interrupt processing */ + +static inline void stm32_resumeinterrupt(FAR struct stm32_usbdev_s *priv); +static inline void stm32_suspendinterrupt(FAR struct stm32_usbdev_s *priv); +static inline void stm32_rxinterrupt(FAR struct stm32_usbdev_s *priv); +static inline void stm32_enuminterrupt(FAR struct stm32_usbdev_s *priv); +#ifdef CONFIG_USBDEV_ISOCHRONOUS +static inline void stm32_isocininterrupt(FAR struct stm32_usbdev_s *priv); +static inline void stm32_isocoutinterrupt(FAR struct stm32_usbdev_s *priv); +#endif +#ifdef CONFIG_USBDEV_VBUSSENSING +static inline void stm32_sessioninterrupt(FAR struct stm32_usbdev_s *priv); +static inline void stm32_otginterrupt(FAR struct stm32_usbdev_s *priv); +#endif + +/* First level interrupt processing */ + +static int stm32_usbinterrupt(int irq, FAR void *context); + +/* Endpoint operations *********************************************************/ +/* Global OUT NAK controls */ + +static void stm32_enablegonak(FAR struct stm32_ep_s *privep); +static void stm32_disablegonak(FAR struct stm32_ep_s *privep); + +/* Endpoint configuration */ + +static int stm32_epout_configure(FAR struct stm32_ep_s *privep, + uint8_t eptype, uint16_t maxpacket); +static int stm32_epin_configure(FAR struct stm32_ep_s *privep, + uint8_t eptype, uint16_t maxpacket); +static int stm32_ep_configure(FAR struct usbdev_ep_s *ep, + FAR const struct usb_epdesc_s *desc, bool last); +static void stm32_ep0_configure(FAR struct stm32_usbdev_s *priv); + +/* Endpoint disable */ + +static void stm32_epout_disable(FAR struct stm32_ep_s *privep); +static void stm32_epin_disable(FAR struct stm32_ep_s *privep); +static int stm32_ep_disable(FAR struct usbdev_ep_s *ep); + +/* Endpoint request management */ + +static FAR struct usbdev_req_s *stm32_ep_allocreq(FAR struct usbdev_ep_s *ep); +static void stm32_ep_freereq(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *); + +/* Endpoint buffer management */ + +#ifdef CONFIG_USBDEV_DMA +static void *stm32_ep_allocbuffer(FAR struct usbdev_ep_s *ep, unsigned bytes); +static void stm32_ep_freebuffer(FAR struct usbdev_ep_s *ep, FAR void *buf); +#endif + +/* Endpoint request submission */ + +static int stm32_ep_submit(FAR struct usbdev_ep_s *ep, + struct usbdev_req_s *req); + +/* Endpoint request cancellation */ + +static int stm32_ep_cancel(FAR struct usbdev_ep_s *ep, + struct usbdev_req_s *req); + +/* Stall handling */ + +static int stm32_epout_setstall(FAR struct stm32_ep_s *privep); +static int stm32_epin_setstall(FAR struct stm32_ep_s *privep); +static int stm32_ep_setstall(FAR struct stm32_ep_s *privep); +static int stm32_ep_clrstall(FAR struct stm32_ep_s *privep); +static int stm32_ep_stall(FAR struct usbdev_ep_s *ep, bool resume); +static void stm32_ep0_stall(FAR struct stm32_usbdev_s *priv); + +/* Endpoint allocation */ + +static FAR struct usbdev_ep_s *stm32_ep_alloc(FAR struct usbdev_s *dev, + uint8_t epno, bool in, uint8_t eptype); +static void stm32_ep_free(FAR struct usbdev_s *dev, + FAR struct usbdev_ep_s *ep); + +/* USB device controller operations ********************************************/ + +static int stm32_getframe(struct usbdev_s *dev); +static int stm32_wakeup(struct usbdev_s *dev); +static int stm32_selfpowered(struct usbdev_s *dev, bool selfpowered); +static int stm32_pullup(struct usbdev_s *dev, bool enable); +static void stm32_setaddress(struct stm32_usbdev_s *priv, + uint16_t address); +static int stm32_txfifo_flush(uint32_t txfnum); +static int stm32_rxfifo_flush(void); + +/* Initialization **************************************************************/ + +static void stm32_swinitialize(FAR struct stm32_usbdev_s *priv); +static void stm32_hwinitialize(FAR struct stm32_usbdev_s *priv); + +/**************************************************************************** + * Private Data + ****************************************************************************/ +/* Since there is only a single USB interface, all status information can be + * be simply retained in a single global instance. + */ + +static struct stm32_usbdev_s g_otghsdev; + +static const struct usbdev_epops_s g_epops = +{ + .configure = stm32_ep_configure, + .disable = stm32_ep_disable, + .allocreq = stm32_ep_allocreq, + .freereq = stm32_ep_freereq, +#ifdef CONFIG_USBDEV_DMA + .allocbuffer = stm32_ep_allocbuffer, + .freebuffer = stm32_ep_freebuffer, +#endif + .submit = stm32_ep_submit, + .cancel = stm32_ep_cancel, + .stall = stm32_ep_stall, +}; + +static const struct usbdev_ops_s g_devops = +{ + .allocep = stm32_ep_alloc, + .freeep = stm32_ep_free, + .getframe = stm32_getframe, + .wakeup = stm32_wakeup, + .selfpowered = stm32_selfpowered, + .pullup = stm32_pullup, +}; + +/* Device error strings that may be enabled for more descriptive USB trace + * output. + */ + +#ifdef CONFIG_USBDEV_TRACE_STRINGS +const struct trace_msg_t g_usb_trace_strings_deverror[] = +{ + TRACE_STR(STM32_TRACEERR_ALLOCFAIL ), + TRACE_STR(STM32_TRACEERR_BADCLEARFEATURE ), + TRACE_STR(STM32_TRACEERR_BADDEVGETSTATUS ), + TRACE_STR(STM32_TRACEERR_BADEPNO ), + TRACE_STR(STM32_TRACEERR_BADEPGETSTATUS ), + TRACE_STR(STM32_TRACEERR_BADGETCONFIG ), + TRACE_STR(STM32_TRACEERR_BADGETSETDESC ), + TRACE_STR(STM32_TRACEERR_BADGETSTATUS ), + TRACE_STR(STM32_TRACEERR_BADSETADDRESS ), + TRACE_STR(STM32_TRACEERR_BADSETCONFIG ), + TRACE_STR(STM32_TRACEERR_BADSETFEATURE ), + TRACE_STR(STM32_TRACEERR_BADTESTMODE ), + TRACE_STR(STM32_TRACEERR_BINDFAILED ), + TRACE_STR(STM32_TRACEERR_DISPATCHSTALL ), + TRACE_STR(STM32_TRACEERR_DRIVER ), + TRACE_STR(STM32_TRACEERR_DRIVERREGISTERED), + TRACE_STR(STM32_TRACEERR_EP0NOSETUP ), + TRACE_STR(STM32_TRACEERR_EP0SETUPSTALLED ), + TRACE_STR(STM32_TRACEERR_EPINNULLPACKET ), + TRACE_STR(STM32_TRACEERR_EPINUNEXPECTED ), + TRACE_STR(STM32_TRACEERR_EPOUTNULLPACKET ), + TRACE_STR(STM32_TRACEERR_EPOUTUNEXPECTED ), + TRACE_STR(STM32_TRACEERR_INVALIDCTRLREQ ), + TRACE_STR(STM32_TRACEERR_INVALIDPARMS ), + TRACE_STR(STM32_TRACEERR_IRQREGISTRATION ), + TRACE_STR(STM32_TRACEERR_NOEP ), + TRACE_STR(STM32_TRACEERR_NOTCONFIGURED ), + TRACE_STR(STM32_TRACEERR_EPOUTQEMPTY ), + TRACE_STR(STM32_TRACEERR_EPINREQEMPTY ), + TRACE_STR(STM32_TRACEERR_NOOUTSETUP ), + TRACE_STR(STM32_TRACEERR_POLLTIMEOUT ), + TRACE_STR_END +}; +#endif + +/* Interrupt event strings that may be enabled for more descriptive USB trace + * output. + */ + +#ifdef CONFIG_USBDEV_TRACE_STRINGS +const struct trace_msg_t g_usb_trace_strings_intdecode[] = +{ + TRACE_STR(STM32_TRACEINTID_USB ), + TRACE_STR(STM32_TRACEINTID_INTPENDING ), + TRACE_STR(STM32_TRACEINTID_EPOUT ), + TRACE_STR(STM32_TRACEINTID_EPIN ), + TRACE_STR(STM32_TRACEINTID_MISMATCH ), + TRACE_STR(STM32_TRACEINTID_WAKEUP ), + TRACE_STR(STM32_TRACEINTID_SUSPEND ), + TRACE_STR(STM32_TRACEINTID_SOF ), + TRACE_STR(STM32_TRACEINTID_RXFIFO ), + TRACE_STR(STM32_TRACEINTID_DEVRESET ), + TRACE_STR(STM32_TRACEINTID_ENUMDNE ), + TRACE_STR(STM32_TRACEINTID_IISOIXFR ), + TRACE_STR(STM32_TRACEINTID_IISOOXFR ), + TRACE_STR(STM32_TRACEINTID_SRQ ), + TRACE_STR(STM32_TRACEINTID_OTG ), + TRACE_STR(STM32_TRACEINTID_EPOUT_XFRC ), + TRACE_STR(STM32_TRACEINTID_EPOUT_EPDISD), + TRACE_STR(STM32_TRACEINTID_EPOUT_SETUP ), + TRACE_STR(STM32_TRACEINTID_DISPATCH ), + TRACE_STR(STM32_TRACEINTID_GETSTATUS ), + TRACE_STR(STM32_TRACEINTID_EPGETSTATUS ), + TRACE_STR(STM32_TRACEINTID_DEVGETSTATUS), + TRACE_STR(STM32_TRACEINTID_IFGETSTATUS ), + TRACE_STR(STM32_TRACEINTID_CLEARFEATURE), + TRACE_STR(STM32_TRACEINTID_SETFEATURE ), + TRACE_STR(STM32_TRACEINTID_SETADDRESS ), + TRACE_STR(STM32_TRACEINTID_GETSETDESC ), + TRACE_STR(STM32_TRACEINTID_GETCONFIG ), + TRACE_STR(STM32_TRACEINTID_SETCONFIG ), + TRACE_STR(STM32_TRACEINTID_GETSETIF ), + TRACE_STR(STM32_TRACEINTID_SYNCHFRAME ), + TRACE_STR(STM32_TRACEINTID_EPIN_XFRC ), + TRACE_STR(STM32_TRACEINTID_EPIN_TOC ), + TRACE_STR(STM32_TRACEINTID_EPIN_ITTXFE ), + TRACE_STR(STM32_TRACEINTID_EPIN_EPDISD ), + TRACE_STR(STM32_TRACEINTID_EPIN_TXFE ), + TRACE_STR(STM32_TRACEINTID_EPIN_EMPWAIT), + TRACE_STR(STM32_TRACEINTID_OUTNAK ), + TRACE_STR(STM32_TRACEINTID_OUTRECVD ), + TRACE_STR(STM32_TRACEINTID_OUTDONE ), + TRACE_STR(STM32_TRACEINTID_SETUPDONE ), + TRACE_STR(STM32_TRACEINTID_SETUPRECVD ), + TRACE_STR_END +}; +#endif + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_getreg + * + * Description: + * Get the contents of an STM32 register + * + ****************************************************************************/ + +#if defined(CONFIG_STM32_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG_FEATURES) +static uint32_t stm32_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) + { + llerr("...\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 */ + + llerr("[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 */ + + llerr("%08x->%08x\n", addr, val); + return val; +} +#endif + +/**************************************************************************** + * Name: stm32_putreg + * + * Description: + * Set the contents of an STM32 register to a value + * + ****************************************************************************/ + +#if defined(CONFIG_STM32_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG_FEATURES) +static void stm32_putreg(uint32_t val, uint32_t addr) +{ + /* Show the register value being written */ + + llerr("%08x<-%08x\n", addr, val); + + /* Write the value */ + + putreg32(val, addr); +} +#endif + +/**************************************************************************** + * Name: stm32_req_remfirst + * + * Description: + * Remove a request from the head of an endpoint request queue + * + ****************************************************************************/ + +static FAR struct stm32_req_s *stm32_req_remfirst(FAR struct stm32_ep_s *privep) +{ + FAR struct stm32_req_s *ret = privep->head; + + if (ret) + { + privep->head = ret->flink; + if (!privep->head) + { + privep->tail = NULL; + } + + ret->flink = NULL; + } + + return ret; +} + +/**************************************************************************** + * Name: stm32_req_addlast + * + * Description: + * Add a request to the end of an endpoint request queue + * + ****************************************************************************/ + +static bool stm32_req_addlast(FAR struct stm32_ep_s *privep, + FAR struct stm32_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: stm32_ep0in_setupresponse + * + * Description: + * Schedule a short transfer on Endpoint 0 (IN or OUT) + * + ****************************************************************************/ + +static void stm32_ep0in_setupresponse(FAR struct stm32_usbdev_s *priv, + FAR uint8_t *buf, uint32_t nbytes) +{ + stm32_epin_transfer(&priv->epin[EP0], buf, nbytes); + priv->ep0state = EP0STATE_SETUPRESPONSE; + stm32_ep0out_ctrlsetup(priv); +} + +/**************************************************************************** + * Name: stm32_ep0in_transmitzlp + * + * Description: + * Send a zero length packet (ZLP) on endpoint 0 IN + * + ****************************************************************************/ + +static inline void stm32_ep0in_transmitzlp(FAR struct stm32_usbdev_s *priv) +{ + stm32_ep0in_setupresponse(priv, NULL, 0); +} + +/**************************************************************************** + * Name: stm32_ep0in_activate + * + * Description: + * Activate the endpoint 0 IN endpoint. + * + ****************************************************************************/ + +static void stm32_ep0in_activate(void) +{ + uint32_t regval; + + /* Set the max packet size of the IN EP. */ + + regval = stm32_getreg(STM32_OTG_DIEPCTL0); + regval &= ~OTG_DIEPCTL0_MPSIZ_MASK; + +#if CONFIG_USBDEV_EP0_MAXSIZE == 8 + regval |= OTG_DIEPCTL0_MPSIZ_8; +#elif CONFIG_USBDEV_EP0_MAXSIZE == 16 + regval |= OTG_DIEPCTL0_MPSIZ_16; +#elif CONFIG_USBDEV_EP0_MAXSIZE == 32 + regval |= OTG_DIEPCTL0_MPSIZ_32; +#elif CONFIG_USBDEV_EP0_MAXSIZE == 64 + regval |= OTG_DIEPCTL0_MPSIZ_64; +#else +# error "Unsupported value of CONFIG_USBDEV_EP0_MAXSIZE" +#endif + + stm32_putreg(regval, STM32_OTG_DIEPCTL0); + + /* Clear global IN NAK */ + + regval = stm32_getreg(STM32_OTG_DCTL); + regval |= OTG_DCTL_CGINAK; + stm32_putreg(regval, STM32_OTG_DCTL); +} + +/**************************************************************************** + * Name: stm32_ep0out_ctrlsetup + * + * Description: + * Setup to receive a SETUP packet. + * + ****************************************************************************/ + +static void stm32_ep0out_ctrlsetup(FAR struct stm32_usbdev_s *priv) +{ + uint32_t regval; + + /* Setup the hardware to perform the SETUP transfer */ + + regval = (USB_SIZEOF_CTRLREQ * 3 << OTG_DOEPTSIZ0_XFRSIZ_SHIFT) | + (OTG_DOEPTSIZ0_PKTCNT) | + (3 << OTG_DOEPTSIZ0_STUPCNT_SHIFT); + stm32_putreg(regval, STM32_OTG_DOEPTSIZ0); + + /* Then clear NAKing and enable the transfer */ + + regval = stm32_getreg(STM32_OTG_DOEPCTL0); + regval |= (OTG_DOEPCTL0_CNAK | OTG_DOEPCTL0_EPENA); + stm32_putreg(regval, STM32_OTG_DOEPCTL0); +} + +/**************************************************************************** + * Name: stm32_txfifo_write + * + * Description: + * Send data to the endpoint's TxFIFO. + * + ****************************************************************************/ + +static void stm32_txfifo_write(FAR struct stm32_ep_s *privep, + FAR uint8_t *buf, int nbytes) +{ + uint32_t regaddr; + uint32_t regval; + int nwords; + int i; + + /* Convert the number of bytes to words */ + + nwords = (nbytes + 3) >> 2; + + /* Get the TxFIFO for this endpoint (same as the endpoint number) */ + + regaddr = STM32_OTG_DFIFO_DEP(privep->epphy); + + /* Then transfer each word to the TxFIFO */ + + for (i = 0; i < nwords; i++) + { + /* Read four bytes from the source buffer (to avoid unaligned accesses) + * and pack these into one 32-bit word (little endian). + */ + + regval = (uint32_t)*buf++; + regval |= ((uint32_t)*buf++) << 8; + regval |= ((uint32_t)*buf++) << 16; + regval |= ((uint32_t)*buf++) << 24; + + /* Then write the packet data to the TxFIFO */ + + stm32_putreg(regval, regaddr); + } +} + +/**************************************************************************** + * Name: stm32_epin_transfer + * + * Description: + * Start the Tx data transfer + * + ****************************************************************************/ + +static void stm32_epin_transfer(FAR struct stm32_ep_s *privep, + FAR uint8_t *buf, int nbytes) +{ + uint32_t pktcnt; + uint32_t regval; + + /* Read the DIEPSIZx register */ + + regval = stm32_getreg(STM32_OTG_DIEPTSIZ(privep->epphy)); + + /* Clear the XFRSIZ, PKTCNT, and MCNT field of the DIEPSIZx register */ + + regval &= ~(OTG_DIEPTSIZ_XFRSIZ_MASK | OTG_DIEPTSIZ_PKTCNT_MASK | + OTG_DIEPTSIZ_MCNT_MASK); + + /* Are we sending a zero length packet (ZLP) */ + + if (nbytes == 0) + { + /* Yes.. leave the transfer size at zero and set the packet count to 1 */ + + pktcnt = 1; + } + else + { + /* No.. Program the transfer size and packet count . First calculate: + * + * xfrsize = The total number of bytes to be sent. + * pktcnt = the number of packets (of maxpacket bytes) required to + * perform the transfer. + */ + + pktcnt = ((uint32_t)nbytes + (privep->ep.maxpacket - 1)) / privep->ep.maxpacket; + } + + /* Set the XFRSIZ and PKTCNT */ + + regval |= (pktcnt << OTG_DIEPTSIZ_PKTCNT_SHIFT); + regval |= ((uint32_t)nbytes << OTG_DIEPTSIZ_XFRSIZ_SHIFT); + + /* If this is an isochronous endpoint, then set the multi-count field to + * the PKTCNT as well. + */ + + if (privep->eptype == USB_EP_ATTR_XFER_ISOC) + { + regval |= (pktcnt << OTG_DIEPTSIZ_MCNT_SHIFT); + } + + /* Save DIEPSIZx register value */ + + stm32_putreg(regval, STM32_OTG_DIEPTSIZ(privep->epphy)); + + /* Read the DIEPCTLx register */ + + regval = stm32_getreg(STM32_OTG_DIEPCTL(privep->epphy)); + + /* If this is an isochronous endpoint, then set the even/odd frame bit + * the DIEPCTLx register. + */ + + if (privep->eptype == USB_EP_ATTR_XFER_ISOC) + { + /* Check bit 0 of the frame number of the received SOF and set the + * even/odd frame to match. + */ + + uint32_t status = stm32_getreg(STM32_OTG_DSTS); + if ((status & OTG_DSTS_SOFFN0) == OTG_DSTS_SOFFN_EVEN) + { + regval |= OTG_DIEPCTL_SEVNFRM; + } + else + { + regval |= OTG_DIEPCTL_SODDFRM; + } + } + + /* EP enable, IN data in FIFO */ + + regval &= ~OTG_DIEPCTL_EPDIS; + regval |= (OTG_DIEPCTL_CNAK | OTG_DIEPCTL_EPENA); + stm32_putreg(regval, STM32_OTG_DIEPCTL(privep->epphy)); + + /* Transfer the data to the TxFIFO. At this point, the caller has already + * assured that there is sufficient space in the TxFIFO to hold the transfer + * we can just blindly continue. + */ + + stm32_txfifo_write(privep, buf, nbytes); +} + +/**************************************************************************** + * Name: stm32_epin_request + * + * Description: + * Begin or continue write request processing. + * + ****************************************************************************/ + +static void stm32_epin_request(FAR struct stm32_usbdev_s *priv, + FAR struct stm32_ep_s *privep) +{ + struct stm32_req_s *privreq; + uint32_t regaddr; + uint32_t regval; + uint8_t *buf; + int nbytes; + int nwords; + int bytesleft; + + /* We get here in one of four possible ways. From three interrupting + * events: + * + * 1. From stm32_epin as part of the transfer complete interrupt processing + * This interrupt indicates that the last transfer has completed. + * 2. As part of the ITTXFE interrupt processing. That interrupt indicates + * that an IN token was received when the associated TxFIFO was empty. + * 3. From stm32_epin_txfifoempty as part of the TXFE interrupt processing. + * The TXFE interrupt is only enabled when the TxFIFO is full and the + * software must wait for space to become available in the TxFIFO. + * + * And this function may be called immediately when the write request is + * queue to start up the next transaction. + * + * 4. From stm32_ep_submit when a new write request is received WHILE the + * endpoint is not active (privep->active == false). + */ + + /* Check the request from the head of the endpoint request queue */ + + privreq = stm32_rqpeek(privep); + if (!privreq) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPINREQEMPTY), privep->epphy); + + /* There is no TX transfer in progress and no new pending TX + * requests to send. To stop transmitting any data on a particular + * IN endpoint, the application must set the IN NAK bit. To set this + * bit, the following field must be programmed. + */ + + regaddr = STM32_OTG_DIEPCTL(privep->epphy); + regval = stm32_getreg(regaddr); + regval |= OTG_DIEPCTL_SNAK; + stm32_putreg(regval, regaddr); + + /* The endpoint is no longer active */ + + privep->active = false; + return; + } + + ullinfo("EP%d req=%p: len=%d xfrd=%d zlp=%d\n", + privep->epphy, privreq, privreq->req.len, + privreq->req.xfrd, privep->zlp); + + /* Check for a special case: If we are just starting a request (xfrd==0) and + * the class driver is trying to send a zero-length packet (len==0). Then set + * the ZLP flag so that the packet will be sent. + */ + + if (privreq->req.len == 0) + { + /* The ZLP flag is set TRUE whenever we want to force the driver to + * send a zero-length-packet on the next pass through the loop (below). + * The flag is cleared whenever a packet is sent in the loop below. + */ + + privep->zlp = true; + } + + /* Add one more packet to the TxFIFO. We will wait for the transfer + * complete event before we add the next packet (or part of a packet + * to the TxFIFO). + * + * The documentation says that we can can multiple packets to the TxFIFO, + * but it seems that we need to get the transfer complete event before + * we can add the next (or maybe I have got something wrong?) + */ + +#if 0 + while (privreq->req.xfrd < privreq->req.len || privep->zlp) +#else + if (privreq->req.xfrd < privreq->req.len || privep->zlp) +#endif + { + /* Get the number of bytes left to be sent in the request */ + + bytesleft = privreq->req.len - privreq->req.xfrd; + nbytes = bytesleft; + + /* Assume no zero-length-packet on the next pass through this loop */ + + privep->zlp = false; + + /* Limit the size of the transfer to one full packet and handle + * zero-length packets (ZLPs). + */ + + if (nbytes > 0) + { + /* Either send the maxpacketsize or all of the remaining data in + * the request. + */ + + if (nbytes >= privep->ep.maxpacket) + { + nbytes = privep->ep.maxpacket; + + /* Handle the case where this packet is exactly the + * maxpacketsize. Do we need to send a zero-length packet + * in this case? + */ + + if (bytesleft == privep->ep.maxpacket && + (privreq->req.flags & USBDEV_REQFLAGS_NULLPKT) != 0) + { + /* The ZLP flag is set TRUE whenever we want to force + * the driver to send a zero-length-packet on the next + * pass through this loop. The flag is cleared (above) + * whenever we are committed to sending any packet and + * set here when we want to force one more pass through + * the loop. + */ + + privep->zlp = true; + } + } + } + + /* Get the transfer size in 32-bit words */ + + nwords = (nbytes + 3) >> 2; + + /* Get the number of 32-bit words available in the TxFIFO. The + * DXTFSTS indicates the amount of free space available in the + * endpoint TxFIFO. Values are in terms of 32-bit words: + * + * 0: Endpoint TxFIFO is full + * 1: 1 word available + * 2: 2 words available + * n: n words available + */ + + regaddr = STM32_OTG_DTXFSTS(privep->epphy); + + /* Check for space in the TxFIFO. If space in the TxFIFO is not + * available, then set up an interrupt to resume the transfer when + * the TxFIFO is empty. + */ + + regval = stm32_getreg(regaddr); + if ((int)(regval & OTG_DTXFSTS_MASK) < nwords) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN_EMPWAIT), (uint16_t)regval); + + /* There is insufficient space in the TxFIFO. Wait for a TxFIFO + * empty interrupt and try again. + */ + + uint32_t empmsk = stm32_getreg(STM32_OTG_DIEPEMPMSK); + empmsk |= OTG_DIEPEMPMSK(privep->epphy); + stm32_putreg(empmsk, STM32_OTG_DIEPEMPMSK); + + /* Terminate the transfer. We will try again when the TxFIFO empty + * interrupt is received. + */ + + return; + } + + /* Transfer data to the TxFIFO */ + + buf = privreq->req.buf + privreq->req.xfrd; + stm32_epin_transfer(privep, buf, nbytes); + + /* If it was not before, the OUT endpoint is now actively transferring + * data. + */ + + privep->active = true; + + /* EP0 is a special case */ + + if (privep->epphy == EP0) + { + priv->ep0state = EP0STATE_DATA_IN; + } + + /* Update for the next time through the loop */ + + privreq->req.xfrd += nbytes; + } + + /* Note that the ZLP, if any, must be sent as a separate transfer. The need + * for a ZLP is indicated by privep->zlp. If all of the bytes were sent + * (including any final null packet) then we are finished with the transfer + */ + + if (privreq->req.xfrd >= privreq->req.len && !privep->zlp) + { + usbtrace(TRACE_COMPLETE(privep->epphy), privreq->req.xfrd); + + /* We are finished with the request (although the transfer has not + * yet completed). + */ + + stm32_req_complete(privep, OK); + } +} + +/**************************************************************************** + * Name: stm32_rxfifo_read + * + * Description: + * Read packet from the RxFIFO into a read request. + * + ****************************************************************************/ + +static void stm32_rxfifo_read(FAR struct stm32_ep_s *privep, + FAR uint8_t *dest, uint16_t len) +{ + uint32_t regaddr; + int i; + + /* Get the address of the RxFIFO. Note: there is only one RxFIFO so + * we might as well use the address associated with EP0. + */ + + regaddr = STM32_OTG_DFIFO_DEP(EP0); + + /* Read 32-bits and write 4 x 8-bits at time (to avoid unaligned accesses) */ + + for (i = 0; i < len; i += 4) + { + union + { + uint32_t w; + uint8_t b[4]; + } data; + + /* Read 1 x 32-bits of EP0 packet data */ + + data.w = stm32_getreg(regaddr); + + /* Write 4 x 8-bits of EP0 packet data */ + + *dest++ = data.b[0]; + *dest++ = data.b[1]; + *dest++ = data.b[2]; + *dest++ = data.b[3]; + } +} + +/**************************************************************************** + * Name: stm32_rxfifo_discard + * + * Description: + * Discard packet data from the RxFIFO. + * + ****************************************************************************/ + +static void stm32_rxfifo_discard(FAR struct stm32_ep_s *privep, int len) +{ + if (len > 0) + { + uint32_t regaddr; + int i; + + /* Get the address of the RxFIFO Note: there is only one RxFIFO so + * we might as well use the address associated with EP0. + */ + + regaddr = STM32_OTG_DFIFO_DEP(EP0); + + /* Read 32-bits at time */ + + for (i = 0; i < len; i += 4) + { + volatile uint32_t data = stm32_getreg(regaddr); + (void)data; + } + } +} + +/**************************************************************************** + * Name: stm32_epout_complete + * + * Description: + * This function is called when an OUT transfer complete interrupt is + * received. It completes the read request at the head of the endpoint's + * request queue. + * + ****************************************************************************/ + +static void stm32_epout_complete(FAR struct stm32_usbdev_s *priv, + FAR struct stm32_ep_s *privep) +{ + struct stm32_req_s *privreq; + + /* Since a transfer just completed, there must be a read request at the head of + * the endpoint request queue. + */ + + privreq = stm32_rqpeek(privep); + DEBUGASSERT(privreq); + + if (!privreq) + { + /* An OUT transfer completed, but no packet to receive the data. This + * should not happen. + */ + + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPOUTQEMPTY), privep->epphy); + privep->active = false; + return; + } + + ullinfo("EP%d: len=%d xfrd=%d\n", + privep->epphy, privreq->req.len, privreq->req.xfrd); + + /* Return the completed read request to the class driver and mark the state + * IDLE. + */ + + usbtrace(TRACE_COMPLETE(privep->epphy), privreq->req.xfrd); + stm32_req_complete(privep, OK); + privep->active = false; + + /* Now set up the next read request (if any) */ + + stm32_epout_request(priv, privep); +} + +/**************************************************************************** + * Name: stm32_ep0out_receive + * + * Description: + * This function is called from the RXFLVL interrupt handler when new incoming + * data is available in the endpoint's RxFIFO. This function will simply + * copy the incoming data into pending request's data buffer. + * + ****************************************************************************/ + +static inline void stm32_ep0out_receive(FAR struct stm32_ep_s *privep, int bcnt) +{ + FAR struct stm32_usbdev_s *priv; + + /* Sanity Checking */ + + DEBUGASSERT(privep && privep->ep.priv); + priv = (FAR struct stm32_usbdev_s *)privep->ep.priv; + + ullinfo("EP0: bcnt=%d\n", bcnt); + usbtrace(TRACE_READ(EP0), bcnt); + + /* Verify that an OUT SETUP request as received before this data was + * received in the RxFIFO. + */ + + if (priv->ep0state == EP0STATE_SETUP_OUT) + { + /* Read the data into our special buffer for SETUP data */ + + int readlen = MIN(CONFIG_USBDEV_SETUP_MAXDATASIZE, bcnt); + stm32_rxfifo_read(privep, priv->ep0data, readlen); + + /* Do we have to discard any excess bytes? */ + + stm32_rxfifo_discard(privep, bcnt - readlen); + + /* Now we can process the setup command */ + + privep->active = false; + priv->ep0state = EP0STATE_SETUP_READY; + priv->ep0datlen = readlen; + + stm32_ep0out_setup(priv); + } + else + { + /* This is an error. We don't have any idea what to do with the EP0 + * data in this case. Just read and discard it so that the RxFIFO + * does not become constipated. + */ + + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_NOOUTSETUP), priv->ep0state); + stm32_rxfifo_discard(privep, bcnt); + privep->active = false; + } +} + +/**************************************************************************** + * Name: stm32_epout_receive + * + * Description: + * This function is called from the RXFLVL interrupt handler when new incoming + * data is available in the endpoint's RxFIFO. This function will simply + * copy the incoming data into pending request's data buffer. + * + ****************************************************************************/ + +static inline void stm32_epout_receive(FAR struct stm32_ep_s *privep, int bcnt) +{ + struct stm32_req_s *privreq; + uint8_t *dest; + int buflen; + int readlen; + + /* Get a reference to the request at the head of the endpoint's request + * queue. + */ + + privreq = stm32_rqpeek(privep); + if (!privreq) + { + /* Incoming data is available in the RxFIFO, but there is no read setup + * to receive the receive the data. This should not happen for data + * endpoints; those endpoints should have been NAKing any OUT data tokens. + * + * We should get here normally on OUT data phase following an OUT + * SETUP command. EP0 data will still receive data in this case and it + * should not be NAKing. + */ + + if (privep->epphy == 0) + { + stm32_ep0out_receive(privep, bcnt); + } + else + { + /* Otherwise, the data is lost. This really should not happen if + * NAKing is working as expected. + */ + + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPOUTQEMPTY), privep->epphy); + + /* Discard the data in the RxFIFO */ + + stm32_rxfifo_discard(privep, bcnt); + } + + privep->active = false; + return; + } + + ullinfo("EP%d: len=%d xfrd=%d\n", privep->epphy, privreq->req.len, privreq->req.xfrd); + usbtrace(TRACE_READ(privep->epphy), bcnt); + + /* Get the number of bytes to transfer from the RxFIFO */ + + buflen = privreq->req.len - privreq->req.xfrd; + DEBUGASSERT(buflen > 0 && buflen >= bcnt); + readlen = MIN(buflen, bcnt); + + /* Get the destination of the data transfer */ + + dest = privreq->req.buf + privreq->req.xfrd; + + /* Transfer the data from the RxFIFO to the request's data buffer */ + + stm32_rxfifo_read(privep, dest, readlen); + + /* If there were more bytes in the RxFIFO than could be held in the read + * request, then we will have to discard those. + */ + + stm32_rxfifo_discard(privep, bcnt - readlen); + + /* Update the number of bytes transferred */ + + privreq->req.xfrd += readlen; +} + +/**************************************************************************** + * Name: stm32_epout_request + * + * Description: + * This function is called when either (1) new read request is received, or + * (2) a pending receive request completes. If there is no read in pending, + * then this function will initiate the next OUT (read) operation. + * + ****************************************************************************/ + +static void stm32_epout_request(FAR struct stm32_usbdev_s *priv, + FAR struct stm32_ep_s *privep) +{ + struct stm32_req_s *privreq; + uint32_t regaddr; + uint32_t regval; + uint32_t xfrsize; + uint32_t pktcnt; + + /* Make sure that there is not already a pending request request. If there is, + * just return, leaving the newly received request in the request queue. + */ + + if (!privep->active) + { + /* Loop until a valid request is found (or the request queue is empty). + * The loop is only need to look at the request queue again is an invalid + * read request is encountered. + */ + + for (; ; ) + { + /* Get a reference to the request at the head of the endpoint's request queue */ + + privreq = stm32_rqpeek(privep); + if (!privreq) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPOUTQEMPTY), privep->epphy); + + /* There are no read requests to be setup. Configure the hardware to + * NAK any incoming packets. (This should already be the case. I + * think that the hardware will automatically NAK after a transfer is + * completed until SNAK is cleared). + */ + + regaddr = STM32_OTG_DOEPCTL(privep->epphy); + regval = stm32_getreg(regaddr); + regval |= OTG_DOEPCTL_SNAK; + stm32_putreg(regval, regaddr); + + /* This endpoint is no longer actively transferring */ + + privep->active = false; + return; + } + + ullinfo("EP%d: len=%d\n", privep->epphy, privreq->req.len); + + /* Ignore any attempt to receive a zero length packet (this really + * should not happen. + */ + + if (privreq->req.len <= 0) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPOUTNULLPACKET), 0); + stm32_req_complete(privep, OK); + } + + /* Otherwise, we have a usable read request... break out of the loop */ + + else + { + break; + } + } + + /* Setup the pending read into the request buffer. First calculate: + * + * pktcnt = the number of packets (of maxpacket bytes) required to + * perform the transfer. + * xfrsize = The total number of bytes required (in units of + * maxpacket bytes). + */ + + pktcnt = (privreq->req.len + (privep->ep.maxpacket - 1)) / privep->ep.maxpacket; + xfrsize = pktcnt * privep->ep.maxpacket; + + /* Then setup the hardware to perform this transfer */ + + regaddr = STM32_OTG_DOEPTSIZ(privep->epphy); + regval = stm32_getreg(regaddr); + regval &= ~(OTG_DOEPTSIZ_XFRSIZ_MASK | OTG_DOEPTSIZ_PKTCNT_MASK); + regval |= (xfrsize << OTG_DOEPTSIZ_XFRSIZ_SHIFT); + regval |= (pktcnt << OTG_DOEPTSIZ_PKTCNT_SHIFT); + stm32_putreg(regval, regaddr); + + /* Then enable the transfer */ + + regaddr = STM32_OTG_DOEPCTL(privep->epphy); + regval = stm32_getreg(regaddr); + + /* When an isochronous transfer is enabled the Even/Odd frame bit must + * also be set appropriately. + */ + +#ifdef CONFIG_USBDEV_ISOCHRONOUS + if (privep->eptype == USB_EP_ATTR_XFER_ISOC) + { + if (privep->odd) + { + regval |= OTG_DOEPCTL_SODDFRM; + } + else + { + regval |= OTG_DOEPCTL_SEVNFRM; + } + } +#endif + + /* Clearing NAKing and enable the transfer. */ + + regval |= (OTG_DOEPCTL_CNAK | OTG_DOEPCTL_EPENA); + stm32_putreg(regval, regaddr); + + /* A transfer is now active on this endpoint */ + + privep->active = true; + + /* EP0 is a special case. We need to know when to switch back to + * normal SETUP processing. + */ + + if (privep->epphy == EP0) + { + priv->ep0state = EP0STATE_DATA_OUT; + } + } +} + +/**************************************************************************** + * Name: stm32_ep_flush + * + * Description: + * Flush any primed descriptors from this ep + * + ****************************************************************************/ + +static void stm32_ep_flush(struct stm32_ep_s *privep) +{ + if (privep->isin) + { + stm32_txfifo_flush(OTG_GRSTCTL_TXFNUM_D(privep->epphy)); + } + else + { + stm32_rxfifo_flush(); + } +} + +/**************************************************************************** + * Name: stm32_req_complete + * + * Description: + * Handle termination of the request at the head of the endpoint request queue. + * + ****************************************************************************/ + +static void stm32_req_complete(struct stm32_ep_s *privep, int16_t result) +{ + FAR struct stm32_req_s *privreq; + + /* Remove the request at the head of the request list */ + + privreq = stm32_req_remfirst(privep); + DEBUGASSERT(privreq != NULL); + + /* If endpoint 0, temporarily reflect the state of protocol stalled + * in the callback. + */ + + bool stalled = privep->stalled; + if (privep->epphy == EP0) + { + privep->stalled = privep->dev->stalled; + } + + /* Save the result in the request structure */ + + privreq->req.result = result; + + /* Callback to the request completion handler */ + + privreq->req.callback(&privep->ep, &privreq->req); + + /* Restore the stalled indication */ + + privep->stalled = stalled; +} + +/**************************************************************************** + * Name: stm32_req_cancel + * + * Description: + * Cancel all pending requests for an endpoint + * + ****************************************************************************/ + +static void stm32_req_cancel(struct stm32_ep_s *privep, int16_t status) +{ + if (!stm32_rqempty(privep)) + { + stm32_ep_flush(privep); + } + + while (!stm32_rqempty(privep)) + { + usbtrace(TRACE_COMPLETE(privep->epphy), + (stm32_rqpeek(privep))->req.xfrd); + stm32_req_complete(privep, status); + } +} + +/**************************************************************************** + * Name: stm32_ep_findbyaddr + * + * Description: + * Find the physical endpoint structure corresponding to a logic endpoint + * address + * + ****************************************************************************/ + +static struct stm32_ep_s *stm32_ep_findbyaddr(struct stm32_usbdev_s *priv, + uint16_t eplog) +{ + struct stm32_ep_s *privep; + uint8_t epphy = USB_EPNO(eplog); + + if (epphy >= STM32_NENDPOINTS) + { + return NULL; + } + + /* Is this an IN or an OUT endpoint? */ + + if (USB_ISEPIN(eplog)) + { + privep = &priv->epin[epphy]; + } + else + { + privep = &priv->epout[epphy]; + } + + /* Return endpoint reference */ + + DEBUGASSERT(privep->epphy == epphy); + return privep; +} + +/**************************************************************************** + * Name: stm32_req_dispatch + * + * Description: + * Provide unhandled setup actions to the class driver. This is logically part + * of the USB interrupt handler. + * + ****************************************************************************/ + +static int stm32_req_dispatch(struct stm32_usbdev_s *priv, + const struct usb_ctrlreq_s *ctrl) +{ + int ret = -EIO; + + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_DISPATCH), 0); + if (priv->driver) + { + /* Forward to the control request to the class driver implementation */ + + ret = CLASS_SETUP(priv->driver, &priv->usbdev, ctrl, + priv->ep0data, priv->ep0datlen); + } + + if (ret < 0) + { + /* Stall on failure */ + + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_DISPATCHSTALL), 0); + priv->stalled = true; + } + + return ret; +} + +/**************************************************************************** + * Name: stm32_usbreset + * + * Description: + * Reset Usb engine + * + ****************************************************************************/ + +static void stm32_usbreset(struct stm32_usbdev_s *priv) +{ + FAR struct stm32_ep_s *privep; + uint32_t regval; + int i; + + /* Clear the Remote Wake-up Signaling */ + + regval = stm32_getreg(STM32_OTG_DCTL); + regval &= ~OTG_DCTL_RWUSIG; + stm32_putreg(regval, STM32_OTG_DCTL); + + /* Flush the EP0 Tx FIFO */ + + stm32_txfifo_flush(OTG_GRSTCTL_TXFNUM_D(EP0)); + + /* Tell the class driver that we are disconnected. The class + * driver should then accept any new configurations. + */ + + if (priv->driver) + { + CLASS_DISCONNECT(priv->driver, &priv->usbdev); + } + + /* Mark all endpoints as available */ + + priv->epavail[0] = STM32_EP_AVAILABLE; + priv->epavail[1] = STM32_EP_AVAILABLE; + + /* Disable all end point interrupts */ + + for (i = 0; i < STM32_NENDPOINTS ; i++) + { + /* Disable endpoint interrupts */ + + stm32_putreg(0xff, STM32_OTG_DIEPINT(i)); + stm32_putreg(0xff, STM32_OTG_DOEPINT(i)); + + /* Return write requests to the class implementation */ + + privep = &priv->epin[i]; + stm32_req_cancel(privep, -ESHUTDOWN); + + /* Reset IN endpoint status */ + + privep->stalled = false; + + /* Return read requests to the class implementation */ + + privep = &priv->epout[i]; + stm32_req_cancel(privep, -ESHUTDOWN); + + /* Reset endpoint status */ + + privep->stalled = false; + } + + stm32_putreg(0xffffffff, STM32_OTG_DAINT); + + /* Mask all device endpoint interrupts except EP0 */ + + regval = (OTG_DAINT_IEP(EP0) | OTG_DAINT_OEP(EP0)); + stm32_putreg(regval, STM32_OTG_DAINTMSK); + + /* Unmask OUT interrupts */ + + regval = (OTG_DOEPMSK_XFRCM | OTG_DOEPMSK_STUPM | OTG_DOEPMSK_EPDM); + stm32_putreg(regval, STM32_OTG_DOEPMSK); + + /* Unmask IN interrupts */ + + regval = (OTG_DIEPMSK_XFRCM | OTG_DIEPMSK_EPDM | OTG_DIEPMSK_TOM); + stm32_putreg(regval, STM32_OTG_DIEPMSK); + + /* Reset device address to 0 */ + + stm32_setaddress(priv, 0); + priv->devstate = DEVSTATE_DEFAULT; + priv->usbdev.speed = USB_SPEED_FULL; + + /* Re-configure EP0 */ + + stm32_ep0_configure(priv); + + /* Setup EP0 to receive SETUP packets */ + + stm32_ep0out_ctrlsetup(priv); +} + +/**************************************************************************** + * Name: stm32_ep0out_testmode + * + * Description: + * Select test mode + * + ****************************************************************************/ + +static inline void stm32_ep0out_testmode(FAR struct stm32_usbdev_s *priv, + uint16_t index) +{ + uint8_t testmode; + + testmode = index >> 8; + switch (testmode) + { + case 1: + priv->testmode = OTG_TESTMODE_J; + break; + + case 2: + priv->testmode = OTG_TESTMODE_K; + break; + + case 3: + priv->testmode = OTG_TESTMODE_SE0_NAK; + break; + + case 4: + priv->testmode = OTG_TESTMODE_PACKET; + break; + + case 5: + priv->testmode = OTG_TESTMODE_FORCE; + break; + + default: + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADTESTMODE), testmode); + priv->dotest = false; + priv->testmode = OTG_TESTMODE_DISABLED; + priv->stalled = true; + } + + priv->dotest = true; + stm32_ep0in_transmitzlp(priv); +} + +/**************************************************************************** + * Name: stm32_ep0out_stdrequest + * + * Description: + * Handle a stanard request on EP0. Pick off the things of interest to the + * USB device controller driver; pass what is left to the class driver. + * + ****************************************************************************/ + +static inline void stm32_ep0out_stdrequest(struct stm32_usbdev_s *priv, + FAR struct stm32_ctrlreq_s *ctrlreq) +{ + FAR struct stm32_ep_s *privep; + + /* Handle standard request */ + + switch (ctrlreq->req) + { + case USB_REQ_GETSTATUS: + { + /* type: device-to-host; recipient = device, interface, endpoint + * value: 0 + * index: zero interface endpoint + * len: 2; data = status + */ + + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_GETSTATUS), 0); + if (!priv->addressed || + ctrlreq->len != 2 || + USB_REQ_ISOUT(ctrlreq->type) || + ctrlreq->value != 0) + { + priv->stalled = true; + } + else + { + switch (ctrlreq->type & USB_REQ_RECIPIENT_MASK) + { + case USB_REQ_RECIPIENT_ENDPOINT: + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPGETSTATUS), 0); + privep = stm32_ep_findbyaddr(priv, ctrlreq->index); + if (!privep) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADEPGETSTATUS), 0); + priv->stalled = true; + } + else + { + if (privep->stalled) + { + priv->ep0data[0] = (1 << USB_FEATURE_ENDPOINTHALT); + } + else + { + priv->ep0data[0] = 0; /* Not stalled */ + } + + priv->ep0data[1] = 0; + stm32_ep0in_setupresponse(priv, priv->ep0data, 2); + } + } + break; + + case USB_REQ_RECIPIENT_DEVICE: + { + if (ctrlreq->index == 0) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_DEVGETSTATUS), 0); + + /* Features: Remote Wakeup and self-powered */ + + priv->ep0data[0] = (priv->selfpowered << USB_FEATURE_SELFPOWERED); + priv->ep0data[0] |= (priv->wakeup << USB_FEATURE_REMOTEWAKEUP); + priv->ep0data[1] = 0; + + stm32_ep0in_setupresponse(priv, priv->ep0data, 2); + } + else + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADDEVGETSTATUS), 0); + priv->stalled = true; + } + } + break; + + case USB_REQ_RECIPIENT_INTERFACE: + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_IFGETSTATUS), 0); + priv->ep0data[0] = 0; + priv->ep0data[1] = 0; + + stm32_ep0in_setupresponse(priv, priv->ep0data, 2); + } + break; + + default: + { + usbtrace(TRACE_DEVERROR(STM32_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(STM32_TRACEINTID_CLEARFEATURE), 0); + if (priv->addressed != 0 && ctrlreq->len == 0) + { + uint8_t recipient = ctrlreq->type & USB_REQ_RECIPIENT_MASK; + if (recipient == USB_REQ_RECIPIENT_ENDPOINT && + ctrlreq->value == USB_FEATURE_ENDPOINTHALT && + (privep = stm32_ep_findbyaddr(priv, ctrlreq->index)) != NULL) + { + stm32_ep_clrstall(privep); + stm32_ep0in_transmitzlp(priv); + } + else if (recipient == USB_REQ_RECIPIENT_DEVICE && + ctrlreq->value == USB_FEATURE_REMOTEWAKEUP) + { + priv->wakeup = 0; + stm32_ep0in_transmitzlp(priv); + } + else + { + /* Actually, I think we could just stall here. */ + + (void)stm32_req_dispatch(priv, &priv->ctrlreq); + } + } + else + { + usbtrace(TRACE_DEVERROR(STM32_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(STM32_TRACEINTID_SETFEATURE), 0); + if (priv->addressed != 0 && ctrlreq->len == 0) + { + uint8_t recipient = ctrlreq->type & USB_REQ_RECIPIENT_MASK; + if (recipient == USB_REQ_RECIPIENT_ENDPOINT && + ctrlreq->value == USB_FEATURE_ENDPOINTHALT && + (privep = stm32_ep_findbyaddr(priv, ctrlreq->index)) != NULL) + { + stm32_ep_setstall(privep); + stm32_ep0in_transmitzlp(priv); + } + else if (recipient == USB_REQ_RECIPIENT_DEVICE && + ctrlreq->value == USB_FEATURE_REMOTEWAKEUP) + { + priv->wakeup = 1; + stm32_ep0in_transmitzlp(priv); + } + else if (recipient == USB_REQ_RECIPIENT_DEVICE && + ctrlreq->value == USB_FEATURE_TESTMODE && + ((ctrlreq->index & 0xff) == 0)) + { + stm32_ep0out_testmode(priv, ctrlreq->index); + } + else if (priv->configured) + { + /* Actually, I think we could just stall here. */ + + (void)stm32_req_dispatch(priv, &priv->ctrlreq); + } + else + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADSETFEATURE), 0); + priv->stalled = true; + } + } + else + { + usbtrace(TRACE_DEVERROR(STM32_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(STM32_TRACEINTID_SETADDRESS), ctrlreq->value); + if ((ctrlreq->type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE && + ctrlreq->index == 0 && + ctrlreq->len == 0 && + ctrlreq->value < 128 && + priv->devstate != DEVSTATE_CONFIGURED) + { + /* Save the address. We cannot actually change to the next address until + * the completion of the status phase. + */ + + stm32_setaddress(priv, (uint16_t)priv->ctrlreq.value[0]); + stm32_ep0in_transmitzlp(priv); + } + else + { + usbtrace(TRACE_DEVERROR(STM32_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(STM32_TRACEINTID_GETSETDESC), 0); + if ((ctrlreq->type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE) + { + (void)stm32_req_dispatch(priv, &priv->ctrlreq); + } + else + { + usbtrace(TRACE_DEVERROR(STM32_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(STM32_TRACEINTID_GETCONFIG), 0); + if (priv->addressed && + (ctrlreq->type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE && + ctrlreq->value == 0 && + ctrlreq->index == 0 && + ctrlreq->len == 1) + { + (void)stm32_req_dispatch(priv, &priv->ctrlreq); + } + else + { + usbtrace(TRACE_DEVERROR(STM32_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(STM32_TRACEINTID_SETCONFIG), 0); + if (priv->addressed && + (ctrlreq->type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE && + ctrlreq->index == 0 && + ctrlreq->len == 0) + { + /* Give the configuration to the class driver */ + + int ret = stm32_req_dispatch(priv, &priv->ctrlreq); + + /* If the class driver accepted the configuration, then mark the + * device state as configured (or not, depending on the + * configuration). + */ + + if (ret == OK) + { + uint8_t cfg = (uint8_t)ctrlreq->value; + if (cfg != 0) + { + priv->devstate = DEVSTATE_CONFIGURED; + priv->configured = true; + } + else + { + priv->devstate = DEVSTATE_ADDRESSED; + priv->configured = false; + } + } + } + else + { + usbtrace(TRACE_DEVERROR(STM32_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(STM32_TRACEINTID_GETSETIF), 0); + (void)stm32_req_dispatch(priv, &priv->ctrlreq); + } + break; + + case USB_REQ_SYNCHFRAME: + /* type: device-to-host; recipient = endpoint + * value: 0 + * index: endpoint; + * len: 2; data = frame number + */ + + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SYNCHFRAME), 0); + } + break; + + default: + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDCTRLREQ), 0); + priv->stalled = true; + } + break; + } +} + +/**************************************************************************** + * Name: stm32_ep0out_setup + * + * Description: + * USB Ctrl EP Setup Event. This is logically part of the USB interrupt + * handler. This event occurs when a setup packet is receive on EP0 OUT. + * + ****************************************************************************/ + +static inline void stm32_ep0out_setup(struct stm32_usbdev_s *priv) +{ + struct stm32_ctrlreq_s ctrlreq; + + /* Verify that a SETUP was received */ + + if (priv->ep0state != EP0STATE_SETUP_READY) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EP0NOSETUP), priv->ep0state); + return; + } + + /* Terminate any pending requests */ + + stm32_req_cancel(&priv->epout[EP0], -EPROTO); + stm32_req_cancel(&priv->epin[EP0], -EPROTO); + + /* Assume NOT stalled */ + + priv->epout[EP0].stalled = false; + priv->epin[EP0].stalled = false; + priv->stalled = false; + + /* Starting to process a control request - update state */ + + priv->ep0state = EP0STATE_SETUP_PROCESS; + + /* And extract the little-endian 16-bit values to host order */ + + ctrlreq.type = priv->ctrlreq.type; + ctrlreq.req = priv->ctrlreq.req; + ctrlreq.value = GETUINT16(priv->ctrlreq.value); + ctrlreq.index = GETUINT16(priv->ctrlreq.index); + ctrlreq.len = GETUINT16(priv->ctrlreq.len); + + ullinfo("type=%02x req=%02x value=%04x index=%04x len=%04x\n", + ctrlreq.type, ctrlreq.req, ctrlreq.value, ctrlreq.index, ctrlreq.len); + + /* Check for a standard request */ + + if ((ctrlreq.type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD) + { + /* Dispatch any non-standard requests */ + + (void)stm32_req_dispatch(priv, &priv->ctrlreq); + } + else + { + /* Handle standard requests. */ + + stm32_ep0out_stdrequest(priv, &ctrlreq); + } + + /* Check if the setup processing resulted in a STALL */ + + if (priv->stalled) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EP0SETUPSTALLED), priv->ep0state); + stm32_ep0_stall(priv); + } + + /* Reset state/data associated with thie SETUP request */ + + priv->ep0datlen = 0; +} + +/**************************************************************************** + * Name: stm32_epout + * + * Description: + * This is part of the OUT endpoint interrupt processing. This function + * handles the OUT event for a single endpoint. + * + ****************************************************************************/ + +static inline void stm32_epout(FAR struct stm32_usbdev_s *priv, uint8_t epno) +{ + FAR struct stm32_ep_s *privep; + + /* Endpoint 0 is a special case. */ + + if (epno == 0) + { + privep = &priv->epout[EP0]; + + /* In the EP0STATE_DATA_OUT state, we are receiving data into the + * request buffer. In that case, we must continue the request + * processing. + */ + + if (priv->ep0state == EP0STATE_DATA_OUT) + { + /* Continue processing data from the EP0 OUT request queue */ + + stm32_epout_complete(priv, privep); + + /* If we are not actively processing an OUT request, then we + * need to setup to receive the next control request. + */ + + if (!privep->active) + { + stm32_ep0out_ctrlsetup(priv); + priv->ep0state = EP0STATE_IDLE; + } + } + } + + /* For other endpoints, the only possibility is that we are continuing + * or finishing an OUT request. + */ + + else if (priv->devstate == DEVSTATE_CONFIGURED) + { + stm32_epout_complete(priv, &priv->epout[epno]); + } +} + +/**************************************************************************** + * Name: stm32_epout_interrupt + * + * Description: + * USB OUT endpoint interrupt handler. The core generates this interrupt when + * there is an interrupt is pending on one of the OUT endpoints of the core. + * The driver must read the OTG DAINT register to determine the exact number + * of the OUT endpoint on which the interrupt occurred, and then read the + * corresponding OTG DOEPINTx register to determine the exact cause of the + * interrupt. + * + ****************************************************************************/ + +static inline void stm32_epout_interrupt(FAR struct stm32_usbdev_s *priv) +{ + uint32_t daint; + uint32_t regval; + uint32_t doepint; + int epno; + + /* Get the pending, enabled interrupts for the OUT endpoint from the endpoint + * interrupt status register. + */ + + regval = stm32_getreg(STM32_OTG_DAINT); + regval &= stm32_getreg(STM32_OTG_DAINTMSK); + daint = (regval & OTG_DAINT_OEP_MASK) >> OTG_DAINT_OEP_SHIFT; + + if (daint == 0) + { + /* We got an interrupt, but there is no unmasked endpoint that caused + * it ?! When this happens, the interrupt flag never gets cleared and + * we are stuck in infinite interrupt loop. + * + * This shouldn't happen if we are diligent about handling timing + * issues when masking endpoint interrupts. However, this workaround + * avoids infinite loop and allows operation to continue normally. It + * works by clearing each endpoint flags, masked or not. + */ + + regval = stm32_getreg(STM32_OTG_DAINT); + daint = (regval & OTG_DAINT_OEP_MASK) >> OTG_DAINT_OEP_SHIFT; + + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPOUTUNEXPECTED), + (uint16_t)regval); + + epno = 0; + while (daint) + { + if ((daint & 1) != 0) + { + regval = stm32_getreg(STM32_OTG_DOEPINT(epno)); + ullerr("DOEPINT(%d) = %08x\n", epno, regval); + stm32_putreg(0xFF, STM32_OTG_DOEPINT(epno)); + } + + epno++; + daint >>= 1; + } + + return; + } + + /* Process each pending IN endpoint interrupt */ + + epno = 0; + while (daint) + { + /* Is an OUT interrupt pending for this endpoint? */ + + if ((daint & 1) != 0) + { + /* Yes.. get the OUT endpoint interrupt status */ + + doepint = stm32_getreg(STM32_OTG_DOEPINT(epno)); + doepint &= stm32_getreg(STM32_OTG_DOEPMSK); + + /* Transfer completed interrupt. This interrupt is trigged when + * stm32_rxinterrupt() removes the last packet data from the RxFIFO. + * In this case, core internally sets the NAK bit for this endpoint to + * prevent it from receiving any more packets. + */ + + if ((doepint & OTG_DOEPINT_XFRC) != 0) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUT_XFRC), (uint16_t)doepint); + + /* Clear the bit in DOEPINTn for this interrupt */ + + stm32_putreg(OTG_DOEPINT_XFRC, STM32_OTG_DOEPINT(epno)); + + /* Handle the RX transfer data ready event */ + + stm32_epout(priv, epno); + } + + /* Endpoint disabled interrupt (ignored because this interrupt is + * used in polled mode by the endpoint disable logic). + */ +#if 1 + /* REVISIT: */ + if ((doepint & OTG_DOEPINT_EPDISD) != 0) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUT_EPDISD), (uint16_t)doepint); + + /* Clear the bit in DOEPINTn for this interrupt */ + + stm32_putreg(OTG_DOEPINT_EPDISD, STM32_OTG_DOEPINT(epno)); + } +#endif + /* Setup Phase Done (control EPs) */ + + if ((doepint & OTG_DOEPINT_SETUP) != 0) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUT_SETUP), priv->ep0state); + + /* Handle the receipt of the IN SETUP packets now (OUT setup + * packet processing may be delayed until the accompanying + * OUT DATA is received) + */ + + if (priv->ep0state == EP0STATE_SETUP_READY) + { + stm32_ep0out_setup(priv); + } + stm32_putreg(OTG_DOEPINT_SETUP, STM32_OTG_DOEPINT(epno)); + } + } + + epno++; + daint >>= 1; + } +} + +/**************************************************************************** + * Name: stm32_epin_runtestmode + * + * Description: + * Execute the test mode setup by the SET FEATURE request + * + ****************************************************************************/ + +static inline void stm32_epin_runtestmode(FAR struct stm32_usbdev_s *priv) +{ + uint32_t regval = stm32_getreg(STM32_OTG_DCTL); + regval &= OTG_DCTL_TCTL_MASK; + regval |= (uint32_t)priv->testmode << OTG_DCTL_TCTL_SHIFT; + stm32_putreg(regval , STM32_OTG_DCTL); + + priv->dotest = 0; + priv->testmode = OTG_TESTMODE_DISABLED; +} + +/**************************************************************************** + * Name: stm32_epin + * + * Description: + * This is part of the IN endpoint interrupt processing. This function + * handles the IN event for a single endpoint. + * + ****************************************************************************/ + +static inline void stm32_epin(FAR struct stm32_usbdev_s *priv, uint8_t epno) +{ + FAR struct stm32_ep_s *privep = &priv->epin[epno]; + + /* Endpoint 0 is a special case. */ + + if (epno == 0) + { + /* In the EP0STATE_DATA_IN state, we are sending data from request + * buffer. In that case, we must continue the request processing. + */ + + if (priv->ep0state == EP0STATE_DATA_IN) + { + /* Continue processing data from the EP0 OUT request queue */ + + stm32_epin_request(priv, privep); + + /* If we are not actively processing an OUT request, then we + * need to setup to receive the next control request. + */ + + if (!privep->active) + { + stm32_ep0out_ctrlsetup(priv); + priv->ep0state = EP0STATE_IDLE; + } + } + + /* Test mode is another special case */ + + if (priv->dotest) + { + stm32_epin_runtestmode(priv); + } + } + + /* For other endpoints, the only possibility is that we are continuing + * or finishing an IN request. + */ + + else if (priv->devstate == DEVSTATE_CONFIGURED) + { + /* Continue processing data from the endpoint write request queue */ + + stm32_epin_request(priv, privep); + } +} + +/**************************************************************************** + * Name: stm32_epin_txfifoempty + * + * Description: + * TxFIFO empty interrupt handling + * + ****************************************************************************/ + +static inline void stm32_epin_txfifoempty(FAR struct stm32_usbdev_s *priv, int epno) +{ + FAR struct stm32_ep_s *privep = &priv->epin[epno]; + + /* Continue processing the write request queue. This may mean sending + * more data from the existing request or terminating the current requests + * and (perhaps) starting the IN transfer from the next write request. + */ + + stm32_epin_request(priv, privep); +} + +/**************************************************************************** + * Name: stm32_epin_interrupt + * + * Description: + * USB IN endpoint interrupt handler. The core generates this interrupt when + * an interrupt is pending on one of the IN endpoints of the core. The driver + * must read the OTG DAINT register to determine the exact number of the IN + * endpoint on which the interrupt occurred, and then read the corresponding + * OTG DIEPINTx register to determine the exact cause of the interrupt. + * + ****************************************************************************/ + +static inline void stm32_epin_interrupt(FAR struct stm32_usbdev_s *priv) +{ + uint32_t diepint; + uint32_t daint; + uint32_t mask; + uint32_t empty; + int epno; + + /* Get the pending, enabled interrupts for the IN endpoint from the endpoint + * interrupt status register. + */ + + daint = stm32_getreg(STM32_OTG_DAINT); + daint &= stm32_getreg(STM32_OTG_DAINTMSK); + daint &= OTG_DAINT_IEP_MASK; + + if (daint == 0) + { + /* We got an interrupt, but there is no unmasked endpoint that caused + * it ?! When this happens, the interrupt flag never gets cleared and + * we are stuck in infinite interrupt loop. + * + * This shouldn't happen if we are diligent about handling timing + * issues when masking endpoint interrupts. However, this workaround + * avoids infinite loop and allows operation to continue normally. It + * works by clearing each endpoint flags, masked or not. + */ + + daint = stm32_getreg(STM32_OTG_DAINT); + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPINUNEXPECTED), + (uint16_t)daint); + + daint &= OTG_DAINT_IEP_MASK; + epno = 0; + + while (daint) + { + if ((daint & 1) != 0) + { + ullerr("DIEPINT(%d) = %08x\n", + epno, stm32_getreg(STM32_OTG_DIEPINT(epno))); + stm32_putreg(0xFF, STM32_OTG_DIEPINT(epno)); + } + + epno++; + daint >>= 1; + } + + return; + } + + /* Process each pending IN endpoint interrupt */ + + epno = 0; + while (daint) + { + /* Is an IN interrupt pending for this endpoint? */ + + if ((daint & 1) != 0) + { + /* Get IN interrupt mask register. Bits 0-6 correspond to enabled + * interrupts as will be found in the DIEPINT interrupt status + * register. + */ + + mask = stm32_getreg(STM32_OTG_DIEPMSK); + + /* Check if the TxFIFO not empty interrupt is enabled for this + * endpoint in the DIEPMSK register. Bits n corresponds to + * endpoint n in the register. That condition corresponds to + * bit 7 of the DIEPINT interrupt status register. There is + * no TXFE bit in the mask register, so we fake one here. + */ + + empty = stm32_getreg(STM32_OTG_DIEPEMPMSK); + if ((empty & OTG_DIEPEMPMSK(epno)) != 0) + { + mask |= OTG_DIEPINT_TXFE; + } + + /* Now, read the interrupt status and mask out all disabled + * interrupts. + */ + + diepint = stm32_getreg(STM32_OTG_DIEPINT(epno)) & mask; + + /* Decode and process the enabled, pending interrupts */ + /* Transfer completed interrupt */ + + if ((diepint & OTG_DIEPINT_XFRC) != 0) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN_XFRC), + (uint16_t)diepint); + + /* It is possible that logic may be waiting for a the + * TxFIFO to become empty. We disable the TxFIFO empty + * interrupt here; it will be re-enabled if there is still + * insufficient space in the TxFIFO. + */ + + empty &= ~OTG_DIEPEMPMSK(epno); + stm32_putreg(empty, STM32_OTG_DIEPEMPMSK); + stm32_putreg(OTG_DIEPINT_XFRC, STM32_OTG_DIEPINT(epno)); + + /* IN transfer complete */ + + stm32_epin(priv, epno); + } + + /* Timeout condition */ + + if ((diepint & OTG_DIEPINT_TOC) != 0) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN_TOC), (uint16_t)diepint); + stm32_putreg(OTG_DIEPINT_TOC, STM32_OTG_DIEPINT(epno)); + } + + /* IN token received when TxFIFO is empty. Applies to non-periodic IN + * endpoints only. This interrupt indicates that an IN token was received + * when the associated TxFIFO (periodic/non-periodic) was empty. This + * interrupt is asserted on the endpoint for which the IN token was + * received. + */ + + if ((diepint & OTG_DIEPINT_ITTXFE) != 0) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN_ITTXFE), (uint16_t)diepint); + stm32_epin_request(priv, &priv->epin[epno]); + stm32_putreg(OTG_DIEPINT_ITTXFE, STM32_OTG_DIEPINT(epno)); + } + + /* IN endpoint NAK effective (ignored as this used only in polled + * mode) + */ +#if 0 + if ((diepint & OTG_DIEPINT_INEPNE) != 0) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN_INEPNE), (uint16_t)diepint); + stm32_putreg(OTG_DIEPINT_INEPNE, STM32_OTG_DIEPINT(epno)); + } +#endif + /* Endpoint disabled interrupt (ignored as this used only in polled + * mode) + */ +#if 0 + if ((diepint & OTG_DIEPINT_EPDISD) != 0) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN_EPDISD), (uint16_t)diepint); + stm32_putreg(OTG_DIEPINT_EPDISD, STM32_OTG_DIEPINT(epno)); + } +#endif + /* Transmit FIFO empty */ + + if ((diepint & OTG_DIEPINT_TXFE) != 0) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN_TXFE), (uint16_t)diepint); + + /* If we were waiting for TxFIFO to become empty, the we might have both + * XFRC and TXFE interrupts pending. Since we do the same thing for both + * cases, ignore the TXFE if we have already processed the XFRC. + */ + + if ((diepint & OTG_DIEPINT_XFRC) == 0) + { + /* Mask further FIFO empty interrupts. This will be re-enabled + * whenever we need to wait for a FIFO event. + */ + + empty &= ~OTG_DIEPEMPMSK(epno); + stm32_putreg(empty, STM32_OTG_DIEPEMPMSK); + + /* Handle TxFIFO empty */ + + stm32_epin_txfifoempty(priv, epno); + } + + /* Clear the pending TxFIFO empty interrupt */ + + stm32_putreg(OTG_DIEPINT_TXFE, STM32_OTG_DIEPINT(epno)); + } + } + + epno++; + daint >>= 1; + } +} + +/**************************************************************************** + * Name: stm32_resumeinterrupt + * + * Description: + * Resume/remote wakeup detected interrupt + * + ****************************************************************************/ + +static inline void stm32_resumeinterrupt(FAR struct stm32_usbdev_s *priv) +{ + uint32_t regval; + + /* Restart the PHY clock and un-gate USB core clock (HCLK) */ + +#ifdef CONFIG_USBDEV_LOWPOWER + regval = stm32_getreg(STM32_OTG_PCGCCTL); + regval &= ~(OTG_PCGCCTL_STPPCLK | OTG_PCGCCTL_GATEHCLK); + stm32_putreg(regval, STM32_OTG_PCGCCTL); +#endif + + /* Clear remote wake-up signaling */ + + regval = stm32_getreg(STM32_OTG_DCTL); + regval &= ~OTG_DCTL_RWUSIG; + stm32_putreg(regval, STM32_OTG_DCTL); + + /* Restore full power -- whatever that means for this particular board */ + + stm32_usbsuspend((struct usbdev_s *)priv, true); + + /* Notify the class driver of the resume event */ + + if (priv->driver) + { + CLASS_RESUME(priv->driver, &priv->usbdev); + } +} + +/**************************************************************************** + * Name: stm32_suspendinterrupt + * + * Description: + * USB suspend interrupt + * + ****************************************************************************/ + +static inline void stm32_suspendinterrupt(FAR struct stm32_usbdev_s *priv) +{ +#ifdef CONFIG_USBDEV_LOWPOWER + uint32_t regval; +#endif + + /* Notify the class driver of the suspend event */ + + if (priv->driver) + { + CLASS_SUSPEND(priv->driver, &priv->usbdev); + } + +#ifdef CONFIG_USBDEV_LOWPOWER + /* OTG_DSTS_SUSPSTS is set as long as the suspend condition is detected + * on USB. Check if we are still have the suspend condition, that we are + * connected to the host, and that we have been configured. + */ + + regval = stm32_getreg(STM32_OTG_DSTS); + + if ((regval & OTG_DSTS_SUSPSTS) != 0 && devstate == DEVSTATE_CONFIGURED) + { + /* Switch off OTG clocking. Setting OTG_PCGCCTL_STPPCLK stops the + * PHY clock. + */ + + regval = stm32_getreg(STM32_OTG_PCGCCTL); + regval |= OTG_PCGCCTL_STPPCLK; + stm32_putreg(regval, STM32_OTG_PCGCCTL); + + /* Setting OTG_PCGCCTL_GATEHCLK gate HCLK to modules other than + * the AHB Slave and Master and wakeup logic. + */ + + regval |= OTG_PCGCCTL_GATEHCLK; + stm32_putreg(regval, STM32_OTG_PCGCCTL); + } +#endif + + /* Let the board-specific logic know that we have entered the suspend + * state + */ + + stm32_usbsuspend((FAR struct usbdev_s *)priv, false); +} + +/**************************************************************************** + * Name: stm32_rxinterrupt + * + * Description: + * RxFIFO non-empty interrupt. This interrupt indicates that there is at + * least one packet pending to be read from the RxFIFO. + * + ****************************************************************************/ + +static inline void stm32_rxinterrupt(FAR struct stm32_usbdev_s *priv) +{ + FAR struct stm32_ep_s *privep; + uint32_t regval; + int bcnt; + int epphy; + + /* Disable the Rx status queue level interrupt */ + + regval = stm32_getreg(STM32_OTG_GINTMSK); + regval &= ~OTG_GINT_RXFLVL; + stm32_putreg(regval, STM32_OTG_GINTMSK); + + /* Get the status from the top of the FIFO */ + + regval = stm32_getreg(STM32_OTG_GRXSTSP); + + /* Decode status fields */ + + epphy = (regval & OTG_GRXSTSD_EPNUM_MASK) >> OTG_GRXSTSD_EPNUM_SHIFT; + + if (epphy < STM32_NENDPOINTS) + { + privep = &priv->epout[epphy]; + + /* Handle the RX event according to the packet status field */ + + switch (regval & OTG_GRXSTSD_PKTSTS_MASK) + { + /* Global OUT NAK. This indicate that the global OUT NAK bit has taken + * effect. + * + * PKTSTS = Global OUT NAK, BCNT = 0, EPNUM = Don't Care, DPID = Don't + * Care. + */ + + case OTG_GRXSTSD_PKTSTS_OUTNAK: + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_OUTNAK), 0); + } + break; + + /* OUT data packet received. + * + * PKTSTS = DataOUT, BCNT = size of the received data OUT packet, + * EPNUM = EPNUM on which the packet was received, DPID = Actual Data PID. + */ + + case OTG_GRXSTSD_PKTSTS_OUTRECVD: + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_OUTRECVD), epphy); + bcnt = (regval & OTG_GRXSTSD_BCNT_MASK) >> OTG_GRXSTSD_BCNT_SHIFT; + if (bcnt > 0) + { + stm32_epout_receive(privep, bcnt); + } + } + break; + + /* OUT transfer completed. This indicates that an OUT data transfer for + * the specified OUT endpoint has completed. After this entry is popped + * from the receive FIFO, the core asserts a Transfer Completed interrupt + * on the specified OUT endpoint. + * + * PKTSTS = Data OUT Transfer Done, BCNT = 0, EPNUM = OUT EP Num on + * which the data transfer is complete, DPID = Don't Care. + */ + + case OTG_GRXSTSD_PKTSTS_OUTDONE: + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_OUTDONE), epphy); + } + break; + + /* SETUP transaction completed. This indicates that the Setup stage for + * the specified endpoint has completed and the Data stage has started. + * After this entry is popped from the receive FIFO, the core asserts a + * Setup interrupt on the specified control OUT endpoint (triggers an + * interrupt). + * + * PKTSTS = Setup Stage Done, BCNT = 0, EPNUM = Control EP Num, + * DPID = Don't Care. + */ + + case OTG_GRXSTSD_PKTSTS_SETUPDONE: + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SETUPDONE), epphy); + } + break; + + /* SETUP data packet received. This indicates that a SETUP packet for the + * specified endpoint is now available for reading from the receive FIFO. + * + * PKTSTS = SETUP, BCNT = 8, EPNUM = Control EP Num, DPID = D0. + */ + + case OTG_GRXSTSD_PKTSTS_SETUPRECVD: + { + uint16_t datlen; + + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SETUPRECVD), epphy); + + /* Read EP0 setup data. NOTE: If multiple SETUP packets are received, + * the last one overwrites the previous setup packets and only that + * last SETUP packet will be processed. + */ + + stm32_rxfifo_read(&priv->epout[EP0], (FAR uint8_t *)&priv->ctrlreq, + USB_SIZEOF_CTRLREQ); + + /* Was this an IN or an OUT SETUP packet. If it is an OUT SETUP, + * then we need to wait for the completion of the data phase to + * process the setup command. If it is an IN SETUP packet, then + * we must processing the command BEFORE we enter the DATA phase. + * + * If the data associated with the OUT SETUP packet is zero length, + * then, of course, we don't need to wait. + */ + + datlen = GETUINT16(priv->ctrlreq.len); + if (USB_REQ_ISOUT(priv->ctrlreq.type) && datlen > 0) + { + /* Clear NAKSTS so that we can receive the data */ + + regval = stm32_getreg(STM32_OTG_DOEPCTL0); + regval |= OTG_DOEPCTL0_CNAK; + stm32_putreg(regval, STM32_OTG_DOEPCTL0); + + /* Wait for the data phase. */ + + priv->ep0state = EP0STATE_SETUP_OUT; + } + else + { + /* We can process the setup data as soon as SETUP done word is + * popped of the RxFIFO. + */ + + priv->ep0state = EP0STATE_SETUP_READY; + } + } + break; + + default: + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), + (regval & OTG_GRXSTSD_PKTSTS_MASK) >> OTG_GRXSTSD_PKTSTS_SHIFT); + } + break; + } + } + + /* Enable the Rx Status Queue Level interrupt */ + + regval = stm32_getreg(STM32_OTG_GINTMSK); + regval |= OTG_GINT_RXFLVL; + stm32_putreg(regval, STM32_OTG_GINTMSK); +} + +/**************************************************************************** + * Name: stm32_enuminterrupt + * + * Description: + * Enumeration done interrupt + * + ****************************************************************************/ + +static inline void stm32_enuminterrupt(FAR struct stm32_usbdev_s *priv) +{ + uint32_t regval; + + /* Activate EP0 */ + + stm32_ep0in_activate(); + + /* Set USB turn-around time for the full speed device with internal PHY interface. */ + + regval = stm32_getreg(STM32_OTG_GUSBCFG); + regval &= ~OTG_GUSBCFG_TRDT_MASK; + regval |= OTG_GUSBCFG_TRDT(5); + stm32_putreg(regval, STM32_OTG_GUSBCFG); +} + +/**************************************************************************** + * Name: stm32_isocininterrupt + * + * Description: + * Incomplete isochronous IN transfer interrupt. Assertion of the incomplete + * isochronous IN transfer interrupt indicates an incomplete isochronous IN + * transfer on at least one of the isochronous IN endpoints. + * + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_ISOCHRONOUS +static inline void stm32_isocininterrupt(FAR struct stm32_usbdev_s *priv) +{ + int i; + + /* The application must read the endpoint control register for all isochronous + * IN endpoints to detect endpoints with incomplete IN data transfers. + */ + + for (i = 0; i < STM32_NENDPOINTS; i++) + { + /* Is this an isochronous IN endpoint? */ + + privep = &priv->epin[i]; + if (privep->eptype != USB_EP_ATTR_XFER_ISOC) + { + /* No... keep looking */ + + continue; + } + + /* Is there an active read request on the isochronous OUT endpoint? */ + + if (!privep->active) + { + /* No.. the endpoint is not actively transmitting data */ + + continue; + } + + /* Check if this is the endpoint that had the incomplete transfer */ + + regaddr = STM32_OTG_DIEPCTL(privep->epphy); + doepctl = stm32_getreg(regaddr); + dsts = stm32_getreg(STM32_OTG_DSTS); + + /* EONUM = 0:even frame, 1:odd frame + * SOFFN = Frame number of the received SOF + */ + + eonum = ((doepctl & OTG_DIEPCTL_EONUM) != 0); + soffn = ((dsts & OTG_DSTS_SOFFN0) != 0); + + if (eonum != soffn) + { + /* Not this endpoint */ + + continue; + } + + /* For isochronous IN endpoints with incomplete transfers, + * the application must discard the data in the memory and + * disable the endpoint. + */ + + stm32_req_complete(privep, -EIO); +#warning "Will clear OTG_DIEPCTL_USBAEP too" + stm32_epin_disable(privep); + break; + } +} +#endif + +/**************************************************************************** + * Name: stm32_isocoutinterrupt + * + * Description: + * Incomplete periodic transfer interrupt + * + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_ISOCHRONOUS +static inline void stm32_isocoutinterrupt(FAR struct stm32_usbdev_s *priv) +{ + FAR struct stm32_ep_s *privep; + FAR struct stm32_req_s *privreq; + uint32_t regaddr; + uint32_t doepctl; + uint32_t dsts; + bool eonum; + bool soffn; + + /* When it receives an IISOOXFR interrupt, the application must read the + * control registers of all isochronous OUT endpoints to determine which + * endpoints had an incomplete transfer in the current microframe. An + * endpoint transfer is incomplete if both the following conditions are true: + * + * DOEPCTLx:EONUM = DSTS:SOFFN[0], and + * DOEPCTLx:EPENA = 1 + */ + + for (i = 0; i < STM32_NENDPOINTS; i++) + { + /* Is this an isochronous OUT endpoint? */ + + privep = &priv->epout[i]; + if (privep->eptype != USB_EP_ATTR_XFER_ISOC) + { + /* No... keep looking */ + + continue; + } + + /* Is there an active read request on the isochronous OUT endpoint? */ + + if (!privep->active) + { + /* No.. the endpoint is not actively transmitting data */ + + continue; + } + + /* Check if this is the endpoint that had the incomplete transfer */ + + regaddr = STM32_OTG_DOEPCTL(privep->epphy); + doepctl = stm32_getreg(regaddr); + dsts = stm32_getreg(STM32_OTG_DSTS); + + /* EONUM = 0:even frame, 1:odd frame + * SOFFN = Frame number of the received SOF + */ + + eonum = ((doepctl & OTG_DOEPCTL_EONUM) != 0); + soffn = ((dsts & OTG_DSTS_SOFFN0) != 0); + + if (eonum != soffn) + { + /* Not this endpoint */ + + continue; + } + + /* For isochronous OUT endpoints with incomplete transfers, + * the application must discard the data in the memory and + * disable the endpoint. + */ + + stm32_req_complete(privep, -EIO); +#warning "Will clear OTG_DOEPCTL_USBAEP too" + stm32_epout_disable(privep); + break; + } +} +#endif + +/**************************************************************************** + * Name: stm32_sessioninterrupt + * + * Description: + * Session request/new session detected interrupt + * + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_VBUSSENSING +static inline void stm32_sessioninterrupt(FAR struct stm32_usbdev_s *priv) +{ +#warning "Missing logic" +} +#endif + +/**************************************************************************** + * Name: stm32_otginterrupt + * + * Description: + * OTG interrupt + * + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_VBUSSENSING +static inline void stm32_otginterrupt(FAR struct stm32_usbdev_s *priv) +{ + uint32_t regval; + + /* Check for session end detected */ + + regval = stm32_getreg(STM32_OTG_GOTGINT); + if ((regval & OTG_GOTGINT_SEDET) != 0) + { +#warning "Missing logic" + } + + /* Clear OTG interrupt */ + + stm32_putreg(retval, STM32_OTG_GOTGINT); +} +#endif + +/**************************************************************************** + * Name: stm32_usbinterrupt + * + * Description: + * USB interrupt handler + * + ****************************************************************************/ + +static int stm32_usbinterrupt(int irq, FAR void *context) +{ + /* At present, there is only a single OTG device support. Hence it is + * pre-allocated as g_otghsdev. However, in most code, the private data + * structure will be referenced using the 'priv' pointer (rather than the + * global data) in order to simplify any future support for multiple devices. + */ + + FAR struct stm32_usbdev_s *priv = &g_otghsdev; + uint32_t regval; + + usbtrace(TRACE_INTENTRY(STM32_TRACEINTID_USB), 0); + + /* Assure that we are in device mode */ + + DEBUGASSERT((stm32_getreg(STM32_OTG_GINTSTS) & OTG_GINTSTS_CMOD) == OTG_GINTSTS_DEVMODE); + + /* Get the state of all enabled interrupts. We will do this repeatedly + * some interrupts (like RXFLVL) will generate additional interrupting + * events. + */ + + for (; ; ) + { + /* Get the set of pending, un-masked interrupts */ + + regval = stm32_getreg(STM32_OTG_GINTSTS); + regval &= stm32_getreg(STM32_OTG_GINTMSK); + + /* Break out of the loop when there are no further pending (and + * unmasked) interrupts to be processes. + */ + + if (regval == 0) + { + break; + } + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_INTPENDING), (uint16_t)regval); + + /* OUT endpoint interrupt. The core sets this bit to indicate that an + * interrupt is pending on one of the OUT endpoints of the core. + */ + + if ((regval & OTG_GINT_OEP) != 0) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUT), (uint16_t)regval); + stm32_epout_interrupt(priv); + stm32_putreg(OTG_GINT_OEP, STM32_OTG_GINTSTS); + } + + /* IN endpoint interrupt. The core sets this bit to indicate that + * an interrupt is pending on one of the IN endpoints of the core. + */ + + if ((regval & OTG_GINT_IEP) != 0) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN), (uint16_t)regval); + stm32_epin_interrupt(priv); + stm32_putreg(OTG_GINT_IEP, STM32_OTG_GINTSTS); + } + + /* Host/device mode mismatch error interrupt */ + +#ifdef CONFIG_DEBUG_USB + if ((regval & OTG_GINT_MMIS) != 0) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_MISMATCH), (uint16_t)regval); + stm32_putreg(OTG_GINT_MMIS, STM32_OTG_GINTSTS); + } +#endif + + /* Resume/remote wakeup detected interrupt */ + + if ((regval & OTG_GINT_WKUP) != 0) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_WAKEUP), (uint16_t)regval); + stm32_resumeinterrupt(priv); + stm32_putreg(OTG_GINT_WKUP, STM32_OTG_GINTSTS); + } + + /* USB suspend interrupt */ + + if ((regval & OTG_GINT_USBSUSP) != 0) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SUSPEND), (uint16_t)regval); + stm32_suspendinterrupt(priv); + stm32_putreg(OTG_GINT_USBSUSP, STM32_OTG_GINTSTS); + } + + /* Start of frame interrupt */ + +#ifdef CONFIG_USBDEV_SOFINTERRUPT + if ((regval & OTG_GINT_SOF) != 0) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SOF), (uint16_t)regval); + stm32_putreg(OTG_GINT_SOF, STM32_OTG_GINTSTS); + } +#endif + + /* RxFIFO non-empty interrupt. Indicates that there is at least one + * packet pending to be read from the RxFIFO. + */ + + if ((regval & OTG_GINT_RXFLVL) != 0) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_RXFIFO), (uint16_t)regval); + stm32_rxinterrupt(priv); + stm32_putreg(OTG_GINT_RXFLVL, STM32_OTG_GINTSTS); + } + + /* USB reset interrupt */ + + if ((regval & OTG_GINT_USBRST) != 0) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_DEVRESET), (uint16_t)regval); + + /* Perform the device reset */ + + stm32_usbreset(priv); + usbtrace(TRACE_INTEXIT(STM32_TRACEINTID_USB), 0); + stm32_putreg(OTG_GINT_USBRST, STM32_OTG_GINTSTS); + return OK; + } + + /* Enumeration done interrupt */ + + if ((regval & OTG_GINT_ENUMDNE) != 0) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_ENUMDNE), (uint16_t)regval); + stm32_enuminterrupt(priv); + stm32_putreg(OTG_GINT_ENUMDNE, STM32_OTG_GINTSTS); + } + + /* Incomplete isochronous IN transfer interrupt. When the core finds + * non-empty any of the isochronous IN endpoint FIFOs scheduled for + * the current frame non-empty, the core generates an IISOIXFR + * interrupt. + */ + +#ifdef CONFIG_USBDEV_ISOCHRONOUS + if ((regval & OTG_GINT_IISOIXFR) != 0) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_IISOIXFR), (uint16_t)regval); + stm32_isocininterrupt(priv); + stm32_putreg(OTG_GINT_IISOIXFR, STM32_OTG_GINTSTS); + } + + /* Incomplete isochronous OUT transfer. For isochronous OUT + * endpoints, the XFRC interrupt may not always be asserted. If the + * core drops isochronous OUT data packets, the application could fail + * to detect the XFRC interrupt. The incomplete Isochronous OUT data + * interrupt indicates that an XFRC interrupt was not asserted on at + * least one of the isochronous OUT endpoints. At this point, the + * endpoint with the incomplete transfer remains enabled, but no active + * transfers remain in progress on this endpoint on the USB. + */ + + if ((regval & OTG_GINT_IISOOXFR) != 0) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_IISOOXFR), (uint16_t)regval); + stm32_isocoutinterrupt(priv); + stm32_putreg(OTG_GINT_IISOOXFR, STM32_OTG_GINTSTS); + } +#endif + + /* Session request/new session detected interrupt */ + +#ifdef CONFIG_USBDEV_VBUSSENSING + if ((regval & OTG_GINT_SRQ) != 0) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SRQ), (uint16_t)regval); + stm32_sessioninterrupt(priv); + stm32_putreg(OTG_GINT_SRQ, STM32_OTG_GINTSTS); + } + + /* OTG interrupt */ + + if ((regval & OTG_GINT_OTG) != 0) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_OTG), (uint16_t)regval); + stm32_otginterrupt(priv); + stm32_putreg(OTG_GINT_OTG, STM32_OTG_GINTSTS); + } +#endif + } + + usbtrace(TRACE_INTEXIT(STM32_TRACEINTID_USB), 0); + return OK; +} + +/**************************************************************************** + * Endpoint operations + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_enablegonak + * + * Description: + * Enable global OUT NAK mode + * + ****************************************************************************/ + +static void stm32_enablegonak(FAR struct stm32_ep_s *privep) +{ + uint32_t regval; + + /* First, make sure that there is no GNOAKEFF interrupt pending. */ + +#if 0 + stm32_putreg(OTG_GINT_GONAKEFF, STM32_OTG_GINTSTS); +#endif + + /* Enable Global OUT NAK mode in the core. */ + + regval = stm32_getreg(STM32_OTG_DCTL); + regval |= OTG_DCTL_SGONAK; + stm32_putreg(regval, STM32_OTG_DCTL); + +#if 0 + /* Wait for the GONAKEFF interrupt that indicates that the OUT NAK + * mode is in effect. When the interrupt handler pops the OUTNAK word + * from the RxFIFO, the core sets the GONAKEFF interrupt. + */ + + while ((stm32_getreg(STM32_OTG_GINTSTS) & OTG_GINT_GONAKEFF) == 0); + stm32_putreg(OTG_GINT_GONAKEFF, STM32_OTG_GINTSTS); + +#else + /* Since we are in the interrupt handler, we cannot wait inline for the + * GONAKEFF because it cannot occur until service the RXFLVL global interrupt + * and pop the OUTNAK word from the RxFIFO. + * + * Perhaps it is sufficient to wait for Global OUT NAK status to be reported + * in OTG DCTL register? + */ + + while ((stm32_getreg(STM32_OTG_DCTL) & OTG_DCTL_GONSTS) == 0); +#endif +} + +/**************************************************************************** + * Name: stm32_disablegonak + * + * Description: + * Disable global OUT NAK mode + * + ****************************************************************************/ + +static void stm32_disablegonak(FAR struct stm32_ep_s *privep) +{ + uint32_t regval; + + /* Set the "Clear the Global OUT NAK bit" to disable global OUT NAK mode */ + + regval = stm32_getreg(STM32_OTG_DCTL); + regval |= OTG_DCTL_CGONAK; + stm32_putreg(regval, STM32_OTG_DCTL); +} + +/**************************************************************************** + * Name: stm32_epout_configure + * + * Description: + * Configure an OUT endpoint, making it usable + * + * Input Parameters: + * privep - a pointer to an internal endpoint structure + * eptype - The type of the endpoint + * maxpacket - The max packet size of the endpoint + * + ****************************************************************************/ + +static int stm32_epout_configure(FAR struct stm32_ep_s *privep, uint8_t eptype, + uint16_t maxpacket) +{ + uint32_t mpsiz; + uint32_t regaddr; + uint32_t regval; + + usbtrace(TRACE_EPCONFIGURE, privep->epphy); + + /* For EP0, the packet size is encoded */ + + if (privep->epphy == EP0) + { + DEBUGASSERT(eptype == USB_EP_ATTR_XFER_CONTROL); + + /* Map the size in bytes to the encoded value in the register */ + + switch (maxpacket) + { + case 8: + mpsiz = OTG_DOEPCTL0_MPSIZ_8; + break; + + case 16: + mpsiz = OTG_DOEPCTL0_MPSIZ_16; + break; + + case 32: + mpsiz = OTG_DOEPCTL0_MPSIZ_32; + break; + + case 64: + mpsiz = OTG_DOEPCTL0_MPSIZ_64; + break; + + default: + uerr("Unsupported maxpacket: %d\n", maxpacket); + return -EINVAL; + } + } + + /* For other endpoints, the packet size is in bytes */ + + else + { + mpsiz = (maxpacket << OTG_DOEPCTL_MPSIZ_SHIFT); + } + + /* If the endpoint is already active don't change the endpoint control + * register. + */ + + regaddr = STM32_OTG_DOEPCTL(privep->epphy); + regval = stm32_getreg(regaddr); + if ((regval & OTG_DOEPCTL_USBAEP) == 0) + { + if (regval & OTG_DOEPCTL_NAKSTS) + { + regval |= OTG_DOEPCTL_CNAK; + } + + regval &= ~(OTG_DOEPCTL_MPSIZ_MASK | OTG_DOEPCTL_EPTYP_MASK); + regval |= mpsiz; + regval |= (eptype << OTG_DOEPCTL_EPTYP_SHIFT); + regval |= (OTG_DOEPCTL_SD0PID | OTG_DOEPCTL_USBAEP); + stm32_putreg(regval, regaddr); + + /* Save the endpoint configuration */ + + privep->ep.maxpacket = maxpacket; + privep->eptype = eptype; + privep->stalled = false; + } + + /* Enable the interrupt for this endpoint */ + + regval = stm32_getreg(STM32_OTG_DAINTMSK); + regval |= OTG_DAINT_OEP(privep->epphy); + stm32_putreg(regval, STM32_OTG_DAINTMSK); + return OK; +} + +/**************************************************************************** + * Name: stm32_epin_configure + * + * Description: + * Configure an IN endpoint, making it usable + * + * Input Parameters: + * privep - a pointer to an internal endpoint structure + * eptype - The type of the endpoint + * maxpacket - The max packet size of the endpoint + * + ****************************************************************************/ + +static int stm32_epin_configure(FAR struct stm32_ep_s *privep, uint8_t eptype, + uint16_t maxpacket) +{ + uint32_t mpsiz; + uint32_t regaddr; + uint32_t regval; + + usbtrace(TRACE_EPCONFIGURE, privep->epphy); + + /* For EP0, the packet size is encoded */ + + if (privep->epphy == EP0) + { + DEBUGASSERT(eptype == USB_EP_ATTR_XFER_CONTROL); + + /* Map the size in bytes to the encoded value in the register */ + + switch (maxpacket) + { + case 8: + mpsiz = OTG_DIEPCTL0_MPSIZ_8; + break; + + case 16: + mpsiz = OTG_DIEPCTL0_MPSIZ_16; + break; + + case 32: + mpsiz = OTG_DIEPCTL0_MPSIZ_32; + break; + + case 64: + mpsiz = OTG_DIEPCTL0_MPSIZ_64; + break; + + default: + uerr("Unsupported maxpacket: %d\n", maxpacket); + return -EINVAL; + } + } + + /* For other endpoints, the packet size is in bytes */ + + else + { + mpsiz = (maxpacket << OTG_DIEPCTL_MPSIZ_SHIFT); + } + + + /* If the endpoint is already active don't change the endpoint control + * register. + */ + + regaddr = STM32_OTG_DIEPCTL(privep->epphy); + regval = stm32_getreg(regaddr); + if ((regval & OTG_DIEPCTL_USBAEP) == 0) + { + if (regval & OTG_DIEPCTL_NAKSTS) + { + regval |= OTG_DIEPCTL_CNAK; + } + + regval &= ~(OTG_DIEPCTL_MPSIZ_MASK | OTG_DIEPCTL_EPTYP_MASK | OTG_DIEPCTL_TXFNUM_MASK); + regval |= mpsiz; + regval |= (eptype << OTG_DIEPCTL_EPTYP_SHIFT); + regval |= (eptype << OTG_DIEPCTL_TXFNUM_SHIFT); + regval |= (OTG_DIEPCTL_SD0PID | OTG_DIEPCTL_USBAEP); + stm32_putreg(regval, regaddr); + + /* Save the endpoint configuration */ + + privep->ep.maxpacket = maxpacket; + privep->eptype = eptype; + privep->stalled = false; + } + + /* Enable the interrupt for this endpoint */ + + regval = stm32_getreg(STM32_OTG_DAINTMSK); + regval |= OTG_DAINT_IEP(privep->epphy); + stm32_putreg(regval, STM32_OTG_DAINTMSK); + + return OK; +} + +/**************************************************************************** + * Name: stm32_ep_configure + * + * Description: + * Configure endpoint, making it usable + * + * Input Parameters: + * ep - the struct usbdev_ep_s instance obtained from allocep() + * desc - A struct usb_epdesc_s instance describing the endpoint + * last - true if this this last endpoint to be configured. Some hardware + * needs to take special action when all of the endpoints have been + * configured. + * + ****************************************************************************/ + +static int stm32_ep_configure(FAR struct usbdev_ep_s *ep, + FAR const struct usb_epdesc_s *desc, + bool last) +{ + FAR struct stm32_ep_s *privep = (FAR struct stm32_ep_s *)ep; + uint16_t maxpacket; + uint8_t eptype; + int ret; + + usbtrace(TRACE_EPCONFIGURE, privep->epphy); + DEBUGASSERT(desc->addr == ep->eplog); + + /* Initialize EP capabilities */ + + maxpacket = GETUINT16(desc->mxpacketsize); + eptype = desc->attr & USB_EP_ATTR_XFERTYPE_MASK; + + /* Setup Endpoint Control Register */ + + if (privep->isin) + { + ret = stm32_epin_configure(privep, eptype, maxpacket); + } + else + { + ret = stm32_epout_configure(privep, eptype, maxpacket); + } + + return ret; +} + +/**************************************************************************** + * Name: stm32_ep0_configure + * + * Description: + * Reset Usb engine + * + ****************************************************************************/ + +static void stm32_ep0_configure(FAR struct stm32_usbdev_s *priv) +{ + /* Enable EP0 IN and OUT */ + + (void)stm32_epin_configure(&priv->epin[EP0], USB_EP_ATTR_XFER_CONTROL, + CONFIG_USBDEV_EP0_MAXSIZE); + (void)stm32_epout_configure(&priv->epout[EP0], USB_EP_ATTR_XFER_CONTROL, + CONFIG_USBDEV_EP0_MAXSIZE); +} + +/**************************************************************************** + * Name: stm32_epout_disable + * + * Description: + * Diable an OUT endpoint will no longer be used + * + ****************************************************************************/ + +static void stm32_epout_disable(FAR struct stm32_ep_s *privep) +{ + uint32_t regaddr; + uint32_t regval; + irqstate_t flags; + + usbtrace(TRACE_EPDISABLE, privep->epphy); + + /* Is this an IN or an OUT endpoint */ + + /* Before disabling any OUT endpoint, the application must enable + * Global OUT NAK mode in the core. + */ + + flags = enter_critical_section(); + stm32_enablegonak(privep); + + /* Disable the required OUT endpoint by setting the EPDIS and SNAK bits + * int DOECPTL register. + */ + + regaddr = STM32_OTG_DOEPCTL(privep->epphy); + regval = stm32_getreg(regaddr); + regval &= ~OTG_DOEPCTL_USBAEP; + regval |= (OTG_DOEPCTL_EPDIS | OTG_DOEPCTL_SNAK); + stm32_putreg(regval, regaddr); + + /* Wait for the EPDISD interrupt which indicates that the OUT + * endpoint is completely disabled. + */ + +#if 0 /* Doesn't happen */ + regaddr = STM32_OTG_DOEPINT(privep->epphy); + while ((stm32_getreg(regaddr) & OTG_DOEPINT_EPDISD) == 0); +#else + /* REVISIT: */ + up_udelay(10); +#endif + + /* Clear the EPDISD interrupt indication */ + + stm32_putreg(OTG_DOEPINT_EPDISD, STM32_OTG_DOEPINT(privep->epphy)); + + /* Then disable the Global OUT NAK mode to continue receiving data + * from other non-disabled OUT endpoints. + */ + + stm32_disablegonak(privep); + + /* Disable endpoint interrupts */ + + regval = stm32_getreg(STM32_OTG_DAINTMSK); + regval &= ~OTG_DAINT_OEP(privep->epphy); + stm32_putreg(regval, STM32_OTG_DAINTMSK); + + /* Cancel any queued read requests */ + + stm32_req_cancel(privep, -ESHUTDOWN); + + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: stm32_epin_disable + * + * Description: + * Disable an IN endpoint when it will no longer be used + * + ****************************************************************************/ + +static void stm32_epin_disable(FAR struct stm32_ep_s *privep) +{ + uint32_t regaddr; + uint32_t regval; + irqstate_t flags; + + usbtrace(TRACE_EPDISABLE, privep->epphy); + + /* After USB reset, the endpoint will already be deactivated by the + * hardware. Trying to disable again will just hang in the wait. + */ + + regaddr = STM32_OTG_DIEPCTL(privep->epphy); + regval = stm32_getreg(regaddr); + if ((regval & OTG_DIEPCTL_USBAEP) == 0) + { + return; + } + + /* This INEPNE wait logic is suggested by reference manual, but seems + * to get stuck to infinite loop. + */ + +#if 0 + /* Make sure that there is no pending IPEPNE interrupt (because we are + * to poll this bit below). + */ + + stm32_putreg(OTG_DIEPINT_INEPNE, STM32_OTG_DIEPINT(privep->epphy)); + + /* Set the endpoint in NAK mode */ + + regaddr = STM32_OTG_DIEPCTL(privep->epphy); + regval = stm32_getreg(regaddr); + regval &= ~OTG_DIEPCTL_USBAEP; + regval |= (OTG_DIEPCTL_EPDIS | OTG_DIEPCTL_SNAK); + stm32_putreg(regval, regaddr); + + /* Wait for the INEPNE interrupt that indicates that we are now in NAK mode */ + + regaddr = STM32_OTG_DIEPINT(privep->epphy); + while ((stm32_getreg(regaddr) & OTG_DIEPINT_INEPNE) == 0); + + /* Clear the INEPNE interrupt indication */ + + stm32_putreg(OTG_DIEPINT_INEPNE, regaddr); +#endif + + /* Deactivate and disable the endpoint by setting the EPDIS and SNAK bits + * the DIEPCTLx register. + */ + + flags = enter_critical_section(); + regaddr = STM32_OTG_DIEPCTL(privep->epphy); + regval = stm32_getreg(regaddr); + regval &= ~OTG_DIEPCTL_USBAEP; + regval |= (OTG_DIEPCTL_EPDIS | OTG_DIEPCTL_SNAK); + stm32_putreg(regval, regaddr); + + /* Wait for the EPDISD interrupt which indicates that the IN + * endpoint is completely disabled. + */ + + regaddr = STM32_OTG_DIEPINT(privep->epphy); + while ((stm32_getreg(regaddr) & OTG_DIEPINT_EPDISD) == 0); + + /* Clear the EPDISD interrupt indication */ + + stm32_putreg(OTG_DIEPINT_EPDISD, stm32_getreg(regaddr)); + + /* Flush any data remaining in the TxFIFO */ + + stm32_txfifo_flush(OTG_GRSTCTL_TXFNUM_D(privep->epphy)); + + /* Disable endpoint interrupts */ + + regval = stm32_getreg(STM32_OTG_DAINTMSK); + regval &= ~OTG_DAINT_IEP(privep->epphy); + stm32_putreg(regval, STM32_OTG_DAINTMSK); + + /* Cancel any queued write requests */ + + stm32_req_cancel(privep, -ESHUTDOWN); + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: stm32_ep_disable + * + * Description: + * The endpoint will no longer be used + * + ****************************************************************************/ + +static int stm32_ep_disable(FAR struct usbdev_ep_s *ep) +{ + FAR struct stm32_ep_s *privep = (FAR struct stm32_ep_s *)ep; + +#ifdef CONFIG_DEBUG_FEATURES + if (!ep) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + + usbtrace(TRACE_EPDISABLE, privep->epphy); + + /* Is this an IN or an OUT endpoint */ + + if (privep->isin) + { + /* Disable the IN endpoint */ + + stm32_epin_disable(privep); + } + else + { + /* Disable the OUT endpoint */ + + stm32_epout_disable(privep); + } + + return OK; +} + +/**************************************************************************** + * Name: stm32_ep_allocreq + * + * Description: + * Allocate an I/O request + * + ****************************************************************************/ + +static FAR struct usbdev_req_s *stm32_ep_allocreq(FAR struct usbdev_ep_s *ep) +{ + FAR struct stm32_req_s *privreq; + +#ifdef CONFIG_DEBUG_FEATURES + if (!ep) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); + return NULL; + } +#endif + + usbtrace(TRACE_EPALLOCREQ, ((FAR struct stm32_ep_s *)ep)->epphy); + + privreq = (FAR struct stm32_req_s *)kmm_malloc(sizeof(struct stm32_req_s)); + if (!privreq) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_ALLOCFAIL), 0); + return NULL; + } + + memset(privreq, 0, sizeof(struct stm32_req_s)); + return &privreq->req; +} + +/**************************************************************************** + * Name: stm32_ep_freereq + * + * Description: + * Free an I/O request + * + ****************************************************************************/ + +static void stm32_ep_freereq(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req) +{ + FAR struct stm32_req_s *privreq = (FAR struct stm32_req_s *)req; + +#ifdef CONFIG_DEBUG_FEATURES + if (!ep || !req) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); + return; + } +#endif + + usbtrace(TRACE_EPFREEREQ, ((FAR struct stm32_ep_s *)ep)->epphy); + kmm_free(privreq); +} + +/**************************************************************************** + * Name: stm32_ep_allocbuffer + * + * Description: + * Allocate an I/O buffer + * + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_DMA +static void *stm32_ep_allocbuffer(FAR struct usbdev_ep_s *ep, unsigned bytes) +{ + usbtrace(TRACE_EPALLOCBUFFER, privep->epphy); + +#ifdef CONFIG_USBDEV_DMAMEMORY + return usbdev_dma_alloc(bytes); +#else + return kmm_malloc(bytes); +#endif +} +#endif + +/**************************************************************************** + * Name: stm32_ep_freebuffer + * + * Description: + * Free an I/O buffer + * + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_DMA +static void stm32_ep_freebuffer(FAR struct usbdev_ep_s *ep, FAR void *buf) +{ + usbtrace(TRACE_EPFREEBUFFER, privep->epphy); + +#ifdef CONFIG_USBDEV_DMAMEMORY + usbdev_dma_free(buf); +#else + kmm_free(buf); +#endif +} +#endif + +/**************************************************************************** + * Name: stm32_ep_submit + * + * Description: + * Submit an I/O request to the endpoint + * + ****************************************************************************/ + +static int stm32_ep_submit(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req) +{ + FAR struct stm32_req_s *privreq = (FAR struct stm32_req_s *)req; + FAR struct stm32_ep_s *privep = (FAR struct stm32_ep_s *)ep; + FAR struct stm32_usbdev_s *priv; + irqstate_t flags; + int ret = OK; + + /* Some sanity checking */ + +#ifdef CONFIG_DEBUG_FEATURES + if (!req || !req->callback || !req->buf || !ep) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); + ullinfo("req=%p callback=%p buf=%p ep=%p\n", req, req->callback, req->buf, ep); + return -EINVAL; + } +#endif + + usbtrace(TRACE_EPSUBMIT, privep->epphy); + priv = privep->dev; + +#ifdef CONFIG_DEBUG_FEATURES + if (!priv->driver) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_NOTCONFIGURED), priv->usbdev.speed); + return -ESHUTDOWN; + } +#endif + + /* Handle the request from the class driver */ + + req->result = -EINPROGRESS; + req->xfrd = 0; + + /* Disable Interrupts */ + + flags = enter_critical_section(); + + /* If we are stalled, then drop all requests on the floor */ + + if (privep->stalled) + { + ret = -EBUSY; + } + else + { + /* Add the new request to the request queue for the endpoint. */ + + if (stm32_req_addlast(privep, privreq) && !privep->active) + { + /* If a request was added to an IN endpoint, then attempt to send + * the request data buffer now. + */ + + if (privep->isin) + { + usbtrace(TRACE_INREQQUEUED(privep->epphy), privreq->req.len); + + /* If the endpoint is not busy with another write request, + * then process the newly received write request now. + */ + + if (!privep->active) + { + stm32_epin_request(priv, privep); + } + } + + /* If the request was added to an OUT endpoint, then attempt to + * setup a read into the request data buffer now (this will, of + * course, fail if there is already a read in place). + */ + + else + { + usbtrace(TRACE_OUTREQQUEUED(privep->epphy), privreq->req.len); + stm32_epout_request(priv, privep); + } + } + } + + leave_critical_section(flags); + return ret; +} + +/**************************************************************************** + * Name: stm32_ep_cancel + * + * Description: + * Cancel an I/O request previously sent to an endpoint + * + ****************************************************************************/ + +static int stm32_ep_cancel(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req) +{ + FAR struct stm32_ep_s *privep = (FAR struct stm32_ep_s *)ep; + irqstate_t flags; + +#ifdef CONFIG_DEBUG_FEATURES + if (!ep || !req) + { + usbtrace(TRACE_DEVERROR(STM32_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 ... + */ + + stm32_req_cancel(privep, -ESHUTDOWN); + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: stm32_epout_setstall + * + * Description: + * Stall an OUT endpoint + * + ****************************************************************************/ + +static int stm32_epout_setstall(FAR struct stm32_ep_s *privep) +{ +#if 1 + /* This implementation follows the requirements from the STM32 F4 reference + * manual. + */ + + uint32_t regaddr; + uint32_t regval; + + /* Put the core in the Global OUT NAK mode */ + + stm32_enablegonak(privep); + + /* Disable and STALL the OUT endpoint by setting the EPDIS and STALL bits + * in the DOECPTL register. + */ + + regaddr = STM32_OTG_DOEPCTL(privep->epphy); + regval = stm32_getreg(regaddr); + regval |= (OTG_DOEPCTL_EPDIS | OTG_DOEPCTL_STALL); + stm32_putreg(regval, regaddr); + + /* Wait for the EPDISD interrupt which indicates that the OUT + * endpoint is completely disabled. + */ + +#if 0 /* Doesn't happen */ + regaddr = STM32_OTG_DOEPINT(privep->epphy); + while ((stm32_getreg(regaddr) & OTG_DOEPINT_EPDISD) == 0); +#else + /* REVISIT: */ + up_udelay(10); +#endif + + /* Disable Global OUT NAK mode */ + + stm32_disablegonak(privep); + + /* The endpoint is now stalled */ + + privep->stalled = true; + return OK; +#else + /* This implementation follows the STMicro code example. */ + /* REVISIT: */ + + uint32_t regaddr; + uint32_t regval; + + /* Stall the OUT endpoint by setting the STALL bit in the DOECPTL register. */ + + regaddr = STM32_OTG_DOEPCTL(privep->epphy); + regval = stm32_getreg(regaddr); + regval |= OTG_DOEPCTL_STALL; + stm32_putreg(regval, regaddr); + + /* The endpoint is now stalled */ + + privep->stalled = true; + return OK; +#endif +} + +/**************************************************************************** + * Name: stm32_epin_setstall + * + * Description: + * Stall an IN endpoint + * + ****************************************************************************/ + +static int stm32_epin_setstall(FAR struct stm32_ep_s *privep) +{ + uint32_t regaddr; + uint32_t regval; + + /* Get the IN endpoint device control register */ + + regaddr = STM32_OTG_DIEPCTL(privep->epphy); + regval = stm32_getreg(regaddr); + + /* Then stall the endpoint */ + + regval |= OTG_DIEPCTL_STALL; + stm32_putreg(regval, regaddr); + + /* The endpoint is now stalled */ + + privep->stalled = true; + return OK; +} + +/**************************************************************************** + * Name: stm32_ep_setstall + * + * Description: + * Stall an endpoint + * + ****************************************************************************/ + +static int stm32_ep_setstall(FAR struct stm32_ep_s *privep) +{ + usbtrace(TRACE_EPSTALL, privep->epphy); + + /* Is this an IN endpoint? */ + + if (privep->isin == 1) + { + return stm32_epin_setstall(privep); + } + else + { + return stm32_epout_setstall(privep); + } +} + +/**************************************************************************** + * Name: stm32_ep_clrstall + * + * Description: + * Resume a stalled endpoint + * + ****************************************************************************/ + +static int stm32_ep_clrstall(FAR struct stm32_ep_s *privep) +{ + uint32_t regaddr; + uint32_t regval; + uint32_t stallbit; + uint32_t data0bit; + + usbtrace(TRACE_EPRESUME, privep->epphy); + + /* Is this an IN endpoint? */ + + if (privep->isin == 1) + { + /* Clear the stall bit in the IN endpoint device control register */ + + regaddr = STM32_OTG_DIEPCTL(privep->epphy); + stallbit = OTG_DIEPCTL_STALL; + data0bit = OTG_DIEPCTL_SD0PID; + } + else + { + /* Clear the stall bit in the IN endpoint device control register */ + + regaddr = STM32_OTG_DOEPCTL(privep->epphy); + stallbit = OTG_DOEPCTL_STALL; + data0bit = OTG_DOEPCTL_SD0PID; + } + + /* Clear the stall bit */ + + regval = stm32_getreg(regaddr); + regval &= ~stallbit; + + /* Set the DATA0 pid for interrupt and bulk endpoints */ + + if (privep->eptype == USB_EP_ATTR_XFER_INT || + privep->eptype == USB_EP_ATTR_XFER_BULK) + { + /* Writing this bit sets the DATA0 PID */ + + regval |= data0bit; + } + + stm32_putreg(regval, regaddr); + + /* The endpoint is no longer stalled */ + + privep->stalled = false; + return OK; +} + +/**************************************************************************** + * Name: stm32_ep_stall + * + * Description: + * Stall or resume an endpoint + * + ****************************************************************************/ + +static int stm32_ep_stall(FAR struct usbdev_ep_s *ep, bool resume) +{ + FAR struct stm32_ep_s *privep = (FAR struct stm32_ep_s *)ep; + irqstate_t flags; + int ret; + + /* Set or clear the stall condition as requested */ + + flags = enter_critical_section(); + if (resume) + { + ret = stm32_ep_clrstall(privep); + } + else + { + ret = stm32_ep_setstall(privep); + } + leave_critical_section(flags); + + return ret; +} + +/**************************************************************************** + * Name: stm32_ep0_stall + * + * Description: + * Stall endpoint 0 + * + ****************************************************************************/ + +static void stm32_ep0_stall(FAR struct stm32_usbdev_s *priv) +{ + stm32_epin_setstall(&priv->epin[EP0]); + stm32_epout_setstall(&priv->epout[EP0]); + priv->stalled = true; + stm32_ep0out_ctrlsetup(priv); +} + +/**************************************************************************** + * Device operations + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_ep_alloc + * + * Description: + * Allocate an endpoint matching the parameters. + * + * Input Parameters: + * eplog - 7-bit logical endpoint number (direction bit ignored). Zero means + * that any endpoint matching the other requirements will suffice. The + * assigned endpoint can be found in the eplog field. + * in - true: IN (device-to-host) endpoint requested + * eptype - Endpoint type. One of {USB_EP_ATTR_XFER_ISOC, USB_EP_ATTR_XFER_BULK, + * USB_EP_ATTR_XFER_INT} + * + ****************************************************************************/ + +static FAR struct usbdev_ep_s *stm32_ep_alloc(FAR struct usbdev_s *dev, + uint8_t eplog, bool in, + uint8_t eptype) +{ + FAR struct stm32_usbdev_s *priv = (FAR struct stm32_usbdev_s *)dev; + uint8_t epavail; + irqstate_t flags; + int epphy; + int epno = 0; + + usbtrace(TRACE_DEVALLOCEP, (uint16_t)eplog); + + /* Ignore any direction bits in the logical address */ + + epphy = USB_EPNO(eplog); + + /* Get the set of available endpoints depending on the direction */ + + flags = enter_critical_section(); + epavail = priv->epavail[in]; + + /* A physical address of 0 means that any endpoint will do */ + + if (epphy > 0) + { + /* Otherwise, we will return the endpoint structure only for the requested + * 'logical' endpoint. All of the other checks will still be performed. + * + * First, verify that the logical endpoint is in the range supported by + * by the hardware. + */ + + if (epphy >= STM32_NENDPOINTS) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADEPNO), (uint16_t)epphy); + return NULL; + } + + /* Remove all of the candidate endpoints from the bitset except for the + * this physical endpoint number. + */ + + epavail &= (1 << epphy); + } + + /* Is there an available endpoint? */ + + if (epavail) + { + /* Yes.. Select the lowest numbered endpoint in the set of available + * endpoints. + */ + + for (epno = 1; epno < STM32_NENDPOINTS; epno++) + { + uint8_t bit = 1 << epno; + if ((epavail & bit) != 0) + { + /* Mark the endpoint no longer available */ + + priv->epavail[in] &= ~(1 << epno); + + /* And return the pointer to the standard endpoint structure */ + + leave_critical_section(flags); + return in ? &priv->epin[epno].ep : &priv->epout[epno].ep; + } + } + + /* We should not get here */ + } + + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_NOEP), (uint16_t)eplog); + leave_critical_section(flags); + return NULL; +} + +/**************************************************************************** + * Name: stm32_ep_free + * + * Description: + * Free the previously allocated endpoint + * + ****************************************************************************/ + +static void stm32_ep_free(FAR struct usbdev_s *dev, FAR struct usbdev_ep_s *ep) +{ + FAR struct stm32_usbdev_s *priv = (FAR struct stm32_usbdev_s *)dev; + FAR struct stm32_ep_s *privep = (FAR struct stm32_ep_s *)ep; + irqstate_t flags; + + usbtrace(TRACE_DEVFREEEP, (uint16_t)privep->epphy); + + if (priv && privep) + { + /* Mark the endpoint as available */ + + flags = enter_critical_section(); + priv->epavail[privep->isin] |= (1 << privep->epphy); + leave_critical_section(flags); + } +} + +/**************************************************************************** + * Name: stm32_getframe + * + * Description: + * Returns the current frame number + * + ****************************************************************************/ + +static int stm32_getframe(struct usbdev_s *dev) +{ + uint32_t regval; + + usbtrace(TRACE_DEVGETFRAME, 0); + + /* Return the last frame number of the last SOF detected by the hardware */ + + regval = stm32_getreg(STM32_OTG_DSTS); + return (int)((regval & OTG_DSTS_SOFFN_MASK) >> OTG_DSTS_SOFFN_SHIFT); +} + +/**************************************************************************** + * Name: stm32_wakeup + * + * Description: + * Exit suspend mode. + * + ****************************************************************************/ + +static int stm32_wakeup(struct usbdev_s *dev) +{ + FAR struct stm32_usbdev_s *priv = (FAR struct stm32_usbdev_s *)dev; + uint32_t regval; + irqstate_t flags; + + usbtrace(TRACE_DEVWAKEUP, 0); + + /* Is wakeup enabled? */ + + flags = enter_critical_section(); + if (priv->wakeup) + { + /* Yes... is the core suspended? */ + + regval = stm32_getreg(STM32_OTG_DSTS); + if ((regval & OTG_DSTS_SUSPSTS) != 0) + { + /* Re-start the PHY clock and un-gate USB core clock (HCLK) */ + +#ifdef CONFIG_USBDEV_LOWPOWER + regval = stm32_getreg(STM32_OTG_PCGCCTL); + regval &= ~(OTG_PCGCCTL_STPPCLK | OTG_PCGCCTL_GATEHCLK); + stm32_putreg(regval, STM32_OTG_PCGCCTL); +#endif + /* Activate Remote wakeup signaling */ + + regval = stm32_getreg(STM32_OTG_DCTL); + regval |= OTG_DCTL_RWUSIG; + stm32_putreg(regval, STM32_OTG_DCTL); + up_mdelay(5); + regval &= ~OTG_DCTL_RWUSIG; + stm32_putreg(regval, STM32_OTG_DCTL); + } + } + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: stm32_selfpowered + * + * Description: + * Sets/clears the device self-powered feature + * + ****************************************************************************/ + +static int stm32_selfpowered(struct usbdev_s *dev, bool selfpowered) +{ + FAR struct stm32_usbdev_s *priv = (FAR struct stm32_usbdev_s *)dev; + + usbtrace(TRACE_DEVSELFPOWERED, (uint16_t)selfpowered); + +#ifdef CONFIG_DEBUG_FEATURES + if (!dev) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); + return -ENODEV; + } +#endif + + priv->selfpowered = selfpowered; + return OK; +} + +/**************************************************************************** + * Name: stm32_pullup + * + * Description: + * Software-controlled connect to/disconnect from USB host + * + ****************************************************************************/ + +static int stm32_pullup(struct usbdev_s *dev, bool enable) +{ + uint32_t regval; + + usbtrace(TRACE_DEVPULLUP, (uint16_t)enable); + + irqstate_t flags = enter_critical_section(); + regval = stm32_getreg(STM32_OTG_DCTL); + if (enable) + { + /* Connect the device by clearing the soft disconnect bit in the DCTL + * register + */ + + regval &= ~OTG_DCTL_SDIS; + } + else + { + /* Connect the device by setting the soft disconnect bit in the DCTL + * register + */ + + regval |= OTG_DCTL_SDIS; + } + + stm32_putreg(regval, STM32_OTG_DCTL); + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: stm32_setaddress + * + * Description: + * Set the devices USB address + * + ****************************************************************************/ + +static void stm32_setaddress(struct stm32_usbdev_s *priv, uint16_t address) +{ + uint32_t regval; + + /* Set the device address in the DCFG register */ + + regval = stm32_getreg(STM32_OTG_DCFG); + regval &= ~OTG_DCFG_DAD_MASK; + regval |= ((uint32_t)address << OTG_DCFG_DAD_SHIFT); + stm32_putreg(regval, STM32_OTG_DCFG); + + /* Are we now addressed? (i.e., do we have a non-NULL device + * address?) + */ + + if (address != 0) + { + priv->devstate = DEVSTATE_ADDRESSED; + priv->addressed = true; + } + else + { + priv->devstate = DEVSTATE_DEFAULT; + priv->addressed = false; + } +} + +/**************************************************************************** + * Name: stm32_txfifo_flush + * + * Description: + * Flush the specific TX fifo. + * + ****************************************************************************/ + +static int stm32_txfifo_flush(uint32_t txfnum) +{ + uint32_t regval; + uint32_t timeout; + + /* Initiate the TX FIFO flush operation */ + + regval = OTG_GRSTCTL_TXFFLSH | txfnum; + stm32_putreg(regval, STM32_OTG_GRSTCTL); + + /* Wait for the FLUSH to complete */ + + for (timeout = 0; timeout < STM32_FLUSH_DELAY; timeout++) + { + regval = stm32_getreg(STM32_OTG_GRSTCTL); + if ((regval & OTG_GRSTCTL_TXFFLSH) == 0) + { + break; + } + } + + /* Wait for 3 PHY Clocks */ + + up_udelay(3); + return OK; +} + +/**************************************************************************** + * Name: stm32_rxfifo_flush + * + * Description: + * Flush the RX fifo. + * + ****************************************************************************/ + +static int stm32_rxfifo_flush(void) +{ + uint32_t regval; + uint32_t timeout; + + /* Initiate the RX FIFO flush operation */ + + stm32_putreg(OTG_GRSTCTL_RXFFLSH, STM32_OTG_GRSTCTL); + + /* Wait for the FLUSH to complete */ + + for (timeout = 0; timeout < STM32_FLUSH_DELAY; timeout++) + { + regval = stm32_getreg(STM32_OTG_GRSTCTL); + if ((regval & OTG_GRSTCTL_RXFFLSH) == 0) + { + break; + } + } + + /* Wait for 3 PHY Clocks */ + + up_udelay(3); + return OK; +} + +/**************************************************************************** + * Name: stm32_swinitialize + * + * Description: + * Initialize all driver data structures. + * + ****************************************************************************/ + +static void stm32_swinitialize(FAR struct stm32_usbdev_s *priv) +{ + FAR struct stm32_ep_s *privep; + int i; + + /* Initialize the device state structure */ + + memset(priv, 0, sizeof(struct stm32_usbdev_s)); + + priv->usbdev.ops = &g_devops; + priv->usbdev.ep0 = &priv->epin[EP0].ep; + + priv->epavail[0] = STM32_EP_AVAILABLE; + priv->epavail[1] = STM32_EP_AVAILABLE; + + priv->epin[EP0].ep.priv = priv; + priv->epout[EP0].ep.priv = priv; + + /* Initialize the endpoint lists */ + + for (i = 0; i < STM32_NENDPOINTS; i++) + { + /* Set endpoint operations, reference to driver structure (not + * really necessary because there is only one controller), and + * the physical endpoint number (which is just the index to the + * endpoint). + */ + + privep = &priv->epin[i]; + privep->ep.ops = &g_epops; + privep->dev = priv; + privep->isin = 1; + + /* The index, i, is the physical endpoint address; Map this + * to a logical endpoint address usable by the class driver. + */ + + privep->epphy = i; + privep->ep.eplog = STM32_EPPHYIN2LOG(i); + + /* Control until endpoint is activated */ + + privep->eptype = USB_EP_ATTR_XFER_CONTROL; + privep->ep.maxpacket = CONFIG_USBDEV_EP0_MAXSIZE; + } + + /* Initialize the endpoint lists */ + + for (i = 0; i < STM32_NENDPOINTS; i++) + { + /* Set endpoint operations, reference to driver structure (not + * really necessary because there is only one controller), and + * the physical endpoint number (which is just the index to the + * endpoint). + */ + + privep = &priv->epout[i]; + privep->ep.ops = &g_epops; + privep->dev = priv; + + /* The index, i, is the physical endpoint address; Map this + * to a logical endpoint address usable by the class driver. + */ + + privep->epphy = i; + privep->ep.eplog = STM32_EPPHYOUT2LOG(i); + + /* Control until endpoint is activated */ + + privep->eptype = USB_EP_ATTR_XFER_CONTROL; + privep->ep.maxpacket = CONFIG_USBDEV_EP0_MAXSIZE; + } +} + +/**************************************************************************** + * Name: stm32_hwinitialize + * + * Description: + * Configure the OTG core for operation. + * + ****************************************************************************/ + +static void stm32_hwinitialize(FAR struct stm32_usbdev_s *priv) +{ + uint32_t regval; + uint32_t timeout; + uint32_t address; + int i; + + /* At start-up the core is in FS/HS mode. */ + + /* Disable global interrupts by clearing the GINTMASK bit in the GAHBCFG + * register; Set the TXFELVL bit in the GAHBCFG register so that TxFIFO + * interrupts will occur when the TxFIFO is truly empty (not just half full). + */ + + stm32_putreg(OTG_GAHBCFG_TXFELVL, STM32_OTG_GAHBCFG); + +#if defined(CONFIG_STM32_OTGHS) + /* Set the PHYSEL bit in the GUSBCFG register to select the OTG HS serial + * transceiver: "This bit is always 1 with write-only access" + */ + + regval = stm32_getreg(STM32_OTG_GUSBCFG); + regval |= OTG_GUSBCFG_PHYSEL; + stm32_putreg(regval, STM32_OTG_GUSBCFG); +#endif + + /* Common USB OTG core initialization */ + /* Reset after a PHY select and set Host mode. First, wait for AHB master + * IDLE state. + */ + + for (timeout = 0; timeout < STM32_READY_DELAY; timeout++) + { + up_udelay(3); + regval = stm32_getreg(STM32_OTG_GRSTCTL); + if ((regval & OTG_GRSTCTL_AHBIDL) != 0) + { + break; + } + } + + /* Then perform the core soft reset. */ + + stm32_putreg(OTG_GRSTCTL_CSRST, STM32_OTG_GRSTCTL); + for (timeout = 0; timeout < STM32_READY_DELAY; timeout++) + { + regval = stm32_getreg(STM32_OTG_GRSTCTL); + if ((regval & OTG_GRSTCTL_CSRST) == 0) + { + break; + } + } + + /* Wait for 3 PHY Clocks */ + + up_udelay(3); + + /* Deactivate the power down */ + + + /* Detection Enable when set + */ + + regval = OTG_GCCFG_PWRDWN; + +# ifdef CONFIG_USBDEV_VBUSSENSING + regval |= OTG_GCCFG_VBDEN; +# endif + + + stm32_putreg(regval, STM32_OTG_GCCFG); + up_mdelay(20); + + /* When VBUS sensing is not used we + * need to force the B session valid + */ + + +# ifndef CONFIG_USBDEV_VBUSSENSING + regval = stm32_getreg(STM32_OTG_GOTGCTL); + regval |= (OTG_GOTGCTL_BVALOEN | OTG_GOTGCTL_BVALOVAL); + stm32_putreg(regval, STM32_OTG_GOTGCTL); +# endif + + + /* Force Device Mode */ + + regval = stm32_getreg(STM32_OTG_GUSBCFG); + regval &= ~OTG_GUSBCFG_FHMOD; + regval |= OTG_GUSBCFG_FDMOD; + stm32_putreg(regval, STM32_OTG_GUSBCFG); + up_mdelay(50); + + /* Initialize device mode */ + /* Restart the PHY Clock */ + + stm32_putreg(0, STM32_OTG_PCGCCTL); + + /* Device configuration register */ + + regval = stm32_getreg(STM32_OTG_DCFG); + regval &= ~OTG_DCFG_PFIVL_MASK; + regval |= OTG_DCFG_PFIVL_80PCT; + stm32_putreg(regval, STM32_OTG_DCFG); + + /* Set full speed PHY */ + + regval = stm32_getreg(STM32_OTG_DCFG); + regval &= ~OTG_DCFG_DSPD_MASK; + regval |= OTG_DCFG_DSPD_FS; + stm32_putreg(regval, STM32_OTG_DCFG); + + /* Set Rx FIFO size */ + + stm32_putreg(STM32_RXFIFO_WORDS, STM32_OTG_GRXFSIZ); + + /* EP0 TX */ + + address = STM32_RXFIFO_WORDS; + regval = (address << OTG_DIEPTXF0_TX0FD_SHIFT) | + (STM32_EP0_TXFIFO_WORDS << OTG_DIEPTXF0_TX0FSA_SHIFT); + stm32_putreg(regval, STM32_OTG_DIEPTXF0); + + /* EP1 TX */ + + address += STM32_EP0_TXFIFO_WORDS; + regval = (address << OTG_DIEPTXF_INEPTXSA_SHIFT) | + (STM32_EP1_TXFIFO_WORDS << OTG_DIEPTXF_INEPTXFD_SHIFT); + stm32_putreg(regval, STM32_OTG_DIEPTXF1); + + /* EP2 TX */ + + address += STM32_EP1_TXFIFO_WORDS; + regval = (address << OTG_DIEPTXF_INEPTXSA_SHIFT) | + (STM32_EP2_TXFIFO_WORDS << OTG_DIEPTXF_INEPTXFD_SHIFT); + stm32_putreg(regval, STM32_OTG_DIEPTXF2); + + /* EP3 TX */ + + address += STM32_EP2_TXFIFO_WORDS; + regval = (address << OTG_DIEPTXF_INEPTXSA_SHIFT) | + (STM32_EP3_TXFIFO_WORDS << OTG_DIEPTXF_INEPTXFD_SHIFT); + stm32_putreg(regval, STM32_OTG_DIEPTXF3); + + /* Flush the FIFOs */ + + stm32_txfifo_flush(OTG_GRSTCTL_TXFNUM_DALL); + stm32_rxfifo_flush(); + + /* Clear all pending Device Interrupts */ + + stm32_putreg(0, STM32_OTG_DIEPMSK); + stm32_putreg(0, STM32_OTG_DOEPMSK); + stm32_putreg(0, STM32_OTG_DIEPEMPMSK); + stm32_putreg(0xffffffff, STM32_OTG_DAINT); + stm32_putreg(0, STM32_OTG_DAINTMSK); + + /* Configure all IN endpoints */ + + for (i = 0; i < STM32_NENDPOINTS; i++) + { + regval = stm32_getreg(STM32_OTG_DIEPCTL(i)); + if ((regval & OTG_DIEPCTL_EPENA) != 0) + { + /* The endpoint is already enabled */ + + regval = OTG_DIEPCTL_EPENA | OTG_DIEPCTL_SNAK; + } + else + { + regval = 0; + } + + stm32_putreg(regval, STM32_OTG_DIEPCTL(i)); + stm32_putreg(0, STM32_OTG_DIEPTSIZ(i)); + stm32_putreg(0xff, STM32_OTG_DIEPINT(i)); + } + + /* Configure all OUT endpoints */ + + for (i = 0; i < STM32_NENDPOINTS; i++) + { + regval = stm32_getreg(STM32_OTG_DOEPCTL(i)); + if ((regval & OTG_DOEPCTL_EPENA) != 0) + { + /* The endpoint is already enabled */ + + regval = OTG_DOEPCTL_EPENA | OTG_DOEPCTL_SNAK; + } + else + { + regval = 0; + } + + stm32_putreg(regval, STM32_OTG_DOEPCTL(i)); + stm32_putreg(0, STM32_OTG_DOEPTSIZ(i)); + stm32_putreg(0xff, STM32_OTG_DOEPINT(i)); + } + + /* Disable all interrupts. */ + + stm32_putreg(0, STM32_OTG_GINTMSK); + + /* Clear any pending USB_OTG Interrupts */ + + stm32_putreg(0xffffffff, STM32_OTG_GOTGINT); + + /* Clear any pending interrupts */ + + stm32_putreg(0xbfffffff, STM32_OTG_GINTSTS); + +#ifdef defined(CONFIG_STM32_OTGHS) + /* Disable the ULPI Clock enable in RCC AHB1 Register. This must + * be done because if both the ULPI and the FS PHY clock enable bits + * are set at the same time, the ARM never awakens from WFI due to + * some bug / errata in the chip. + */ + + regval = stm32_getreg(STM32_RCC_AHB1LPENR); + regval &= ~RCC_AHB1ENR_OTGULPIEN; + stm32_putreg(regval, STM32_RCC_AHB1LPENR); +#endif + + /* Enable the interrupts in the INTMSK */ + + regval = (OTG_GINT_RXFLVL | OTG_GINT_USBSUSP | OTG_GINT_ENUMDNE | + OTG_GINT_IEP | OTG_GINT_OEP | OTG_GINT_USBRST); + +#ifdef CONFIG_USBDEV_ISOCHRONOUS + regval |= (OTG_GINT_IISOIXFR | OTG_GINT_IISOOXFR); +#endif + +#ifdef CONFIG_USBDEV_SOFINTERRUPT + regval |= OTG_GINT_SOF; +#endif + +#ifdef CONFIG_USBDEV_VBUSSENSING + regval |= (OTG_GINT_OTG | OTG_GINT_SRQ); +#endif + +#ifdef CONFIG_DEBUG_USB + regval |= OTG_GINT_MMIS; +#endif + + stm32_putreg(regval, STM32_OTG_GINTMSK); + + /* Enable the USB global interrupt by setting GINTMSK in the global OTG + * AHB configuration register; Set the TXFELVL bit in the GAHBCFG + * register so that TxFIFO interrupts will occur when the TxFIFO is truly + * empty (not just half full). + */ + + stm32_putreg(OTG_GAHBCFG_GINTMSK | OTG_GAHBCFG_TXFELVL, + STM32_OTG_GAHBCFG); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_usbinitialize + * + * Description: + * Initialize USB hardware. + * + * Assumptions: + * - This function is called very early in the initialization sequence + * - PLL and GIO pin initialization is not performed here but should been in + * the low-level boot logic: PLL1 must be configured for operation at 48MHz + * and P0.23 and PO.31 in PINSEL1 must be configured for Vbus and USB connect + * LED. + * + ****************************************************************************/ + +void up_usbinitialize(void) +{ + /* At present, there is only a single OTG device support. Hence it is + * pre-allocated as g_otghsdev. However, in most code, the private data + * structure will be referenced using the 'priv' pointer (rather than the + * global data) in order to simplify any future support for multiple devices. + */ + + FAR struct stm32_usbdev_s *priv = &g_otghsdev; + int ret; + + usbtrace(TRACE_DEVINIT, 0); + + /* Here we assume that: + * + * 1. GPIOA and OTG peripheral clocking has already been enabled as part + * of the boot sequence. + * 2. Board-specific logic has already enabled other board specific GPIOs + * for things like soft pull-up, VBUS sensing, power controls, and over- + * current detection. + */ + + /* Configure OTG alternate function pins + */ + + stm32_configgpio(GPIO_OTG_DM); + stm32_configgpio(GPIO_OTG_DP); + stm32_configgpio(GPIO_OTG_ID); /* Only needed for OTG */ + + /* SOF output pin configuration is configurable. */ + +#ifdef CONFIG_STM32_OTG_SOFOUTPUT + stm32_configgpio(GPIO_OTG_SOF); +#endif + + /* Uninitialize the hardware so that we know that we are starting from a + * known state. */ + + up_usbuninitialize(); + + /* Initialie the driver data structure */ + + stm32_swinitialize(priv); + + /* Attach the OTG interrupt handler */ + + ret = irq_attach(STM32_IRQ_OTG, stm32_usbinterrupt); + if (ret < 0) + { + uerr("irq_attach failed\n", ret); + goto errout; + } + + /* Initialize the USB OTG core */ + + stm32_hwinitialize(priv); + + /* Disconnect device */ + + stm32_pullup(&priv->usbdev, false); + + /* Reset/Re-initialize the USB hardware */ + + stm32_usbreset(priv); + + /* Enable USB controller interrupts at the NVIC */ + + up_enable_irq(STM32_IRQ_OTG); + +#ifdef CONFIG_ARCH_IRQPRIO + /* Set the interrupt priority */ + + up_prioritize_irq(STM32_IRQ_OTG, CONFIG_OTG_PRI); +#endif + return; + +errout: + up_usbuninitialize(); +} + +/**************************************************************************** + * Name: up_usbuninitialize + ****************************************************************************/ + +void up_usbuninitialize(void) +{ + /* At present, there is only a single OTG device support. Hence it is + * pre-allocated as g_otghsdev. However, in most code, the private data + * structure will be referenced using the 'priv' pointer (rather than the + * global data) in order to simplify any future support for multiple devices. + */ + + FAR struct stm32_usbdev_s *priv = &g_otghsdev; + irqstate_t flags; + int i; + + usbtrace(TRACE_DEVUNINIT, 0); + + if (priv->driver) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_DRIVERREGISTERED), 0); + usbdev_unregister(priv->driver); + } + + /* Disconnect device */ + + flags = enter_critical_section(); + stm32_pullup(&priv->usbdev, false); + priv->usbdev.speed = USB_SPEED_UNKNOWN; + + /* Disable and detach IRQs */ + + up_disable_irq(STM32_IRQ_OTG); + irq_detach(STM32_IRQ_OTG); + + /* Disable all endpoint interrupts */ + + for (i = 0; i < STM32_NENDPOINTS; i++) + { + stm32_putreg(0xff, STM32_OTG_DIEPINT(i)); + stm32_putreg(0xff, STM32_OTG_DOEPINT(i)); + } + + stm32_putreg(0, STM32_OTG_DIEPMSK); + stm32_putreg(0, STM32_OTG_DOEPMSK); + stm32_putreg(0, STM32_OTG_DIEPEMPMSK); + stm32_putreg(0, STM32_OTG_DAINTMSK); + stm32_putreg(0xffffffff, STM32_OTG_DAINT); + + /* Flush the FIFOs */ + + stm32_txfifo_flush(OTG_GRSTCTL_TXFNUM_DALL); + stm32_rxfifo_flush(); + + /* TODO: Turn off USB power and clocking */ + + priv->devstate = DEVSTATE_DEFAULT; + 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) +{ + /* At present, there is only a single OTG device support. Hence it is + * pre-allocated as g_otghsdev. However, in most code, the private data + * structure will be referenced using the 'priv' pointer (rather than the + * global data) in order to simplify any future support for multiple devices. + */ + + FAR struct stm32_usbdev_s *priv = &g_otghsdev; + 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(STM32_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } + + if (priv->driver) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_DRIVER), 0); + return -EBUSY; + } +#endif + + /* First hook up the driver */ + + priv->driver = driver; + + /* Then bind the class driver */ + + ret = CLASS_BIND(driver, &priv->usbdev); + if (ret) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BINDFAILED), (uint16_t)-ret); + priv->driver = NULL; + } + else + { + /* Enable USB controller interrupts */ + + up_enable_irq(STM32_IRQ_OTG); + + /* FIXME: nothing seems to call DEV_CONNECT(), but we need to set + * the RS bit to enable the controller. It kind of makes sense + * to do this after the class has bound to us... + * GEN: This bug is really in the class driver. It should make the + * soft connect when it is ready to be enumerated. I have added + * that logic to the class drivers but left this logic here. + */ + + stm32_pullup(&priv->usbdev, true); + priv->usbdev.speed = USB_SPEED_FULL; + } + + return ret; +} + +/**************************************************************************** + * Name: usbdev_unregister + * + * Description: + * Un-register usbdev class driver.If the USB device is connected to a USB host, + * it will first disconnect(). The driver is also requested to unbind() and clean + * up any device state, before this procedure finally returns. + * + ****************************************************************************/ + +int usbdev_unregister(struct usbdevclass_driver_s *driver) +{ + /* At present, there is only a single OTG device support. Hence it is + * pre-allocated as g_otghsdev. However, in most code, the private data + * structure will be referenced using the 'priv' pointer (rather than the + * global data) in order to simplify any future support for multiple devices. + */ + + FAR struct stm32_usbdev_s *priv = &g_otghsdev; + irqstate_t flags; + + usbtrace(TRACE_DEVUNREGISTER, 0); + +#ifdef CONFIG_DEBUG_FEATURES + if (driver != priv->driver) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + + /* Reset the hardware and cancel all requests. All requests must be + * canceled while the class driver is still bound. + */ + + flags = enter_critical_section(); + stm32_usbreset(priv); + leave_critical_section(flags); + + /* Unbind the class driver */ + + CLASS_UNBIND(driver, &priv->usbdev); + + /* Disable USB controller interrupts */ + + flags = enter_critical_section(); + up_disable_irq(STM32_IRQ_OTG); + + /* Disconnect device */ + + stm32_pullup(&priv->usbdev, false); + + /* Unhook the driver */ + + priv->driver = NULL; + leave_critical_section(flags); + + return OK; +} + +#endif /* CONFIG_USBDEV && CONFIG_STM32_OTGDEV */ diff --git a/arch/arm/src/stm32f7/stm32_otghost.c b/arch/arm/src/stm32f7/stm32_otghost.c new file mode 100644 index 00000000000..cc00b1336f2 --- /dev/null +++ b/arch/arm/src/stm32f7/stm32_otghost.c @@ -0,0 +1,5306 @@ +/**************************************************************************** + * arch/arm/src/stm32f7/stm32_otghost.c + * + * Copyright (C) 2012-2016 Gregory Nutt. All rights reserved. + * Authors: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "chip.h" /* Includes default GPIO settings */ +#include /* May redefine GPIO settings */ + +#include "up_arch.h" +#include "up_internal.h" + +#include "stm32_otg.h" + +#if defined(CONFIG_USBHOST) && defined(CONFIG_STM32_OTGFS) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* Configuration ***************************************************************/ +/* STM32 USB OTG FS Host Driver Support + * + * Pre-requisites + * + * CONFIG_USBHOST - Enable general USB host support + * CONFIG_STM32_OTGFS - Enable the STM32 USB OTG FS block + * CONFIG_STM32_SYSCFG - Needed + * + * Options: + * + * CONFIG_STM32_OTG_RXFIFO_SIZE - Size of the RX FIFO in 32-bit words. + * Default 128 (512 bytes) + * CONFIG_STM32_OTG_NPTXFIFO_SIZE - Size of the non-periodic Tx FIFO + * in 32-bit words. Default 96 (384 bytes) + * CONFIG_STM32_OTG_PTXFIFO_SIZE - Size of the periodic Tx FIFO in 32-bit + * words. Default 96 (384 bytes) + * CONFIG_STM32_OTG_DESCSIZE - Maximum size of a descriptor. Default: 128 + * CONFIG_STM32_OTG_SOFINTR - Enable SOF interrupts. Why would you ever + * want to do that? + * CONFIG_STM32_USBHOST_REGDEBUG - Enable very low-level register access + * debug. Depends on CONFIG_DEBUG_FEATURES. + * CONFIG_STM32_USBHOST_PKTDUMP - Dump all incoming and outgoing USB + * packets. Depends on CONFIG_DEBUG_FEATURES. + */ + +/* Pre-requisites (partial) */ + +#ifndef CONFIG_STM32_SYSCFG +# error "CONFIG_STM32_SYSCFG is required" +#endif + +/* Default RxFIFO size */ + +#ifndef CONFIG_STM32_OTG_RXFIFO_SIZE +# define CONFIG_STM32_OTG_RXFIFO_SIZE 128 +#endif + +/* Default host non-periodic Tx FIFO size */ + +#ifndef CONFIG_STM32_OTG_NPTXFIFO_SIZE +# define CONFIG_STM32_OTG_NPTXFIFO_SIZE 96 +#endif + +/* Default host periodic Tx fifo size register */ + +#ifndef CONFIG_STM32_OTG_PTXFIFO_SIZE +# define CONFIG_STM32_OTG_PTXFIFO_SIZE 96 +#endif + +/* Maximum size of a descriptor */ + +#ifndef CONFIG_STM32_OTG_DESCSIZE +# define CONFIG_STM32_OTG_DESCSIZE 128 +#endif + +/* Register/packet debug depends on CONFIG_DEBUG_FEATURES */ + +#ifndef CONFIG_DEBUG_FEATURES +# undef CONFIG_STM32_USBHOST_REGDEBUG +# undef CONFIG_STM32_USBHOST_PKTDUMP +#endif + +/* HCD Setup *******************************************************************/ +/* Hardware capabilities */ + +#define STM32_NHOST_CHANNELS 8 /* Number of host channels */ +#define STM32_MAX_PACKET_SIZE 64 /* Full speed max packet size */ +#define STM32_EP0_DEF_PACKET_SIZE 8 /* EP0 default packet size */ +#define STM32_EP0_MAX_PACKET_SIZE 64 /* EP0 FS max packet size */ +#define STM32_MAX_TX_FIFOS 15 /* Max number of TX FIFOs */ +#define STM32_MAX_PKTCOUNT 256 /* Max packet count */ +#define STM32_RETRY_COUNT 3 /* Number of ctrl transfer retries */ + +/* Delays **********************************************************************/ + +#define STM32_READY_DELAY 200000 /* In loop counts */ +#define STM32_FLUSH_DELAY 200000 /* In loop counts */ +#define STM32_SETUP_DELAY SEC2TICK(5) /* 5 seconds in system ticks */ +#define STM32_DATANAK_DELAY SEC2TICK(5) /* 5 seconds in system ticks */ + +/* Ever-present MIN/MAX macros */ + +#ifndef MIN +# define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef MAX +# define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* The following enumeration represents the various states of the USB host + * state machine (for debug purposes only) + */ + +enum stm32_smstate_e +{ + SMSTATE_DETACHED = 0, /* Not attached to a device */ + SMSTATE_ATTACHED, /* Attached to a device */ + SMSTATE_ENUM, /* Attached, enumerating */ + SMSTATE_CLASS_BOUND, /* Enumeration complete, class bound */ +}; + +/* This enumeration provides the reason for the channel halt. */ + +enum stm32_chreason_e +{ + CHREASON_IDLE = 0, /* Inactive (initial state) */ + CHREASON_FREED, /* Channel is no longer in use */ + CHREASON_XFRC, /* Transfer complete */ + CHREASON_NAK, /* NAK received */ + CHREASON_NYET, /* NotYet received */ + CHREASON_STALL, /* Endpoint stalled */ + CHREASON_TXERR, /* Transfer error received */ + CHREASON_DTERR, /* Data toggle error received */ + CHREASON_FRMOR, /* Frame overrun */ + CHREASON_CANCELLED /* Transfer cancelled */ +}; + +/* This structure retains the state of one host channel. NOTE: Since there + * is only one channel operation active at a time, some of the fields in + * in the structure could be moved in struct stm32_ubhost_s to achieve + * some memory savings. + */ + +struct stm32_chan_s +{ + sem_t waitsem; /* Channel wait semaphore */ + volatile uint8_t result; /* The result of the transfer */ + volatile uint8_t chreason; /* Channel halt reason. See enum stm32_chreason_e */ + uint8_t chidx; /* Channel index */ + uint8_t epno; /* Device endpoint number (0-127) */ + uint8_t eptype; /* See OTG_EPTYPE_* definitions */ + uint8_t funcaddr; /* Device function address */ + uint8_t speed; /* Device speed */ + uint8_t pid; /* Data PID */ + uint8_t npackets; /* Number of packets (for data toggle) */ + bool inuse; /* True: This channel is "in use" */ + volatile bool indata1; /* IN data toggle. True: DATA01 (Bulk and INTR only) */ + volatile bool outdata1; /* OUT data toggle. True: DATA01 */ + bool in; /* True: IN endpoint */ + volatile bool waiter; /* True: Thread is waiting for a channel event */ + uint16_t maxpacket; /* Max packet size */ + uint16_t buflen; /* Buffer length (at start of transfer) */ + volatile uint16_t xfrd; /* Bytes transferred (at end of transfer) */ + volatile uint16_t inflight; /* Number of Tx bytes "in-flight" */ + FAR uint8_t *buffer; /* Transfer buffer pointer */ +#ifdef CONFIG_USBHOST_ASYNCH + usbhost_asynch_t callback; /* Transfer complete callback */ + FAR void *arg; /* Argument that accompanies the callback */ +#endif +}; + +/* A channel represents on uni-directional endpoint. So, in the case of the + * bi-directional, control endpoint, there must be two channels to represent + * the endpoint. + */ + +struct stm32_ctrlinfo_s +{ + uint8_t inndx; /* EP0 IN control channel index */ + uint8_t outndx; /* EP0 OUT control channel index */ +}; + +/* This structure retains the state of the USB host controller */ + +struct stm32_usbhost_s +{ + /* Common device fields. This must be the first thing defined in the + * structure so that it is possible to simply cast from struct usbhost_s + * to structstm32_usbhost_s. + */ + + struct usbhost_driver_s drvr; + + /* This is the hub port description understood by class drivers */ + + struct usbhost_roothubport_s rhport; + + /* Overall driver status */ + + volatile uint8_t smstate; /* The state of the USB host state machine */ + uint8_t chidx; /* ID of channel waiting for space in Tx FIFO */ + volatile bool connected; /* Connected to device */ + volatile bool change; /* Connection change */ + volatile bool pscwait; /* True: Thread is waiting for a port event */ + sem_t exclsem; /* Support mutually exclusive access */ + sem_t pscsem; /* Semaphore to wait for a port event */ + struct stm32_ctrlinfo_s ep0; /* Root hub port EP0 description */ + +#ifdef CONFIG_USBHOST_HUB + /* Used to pass external hub port events */ + + volatile struct usbhost_hubport_s *hport; +#endif + + /* The state of each host channel */ + + struct stm32_chan_s chan[STM32_MAX_TX_FIFOS]; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Register operations ********************************************************/ + +#ifdef CONFIG_STM32_USBHOST_REGDEBUG +static void stm32_printreg(uint32_t addr, uint32_t val, bool iswrite); +static void stm32_checkreg(uint32_t addr, uint32_t val, bool iswrite); +static uint32_t stm32_getreg(uint32_t addr); +static void stm32_putreg(uint32_t addr, uint32_t value); +#else +# define stm32_getreg(addr) getreg32(addr) +# define stm32_putreg(addr,val) putreg32(val,addr) +#endif + +static inline void stm32_modifyreg(uint32_t addr, uint32_t clrbits, + uint32_t setbits); + +#ifdef CONFIG_STM32_USBHOST_PKTDUMP +# define stm32_pktdump(m,b,n) lib_dumpbuffer(m,b,n) +#else +# define stm32_pktdump(m,b,n) +#endif + +/* Semaphores ******************************************************************/ + +static void stm32_takesem(sem_t *sem); +#define stm32_givesem(s) sem_post(s); + +/* Byte stream access helper functions *****************************************/ + +static inline uint16_t stm32_getle16(const uint8_t *val); + +/* Channel management **********************************************************/ + +static int stm32_chan_alloc(FAR struct stm32_usbhost_s *priv); +static inline void stm32_chan_free(FAR struct stm32_usbhost_s *priv, int chidx); +static inline void stm32_chan_freeall(FAR struct stm32_usbhost_s *priv); +static void stm32_chan_configure(FAR struct stm32_usbhost_s *priv, int chidx); +static void stm32_chan_halt(FAR struct stm32_usbhost_s *priv, int chidx, + enum stm32_chreason_e chreason); +static int stm32_chan_waitsetup(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_chan_s *chan); +#ifdef CONFIG_USBHOST_ASYNCH +static int stm32_chan_asynchsetup(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_chan_s *chan, + usbhost_asynch_t callback, FAR void *arg); +#endif +static int stm32_chan_wait(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_chan_s *chan); +static void stm32_chan_wakeup(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_chan_s *chan); +static int stm32_ctrlchan_alloc(FAR struct stm32_usbhost_s *priv, + uint8_t epno, uint8_t funcaddr, uint8_t speed, + FAR struct stm32_ctrlinfo_s *ctrlep); +static int stm32_ctrlep_alloc(FAR struct stm32_usbhost_s *priv, + FAR const struct usbhost_epdesc_s *epdesc, + FAR usbhost_ep_t *ep); +static int stm32_xfrep_alloc(FAR struct stm32_usbhost_s *priv, + FAR const struct usbhost_epdesc_s *epdesc, + FAR usbhost_ep_t *ep); + +/* Control/data transfer logic *************************************************/ + +static void stm32_transfer_start(FAR struct stm32_usbhost_s *priv, int chidx); +#if 0 /* Not used */ +static inline uint16_t stm32_getframe(void); +#endif +static int stm32_ctrl_sendsetup(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_ctrlinfo_s *ep0, + FAR const struct usb_ctrlreq_s *req); +static int stm32_ctrl_senddata(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_ctrlinfo_s *ep0, + FAR uint8_t *buffer, unsigned int buflen); +static int stm32_ctrl_recvdata(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_ctrlinfo_s *ep0, + FAR uint8_t *buffer, unsigned int buflen); +static int stm32_in_setup(FAR struct stm32_usbhost_s *priv, int chidx); +static ssize_t stm32_in_transfer(FAR struct stm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen); +#ifdef CONFIG_USBHOST_ASYNCH +static void stm32_in_next(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_chan_s *chan); +static int stm32_in_asynch(FAR struct stm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen, + usbhost_asynch_t callback, FAR void *arg); +#endif +static int stm32_out_setup(FAR struct stm32_usbhost_s *priv, int chidx); +static ssize_t stm32_out_transfer(FAR struct stm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen); +#ifdef CONFIG_USBHOST_ASYNCH +static void stm32_out_next(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_chan_s *chan); +static int stm32_out_asynch(FAR struct stm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen, + usbhost_asynch_t callback, FAR void *arg); +#endif + +/* Interrupt handling **********************************************************/ +/* Lower level interrupt handlers */ + +static void stm32_gint_wrpacket(FAR struct stm32_usbhost_s *priv, + FAR uint8_t *buffer, int chidx, int buflen); +static inline void stm32_gint_hcinisr(FAR struct stm32_usbhost_s *priv, + int chidx); +static inline void stm32_gint_hcoutisr(FAR struct stm32_usbhost_s *priv, + int chidx); +static void stm32_gint_connected(FAR struct stm32_usbhost_s *priv); +static void stm32_gint_disconnected(FAR struct stm32_usbhost_s *priv); + +/* Second level interrupt handlers */ + +#ifdef CONFIG_STM32_OTG_SOFINTR +static inline void stm32_gint_sofisr(FAR struct stm32_usbhost_s *priv); +#endif +static inline void stm32_gint_rxflvlisr(FAR struct stm32_usbhost_s *priv); +static inline void stm32_gint_nptxfeisr(FAR struct stm32_usbhost_s *priv); +static inline void stm32_gint_ptxfeisr(FAR struct stm32_usbhost_s *priv); +static inline void stm32_gint_hcisr(FAR struct stm32_usbhost_s *priv); +static inline void stm32_gint_hprtisr(FAR struct stm32_usbhost_s *priv); +static inline void stm32_gint_discisr(FAR struct stm32_usbhost_s *priv); +static inline void stm32_gint_ipxfrisr(FAR struct stm32_usbhost_s *priv); + +/* First level, global interrupt handler */ + +static int stm32_gint_isr(int irq, FAR void *context); + +/* Interrupt controls */ + +static void stm32_gint_enable(void); +static void stm32_gint_disable(void); +static inline void stm32_hostinit_enable(void); +static void stm32_txfe_enable(FAR struct stm32_usbhost_s *priv, int chidx); + +/* USB host controller operations **********************************************/ + +static int stm32_wait(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s **hport); +static int stm32_rh_enumerate(FAR struct stm32_usbhost_s *priv, + FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport); +static int stm32_enumerate(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport); + +static int stm32_ep0configure(FAR struct usbhost_driver_s *drvr, + usbhost_ep_t ep0, uint8_t funcaddr, uint8_t speed, + uint16_t maxpacketsize); +static int stm32_epalloc(FAR struct usbhost_driver_s *drvr, + FAR const FAR struct usbhost_epdesc_s *epdesc, + FAR usbhost_ep_t *ep); +static int stm32_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep); +static int stm32_alloc(FAR struct usbhost_driver_s *drvr, + FAR uint8_t **buffer, FAR size_t *maxlen); +static int stm32_free(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer); +static int stm32_ioalloc(FAR struct usbhost_driver_s *drvr, + FAR uint8_t **buffer, size_t buflen); +static int stm32_iofree(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer); +static int stm32_ctrlin(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, + const struct usb_ctrlreq_s *req, + FAR uint8_t *buffer); +static int stm32_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 stm32_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, + FAR uint8_t *buffer, size_t buflen); +#ifdef CONFIG_USBHOST_ASYNCH +static int stm32_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 stm32_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep); +#ifdef CONFIG_USBHOST_HUB +static int stm32_connect(FAR struct usbhost_driver_s *drvr, + FAR struct usbhost_hubport_s *hport, + bool connected); +#endif +static void stm32_disconnect(FAR struct usbhost_driver_s *drvr, + FAR struct usbhost_hubport_s *hport); + +/* Initialization **************************************************************/ + +static void stm32_portreset(FAR struct stm32_usbhost_s *priv); +static void stm32_flush_txfifos(uint32_t txfnum); +static void stm32_flush_rxfifo(void); +static void stm32_vbusdrive(FAR struct stm32_usbhost_s *priv, bool state); +static void stm32_host_initialize(FAR struct stm32_usbhost_s *priv); + +static inline void stm32_sw_initialize(FAR struct stm32_usbhost_s *priv); +static inline int stm32_hw_initialize(FAR struct stm32_usbhost_s *priv); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* In this driver implementation, support is provided for only a single a single + * USB device. All status information can be simply retained in a single global + * instance. + */ + +static struct stm32_usbhost_s g_usbhost; + +/* This is the connection/enumeration interface */ + +static struct usbhost_connection_s g_usbconn = +{ + .wait = stm32_wait, + .enumerate = stm32_enumerate, +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_printreg + * + * Description: + * Print the contents of an STM32xx register operation + * + ****************************************************************************/ + +#ifdef CONFIG_STM32_USBHOST_REGDEBUG +static void stm32_printreg(uint32_t addr, uint32_t val, bool iswrite) +{ + llerr("%08x%s%08x\n", addr, iswrite ? "<-" : "->", val); +} +#endif + +/**************************************************************************** + * Name: stm32_checkreg + * + * Description: + * Get the contents of an STM32 register + * + ****************************************************************************/ + +#ifdef CONFIG_STM32_USBHOST_REGDEBUG +static void stm32_checkreg(uint32_t addr, uint32_t val, bool iswrite) +{ + static uint32_t prevaddr = 0; + static uint32_t preval = 0; + static uint32_t count = 0; + static bool prevwrite = false; + + /* Is this the same value that we read from/wrote to the same register last time? + * Are we polling the register? If so, suppress the output. + */ + + if (addr == prevaddr && val == preval && prevwrite == iswrite) + { + /* Yes.. Just increment the count */ + + count++; + } + else + { + /* No this is a new address or value or operation. Were there any + * duplicate accesses before this one? + */ + + if (count > 0) + { + /* Yes.. Just one? */ + + if (count == 1) + { + /* Yes.. Just one */ + + stm32_printreg(prevaddr, preval, prevwrite); + } + else + { + /* No.. More than one. */ + + llerr("[repeats %d more times]\n", count); + } + } + + /* Save the new address, value, count, and operation for next time */ + + prevaddr = addr; + preval = val; + count = 0; + prevwrite = iswrite; + + /* Show the new regisgter access */ + + stm32_printreg(addr, val, iswrite); + } +} +#endif + +/**************************************************************************** + * Name: stm32_getreg + * + * Description: + * Get the contents of an STM32 register + * + ****************************************************************************/ + +#ifdef CONFIG_STM32_USBHOST_REGDEBUG +static uint32_t stm32_getreg(uint32_t addr) +{ + /* Read the value from the register */ + + uint32_t val = getreg32(addr); + + /* Check if we need to print this value */ + + stm32_checkreg(addr, val, false); + return val; +} +#endif + +/**************************************************************************** + * Name: stm32_putreg + * + * Description: + * Set the contents of an STM32 register to a value + * + ****************************************************************************/ + +#ifdef CONFIG_STM32_USBHOST_REGDEBUG +static void stm32_putreg(uint32_t addr, uint32_t val) +{ + /* Check if we need to print this value */ + + stm32_checkreg(addr, val, true); + + /* Write the value */ + + putreg32(val, addr); +} +#endif + +/**************************************************************************** + * Name: stm32_modifyreg + * + * Description: + * Modify selected bits of an STM32 register. + * + ****************************************************************************/ + +static inline void stm32_modifyreg(uint32_t addr, uint32_t clrbits, uint32_t setbits) +{ + stm32_putreg(addr, (((stm32_getreg(addr)) & ~clrbits) | setbits)); +} + +/**************************************************************************** + * Name: stm32_takesem + * + * Description: + * This is just a wrapper to handle the annoying behavior of semaphore + * waits that return due to the receipt of a signal. + * + ****************************************************************************/ + +static void stm32_takesem(sem_t *sem) +{ + /* Take the semaphore (perhaps waiting) */ + + while (sem_wait(sem) != 0) + { + /* The only case that an error should occr here is if the wait was + * awakened by a signal. + */ + + ASSERT(errno == EINTR); + } +} + +/**************************************************************************** + * Name: stm32_getle16 + * + * Description: + * Get a (possibly unaligned) 16-bit little endian value. + * + ****************************************************************************/ + +static inline uint16_t stm32_getle16(const uint8_t *val) +{ + return (uint16_t)val[1] << 8 | (uint16_t)val[0]; +} + +/**************************************************************************** + * Name: stm32_chan_alloc + * + * Description: + * Allocate a channel. + * + ****************************************************************************/ + +static int stm32_chan_alloc(FAR struct stm32_usbhost_s *priv) +{ + int chidx; + + /* Search the table of channels */ + + for (chidx = 0; chidx < STM32_NHOST_CHANNELS; chidx++) + { + /* Is this channel available? */ + + if (!priv->chan[chidx].inuse) + { + /* Yes... make it "in use" and return the index */ + + priv->chan[chidx].inuse = true; + return chidx; + } + } + + /* All of the channels are "in-use" */ + + return -EBUSY; +} + +/**************************************************************************** + * Name: stm32_chan_free + * + * Description: + * Free a previoiusly allocated channel. + * + ****************************************************************************/ + +static void stm32_chan_free(FAR struct stm32_usbhost_s *priv, int chidx) +{ + DEBUGASSERT((unsigned)chidx < STM32_NHOST_CHANNELS); + + /* Halt the channel */ + + stm32_chan_halt(priv, chidx, CHREASON_FREED); + + /* Mark the channel available */ + + priv->chan[chidx].inuse = false; +} + +/**************************************************************************** + * Name: stm32_chan_freeall + * + * Description: + * Free all channels. + * + ****************************************************************************/ + +static inline void stm32_chan_freeall(FAR struct stm32_usbhost_s *priv) +{ + uint8_t chidx; + + /* Free all host channels */ + + for (chidx = 2; chidx < STM32_NHOST_CHANNELS; chidx ++) + { + stm32_chan_free(priv, chidx); + } +} + +/**************************************************************************** + * Name: stm32_chan_configure + * + * Description: + * Configure or re-configure a host channel. Host channels are configured + * when endpoint is allocated and EP0 (only) is re-configured with the + * max packet size or device address changes. + * + ****************************************************************************/ + +static void stm32_chan_configure(FAR struct stm32_usbhost_s *priv, int chidx) +{ + FAR struct stm32_chan_s *chan = &priv->chan[chidx]; + uint32_t regval; + + /* Clear any old pending interrupts for this host channel. */ + + stm32_putreg(STM32_OTG_HCINT(chidx), 0xffffffff); + + /* Enable channel interrupts required for transfers on this channel. */ + + regval = 0; + + switch (chan->eptype) + { + case OTG_EPTYPE_CTRL: + case OTG_EPTYPE_BULK: + { +#ifdef HAVE_USBHOST_TRACE_VERBOSE + uint16_t intrace; + uint16_t outtrace; + + /* Determine the definitive trace ID to use below */ + + if (chan->eptype == OTG_EPTYPE_CTRL) + { + intrace = OTG_VTRACE2_CHANCONF_CTRL_IN; + outtrace = OTG_VTRACE2_CHANCONF_CTRL_OUT; + } + else + { + intrace = OTG_VTRACE2_CHANCONF_BULK_IN; + outtrace = OTG_VTRACE2_CHANCONF_BULK_OUT; + } +#endif + + /* Interrupts required for CTRL and BULK endpoints */ + + regval |= (OTG_HCINT_XFRC | OTG_HCINT_STALL | OTG_HCINT_NAK | + OTG_HCINT_TXERR | OTG_HCINT_DTERR); + + /* Additional setting for IN/OUT endpoints */ + + if (chan->in) + { + usbhost_vtrace2(intrace, chidx, chan->epno); + regval |= OTG_HCINT_BBERR; + } + else + { + usbhost_vtrace2(outtrace, chidx, chan->epno); + regval |= OTG_HCINT_NYET; + } + } + break; + + case OTG_EPTYPE_INTR: + { + /* Interrupts required for INTR endpoints */ + + regval |= (OTG_HCINT_XFRC | OTG_HCINT_STALL | OTG_HCINT_NAK | + OTG_HCINT_TXERR | OTG_HCINT_FRMOR | OTG_HCINT_DTERR); + + /* Additional setting for IN endpoints */ + + if (chan->in) + { + usbhost_vtrace2(OTG_VTRACE2_CHANCONF_INTR_IN, chidx, + chan->epno); + regval |= OTG_HCINT_BBERR; + } +#ifdef HAVE_USBHOST_TRACE_VERBOSE + else + { + usbhost_vtrace2(OTG_VTRACE2_CHANCONF_INTR_OUT, chidx, + chan->epno); + } +#endif + } + break; + + case OTG_EPTYPE_ISOC: + { + /* Interrupts required for ISOC endpoints */ + + regval |= (OTG_HCINT_XFRC | OTG_HCINT_ACK | OTG_HCINT_FRMOR); + + /* Additional setting for IN endpoints */ + + if (chan->in) + { + usbhost_vtrace2(OTG_VTRACE2_CHANCONF_ISOC_IN, chidx, + chan->epno); + regval |= (OTG_HCINT_TXERR | OTG_HCINT_BBERR); + } +#ifdef HAVE_USBHOST_TRACE_VERBOSE + else + { + usbhost_vtrace2(OTG_VTRACE2_CHANCONF_ISOC_OUT, chidx, + chan->epno); + } +#endif + } + break; + } + + stm32_putreg(STM32_OTG_HCINTMSK(chidx), regval); + + /* Enable the top level host channel interrupt. */ + + stm32_modifyreg(STM32_OTG_HAINTMSK, 0, OTG_HAINT(chidx)); + + /* Make sure host channel interrupts are enabled. */ + + stm32_modifyreg(STM32_OTG_GINTMSK, 0, OTG_GINT_HC); + + /* Program the HCCHAR register */ + + regval = ((uint32_t)chan->maxpacket << OTG_HCCHAR_MPSIZ_SHIFT) | + ((uint32_t)chan->epno << OTG_HCCHAR_EPNUM_SHIFT) | + ((uint32_t)chan->eptype << OTG_HCCHAR_EPTYP_SHIFT) | + ((uint32_t)chan->funcaddr << OTG_HCCHAR_DAD_SHIFT); + + /* Special case settings for low speed devices */ + + if (chan->speed == USB_SPEED_LOW) + { + regval |= OTG_HCCHAR_LSDEV; + } + + /* Special case settings for IN endpoints */ + + if (chan->in) + { + regval |= OTG_HCCHAR_EPDIR_IN; + } + + /* Special case settings for INTR endpoints */ + + if (chan->eptype == OTG_EPTYPE_INTR) + { + regval |= OTG_HCCHAR_ODDFRM; + } + + /* Write the channel configuration */ + + stm32_putreg(STM32_OTG_HCCHAR(chidx), regval); +} + +/**************************************************************************** + * Name: stm32_chan_halt + * + * Description: + * Halt the channel associated with 'chidx' by setting the CHannel DISable + * (CHDIS) bit in in the HCCHAR register. + * + ****************************************************************************/ + +static void stm32_chan_halt(FAR struct stm32_usbhost_s *priv, int chidx, + enum stm32_chreason_e chreason) +{ + uint32_t hcchar; + uint32_t intmsk; + uint32_t eptype; + unsigned int avail; + + /* Save the reason for the halt. We need this in the channel halt interrupt + * handling logic to know what to do next. + */ + + usbhost_vtrace2(OTG_VTRACE2_CHANHALT, chidx, chreason); + + priv->chan[chidx].chreason = (uint8_t)chreason; + + /* "The application can disable any channel by programming the OTG_FS_HCCHARx + * register with the CHDIS and CHENA bits set to 1. This enables the OTG_FS + * host to flush the posted requests (if any) and generates a channel halted + * interrupt. The application must wait for the CHH interrupt in OTG_FS_HCINTx + * before reallocating the channel for other transactions. The OTG_FS host + * does not interrupt the transaction that has already been started on the + * USB." + */ + + hcchar = stm32_getreg(STM32_OTG_HCCHAR(chidx)); + hcchar |= (OTG_HCCHAR_CHDIS | OTG_HCCHAR_CHENA); + + /* Get the endpoint type from the HCCHAR register */ + + eptype = hcchar & OTG_HCCHAR_EPTYP_MASK; + + /* Check for space in the Tx FIFO to issue the halt. + * + * "Before disabling a channel, the application must ensure that there is at + * least one free space available in the non-periodic request queue (when + * disabling a non-periodic channel) or the periodic request queue (when + * disabling a periodic channel). The application can simply flush the + * posted requests when the Request queue is full (before disabling the + * channel), by programming the OTG_FS_HCCHARx register with the CHDIS bit + * set to 1, and the CHENA bit cleared to 0. + */ + + if (eptype == OTG_HCCHAR_EPTYP_CTRL || eptype == OTG_HCCHAR_EPTYP_BULK) + { + /* Get the number of words available in the non-periodic Tx FIFO. */ + + avail = stm32_getreg(STM32_OTG_HNPTXSTS) & OTG_HNPTXSTS_NPTXFSAV_MASK; + } + else /* if (eptype == OTG_HCCHAR_EPTYP_ISOC || eptype == OTG_HCCHAR_EPTYP_INTR) */ + { + /* Get the number of words available in the non-periodic Tx FIFO. */ + + avail = stm32_getreg(STM32_OTG_HPTXSTS) & OTG_HPTXSTS_PTXFSAVL_MASK; + } + + /* Check if there is any space available in the Tx FIFO. */ + + if (avail == 0) + { + /* The Tx FIFO is full... disable the channel to flush the requests */ + + hcchar &= ~OTG_HCCHAR_CHENA; + } + + /* Unmask the CHannel Halted (CHH) interrupt */ + + intmsk = stm32_getreg(STM32_OTG_HCINTMSK(chidx)); + intmsk |= OTG_HCINT_CHH; + stm32_putreg(STM32_OTG_HCINTMSK(chidx), intmsk); + + /* Halt the channel by setting CHDIS (and maybe CHENA) in the HCCHAR */ + + stm32_putreg(STM32_OTG_HCCHAR(chidx), hcchar); +} + +/**************************************************************************** + * Name: stm32_chan_waitsetup + * + * Description: + * Set the request for the transfer complete event well BEFORE enabling the + * transfer (as soon as we are absolutely committed to the to avoid transfer). + * We do this to minimize race conditions. This logic would have to be expanded + * if we want to have more than one packet in flight at a time! + * + * Assumptions: + * Called from a normal thread context BEFORE the transfer has been started. + * + ****************************************************************************/ + +static int stm32_chan_waitsetup(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_chan_s *chan) +{ + irqstate_t flags = enter_critical_section(); + int ret = -ENODEV; + + /* Is the device still connected? */ + + if (priv->connected) + { + /* Yes.. then set waiter to indicate that we expect to be informed when + * either (1) the device is disconnected, or (2) the transfer completed. + */ + + chan->waiter = true; +#ifdef CONFIG_USBHOST_ASYNCH + chan->callback = NULL; + chan->arg = NULL; +#endif + ret = OK; + } + + leave_critical_section(flags); + return ret; +} + +/**************************************************************************** + * Name: stm32_chan_asynchsetup + * + * Description: + * Set the request for the transfer complete event well BEFORE enabling the + * transfer (as soon as we are absolutely committed to the to avoid transfer). + * We do this to minimize race conditions. This logic would have to be expanded + * if we want to have more than one packet in flight at a time! + * + * Assumptions: + * Might be called from the level of an interrupt handler + * + ****************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static int stm32_chan_asynchsetup(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_chan_s *chan, + usbhost_asynch_t callback, FAR void *arg) +{ + irqstate_t flags = enter_critical_section(); + int ret = -ENODEV; + + /* Is the device still connected? */ + + if (priv->connected) + { + /* Yes.. then set waiter to indicate that we expect to be informed when + * either (1) the device is disconnected, or (2) the transfer completed. + */ + + chan->waiter = false; + chan->callback = callback; + chan->arg = arg; + ret = OK; + } + + leave_critical_section(flags); + return ret; +} +#endif + +/**************************************************************************** + * Name: stm32_chan_wait + * + * Description: + * Wait for a transfer on a channel to complete. + * + * Assumptions: + * Called from a normal thread context + * + ****************************************************************************/ + +static int stm32_chan_wait(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_chan_s *chan) +{ + irqstate_t flags; + int ret; + + /* Disable interrupts so that the following operations will be atomic. On + * the OTG FS global interrupt needs to be disabled. However, here we disable + * all interrupts to exploit that fact that interrupts will be re-enabled + * while we wait. + */ + + flags = enter_critical_section(); + + /* Loop, testing for an end of transfer condition. The channel 'result' + * was set to EBUSY and 'waiter' was set to true before the transfer; 'waiter' + * will be set to false and 'result' will be set appropriately when the + * transfer is completed. + */ + + do + { + /* Wait for the transfer to complete. NOTE the transfer may already + * completed before we get here or the transfer may complete while we + * wait here. + */ + + ret = sem_wait(&chan->waitsem); + + /* sem_wait should succeed. But it is possible that we could be + * awakened by a signal too. + */ + + DEBUGASSERT(ret == OK || get_errno() == EINTR); + } + while (chan->waiter); + + /* The transfer is complete re-enable interrupts and return the result */ + + ret = -(int)chan->result; + leave_critical_section(flags); + return ret; +} + +/**************************************************************************** + * Name: stm32_chan_wakeup + * + * Description: + * A channel transfer has completed... wakeup any threads waiting for the + * transfer to complete. + * + * Assumptions: + * This function is called from the transfer complete interrupt handler for + * the channel. Interrupts are disabled. + * + ****************************************************************************/ + +static void stm32_chan_wakeup(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_chan_s *chan) +{ + /* Is the transfer complete? */ + + if (chan->result != EBUSY) + { + /* Is there a thread waiting for this transfer to complete? */ + + if (chan->waiter) + { +#ifdef CONFIG_USBHOST_ASYNCH + /* Yes.. there should not also be a callback scheduled */ + + DEBUGASSERT(chan->callback == NULL); +#endif + /* Wake'em up! */ + + usbhost_vtrace2(chan->in ? OTG_VTRACE2_CHANWAKEUP_IN : + OTG_VTRACE2_CHANWAKEUP_OUT, + chan->epno, chan->result); + + stm32_givesem(&chan->waitsem); + chan->waiter = false; + } + +#ifdef CONFIG_USBHOST_ASYNCH + /* No.. is an asynchronous callback expected when the transfer + * completes? + */ + + else if (chan->callback) + { + /* Handle continuation of IN/OUT pipes */ + + if (chan->in) + { + stm32_in_next(priv, chan); + } + else + { + stm32_out_next(priv, chan); + } + } +#endif + } +} + +/**************************************************************************** + * Name: stm32_ctrlchan_alloc + * + * Description: + * Allocate and configured channels for a control pipe. + * + ****************************************************************************/ + +static int stm32_ctrlchan_alloc(FAR struct stm32_usbhost_s *priv, + uint8_t epno, uint8_t funcaddr, uint8_t speed, + FAR struct stm32_ctrlinfo_s *ctrlep) +{ + FAR struct stm32_chan_s *chan; + int inndx; + int outndx; + + outndx = stm32_chan_alloc(priv); + if (outndx < 0) + { + return -ENOMEM; + } + + ctrlep->outndx = outndx; + chan = &priv->chan[outndx]; + chan->epno = epno; + chan->in = false; + chan->eptype = OTG_EPTYPE_CTRL; + chan->funcaddr = funcaddr; + chan->speed = speed; + chan->maxpacket = STM32_EP0_DEF_PACKET_SIZE; + chan->indata1 = false; + chan->outdata1 = false; + + /* Configure control OUT channels */ + + stm32_chan_configure(priv, outndx); + + /* Allocate and initialize the control IN channel */ + + inndx = stm32_chan_alloc(priv); + if (inndx < 0) + { + stm32_chan_free(priv, outndx); + return -ENOMEM; + } + + ctrlep->inndx = inndx; + chan = &priv->chan[inndx]; + chan->epno = epno; + chan->in = true; + chan->eptype = OTG_EPTYPE_CTRL; + chan->funcaddr = funcaddr; + chan->speed = speed; + chan->maxpacket = STM32_EP0_DEF_PACKET_SIZE; + chan->indata1 = false; + chan->outdata1 = false; + + /* Configure control IN channels */ + + stm32_chan_configure(priv, inndx); + return OK; +} + +/**************************************************************************** + * Name: stm32_ctrlep_alloc + * + * Description: + * Allocate a container and channels for control pipe. + * + * Input Parameters: + * priv - The private USB host driver state. + * epdesc - Describes the endpoint to be allocated. + * ep - A memory location provided by the caller in which to receive the + * allocated endpoint descriptor. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ****************************************************************************/ + +static int stm32_ctrlep_alloc(FAR struct stm32_usbhost_s *priv, + FAR const struct usbhost_epdesc_s *epdesc, + FAR usbhost_ep_t *ep) +{ + FAR struct usbhost_hubport_s *hport; + FAR struct stm32_ctrlinfo_s *ctrlep; + int ret; + + /* Sanity check. NOTE that this method should only be called if a device is + * connected (because we need a valid low speed indication). + */ + + DEBUGASSERT(epdesc->hport != NULL); + hport = epdesc->hport; + + /* Allocate a container for the control endpoint */ + + ctrlep = (FAR struct stm32_ctrlinfo_s *)kmm_malloc(sizeof(struct stm32_ctrlinfo_s)); + if (ctrlep == NULL) + { + uerr("ERROR: Failed to allocate control endpoint container\n"); + return -ENOMEM; + } + + /* Then allocate and configure the IN/OUT channnels */ + + ret = stm32_ctrlchan_alloc(priv, epdesc->addr & USB_EPNO_MASK, + hport->funcaddr, hport->speed, ctrlep); + if (ret < 0) + { + uerr("ERROR: stm32_ctrlchan_alloc failed: %d\n", ret); + kmm_free(ctrlep); + return ret; + } + + /* Return a pointer to the control pipe container as the pipe "handle" */ + + *ep = (usbhost_ep_t)ctrlep; + return OK; +} + +/************************************************************************************ + * Name: stm32_xfrep_alloc + * + * Description: + * Allocate and configure one unidirectional endpoint. + * + * Input Parameters: + * priv - The private USB host driver state. + * epdesc - Describes the endpoint to be allocated. + * ep - A memory location provided by the caller in which to receive the + * allocated endpoint descriptor. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ************************************************************************************/ + +static int stm32_xfrep_alloc(FAR struct stm32_usbhost_s *priv, + FAR const struct usbhost_epdesc_s *epdesc, + FAR usbhost_ep_t *ep) +{ + struct usbhost_hubport_s *hport; + FAR struct stm32_chan_s *chan; + int chidx; + + /* Sanity check. NOTE that this method should only be called if a device is + * connected (because we need a valid low speed indication). + */ + + DEBUGASSERT(epdesc->hport != NULL); + hport = epdesc->hport; + + /* Allocate a host channel for the endpoint */ + + chidx = stm32_chan_alloc(priv); + if (chidx < 0) + { + uerr("ERROR: Failed to allocate a host channel\n"); + return -ENOMEM; + } + + /* Decode the endpoint descriptor to initialize the channel data structures. + * Note: Here we depend on the fact that the endpoint point type is + * encoded in the same way in the endpoint descriptor as it is in the OTG + * HS hardware. + */ + + chan = &priv->chan[chidx]; + chan->epno = epdesc->addr & USB_EPNO_MASK; + chan->in = epdesc->in; + chan->eptype = epdesc->xfrtype; + chan->funcaddr = hport->funcaddr; + chan->speed = hport->speed; + chan->maxpacket = epdesc->mxpacketsize; + chan->indata1 = false; + chan->outdata1 = false; + + /* Then configure the endpoint */ + + stm32_chan_configure(priv, chidx); + + /* Return the index to the allocated channel as the endpoint "handle" */ + + *ep = (usbhost_ep_t)chidx; + return OK; +} + +/**************************************************************************** + * Name: stm32_transfer_start + * + * Description: + * Start at transfer on the select IN or OUT channel. + * + ****************************************************************************/ + +static void stm32_transfer_start(FAR struct stm32_usbhost_s *priv, int chidx) +{ + FAR struct stm32_chan_s *chan; + uint32_t regval; + unsigned int npackets; + unsigned int maxpacket; + unsigned int avail; + unsigned int wrsize; + unsigned int minsize; + + /* Set up the initial state of the transfer */ + + chan = &priv->chan[chidx]; + + usbhost_vtrace2(OTG_VTRACE2_STARTTRANSFER, chidx, chan->buflen); + + chan->result = EBUSY; + chan->inflight = 0; + chan->xfrd = 0; + priv->chidx = chidx; + + /* Compute the expected number of packets associated to the transfer. + * If the transfer length is zero (or less than the size of one maximum + * size packet), then one packet is expected. + */ + + /* If the transfer size is greater than one packet, then calculate the + * number of packets that will be received/sent, including any partial + * final packet. + */ + + maxpacket = chan->maxpacket; + + if (chan->buflen > maxpacket) + { + npackets = (chan->buflen + maxpacket - 1) / maxpacket; + + /* Clip if the buffer length if it exceeds the maximum number of + * packets that can be transferred (this should not happen). + */ + + if (npackets > STM32_MAX_PKTCOUNT) + { + npackets = STM32_MAX_PKTCOUNT; + chan->buflen = STM32_MAX_PKTCOUNT * maxpacket; + usbhost_trace2(OTG_TRACE2_CLIP, chidx, chan->buflen); + } + } + else + { + /* One packet will be sent/received (might be a zero length packet) */ + + npackets = 1; + } + + /* If it is an IN transfer, then adjust the size of the buffer UP to + * a full number of packets. Hmmm... couldn't this cause an overrun + * into unallocated memory? + */ + +#if 0 /* Think about this */ + if (chan->in) + { + /* Force the buffer length to an even multiple of maxpacket */ + + chan->buflen = npackets * maxpacket; + } +#endif + + /* Save the number of packets in the transfer. We will need this in + * order to set the next data toggle correctly when the transfer + * completes. + */ + + chan->npackets = (uint8_t)npackets; + + /* Setup the HCTSIZn register */ + + regval = ((uint32_t)chan->buflen << OTG_HCTSIZ_XFRSIZ_SHIFT) | + ((uint32_t)npackets << OTG_HCTSIZ_PKTCNT_SHIFT) | + ((uint32_t)chan->pid << OTG_HCTSIZ_DPID_SHIFT); + stm32_putreg(STM32_OTG_HCTSIZ(chidx), regval); + + /* Setup the HCCHAR register: Frame oddness and host channel enable */ + + regval = stm32_getreg(STM32_OTG_HCCHAR(chidx)); + + /* Set/clear the Odd Frame bit. Check for an even frame; if so set Odd + * Frame. This field is applicable for only periodic (isochronous and + * interrupt) channels. + */ + + if ((stm32_getreg(STM32_OTG_HFNUM) & 1) == 0) + { + regval |= OTG_HCCHAR_ODDFRM; + } + else + { + regval &= ~OTG_HCCHAR_ODDFRM; + } + + regval &= ~OTG_HCCHAR_CHDIS; + regval |= OTG_HCCHAR_CHENA; + stm32_putreg(STM32_OTG_HCCHAR(chidx), regval); + + /* If this is an out transfer, then we need to do more.. we need to copy + * the outgoing data into the correct TxFIFO. + */ + + if (!chan->in && chan->buflen > 0) + { + /* Handle non-periodic (CTRL and BULK) OUT transfers differently than + * periodic (INTR and ISOC) OUT transfers. + */ + + minsize = MIN(chan->buflen, chan->maxpacket); + + switch (chan->eptype) + { + case OTG_EPTYPE_CTRL: /* Non periodic transfer */ + case OTG_EPTYPE_BULK: + { + /* Read the Non-periodic Tx FIFO status register */ + + regval = stm32_getreg(STM32_OTG_HNPTXSTS); + avail = ((regval & OTG_HNPTXSTS_NPTXFSAV_MASK) >> OTG_HNPTXSTS_NPTXFSAV_SHIFT) << 2; + } + break; + + /* Periodic transfer */ + + case OTG_EPTYPE_INTR: + case OTG_EPTYPE_ISOC: + { + /* Read the Non-periodic Tx FIFO status register */ + + regval = stm32_getreg(STM32_OTG_HPTXSTS); + avail = ((regval & OTG_HPTXSTS_PTXFSAVL_MASK) >> OTG_HPTXSTS_PTXFSAVL_SHIFT) << 2; + } + break; + + default: + DEBUGASSERT(false); + return; + } + + /* Is there space in the TxFIFO to hold the minimum size packet? */ + + if (minsize <= avail) + { + /* Yes.. Get the size of the biggest thing that we can put in the Tx FIFO now */ + + wrsize = chan->buflen; + if (wrsize > avail) + { + /* Clip the write size to the number of full, max sized packets + * that will fit in the Tx FIFO. + */ + + unsigned int wrpackets = avail / chan->maxpacket; + wrsize = wrpackets * chan->maxpacket; + } + + /* Write packet into the Tx FIFO. */ + + stm32_gint_wrpacket(priv, chan->buffer, chidx, wrsize); + } + + /* Did we put the entire buffer into the Tx FIFO? */ + + if (chan->buflen > avail) + { + /* No, there was insufficient space to hold the entire transfer ... + * Enable the Tx FIFO interrupt to handle the transfer when the Tx + * FIFO becomes empty. + */ + + stm32_txfe_enable(priv, chidx); + } + } +} + +/**************************************************************************** + * Name: stm32_getframe + * + * Description: + * Get the current frame number. The frame number (FRNUM) field increments + * when a new SOF is transmitted on the USB, and is cleared to 0 when it + * reaches 0x3fff. + * + ****************************************************************************/ + +#if 0 /* Not used */ +static inline uint16_t stm32_getframe(void) +{ + return (uint16_t)(stm32_getreg(STM32_OTG_HFNUM) & OTG_HFNUM_FRNUM_MASK); +} +#endif + +/**************************************************************************** + * Name: stm32_ctrl_sendsetup + * + * Description: + * Send an IN/OUT SETUP packet. + * + ****************************************************************************/ + +static int stm32_ctrl_sendsetup(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_ctrlinfo_s *ep0, + FAR const struct usb_ctrlreq_s *req) +{ + FAR struct stm32_chan_s *chan; + systime_t start; + systime_t elapsed; + int ret; + + /* Loop while the device reports NAK (and a timeout is not exceeded */ + + chan = &priv->chan[ep0->outndx]; + start = clock_systimer(); + + do + { + /* Send the SETUP packet */ + + chan->pid = OTG_PID_SETUP; + chan->buffer = (FAR uint8_t *)req; + chan->buflen = USB_SIZEOF_CTRLREQ; + chan->xfrd = 0; + + /* Set up for the wait BEFORE starting the transfer */ + + ret = stm32_chan_waitsetup(priv, chan); + if (ret < 0) + { + usbhost_trace1(OTG_TRACE1_DEVDISCONN, 0); + return ret; + } + + /* Start the transfer */ + + stm32_transfer_start(priv, ep0->outndx); + + /* Wait for the transfer to complete */ + + ret = stm32_chan_wait(priv, chan); + + /* Return on success and for all failures other than EAGAIN. EAGAIN + * means that the device NAKed the SETUP command and that we should + * try a few more times. + */ + + if (ret != -EAGAIN) + { + /* Output some debug information if the transfer failed */ + + if (ret < 0) + { + usbhost_trace1(OTG_TRACE1_TRNSFRFAILED, ret); + } + + /* Return the result in any event */ + + return ret; + } + + /* Get the elapsed time (in frames) */ + + elapsed = clock_systimer() - start; + } + while (elapsed < STM32_SETUP_DELAY); + + return -ETIMEDOUT; +} + +/**************************************************************************** + * Name: stm32_ctrl_senddata + * + * Description: + * Send data in the data phase of an OUT control transfer. Or send status + * in the status phase of an IN control transfer + * + ****************************************************************************/ + +static int stm32_ctrl_senddata(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_ctrlinfo_s *ep0, + FAR uint8_t *buffer, unsigned int buflen) +{ + FAR struct stm32_chan_s *chan = &priv->chan[ep0->outndx]; + int ret; + + /* Save buffer information */ + + chan->buffer = buffer; + chan->buflen = buflen; + chan->xfrd = 0; + + /* Set the DATA PID */ + + if (buflen == 0) + { + /* For status OUT stage with buflen == 0, set PID DATA1 */ + + chan->outdata1 = true; + } + + /* Set the Data PID as per the outdata1 boolean */ + + chan->pid = chan->outdata1 ? OTG_PID_DATA1 : OTG_PID_DATA0; + + /* Set up for the wait BEFORE starting the transfer */ + + ret = stm32_chan_waitsetup(priv, chan); + if (ret < 0) + { + usbhost_trace1(OTG_TRACE1_DEVDISCONN, 0); + return ret; + } + + /* Start the transfer */ + + stm32_transfer_start(priv, ep0->outndx); + + /* Wait for the transfer to complete and return the result */ + + return stm32_chan_wait(priv, chan); +} + +/**************************************************************************** + * Name: stm32_ctrl_recvdata + * + * Description: + * Receive data in the data phase of an IN control transfer. Or receive status + * in the status phase of an OUT control transfer + * + ****************************************************************************/ + +static int stm32_ctrl_recvdata(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_ctrlinfo_s *ep0, + FAR uint8_t *buffer, unsigned int buflen) +{ + FAR struct stm32_chan_s *chan = &priv->chan[ep0->inndx]; + int ret; + + /* Save buffer information */ + + chan->pid = OTG_PID_DATA1; + chan->buffer = buffer; + chan->buflen = buflen; + chan->xfrd = 0; + + /* Set up for the wait BEFORE starting the transfer */ + + ret = stm32_chan_waitsetup(priv, chan); + if (ret < 0) + { + usbhost_trace1(OTG_TRACE1_DEVDISCONN, 0); + return ret; + } + + /* Start the transfer */ + + stm32_transfer_start(priv, ep0->inndx); + + /* Wait for the transfer to complete and return the result */ + + return stm32_chan_wait(priv, chan); +} + +/**************************************************************************** + * Name: stm32_in_setup + * + * Description: + * Initiate an IN transfer on an bulk, interrupt, or isochronous pipe. + * + ****************************************************************************/ + +static int stm32_in_setup(FAR struct stm32_usbhost_s *priv, int chidx) +{ + FAR struct stm32_chan_s *chan; + + /* Set up for the transfer based on the direction and the endpoint type */ + + chan = &priv->chan[chidx]; + switch (chan->eptype) + { + default: + case OTG_EPTYPE_CTRL: /* Control */ + { + /* This kind of transfer on control endpoints other than EP0 are not + * currently supported + */ + + return -ENOSYS; + } + + case OTG_EPTYPE_ISOC: /* Isochronous */ + { + /* Set up the IN data PID */ + + usbhost_vtrace2(OTG_VTRACE2_ISOCIN, chidx, chan->buflen); + chan->pid = OTG_PID_DATA0; + } + break; + + case OTG_EPTYPE_BULK: /* Bulk */ + { + /* Setup the IN data PID */ + + usbhost_vtrace2(OTG_VTRACE2_BULKIN, chidx, chan->buflen); + chan->pid = chan->indata1 ? OTG_PID_DATA1 : OTG_PID_DATA0; + } + break; + + case OTG_EPTYPE_INTR: /* Interrupt */ + { + /* Setup the IN data PID */ + + usbhost_vtrace2(OTG_VTRACE2_INTRIN, chidx, chan->buflen); + chan->pid = chan->indata1 ? OTG_PID_DATA1 : OTG_PID_DATA0; + } + break; + } + + /* Start the transfer */ + + stm32_transfer_start(priv, chidx); + return OK; +} + +/**************************************************************************** + * Name: stm32_in_transfer + * + * Description: + * Transfer 'buflen' bytes into 'buffer' from an IN channel. + * + ****************************************************************************/ + +static ssize_t stm32_in_transfer(FAR struct stm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen) +{ + FAR struct stm32_chan_s *chan; + systime_t start; + systime_t elapsed; + int ret; + + /* Loop until the transfer completes (i.e., buflen is decremented to zero) + * or a fatal error occurs (any error other than a simple NAK) + */ + + chan = &priv->chan[chidx]; + chan->buffer = buffer; + chan->buflen = buflen; + chan->xfrd = 0; + + start = clock_systimer(); + while (chan->xfrd < chan->buflen) + { + /* Set up for the wait BEFORE starting the transfer */ + + ret = stm32_chan_waitsetup(priv, chan); + if (ret < 0) + { + usbhost_trace1(OTG_TRACE1_DEVDISCONN, 0); + return (ssize_t)ret; + } + + /* Set up for the transfer based on the direction and the endpoint type */ + + ret = stm32_in_setup(priv, chidx); + if (ret < 0) + { + uerr("ERROR: stm32_in_setup failed: %d\n", ret); + return (ssize_t)ret; + } + + /* Wait for the transfer to complete and get the result */ + + ret = stm32_chan_wait(priv, chan); + + /* EAGAIN indicates that the device NAKed the transfer and we need + * do try again. Anything else (success or other errors) will + * cause use to return + */ + + if (ret < 0) + { + usbhost_trace1(OTG_TRACE1_TRNSFRFAILED, ret); + + /* Check for a special case: If (1) the transfer was NAKed and (2) + * no Tx FIFO empty or Rx FIFO not-empty event occurred, then we + * should be able to just flush the Rx and Tx FIFOs and try again. + * We can detect this latter case because the then the transfer + * buffer pointer and buffer size will be unaltered. + */ + + elapsed = clock_systimer() - start; + if (ret != -EAGAIN || /* Not a NAK condition OR */ + elapsed >= STM32_DATANAK_DELAY || /* Timeout has elapsed OR */ + chan->xfrd > 0) /* Data has been partially transferred */ + { + /* Break out and return the error */ + + uerr("ERROR: stm32_chan_wait failed: %d\n", ret); + return (ssize_t)ret; + } + } + } + + return (ssize_t)chan->xfrd; +} + +/**************************************************************************** + * Name: stm32_in_next + * + * Description: + * Initiate the next of a sequence of asynchronous transfers. + * + * Assumptions: + * This function is always called from an interrupt handler + * + ****************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static void stm32_in_next(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_chan_s *chan) +{ + usbhost_asynch_t callback; + FAR void *arg; + ssize_t nbytes; + int result; + int ret; + + /* Is the full transfer complete? Did the last chunk transfer complete OK? */ + + result = -(int)chan->result; + if (chan->xfrd < chan->buflen && result == OK) + { + /* Yes.. Set up for the next transfer based on the direction and the + * endpoint type + */ + + ret = stm32_in_setup(priv, chan->chidx); + if (ret >= 0) + { + return; + } + + uerr("ERROR: stm32_in_setup failed: %d\n", ret); + result = ret; + } + + /* The transfer is complete, with or without an error */ + + uinfo("Transfer complete: %d\n", result); + + /* Extract the callback information */ + + callback = chan->callback; + arg = chan->arg; + nbytes = chan->xfrd; + + chan->callback = NULL; + chan->arg = NULL; + chan->xfrd = 0; + + /* Then perform the callback */ + + if (result < 0) + { + nbytes = (ssize_t)result; + } + + callback(arg, nbytes); +} +#endif + +/**************************************************************************** + * Name: stm32_in_asynch + * + * Description: + * Initiate the first of a sequence of asynchronous transfers. + * + * Assumptions: + * This function is never called from an interrupt handler + * + ****************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static int stm32_in_asynch(FAR struct stm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen, + usbhost_asynch_t callback, FAR void *arg) +{ + FAR struct stm32_chan_s *chan; + int ret; + + /* Set up for the transfer data and callback BEFORE starting the first transfer */ + + chan = &priv->chan[chidx]; + chan->buffer = buffer; + chan->buflen = buflen; + chan->xfrd = 0; + + ret = stm32_chan_asynchsetup(priv, chan, callback, arg); + if (ret < 0) + { + uerr("ERROR: stm32_chan_asynchsetup failed: %d\n", ret); + return ret; + } + + /* Set up for the transfer based on the direction and the endpoint type */ + + ret = stm32_in_setup(priv, chidx); + if (ret < 0) + { + uerr("ERROR: stm32_in_setup failed: %d\n", ret); + } + + /* And return with the transfer pending */ + + return ret; +} +#endif + +/**************************************************************************** + * Name: stm32_out_setup + * + * Description: + * Initiate an OUT transfer on an bulk, interrupt, or isochronous pipe. + * + ****************************************************************************/ + +static int stm32_out_setup(FAR struct stm32_usbhost_s *priv, int chidx) +{ + FAR struct stm32_chan_s *chan; + + /* Set up for the transfer based on the direction and the endpoint type */ + + chan = &priv->chan[chidx]; + switch (chan->eptype) + { + default: + case OTG_EPTYPE_CTRL: /* Control */ + { + /* This kind of transfer on control endpoints other than EP0 are not + * currently supported + */ + + return -ENOSYS; + } + + case OTG_EPTYPE_ISOC: /* Isochronous */ + { + /* Set up the OUT data PID */ + + usbhost_vtrace2(OTG_VTRACE2_ISOCOUT, chidx, chan->buflen); + chan->pid = OTG_PID_DATA0; + } + break; + + case OTG_EPTYPE_BULK: /* Bulk */ + { + /* Setup the OUT data PID */ + + usbhost_vtrace2(OTG_VTRACE2_BULKOUT, chidx, chan->buflen); + chan->pid = chan->outdata1 ? OTG_PID_DATA1 : OTG_PID_DATA0; + } + break; + + case OTG_EPTYPE_INTR: /* Interrupt */ + { + /* Setup the OUT data PID */ + + usbhost_vtrace2(OTG_VTRACE2_INTROUT, chidx, chan->buflen); + chan->pid = chan->outdata1 ? OTG_PID_DATA1 : OTG_PID_DATA0; + + /* Toggle the OUT data PID for the next transfer */ + + chan->outdata1 ^= true; + } + break; + } + + /* Start the transfer */ + + stm32_transfer_start(priv, chidx); + return OK; +} + +/**************************************************************************** + * Name: stm32_out_transfer + * + * Description: + * Transfer the 'buflen' bytes in 'buffer' through an OUT channel. + * + ****************************************************************************/ + +static ssize_t stm32_out_transfer(FAR struct stm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen) +{ + FAR struct stm32_chan_s *chan; + systime_t start; + systime_t elapsed; + size_t xfrlen; + ssize_t xfrd; + int ret; + + /* Loop until the transfer completes (i.e., buflen is decremented to zero) + * or a fatal error occurs (any error other than a simple NAK) + */ + + chan = &priv->chan[chidx]; + start = clock_systimer(); + xfrd = 0; + + while (buflen > 0) + { + /* Transfer one packet at a time. The hardware is capable of queueing + * multiple OUT packets, but I just haven't figured out how to handle + * the case where a single OUT packet in the group is NAKed. + */ + + xfrlen = MIN(chan->maxpacket, buflen); + chan->buffer = buffer; + chan->buflen = xfrlen; + chan->xfrd = 0; + + /* Set up for the wait BEFORE starting the transfer */ + + ret = stm32_chan_waitsetup(priv, chan); + if (ret < 0) + { + usbhost_trace1(OTG_TRACE1_DEVDISCONN, 0); + return (ssize_t)ret; + } + + /* Set up for the transfer based on the direction and the endpoint type */ + + ret = stm32_out_setup(priv, chidx); + if (ret < 0) + { + uerr("ERROR: stm32_out_setup failed: %d\n", ret); + return (ssize_t)ret; + } + + /* Wait for the transfer to complete and get the result */ + + ret = stm32_chan_wait(priv, chan); + + /* Handle transfer failures */ + + if (ret < 0) + { + usbhost_trace1(OTG_TRACE1_TRNSFRFAILED, ret); + + /* Check for a special case: If (1) the transfer was NAKed and (2) + * no Tx FIFO empty or Rx FIFO not-empty event occurred, then we + * should be able to just flush the Rx and Tx FIFOs and try again. + * We can detect this latter case because the then the transfer + * buffer pointer and buffer size will be unaltered. + */ + + elapsed = clock_systimer() - start; + if (ret != -EAGAIN || /* Not a NAK condition OR */ + elapsed >= STM32_DATANAK_DELAY || /* Timeout has elapsed OR */ + chan->xfrd > 0) /* Data has been partially transferred */ + { + /* Break out and return the error */ + + uerr("ERROR: stm32_chan_wait failed: %d\n", ret); + return (ssize_t)ret; + } + + /* Is this flush really necessary? What does the hardware do with the + * data in the FIFO when the NAK occurs? Does it discard it? + */ + + stm32_flush_txfifos(OTG_GRSTCTL_TXFNUM_HALL); + + /* Get the device a little time to catch up. Then retry the transfer + * using the same buffer pointer and length. + */ + + usleep(20*1000); + } + else + { + /* Successfully transferred. Update the buffer pointer and length */ + + buffer += xfrlen; + buflen -= xfrlen; + xfrd += chan->xfrd; + } + } + + return xfrd; +} + +/**************************************************************************** + * Name: stm32_out_next + * + * Description: + * Initiate the next of a sequence of asynchronous transfers. + * + * Assumptions: + * This function is always called from an interrupt handler + * + ****************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static void stm32_out_next(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_chan_s *chan) +{ + usbhost_asynch_t callback; + FAR void *arg; + ssize_t nbytes; + int result; + int ret; + + /* Is the full transfer complete? Did the last chunk transfer complete OK? */ + + result = -(int)chan->result; + if (chan->xfrd < chan->buflen && result == OK) + { + /* Yes.. Set up for the next transfer based on the direction and the + * endpoint type + */ + + ret = stm32_out_setup(priv, chan->chidx); + if (ret >= 0) + { + return; + } + + uerr("ERROR: stm32_out_setup failed: %d\n", ret); + result = ret; + } + + /* The transfer is complete, with or without an error */ + + uinfo("Transfer complete: %d\n", result); + + /* Extract the callback information */ + + callback = chan->callback; + arg = chan->arg; + nbytes = chan->xfrd; + + chan->callback = NULL; + chan->arg = NULL; + chan->xfrd = 0; + + /* Then perform the callback */ + + if (result < 0) + { + nbytes = (ssize_t)result; + } + + callback(arg, nbytes); +} +#endif + +/**************************************************************************** + * Name: stm32_out_asynch + * + * Description: + * Initiate the first of a sequence of asynchronous transfers. + * + * Assumptions: + * This function is never called from an interrupt handler + * + ****************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static int stm32_out_asynch(FAR struct stm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen, + usbhost_asynch_t callback, FAR void *arg) +{ + FAR struct stm32_chan_s *chan; + int ret; + + /* Set up for the transfer data and callback BEFORE starting the first transfer */ + + chan = &priv->chan[chidx]; + chan->buffer = buffer; + chan->buflen = buflen; + chan->xfrd = 0; + + ret = stm32_chan_asynchsetup(priv, chan, callback, arg); + if (ret < 0) + { + uerr("ERROR: stm32_chan_asynchsetup failed: %d\n", ret); + return ret; + } + + /* Set up for the transfer based on the direction and the endpoint type */ + + ret = stm32_out_setup(priv, chidx); + if (ret < 0) + { + uerr("ERROR: stm32_out_setup failed: %d\n", ret); + } + + /* And return with the transfer pending */ + + return ret; +} +#endif + +/**************************************************************************** + * Name: stm32_gint_wrpacket + * + * Description: + * Transfer the 'buflen' bytes in 'buffer' to the Tx FIFO associated with + * 'chidx' (non-DMA). + * + ****************************************************************************/ + +static void stm32_gint_wrpacket(FAR struct stm32_usbhost_s *priv, + FAR uint8_t *buffer, int chidx, int buflen) +{ + FAR uint32_t *src; + uint32_t fifo; + int buflen32; + + stm32_pktdump("Sending", buffer, buflen); + + /* Get the number of 32-byte words associated with this byte size */ + + buflen32 = (buflen + 3) >> 2; + + /* Get the address of the Tx FIFO associated with this channel */ + + fifo = STM32_OTG_DFIFO_HCH(chidx); + + /* Transfer all of the data into the Tx FIFO */ + + src = (FAR uint32_t *)buffer; + for (; buflen32 > 0; buflen32--) + { + uint32_t data = *src++; + stm32_putreg(fifo, data); + } + + /* Increment the count of bytes "in-flight" in the Tx FIFO */ + + priv->chan[chidx].inflight += buflen; +} + +/**************************************************************************** + * Name: stm32_gint_hcinisr + * + * Description: + * USB OTG FS host IN channels interrupt handler + * + * One the completion of the transfer, the channel result byte may be set as + * follows: + * + * OK - Transfer completed successfully + * EAGAIN - If devices NAKs the transfer or NYET occurs + * EPERM - If the endpoint stalls + * EIO - On a TX or data toggle error + * EPIPE - Frame overrun + * + * EBUSY in the result field indicates that the transfer has not completed. + * + ****************************************************************************/ + +static inline void stm32_gint_hcinisr(FAR struct stm32_usbhost_s *priv, + int chidx) +{ + FAR struct stm32_chan_s *chan = &priv->chan[chidx]; + uint32_t regval; + uint32_t pending; + + /* Read the HCINT register to get the pending HC interrupts. Read the + * HCINTMSK register to get the set of enabled HC interrupts. + */ + + pending = stm32_getreg(STM32_OTG_HCINT(chidx)); + regval = stm32_getreg(STM32_OTG_HCINTMSK(chidx)); + + /* AND the two to get the set of enabled, pending HC interrupts */ + + pending &= regval; + ullinfo("HCINTMSK%d: %08x pending: %08x\n", chidx, regval, pending); + + /* Check for a pending ACK response received/transmitted (ACK) interrupt */ + + if ((pending & OTG_HCINT_ACK) != 0) + { + /* Clear the pending the ACK response received/transmitted (ACK) interrupt */ + + stm32_putreg(STM32_OTG_HCINT(chidx), OTG_HCINT_ACK); + } + + /* Check for a pending STALL response receive (STALL) interrupt */ + + else if ((pending & OTG_HCINT_STALL) != 0) + { + /* Clear the NAK and STALL Conditions. */ + + stm32_putreg(STM32_OTG_HCINT(chidx), (OTG_HCINT_NAK | OTG_HCINT_STALL)); + + /* Halt the channel when a STALL, TXERR, BBERR or DTERR interrupt is + * received on the channel. + */ + + stm32_chan_halt(priv, chidx, CHREASON_STALL); + + /* When there is a STALL, clear any pending NAK so that it is not + * processed below. + */ + + pending &= ~OTG_HCINT_NAK; + } + + /* Check for a pending Data Toggle ERRor (DTERR) interrupt */ + + else if ((pending & OTG_HCINT_DTERR) != 0) + { + /* Halt the channel when a STALL, TXERR, BBERR or DTERR interrupt is + * received on the channel. + */ + + stm32_chan_halt(priv, chidx, CHREASON_DTERR); + + /* Clear the NAK and data toggle error conditions */ + + stm32_putreg(STM32_OTG_HCINT(chidx), (OTG_HCINT_NAK | OTG_HCINT_DTERR)); + } + + /* Check for a pending FRaMe OverRun (FRMOR) interrupt */ + + if ((pending & OTG_HCINT_FRMOR) != 0) + { + /* Halt the channel -- the CHH interrupt is expected next */ + + stm32_chan_halt(priv, chidx, CHREASON_FRMOR); + + /* Clear the FRaMe OverRun (FRMOR) condition */ + + stm32_putreg(STM32_OTG_HCINT(chidx), OTG_HCINT_FRMOR); + } + + /* Check for a pending TransFeR Completed (XFRC) interrupt */ + + else if ((pending & OTG_HCINT_XFRC) != 0) + { + /* Clear the TransFeR Completed (XFRC) condition */ + + stm32_putreg(STM32_OTG_HCINT(chidx), OTG_HCINT_XFRC); + + /* Then handle the transfer completion event based on the endpoint type */ + + if (chan->eptype == OTG_EPTYPE_CTRL || chan->eptype == OTG_EPTYPE_BULK) + { + /* Halt the channel -- the CHH interrupt is expected next */ + + stm32_chan_halt(priv, chidx, CHREASON_XFRC); + + /* Clear any pending NAK condition. The 'indata1' data toggle + * should have been appropriately updated by the RxFIFO + * logic as each packet was received. + */ + + stm32_putreg(STM32_OTG_HCINT(chidx), OTG_HCINT_NAK); + } + else if (chan->eptype == OTG_EPTYPE_INTR) + { + /* Force the next transfer on an ODD frame */ + + regval = stm32_getreg(STM32_OTG_HCCHAR(chidx)); + regval |= OTG_HCCHAR_ODDFRM; + stm32_putreg(STM32_OTG_HCCHAR(chidx), regval); + + /* Set the request done state */ + + chan->result = OK; + } + } + + /* Check for a pending CHannel Halted (CHH) interrupt */ + + else if ((pending & OTG_HCINT_CHH) != 0) + { + /* Mask the CHannel Halted (CHH) interrupt */ + + regval = stm32_getreg(STM32_OTG_HCINTMSK(chidx)); + regval &= ~OTG_HCINT_CHH; + stm32_putreg(STM32_OTG_HCINTMSK(chidx), regval); + + /* Update the request state based on the host state machine state */ + + if (chan->chreason == CHREASON_XFRC) + { + /* Set the request done result */ + + chan->result = OK; + } + else if (chan->chreason == CHREASON_STALL) + { + /* Set the request stall result */ + + chan->result = EPERM; + } + else if ((chan->chreason == CHREASON_TXERR) || + (chan->chreason == CHREASON_DTERR)) + { + /* Set the request I/O error result */ + + chan->result = EIO; + } + else if (chan->chreason == CHREASON_NAK) + { + /* Halt on NAK only happens on an INTR channel. Fetch the HCCHAR register + * and check for an interrupt endpoint. + */ + + regval = stm32_getreg(STM32_OTG_HCCHAR(chidx)); + if ((regval & OTG_HCCHAR_EPTYP_MASK) == OTG_HCCHAR_EPTYP_INTR) + { + /* Toggle the IN data toggle (Used by Bulk and INTR only) */ + + chan->indata1 ^= true; + } + + /* Set the NAK error result */ + + chan->result = EAGAIN; + } + else /* if (chan->chreason == CHREASON_FRMOR) */ + { + /* Set the frame overrun error result */ + + chan->result = EPIPE; + } + + /* Clear the CHannel Halted (CHH) condition */ + + stm32_putreg(STM32_OTG_HCINT(chidx), OTG_HCINT_CHH); + } + + /* Check for a pending Transaction ERror (TXERR) interrupt */ + + else if ((pending & OTG_HCINT_TXERR) != 0) + { + /* Halt the channel when a STALL, TXERR, BBERR or DTERR interrupt is + * received on the channel. + */ + + stm32_chan_halt(priv, chidx, CHREASON_TXERR); + + /* Clear the Transaction ERror (TXERR) condition */ + + stm32_putreg(STM32_OTG_HCINT(chidx), OTG_HCINT_TXERR); + } + + /* Check for a pending NAK response received (NAK) interrupt */ + + else if ((pending & OTG_HCINT_NAK) != 0) + { + /* For a BULK transfer, the hardware is capable of retrying + * automatically on a NAK. However, this is not always + * what we need to do. So we always halt the transfer and + * return control to high level logic in the event of a NAK. + */ + +#if 1 + /* Halt the interrupt channel */ + + if (chan->eptype == OTG_EPTYPE_INTR) + { + /* Halt the channel -- the CHH interrupt is expected next */ + + stm32_chan_halt(priv, chidx, CHREASON_NAK); + } + + /* Re-activate CTRL and BULK channels. + * REVISIT: This can cause a lot of interrupts! + */ + + else if (chan->eptype == OTG_EPTYPE_CTRL || + chan->eptype == OTG_EPTYPE_BULK) + { + /* Re-activate the channel by clearing CHDIS and assuring that + * CHENA is set + */ + + regval = stm32_getreg(STM32_OTG_HCCHAR(chidx)); + regval |= OTG_HCCHAR_CHENA; + regval &= ~OTG_HCCHAR_CHDIS; + stm32_putreg(STM32_OTG_HCCHAR(chidx), regval); + } +#else + /* Halt all transfers on the NAK -- the CHH interrupt is expected next */ + + stm32_chan_halt(priv, chidx, CHREASON_NAK); +#endif + + /* Clear the NAK condition */ + + stm32_putreg(STM32_OTG_HCINT(chidx), OTG_HCINT_NAK); + } + + /* Check for a transfer complete event */ + + stm32_chan_wakeup(priv, chan); +} + +/**************************************************************************** + * Name: stm32_gint_hcoutisr + * + * Description: + * USB OTG FS host OUT channels interrupt handler + * + * One the completion of the transfer, the channel result byte may be set as + * follows: + * + * OK - Transfer completed successfully + * EAGAIN - If devices NAKs the transfer or NYET occurs + * EPERM - If the endpoint stalls + * EIO - On a TX or data toggle error + * EPIPE - Frame overrun + * + * EBUSY in the result field indicates that the transfer has not completed. + * + ****************************************************************************/ + +static inline void stm32_gint_hcoutisr(FAR struct stm32_usbhost_s *priv, + int chidx) +{ + FAR struct stm32_chan_s *chan = &priv->chan[chidx]; + uint32_t regval; + uint32_t pending; + + /* Read the HCINT register to get the pending HC interrupts. Read the + * HCINTMSK register to get the set of enabled HC interrupts. + */ + + pending = stm32_getreg(STM32_OTG_HCINT(chidx)); + regval = stm32_getreg(STM32_OTG_HCINTMSK(chidx)); + + /* AND the two to get the set of enabled, pending HC interrupts */ + + pending &= regval; + ullinfo("HCINTMSK%d: %08x pending: %08x\n", chidx, regval, pending); + + /* Check for a pending ACK response received/transmitted (ACK) interrupt */ + + if ((pending & OTG_HCINT_ACK) != 0) + { + /* Clear the pending the ACK response received/transmitted (ACK) interrupt */ + + stm32_putreg(STM32_OTG_HCINT(chidx), OTG_HCINT_ACK); + } + + /* Check for a pending FRaMe OverRun (FRMOR) interrupt */ + + else if ((pending & OTG_HCINT_FRMOR) != 0) + { + /* Halt the channel (probably not necessary for FRMOR) */ + + stm32_chan_halt(priv, chidx, CHREASON_FRMOR); + + /* Clear the pending the FRaMe OverRun (FRMOR) interrupt */ + + stm32_putreg(STM32_OTG_HCINT(chidx), OTG_HCINT_FRMOR); + } + + /* Check for a pending TransFeR Completed (XFRC) interrupt */ + + else if ((pending & OTG_HCINT_XFRC) != 0) + { + /* Decrement the number of bytes remaining by the number of + * bytes that were "in-flight". + */ + + priv->chan[chidx].buffer += priv->chan[chidx].inflight; + priv->chan[chidx].xfrd += priv->chan[chidx].inflight; + priv->chan[chidx].inflight = 0; + + /* Halt the channel -- the CHH interrupt is expected next */ + + stm32_chan_halt(priv, chidx, CHREASON_XFRC); + + /* Clear the pending the TransFeR Completed (XFRC) interrupt */ + + stm32_putreg(STM32_OTG_HCINT(chidx), OTG_HCINT_XFRC); + } + + /* Check for a pending STALL response receive (STALL) interrupt */ + + else if ((pending & OTG_HCINT_STALL) != 0) + { + /* Clear the pending the STALL response receiv (STALL) interrupt */ + + stm32_putreg(STM32_OTG_HCINT(chidx), OTG_HCINT_STALL); + + /* Halt the channel when a STALL, TXERR, BBERR or DTERR interrupt is + * received on the channel. + */ + + stm32_chan_halt(priv, chidx, CHREASON_STALL); + } + + /* Check for a pending NAK response received (NAK) interrupt */ + + else if ((pending & OTG_HCINT_NAK) != 0) + { + /* Halt the channel -- the CHH interrupt is expected next */ + + stm32_chan_halt(priv, chidx, CHREASON_NAK); + + /* Clear the pending the NAK response received (NAK) interrupt */ + + stm32_putreg(STM32_OTG_HCINT(chidx), OTG_HCINT_NAK); + } + + /* Check for a pending Transaction ERror (TXERR) interrupt */ + + else if ((pending & OTG_HCINT_TXERR) != 0) + { + /* Halt the channel when a STALL, TXERR, BBERR or DTERR interrupt is + * received on the channel. + */ + + stm32_chan_halt(priv, chidx, CHREASON_TXERR); + + /* Clear the pending the Transaction ERror (TXERR) interrupt */ + + stm32_putreg(STM32_OTG_HCINT(chidx), OTG_HCINT_TXERR); + } + + /* Check for a NYET interrupt */ + +#if 0 /* NYET is a reserved bit in the HCINT register */ + else if ((pending & OTG_HCINT_NYET) != 0) + { + /* Halt the channel */ + + stm32_chan_halt(priv, chidx, CHREASON_NYET); + + /* Clear the pending the NYET interrupt */ + + stm32_putreg(STM32_OTG_HCINT(chidx), OTG_HCINT_NYET); + } +#endif + + /* Check for a pending Data Toggle ERRor (DTERR) interrupt */ + + else if (pending & OTG_HCINT_DTERR) + { + /* Halt the channel when a STALL, TXERR, BBERR or DTERR interrupt is + * received on the channel. + */ + + stm32_chan_halt(priv, chidx, CHREASON_DTERR); + + /* Clear the pending the Data Toggle ERRor (DTERR) and NAK interrupts */ + + stm32_putreg(STM32_OTG_HCINT(chidx), (OTG_HCINT_DTERR | OTG_HCINT_NAK)); + } + + /* Check for a pending CHannel Halted (CHH) interrupt */ + + else if ((pending & OTG_HCINT_CHH) != 0) + { + /* Mask the CHannel Halted (CHH) interrupt */ + + regval = stm32_getreg(STM32_OTG_HCINTMSK(chidx)); + regval &= ~OTG_HCINT_CHH; + stm32_putreg(STM32_OTG_HCINTMSK(chidx), regval); + + if (chan->chreason == CHREASON_XFRC) + { + /* Set the request done result */ + + chan->result = OK; + + /* Read the HCCHAR register to get the HCCHAR register to get + * the endpoint type. + */ + + regval = stm32_getreg(STM32_OTG_HCCHAR(chidx)); + + /* Is it a bulk endpoint? Were an odd number of packets + * transferred? + */ + + if ((regval & OTG_HCCHAR_EPTYP_MASK) == OTG_HCCHAR_EPTYP_BULK && + (chan->npackets & 1) != 0) + { + /* Yes to both... toggle the data out PID */ + + chan->outdata1 ^= true; + } + } + else if (chan->chreason == CHREASON_NAK || + chan->chreason == CHREASON_NYET) + { + /* Set the try again later result */ + + chan->result = EAGAIN; + } + else if (chan->chreason == CHREASON_STALL) + { + /* Set the request stall result */ + + chan->result = EPERM; + } + else if ((chan->chreason == CHREASON_TXERR) || + (chan->chreason == CHREASON_DTERR)) + { + /* Set the I/O failure result */ + + chan->result = EIO; + } + else /* if (chan->chreason == CHREASON_FRMOR) */ + { + /* Set the frame error result */ + + chan->result = EPIPE; + } + + /* Clear the pending the CHannel Halted (CHH) interrupt */ + + stm32_putreg(STM32_OTG_HCINT(chidx), OTG_HCINT_CHH); + } + + /* Check for a transfer complete event */ + + stm32_chan_wakeup(priv, chan); +} + +/**************************************************************************** + * Name: stm32_gint_connected + * + * Description: + * Handle a connection event. + * + ****************************************************************************/ + +static void stm32_gint_connected(FAR struct stm32_usbhost_s *priv) +{ + /* We we previously disconnected? */ + + if (!priv->connected) + { + /* Yes.. then now we are connected */ + + usbhost_vtrace1(OTG_VTRACE1_CONNECTED, 0); + priv->connected = true; + priv->change = true; + DEBUGASSERT(priv->smstate == SMSTATE_DETACHED); + + /* Notify any waiters */ + + priv->smstate = SMSTATE_ATTACHED; + if (priv->pscwait) + { + stm32_givesem(&priv->pscsem); + priv->pscwait = false; + } + } +} + +/**************************************************************************** + * Name: stm32_gint_disconnected + * + * Description: + * Handle a disconnection event. + * + ****************************************************************************/ + +static void stm32_gint_disconnected(FAR struct stm32_usbhost_s *priv) +{ + /* Were we previously connected? */ + + if (priv->connected) + { + /* Yes.. then we no longer connected */ + + usbhost_vtrace1(OTG_VTRACE1_DISCONNECTED, 0); + + /* Are we bound to a class driver? */ + + if (priv->rhport.hport.devclass) + { + /* Yes.. Disconnect the class driver */ + + CLASS_DISCONNECTED(priv->rhport.hport.devclass); + priv->rhport.hport.devclass = NULL; + } + + /* Re-Initialize Host for new Enumeration */ + + priv->smstate = SMSTATE_DETACHED; + priv->connected = false; + priv->change = true; + stm32_chan_freeall(priv); + + priv->rhport.hport.speed = USB_SPEED_FULL; + + /* Notify any waiters that there is a change in the connection state */ + + if (priv->pscwait) + { + stm32_givesem(&priv->pscsem); + priv->pscwait = false; + } + } +} + +/**************************************************************************** + * Name: stm32_gint_sofisr + * + * Description: + * USB OTG FS start-of-frame interrupt handler + * + ****************************************************************************/ + +#ifdef CONFIG_STM32_OTG_SOFINTR +static inline void stm32_gint_sofisr(FAR struct stm32_usbhost_s *priv) +{ + /* Handle SOF interrupt */ +#warning "Do what?" + + /* Clear pending SOF interrupt */ + + stm32_putreg(STM32_OTG_GINTSTS, OTG_GINT_SOF); +} +#endif + +/**************************************************************************** + * Name: stm32_gint_rxflvlisr + * + * Description: + * USB OTG FS RxFIFO non-empty interrupt handler + * + ****************************************************************************/ + +static inline void stm32_gint_rxflvlisr(FAR struct stm32_usbhost_s *priv) +{ + FAR uint32_t *dest; + uint32_t grxsts; + uint32_t intmsk; + uint32_t hcchar; + uint32_t hctsiz; + uint32_t fifo; + int bcnt; + int bcnt32; + int chidx; + int i; + + /* Disable the RxFIFO non-empty interrupt */ + + intmsk = stm32_getreg(STM32_OTG_GINTMSK); + intmsk &= ~OTG_GINT_RXFLVL; + stm32_putreg(STM32_OTG_GINTMSK, intmsk); + + /* Read and pop the next status from the Rx FIFO */ + + grxsts = stm32_getreg(STM32_OTG_GRXSTSP); + ullinfo("GRXSTS: %08x\n", grxsts); + + /* Isolate the channel number/index in the status word */ + + chidx = (grxsts & OTG_GRXSTSH_CHNUM_MASK) >> OTG_GRXSTSH_CHNUM_SHIFT; + + /* Get the host channel characteristics register (HCCHAR) for this channel */ + + hcchar = stm32_getreg(STM32_OTG_HCCHAR(chidx)); + + /* Then process the interrupt according to the packet status */ + + switch (grxsts & OTG_GRXSTSH_PKTSTS_MASK) + { + case OTG_GRXSTSH_PKTSTS_INRECVD: /* IN data packet received */ + { + /* Read the data into the host buffer. */ + + bcnt = (grxsts & OTG_GRXSTSH_BCNT_MASK) >> OTG_GRXSTSH_BCNT_SHIFT; + if (bcnt > 0 && priv->chan[chidx].buffer != NULL) + { + /* Transfer the packet from the Rx FIFO into the user buffer */ + + dest = (FAR uint32_t *)priv->chan[chidx].buffer; + fifo = STM32_OTG_DFIFO_HCH(0); + bcnt32 = (bcnt + 3) >> 2; + + for (i = 0; i < bcnt32; i++) + { + *dest++ = stm32_getreg(fifo); + } + + stm32_pktdump("Received", priv->chan[chidx].buffer, bcnt); + + /* Toggle the IN data pid (Used by Bulk and INTR only) */ + + priv->chan[chidx].indata1 ^= true; + + /* Manage multiple packet transfers */ + + priv->chan[chidx].buffer += bcnt; + priv->chan[chidx].xfrd += bcnt; + + /* Check if more packets are expected */ + + hctsiz = stm32_getreg(STM32_OTG_HCTSIZ(chidx)); + if ((hctsiz & OTG_HCTSIZ_PKTCNT_MASK) != 0) + { + /* Re-activate the channel when more packets are expected */ + + hcchar |= OTG_HCCHAR_CHENA; + hcchar &= ~OTG_HCCHAR_CHDIS; + stm32_putreg(STM32_OTG_HCCHAR(chidx), hcchar); + } + } + } + break; + + case OTG_GRXSTSH_PKTSTS_INDONE: /* IN transfer completed */ + case OTG_GRXSTSH_PKTSTS_DTOGERR: /* Data toggle error */ + case OTG_GRXSTSH_PKTSTS_HALTED: /* Channel halted */ + default: + break; + } + + /* Re-enable the RxFIFO non-empty interrupt */ + + intmsk |= OTG_GINT_RXFLVL; + stm32_putreg(STM32_OTG_GINTMSK, intmsk); +} + +/**************************************************************************** + * Name: stm32_gint_nptxfeisr + * + * Description: + * USB OTG FS non-periodic TxFIFO empty interrupt handler + * + ****************************************************************************/ + +static inline void stm32_gint_nptxfeisr(FAR struct stm32_usbhost_s *priv) +{ + FAR struct stm32_chan_s *chan; + uint32_t regval; + unsigned int wrsize; + unsigned int avail; + unsigned int chidx; + + /* Recover the index of the channel that is waiting for space in the Tx + * FIFO. + */ + + chidx = priv->chidx; + chan = &priv->chan[chidx]; + + /* Reduce the buffer size by the number of bytes that were previously placed + * in the Tx FIFO. + */ + + chan->buffer += chan->inflight; + chan->xfrd += chan->inflight; + chan->inflight = 0; + + /* If we have now transferred the entire buffer, then this transfer is + * complete (this case really should never happen because we disable + * the NPTXFE interrupt on the final packet). + */ + + if (chan->xfrd >= chan->buflen) + { + /* Disable further Tx FIFO empty interrupts and bail. */ + + stm32_modifyreg(STM32_OTG_GINTMSK, OTG_GINT_NPTXFE, 0); + return; + } + + /* Read the status from the top of the non-periodic TxFIFO */ + + regval = stm32_getreg(STM32_OTG_HNPTXSTS); + + /* Extract the number of bytes available in the non-periodic Tx FIFO. */ + + avail = ((regval & OTG_HNPTXSTS_NPTXFSAV_MASK) >> OTG_HNPTXSTS_NPTXFSAV_SHIFT) << 2; + + /* Get the size to put in the Tx FIFO now */ + + wrsize = chan->buflen - chan->xfrd; + + /* Get minimal size packet that can be sent. Something is seriously + * configured wrong if one packet will not fit into the empty Tx FIFO. + */ + + DEBUGASSERT(wrsize > 0 && avail >= MIN(wrsize, chan->maxpacket)); + if (wrsize > avail) + { + /* Clip the write size to the number of full, max sized packets + * that will fit in the Tx FIFO. + */ + + unsigned int wrpackets = avail / chan->maxpacket; + wrsize = wrpackets * chan->maxpacket; + } + + /* Otherwise, this will be the last packet to be sent in this transaction. + * We now need to disable further NPTXFE interrupts. + */ + + else + { + stm32_modifyreg(STM32_OTG_GINTMSK, OTG_GINT_NPTXFE, 0); + } + + /* Write the next group of packets into the Tx FIFO */ + + ullinfo("HNPTXSTS: %08x chidx: %d avail: %d buflen: %d xfrd: %d wrsize: %d\n", + regval, chidx, avail, chan->buflen, chan->xfrd, wrsize); + + stm32_gint_wrpacket(priv, chan->buffer, chidx, wrsize); +} + +/**************************************************************************** + * Name: stm32_gint_ptxfeisr + * + * Description: + * USB OTG FS periodic TxFIFO empty interrupt handler + * + ****************************************************************************/ + +static inline void stm32_gint_ptxfeisr(FAR struct stm32_usbhost_s *priv) +{ + FAR struct stm32_chan_s *chan; + uint32_t regval; + unsigned int wrsize; + unsigned int avail; + unsigned int chidx; + + /* Recover the index of the channel that is waiting for space in the Tx + * FIFO. + */ + + chidx = priv->chidx; + chan = &priv->chan[chidx]; + + /* Reduce the buffer size by the number of bytes that were previously placed + * in the Tx FIFO. + */ + + chan->buffer += chan->inflight; + chan->xfrd += chan->inflight; + chan->inflight = 0; + + /* If we have now transfered the entire buffer, then this transfer is + * complete (this case really should never happen because we disable + * the PTXFE interrupt on the final packet). + */ + + if (chan->xfrd >= chan->buflen) + { + /* Disable further Tx FIFO empty interrupts and bail. */ + + stm32_modifyreg(STM32_OTG_GINTMSK, OTG_GINT_PTXFE, 0); + return; + } + + /* Read the status from the top of the periodic TxFIFO */ + + regval = stm32_getreg(STM32_OTG_HPTXSTS); + + /* Extract the number of bytes available in the periodic Tx FIFO. */ + + avail = ((regval & OTG_HPTXSTS_PTXFSAVL_MASK) >> OTG_HPTXSTS_PTXFSAVL_SHIFT) << 2; + + /* Get the size to put in the Tx FIFO now */ + + wrsize = chan->buflen - chan->xfrd; + + /* Get minimal size packet that can be sent. Something is seriously + * configured wrong if one packet will not fit into the empty Tx FIFO. + */ + + DEBUGASSERT(wrsize && avail >= MIN(wrsize, chan->maxpacket)); + if (wrsize > avail) + { + /* Clip the write size to the number of full, max sized packets + * that will fit in the Tx FIFO. + */ + + unsigned int wrpackets = avail / chan->maxpacket; + wrsize = wrpackets * chan->maxpacket; + } + + /* Otherwise, this will be the last packet to be sent in this transaction. + * We now need to disable further PTXFE interrupts. + */ + + else + { + stm32_modifyreg(STM32_OTG_GINTMSK, OTG_GINT_PTXFE, 0); + } + + /* Write the next group of packets into the Tx FIFO */ + + ullinfo("HPTXSTS: %08x chidx: %d avail: %d buflen: %d xfrd: %d wrsize: %d\n", + regval, chidx, avail, chan->buflen, chan->xfrd, wrsize); + + stm32_gint_wrpacket(priv, chan->buffer, chidx, wrsize); +} + +/**************************************************************************** + * Name: stm32_gint_hcisr + * + * Description: + * USB OTG FS host channels interrupt handler + * + ****************************************************************************/ + +static inline void stm32_gint_hcisr(FAR struct stm32_usbhost_s *priv) +{ + uint32_t haint; + uint32_t hcchar; + int i = 0; + + /* Read the Host all channels interrupt register and test each bit in the + * register. Each bit i, i=0...(STM32_NHOST_CHANNELS-1), corresponds to + * a pending interrupt on channel i. + */ + + haint = stm32_getreg(STM32_OTG_HAINT); + for (i = 0; i < STM32_NHOST_CHANNELS; i++) + { + /* Is an interrupt pending on this channel? */ + + if ((haint & OTG_HAINT(i)) != 0) + { + /* Yes... read the HCCHAR register to get the direction bit */ + + hcchar = stm32_getreg(STM32_OTG_HCCHAR(i)); + + /* Was this an interrupt on an IN or an OUT channel? */ + + if ((hcchar & OTG_HCCHAR_EPDIR) != 0) + { + /* Handle the HC IN channel interrupt */ + + stm32_gint_hcinisr(priv, i); + } + else + { + /* Handle the HC OUT channel interrupt */ + + stm32_gint_hcoutisr(priv, i); + } + } + } +} + +/**************************************************************************** + * Name: stm32_gint_hprtisr + * + * Description: + * USB OTG FS host port interrupt handler + * + ****************************************************************************/ + +static inline void stm32_gint_hprtisr(FAR struct stm32_usbhost_s *priv) +{ + uint32_t hprt; + uint32_t newhprt; + uint32_t hcfg; + + usbhost_vtrace1(OTG_VTRACE1_GINT_HPRT, 0); + /* Read the port status and control register (HPRT) */ + + hprt = stm32_getreg(STM32_OTG_HPRT); + + /* Setup to clear the interrupt bits in GINTSTS by setting the corresponding + * bits in the HPRT. The HCINT interrupt bit is cleared when the appropriate + * status bits in the HPRT register are cleared. + */ + + newhprt = hprt & ~(OTG_HPRT_PENA | OTG_HPRT_PCDET | + OTG_HPRT_PENCHNG | OTG_HPRT_POCCHNG); + + /* Check for Port Overcurrent CHaNGe (POCCHNG) */ + + if ((hprt & OTG_HPRT_POCCHNG) != 0) + { + /* Set up to clear the POCCHNG status in the new HPRT contents. */ + + usbhost_vtrace1(OTG_VTRACE1_GINT_HPRT_POCCHNG, 0); + newhprt |= OTG_HPRT_POCCHNG; + } + + /* Check for Port Connect DETected (PCDET). The core sets this bit when a + * device connection is detected. + */ + + if ((hprt & OTG_HPRT_PCDET) != 0) + { + /* Set up to clear the PCDET status in the new HPRT contents. Then + * process the new connection event. + */ + + usbhost_vtrace1(OTG_VTRACE1_GINT_HPRT_PCDET, 0); + newhprt |= OTG_HPRT_PCDET; + stm32_portreset(priv); + stm32_gint_connected(priv); + } + + /* Check for Port Enable CHaNGed (PENCHNG) */ + + if ((hprt & OTG_HPRT_PENCHNG) != 0) + { + /* Set up to clear the PENCHNG status in the new HPRT contents. */ + + usbhost_vtrace1(OTG_VTRACE1_GINT_HPRT_PENCHNG, 0); + newhprt |= OTG_HPRT_PENCHNG; + + /* Was the port enabled? */ + + if ((hprt & OTG_HPRT_PENA) != 0) + { + /* Yes.. handle the new connection event */ + + stm32_gint_connected(priv); + + /* Check the Host ConFiGuration register (HCFG) */ + + hcfg = stm32_getreg(STM32_OTG_HCFG); + + /* Is this a low speed or full speed connection (OTG FS does not + * support high speed) + */ + + if ((hprt & OTG_HPRT_PSPD_MASK) == OTG_HPRT_PSPD_LS) + { + /* Set the Host Frame Interval Register for the 6KHz speed */ + + usbhost_vtrace1(OTG_VTRACE1_GINT_HPRT_LSDEV, 0); + stm32_putreg(STM32_OTG_HFIR, 6000); + + /* Are we switching from FS to LS? */ + + if ((hcfg & OTG_HCFG_FSLSPCS_MASK) != OTG_HCFG_FSLSPCS_LS6MHz) + { + usbhost_vtrace1(OTG_VTRACE1_GINT_HPRT_FSLSSW, 0); + + /* Yes... configure for LS */ + + hcfg &= ~OTG_HCFG_FSLSPCS_MASK; + hcfg |= OTG_HCFG_FSLSPCS_LS6MHz; + stm32_putreg(STM32_OTG_HCFG, hcfg); + + /* And reset the port */ + + stm32_portreset(priv); + } + } + else /* if ((hprt & OTG_HPRT_PSPD_MASK) == OTG_HPRT_PSPD_FS) */ + { + + usbhost_vtrace1(OTG_VTRACE1_GINT_HPRT_FSDEV, 0); + stm32_putreg(STM32_OTG_HFIR, 48000); + + /* Are we switching from LS to FS? */ + + if ((hcfg & OTG_HCFG_FSLSPCS_MASK) != OTG_HCFG_FSLSPCS_FS48MHz) + { + + usbhost_vtrace1(OTG_VTRACE1_GINT_HPRT_LSFSSW, 0); + /* Yes... configure for FS */ + + hcfg &= ~OTG_HCFG_FSLSPCS_MASK; + hcfg |= OTG_HCFG_FSLSPCS_FS48MHz; + stm32_putreg(STM32_OTG_HCFG, hcfg); + + /* And reset the port */ + + stm32_portreset(priv); + } + } + } + } + + /* Clear port interrupts by setting bits in the HPRT */ + + stm32_putreg(STM32_OTG_HPRT, newhprt); +} + +/**************************************************************************** + * Name: stm32_gint_discisr + * + * Description: + * USB OTG FS disconnect detected interrupt handler + * + ****************************************************************************/ + +static inline void stm32_gint_discisr(FAR struct stm32_usbhost_s *priv) +{ + /* Handle the disconnection event */ + + stm32_gint_disconnected(priv); + + /* Clear the dicsonnect interrupt */ + + stm32_putreg(STM32_OTG_GINTSTS, OTG_GINT_DISC); +} + +/**************************************************************************** + * Name: stm32_gint_ipxfrisr + * + * Description: + * USB OTG FS incomplete periodic interrupt handler + * + ****************************************************************************/ + +static inline void stm32_gint_ipxfrisr(FAR struct stm32_usbhost_s *priv) +{ + uint32_t regval; + + /* CHENA : Set to enable the channel + * CHDIS : Set to stop transmitting/receiving data on a channel + */ + + regval = stm32_getreg(STM32_OTG_HCCHAR(0)); + regval |= (OTG_HCCHAR_CHDIS | OTG_HCCHAR_CHENA); + stm32_putreg(STM32_OTG_HCCHAR(0), regval); + + /* Clear the incomplete isochronous OUT interrupt */ + + stm32_putreg(STM32_OTG_GINTSTS, OTG_GINT_IPXFR); +} + +/**************************************************************************** + * Name: stm32_gint_isr + * + * Description: + * USB OTG FS global interrupt handler + * + ****************************************************************************/ + +static int stm32_gint_isr(int irq, FAR void *context) +{ + /* At present, there is only support for a single OTG FS host. Hence it is + * pre-allocated as g_usbhost. However, in most code, the private data + * structure will be referenced using the 'priv' pointer (rather than the + * global data) in order to simplify any future support for multiple devices. + */ + + FAR struct stm32_usbhost_s *priv = &g_usbhost; + uint32_t pending; + + /* If OTG were supported, we would need to check if we are in host or + * device mode when the global interrupt occurs. Here we support only + * host mode + */ + + /* Loop while there are pending interrupts to process. This loop may save a + * little interrupt handling overhead. + */ + + for (; ; ) + { + /* Get the unmasked bits in the GINT status */ + + pending = stm32_getreg(STM32_OTG_GINTSTS); + pending &= stm32_getreg(STM32_OTG_GINTMSK); + + /* Return from the interrupt when there are no further pending + * interrupts. + */ + + if (pending == 0) + { + return OK; + } + + /* Otherwise, process each pending, unmasked GINT interrupts */ + + /* Handle the start of frame interrupt */ + +#ifdef CONFIG_STM32_OTG_SOFINTR + if ((pending & OTG_GINT_SOF) != 0) + { + usbhost_vtrace1(OTG_VTRACE1_GINT_SOF, 0); + stm32_gint_sofisr(priv); + } +#endif + + /* Handle the RxFIFO non-empty interrupt */ + + if ((pending & OTG_GINT_RXFLVL) != 0) + { + usbhost_vtrace1(OTG_VTRACE1_GINT_RXFLVL, 0); + stm32_gint_rxflvlisr(priv); + } + + /* Handle the non-periodic TxFIFO empty interrupt */ + + if ((pending & OTG_GINT_NPTXFE) != 0) + { + usbhost_vtrace1(OTG_VTRACE1_GINT_NPTXFE, 0); + stm32_gint_nptxfeisr(priv); + } + + /* Handle the periodic TxFIFO empty interrupt */ + + if ((pending & OTG_GINT_PTXFE) != 0) + { + usbhost_vtrace1(OTG_VTRACE1_GINT_PTXFE, 0); + stm32_gint_ptxfeisr(priv); + } + + /* Handle the host channels interrupt */ + + if ((pending & OTG_GINT_HC) != 0) + { + usbhost_vtrace1(OTG_VTRACE1_GINT_HC, 0); + stm32_gint_hcisr(priv); + } + + /* Handle the host port interrupt */ + + if ((pending & OTG_GINT_HPRT) != 0) + { + stm32_gint_hprtisr(priv); + } + + /* Handle the disconnect detected interrupt */ + + if ((pending & OTG_GINT_DISC) != 0) + { + usbhost_vtrace1(OTG_VTRACE1_GINT_DISC, 0); + stm32_gint_discisr(priv); + } + + /* Handle the incomplete periodic transfer */ + + if ((pending & OTG_GINT_IPXFR) != 0) + { + usbhost_vtrace1(OTG_VTRACE1_GINT_IPXFR, 0); + stm32_gint_ipxfrisr(priv); + } + } + + /* We won't get here */ + + return OK; +} + +/**************************************************************************** + * Name: stm32_gint_enable and stm32_gint_disable + * + * Description: + * Respectively enable or disable the global OTG FS interrupt. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32_gint_enable(void) +{ + uint32_t regval; + + /* Set the GINTMSK bit to unmask the interrupt */ + + regval = stm32_getreg(STM32_OTG_GAHBCFG); + regval |= OTG_GAHBCFG_GINTMSK; + stm32_putreg(STM32_OTG_GAHBCFG, regval); +} + +static void stm32_gint_disable(void) +{ + uint32_t regval; + + /* Clear the GINTMSK bit to mask the interrupt */ + + regval = stm32_getreg(STM32_OTG_GAHBCFG); + regval &= ~OTG_GAHBCFG_GINTMSK; + stm32_putreg(STM32_OTG_GAHBCFG, regval); +} + +/**************************************************************************** + * Name: stm32_hostinit_enable + * + * Description: + * Enable host interrupts. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void stm32_hostinit_enable(void) +{ + uint32_t regval; + + /* Disable all interrupts. */ + + stm32_putreg(STM32_OTG_GINTMSK, 0); + + /* Clear any pending interrupts. */ + + stm32_putreg(STM32_OTG_GINTSTS, 0xffffffff); + + /* Clear any pending USB OTG Interrupts (should be done elsewhere if OTG is supported) */ + + stm32_putreg(STM32_OTG_GOTGINT, 0xffffffff); + + /* Clear any pending USB OTG interrupts */ + + stm32_putreg(STM32_OTG_GINTSTS, 0xbfffffff); + + /* Enable the host interrupts */ + /* Common interrupts: + * + * OTG_GINT_WKUP : Resume/remote wakeup detected interrupt + * OTG_GINT_USBSUSP : USB suspend + */ + + regval = (OTG_GINT_WKUP | OTG_GINT_USBSUSP); + + /* If OTG were supported, we would need to enable the following as well: + * + * OTG_GINT_OTG : OTG interrupt + * OTG_GINT_SRQ : Session request/new session detected interrupt + * OTG_GINT_CIDSCHG : Connector ID status change + */ + + /* Host-specific interrupts + * + * OTG_GINT_SOF : Start of frame + * OTG_GINT_RXFLVL : RxFIFO non-empty + * OTG_GINT_IISOOXFR : Incomplete isochronous OUT transfer + * OTG_GINT_HPRT : Host port interrupt + * OTG_GINT_HC : Host channels interrupt + * OTG_GINT_DISC : Disconnect detected interrupt + */ + +#ifdef CONFIG_STM32_OTG_SOFINTR + regval |= (OTG_GINT_SOF | OTG_GINT_RXFLVL | OTG_GINT_IISOOXFR | + OTG_GINT_HPRT | OTG_GINT_HC | OTG_GINT_DISC); +#else + regval |= (OTG_GINT_RXFLVL | OTG_GINT_IPXFR | OTG_GINT_HPRT | + OTG_GINT_HC | OTG_GINT_DISC); +#endif + stm32_putreg(STM32_OTG_GINTMSK, regval); +} + +/**************************************************************************** + * Name: stm32_txfe_enable + * + * Description: + * Enable Tx FIFO empty interrupts. This is necessary when the entire + * transfer will not fit into Tx FIFO. The transfer will then be completed + * when the Tx FIFO is empty. NOTE: The Tx FIFO interrupt is disabled + * the fifo empty interrupt handler when the transfer is complete. + * + * Input Parameters: + * priv - Driver state structure reference + * chidx - The channel that requires the Tx FIFO empty interrupt + * + * Returned Value: + * None + * + * Assumptions: + * Called from user task context. Interrupts must be disabled to assure + * exclusive access to the GINTMSK register. + * + ****************************************************************************/ + +static void stm32_txfe_enable(FAR struct stm32_usbhost_s *priv, int chidx) +{ + FAR struct stm32_chan_s *chan = &priv->chan[chidx]; + irqstate_t flags; + uint32_t regval; + + /* Disable all interrupts so that we have exclusive access to the GINTMSK + * (it would be sufficent just to disable the GINT interrupt). + */ + + flags = enter_critical_section(); + + /* Should we enable the periodic or non-peridic Tx FIFO empty interrupts */ + + regval = stm32_getreg(STM32_OTG_GINTMSK); + switch (chan->eptype) + { + default: + case OTG_EPTYPE_CTRL: /* Non periodic transfer */ + case OTG_EPTYPE_BULK: + regval |= OTG_GINT_NPTXFE; + break; + + case OTG_EPTYPE_INTR: /* Periodic transfer */ + case OTG_EPTYPE_ISOC: + regval |= OTG_GINT_PTXFE; + break; + } + + /* Enable interrupts */ + + stm32_putreg(STM32_OTG_GINTMSK, regval); + leave_critical_section(flags); +} + +/**************************************************************************** + * USB Host Controller Operations + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_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 Values: + * Zero (OK) is returned on success when a device in 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 stm32_wait(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s **hport) +{ + FAR struct stm32_usbhost_s *priv = &g_usbhost; + struct usbhost_hubport_s *connport; + irqstate_t flags; + + /* Loop until a change in connection state is detected */ + + flags = enter_critical_section(); + for (; ; ) + { + /* Is there a change in the connection state of the single root hub + * port? + */ + + if (priv->change) + { + connport = &priv->rhport.hport; + + /* Yes. Remember the new state */ + + connport->connected = priv->connected; + priv->change = false; + + /* And return the root hub port */ + + *hport = connport; + leave_critical_section(flags); + + uinfo("RHport Connected: %s\n", connport->connected ? "YES" : "NO"); + return OK; + } + +#ifdef CONFIG_USBHOST_HUB + /* Is a device connected to an external hub? */ + + if (priv->hport) + { + /* Yes.. return the external hub port */ + + connport = (struct usbhost_hubport_s *)priv->hport; + priv->hport = NULL; + + *hport = connport; + leave_critical_section(flags); + + uinfo("Hub port Connected: %s\n", connport->connected ? "YES" : "NO"); + return OK; + } +#endif + + /* Wait for the next connection event */ + + priv->pscwait = true; + stm32_takesem(&priv->pscsem); + } +} + +/**************************************************************************** + * Name: stm32_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 Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ****************************************************************************/ + +static int stm32_rh_enumerate(FAR struct stm32_usbhost_s *priv, + FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport) +{ + uint32_t regval; + int ret; + + DEBUGASSERT(conn != NULL && hport != NULL && hport->port == 0); + + /* Are we connected to a device? The caller should have called the wait() + * method first to be assured that a device is connected. + */ + + while (!priv->connected) + { + /* No, return an error */ + + usbhost_trace1(OTG_TRACE1_DEVDISCONN, 0); + return -ENODEV; + } + + DEBUGASSERT(priv->smstate == SMSTATE_ATTACHED); + + /* USB 2.0 spec says at least 50ms delay before port reset. We wait 100ms. */ + + usleep(100*1000); + + /* Reset the host port */ + + stm32_portreset(priv); + + /* Get the current device speed */ + + regval = stm32_getreg(STM32_OTG_HPRT); + if ((regval & OTG_HPRT_PSPD_MASK) == OTG_HPRT_PSPD_LS) + { + priv->rhport.hport.speed = USB_SPEED_LOW; + } + else + { + priv->rhport.hport.speed = USB_SPEED_FULL; + } + + /* Allocate and initialize the root hub port EP0 channels */ + + ret = stm32_ctrlchan_alloc(priv, 0, 0, priv->rhport.hport.speed, &priv->ep0); + if (ret < 0) + { + uerr("ERROR: Failed to allocate a control endpoint: %d\n", ret); + } + + return ret; +} + +static int stm32_enumerate(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport) +{ + FAR struct stm32_usbhost_s *priv = &g_usbhost; + int ret; + + DEBUGASSERT(hport); + + /* 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. + */ + +#ifdef CONFIG_USBHOST_HUB + if (ROOTHUB(hport)) +#endif + { + ret = stm32_rh_enumerate(priv, conn, hport); + if (ret < 0) + { + return ret; + } + } + + /* Then let the common usbhost_enumerate do the real enumeration. */ + + uinfo("Enumerate the device\n"); + priv->smstate = SMSTATE_ENUM; + ret = usbhost_enumerate(hport, &hport->devclass); + + /* The enumeration may fail either because of some HCD interfaces failure + * or because the device class is not supported. In either case, we just + * need to perform the disconnection operation and make ready for a new + * enumeration. + */ + + if (ret < 0) + { + /* Return to the disconnected state */ + + uerr("ERROR: Enumeration failed: %d\n", ret); + stm32_gint_disconnected(priv); + } + + return ret; +} + +/************************************************************************************ + * Name: stm32_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. + * ep0 - The (opaque) EP0 endpoint instance + * funcaddr - The USB address of the function containing the endpoint that EP0 + * controls + * 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 Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ************************************************************************************/ + +static int stm32_ep0configure(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, + uint8_t funcaddr, uint8_t speed, + uint16_t maxpacketsize) +{ + FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; + FAR struct stm32_ctrlinfo_s *ep0info = (FAR struct stm32_ctrlinfo_s *)ep0; + FAR struct stm32_chan_s *chan; + + DEBUGASSERT(drvr != NULL && ep0info != NULL && funcaddr < 128 && + maxpacketsize <= 64); + + /* We must have exclusive access to the USB host hardware and state structures */ + + stm32_takesem(&priv->exclsem); + + /* Configure the EP0 OUT channel */ + + chan = &priv->chan[ep0info->outndx]; + chan->funcaddr = funcaddr; + chan->speed = speed; + chan->maxpacket = maxpacketsize; + + stm32_chan_configure(priv, ep0info->outndx); + + /* Configure the EP0 IN channel */ + + chan = &priv->chan[ep0info->inndx]; + chan->funcaddr = funcaddr; + chan->speed = speed; + chan->maxpacket = maxpacketsize; + + stm32_chan_configure(priv, ep0info->inndx); + + stm32_givesem(&priv->exclsem); + return OK; +} + +/************************************************************************************ + * Name: stm32_epalloc + * + * Description: + * Allocate and configure one endpoint. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * epdesc - Describes the endpoint to be allocated. + * ep - A memory location provided by the caller in which to receive the + * allocated endpoint descriptor. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ************************************************************************************/ + +static int stm32_epalloc(FAR struct usbhost_driver_s *drvr, + FAR const struct usbhost_epdesc_s *epdesc, + FAR usbhost_ep_t *ep) +{ + FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; + int ret; + + /* Sanity check. NOTE that this method should only be called if a device is + * connected (because we need a valid low speed indication). + */ + + DEBUGASSERT(drvr != 0 && epdesc != NULL && ep != NULL); + + /* We must have exclusive access to the USB host hardware and state structures */ + + stm32_takesem(&priv->exclsem); + + /* Handler control pipes differently from other endpoint types. This is + * because the normal, "transfer" endpoints are unidirectional an require + * only a single channel. Control endpoints, however, are bi-diretional + * and require two channels, one for the IN and one for the OUT direction. + */ + + if (epdesc->xfrtype == OTG_EPTYPE_CTRL) + { + ret = stm32_ctrlep_alloc(priv, epdesc, ep); + } + else + { + ret = stm32_xfrep_alloc(priv, epdesc, ep); + } + + stm32_givesem(&priv->exclsem); + return ret; +} + +/************************************************************************************ + * Name: stm32_epfree + * + * Description: + * Free and endpoint previously allocated by DRVR_EPALLOC. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * ep - The endpoint to be freed. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ************************************************************************************/ + +static int stm32_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep) +{ + FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; + + DEBUGASSERT(priv); + + /* We must have exclusive access to the USB host hardware and state structures */ + + stm32_takesem(&priv->exclsem); + + /* A single channel is represent by an index in the range of 0 to STM32_MAX_TX_FIFOS. + * Otherwise, the ep must be a pointer to an allocated control endpoint structure. + */ + + if ((uintptr_t)ep < STM32_MAX_TX_FIFOS) + { + /* Halt the channel and mark the channel available */ + + stm32_chan_free(priv, (int)ep); + } + else + { + /* Halt both control channel and mark the channels available */ + + FAR struct stm32_ctrlinfo_s *ctrlep = (FAR struct stm32_ctrlinfo_s *)ep; + stm32_chan_free(priv, ctrlep->inndx); + stm32_chan_free(priv, ctrlep->outndx); + + /* And free the control endpoint container */ + + kmm_free(ctrlep); + } + + stm32_givesem(&priv->exclsem); + return OK; +} + +/**************************************************************************** + * Name: stm32_alloc + * + * Description: + * Some hardware supports special memory in which request and descriptor data can + * be accessed more efficiently. This method provides a mechanism to allocate + * the request/descriptor memory. If the underlying hardware does not support + * such "special" memory, this functions may simply map to kmm_malloc. + * + * This interface was optimized under a particular assumption. It was assumed + * that the driver maintains a pool of small, pre-allocated buffers for descriptor + * traffic. NOTE that size is not an input, but an output: The size of the + * pre-allocated buffer is returned. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * buffer - The address of a memory location provided by the caller in which to + * return the allocated buffer memory address. + * maxlen - The address of a memory location provided by the caller in which to + * return the maximum size of the allocated buffer memory. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * - Called from a single thread so no mutual exclusion is required. + * - Never called from an interrupt handler. + * + ****************************************************************************/ + +static int stm32_alloc(FAR struct usbhost_driver_s *drvr, + FAR uint8_t **buffer, FAR size_t *maxlen) +{ + FAR uint8_t *alloc; + + DEBUGASSERT(drvr && buffer && maxlen); + + /* There is no special memory requirement for the STM32. */ + + alloc = (FAR uint8_t *)kmm_malloc(CONFIG_STM32_OTG_DESCSIZE); + if (!alloc) + { + return -ENOMEM; + } + + /* Return the allocated address and size of the descriptor buffer */ + + *buffer = alloc; + *maxlen = CONFIG_STM32_OTG_DESCSIZE; + return OK; +} + +/**************************************************************************** + * Name: stm32_free + * + * Description: + * Some hardware supports special memory in which request and descriptor data can + * be accessed more efficiently. This method provides a mechanism to free that + * request/descriptor memory. If the underlying hardware does not support + * such "special" memory, this functions may simply map to kmm_free(). + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * buffer - The address of the allocated buffer memory to be freed. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * - Never called from an interrupt handler. + * + ****************************************************************************/ + +static int stm32_free(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer) +{ + /* There is no special memory requirement */ + + DEBUGASSERT(drvr && buffer); + kmm_free(buffer); + return OK; +} + +/************************************************************************************ + * Name: stm32_ioalloc + * + * Description: + * Some hardware supports special memory in which larger IO buffers can + * be accessed more efficiently. This method provides a mechanism to allocate + * the request/descriptor memory. If the underlying hardware does not support + * such "special" memory, this functions may simply map to kmm_malloc. + * + * This interface differs from DRVR_ALLOC in that the buffers are variable-sized. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * buffer - The address of a memory location provided by the caller in which to + * return the allocated buffer memory address. + * buflen - The size of the buffer required. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ************************************************************************************/ + +static int stm32_ioalloc(FAR struct usbhost_driver_s *drvr, + FAR uint8_t **buffer, size_t buflen) +{ + FAR uint8_t *alloc; + + DEBUGASSERT(drvr && buffer && buflen > 0); + + /* There is no special memory requirement */ + + alloc = (FAR uint8_t *)kmm_malloc(buflen); + if (!alloc) + { + return -ENOMEM; + } + + /* Return the allocated buffer */ + + *buffer = alloc; + return OK; +} + +/************************************************************************************ + * Name: stm32_iofree + * + * Description: + * Some hardware supports special memory in which IO data can be accessed more + * efficiently. This method provides a mechanism to free that IO buffer + * memory. If the underlying hardware does not support such "special" memory, + * this functions may simply map to kmm_free(). + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * buffer - The address of the allocated buffer memory to be freed. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ************************************************************************************/ + +static int stm32_iofree(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer) +{ + /* There is no special memory requirement */ + + DEBUGASSERT(drvr && buffer); + kmm_free(buffer); + return OK; +} + +/**************************************************************************** + * Name: stm32_ctrlin and stm32_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 Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * - Called from a single thread so no mutual exclusion is required. + * - Never called from an interrupt handler. + * + ****************************************************************************/ + +static int stm32_ctrlin(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, + FAR const struct usb_ctrlreq_s *req, + FAR uint8_t *buffer) +{ + FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; + FAR struct stm32_ctrlinfo_s *ep0info = (FAR struct stm32_ctrlinfo_s *)ep0; + uint16_t buflen; + systime_t start; + systime_t elapsed; + int retries; + int ret; + + DEBUGASSERT(priv != NULL && ep0info != NULL && req != NULL); + usbhost_vtrace2(OTG_VTRACE2_CTRLIN, req->type, req->req); + uinfo("type:%02x req:%02x value:%02x%02x index:%02x%02x len:%02x%02x\n", + req->type, req->req, req->value[1], req->value[0], + req->index[1], req->index[0], req->len[1], req->len[0]); + + /* Extract values from the request */ + + buflen = stm32_getle16(req->len); + + /* We must have exclusive access to the USB host hardware and state structures */ + + stm32_takesem(&priv->exclsem); + + /* Loop, retrying until the retry time expires */ + + for (retries = 0; retries < STM32_RETRY_COUNT; retries++) + { + /* Send the SETUP request */ + + ret = stm32_ctrl_sendsetup(priv, ep0info, req); + if (ret < 0) + { + usbhost_trace1(OTG_TRACE1_SENDSETUP, -ret); + continue; + } + + /* Get the start time. Loop again until the timeout expires */ + + start = clock_systimer(); + do + { + /* Handle the IN data phase (if any) */ + + if (buflen > 0) + { + ret = stm32_ctrl_recvdata(priv, ep0info, buffer, buflen); + if (ret < 0) + { + usbhost_trace1(OTG_TRACE1_RECVDATA, -ret); + } + } + + /* Handle the status OUT phase */ + + if (ret == OK) + { + priv->chan[ep0info->outndx].outdata1 ^= true; + ret = stm32_ctrl_senddata(priv, ep0info, NULL, 0); + if (ret == OK) + { + /* All success transactions exit here */ + + stm32_givesem(&priv->exclsem); + return OK; + } + + usbhost_trace1(OTG_TRACE1_SENDDATA, ret < 0 ? -ret : ret); + } + + /* Get the elapsed time (in frames) */ + + elapsed = clock_systimer() - start; + } + while (elapsed < STM32_DATANAK_DELAY); + } + + /* All failures exit here after all retries and timeouts have been exhausted */ + + stm32_givesem(&priv->exclsem); + return -ETIMEDOUT; +} + +static int stm32_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, + FAR const struct usb_ctrlreq_s *req, + FAR const uint8_t *buffer) +{ + FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; + FAR struct stm32_ctrlinfo_s *ep0info = (FAR struct stm32_ctrlinfo_s *)ep0; + uint16_t buflen; + systime_t start; + systime_t elapsed; + int retries; + int ret; + + DEBUGASSERT(priv != NULL && ep0info != NULL && req != NULL); + usbhost_vtrace2(OTG_VTRACE2_CTRLOUT, req->type, req->req); + uinfo("type:%02x req:%02x value:%02x%02x index:%02x%02x len:%02x%02x\n", + req->type, req->req, req->value[1], req->value[0], + req->index[1], req->index[0], req->len[1], req->len[0]); + + /* Extract values from the request */ + + buflen = stm32_getle16(req->len); + + /* We must have exclusive access to the USB host hardware and state structures */ + + stm32_takesem(&priv->exclsem); + + /* Loop, retrying until the retry time expires */ + + for (retries = 0; retries < STM32_RETRY_COUNT; retries++) + { + /* Send the SETUP request */ + + ret = stm32_ctrl_sendsetup(priv, ep0info, req); + if (ret < 0) + { + usbhost_trace1(OTG_TRACE1_SENDSETUP, -ret); + continue; + } + + /* Get the start time. Loop again until the timeout expires */ + + start = clock_systimer(); + do + { + /* Handle the data OUT phase (if any) */ + + if (buflen > 0) + { + /* Start DATA out transfer (only one DATA packet) */ + + priv->chan[ep0info->outndx].outdata1 = true; + ret = stm32_ctrl_senddata(priv, ep0info, NULL, 0); + if (ret < 0) + { + usbhost_trace1(OTG_TRACE1_SENDDATA, -ret); + } + } + + /* Handle the status IN phase */ + + if (ret == OK) + { + ret = stm32_ctrl_recvdata(priv, ep0info, NULL, 0); + if (ret == OK) + { + /* All success transactins exit here */ + + stm32_givesem(&priv->exclsem); + return OK; + } + + usbhost_trace1(OTG_TRACE1_RECVDATA, ret < 0 ? -ret : ret); + } + + /* Get the elapsed time (in frames) */ + + elapsed = clock_systimer() - start; + } + while (elapsed < STM32_DATANAK_DELAY); + } + + /* All failures exit here after all retries and timeouts have been exhausted */ + + stm32_givesem(&priv->exclsem); + return -ETIMEDOUT; +} + +/**************************************************************************** + * Name: stm32_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 Values: + * 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 stm32_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, + FAR uint8_t *buffer, size_t buflen) +{ + FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; + unsigned int chidx = (unsigned int)ep; + ssize_t nbytes; + + uinfo("chidx: %d buflen: %d\n", (unsigned int)ep, buflen); + + DEBUGASSERT(priv && buffer && chidx < STM32_MAX_TX_FIFOS && buflen > 0); + + /* We must have exclusive access to the USB host hardware and state structures */ + + stm32_takesem(&priv->exclsem); + + /* Handle IN and OUT transfer slightly differently */ + + if (priv->chan[chidx].in) + { + nbytes = stm32_in_transfer(priv, chidx, buffer, buflen); + } + else + { + nbytes = stm32_out_transfer(priv, chidx, buffer, buflen); + } + + stm32_givesem(&priv->exclsem); + return nbytes; +} + +/**************************************************************************** + * Name: stm32_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 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 Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * - Called from a single thread so no mutual exclusion is required. + * - Never called from an interrupt handler. + * + ****************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static int stm32_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) +{ + FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; + unsigned int chidx = (unsigned int)ep; + int ret; + + uinfo("chidx: %d buflen: %d\n", (unsigned int)ep, buflen); + + DEBUGASSERT(priv && buffer && chidx < STM32_MAX_TX_FIFOS && buflen > 0); + + /* We must have exclusive access to the USB host hardware and state structures */ + + stm32_takesem(&priv->exclsem); + + /* Handle IN and OUT transfer slightly differently */ + + if (priv->chan[chidx].in) + { + ret = stm32_in_asynch(priv, chidx, buffer, buflen, callback, arg); + } + else + { + ret = stm32_out_asynch(priv, chidx, buffer, buflen, callback, arg); + } + + stm32_givesem(&priv->exclsem); + return ret; +} +#endif /* CONFIG_USBHOST_ASYNCH */ + +/************************************************************************************ + * Name: stm32_cancel + * + * Description: + * Cancel a pending transfer on an endpoint. Cancelled 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 Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure. + * + ************************************************************************************/ + +static int stm32_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep) +{ + FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; + FAR struct stm32_chan_s *chan; + unsigned int chidx = (unsigned int)ep; + irqstate_t flags; + + uinfo("chidx: %u: %d\n", chidx); + + DEBUGASSERT(priv && chidx < STM32_MAX_TX_FIFOS); + chan = &priv->chan[chidx]; + + /* We need to disable interrupts to avoid race conditions with the asynchronous + * completion of the transfer being cancelled. + */ + + flags = enter_critical_section(); + + /* Halt the channel */ + + stm32_chan_halt(priv, chidx, CHREASON_CANCELLED); + chan->result = -ESHUTDOWN; + + /* Is there a thread waiting for this transfer to complete? */ + + if (chan->waiter) + { +#ifdef CONFIG_USBHOST_ASYNCH + /* Yes.. there should not also be a callback scheduled */ + + DEBUGASSERT(chan->callback == NULL); +#endif + + /* Wake'em up! */ + + stm32_givesem(&chan->waitsem); + chan->waiter = false; + } + +#ifdef CONFIG_USBHOST_ASYNCH + /* No.. is an asynchronous callback expected when the transfer + * completes? + */ + + else if (chan->callback) + { + usbhost_asynch_t callback; + FAR void *arg; + + /* Extract the callback information */ + + callback = chan->callback; + arg = chan->arg; + + chan->callback = NULL; + chan->arg = NULL; + chan->xfrd = 0; + + /* Then perform the callback */ + + callback(arg, -ESHUTDOWN); + } +#endif + + leave_critical_section(flags); + return OK; +} + +/************************************************************************************ + * Name: stm32_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 Values: + * 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 stm32_connect(FAR struct usbhost_driver_s *drvr, + FAR struct usbhost_hubport_s *hport, + bool connected) +{ + FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; + irqstate_t flags; + + DEBUGASSERT(priv != NULL && hport != NULL); + + /* Set the connected/disconnected flag */ + + hport->connected = connected; + ullinfo("Hub port %d connected: %s\n", hport->port, connected ? "YES" : "NO"); + + /* Report the connection event */ + + flags = enter_critical_section(); + priv->hport = hport; + if (priv->pscwait) + { + priv->pscwait = false; + stm32_givesem(&priv->pscsem); + } + + leave_critical_section(flags); + return OK; +} +#endif + +/**************************************************************************** + * Name: stm32_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 Values: + * None + * + * Assumptions: + * - Only a single class bound to a single device is supported. + * - Never called from an interrupt handler. + * + ****************************************************************************/ + +static void stm32_disconnect(FAR struct usbhost_driver_s *drvr, + FAR struct usbhost_hubport_s *hport) +{ + DEBUGASSERT(hport != NULL); + hport->devclass = NULL; +} + +/**************************************************************************** + * Initialization + ****************************************************************************/ +/**************************************************************************** + * Name: stm32_portreset + * + * Description: + * Reset the USB host port. + * + * NOTE: "Before starting to drive a USB reset, the application waits for the + * OTG interrupt triggered by the debounce done bit (DBCDNE bit in + * OTG_FS_GOTGINT), which indicates that the bus is stable again after the + * electrical debounce caused by the attachment of a pull-up resistor on DP + * (FS) or DM (LS). + * + * Input Parameters: + * priv -- USB host driver private data structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32_portreset(FAR struct stm32_usbhost_s *priv) +{ + uint32_t regval; + + regval = stm32_getreg(STM32_OTG_HPRT); + regval &= ~(OTG_HPRT_PENA | OTG_HPRT_PCDET | OTG_HPRT_PENCHNG | + OTG_HPRT_POCCHNG); + regval |= OTG_HPRT_PRST; + stm32_putreg(STM32_OTG_HPRT, regval); + + up_mdelay(20); + + regval &= ~OTG_HPRT_PRST; + stm32_putreg(STM32_OTG_HPRT, regval); + + up_mdelay(20); +} + +/**************************************************************************** + * Name: stm32_flush_txfifos + * + * Description: + * Flush the selected Tx FIFO. + * + * Input Parameters: + * txfnum -- USB host driver private data structure. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void stm32_flush_txfifos(uint32_t txfnum) +{ + uint32_t regval; + uint32_t timeout; + + /* Initiate the TX FIFO flush operation */ + + regval = OTG_GRSTCTL_TXFFLSH | txfnum; + stm32_putreg(STM32_OTG_GRSTCTL, regval); + + /* Wait for the FLUSH to complete */ + + for (timeout = 0; timeout < STM32_FLUSH_DELAY; timeout++) + { + regval = stm32_getreg(STM32_OTG_GRSTCTL); + if ((regval & OTG_GRSTCTL_TXFFLSH) == 0) + { + break; + } + } + + /* Wait for 3 PHY Clocks */ + + up_udelay(3); +} + +/**************************************************************************** + * Name: stm32_flush_rxfifo + * + * Description: + * Flush the Rx FIFO. + * + * Input Parameters: + * priv -- USB host driver private data structure. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void stm32_flush_rxfifo(void) +{ + uint32_t regval; + uint32_t timeout; + + /* Initiate the RX FIFO flush operation */ + + stm32_putreg(STM32_OTG_GRSTCTL, OTG_GRSTCTL_RXFFLSH); + + /* Wait for the FLUSH to complete */ + + for (timeout = 0; timeout < STM32_FLUSH_DELAY; timeout++) + { + regval = stm32_getreg(STM32_OTG_GRSTCTL); + if ((regval & OTG_GRSTCTL_RXFFLSH) == 0) + { + break; + } + } + + /* Wait for 3 PHY Clocks */ + + up_udelay(3); +} + +/**************************************************************************** + * Name: stm32_vbusdrive + * + * Description: + * Drive the Vbus +5V. + * + * Input Parameters: + * priv - USB host driver private data structure. + * state - True: Drive, False: Don't drive + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void stm32_vbusdrive(FAR struct stm32_usbhost_s *priv, bool state) +{ + uint32_t regval; + + /* Enable/disable the external charge pump */ + + stm32_usbhost_vbusdrive(0, state); + + /* Turn on the Host port power. */ + + regval = stm32_getreg(STM32_OTG_HPRT); + regval &= ~(OTG_HPRT_PENA | OTG_HPRT_PCDET | OTG_HPRT_PENCHNG | + OTG_HPRT_POCCHNG); + + if (((regval & OTG_HPRT_PPWR) == 0) && state) + { + regval |= OTG_HPRT_PPWR; + stm32_putreg(STM32_OTG_HPRT, regval); + } + + if (((regval & OTG_HPRT_PPWR) != 0) && !state) + { + regval &= ~OTG_HPRT_PPWR; + stm32_putreg(STM32_OTG_HPRT, regval); + } + + up_mdelay(200); +} + +/**************************************************************************** + * Name: stm32_host_initialize + * + * Description: + * Initialize/re-initialize hardware for host mode operation. At present, + * this function is called only from stm32_hw_initialize(). But if OTG mode + * were supported, this function would also be called to swtich between + * host and device modes on a connector ID change interrupt. + * + * Input Parameters: + * priv -- USB host driver private data structure. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void stm32_host_initialize(FAR struct stm32_usbhost_s *priv) +{ + uint32_t regval; + uint32_t offset; + int i; + + /* Restart the PHY Clock */ + + stm32_putreg(STM32_OTG_PCGCCTL, 0); + + /* Initialize Host Configuration (HCFG) register */ + + regval = stm32_getreg(STM32_OTG_HCFG); + regval &= ~OTG_HCFG_FSLSPCS_MASK; + regval |= OTG_HCFG_FSLSPCS_FS48MHz; + stm32_putreg(STM32_OTG_HCFG, regval); + + /* Reset the host port */ + + stm32_portreset(priv); + + /* Clear the FS-/LS-only support bit in the HCFG register */ + + regval = stm32_getreg(STM32_OTG_HCFG); + regval &= ~OTG_HCFG_FSLSS; + stm32_putreg(STM32_OTG_HCFG, regval); + + /* Carve up FIFO memory for the Rx FIFO and the periodic and non-periodic Tx FIFOs */ + /* Configure Rx FIFO size (GRXFSIZ) */ + + stm32_putreg(STM32_OTG_GRXFSIZ, CONFIG_STM32_OTG_RXFIFO_SIZE); + offset = CONFIG_STM32_OTG_RXFIFO_SIZE; + + /* Setup the host non-periodic Tx FIFO size (HNPTXFSIZ) */ + + regval = (offset | (CONFIG_STM32_OTG_NPTXFIFO_SIZE << OTG_HNPTXFSIZ_NPTXFD_SHIFT)); + stm32_putreg(STM32_OTG_HNPTXFSIZ, regval); + offset += CONFIG_STM32_OTG_NPTXFIFO_SIZE; + + /* Set up the host periodic Tx fifo size register (HPTXFSIZ) */ + + regval = (offset | (CONFIG_STM32_OTG_PTXFIFO_SIZE << OTG_HPTXFSIZ_PTXFD_SHIFT)); + stm32_putreg(STM32_OTG_HPTXFSIZ, regval); + + /* If OTG were supported, we sould need to clear HNP enable bit in the + * USB_OTG control register about here. + */ + + /* Flush all FIFOs */ + + stm32_flush_txfifos(OTG_GRSTCTL_TXFNUM_HALL); + stm32_flush_rxfifo(); + + /* Clear all pending HC Interrupts */ + + for (i = 0; i < STM32_NHOST_CHANNELS; i++) + { + stm32_putreg(STM32_OTG_HCINT(i), 0xffffffff); + stm32_putreg(STM32_OTG_HCINTMSK(i), 0); + } + + /* Driver Vbus +5V (the smoke test). Should be done elsewhere in OTG + * mode. + */ + + stm32_vbusdrive(priv, true); + + /* Enable host interrupts */ + + stm32_hostinit_enable(); +} + +/**************************************************************************** + * Name: stm32_sw_initialize + * + * Description: + * One-time setup of the host driver state structure. + * + * Input Parameters: + * priv -- USB host driver private data structure. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static inline void stm32_sw_initialize(FAR struct stm32_usbhost_s *priv) +{ + FAR struct usbhost_driver_s *drvr; + FAR struct usbhost_hubport_s *hport; + int i; + + /* Initialize the device operations */ + + drvr = &priv->drvr; + drvr->ep0configure = stm32_ep0configure; + drvr->epalloc = stm32_epalloc; + drvr->epfree = stm32_epfree; + drvr->alloc = stm32_alloc; + drvr->free = stm32_free; + drvr->ioalloc = stm32_ioalloc; + drvr->iofree = stm32_iofree; + drvr->ctrlin = stm32_ctrlin; + drvr->ctrlout = stm32_ctrlout; + drvr->transfer = stm32_transfer; +#ifdef CONFIG_USBHOST_ASYNCH + drvr->asynch = stm32_asynch; +#endif + drvr->cancel = stm32_cancel; +#ifdef CONFIG_USBHOST_HUB + drvr->connect = stm32_connect; +#endif + drvr->disconnect = stm32_disconnect; + + /* Initialize the public port representation */ + + hport = &priv->rhport.hport; + hport->drvr = drvr; +#ifdef CONFIG_USBHOST_HUB + hport->parent = NULL; +#endif + hport->ep0 = (usbhost_ep_t)&priv->ep0; + hport->speed = USB_SPEED_FULL; + + /* Initialize function address generation logic */ + + usbhost_devaddr_initialize(&priv->rhport); + + /* Initialize semaphores */ + + sem_init(&priv->pscsem, 0, 0); + sem_init(&priv->exclsem, 0, 1); + + /* Initialize the driver state data */ + + priv->smstate = SMSTATE_DETACHED; + priv->connected = false; + priv->change = false; + + /* Put all of the channels back in their initial, allocated state */ + + memset(priv->chan, 0, STM32_MAX_TX_FIFOS * sizeof(struct stm32_chan_s)); + + /* Initialize each channel */ + + for (i = 0; i < STM32_MAX_TX_FIFOS; i++) + { + FAR struct stm32_chan_s *chan = &priv->chan[i]; + chan->chidx = i; + sem_init(&chan->waitsem, 0, 0); + } +} + +/**************************************************************************** + * Name: stm32_hw_initialize + * + * Description: + * One-time setup of the host controller harware for normal operations. + * + * Input Parameters: + * priv -- USB host driver private data structure. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +static inline int stm32_hw_initialize(FAR struct stm32_usbhost_s *priv) +{ + uint32_t regval; + unsigned long timeout; + + /* Set the PHYSEL bit in the GUSBCFG register to select the OTG FS serial + * transceiver: "This bit is always 1 with write-only access" + */ + + regval = stm32_getreg(STM32_OTG_GUSBCFG); + regval |= OTG_GUSBCFG_PHYSEL; + stm32_putreg(STM32_OTG_GUSBCFG, regval); + + /* Reset after a PHY select and set Host mode. First, wait for AHB master + * IDLE state. + */ + + for (timeout = 0; timeout < STM32_READY_DELAY; timeout++) + { + up_udelay(3); + regval = stm32_getreg(STM32_OTG_GRSTCTL); + if ((regval & OTG_GRSTCTL_AHBIDL) != 0) + { + break; + } + } + + /* Then perform the core soft reset. */ + + stm32_putreg(STM32_OTG_GRSTCTL, OTG_GRSTCTL_CSRST); + for (timeout = 0; timeout < STM32_READY_DELAY; timeout++) + { + regval = stm32_getreg(STM32_OTG_GRSTCTL); + if ((regval & OTG_GRSTCTL_CSRST) == 0) + { + break; + } + } + + /* Wait for 3 PHY Clocks */ + + up_udelay(3); + + /* Deactivate the power down */ + + regval = (OTG_GCCFG_PWRDWN | OTG_GCCFG_VBUSASEN | OTG_GCCFG_VBUSBSEN); +#ifndef CONFIG_USBDEV_VBUSSENSING + regval |= OTG_GCCFG_NOVBUSSENS; +#endif +#ifdef CONFIG_STM32_OTG_SOFOUTPUT + regval |= OTG_GCCFG_SOFOUTEN; +#endif + stm32_putreg(STM32_OTG_GCCFG, regval); + up_mdelay(20); + + /* Initialize OTG features: In order to support OTP, the HNPCAP and SRPCAP + * bits would need to be set in the GUSBCFG register about here. + */ + + /* Force Host Mode */ + + regval = stm32_getreg(STM32_OTG_GUSBCFG); + regval &= ~OTG_GUSBCFG_FDMOD; + regval |= OTG_GUSBCFG_FHMOD; + stm32_putreg(STM32_OTG_GUSBCFG, regval); + up_mdelay(50); + + /* Initialize host mode and return success */ + + stm32_host_initialize(priv); + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_otgfshost_initialize + * + * Description: + * Initialize USB host device controller hardware. + * + * Input Parameters: + * controller -- If the device supports more than USB host controller, then + * this identifies which controller is being initialized. Normally, this + * is just zero. + * + * Returned Value: + * And instance of the USB host interface. The controlling task should + * use this interface to (1) call the wait() method to wait for a device + * to be connected, and (2) call the enumerate() method to bind the device + * to a class driver. + * + * Assumptions: + * - This function should called in the initialization sequence in order + * to initialize the USB device functionality. + * - Class drivers should be initialized prior to calling this function. + * Otherwise, there is a race condition if the device is already connected. + * + ****************************************************************************/ + +FAR struct usbhost_connection_s *stm32_otgfshost_initialize(int controller) +{ + /* At present, there is only support for a single OTG FS host. Hence it is + * pre-allocated as g_usbhost. However, in most code, the private data + * structure will be referenced using the 'priv' pointer (rather than the + * global data) in order to simplify any future support for multiple devices. + */ + + FAR struct stm32_usbhost_s *priv = &g_usbhost; + + /* Sanity checks */ + + DEBUGASSERT(controller == 0); + + /* Make sure that interrupts from the OTG FS core are disabled */ + + stm32_gint_disable(); + + /* Reset the state of the host driver */ + + stm32_sw_initialize(priv); + + /* Alternate function pin configuration. Here we assume that: + * + * 1. GPIOA, SYSCFG, and OTG FS peripheral clocking have already been\ + * enabled as part of the boot sequence. + * 2. Board-specific logic has already enabled other board specific GPIOs + * for things like soft pull-up, VBUS sensing, power controls, and over- + * current detection. + */ + + /* Configure OTG FS alternate function pins for DM, DP, ID, and SOF. + * + * PIN* SIGNAL DIRECTION + * ---- ----------- ---------- + * PA8 OTG_FS_SOF SOF clock output + * PA9 OTG_FS_VBUS VBUS input for device, Driven by external regulator by + * host (not an alternate function) + * PA10 OTG_FS_ID OTG ID pin (only needed in Dual mode) + * PA11 OTG_FS_DM D- I/O + * PA12 OTG_FS_DP D+ I/O + * + * *Pins may vary from device-to-device. + */ + + stm32_configgpio(GPIO_OTG_DM); + stm32_configgpio(GPIO_OTG_DP); + stm32_configgpio(GPIO_OTG_ID); /* Only needed for OTG */ + + /* SOF output pin configuration is configurable */ + +#ifdef CONFIG_STM32_OTG_SOFOUTPUT + stm32_configgpio(GPIO_OTG_SOF); +#endif + + /* Initialize the USB OTG FS core */ + + stm32_hw_initialize(priv); + + /* Attach USB host controller interrupt handler */ + + if (irq_attach(STM32_IRQ_OTGFS, stm32_gint_isr) != 0) + { + usbhost_trace1(OTG_TRACE1_IRQATTACH, 0); + return NULL; + } + + /* Enable USB OTG FS global interrupts */ + + stm32_gint_enable(); + + /* Enable interrupts at the interrupt controller */ + + up_enable_irq(STM32_IRQ_OTGFS); + return &g_usbconn; +} + +#endif /* CONFIG_USBHOST && CONFIG_STM32_OTGFS */ diff --git a/arch/arm/src/stm32f7/stm32_sdmmc.h b/arch/arm/src/stm32f7/stm32_sdmmc.h index 29e14683cea..18e6c446010 100644 --- a/arch/arm/src/stm32f7/stm32_sdmmc.h +++ b/arch/arm/src/stm32f7/stm32_sdmmc.h @@ -1,5 +1,5 @@ /************************************************************************************ - * arch/arm/src/stm32/stm32_sdio.h + * arch/arm/src/stm32f7/stm32_sdio.h * * Copyright (C) 2009, 2011, 2015 Gregory Nutt. All rights reserved. * Author: Gregory Nutt From 6f2e37e3adb3da2bcb3a7fe0bacfc23b7dd9707d Mon Sep 17 00:00:00 2001 From: Lok Tep Date: Sun, 19 Jun 2016 23:06:21 +0200 Subject: [PATCH 03/24] mmc + usb --- arch/arm/src/stm32f7/stm32_otg.h | 32 +- arch/arm/src/stm32f7/stm32_otgdev.c | 17 +- arch/arm/src/stm32f7/stm32_otghost.c | 84 ++--- arch/arm/src/stm32f7/stm32_sdmmc.c | 5 + configs/stm32f746-ws/nsh/defconfig | 86 ++++- configs/stm32f746-ws/src/Makefile | 10 +- .../stm32f746-ws/src/stm32_appinitialize.c | 18 + configs/stm32f746-ws/src/stm32_dma_alloc.c | 117 ++++++ configs/stm32f746-ws/src/stm32_sdmmc.c | 171 +++++++++ configs/stm32f746-ws/src/stm32_usb.c | 340 ++++++++++++++++++ configs/stm32f746-ws/src/stm32f746-ws.h | 33 ++ 11 files changed, 842 insertions(+), 71 deletions(-) create mode 100644 configs/stm32f746-ws/src/stm32_dma_alloc.c create mode 100644 configs/stm32f746-ws/src/stm32_sdmmc.c create mode 100644 configs/stm32f746-ws/src/stm32_usb.c diff --git a/arch/arm/src/stm32f7/stm32_otg.h b/arch/arm/src/stm32f7/stm32_otg.h index 69def146083..0bb6b9b3938 100644 --- a/arch/arm/src/stm32f7/stm32_otg.h +++ b/arch/arm/src/stm32f7/stm32_otg.h @@ -44,10 +44,10 @@ #include - +#include "chip.h" #include "chip/stm32_otg.h" -#if defined(CONFIG_STM32_OTGFS) || defined(CONFIG_STM32_OTGHS) +#if defined(CONFIG_STM32F7_OTGFS) || defined(CONFIG_STM32F7_OTGHS) /************************************************************************************ * Pre-processor Definitions @@ -58,16 +58,26 @@ # define CONFIG_OTG_PRI NVIC_SYSH_PRIORITY_DEFAULT #endif -#if defined(CONFIG_STM32_OTGFS) -# define STM32_IRQ_OTG STM32_IRQ_OTGFS -# define STM32_OTG_BASE STM32_USBOTGFS_BASE -# define STM32_NENDPOINTS (6) /* ep0-5 x 2 for IN and OUT */ +#if defined(CONFIG_STM32F7_OTGFS) +# define STM32_IRQ_OTG STM32_IRQ_OTGFS +# define STM32_OTG_BASE STM32_USBOTGFS_BASE +# define STM32_NENDPOINTS (6) /* ep0-5 x 2 for IN and OUT */ +# define GPIO_OTG_DM GPIO_OTGFS_DM +# define GPIO_OTG_DP GPIO_OTGFS_DP +# define GPIO_OTG_ID GPIO_OTGFS_ID +# define GPIO_OTG_SOF GPIO_OTGFS_SOF + #endif -#if defined(CONFIG_STM32_OTGHS) -# define STM32_IRQ_OTG STM32_IRQ_OTGHS -# define STM32_OTG_BASE STM32_USBOTGHS_BASE -# define STM32_NENDPOINTS (8) /* ep0-7 x 2 for IN and OUT */ +#if defined(CONFIG_STM32F7_OTGHS) +# define STM32_IRQ_OTG STM32_IRQ_OTGHS +# define STM32_OTG_BASE STM32_USBOTGHS_BASE +# define STM32_NENDPOINTS (8) /* ep0-7 x 2 for IN and OUT */ +# define GPIO_OTG_DM GPIO_OTGHS_DM +# define GPIO_OTG_DP GPIO_OTGHS_DP +# define GPIO_OTG_ID GPIO_OTGHS_ID +# define GPIO_OTG_SOF GPIO_OTGHS_SOF + #endif /************************************************************************************ @@ -134,6 +144,6 @@ void stm32_usbsuspend(FAR struct usbdev_s *dev, bool resume); #endif #endif /* __ASSEMBLY__ */ -#endif /* CONFIG_STM32_OTGFS */ +#endif /* CONFIG_STM32F7_OTGFS */ #endif /* __ARCH_ARM_SRC_STM32F7_STM32_OTG_H */ diff --git a/arch/arm/src/stm32f7/stm32_otgdev.c b/arch/arm/src/stm32f7/stm32_otgdev.c index e362ef2b450..d4d46c4efb1 100644 --- a/arch/arm/src/stm32f7/stm32_otgdev.c +++ b/arch/arm/src/stm32f7/stm32_otgdev.c @@ -57,12 +57,13 @@ #include #include "chip.h" +#include "stm32_gpio.h" #include "stm32_otg.h" #include "up_arch.h" #include "up_internal.h" -#if defined(CONFIG_USBDEV) && (defined(CONFIG_STM32_OTGFS) || defined(CONFIG_STM32_OTGHS)) +#if defined(CONFIG_USBDEV) && (defined(CONFIG_STM32F7_OTGFS) || defined(CONFIG_STM32F7_OTGHS)) /**************************************************************************** * Pre-processor Definitions @@ -469,7 +470,7 @@ struct stm32_usbdev_s /* Register operations ********************************************************/ -#if defined(CONFIG_STM32_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG_FEATURES) +#if defined(CONFIG_STM32F7_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG_FEATURES) static uint32_t stm32_getreg(uint32_t addr); static void stm32_putreg(uint32_t val, uint32_t addr); #else @@ -789,7 +790,7 @@ const struct trace_msg_t g_usb_trace_strings_intdecode[] = * ****************************************************************************/ -#if defined(CONFIG_STM32_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG_FEATURES) +#if defined(CONFIG_STM32F7_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG_FEATURES) static uint32_t stm32_getreg(uint32_t addr) { static uint32_t prevaddr = 0; @@ -852,7 +853,7 @@ static uint32_t stm32_getreg(uint32_t addr) * ****************************************************************************/ -#if defined(CONFIG_STM32_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG_FEATURES) +#if defined(CONFIG_STM32F7_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG_FEATURES) static void stm32_putreg(uint32_t val, uint32_t addr) { /* Show the register value being written */ @@ -5134,7 +5135,7 @@ static void stm32_hwinitialize(FAR struct stm32_usbdev_s *priv) stm32_putreg(OTG_GAHBCFG_TXFELVL, STM32_OTG_GAHBCFG); -#if defined(CONFIG_STM32_OTGHS) +#if defined(CONFIG_STM32F7_OTGHS) /* Set the PHYSEL bit in the GUSBCFG register to select the OTG HS serial * transceiver: "This bit is always 1 with write-only access" */ @@ -5329,7 +5330,7 @@ static void stm32_hwinitialize(FAR struct stm32_usbdev_s *priv) stm32_putreg(0xbfffffff, STM32_OTG_GINTSTS); -#ifdef defined(CONFIG_STM32_OTGHS) +#if defined(CONFIG_STM32F7_OTGHS) /* Disable the ULPI Clock enable in RCC AHB1 Register. This must * be done because if both the ULPI and the FS PHY clock enable bits * are set at the same time, the ARM never awakens from WFI due to @@ -5424,7 +5425,7 @@ void up_usbinitialize(void) /* SOF output pin configuration is configurable. */ -#ifdef CONFIG_STM32_OTG_SOFOUTPUT +#ifdef CONFIG_STM32F7_OTG_SOFOUTPUT stm32_configgpio(GPIO_OTG_SOF); #endif @@ -5663,4 +5664,4 @@ int usbdev_unregister(struct usbdevclass_driver_s *driver) return OK; } -#endif /* CONFIG_USBDEV && CONFIG_STM32_OTGDEV */ +#endif /* CONFIG_USBDEV && CONFIG_STM32F7_OTGDEV */ diff --git a/arch/arm/src/stm32f7/stm32_otghost.c b/arch/arm/src/stm32f7/stm32_otghost.c index cc00b1336f2..0feb6c98da0 100644 --- a/arch/arm/src/stm32f7/stm32_otghost.c +++ b/arch/arm/src/stm32f7/stm32_otghost.c @@ -67,7 +67,7 @@ #include "stm32_otg.h" -#if defined(CONFIG_USBHOST) && defined(CONFIG_STM32_OTGFS) +#if defined(CONFIG_USBHOST) && defined(CONFIG_STM32F7_OTGFS) /**************************************************************************** * Pre-processor Definitions @@ -78,61 +78,61 @@ * Pre-requisites * * CONFIG_USBHOST - Enable general USB host support - * CONFIG_STM32_OTGFS - Enable the STM32 USB OTG FS block - * CONFIG_STM32_SYSCFG - Needed + * CONFIG_STM32F7_OTGFS - Enable the STM32 USB OTG FS block + * CONFIG_STM32F7_SYSCFG - Needed * * Options: * - * CONFIG_STM32_OTG_RXFIFO_SIZE - Size of the RX FIFO in 32-bit words. + * CONFIG_STM32F7_OTG_RXFIFO_SIZE - Size of the RX FIFO in 32-bit words. * Default 128 (512 bytes) - * CONFIG_STM32_OTG_NPTXFIFO_SIZE - Size of the non-periodic Tx FIFO + * CONFIG_STM32F7_OTG_NPTXFIFO_SIZE - Size of the non-periodic Tx FIFO * in 32-bit words. Default 96 (384 bytes) - * CONFIG_STM32_OTG_PTXFIFO_SIZE - Size of the periodic Tx FIFO in 32-bit + * CONFIG_STM32F7_OTG_PTXFIFO_SIZE - Size of the periodic Tx FIFO in 32-bit * words. Default 96 (384 bytes) - * CONFIG_STM32_OTG_DESCSIZE - Maximum size of a descriptor. Default: 128 - * CONFIG_STM32_OTG_SOFINTR - Enable SOF interrupts. Why would you ever + * CONFIG_STM32F7_OTG_DESCSIZE - Maximum size of a descriptor. Default: 128 + * CONFIG_STM32F7_OTG_SOFINTR - Enable SOF interrupts. Why would you ever * want to do that? - * CONFIG_STM32_USBHOST_REGDEBUG - Enable very low-level register access + * CONFIG_STM32F7_USBHOST_REGDEBUG - Enable very low-level register access * debug. Depends on CONFIG_DEBUG_FEATURES. - * CONFIG_STM32_USBHOST_PKTDUMP - Dump all incoming and outgoing USB + * CONFIG_STM32F7_USBHOST_PKTDUMP - Dump all incoming and outgoing USB * packets. Depends on CONFIG_DEBUG_FEATURES. */ /* Pre-requisites (partial) */ -#ifndef CONFIG_STM32_SYSCFG -# error "CONFIG_STM32_SYSCFG is required" +#ifndef CONFIG_STM32F7_SYSCFG +# error "CONFIG_STM32F7_SYSCFG is required" #endif /* Default RxFIFO size */ -#ifndef CONFIG_STM32_OTG_RXFIFO_SIZE -# define CONFIG_STM32_OTG_RXFIFO_SIZE 128 +#ifndef CONFIG_STM32F7_OTG_RXFIFO_SIZE +# define CONFIG_STM32F7_OTG_RXFIFO_SIZE 128 #endif /* Default host non-periodic Tx FIFO size */ -#ifndef CONFIG_STM32_OTG_NPTXFIFO_SIZE -# define CONFIG_STM32_OTG_NPTXFIFO_SIZE 96 +#ifndef CONFIG_STM32F7_OTG_NPTXFIFO_SIZE +# define CONFIG_STM32F7_OTG_NPTXFIFO_SIZE 96 #endif /* Default host periodic Tx fifo size register */ -#ifndef CONFIG_STM32_OTG_PTXFIFO_SIZE -# define CONFIG_STM32_OTG_PTXFIFO_SIZE 96 +#ifndef CONFIG_STM32F7_OTG_PTXFIFO_SIZE +# define CONFIG_STM32F7_OTG_PTXFIFO_SIZE 96 #endif /* Maximum size of a descriptor */ -#ifndef CONFIG_STM32_OTG_DESCSIZE -# define CONFIG_STM32_OTG_DESCSIZE 128 +#ifndef CONFIG_STM32F7_OTG_DESCSIZE +# define CONFIG_STM32F7_OTG_DESCSIZE 128 #endif /* Register/packet debug depends on CONFIG_DEBUG_FEATURES */ #ifndef CONFIG_DEBUG_FEATURES -# undef CONFIG_STM32_USBHOST_REGDEBUG -# undef CONFIG_STM32_USBHOST_PKTDUMP +# undef CONFIG_STM32F7_USBHOST_REGDEBUG +# undef CONFIG_STM32F7_USBHOST_PKTDUMP #endif /* HCD Setup *******************************************************************/ @@ -283,7 +283,7 @@ struct stm32_usbhost_s /* Register operations ********************************************************/ -#ifdef CONFIG_STM32_USBHOST_REGDEBUG +#ifdef CONFIG_STM32F7_USBHOST_REGDEBUG static void stm32_printreg(uint32_t addr, uint32_t val, bool iswrite); static void stm32_checkreg(uint32_t addr, uint32_t val, bool iswrite); static uint32_t stm32_getreg(uint32_t addr); @@ -296,7 +296,7 @@ static void stm32_putreg(uint32_t addr, uint32_t value); static inline void stm32_modifyreg(uint32_t addr, uint32_t clrbits, uint32_t setbits); -#ifdef CONFIG_STM32_USBHOST_PKTDUMP +#ifdef CONFIG_STM32F7_USBHOST_PKTDUMP # define stm32_pktdump(m,b,n) lib_dumpbuffer(m,b,n) #else # define stm32_pktdump(m,b,n) @@ -390,7 +390,7 @@ static void stm32_gint_disconnected(FAR struct stm32_usbhost_s *priv); /* Second level interrupt handlers */ -#ifdef CONFIG_STM32_OTG_SOFINTR +#ifdef CONFIG_STM32F7_OTG_SOFINTR static inline void stm32_gint_sofisr(FAR struct stm32_usbhost_s *priv); #endif static inline void stm32_gint_rxflvlisr(FAR struct stm32_usbhost_s *priv); @@ -503,7 +503,7 @@ static struct usbhost_connection_s g_usbconn = * ****************************************************************************/ -#ifdef CONFIG_STM32_USBHOST_REGDEBUG +#ifdef CONFIG_STM32F7_USBHOST_REGDEBUG static void stm32_printreg(uint32_t addr, uint32_t val, bool iswrite) { llerr("%08x%s%08x\n", addr, iswrite ? "<-" : "->", val); @@ -518,7 +518,7 @@ static void stm32_printreg(uint32_t addr, uint32_t val, bool iswrite) * ****************************************************************************/ -#ifdef CONFIG_STM32_USBHOST_REGDEBUG +#ifdef CONFIG_STM32F7_USBHOST_REGDEBUG static void stm32_checkreg(uint32_t addr, uint32_t val, bool iswrite) { static uint32_t prevaddr = 0; @@ -582,7 +582,7 @@ static void stm32_checkreg(uint32_t addr, uint32_t val, bool iswrite) * ****************************************************************************/ -#ifdef CONFIG_STM32_USBHOST_REGDEBUG +#ifdef CONFIG_STM32F7_USBHOST_REGDEBUG static uint32_t stm32_getreg(uint32_t addr) { /* Read the value from the register */ @@ -604,7 +604,7 @@ static uint32_t stm32_getreg(uint32_t addr) * ****************************************************************************/ -#ifdef CONFIG_STM32_USBHOST_REGDEBUG +#ifdef CONFIG_STM32F7_USBHOST_REGDEBUG static void stm32_putreg(uint32_t addr, uint32_t val) { /* Check if we need to print this value */ @@ -2907,7 +2907,7 @@ static void stm32_gint_disconnected(FAR struct stm32_usbhost_s *priv) * ****************************************************************************/ -#ifdef CONFIG_STM32_OTG_SOFINTR +#ifdef CONFIG_STM32F7_OTG_SOFINTR static inline void stm32_gint_sofisr(FAR struct stm32_usbhost_s *priv) { /* Handle SOF interrupt */ @@ -3469,7 +3469,7 @@ static int stm32_gint_isr(int irq, FAR void *context) /* Handle the start of frame interrupt */ -#ifdef CONFIG_STM32_OTG_SOFINTR +#ifdef CONFIG_STM32F7_OTG_SOFINTR if ((pending & OTG_GINT_SOF) != 0) { usbhost_vtrace1(OTG_VTRACE1_GINT_SOF, 0); @@ -3634,7 +3634,7 @@ static inline void stm32_hostinit_enable(void) * OTG_GINT_DISC : Disconnect detected interrupt */ -#ifdef CONFIG_STM32_OTG_SOFINTR +#ifdef CONFIG_STM32F7_OTG_SOFINTR regval |= (OTG_GINT_SOF | OTG_GINT_RXFLVL | OTG_GINT_IISOOXFR | OTG_GINT_HPRT | OTG_GINT_HC | OTG_GINT_DISC); #else @@ -4138,7 +4138,7 @@ static int stm32_alloc(FAR struct usbhost_driver_s *drvr, /* There is no special memory requirement for the STM32. */ - alloc = (FAR uint8_t *)kmm_malloc(CONFIG_STM32_OTG_DESCSIZE); + alloc = (FAR uint8_t *)kmm_malloc(CONFIG_STM32F7_OTG_DESCSIZE); if (!alloc) { return -ENOMEM; @@ -4147,7 +4147,7 @@ static int stm32_alloc(FAR struct usbhost_driver_s *drvr, /* Return the allocated address and size of the descriptor buffer */ *buffer = alloc; - *maxlen = CONFIG_STM32_OTG_DESCSIZE; + *maxlen = CONFIG_STM32F7_OTG_DESCSIZE; return OK; } @@ -4987,18 +4987,18 @@ static void stm32_host_initialize(FAR struct stm32_usbhost_s *priv) /* Carve up FIFO memory for the Rx FIFO and the periodic and non-periodic Tx FIFOs */ /* Configure Rx FIFO size (GRXFSIZ) */ - stm32_putreg(STM32_OTG_GRXFSIZ, CONFIG_STM32_OTG_RXFIFO_SIZE); - offset = CONFIG_STM32_OTG_RXFIFO_SIZE; + stm32_putreg(STM32_OTG_GRXFSIZ, CONFIG_STM32F7_OTG_RXFIFO_SIZE); + offset = CONFIG_STM32F7_OTG_RXFIFO_SIZE; /* Setup the host non-periodic Tx FIFO size (HNPTXFSIZ) */ - regval = (offset | (CONFIG_STM32_OTG_NPTXFIFO_SIZE << OTG_HNPTXFSIZ_NPTXFD_SHIFT)); + regval = (offset | (CONFIG_STM32F7_OTG_NPTXFIFO_SIZE << OTG_HNPTXFSIZ_NPTXFD_SHIFT)); stm32_putreg(STM32_OTG_HNPTXFSIZ, regval); - offset += CONFIG_STM32_OTG_NPTXFIFO_SIZE; + offset += CONFIG_STM32F7_OTG_NPTXFIFO_SIZE; /* Set up the host periodic Tx fifo size register (HPTXFSIZ) */ - regval = (offset | (CONFIG_STM32_OTG_PTXFIFO_SIZE << OTG_HPTXFSIZ_PTXFD_SHIFT)); + regval = (offset | (CONFIG_STM32F7_OTG_PTXFIFO_SIZE << OTG_HPTXFSIZ_PTXFD_SHIFT)); stm32_putreg(STM32_OTG_HPTXFSIZ, regval); /* If OTG were supported, we sould need to clear HNP enable bit in the @@ -5173,7 +5173,7 @@ static inline int stm32_hw_initialize(FAR struct stm32_usbhost_s *priv) #ifndef CONFIG_USBDEV_VBUSSENSING regval |= OTG_GCCFG_NOVBUSSENS; #endif -#ifdef CONFIG_STM32_OTG_SOFOUTPUT +#ifdef CONFIG_STM32F7_OTG_SOFOUTPUT regval |= OTG_GCCFG_SOFOUTEN; #endif stm32_putreg(STM32_OTG_GCCFG, regval); @@ -5277,7 +5277,7 @@ FAR struct usbhost_connection_s *stm32_otgfshost_initialize(int controller) /* SOF output pin configuration is configurable */ -#ifdef CONFIG_STM32_OTG_SOFOUTPUT +#ifdef CONFIG_STM32F7_OTG_SOFOUTPUT stm32_configgpio(GPIO_OTG_SOF); #endif @@ -5303,4 +5303,4 @@ FAR struct usbhost_connection_s *stm32_otgfshost_initialize(int controller) return &g_usbconn; } -#endif /* CONFIG_USBHOST && CONFIG_STM32_OTGFS */ +#endif /* CONFIG_USBHOST && CONFIG_STM32F7_OTGFS */ diff --git a/arch/arm/src/stm32f7/stm32_sdmmc.c b/arch/arm/src/stm32f7/stm32_sdmmc.c index 58f7b7318df..4f2b7a8d01b 100644 --- a/arch/arm/src/stm32f7/stm32_sdmmc.c +++ b/arch/arm/src/stm32f7/stm32_sdmmc.c @@ -964,6 +964,11 @@ static void stm32_dataconfig(uint32_t timeout, uint32_t dlen, uint32_t dctrl) regval &= ~(SDIO_DCTRL_DTDIR | SDIO_DCTRL_DTMODE | SDIO_DCTRL_DBLOCKSIZE_MASK); dctrl &= (SDIO_DCTRL_DTDIR | SDIO_DCTRL_DTMODE | SDIO_DCTRL_DBLOCKSIZE_MASK); regval |= (dctrl | SDIO_DCTRL_DTEN); + +#ifdef CONFIG_SDIO_DMA + regval |= SDIO_DCTRL_DMAEN; +#endif + putreg32(regval, STM32_SDMMC1_DCTRL); } diff --git a/configs/stm32f746-ws/nsh/defconfig b/configs/stm32f746-ws/nsh/defconfig index 97b867c160d..44a25aec95a 100644 --- a/configs/stm32f746-ws/nsh/defconfig +++ b/configs/stm32f746-ws/nsh/defconfig @@ -52,6 +52,12 @@ CONFIG_ARCH_HAVE_CUSTOMOPT=y CONFIG_DEBUG_NOOPT=y # CONFIG_DEBUG_CUSTOMOPT is not set # CONFIG_DEBUG_FULLOPT is not set +CONFIG_DEBUG_ERROR=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_WARN=y +CONFIG_DEBUG_FS_INFO=y +CONFIG_DEBUG_FS_ERROR=y +CONFIG_DEBUG_FS_WARN=y # # System Type @@ -267,7 +273,7 @@ CONFIG_STM32F7_HAVE_DMA2D=y CONFIG_STM32F7_ADC=y # CONFIG_STM32F7_CAN is not set # CONFIG_STM32F7_DAC is not set -# CONFIG_STM32F7_DMA is not set +CONFIG_STM32F7_DMA=y CONFIG_STM32F7_I2C=y # CONFIG_STM32F7_SAI is not set CONFIG_STM32F7_SPI=y @@ -282,7 +288,7 @@ CONFIG_STM32F7_ADC1=y # CONFIG_STM32F7_CEC is not set # CONFIG_STM32F7_CRC is not set # CONFIG_STM32F7_DMA1 is not set -# CONFIG_STM32F7_DMA2 is not set +CONFIG_STM32F7_DMA2=y # CONFIG_STM32F7_DAC1 is not set # CONFIG_STM32F7_DAC2 is not set # CONFIG_STM32F7_DCMI is not set @@ -294,13 +300,13 @@ CONFIG_STM32F7_I2C1=y # CONFIG_STM32F7_I2C3 is not set # CONFIG_STM32F7_LPTIM1 is not set # CONFIG_STM32F7_LTDC is not set -# CONFIG_STM32F7_OTGFS is not set +##CONFIG_STM32F7_OTGFS=y # CONFIG_STM32F7_OTGHS is not set # CONFIG_STM32F7_QUADSPI is not set # CONFIG_STM32F7_RNG is not set # CONFIG_STM32F7_SAI1 is not set # CONFIG_STM32F7_SAI2 is not set -# CONFIG_STM32F7_SDMMC1 is not set +CONFIG_STM32F7_SDMMC1=y # CONFIG_STM32F7_SPDIFRX is not set CONFIG_STM32F7_SPI1=y # CONFIG_STM32F7_SPI2 is not set @@ -352,7 +358,7 @@ CONFIG_STM32F7_USART6=y # # CONFIG_ARCH_NOINTC is not set # CONFIG_ARCH_VECNOTIRQ is not set -# CONFIG_ARCH_DMA is not set +CONFIG_ARCH_DMA=y CONFIG_ARCH_HAVE_IRQPRIO=y # CONFIG_ARCH_L2CACHE is not set # CONFIG_ARCH_HAVE_COHERENT_DCACHE is not set @@ -416,6 +422,7 @@ CONFIG_ARCH_BOARD="stm32f746-ws" # Common Board Options # CONFIG_NSH_MMCSDMINOR=0 +CONFIG_NSH_MMCSDSLOTNO=0 # # Board-Specific Options @@ -427,6 +434,7 @@ CONFIG_LIB_BOARDCTL=y # CONFIG_BOARDCTL_PWMTEST is not set # CONFIG_BOARDCTL_GRAPHICS is not set # CONFIG_BOARDCTL_IOCTL is not set +##CONFIG_BOARDCTL_USBDEVCTRL=y # # RTOS Features @@ -550,6 +558,17 @@ CONFIG_DEV_NULL=y # CONFIG_DEV_ZERO is not set # CONFIG_DEV_LOOP is not set +CONFIG_MMCSD=y +CONFIG_MMCSD_NSLOTS=1 +# CONFIG_MMCSD_READONLY is not set +CONFIG_MMCSD_MULTIBLOCK_DISABLE=y +# CONFIG_MMCSD_MMCSUPPORT is not set +CONFIG_MMCSD_HAVECARDDETECT=y +# CONFIG_MMCSD_SPI is not set +CONFIG_ARCH_HAVE_SDIO=y +CONFIG_ARCH_HAVE_SDIOWAIT_WRCOMPLETE=y +CONFIG_MMCSD_SDIO=y + # # Buffering # @@ -579,6 +598,8 @@ CONFIG_SPI_EXCHANGE=y # CONFIG_SPI_CS_DELAY_CONTROL is not set # CONFIG_I2S is not set +CONFIG_SDIO_DMA=y + # # Timer Driver Support # @@ -669,7 +690,50 @@ CONFIG_USART6_2STOP=0 # CONFIG_USART6_IFLOWCONTROL is not set # CONFIG_USART6_OFLOWCONTROL is not set # CONFIG_USART6_DMA is not set -# CONFIG_USBDEV is not set +##CONFIG_USBDEV=y +# CONFIG_USBHOST is not set +# CONFIG_DRIVERS_WIRELESS is not set + + +# +# USB Device Controller Driver Options +# +# CONFIG_USBDEV_ISOCHRONOUS is not set +# CONFIG_USBDEV_DUALSPEED is not set +##CONFIG_USBDEV_SELFPOWERED=y +# CONFIG_USBDEV_BUSPOWERED is not set +##CONFIG_USBDEV_MAXPOWER=100 +# CONFIG_USBDEV_DMA is not set +# CONFIG_ARCH_USBDEV_STALLQUEUE is not set +# CONFIG_USBDEV_TRACE is not set + +# +# USB Device Class Driver Options +# +# CONFIG_USBDEV_COMPOSITE is not set +# CONFIG_PL2303 is not set +##CONFIG_CDCACM=y +##CONFIG_CDCACM_CONSOLE=y +##CONFIG_CDCACM_EP0MAXPACKET=64 +##CONFIG_CDCACM_EPINTIN=1 +##CONFIG_CDCACM_EPINTIN_FSSIZE=64 +##CONFIG_CDCACM_EPINTIN_HSSIZE=64 +##CONFIG_CDCACM_EPBULKOUT=3 +##CONFIG_CDCACM_EPBULKOUT_FSSIZE=64 +##CONFIG_CDCACM_EPBULKOUT_HSSIZE=512 +##CONFIG_CDCACM_EPBULKIN=2 +##CONFIG_CDCACM_EPBULKIN_FSSIZE=64 +##CONFIG_CDCACM_EPBULKIN_HSSIZE=512 +##CONFIG_CDCACM_NRDREQS=4 +##CONFIG_CDCACM_NWRREQS=4 +##CONFIG_CDCACM_BULKIN_REQLEN=96 +##CONFIG_CDCACM_RXBUFSIZE=256 +##CONFIG_CDCACM_TXBUFSIZE=256 +##CONFIG_CDCACM_VENDORID=0x0525 +##CONFIG_CDCACM_PRODUCTID=0xa4a7 +##CONFIG_CDCACM_VENDORSTR="NuttX" +##CONFIG_CDCACM_PRODUCTSTR="CDC/ACM Serial" +# CONFIG_USBMSC is not set # CONFIG_USBHOST is not set # CONFIG_DRIVERS_WIRELESS is not set @@ -706,13 +770,17 @@ CONFIG_USART6_2STOP=0 # CONFIG_DISABLE_MOUNTPOINT is not set # CONFIG_FS_AUTOMOUNTER is not set # CONFIG_DISABLE_PSEUDOFS_OPERATIONS is not set -# CONFIG_FS_READABLE is not set -# CONFIG_FS_WRITABLE is not set +CONFIG_FS_READABLE=y +CONFIG_FS_WRITABLE=y # CONFIG_FS_AIO is not set # CONFIG_FS_NAMED_SEMAPHORES is not set CONFIG_FS_MQUEUE_MPATH="/var/mqueue" # CONFIG_FS_RAMMAP is not set -# CONFIG_FS_FAT is not set +CONFIG_FS_FAT=y +CONFIG_FAT_LCNAMES=y +CONFIG_FAT_LFN=y +CONFIG_FAT_MAXFNAME=32 +CONFIG_FAT_DMAMEMORY=y # CONFIG_FS_NXFFS is not set # CONFIG_FS_ROMFS is not set # CONFIG_FS_TMPFS is not set diff --git a/configs/stm32f746-ws/src/Makefile b/configs/stm32f746-ws/src/Makefile index b20f916b7e0..4096729548c 100644 --- a/configs/stm32f746-ws/src/Makefile +++ b/configs/stm32f746-ws/src/Makefile @@ -36,10 +36,18 @@ -include $(TOPDIR)/Make.defs ASRCS = -CSRCS = stm32_boot.c stm32_spi.c +CSRCS = stm32_boot.c stm32_spi.c stm32_dma_alloc.c ifeq ($(CONFIG_LIB_BOARDCTL),y) CSRCS += stm32_appinitialize.c endif +ifeq ($(CONFIG_STM32F7_OTGFS),y) +CSRCS += stm32_usb.c +endif + +ifeq ($(CONFIG_STM32F7_SDMMC1),y) +CSRCS += stm32_sdmmc.c +endif + include $(TOPDIR)/configs/Board.mk diff --git a/configs/stm32f746-ws/src/stm32_appinitialize.c b/configs/stm32f746-ws/src/stm32_appinitialize.c index 734e35957a8..689bea75fe8 100644 --- a/configs/stm32f746-ws/src/stm32_appinitialize.c +++ b/configs/stm32f746-ws/src/stm32_appinitialize.c @@ -93,5 +93,23 @@ int board_app_initialize(void) stm32_i2ctool(); +#if defined(CONFIG_FAT_DMAMEMORY) + if (stm32_dma_alloc_init() < 0) + { + syslog(LOG_ERR, "DMA alloc FAILED"); + } +#endif + + /* Initialize the SDIO block driver */ + + int ret = OK; + + ret = stm32_sdio_initialize(); + if (ret != OK) + { + ferr("ERROR: Failed to initialize MMC/SD driver: %d\n", ret); + return ret; + } + return OK; } diff --git a/configs/stm32f746-ws/src/stm32_dma_alloc.c b/configs/stm32f746-ws/src/stm32_dma_alloc.c new file mode 100644 index 00000000000..e6d2744529a --- /dev/null +++ b/configs/stm32f746-ws/src/stm32_dma_alloc.c @@ -0,0 +1,117 @@ +/**************************************************************************** + * configs/nucleo-144/stc/stm32_dma_alloc.c + * + * Copyright (C) 2016 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include +#include +#include +#include +#include + +#include "stm32f746-ws.h" + +#if defined(CONFIG_FAT_DMAMEMORY) + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +#if !defined(CONFIG_GRAN) +# error microSD DMA support requires CONFIG_GRAN +#endif + +#define BOARD_DMA_ALLOC_POOL_SIZE (8*512) + +/************************************************************************************ + * Private Data + ************************************************************************************/ + +static GRAN_HANDLE dma_allocator; + +/* The DMA heap size constrains the total number of things that can be + * ready to do DMA at a time. + * + * For example, FAT DMA depends on one sector-sized buffer per filesystem plus + * one sector-sized buffer per file. + * + * We use a fundamental alignment / granule size of 64B; this is sufficient + * to guarantee alignment for the largest STM32 DMA burst (16 beats x 32bits). + */ + +static uint8_t g_dma_heap[BOARD_DMA_ALLOC_POOL_SIZE] __attribute__((aligned(64))); + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: stm32_dma_alloc_init + * + * Description: + * All boards may optionally provide this API to instantiate a pool of + * memory for uses with FAST FS DMA operations. + * + ************************************************************************************/ + +int stm32_dma_alloc_init(void) +{ + dma_allocator = gran_initialize(g_dma_heap, + sizeof(g_dma_heap), + 7, /* 128B granule - must be > alignment (XXX bug?) */ + 6); /* 64B alignment */ + + if (dma_allocator == NULL) + { + return -ENOMEM; + } + + return OK; +} + +/* DMA-aware allocator stubs for the FAT filesystem. */ + +void *fat_dma_alloc(size_t size) +{ + return gran_alloc(dma_allocator, size); +} + +void fat_dma_free(FAR void *memory, size_t size) +{ + gran_free(dma_allocator, memory, size); +} + +#endif /* CONFIG_FAT_DMAMEMORY */ diff --git a/configs/stm32f746-ws/src/stm32_sdmmc.c b/configs/stm32f746-ws/src/stm32_sdmmc.c new file mode 100644 index 00000000000..bdbe7a7f655 --- /dev/null +++ b/configs/stm32f746-ws/src/stm32_sdmmc.c @@ -0,0 +1,171 @@ +/**************************************************************************** + * config/stm32f746-ws/src/stm32_sdio.c + * + * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include + +#include "stm32_sdmmc.h" +#include "stm32f746-ws.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* Configuration ************************************************************/ + +/* Card detections requires card support and a card detection GPIO */ + +#define HAVE_NCD 1 +#if !defined(HAVE_SDIO) || !defined(GPIO_SDIO_NCD) +# undef HAVE_NCD +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static FAR struct sdio_dev_s *g_sdio_dev; +#ifdef HAVE_NCD +static bool g_sd_inserted = 0xff; /* Impossible value */ +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_ncd_interrupt + * + * Description: + * Card detect interrupt handler. + * + ****************************************************************************/ + +#ifdef HAVE_NCD +static int stm32_ncd_interrupt(int irq, FAR void *context) +{ + bool present; + + present = !stm32_gpioread(GPIO_SDIO_NCD); + if (present != g_sd_inserted) + { + sdio_mediachange(g_sdio_dev, present); + g_sd_inserted = present; + } + + return OK; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_sdio_initialize + * + * Description: + * Initialize SDIO-based MMC/SD card support + * + ****************************************************************************/ + +int stm32_sdio_initialize(void) +{ + int ret; + +#ifdef HAVE_NCD + /* Card detect */ + + bool cd_status; + + /* Configure the card detect GPIO */ + + stm32_configgpio(GPIO_SDIO_NCD); + + /* Register an interrupt handler for the card detect pin */ + + stm32_gpiosetevent(GPIO_SDIO_NCD, true, true, true, stm32_ncd_interrupt); +#endif + + /* Mount the SDIO-based MMC/SD block driver */ + /* First, get an instance of the SDIO interface */ + + finfo("Initializing SDIO slot %d\n", SDIO_SLOTNO); + + g_sdio_dev = sdio_initialize(SDIO_SLOTNO); + if (!g_sdio_dev) + { + ferr("ERROR: Failed to initialize SDIO slot %d\n", SDIO_SLOTNO); + return -ENODEV; + } + + /* Now bind the SDIO interface to the MMC/SD driver */ + + finfo("Bind SDIO to the MMC/SD driver, minor=%d\n", SDIO_MINOR); + + ret = mmcsd_slotinitialize(SDIO_MINOR, g_sdio_dev); + if (ret != OK) + { + ferr("ERROR: Failed to bind SDIO to the MMC/SD driver: %d\n", ret); + return ret; + } + + finfo("Successfully bound SDIO to the MMC/SD driver\n"); + +#ifdef HAVE_NCD + /* Use SD card detect pin to check if a card is g_sd_inserted */ + + cd_status = !stm32_gpioread(GPIO_SDIO_NCD); + finfo("Card detect : %d\n", cd_status); + + sdio_mediachange(g_sdio_dev, cd_status); +#else + /* Assume that the SD card is inserted. What choice do we have? */ + + sdio_mediachange(g_sdio_dev, true); +#endif + + return OK; +} diff --git a/configs/stm32f746-ws/src/stm32_usb.c b/configs/stm32f746-ws/src/stm32_usb.c new file mode 100644 index 00000000000..b81aacd6caa --- /dev/null +++ b/configs/stm32f746-ws/src/stm32_usb.c @@ -0,0 +1,340 @@ +/************************************************************************************ + * configs/stm32f4discovery/src/stm32_usb.c + * + * Copyright (C) 2012-2013, 2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "up_arch.h" +#include "stm32_otg.h" +#include "stm32_gpio.h" +#include "stm32f746-ws.h" + +#ifdef CONFIG_STM32F7_OTGFS + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +#if defined(CONFIG_USBDEV) || defined(CONFIG_USBHOST) +# define HAVE_USB 1 +#else +# warning "CONFIG_STM32F7_OTGFS is enabled but neither CONFIG_USBDEV nor CONFIG_USBHOST" +# undef HAVE_USB +#endif + +#ifndef CONFIG_STM32F7F4DISCO_USBHOST_PRIO +# define CONFIG_STM32F7F4DISCO_USBHOST_PRIO 100 +#endif + +#ifndef CONFIG_STM32F7F4DISCO_USBHOST_STACKSIZE +# define CONFIG_STM32F7F4DISCO_USBHOST_STACKSIZE 1024 +#endif + +/************************************************************************************ + * Private Data + ************************************************************************************/ + +#ifdef CONFIG_USBHOST +static struct usbhost_connection_s *g_usbconn; +#endif + +/************************************************************************************ + * Private Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: usbhost_waiter + * + * Description: + * Wait for USB devices to be connected. + * + ************************************************************************************/ + +#ifdef CONFIG_USBHOST +static int usbhost_waiter(int argc, char *argv[]) +{ + struct usbhost_hubport_s *hport; + + uinfo("Running\n"); + for (;;) + { + /* Wait for the device to change state */ + + DEBUGVERIFY(CONN_WAIT(g_usbconn, &hport)); + uinfo("%s\n", hport->connected ? "connected" : "disconnected"); + + /* Did we just become connected? */ + + if (hport->connected) + { + /* Yes.. enumerate the newly connected device */ + + (void)CONN_ENUMERATE(g_usbconn, hport); + } + } + + /* Keep the compiler from complaining */ + + return 0; +} +#endif + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: stm32_usbinitialize + * + * Description: + * Called from stm32_usbinitialize very early in inialization to setup USB-related + * GPIO pins for the STM32F4Discovery board. + * + ************************************************************************************/ + +void stm32_usbinitialize(void) +{ + /* The OTG FS has an internal soft pull-up. No GPIO configuration is required */ + + /* Configure the OTG FS VBUS sensing GPIO, Power On, and Overcurrent GPIOs */ + +#ifdef CONFIG_STM32F7_OTGFS + stm32_configgpio(GPIO_OTGFS_VBUS); + +#ifdef CONFIG_USBHOST + stm32_configgpio(GPIO_OTGFS_PWRON); + stm32_configgpio(GPIO_OTGFS_OVER); +#endif + +#endif +} + +/*********************************************************************************** + * Name: stm32_usbhost_initialize + * + * Description: + * Called at application startup time to initialize the USB host functionality. + * This function will start a thread that will monitor for device + * connection/disconnection events. + * + ***********************************************************************************/ + +#ifdef CONFIG_USBHOST +int stm32_usbhost_initialize(void) +{ + int pid; +#if defined(CONFIG_USBHOST_HUB) || defined(CONFIG_USBHOST_MSC) || \ + defined(CONFIG_USBHOST_HIDKBD) || defined(CONFIG_USBHOST_HIDMOUSE) + int ret; +#endif + + /* First, register all of the class drivers needed to support the drivers + * that we care about: + */ + + uinfo("Register class drivers\n"); + +#ifdef CONFIG_USBHOST_HUB + /* Initialize USB hub class support */ + + ret = usbhost_hub_initialize(); + if (ret < 0) + { + uerr("ERROR: usbhost_hub_initialize failed: %d\n", ret); + } +#endif + +#ifdef CONFIG_USBHOST_MSC + /* Register the USB mass storage class class */ + + ret = usbhost_msc_initialize(); + if (ret != OK) + { + uerr("ERROR: Failed to register the mass storage class: %d\n", ret); + } +#endif + +#ifdef CONFIG_USBHOST_CDCACM + /* Register the CDC/ACM serial class */ + + ret = usbhost_cdcacm_initialize(); + if (ret != OK) + { + uerr("ERROR: Failed to register the CDC/ACM serial class: %d\n", ret); + } +#endif + +#ifdef CONFIG_USBHOST_HIDKBD + /* Initialize the HID keyboard class */ + + ret = usbhost_kbdinit(); + if (ret != OK) + { + uerr("ERROR: Failed to register the HID keyboard class\n"); + } +#endif + +#ifdef CONFIG_USBHOST_HIDMOUSE + /* Initialize the HID mouse class */ + + ret = usbhost_mouse_init(); + if (ret != OK) + { + uerr("ERROR: Failed to register the HID mouse class\n"); + } +#endif + + /* Then get an instance of the USB host interface */ + + uinfo("Initialize USB host\n"); + g_usbconn = stm32_otgfshost_initialize(0); + if (g_usbconn) + { + /* Start a thread to handle device connection. */ + + uinfo("Start usbhost_waiter\n"); + + pid = task_create("usbhost", CONFIG_STM32F7F4DISCO_USBHOST_PRIO, + CONFIG_STM32F7F4DISCO_USBHOST_STACKSIZE, + (main_t)usbhost_waiter, (FAR char * const *)NULL); + return pid < 0 ? -ENOEXEC : OK; + } + + return -ENODEV; +} +#endif + +/*********************************************************************************** + * Name: stm32_usbhost_vbusdrive + * + * Description: + * Enable/disable driving of VBUS 5V output. This function must be provided be + * each platform that implements the STM32 OTG FS host interface + * + * "On-chip 5 V VBUS generation is not supported. For this reason, a charge pump + * or, if 5 V are available on the application board, a basic power switch, must + * be added externally to drive the 5 V VBUS line. The external charge pump can + * be driven by any GPIO output. When the application decides to power on VBUS + * using the chosen GPIO, it must also set the port power bit in the host port + * control and status register (PPWR bit in OTG_FS_HPRT). + * + * "The application uses this field to control power to this port, and the core + * clears this bit on an overcurrent condition." + * + * Input Parameters: + * iface - For future growth to handle multiple USB host interface. Should be zero. + * enable - true: enable VBUS power; false: disable VBUS power + * + * Returned Value: + * None + * + ***********************************************************************************/ + +#ifdef CONFIG_USBHOST +void stm32_usbhost_vbusdrive(int iface, bool enable) +{ + DEBUGASSERT(iface == 0); + + if (enable) + { + /* Enable the Power Switch by driving the enable pin low */ + + stm32_gpiowrite(GPIO_OTGFS_PWRON, false); + } + else + { + /* Disable the Power Switch by driving the enable pin high */ + + stm32_gpiowrite(GPIO_OTGFS_PWRON, true); + } +} +#endif + +/************************************************************************************ + * Name: stm32_setup_overcurrent + * + * Description: + * Setup to receive an interrupt-level callback if an overcurrent condition is + * detected. + * + * Input Parameter: + * handler - New overcurrent interrupt handler + * + * Returned value: + * Old overcurrent interrupt handler + * + ************************************************************************************/ + +#ifdef CONFIG_USBHOST +xcpt_t stm32_setup_overcurrent(xcpt_t handler) +{ + return stm32_gpiosetevent(GPIO_OTGFS_OVER, true, true, true, handler); +} +#endif + +/************************************************************************************ + * Name: stm32_usbsuspend + * + * Description: + * Board logic must provide the stm32_usbsuspend logic if the USBDEV driver is + * used. This function is called whenever the USB enters or leaves suspend mode. + * This is an opportunity for the board logic to shutdown clocks, power, etc. + * while the USB is suspended. + * + ************************************************************************************/ + +#ifdef CONFIG_USBDEV +void stm32_usbsuspend(FAR struct usbdev_s *dev, bool resume) +{ + ullinfo("resume: %d\n", resume); +} +#endif + +#endif /* CONFIG_STM32F7_OTGFS */ diff --git a/configs/stm32f746-ws/src/stm32f746-ws.h b/configs/stm32f746-ws/src/stm32f746-ws.h index 07d2c6c11f9..0a61a7b162f 100644 --- a/configs/stm32f746-ws/src/stm32f746-ws.h +++ b/configs/stm32f746-ws/src/stm32f746-ws.h @@ -82,6 +82,12 @@ #define GPIO_BTN_USER (GPIO_INPUT | GPIO_FLOAT | GPIO_EXTI | GPIO_PORTC | GPIO_PIN13) +#define GPIO_OTGFS_VBUS (GPIO_INPUT|GPIO_FLOAT|GPIO_SPEED_100MHz|\ + GPIO_OPENDRAIN|GPIO_PORTA|GPIO_PIN9) + +#define SDIO_SLOTNO 0 +#define SDIO_MINOR 0 + /**************************************************************************************************** * Public data ****************************************************************************************************/ @@ -102,5 +108,32 @@ void weak_function stm32_spidev_initialize(void); + /**************************************************************************** + * Name: stm32_sdio_initialize + * + * Description: + * Initialize SDIO-based MMC/SD card support + * + ****************************************************************************/ + + #if !defined(CONFIG_DISABLE_MOUNTPOINT) && defined(CONFIG_STM32F7_SDMMC1) + int stm32_sdio_initialize(void); + #endif + + /************************************************************************************ + * Name: stm32_dma_alloc_init + * + * Description: + * Called to create a FAT DMA allocator + * + * Returned Value: + * 0 on success or -ENOMEM + * + ************************************************************************************/ + + #if defined (CONFIG_FAT_DMAMEMORY) + int stm32_dma_alloc_init(void); + #endif + #endif /* __ASSEMBLY__ */ #endif /* __CONFIGS_STM32F746_WS_SRC_STM32F746_WS_H */ From 495e475357d8cf0e2219b116ee5fc935d108b216 Mon Sep 17 00:00:00 2001 From: Lok Tep Date: Mon, 20 Jun 2016 10:55:38 +0200 Subject: [PATCH 04/24] ignore CRC for SD_CMD55 --- arch/arm/src/stm32f7/stm32_sdmmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/src/stm32f7/stm32_sdmmc.c b/arch/arm/src/stm32f7/stm32_sdmmc.c index 4f2b7a8d01b..c1eb3542d7f 100644 --- a/arch/arm/src/stm32f7/stm32_sdmmc.c +++ b/arch/arm/src/stm32f7/stm32_sdmmc.c @@ -2093,7 +2093,7 @@ static int stm32_recvshortcrc(FAR struct sdio_dev_s *dev, uint32_t cmd, uint32_t else #endif { - /* Check if a timeout or CRC error occurred */ + /* Check if a timeout or CRC error occurred (not for SD_CMD55 - see ERRATA) */ regval = getreg32(STM32_SDMMC1_STA); if ((regval & SDIO_STA_CTIMEOUT) != 0) @@ -2101,7 +2101,7 @@ static int stm32_recvshortcrc(FAR struct sdio_dev_s *dev, uint32_t cmd, uint32_t ferr("ERROR: Command timeout: %08x\n", regval); ret = -ETIMEDOUT; } - else if ((regval & SDIO_STA_CCRCFAIL) != 0) + else if (cmd != SD_CMD55 && (regval & SDIO_STA_CCRCFAIL) != 0) { ferr("ERROR: CRC failure: %08x\n", regval); ret = -EIO; From e70ac97cef06b86b54af3e2c199b40695b8874b3 Mon Sep 17 00:00:00 2001 From: Lok Tep Date: Tue, 21 Jun 2016 13:17:50 +0200 Subject: [PATCH 05/24] should be cmd5 not cmd55 --- arch/arm/src/stm32f7/stm32_sdmmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/src/stm32f7/stm32_sdmmc.c b/arch/arm/src/stm32f7/stm32_sdmmc.c index c1eb3542d7f..4f2b7a8d01b 100644 --- a/arch/arm/src/stm32f7/stm32_sdmmc.c +++ b/arch/arm/src/stm32f7/stm32_sdmmc.c @@ -2093,7 +2093,7 @@ static int stm32_recvshortcrc(FAR struct sdio_dev_s *dev, uint32_t cmd, uint32_t else #endif { - /* Check if a timeout or CRC error occurred (not for SD_CMD55 - see ERRATA) */ + /* Check if a timeout or CRC error occurred */ regval = getreg32(STM32_SDMMC1_STA); if ((regval & SDIO_STA_CTIMEOUT) != 0) @@ -2101,7 +2101,7 @@ static int stm32_recvshortcrc(FAR struct sdio_dev_s *dev, uint32_t cmd, uint32_t ferr("ERROR: Command timeout: %08x\n", regval); ret = -ETIMEDOUT; } - else if (cmd != SD_CMD55 && (regval & SDIO_STA_CCRCFAIL) != 0) + else if ((regval & SDIO_STA_CCRCFAIL) != 0) { ferr("ERROR: CRC failure: %08x\n", regval); ret = -EIO; From bdfd3e824834b1faf47ffcb5b32a8a2bed202617 Mon Sep 17 00:00:00 2001 From: Lok Tep Date: Tue, 21 Jun 2016 14:02:53 +0200 Subject: [PATCH 06/24] flush cache before setup --- arch/arm/src/stm32f7/stm32_sdmmc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm/src/stm32f7/stm32_sdmmc.c b/arch/arm/src/stm32f7/stm32_sdmmc.c index 4f2b7a8d01b..dbeb880f93a 100644 --- a/arch/arm/src/stm32f7/stm32_sdmmc.c +++ b/arch/arm/src/stm32f7/stm32_sdmmc.c @@ -2694,6 +2694,10 @@ static int stm32_dmasendsetup(FAR struct sdio_dev_s *dev, stm32_sampleinit(); stm32_sample(priv, SAMPLENDX_BEFORE_SETUP); + /* Flush cache to physical memory */ + + arch_flush_dcache((uintptr_t)buffer, (uintptr_t)buffer + buflen); + /* Save the source buffer information for use by the interrupt handler */ priv->buffer = (uint32_t *)buffer; @@ -2712,10 +2716,6 @@ static int stm32_dmasendsetup(FAR struct sdio_dev_s *dev, stm32_sample(priv, SAMPLENDX_BEFORE_ENABLE); - /* Flush cache to physical memory */ - - arch_flush_dcache((uintptr_t)buffer, (uintptr_t)buffer + buflen); - /* Start the DMA */ stm32_dmastart(priv->dma, stm32_dmacallback, priv, false); From 0369bbff508bf93fad3413e839f7c7e33ecb9a9d Mon Sep 17 00:00:00 2001 From: Lok Tep Date: Wed, 22 Jun 2016 10:54:06 +0200 Subject: [PATCH 07/24] styling --- arch/arm/src/stm32f7/chip/stm32_sdmmc.h | 4 ++-- arch/arm/src/stm32f7/chip/stm32f74xx75xx_sdmmc.h | 4 ++-- arch/arm/src/stm32f7/stm32_sdmmc.c | 2 +- arch/arm/src/stm32f7/stm32_sdmmc.h | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/arm/src/stm32f7/chip/stm32_sdmmc.h b/arch/arm/src/stm32f7/chip/stm32_sdmmc.h index e04bdddc1e6..10ed29f2384 100644 --- a/arch/arm/src/stm32f7/chip/stm32_sdmmc.h +++ b/arch/arm/src/stm32f7/chip/stm32_sdmmc.h @@ -1,7 +1,7 @@ /************************************************************************************ - * arch/arm/src/stm32f7/chip/stm32_i2c.h + * arch/arm/src/stm32f7/chip/stm32_sdmmc.h * - * Copyright (C) 2015 Gregory Nutt. All rights reserved. + * Copyright (C) 2016 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without diff --git a/arch/arm/src/stm32f7/chip/stm32f74xx75xx_sdmmc.h b/arch/arm/src/stm32f7/chip/stm32f74xx75xx_sdmmc.h index f710aed6d1b..117c4da2faf 100644 --- a/arch/arm/src/stm32f7/chip/stm32f74xx75xx_sdmmc.h +++ b/arch/arm/src/stm32f7/chip/stm32f74xx75xx_sdmmc.h @@ -1,7 +1,7 @@ /************************************************************************************ - * arch/arm/src/stm32f7/chip/stm32_sdio.h + * arch/arm/src/stm32f7/chip/stm32_sdmmc.h * - * Copyright (C) 2009, 2011-2013 Gregory Nutt. All rights reserved. + * Copyright (C) 2009, 2011-2016 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without diff --git a/arch/arm/src/stm32f7/stm32_sdmmc.c b/arch/arm/src/stm32f7/stm32_sdmmc.c index dbeb880f93a..16473094b5a 100644 --- a/arch/arm/src/stm32f7/stm32_sdmmc.c +++ b/arch/arm/src/stm32f7/stm32_sdmmc.c @@ -1,5 +1,5 @@ /**************************************************************************** - * arch/arm/src/stm32f7/stm32_sdio.c + * arch/arm/src/stm32f7/stm32_sdmmc.c * * Copyright (C) 2009, 2011-2016 Gregory Nutt. All rights reserved. * Author: Gregory Nutt diff --git a/arch/arm/src/stm32f7/stm32_sdmmc.h b/arch/arm/src/stm32f7/stm32_sdmmc.h index 18e6c446010..12b6348559f 100644 --- a/arch/arm/src/stm32f7/stm32_sdmmc.h +++ b/arch/arm/src/stm32f7/stm32_sdmmc.h @@ -1,7 +1,7 @@ /************************************************************************************ - * arch/arm/src/stm32f7/stm32_sdio.h + * arch/arm/src/stm32f7/stm32_sdmmc.h * - * Copyright (C) 2009, 2011, 2015 Gregory Nutt. All rights reserved. + * Copyright (C) 2009, 2011, 2016 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without From 47a5f81a63bb408f691550283c844bd6f86c0c83 Mon Sep 17 00:00:00 2001 From: Lok Tep Date: Sat, 25 Jun 2016 17:55:33 +0200 Subject: [PATCH 08/24] mmc1 --- configs/stm32f746-ws/src/stm32_appinitialize.c | 2 ++ configs/stm32f746-ws/src/stm32_sdmmc.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/configs/stm32f746-ws/src/stm32_appinitialize.c b/configs/stm32f746-ws/src/stm32_appinitialize.c index 689bea75fe8..0a58e61e36f 100644 --- a/configs/stm32f746-ws/src/stm32_appinitialize.c +++ b/configs/stm32f746-ws/src/stm32_appinitialize.c @@ -100,6 +100,7 @@ int board_app_initialize(void) } #endif +#ifdef CONFIG_STM32F7_SDMMC1 /* Initialize the SDIO block driver */ int ret = OK; @@ -110,6 +111,7 @@ int board_app_initialize(void) ferr("ERROR: Failed to initialize MMC/SD driver: %d\n", ret); return ret; } +#endif return OK; } diff --git a/configs/stm32f746-ws/src/stm32_sdmmc.c b/configs/stm32f746-ws/src/stm32_sdmmc.c index bdbe7a7f655..4169d511a20 100644 --- a/configs/stm32f746-ws/src/stm32_sdmmc.c +++ b/configs/stm32f746-ws/src/stm32_sdmmc.c @@ -1,5 +1,5 @@ /**************************************************************************** - * config/stm32f746-ws/src/stm32_sdio.c + * config/stm32f746-ws/src/stm32_sdmmc.c * * Copyright (C) 2014 Gregory Nutt. All rights reserved. * Author: Gregory Nutt From 2723de9a09a1a16d1135afef193308dd27a11cff Mon Sep 17 00:00:00 2001 From: Lok Tep Date: Sat, 25 Jun 2016 18:31:37 +0200 Subject: [PATCH 09/24] usb ep 0-8 --- arch/arm/src/stm32f7/chip/stm32_otg.h | 148 --------------------- arch/arm/src/stm32f7/stm32_otg.h | 6 +- arch/arm/src/stm32f7/stm32_otgdev.c | 182 +++++++++++++++++++------- configs/stm32f746-ws/nsh/defconfig | 56 ++++---- 4 files changed, 170 insertions(+), 222 deletions(-) diff --git a/arch/arm/src/stm32f7/chip/stm32_otg.h b/arch/arm/src/stm32f7/chip/stm32_otg.h index ee064971025..474d063026c 100644 --- a/arch/arm/src/stm32f7/chip/stm32_otg.h +++ b/arch/arm/src/stm32f7/chip/stm32_otg.h @@ -78,9 +78,6 @@ #define STM32_OTG_HPTXFSIZ_OFFSET 0x0100 /* Host periodic transmit FIFO size register */ #define STM32_OTG_DIEPTXF_OFFSET(n) (104+(((n)-1) << 2)) -#define STM32_OTG_DIEPTXF1_OFFSET 0x0104 /* Device IN endpoint transmit FIFO1 size register */ -#define STM32_OTG_DIEPTXF2_OFFSET 0x0108 /* Device IN endpoint transmit FIFO2 size register */ -#define STM32_OTG_DIEPTXF3_OFFSET 0x010c /* Device IN endpoint transmit FIFO3 size register */ /* Host-mode control and status registers */ @@ -99,44 +96,12 @@ #define STM32_OTG_HCTSIZ_CHOFFSET 0x0010 /* Host channel interrupt register */ #define STM32_OTG_HCCHAR_OFFSET(n) (0x500 + ((n) << 5)) -#define STM32_OTG_HCCHAR0_OFFSET 0x0500 /* Host channel-0 characteristics register */ -#define STM32_OTG_HCCHAR1_OFFSET 0x0520 /* Host channel-1 characteristics register */ -#define STM32_OTG_HCCHAR2_OFFSET 0x0540 /* Host channel-2 characteristics register */ -#define STM32_OTG_HCCHAR3_OFFSET 0x0560 /* Host channel-3 characteristics register */ -#define STM32_OTG_HCCHAR4_OFFSET 0x0580 /* Host channel-4 characteristics register */ -#define STM32_OTG_HCCHAR5_OFFSET 0x05a0 /* Host channel-5 characteristics register */ -#define STM32_OTG_HCCHAR6_OFFSET 0x05c0 /* Host channel-6 characteristics register */ -#define STM32_OTG_HCCHAR7_OFFSET 0x05e0 /* Host channel-7 characteristics register */ #define STM32_OTG_HCINT_OFFSET(n) (0x508 + ((n) << 5)) -#define STM32_OTG_HCINT0_OFFSET 0x0508 /* Host channel-0 interrupt register */ -#define STM32_OTG_HCINT1_OFFSET 0x0528 /* Host channel-1 interrupt register */ -#define STM32_OTG_HCINT2_OFFSET 0x0548 /* Host channel-2 interrupt register */ -#define STM32_OTG_HCINT3_OFFSET 0x0568 /* Host channel-3 interrupt register */ -#define STM32_OTG_HCINT4_OFFSET 0x0588 /* Host channel-4 interrupt register */ -#define STM32_OTG_HCINT5_OFFSET 0x05a8 /* Host channel-5 interrupt register */ -#define STM32_OTG_HCINT6_OFFSET 0x05c8 /* Host channel-6 interrupt register */ -#define STM32_OTG_HCINT7_OFFSET 0x05e8 /* Host channel-7 interrupt register */ #define STM32_OTG_HCINTMSK_OFFSET(n) (0x50c + ((n) << 5)) -#define STM32_OTG_HCINTMSK0_OFFSET 0x050c /* Host channel-0 interrupt mask register */ -#define STM32_OTG_HCINTMSK1_OFFSET 0x052c /* Host channel-1 interrupt mask register */ -#define STM32_OTG_HCINTMSK2_OFFSET 0x054c /* Host channel-2 interrupt mask register */ -#define STM32_OTG_HCINTMSK3_OFFSET 0x056c /* Host channel-3 interrupt mask register */ -#define STM32_OTG_HCINTMSK4_OFFSET 0x058c /* Host channel-4 interrupt mask register */ -#define STM32_OTG_HCINTMSK5_OFFSET 0x05ac /* Host channel-5 interrupt mask register */ -#define STM32_OTG_HCINTMSK6_OFFSET 0x05cc /* Host channel-6 interrupt mask register */ -#define STM32_OTG_HCINTMSK7_OFFSET 0x05ec /* Host channel-7 interrupt mask register */ #define STM32_OTG_HCTSIZ_OFFSET(n) (0x510 + ((n) << 5)) -#define STM32_OTG_HCTSIZ0_OFFSET 0x0510 /* Host channel-0 interrupt register */ -#define STM32_OTG_HCTSIZ1_OFFSET 0x0530 /* Host channel-1 interrupt register */ -#define STM32_OTG_HCTSIZ2_OFFSET 0x0550 /* Host channel-2 interrupt register */ -#define STM32_OTG_HCTSIZ3_OFFSET 0x0570 /* Host channel-3 interrupt register */ -#define STM32_OTG_HCTSIZ4_OFFSET 0x0590 /* Host channel-4 interrupt register */ -#define STM32_OTG_HCTSIZ5_OFFSET 0x05b0 /* Host channel-5 interrupt register */ -#define STM32_OTG_HCTSIZ6_OFFSET 0x05d0 /* Host channel-6 interrupt register */ -#define STM32_OTG_HCTSIZ7_OFFSET 0x05f0 /* Host channel-7 interrupt register */ /* Device-mode control and status registers */ @@ -158,50 +123,22 @@ #define STM32_OTG_DTXFSTS_EPOFFSET 0x0018 /* Device IN endpoint transmit FIFO status register */ #define STM32_OTG_DIEPCTL_OFFSET(n) (0x0900 + ((n) << 5)) -#define STM32_OTG_DIEPCTL0_OFFSET 0x0900 /* Device control IN endpoint 0 control register */ -#define STM32_OTG_DIEPCTL1_OFFSET 0x0920 /* Device control IN endpoint 2 control register */ -#define STM32_OTG_DIEPCTL2_OFFSET 0x0940 /* Device control IN endpoint 3 control register */ -#define STM32_OTG_DIEPCTL3_OFFSET 0x0960 /* Device control IN endpoint 4 control register */ #define STM32_OTG_DIEPINT_OFFSET(n) (0x0908 + ((n) << 5)) -#define STM32_OTG_DIEPINT0_OFFSET 0x0908 /* Device endpoint-0 interrupt register */ -#define STM32_OTG_DIEPINT1_OFFSET 0x0928 /* Device endpoint-1 interrupt register */ -#define STM32_OTG_DIEPINT2_OFFSET 0x0948 /* Device endpoint-2 interrupt register */ -#define STM32_OTG_DIEPINT3_OFFSET 0x0968 /* Device endpoint-3 interrupt register */ #define STM32_OTG_DIEPTSIZ_OFFSET(n) (0x910 + ((n) << 5)) -#define STM32_OTG_DIEPTSIZ0_OFFSET 0x0910 /* Device IN endpoint 0 transfer size register */ -#define STM32_OTG_DIEPTSIZ1_OFFSET 0x0930 /* Device IN endpoint 1 transfer size register */ -#define STM32_OTG_DIEPTSIZ2_OFFSET 0x0950 /* Device IN endpoint 2 transfer size register */ -#define STM32_OTG_DIEPTSIZ3_OFFSET 0x0970 /* Device IN endpoint 3 transfer size register */ #define STM32_OTG_DTXFSTS_OFFSET(n) (0x0918 + ((n) << 5)) -#define STM32_OTG_DTXFSTS0_OFFSET 0x0918 /* Device OUT endpoint-0 TxFIFO status register */ -#define STM32_OTG_DTXFSTS1_OFFSET 0x0938 /* Device OUT endpoint-1 TxFIFO status register */ -#define STM32_OTG_DTXFSTS2_OFFSET 0x0958 /* Device OUT endpoint-2 TxFIFO status register */ -#define STM32_OTG_DTXFSTS3_OFFSET 0x0978 /* Device OUT endpoint-3 TxFIFO status register */ #define STM32_OTG_DOEP_OFFSET(n) (0x0b00 + ((n) << 5)) #define STM32_OTG_DOEPCTL_EPOFFSET 0x0000 /* Device control OUT endpoint 0 control register */ #define STM32_OTG_DOEPINT_EPOFFSET 0x0008 /* Device endpoint-x interrupt register */ #define STM32_OTG_DOEPCTL_OFFSET(n) (0x0b00 + ((n) << 5)) -#define STM32_OTG_DOEPCTL0_OFFSET 0x00b00 /* Device OUT endpoint 0 control register */ -#define STM32_OTG_DOEPCTL1_OFFSET 0x00b20 /* Device OUT endpoint 1 control register */ -#define STM32_OTG_DOEPCTL2_OFFSET 0x00b40 /* Device OUT endpoint 2 control register */ -#define STM32_OTG_DOEPCTL3_OFFSET 0x00b60 /* Device OUT endpoint 3 control register */ #define STM32_OTG_DOEPINT_OFFSET(n) (0x0b08 + ((n) << 5)) -#define STM32_OTG_DOEPINT0_OFFSET 0x00b08 /* Device endpoint-0 interrupt register */ -#define STM32_OTG_DOEPINT1_OFFSET 0x00b28 /* Device endpoint-1 interrupt register */ -#define STM32_OTG_DOEPINT2_OFFSET 0x00b48 /* Device endpoint-2 interrupt register */ -#define STM32_OTG_DOEPINT3_OFFSET 0x00b68 /* Device endpoint-3 interrupt register */ #define STM32_OTG_DOEPTSIZ_OFFSET(n) (0x0b10 + ((n) << 5)) -#define STM32_OTG_DOEPTSIZ0_OFFSET 0x00b10 /* Device OUT endpoint-0 transfer size register */ -#define STM32_OTG_DOEPTSIZ1_OFFSET 0x00b30 /* Device OUT endpoint-1 transfer size register */ -#define STM32_OTG_DOEPTSIZ2_OFFSET 0x00b50 /* Device OUT endpoint-2 transfer size register */ -#define STM32_OTG_DOEPTSIZ3_OFFSET 0x00b70 /* Device OUT endpoint-3 transfer size register */ /* Power and clock gating registers */ @@ -212,17 +149,6 @@ #define STM32_OTG_DFIFO_DEP_OFFSET(n) (0x1000 + ((n) << 12)) #define STM32_OTG_DFIFO_HCH_OFFSET(n) (0x1000 + ((n) << 12)) -#define STM32_OTG_DFIFO_DEP0_OFFSET 0x1000 /* 0x1000-0x1ffc Device IN/OUT Endpoint 0 DFIFO Write/Read Access */ -#define STM32_OTG_DFIFO_HCH0_OFFSET 0x1000 /* 0x1000-0x1ffc Host OUT/IN Channel 0 DFIFO Read/Write Access */ - -#define STM32_OTG_DFIFO_DEP1_OFFSET 0x2000 /* 0x2000-0x2ffc Device IN/OUT Endpoint 1 DFIFO Write/Read Access */ -#define STM32_OTG_DFIFO_HCH1_OFFSET 0x2000 /* 0x2000-0x2ffc Host OUT/IN Channel 1 DFIFO Read/Write Access */ - -#define STM32_OTG_DFIFO_DEP2_OFFSET 0x3000 /* 0x3000-0x3ffc Device IN/OUT Endpoint 2 DFIFO Write/Read Access */ -#define STM32_OTG_DFIFO_HCH2_OFFSET 0x3000 /* 0x3000-0x3ffc Host OUT/IN Channel 2 DFIFO Read/Write Access */ - -#define STM32_OTG_DFIFO_DEP3_OFFSET 0x4000 /* 0x4000-0x4ffc Device IN/OUT Endpoint 3 DFIFO Write/Read Access */ -#define STM32_OTG_DFIFO_HCH3_OFFSET 0x4000 /* 0x4000-0x4ffc Host OUT/IN Channel 3 DFIFO Read/Write Access */ /* Register Addresses *******************************************************************************/ @@ -244,9 +170,6 @@ #define STM32_OTG_HPTXFSIZ (STM32_OTG_BASE+STM32_OTG_HPTXFSIZ_OFFSET) #define STM32_OTG_DIEPTXF(n) (STM32_OTG_BASE+STM32_OTG_DIEPTXF_OFFSET(n)) -#define STM32_OTG_DIEPTXF1 (STM32_OTG_BASE+STM32_OTG_DIEPTXF1_OFFSET) -#define STM32_OTG_DIEPTXF2 (STM32_OTG_BASE+STM32_OTG_DIEPTXF2_OFFSET) -#define STM32_OTG_DIEPTXF3 (STM32_OTG_BASE+STM32_OTG_DIEPTXF3_OFFSET) /* Host-mode control and status registers */ @@ -261,44 +184,12 @@ #define STM32_OTG_CHAN(n) (STM32_OTG_BASE+STM32_OTG_CHAN_OFFSET(n)) #define STM32_OTG_HCCHAR(n) (STM32_OTG_BASE+STM32_OTG_HCCHAR_OFFSET(n)) -#define STM32_OTG_HCCHAR0 (STM32_OTG_BASE+STM32_OTG_HCCHAR0_OFFSET) -#define STM32_OTG_HCCHAR1 (STM32_OTG_BASE+STM32_OTG_HCCHAR1_OFFSET) -#define STM32_OTG_HCCHAR2 (STM32_OTG_BASE+STM32_OTG_HCCHAR2_OFFSET) -#define STM32_OTG_HCCHAR3 (STM32_OTG_BASE+STM32_OTG_HCCHAR3_OFFSET) -#define STM32_OTG_HCCHAR4 (STM32_OTG_BASE+STM32_OTG_HCCHAR4_OFFSET) -#define STM32_OTG_HCCHAR5 (STM32_OTG_BASE+STM32_OTG_HCCHAR5_OFFSET) -#define STM32_OTG_HCCHAR6 (STM32_OTG_BASE+STM32_OTG_HCCHAR6_OFFSET) -#define STM32_OTG_HCCHAR7 (STM32_OTG_BASE+STM32_OTG_HCCHAR7_OFFSET) #define STM32_OTG_HCINT(n) (STM32_OTG_BASE+STM32_OTG_HCINT_OFFSET(n)) -#define STM32_OTG_HCINT0 (STM32_OTG_BASE+STM32_OTG_HCINT0_OFFSET) -#define STM32_OTG_HCINT1 (STM32_OTG_BASE+STM32_OTG_HCINT1_OFFSET) -#define STM32_OTG_HCINT2 (STM32_OTG_BASE+STM32_OTG_HCINT2_OFFSET) -#define STM32_OTG_HCINT3 (STM32_OTG_BASE+STM32_OTG_HCINT3_OFFSET) -#define STM32_OTG_HCINT4 (STM32_OTG_BASE+STM32_OTG_HCINT4_OFFSET) -#define STM32_OTG_HCINT5 (STM32_OTG_BASE+STM32_OTG_HCINT5_OFFSET) -#define STM32_OTG_HCINT6 (STM32_OTG_BASE+STM32_OTG_HCINT6_OFFSET) -#define STM32_OTG_HCINT7 (STM32_OTG_BASE+STM32_OTG_HCINT7_OFFSET) #define STM32_OTG_HCINTMSK(n) (STM32_OTG_BASE+STM32_OTG_HCINTMSK_OFFSET(n)) -#define STM32_OTG_HCINTMSK0 (STM32_OTG_BASE+STM32_OTG_HCINTMSK0_OFFSET) -#define STM32_OTG_HCINTMSK1 (STM32_OTG_BASE+STM32_OTG_HCINTMSK1_OFFSET) -#define STM32_OTG_HCINTMSK2 (STM32_OTG_BASE+STM32_OTG_HCINTMSK2_OFFSET) -#define STM32_OTG_HCINTMSK3 (STM32_OTG_BASE+STM32_OTG_HCINTMSK3_OFFSET) -#define STM32_OTG_HCINTMSK4 (STM32_OTG_BASE+STM32_OTG_HCINTMSK4_OFFSET) -#define STM32_OTG_HCINTMSK5 (STM32_OTG_BASE+STM32_OTG_HCINTMSK5_OFFSET) -#define STM32_OTG_HCINTMSK6 (STM32_OTG_BASE+STM32_OTG_HCINTMSK6_OFFSET) -#define STM32_OTG_HCINTMSK7 (STM32_OTG_BASE+STM32_OTG_HCINTMSK7_OFFSET)_ #define STM32_OTG_HCTSIZ(n) (STM32_OTG_BASE+STM32_OTG_HCTSIZ_OFFSET(n)) -#define STM32_OTG_HCTSIZ0 (STM32_OTG_BASE+STM32_OTG_HCTSIZ0_OFFSET) -#define STM32_OTG_HCTSIZ1 (STM32_OTG_BASE+STM32_OTG_HCTSIZ1_OFFSET) -#define STM32_OTG_HCTSIZ2 (STM32_OTG_BASE+STM32_OTG_HCTSIZ2_OFFSET) -#define STM32_OTG_HCTSIZ3 (STM32_OTG_BASE+STM32_OTG_HCTSIZ3_OFFSET) -#define STM32_OTG_HCTSIZ4 (STM32_OTG_BASE+STM32_OTG_HCTSIZ4_OFFSET) -#define STM32_OTG_HCTSIZ5 (STM32_OTG_BASE+STM32_OTG_HCTSIZ5_OFFSET) -#define STM32_OTG_HCTSIZ6 (STM32_OTG_BASE+STM32_OTG_HCTSIZ6_OFFSET) -#define STM32_OTG_HCTSIZ7 (STM32_OTG_BASE+STM32_OTG_HCTSIZ7_OFFSET) /* Device-mode control and status registers */ @@ -316,48 +207,20 @@ #define STM32_OTG_DIEP(n) (STM32_OTG_BASE+STM32_OTG_DIEP_OFFSET(n)) #define STM32_OTG_DIEPCTL(n) (STM32_OTG_BASE+STM32_OTG_DIEPCTL_OFFSET(n)) -#define STM32_OTG_DIEPCTL0 (STM32_OTG_BASE+STM32_OTG_DIEPCTL0_OFFSET) -#define STM32_OTG_DIEPCTL1 (STM32_OTG_BASE+STM32_OTG_DIEPCTL1_OFFSET) -#define STM32_OTG_DIEPCTL2 (STM32_OTG_BASE+STM32_OTG_DIEPCTL2_OFFSET) -#define STM32_OTG_DIEPCTL3 (STM32_OTG_BASE+STM32_OTG_DIEPCTL3_OFFSET) #define STM32_OTG_DIEPINT(n) (STM32_OTG_BASE+STM32_OTG_DIEPINT_OFFSET(n)) -#define STM32_OTG_DIEPINT0 (STM32_OTG_BASE+STM32_OTG_DIEPINT0_OFFSET) -#define STM32_OTG_DIEPINT1 (STM32_OTG_BASE+STM32_OTG_DIEPINT1_OFFSET) -#define STM32_OTG_DIEPINT2 (STM32_OTG_BASE+STM32_OTG_DIEPINT2_OFFSET) -#define STM32_OTG_DIEPINT3 (STM32_OTG_BASE+STM32_OTG_DIEPINT3_OFFSET) #define STM32_OTG_DIEPTSIZ(n) (STM32_OTG_BASE+STM32_OTG_DIEPTSIZ_OFFSET(n)) -#define STM32_OTG_DIEPTSIZ0 (STM32_OTG_BASE+STM32_OTG_DIEPTSIZ0_OFFSET) -#define STM32_OTG_DIEPTSIZ1 (STM32_OTG_BASE+STM32_OTG_DIEPTSIZ1_OFFSET) -#define STM32_OTG_DIEPTSIZ2 (STM32_OTG_BASE+STM32_OTG_DIEPTSIZ2_OFFSET) -#define STM32_OTG_DIEPTSIZ3 (STM32_OTG_BASE+STM32_OTG_DIEPTSIZ3_OFFSET) #define STM32_OTG_DTXFSTS(n) (STM32_OTG_BASE+STM32_OTG_DTXFSTS_OFFSET(n)) -#define STM32_OTG_DTXFSTS0 (STM32_OTG_BASE+STM32_OTG_DTXFSTS0_OFFSET) -#define STM32_OTG_DTXFSTS1 (STM32_OTG_BASE+STM32_OTG_DTXFSTS1_OFFSET) -#define STM32_OTG_DTXFSTS2 (STM32_OTG_BASE+STM32_OTG_DTXFSTS2_OFFSET) -#define STM32_OTG_DTXFSTS3 (STM32_OTG_BASE+STM32_OTG_DTXFSTS3_OFFSET) #define STM32_OTG_DOEP(n) (STM32_OTG_BASE+STM32_OTG_DOEP_OFFSET(n)) #define STM32_OTG_DOEPCTL(n) (STM32_OTG_BASE+STM32_OTG_DOEPCTL_OFFSET(n)) -#define STM32_OTG_DOEPCTL0 (STM32_OTG_BASE+STM32_OTG_DOEPCTL0_OFFSET) -#define STM32_OTG_DOEPCTL1 (STM32_OTG_BASE+STM32_OTG_DOEPCTL1_OFFSET) -#define STM32_OTG_DOEPCTL2 (STM32_OTG_BASE+STM32_OTG_DOEPCTL2_OFFSET) -#define STM32_OTG_DOEPCTL3 (STM32_OTG_BASE+STM32_OTG_DOEPCTL3_OFFSET) #define STM32_OTG_DOEPINT(n) (STM32_OTG_BASE+STM32_OTG_DOEPINT_OFFSET(n)) -#define STM32_OTG_DOEPINT0 (STM32_OTG_BASE+STM32_OTG_DOEPINT0_OFFSET) -#define STM32_OTG_DOEPINT1 (STM32_OTG_BASE+STM32_OTG_DOEPINT1_OFFSET) -#define STM32_OTG_DOEPINT2 (STM32_OTG_BASE+STM32_OTG_DOEPINT2_OFFSET) -#define STM32_OTG_DOEPINT3 (STM32_OTG_BASE+STM32_OTG_DOEPINT3_OFFSET) #define STM32_OTG_DOEPTSIZ(n) (STM32_OTG_BASE+STM32_OTG_DOEPTSIZ_OFFSET(n)) -#define STM32_OTG_DOEPTSIZ0 (STM32_OTG_BASE+STM32_OTG_DOEPTSIZ0_OFFSET) -#define STM32_OTG_DOEPTSIZ1 (STM32_OTG_BASE+STM32_OTG_DOEPTSIZ1_OFFSET) -#define STM32_OTG_DOEPTSIZ2 (STM32_OTG_BASE+STM32_OTG_DOEPTSIZ2_OFFSET) -#define STM32_OTG_DOEPTSIZ3 (STM32_OTG_BASE+STM32_OTG_DOEPTSIZ3_OFFSET) /* Power and clock gating registers */ @@ -368,17 +231,6 @@ #define STM32_OTG_DFIFO_DEP(n) (STM32_OTG_BASE+STM32_OTG_DFIFO_DEP_OFFSET(n)) #define STM32_OTG_DFIFO_HCH(n) (STM32_OTG_BASE+STM32_OTG_DFIFO_HCH_OFFSET(n)) -#define STM32_OTG_DFIFO_DEP0 (STM32_OTG_BASE+STM32_OTG_DFIFO_DEP0_OFFSET) -#define STM32_OTG_DFIFO_HCH0 (STM32_OTG_BASE+STM32_OTG_DFIFO_HCH0_OFFSET) - -#define STM32_OTG_DFIFO_DEP1 (STM32_OTG_BASE+STM32_OTG_DFIFO_DEP1_OFFSET) -#define STM32_OTG_DFIFO_HCH1 (STM32_OTG_BASE+STM32_OTG_DFIFO_HCH1_OFFSET) - -#define STM32_OTG_DFIFO_DEP2 (STM32_OTG_BASE+STM32_OTG_DFIFO_DEP2_OFFSET) -#define STM32_OTG_DFIFO_HCH2 (STM32_OTG_BASE+STM32_OTG_DFIFO_HCH2_OFFSET) - -#define STM32_OTG_DFIFO_DEP3 (STM32_OTG_BASE+STM32_OTG_DFIFO_DEP3_OFFSET) -#define STM32_OTG_DFIFO_HCH3 (STM32_OTG_BASE+STM32_OTG_DFIFO_HCH3_OFFSET) /* Register Bitfield Definitions ********************************************************************/ /* Core global control and status registers */ diff --git a/arch/arm/src/stm32f7/stm32_otg.h b/arch/arm/src/stm32f7/stm32_otg.h index 0bb6b9b3938..3c60683ea34 100644 --- a/arch/arm/src/stm32f7/stm32_otg.h +++ b/arch/arm/src/stm32f7/stm32_otg.h @@ -66,18 +66,18 @@ # define GPIO_OTG_DP GPIO_OTGFS_DP # define GPIO_OTG_ID GPIO_OTGFS_ID # define GPIO_OTG_SOF GPIO_OTGFS_SOF - +# define STM32_OTG_FIFO_SIZE 1280 #endif #if defined(CONFIG_STM32F7_OTGHS) # define STM32_IRQ_OTG STM32_IRQ_OTGHS # define STM32_OTG_BASE STM32_USBOTGHS_BASE -# define STM32_NENDPOINTS (8) /* ep0-7 x 2 for IN and OUT */ +# define STM32_NENDPOINTS (7) /* ep0-8 x 2 for IN and OUT but driver internals use byte to map + one bit for direction */ # define GPIO_OTG_DM GPIO_OTGHS_DM # define GPIO_OTG_DP GPIO_OTGHS_DP # define GPIO_OTG_ID GPIO_OTGHS_ID # define GPIO_OTG_SOF GPIO_OTGHS_SOF - +# define STM32_OTG_FIFO_SIZE 4096 #endif /************************************************************************************ diff --git a/arch/arm/src/stm32f7/stm32_otgdev.c b/arch/arm/src/stm32f7/stm32_otgdev.c index d4d46c4efb1..7b9db010ada 100644 --- a/arch/arm/src/stm32f7/stm32_otgdev.c +++ b/arch/arm/src/stm32f7/stm32_otgdev.c @@ -89,30 +89,82 @@ */ #ifndef CONFIG_USBDEV_RXFIFO_SIZE -# define CONFIG_USBDEV_RXFIFO_SIZE 512 +# define CONFIG_USBDEV_RXFIFO_SIZE (STM32_OTG_FIFO_SIZE - STM32_OTG_FIFO_SIZE/4/2/STM32_NENDPOINTS*4*STM32_NENDPOINTS) #endif -#ifndef CONFIG_USBDEV_EP0_TXFIFO_SIZE -# define CONFIG_USBDEV_EP0_TXFIFO_SIZE 192 +#if STM32_NENDPOINTS > 0 +# ifndef CONFIG_USBDEV_EP0_TXFIFO_SIZE +# define CONFIG_USBDEV_EP0_TXFIFO_SIZE ((STM32_OTG_FIFO_SIZE - CONFIG_USBDEV_RXFIFO_SIZE)/STM32_NENDPOINTS) +# endif +#else +# define CONFIG_USBDEV_EP0_TXFIFO_SIZE 0 #endif -#ifndef CONFIG_USBDEV_EP1_TXFIFO_SIZE -# define CONFIG_USBDEV_EP1_TXFIFO_SIZE 192 +#if STM32_NENDPOINTS > 1 +# ifndef CONFIG_USBDEV_EP1_TXFIFO_SIZE +# define CONFIG_USBDEV_EP1_TXFIFO_SIZE ((STM32_OTG_FIFO_SIZE - CONFIG_USBDEV_RXFIFO_SIZE)/STM32_NENDPOINTS) +# endif +#else +# define CONFIG_USBDEV_EP1_TXFIFO_SIZE 0 #endif -#ifndef CONFIG_USBDEV_EP2_TXFIFO_SIZE -# define CONFIG_USBDEV_EP2_TXFIFO_SIZE 192 +#if STM32_NENDPOINTS > 2 +# ifndef CONFIG_USBDEV_EP2_TXFIFO_SIZE +# define CONFIG_USBDEV_EP2_TXFIFO_SIZE ((STM32_OTG_FIFO_SIZE - CONFIG_USBDEV_RXFIFO_SIZE)/STM32_NENDPOINTS) +# endif +#else +# define CONFIG_USBDEV_EP2_TXFIFO_SIZE 0 #endif -#ifndef CONFIG_USBDEV_EP3_TXFIFO_SIZE -# define CONFIG_USBDEV_EP3_TXFIFO_SIZE 192 +#if STM32_NENDPOINTS > 3 +# ifndef CONFIG_USBDEV_EP3_TXFIFO_SIZE +# define CONFIG_USBDEV_EP3_TXFIFO_SIZE ((STM32_OTG_FIFO_SIZE - CONFIG_USBDEV_RXFIFO_SIZE)/STM32_NENDPOINTS) +# endif +#else +# define CONFIG_USBDEV_EP3_TXFIFO_SIZE 0 #endif -#if (CONFIG_USBDEV_RXFIFO_SIZE + CONFIG_USBDEV_EP0_TXFIFO_SIZE + \ - CONFIG_USBDEV_EP2_TXFIFO_SIZE + CONFIG_USBDEV_EP3_TXFIFO_SIZE) > 1280 -# error "FIFO allocations exceed FIFO memory size" +#if STM32_NENDPOINTS > 4 +# ifndef CONFIG_USBDEV_EP4_TXFIFO_SIZE +# define CONFIG_USBDEV_EP4_TXFIFO_SIZE ((STM32_OTG_FIFO_SIZE - CONFIG_USBDEV_RXFIFO_SIZE)/STM32_NENDPOINTS) +# endif +#else +# define CONFIG_USBDEV_EP4_TXFIFO_SIZE 0 #endif +#if STM32_NENDPOINTS > 5 +# ifndef CONFIG_USBDEV_EP5_TXFIFO_SIZE +# define CONFIG_USBDEV_EP5_TXFIFO_SIZE ((STM32_OTG_FIFO_SIZE - CONFIG_USBDEV_RXFIFO_SIZE)/STM32_NENDPOINTS) +# endif +#else +# define CONFIG_USBDEV_EP5_TXFIFO_SIZE 0 +#endif + +#if STM32_NENDPOINTS > 6 +# ifndef CONFIG_USBDEV_EP6_TXFIFO_SIZE +# define CONFIG_USBDEV_EP6_TXFIFO_SIZE ((STM32_OTG_FIFO_SIZE - CONFIG_USBDEV_RXFIFO_SIZE)/STM32_NENDPOINTS) +# endif +#else +# define CONFIG_USBDEV_EP6_TXFIFO_SIZE 0 +#endif + +#if STM32_NENDPOINTS > 7 +# ifndef CONFIG_USBDEV_EP7_TXFIFO_SIZE +# define CONFIG_USBDEV_EP7_TXFIFO_SIZE ((STM32_OTG_FIFO_SIZE - CONFIG_USBDEV_RXFIFO_SIZE)/STM32_NENDPOINTS) +# endif +#else +# define CONFIG_USBDEV_EP7_TXFIFO_SIZE 0 +#endif + +#if STM32_NENDPOINTS > 8 +# ifndef CONFIG_USBDEV_EP8_TXFIFO_SIZE +# define CONFIG_USBDEV_EP8_TXFIFO_SIZE ((STM32_OTG_FIFO_SIZE - CONFIG_USBDEV_RXFIFO_SIZE)/STM32_NENDPOINTS) +# endif +#else +# define CONFIG_USBDEV_EP8_TXFIFO_SIZE 0 +#endif + + /* The actual FIFO addresses that we use must be aligned to 4-byte boundaries; * FIFO sizes must be provided in units of 32-bit words. */ @@ -123,29 +175,36 @@ #define STM32_EP0_TXFIFO_BYTES ((CONFIG_USBDEV_EP0_TXFIFO_SIZE + 3) & ~3) #define STM32_EP0_TXFIFO_WORDS ((CONFIG_USBDEV_EP0_TXFIFO_SIZE + 3) >> 2) -#if STM32_EP0_TXFIFO_WORDS < 16 || STM32_EP0_TXFIFO_WORDS > 256 -# error "CONFIG_USBDEV_EP0_TXFIFO_SIZE is out of range" -#endif - #define STM32_EP1_TXFIFO_BYTES ((CONFIG_USBDEV_EP1_TXFIFO_SIZE + 3) & ~3) #define STM32_EP1_TXFIFO_WORDS ((CONFIG_USBDEV_EP1_TXFIFO_SIZE + 3) >> 2) -#if STM32_EP1_TXFIFO_WORDS < 16 -# error "CONFIG_USBDEV_EP1_TXFIFO_SIZE is out of range" -#endif - #define STM32_EP2_TXFIFO_BYTES ((CONFIG_USBDEV_EP2_TXFIFO_SIZE + 3) & ~3) #define STM32_EP2_TXFIFO_WORDS ((CONFIG_USBDEV_EP2_TXFIFO_SIZE + 3) >> 2) -#if STM32_EP2_TXFIFO_WORDS < 16 -# error "CONFIG_USBDEV_EP2_TXFIFO_SIZE is out of range" -#endif - #define STM32_EP3_TXFIFO_BYTES ((CONFIG_USBDEV_EP3_TXFIFO_SIZE + 3) & ~3) #define STM32_EP3_TXFIFO_WORDS ((CONFIG_USBDEV_EP3_TXFIFO_SIZE + 3) >> 2) -#if STM32_EP3_TXFIFO_WORDS < 16 -# error "CONFIG_USBDEV_EP3_TXFIFO_SIZE is out of range" +#define STM32_EP4_TXFIFO_BYTES ((CONFIG_USBDEV_EP4_TXFIFO_SIZE + 3) & ~3) +#define STM32_EP4_TXFIFO_WORDS ((CONFIG_USBDEV_EP4_TXFIFO_SIZE + 3) >> 2) + +#define STM32_EP5_TXFIFO_BYTES ((CONFIG_USBDEV_EP5_TXFIFO_SIZE + 3) & ~3) +#define STM32_EP5_TXFIFO_WORDS ((CONFIG_USBDEV_EP5_TXFIFO_SIZE + 3) >> 2) + +#define STM32_EP6_TXFIFO_BYTES ((CONFIG_USBDEV_EP6_TXFIFO_SIZE + 3) & ~3) +#define STM32_EP6_TXFIFO_WORDS ((CONFIG_USBDEV_EP6_TXFIFO_SIZE + 3) >> 2) + +#define STM32_EP7_TXFIFO_BYTES ((CONFIG_USBDEV_EP7_TXFIFO_SIZE + 3) & ~3) +#define STM32_EP7_TXFIFO_WORDS ((CONFIG_USBDEV_EP7_TXFIFO_SIZE + 3) >> 2) + +#define STM32_EP8_TXFIFO_BYTES ((CONFIG_USBDEV_EP8_TXFIFO_SIZE + 3) & ~3) +#define STM32_EP8_TXFIFO_WORDS ((CONFIG_USBDEV_EP8_TXFIFO_SIZE + 3) >> 2) + + +#if (STM32_RXFIFO_BYTES + \ + STM32_EP0_TXFIFO_BYTES + STM32_EP1_TXFIFO_BYTES + STM32_EP2_TXFIFO_BYTES + STM32_EP3_TXFIFO_BYTES + \ + STM32_EP4_TXFIFO_BYTES + STM32_EP5_TXFIFO_BYTES + STM32_EP6_TXFIFO_BYTES + STM32_EP7_TXFIFO_BYTES + CONFIG_USBDEV_EP8_TXFIFO_SIZE \ + ) > STM32_OTG_FIFO_SIZE +# error "FIFO allocations exceed FIFO memory size" #endif /* Debug ***********************************************************************/ @@ -962,7 +1021,7 @@ static void stm32_ep0in_activate(void) /* Set the max packet size of the IN EP. */ - regval = stm32_getreg(STM32_OTG_DIEPCTL0); + regval = stm32_getreg(STM32_OTG_DIEPCTL(0)); regval &= ~OTG_DIEPCTL0_MPSIZ_MASK; #if CONFIG_USBDEV_EP0_MAXSIZE == 8 @@ -977,7 +1036,7 @@ static void stm32_ep0in_activate(void) # error "Unsupported value of CONFIG_USBDEV_EP0_MAXSIZE" #endif - stm32_putreg(regval, STM32_OTG_DIEPCTL0); + stm32_putreg(regval, STM32_OTG_DIEPCTL(0)); /* Clear global IN NAK */ @@ -1003,13 +1062,13 @@ static void stm32_ep0out_ctrlsetup(FAR struct stm32_usbdev_s *priv) regval = (USB_SIZEOF_CTRLREQ * 3 << OTG_DOEPTSIZ0_XFRSIZ_SHIFT) | (OTG_DOEPTSIZ0_PKTCNT) | (3 << OTG_DOEPTSIZ0_STUPCNT_SHIFT); - stm32_putreg(regval, STM32_OTG_DOEPTSIZ0); + stm32_putreg(regval, STM32_OTG_DOEPTSIZ(0)); /* Then clear NAKing and enable the transfer */ - regval = stm32_getreg(STM32_OTG_DOEPCTL0); + regval = stm32_getreg(STM32_OTG_DOEPCTL(0)); regval |= (OTG_DOEPCTL0_CNAK | OTG_DOEPCTL0_EPENA); - stm32_putreg(regval, STM32_OTG_DOEPCTL0); + stm32_putreg(regval, STM32_OTG_DOEPCTL(0)); } /**************************************************************************** @@ -3227,9 +3286,9 @@ static inline void stm32_rxinterrupt(FAR struct stm32_usbdev_s *priv) { /* Clear NAKSTS so that we can receive the data */ - regval = stm32_getreg(STM32_OTG_DOEPCTL0); + regval = stm32_getreg(STM32_OTG_DOEPCTL(0)); regval |= OTG_DOEPCTL0_CNAK; - stm32_putreg(regval, STM32_OTG_DOEPCTL0); + stm32_putreg(regval, STM32_OTG_DOEPCTL(0)); /* Wait for the data phase. */ @@ -5235,33 +5294,68 @@ static void stm32_hwinitialize(FAR struct stm32_usbdev_s *priv) stm32_putreg(STM32_RXFIFO_WORDS, STM32_OTG_GRXFSIZ); - /* EP0 TX */ - +#if STM32_NENDPOINTS > 0 address = STM32_RXFIFO_WORDS; regval = (address << OTG_DIEPTXF0_TX0FD_SHIFT) | (STM32_EP0_TXFIFO_WORDS << OTG_DIEPTXF0_TX0FSA_SHIFT); stm32_putreg(regval, STM32_OTG_DIEPTXF0); +#endif - /* EP1 TX */ - +#if STM32_NENDPOINTS > 1 address += STM32_EP0_TXFIFO_WORDS; regval = (address << OTG_DIEPTXF_INEPTXSA_SHIFT) | (STM32_EP1_TXFIFO_WORDS << OTG_DIEPTXF_INEPTXFD_SHIFT); - stm32_putreg(regval, STM32_OTG_DIEPTXF1); - - /* EP2 TX */ + stm32_putreg(regval, STM32_OTG_DIEPTXF(1)); +#endif +#if STM32_NENDPOINTS > 2 address += STM32_EP1_TXFIFO_WORDS; regval = (address << OTG_DIEPTXF_INEPTXSA_SHIFT) | (STM32_EP2_TXFIFO_WORDS << OTG_DIEPTXF_INEPTXFD_SHIFT); - stm32_putreg(regval, STM32_OTG_DIEPTXF2); - - /* EP3 TX */ + stm32_putreg(regval, STM32_OTG_DIEPTXF(2)); +#endif +#if STM32_NENDPOINTS > 3 address += STM32_EP2_TXFIFO_WORDS; regval = (address << OTG_DIEPTXF_INEPTXSA_SHIFT) | (STM32_EP3_TXFIFO_WORDS << OTG_DIEPTXF_INEPTXFD_SHIFT); - stm32_putreg(regval, STM32_OTG_DIEPTXF3); + stm32_putreg(regval, STM32_OTG_DIEPTXF(3)); +#endif + +#if STM32_NENDPOINTS > 4 + address += STM32_EP3_TXFIFO_WORDS; + regval = (address << OTG_DIEPTXF_INEPTXSA_SHIFT) | + (STM32_EP4_TXFIFO_WORDS << OTG_DIEPTXF_INEPTXFD_SHIFT); + stm32_putreg(regval, STM32_OTG_DIEPTXF(4)); +#endif + +#if STM32_NENDPOINTS > 5 + address += STM32_EP4_TXFIFO_WORDS; + regval = (address << OTG_DIEPTXF_INEPTXSA_SHIFT) | + (STM32_EP5_TXFIFO_WORDS << OTG_DIEPTXF_INEPTXFD_SHIFT); + stm32_putreg(regval, STM32_OTG_DIEPTXF(5)); +#endif + +#if STM32_NENDPOINTS > 6 + address += STM32_EP5_TXFIFO_WORDS; + regval = (address << OTG_DIEPTXF_INEPTXSA_SHIFT) | + (STM32_EP6_TXFIFO_WORDS << OTG_DIEPTXF_INEPTXFD_SHIFT); + stm32_putreg(regval, STM32_OTG_DIEPTXF(6)); +#endif + +#if STM32_NENDPOINTS > 7 + address += STM32_EP6_TXFIFO_WORDS; + regval = (address << OTG_DIEPTXF_INEPTXSA_SHIFT) | + (STM32_EP7_TXFIFO_WORDS << OTG_DIEPTXF_INEPTXFD_SHIFT); + stm32_putreg(regval, STM32_OTG_DIEPTXF(7)); +#endif + +#if STM32_NENDPOINTS > 8 + address += STM32_EP7_TXFIFO_WORDS; + regval = (address << OTG_DIEPTXF_INEPTXSA_SHIFT) | + (STM32_EP8_TXFIFO_WORDS << OTG_DIEPTXF_INEPTXFD_SHIFT); + stm32_putreg(regval, STM32_OTG_DIEPTXF(8)); +#endif /* Flush the FIFOs */ diff --git a/configs/stm32f746-ws/nsh/defconfig b/configs/stm32f746-ws/nsh/defconfig index 44a25aec95a..653cccf1e63 100644 --- a/configs/stm32f746-ws/nsh/defconfig +++ b/configs/stm32f746-ws/nsh/defconfig @@ -300,13 +300,13 @@ CONFIG_STM32F7_I2C1=y # CONFIG_STM32F7_I2C3 is not set # CONFIG_STM32F7_LPTIM1 is not set # CONFIG_STM32F7_LTDC is not set -##CONFIG_STM32F7_OTGFS=y +CONFIG_STM32F7_OTGFS=y # CONFIG_STM32F7_OTGHS is not set # CONFIG_STM32F7_QUADSPI is not set # CONFIG_STM32F7_RNG is not set # CONFIG_STM32F7_SAI1 is not set # CONFIG_STM32F7_SAI2 is not set -CONFIG_STM32F7_SDMMC1=y +##CONFIG_STM32F7_SDMMC1=y # CONFIG_STM32F7_SPDIFRX is not set CONFIG_STM32F7_SPI1=y # CONFIG_STM32F7_SPI2 is not set @@ -434,7 +434,7 @@ CONFIG_LIB_BOARDCTL=y # CONFIG_BOARDCTL_PWMTEST is not set # CONFIG_BOARDCTL_GRAPHICS is not set # CONFIG_BOARDCTL_IOCTL is not set -##CONFIG_BOARDCTL_USBDEVCTRL=y +CONFIG_BOARDCTL_USBDEVCTRL=y # # RTOS Features @@ -690,7 +690,7 @@ CONFIG_USART6_2STOP=0 # CONFIG_USART6_IFLOWCONTROL is not set # CONFIG_USART6_OFLOWCONTROL is not set # CONFIG_USART6_DMA is not set -##CONFIG_USBDEV=y +CONFIG_USBDEV=y # CONFIG_USBHOST is not set # CONFIG_DRIVERS_WIRELESS is not set @@ -700,9 +700,9 @@ CONFIG_USART6_2STOP=0 # # CONFIG_USBDEV_ISOCHRONOUS is not set # CONFIG_USBDEV_DUALSPEED is not set -##CONFIG_USBDEV_SELFPOWERED=y +CONFIG_USBDEV_SELFPOWERED=y # CONFIG_USBDEV_BUSPOWERED is not set -##CONFIG_USBDEV_MAXPOWER=100 +CONFIG_USBDEV_MAXPOWER=100 # CONFIG_USBDEV_DMA is not set # CONFIG_ARCH_USBDEV_STALLQUEUE is not set # CONFIG_USBDEV_TRACE is not set @@ -712,27 +712,26 @@ CONFIG_USART6_2STOP=0 # # CONFIG_USBDEV_COMPOSITE is not set # CONFIG_PL2303 is not set -##CONFIG_CDCACM=y -##CONFIG_CDCACM_CONSOLE=y -##CONFIG_CDCACM_EP0MAXPACKET=64 -##CONFIG_CDCACM_EPINTIN=1 -##CONFIG_CDCACM_EPINTIN_FSSIZE=64 -##CONFIG_CDCACM_EPINTIN_HSSIZE=64 -##CONFIG_CDCACM_EPBULKOUT=3 -##CONFIG_CDCACM_EPBULKOUT_FSSIZE=64 -##CONFIG_CDCACM_EPBULKOUT_HSSIZE=512 -##CONFIG_CDCACM_EPBULKIN=2 -##CONFIG_CDCACM_EPBULKIN_FSSIZE=64 -##CONFIG_CDCACM_EPBULKIN_HSSIZE=512 -##CONFIG_CDCACM_NRDREQS=4 -##CONFIG_CDCACM_NWRREQS=4 -##CONFIG_CDCACM_BULKIN_REQLEN=96 -##CONFIG_CDCACM_RXBUFSIZE=256 -##CONFIG_CDCACM_TXBUFSIZE=256 -##CONFIG_CDCACM_VENDORID=0x0525 -##CONFIG_CDCACM_PRODUCTID=0xa4a7 -##CONFIG_CDCACM_VENDORSTR="NuttX" -##CONFIG_CDCACM_PRODUCTSTR="CDC/ACM Serial" +CONFIG_CDCACM=y +CONFIG_CDCACM_EP0MAXPACKET=64 +CONFIG_CDCACM_EPINTIN=1 +CONFIG_CDCACM_EPINTIN_FSSIZE=64 +CONFIG_CDCACM_EPINTIN_HSSIZE=64 +CONFIG_CDCACM_EPBULKOUT=2 +CONFIG_CDCACM_EPBULKOUT_FSSIZE=64 +CONFIG_CDCACM_EPBULKOUT_HSSIZE=512 +CONFIG_CDCACM_EPBULKIN=2 +CONFIG_CDCACM_EPBULKIN_FSSIZE=64 +CONFIG_CDCACM_EPBULKIN_HSSIZE=512 +CONFIG_CDCACM_NRDREQS=4 +CONFIG_CDCACM_NWRREQS=4 +CONFIG_CDCACM_BULKIN_REQLEN=96 +CONFIG_CDCACM_RXBUFSIZE=256 +CONFIG_CDCACM_TXBUFSIZE=256 +CONFIG_CDCACM_VENDORID=0x03EB +CONFIG_CDCACM_PRODUCTID=0x2044 +CONFIG_CDCACM_VENDORSTR="NuttX" +CONFIG_CDCACM_PRODUCTSTR="CDC/ACM Serial" # CONFIG_USBMSC is not set # CONFIG_USBHOST is not set # CONFIG_DRIVERS_WIRELESS is not set @@ -1128,3 +1127,6 @@ CONFIG_READLINE_ECHO=y # CONFIG_SYSTEM_UBLOXMODEM is not set # CONFIG_SYSTEM_VI is not set # CONFIG_SYSTEM_ZMODEM is not set + +CONFIG_SYSTEM_CDCACM=y +CONFIG_SYSTEM_CDCACM_DEVMINOR=0 From 1905cb94a09d9f1d7cb8e247691382f817277c00 Mon Sep 17 00:00:00 2001 From: Lok Tep Date: Tue, 27 Sep 2016 16:43:41 +0200 Subject: [PATCH 10/24] to uinfo --- configs/stm32f746-ws/src/stm32_usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/stm32f746-ws/src/stm32_usb.c b/configs/stm32f746-ws/src/stm32_usb.c index b81aacd6caa..d0c118af896 100644 --- a/configs/stm32f746-ws/src/stm32_usb.c +++ b/configs/stm32f746-ws/src/stm32_usb.c @@ -333,7 +333,7 @@ xcpt_t stm32_setup_overcurrent(xcpt_t handler) #ifdef CONFIG_USBDEV void stm32_usbsuspend(FAR struct usbdev_s *dev, bool resume) { - ullinfo("resume: %d\n", resume); + uinfo("resume: %d\n", resume); } #endif From 9b7341b670194815d09713025c85cb2113b7d878 Mon Sep 17 00:00:00 2001 From: Lok Tep Date: Tue, 27 Sep 2016 16:47:49 +0200 Subject: [PATCH 11/24] ignore eclipse .project --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 3ec700458d0..839c74cb5e5 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,5 @@ cscope.out /*.hex /pcode /tags +.cproject +.project \ No newline at end of file From 1cbd7a0e5972ee6bf4d5fb04613de0278a494fd2 Mon Sep 17 00:00:00 2001 From: Lok Tep Date: Fri, 30 Sep 2016 16:00:18 +0200 Subject: [PATCH 12/24] CONFIG_ARCH_IRQPRIO check --- arch/arm/src/stm32f7/stm32_sdmmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/src/stm32f7/stm32_sdmmc.c b/arch/arm/src/stm32f7/stm32_sdmmc.c index 3e6aff9620e..9fa8d00b079 100644 --- a/arch/arm/src/stm32f7/stm32_sdmmc.c +++ b/arch/arm/src/stm32f7/stm32_sdmmc.c @@ -113,7 +113,7 @@ #endif #ifdef CONFIG_STM32F7_SDMMC1 -# ifndef CONFIG_SDMMC1_PRI +# if defined(CONFIG_ARCH_IRQPRIO) && !defined(CONFIG_SDMMC1_PRI) # define CONFIG_SDMMC1_PRI NVIC_SYSH_PRIORITY_DEFAULT # endif @@ -130,7 +130,7 @@ #endif #ifdef CONFIG_STM32F7_SDMMC2 -# ifndef CONFIG_SDMMC2_PRI +# if defined(CONFIG_ARCH_IRQPRIO) && !defined(CONFIG_SDMMC2_PRI) # define CONFIG_SDMMC2_PRI NVIC_SYSH_PRIORITY_DEFAULT # endif From 4cb1ba493b64eb567e7b33261e4df18c15d55cfb Mon Sep 17 00:00:00 2001 From: Lok Tep Date: Fri, 30 Sep 2016 16:00:28 +0200 Subject: [PATCH 13/24] mmc renames --- configs/stm32f746-ws/include/board.h | 10 +++++----- configs/stm32f746-ws/nsh/defconfig | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configs/stm32f746-ws/include/board.h b/configs/stm32f746-ws/include/board.h index ea5072ce203..17ca38417ea 100644 --- a/configs/stm32f746-ws/include/board.h +++ b/configs/stm32f746-ws/include/board.h @@ -274,16 +274,16 @@ * SDIOCLK=48MHz, SDIO_CK=SDIOCLK/(118+2)=400 KHz */ -#define SDMMC1_INIT_CLKDIV (118 << SDIO_CLKCR_CLKDIV_SHIFT) +#define STM32_SDMMC_INIT_CLKDIV (118 << STM32_SDMMC_CLKCR_CLKDIV_SHIFT) /* DMA ON: SDIOCLK=48MHz, SDIO_CK=SDIOCLK/(1+2)=16 MHz * DMA OFF: SDIOCLK=48MHz, SDIO_CK=SDIOCLK/(2+2)=12 MHz */ #ifdef CONFIG_SDIO_DMA -# define SDMMC1_MMCXFR_CLKDIV (1 << SDIO_CLKCR_CLKDIV_SHIFT) +# define STM32_SDMMC_MMCXFR_CLKDIV (1 << STM32_SDMMC_CLKCR_CLKDIV_SHIFT) #else -# define SDMMC1_MMCXFR_CLKDIV (2 << SDIO_CLKCR_CLKDIV_SHIFT) +# define STM32_SDMMC_MMCXFR_CLKDIV (2 << STM32_SDMMC_CLKCR_CLKDIV_SHIFT) #endif /* DMA ON: SDIOCLK=48MHz, SDIO_CK=SDIOCLK/(1+2)=16 MHz @@ -291,9 +291,9 @@ */ #ifdef CONFIG_SDIO_DMA -# define SDMMC1_SDXFR_CLKDIV (1 << SDIO_CLKCR_CLKDIV_SHIFT) +# define STM32_SDMMC_SDXFR_CLKDIV (1 << STM32_SDMMC_CLKCR_CLKDIV_SHIFT) #else -# define SDMMC1_SDXFR_CLKDIV (2 << SDIO_CLKCR_CLKDIV_SHIFT) +# define STM32_SDMMC_SDXFR_CLKDIV (2 << STM32_SDMMC_CLKCR_CLKDIV_SHIFT) #endif /************************************************************************************ diff --git a/configs/stm32f746-ws/nsh/defconfig b/configs/stm32f746-ws/nsh/defconfig index c9598479e3d..6fafd45558f 100644 --- a/configs/stm32f746-ws/nsh/defconfig +++ b/configs/stm32f746-ws/nsh/defconfig @@ -312,7 +312,7 @@ CONFIG_STM32F7_OTGFS=y # CONFIG_STM32F7_RNG is not set # CONFIG_STM32F7_SAI1 is not set # CONFIG_STM32F7_SAI2 is not set -##CONFIG_STM32F7_SDMMC1=y +CONFIG_STM32F7_SDMMC1=y # CONFIG_STM32F7_SPDIFRX is not set CONFIG_STM32F7_SPI1=y # CONFIG_STM32F7_SPI2 is not set From 33cea5038f516d1b2bf57fe3badbafb6b3069d29 Mon Sep 17 00:00:00 2001 From: Lok Tep Date: Sat, 1 Oct 2016 19:38:43 +0200 Subject: [PATCH 14/24] memory corruption, typo addr-value --- arch/arm/src/stm32f7/stm32_otgdev.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/arm/src/stm32f7/stm32_otgdev.c b/arch/arm/src/stm32f7/stm32_otgdev.c index f06175cb5bf..54a0232cdfe 100644 --- a/arch/arm/src/stm32f7/stm32_otgdev.c +++ b/arch/arm/src/stm32f7/stm32_otgdev.c @@ -4230,7 +4230,10 @@ static void stm32_epin_disable(FAR struct stm32_ep_s *privep) /* Clear the EPDISD interrupt indication */ - stm32_putreg(OTG_DIEPINT_EPDISD, stm32_getreg(regaddr)); + regval = stm32_getreg(regaddr); + regval |= OTG_DIEPINT_EPDISD; + stm32_putreg(OTG_DIEPINT_EPDISD, regaddr); + /* Flush any data remaining in the TxFIFO */ From a196f2968c9fe8697ca58717bb1ee56008055108 Mon Sep 17 00:00:00 2001 From: Lok Tep Date: Sat, 1 Oct 2016 19:39:33 +0200 Subject: [PATCH 15/24] more stack size for Idle thread --- configs/stm32f746-ws/nsh/defconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configs/stm32f746-ws/nsh/defconfig b/configs/stm32f746-ws/nsh/defconfig index 6fafd45558f..5c0a5298200 100644 --- a/configs/stm32f746-ws/nsh/defconfig +++ b/configs/stm32f746-ws/nsh/defconfig @@ -312,7 +312,7 @@ CONFIG_STM32F7_OTGFS=y # CONFIG_STM32F7_RNG is not set # CONFIG_STM32F7_SAI1 is not set # CONFIG_STM32F7_SAI2 is not set -CONFIG_STM32F7_SDMMC1=y +##CONFIG_STM32F7_SDMMC1=y # CONFIG_STM32F7_SPDIFRX is not set CONFIG_STM32F7_SPI1=y # CONFIG_STM32F7_SPI2 is not set @@ -416,7 +416,7 @@ CONFIG_BOARD_LOOPSPERMSEC=43103 # Interrupt options # CONFIG_ARCH_HAVE_INTERRUPTSTACK=y -CONFIG_ARCH_INTERRUPTSTACK=600 +CONFIG_ARCH_INTERRUPTSTACK=2600 CONFIG_ARCH_HAVE_HIPRI_INTERRUPT=y # CONFIG_ARCH_HIPRI_INTERRUPT is not set @@ -571,7 +571,7 @@ CONFIG_SCHED_LPWORKSTACKSIZE=1800 # # Stack and heap information # -CONFIG_IDLETHREAD_STACKSIZE=500 +CONFIG_IDLETHREAD_STACKSIZE=2500 CONFIG_USERMAIN_STACKSIZE=2500 CONFIG_PTHREAD_STACK_MIN=512 CONFIG_PTHREAD_STACK_DEFAULT=2048 From a2e4c0e898734ea78a9f22975aeea7eee0e38362 Mon Sep 17 00:00:00 2001 From: Lok Tep Date: Fri, 7 Oct 2016 15:12:34 +0200 Subject: [PATCH 16/24] i2s rcc typo fix --- arch/arm/src/stm32f7/stm32f74xx75xx_rcc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm/src/stm32f7/stm32f74xx75xx_rcc.c b/arch/arm/src/stm32f7/stm32f74xx75xx_rcc.c index aac857b9c39..10e4fe2bb52 100644 --- a/arch/arm/src/stm32f7/stm32f74xx75xx_rcc.c +++ b/arch/arm/src/stm32f7/stm32f74xx75xx_rcc.c @@ -882,10 +882,10 @@ static void stm32_stdclockconfig(void) | RCC_PLLI2SCFGR_PLLI2SP_MASK | RCC_PLLI2SCFGR_PLLI2SQ_MASK | RCC_PLLI2SCFGR_PLLI2SR_MASK); - regval |= (STM32_RCC_PLLSAICFGR_PLLSAIN - | STM32_RCC_PLLSAICFGR_PLLSAIP - | STM32_RCC_PLLSAICFGR_PLLSAIQ - | STM32_RCC_PLLSAICFGR_PLLSAIR); + regval |= (STM32_RCC_PLLI2SCFGR_PLLI2SN + | STM32_RCC_PLLI2SCFGR_PLLI2SP + | STM32_RCC_PLLI2SCFGR_PLLI2SQ + | STM32_RCC_PLLI2SCFGR_PLLI2SR); putreg32(regval, STM32_RCC_PLLI2SCFGR); regval = getreg32(STM32_RCC_DCKCFGR2); From fd92f01f556fbe5cf24365aa153af104469441e1 Mon Sep 17 00:00:00 2001 From: Lok Tep Date: Fri, 7 Oct 2016 15:12:46 +0200 Subject: [PATCH 17/24] exact values for i2c clock --- arch/arm/src/stm32f7/stm32_i2c.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm/src/stm32f7/stm32_i2c.c b/arch/arm/src/stm32f7/stm32_i2c.c index 3786e7d52c2..8d5e4db7c0f 100644 --- a/arch/arm/src/stm32f7/stm32_i2c.c +++ b/arch/arm/src/stm32f7/stm32_i2c.c @@ -1306,17 +1306,17 @@ static void stm32_i2c_setclock(FAR struct stm32_i2c_priv_s *priv, uint32_t frequ if (frequency == 100000) { presc = 0; - scl_delay = 3; + scl_delay = 5; sda_delay = 0; - scl_h_period = 30; - scl_l_period = 120; + scl_h_period = 61; + scl_l_period = 89; } else if (frequency == 400000) { presc = 0; scl_delay = 3; - sda_delay = 9; + sda_delay = 0; scl_h_period = 6; scl_l_period = 24; } From 74284aec14ab5824f2a76497fe72aae40cac9b81 Mon Sep 17 00:00:00 2001 From: Lok Tep Date: Fri, 7 Oct 2016 15:13:07 +0200 Subject: [PATCH 18/24] enable i2c clock config --- configs/stm32f746-ws/include/board.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/stm32f746-ws/include/board.h b/configs/stm32f746-ws/include/board.h index 17ca38417ea..8cbd85bc92c 100644 --- a/configs/stm32f746-ws/include/board.h +++ b/configs/stm32f746-ws/include/board.h @@ -144,7 +144,7 @@ /* Configure factors for PLLI2S clock */ - +#define CONFIG_STM32F7_PLLI2S 1 #define STM32_RCC_PLLI2SCFGR_PLLI2SN RCC_PLLI2SCFGR_PLLI2SN(192) #define STM32_RCC_PLLI2SCFGR_PLLI2SP RCC_PLLI2SCFGR_PLLI2SP(2) #define STM32_RCC_PLLI2SCFGR_PLLI2SQ RCC_PLLI2SCFGR_PLLI2SQ(2) From bd08646768ab324fd220cdcf59ea7e385089c824 Mon Sep 17 00:00:00 2001 From: Lok Tep Date: Fri, 7 Oct 2016 15:18:20 +0200 Subject: [PATCH 19/24] enable PLLSAI --- configs/stm32f746-ws/include/board.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/stm32f746-ws/include/board.h b/configs/stm32f746-ws/include/board.h index 8cbd85bc92c..65a27fc6b7b 100644 --- a/configs/stm32f746-ws/include/board.h +++ b/configs/stm32f746-ws/include/board.h @@ -124,7 +124,7 @@ /* Configure factors for PLLSAI clock */ - +#define CONFIG_STM32F7_PLLSAI 1 #define STM32_RCC_PLLSAICFGR_PLLSAIN RCC_PLLSAICFGR_PLLSAIN(192) #define STM32_RCC_PLLSAICFGR_PLLSAIP RCC_PLLSAICFGR_PLLSAIP(2) #define STM32_RCC_PLLSAICFGR_PLLSAIQ RCC_PLLSAICFGR_PLLSAIQ(2) From 9e3479555db056bb8c2c2f6868d9a6b4e5ae4b5b Mon Sep 17 00:00:00 2001 From: Lok Tep Date: Fri, 7 Oct 2016 15:47:30 +0200 Subject: [PATCH 20/24] usb set value typo --- arch/arm/src/stm32f7/stm32_otgdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/src/stm32f7/stm32_otgdev.c b/arch/arm/src/stm32f7/stm32_otgdev.c index 54a0232cdfe..694cf713a8a 100644 --- a/arch/arm/src/stm32f7/stm32_otgdev.c +++ b/arch/arm/src/stm32f7/stm32_otgdev.c @@ -4232,7 +4232,7 @@ static void stm32_epin_disable(FAR struct stm32_ep_s *privep) regval = stm32_getreg(regaddr); regval |= OTG_DIEPINT_EPDISD; - stm32_putreg(OTG_DIEPINT_EPDISD, regaddr); + stm32_putreg(regval, regaddr); /* Flush any data remaining in the TxFIFO */ From 82f880227569c70e24cadd57f7ac9be73b3d867a Mon Sep 17 00:00:00 2001 From: Lok Tep Date: Fri, 4 Nov 2016 21:58:31 +0100 Subject: [PATCH 21/24] typo, missing ( --- arch/arm/src/stm32f7/chip/stm32f74xx75xx_rcc.h | 2 +- arch/arm/src/stm32f7/chip/stm32f76xx77xx_rcc.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/src/stm32f7/chip/stm32f74xx75xx_rcc.h b/arch/arm/src/stm32f7/chip/stm32f74xx75xx_rcc.h index 70c9a2a5aa7..f7bf4d122c1 100644 --- a/arch/arm/src/stm32f7/chip/stm32f74xx75xx_rcc.h +++ b/arch/arm/src/stm32f7/chip/stm32f74xx75xx_rcc.h @@ -580,7 +580,7 @@ # define RCC_PLLI2SCFGR_PLLI2SQ(n) ((uint32_t)(n) << RCC_PLLI2SCFGR_PLLI2SQ_SHIFT) #define RCC_PLLI2SCFGR_PLLI2SR_SHIFT (28) /* Bits 28-30: PLLI2S division factor for I2S clocks */ #define RCC_PLLI2SCFGR_PLLI2SR_MASK (7 << RCC_PLLI2SCFGR_PLLI2SR_SHIFT) -# define RCC_PLLI2SCFGR_PLLI2SR(n) (uint32_t)(n) << RCC_PLLI2SCFGR_PLLI2SR_SHIFT) +# define RCC_PLLI2SCFGR_PLLI2SR(n) ((uint32_t)(n) << RCC_PLLI2SCFGR_PLLI2SR_SHIFT) /* PLLSAI configuration register */ diff --git a/arch/arm/src/stm32f7/chip/stm32f76xx77xx_rcc.h b/arch/arm/src/stm32f7/chip/stm32f76xx77xx_rcc.h index a1b9ed4e321..6c8a7840681 100644 --- a/arch/arm/src/stm32f7/chip/stm32f76xx77xx_rcc.h +++ b/arch/arm/src/stm32f7/chip/stm32f76xx77xx_rcc.h @@ -601,7 +601,7 @@ # define RCC_PLLI2SCFGR_PLLI2SQ(n) ((uint32_t)(n) << RCC_PLLI2SCFGR_PLLI2SQ_SHIFT) #define RCC_PLLI2SCFGR_PLLI2SR_SHIFT (28) /* Bits 28-30: PLLI2S division factor for I2S clocks */ #define RCC_PLLI2SCFGR_PLLI2SR_MASK (7 << RCC_PLLI2SCFGR_PLLI2SR_SHIFT) -# define RCC_PLLI2SCFGR_PLLI2SR(n) (uint32_t)(n) << RCC_PLLI2SCFGR_PLLI2SR_SHIFT) +# define RCC_PLLI2SCFGR_PLLI2SR(n) ((uint32_t)(n) << RCC_PLLI2SCFGR_PLLI2SR_SHIFT) /* PLLSAI configuration register */ From b1b20080372e2ce32be7be1f3a63d0b3ab129b81 Mon Sep 17 00:00:00 2001 From: Lok Tep Date: Fri, 4 Nov 2016 22:02:15 +0100 Subject: [PATCH 22/24] bad offset --- arch/arm/src/stm32f7/chip/stm32_otg.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/src/stm32f7/chip/stm32_otg.h b/arch/arm/src/stm32f7/chip/stm32_otg.h index aca73655523..b69ef1a9ddc 100644 --- a/arch/arm/src/stm32f7/chip/stm32_otg.h +++ b/arch/arm/src/stm32f7/chip/stm32_otg.h @@ -77,7 +77,7 @@ #define STM32_OTG_CID_OFFSET 0x003c /* Core ID register */ #define STM32_OTG_HPTXFSIZ_OFFSET 0x0100 /* Host periodic transmit FIFO size register */ -#define STM32_OTG_DIEPTXF_OFFSET(n) (104+(((n)-1) << 2)) +#define STM32_OTG_DIEPTXF_OFFSET(n) (0x0104+(((n)-1) << 2)) /* Host-mode control and status registers */ From 0fd38c28836b47ba86321a7a673ce07f06451930 Mon Sep 17 00:00:00 2001 From: Lok Tep Date: Fri, 4 Nov 2016 22:02:46 +0100 Subject: [PATCH 23/24] enable mmc --- configs/stm32f746-ws/nsh/defconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/stm32f746-ws/nsh/defconfig b/configs/stm32f746-ws/nsh/defconfig index 5c0a5298200..3da3bc4b5f6 100644 --- a/configs/stm32f746-ws/nsh/defconfig +++ b/configs/stm32f746-ws/nsh/defconfig @@ -127,7 +127,7 @@ CONFIG_ARCH_FAMILY="armv7-m" CONFIG_ARCH_CHIP="stm32f7" # CONFIG_ARM_TOOLCHAIN_IAR is not set CONFIG_ARM_TOOLCHAIN_GNU=y -# CONFIG_ARMV7M_USEBASEPRI is not set +CONFIG_ARMV7M_USEBASEPRI=y CONFIG_ARCH_HAVE_CMNVECTOR=y CONFIG_ARMV7M_CMNVECTOR=y # CONFIG_ARMV7M_LAZYFPU is not set @@ -312,7 +312,7 @@ CONFIG_STM32F7_OTGFS=y # CONFIG_STM32F7_RNG is not set # CONFIG_STM32F7_SAI1 is not set # CONFIG_STM32F7_SAI2 is not set -##CONFIG_STM32F7_SDMMC1=y +CONFIG_STM32F7_SDMMC1=y # CONFIG_STM32F7_SPDIFRX is not set CONFIG_STM32F7_SPI1=y # CONFIG_STM32F7_SPI2 is not set From d6315a3084c1e7088ba31cc2e3671b7fc38d349a Mon Sep 17 00:00:00 2001 From: Lok Tep Date: Mon, 7 Nov 2016 13:11:59 +0100 Subject: [PATCH 24/24] del stm32f74xx75xx_sdmmc.h --- .../src/stm32f7/chip/stm32f74xx75xx_sdmmc.h | 268 ------------------ 1 file changed, 268 deletions(-) delete mode 100644 arch/arm/src/stm32f7/chip/stm32f74xx75xx_sdmmc.h diff --git a/arch/arm/src/stm32f7/chip/stm32f74xx75xx_sdmmc.h b/arch/arm/src/stm32f7/chip/stm32f74xx75xx_sdmmc.h deleted file mode 100644 index 117c4da2faf..00000000000 --- a/arch/arm/src/stm32f7/chip/stm32f74xx75xx_sdmmc.h +++ /dev/null @@ -1,268 +0,0 @@ -/************************************************************************************ - * arch/arm/src/stm32f7/chip/stm32_sdmmc.h - * - * Copyright (C) 2009, 2011-2016 Gregory Nutt. All rights reserved. - * Author: Gregory Nutt - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * 3. Neither the name NuttX nor the names of its contributors may be - * used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ************************************************************************************/ - -#ifndef __ARCH_ARM_SRC_STM32F7_CHIP_STM32F74XX75XX_SDMMC_H -#define __ARCH_ARM_SRC_STM32F7_CHIP_STM32F74XX75XX_SDMMC_H - -/************************************************************************************ - * Pre-processor Definitions - ************************************************************************************/ - -/* Register Offsets *****************************************************************/ - -#define STM32_SDMMC1_POWER_OFFSET 0x0000 /* SDIO power control register */ -#define STM32_SDMMC1_CLKCR_OFFSET 0x0004 /* SDI clock control register */ -#define STM32_SDMMC1_ARG_OFFSET 0x0008 /* SDIO argument register */ -#define STM32_SDMMC1_CMD_OFFSET 0x000c /* SDIO command register */ -#define STM32_SDMMC1_RESPCMD_OFFSET 0x0010 /* SDIO command response register */ -#define STM32_SDMMC1_RESP_OFFSET(n) (0x0010+4*(n)) -#define STM32_SDMMC1_RESP1_OFFSET 0x0014 /* SDIO response 1 register */ -#define STM32_SDMMC1_RESP2_OFFSET 0x0018 /* SDIO response 2 register */ -#define STM32_SDMMC1_RESP3_OFFSET 0x001c /* SDIO response 3 register */ -#define STM32_SDMMC1_RESP4_OFFSET 0x0020 /* SDIO response 4 register */ -#define STM32_SDMMC1_DTIMER_OFFSET 0x0024 /* SDIO data timer register */ -#define STM32_SDMMC1_DLEN_OFFSET 0x0028 /* SDIO data length register */ -#define STM32_SDMMC1_DCTRL_OFFSET 0x002c /* SDIO data control register */ -#define STM32_SDMMC1_DCOUNT_OFFSET 0x0030 /* SDIO data counter register */ -#define STM32_SDMMC1_STA_OFFSET 0x0034 /* SDIO status register */ -#define STM32_SDMMC1_ICR_OFFSET 0x0038 /* SDIO interrupt clear register */ -#define STM32_SDMMC1_MASK_OFFSET 0x003c /* SDIO mask register */ -#define STM32_SDMMC1_FIFOCNT_OFFSET 0x0048 /* SDIO FIFO counter register */ -#define STM32_SDMMC1_FIFO_OFFSET 0x0080 /* SDIO data FIFO register */ - -/* Register Addresses ***************************************************************/ - -#define STM32_SDMMC1_POWER (STM32_SDMMC1_BASE+STM32_SDMMC1_POWER_OFFSET) -#define STM32_SDMMC1_CLKCR (STM32_SDMMC1_BASE+STM32_SDMMC1_CLKCR_OFFSET) -#define STM32_SDMMC1_ARG (STM32_SDMMC1_BASE+STM32_SDMMC1_ARG_OFFSET) -#define STM32_SDMMC1_CMD (STM32_SDMMC1_BASE+STM32_SDMMC1_CMD_OFFSET) -#define STM32_SDMMC1_RESPCMD (STM32_SDMMC1_BASE+STM32_SDMMC1_RESPCMD_OFFSET) -#define STM32_SDMMC1_RESP(n) (STM32_SDMMC1_BASE+STM32_SDMMC1_RESP_OFFSET(n)) -#define STM32_SDMMC1_RESP1 (STM32_SDMMC1_BASE+STM32_SDMMC1_RESP1_OFFSET) -#define STM32_SDMMC1_RESP2 (STM32_SDMMC1_BASE+STM32_SDMMC1_RESP2_OFFSET) -#define STM32_SDMMC1_RESP3 (STM32_SDMMC1_BASE+STM32_SDMMC1_RESP3_OFFSET) -#define STM32_SDMMC1_RESP4 (STM32_SDMMC1_BASE+STM32_SDMMC1_RESP4_OFFSET) -#define STM32_SDMMC1_DTIMER (STM32_SDMMC1_BASE+STM32_SDMMC1_DTIMER_OFFSET) -#define STM32_SDMMC1_DLEN (STM32_SDMMC1_BASE+STM32_SDMMC1_DLEN_OFFSET) -#define STM32_SDMMC1_DCTRL (STM32_SDMMC1_BASE+STM32_SDMMC1_DCTRL_OFFSET) -#define STM32_SDMMC1_DCOUNT (STM32_SDMMC1_BASE+STM32_SDMMC1_DCOUNT_OFFSET) -#define STM32_SDMMC1_STA (STM32_SDMMC1_BASE+STM32_SDMMC1_STA_OFFSET) -#define STM32_SDMMC1_ICR (STM32_SDMMC1_BASE+STM32_SDMMC1_ICR_OFFSET) -#define STM32_SDMMC1_MASK (STM32_SDMMC1_BASE+STM32_SDMMC1_MASK_OFFSET) -#define STM32_SDMMC1_FIFOCNT (STM32_SDMMC1_BASE+STM32_SDMMC1_FIFOCNT_OFFSET) -#define STM32_SDMMC1_FIFO (STM32_SDMMC1_BASE+STM32_SDMMC1_FIFO_OFFSET) - - -/* Register Bitfield Definitions ****************************************************/ - -#define SDIO_POWER_PWRCTRL_SHIFT (0) /* Bits 0-1: Power supply control bits */ -#define SDIO_POWER_PWRCTRL_MASK (3 << SDIO_POWER_PWRCTRL_SHIFT) -# define SDIO_POWER_PWRCTRL_OFF (0 << SDIO_POWER_PWRCTRL_SHIFT) /* 00: Power-off: card clock stopped */ -# define SDIO_POWER_PWRCTRL_PWRUP (2 << SDIO_POWER_PWRCTRL_SHIFT) /* 10: Reserved power-up */ -# define SDIO_POWER_PWRCTRL_ON (3 << SDIO_POWER_PWRCTRL_SHIFT) /* 11: Power-on: card is clocked */ - -#define SDIO_POWER_RESET (0) /* Reset value */ - -#define SDIO_CLKCR_CLKDIV_SHIFT (0) /* Bits 7-0: Clock divide factor */ -#define SDIO_CLKCR_CLKDIV_MASK (0xff << SDIO_CLKCR_CLKDIV_SHIFT) -#define SDIO_CLKCR_CLKEN (1 << 8) /* Bit 8: Clock enable bit */ -#define SDIO_CLKCR_PWRSAV (1 << 9) /* Bit 9: Power saving configuration bit */ -#define SDIO_CLKCR_BYPASS (1 << 10) /* Bit 10: Clock divider bypass enable bit */ -#define SDIO_CLKCR_WIDBUS_SHIFT (11) /* Bits 12-11: Wide bus mode enable bits */ -#define SDIO_CLKCR_WIDBUS_MASK (3 << SDIO_CLKCR_WIDBUS_SHIFT) -# define SDIO_CLKCR_WIDBUS_D1 (0 << SDIO_CLKCR_WIDBUS_SHIFT) /* 00: Default (SDIO_D0) */ -# define SDIO_CLKCR_WIDBUS_D4 (1 << SDIO_CLKCR_WIDBUS_SHIFT) /* 01: 4-wide (SDIO_D[3:0]) */ -# define SDIO_CLKCR_WIDBUS_D8 (2 << SDIO_CLKCR_WIDBUS_SHIFT) /* 10: 8-wide (SDIO_D[7:0]) */ -#define SDIO_CLKCR_NEGEDGE (1 << 13) /* Bit 13: SDIO_CK dephasing selection bit */ -#define SDIO_CLKCR_HWFC_EN (1 << 14) /* Bit 14: HW Flow Control enable */ - -#define SDIO_CLKCR_RESET (0) /* Reset value */ -#define SDIO_ARG_RESET (0) /* Reset value */ - -#define SDIO_CLKCR_CLKEN_BB (STM32_SDMMC1_CLKCR_BB + (8 * 4)) -#define SDIO_CLKCR_PWRSAV_BB (STM32_SDMMC1_CLKCR_BB + (9 * 4)) -#define SDIO_CLKCR_BYPASS_BB (STM32_SDMMC1_CLKCR_BB + (10 * 4)) -#define SDIO_CLKCR_NEGEDGE_BB (STM32_SDMMC1_CLKCR_BB + (13 * 4)) -#define SDIO_CLKCR_HWFC_EN_BB (STM32_SDMMC1_CLKCR_BB + (14 * 4)) - -#define SDIO_CMD_CMDINDEX_SHIFT (0) -#define SDIO_CMD_CMDINDEX_MASK (0x3f << SDIO_CMD_CMDINDEX_SHIFT) -#define SDIO_CMD_WAITRESP_SHIFT (6) /* Bits 7-6: Wait for response bits */ -#define SDIO_CMD_WAITRESP_MASK (3 << SDIO_CMD_WAITRESP_SHIFT) -# define SDIO_CMD_NORESPONSE (0 << SDIO_CMD_WAITRESP_SHIFT) /* 00/10: No response */ -# define SDIO_CMD_SHORTRESPONSE (1 << SDIO_CMD_WAITRESP_SHIFT) /* 01: Short response */ -# define SDIO_CMD_LONGRESPONSE (3 << SDIO_CMD_WAITRESP_SHIFT) /* 11: Long response */ -#define SDIO_CMD_WAITINT (1 << 8) /* Bit 8: CPSM waits for interrupt request */ -#define SDIO_CMD_WAITPEND (1 << 9) /* Bit 9: CPSM Waits for ends of data transfer */ -#define SDIO_CMD_CPSMEN (1 << 10) /* Bit 10: Command path state machine enable */ -#define SDIO_CMD_SUSPEND (1 << 11) /* Bit 11: SD I/O suspend command */ -#define SDIO_CMD_ENDCMD (1 << 12) /* Bit 12: Enable CMD completion */ -#define SDIO_CMD_NIEN (1 << 13) /* Bit 13: not Interrupt Enable */ -#define SDIO_CMD_ATACMD (1 << 14) /* Bit 14: CE-ATA command */ - -#define SDIO_CMD_RESET (0) /* Reset value */ - -#define SDIO_CMD_WAITINT_BB (STM32_SDMMC1_CMD_BB + (8 * 4)) -#define SDIO_CMD_WAITPEND_BB (STM32_SDMMC1_CMD_BB + (9 * 4)) -#define SDIO_CMD_CPSMEN_BB (STM32_SDMMC1_CMD_BB + (10 * 4)) -#define SDIO_CMD_SUSPEND_BB (STM32_SDMMC1_CMD_BB + (11 * 4)) -#define SDIO_CMD_ENCMD_BB (STM32_SDMMC1_CMD_BB + (12 * 4)) -#define SDIO_CMD_NIEN_BB (STM32_SDMMC1_CMD_BB + (13 * 4)) -#define SDIO_CMD_ATACMD_BB (STM32_SDMMC1_CMD_BB + (14 * 4)) - -#define SDIO_RESPCMD_SHIFT (0) -#define SDIO_RESPCMD_MASK (0x3f << SDIO_RESPCMD_SHIFT) - -#define SDIO_DTIMER_RESET (0) /* Reset value */ - -#define SDIO_DLEN_SHIFT (0) -#define SDIO_DLEN_MASK (0x01ffffff << SDIO_DLEN_SHIFT) - -#define SDIO_DLEN_RESET (0) /* Reset value */ - -#define SDIO_DCTRL_DTEN (1 << 0) /* Bit 0: Data transfer enabled bit */ -#define SDIO_DCTRL_DTDIR (1 << 1) /* Bit 1: Data transfer direction */ -#define SDIO_DCTRL_DTMODE (1 << 2) /* Bit 2: Data transfer mode */ -#define SDIO_DCTRL_DMAEN (1 << 3) /* Bit 3: DMA enable bit */ -#define SDIO_DCTRL_DBLOCKSIZE_SHIFT (4) /* Bits 7-4: Data block size */ -#define SDIO_DCTRL_DBLOCKSIZE_MASK (15 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) -# define SDIO_DCTRL_1BYTE (0 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) -# define SDIO_DCTRL_2BYTES (1 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) -# define SDIO_DCTRL_4BYTES (2 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) -# define SDIO_DCTRL_8BYTES (3 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) -# define SDIO_DCTRL_16BYTES (4 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) -# define SDIO_DCTRL_32BYTES (5 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) -# define SDIO_DCTRL_64BYTES (6 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) -# define SDIO_DCTRL_128BYTES (7 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) -# define SDIO_DCTRL_256BYTES (8 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) -# define SDIO_DCTRL_512BYTES (9 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) -# define SDIO_DCTRL_1KBYTE (10 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) -# define SDIO_DCTRL_2KBYTES (11 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) -# define SDIO_DCTRL_4KBYTES (12 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) -# define SDIO_DCTRL_8KBYTES (13 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) -# define SDIO_DCTRL_16KBYTES (14 << SDIO_DCTRL_DBLOCKSIZE_SHIFT) -#define SDIO_DCTRL_RWSTART (1 << 8) /* Bit 8: Read wait start */ -#define SDIO_DCTRL_RWSTOP (1 << 9) /* Bit 9: Read wait stop */ -#define SDIO_DCTRL_RWMOD (1 << 10) /* Bit 10: Read wait mode */ -#define SDIO_DCTRL_SDIOEN (1 << 11) /* Bit 11: SD I/O enable functions */ - -#define SDIO_DCTRL_RESET (0) /* Reset value */ - -#define SDIO_DCTRL_DTEN_BB (STM32_SDMMC1_DCTRL_BB + (0 * 4)) -#define SDIO_DCTRL_DTDIR_BB (STM32_SDMMC1_DCTRL_BB + (1 * 4)) -#define SDIO_DCTRL_DTMODE_BB (STM32_SDMMC1_DCTRL_BB + (2 * 4)) -#define SDIO_DCTRL_DMAEN_BB (STM32_SDMMC1_DCTRL_BB + (3 * 4)) -#define SDIO_DCTRL_RWSTART_BB (STM32_SDMMC1_DCTRL_BB + (8 * 4)) -#define SDIO_DCTRL_RWSTOP_BB (STM32_SDMMC1_DCTRL_BB + (9 * 4)) -#define SDIO_DCTRL_RWMOD_BB (STM32_SDMMC1_DCTRL_BB + (10 * 4)) -#define SDIO_DCTRL_SDIOEN_BB (STM32_SDMMC1_DCTRL_BB + (11 * 4)) - -#define SDIO_DATACOUNT_SHIFT (0) -#define SDIO_DATACOUNT_MASK (0x01ffffff << SDIO_DATACOUNT_SHIFT) - -#define SDIO_STA_CCRCFAIL (1 << 0) /* Bit 0: Command response CRC fail */ -#define SDIO_STA_DCRCFAIL (1 << 1) /* Bit 1: Data block CRC fail */ -#define SDIO_STA_CTIMEOUT (1 << 2) /* Bit 2: Command response timeout */ -#define SDIO_STA_DTIMEOUT (1 << 3) /* Bit 3: Data timeout */ -#define SDIO_STA_TXUNDERR (1 << 4) /* Bit 4: Transmit FIFO underrun error */ -#define SDIO_STA_RXOVERR (1 << 5) /* Bit 5: Received FIFO overrun error */ -#define SDIO_STA_CMDREND (1 << 6) /* Bit 6: Command response received */ -#define SDIO_STA_CMDSENT (1 << 7) /* Bit 7: Command sent */ -#define SDIO_STA_DATAEND (1 << 8) /* Bit 8: Data end */ -#define SDIO_STA_STBITERR (1 << 9) /* Bit 9: Start bit not detected */ -#define SDIO_STA_DBCKEND (1 << 10) /* Bit 10: Data block sent/received */ -#define SDIO_STA_CMDACT (1 << 11) /* Bit 11: Command transfer in progress */ -#define SDIO_STA_TXACT (1 << 12) /* Bit 12: Data transmit in progress */ -#define SDIO_STA_RXACT (1 << 13) /* Bit 13: Data receive in progress */ -#define SDIO_STA_TXFIFOHE (1 << 14) /* Bit 14: Transmit FIFO half empty */ -#define SDIO_STA_RXFIFOHF (1 << 15) /* Bit 15: Receive FIFO half full */ -#define SDIO_STA_TXFIFOF (1 << 16) /* Bit 16: Transmit FIFO full */ -#define SDIO_STA_RXFIFOF (1 << 17) /* Bit 17: Receive FIFO full */ -#define SDIO_STA_TXFIFOE (1 << 18) /* Bit 18: Transmit FIFO empty */ -#define SDIO_STA_RXFIFOE (1 << 19) /* Bit 19: Receive FIFO empty */ -#define SDIO_STA_TXDAVL (1 << 20) /* Bit 20: Data available in transmit FIFO */ -#define SDIO_STA_RXDAVL (1 << 21) /* Bit 21: Data available in receive FIFO */ -#define SDIO_STA_SDIOIT (1 << 22) /* Bit 22: SDIO interrupt received */ -#define SDIO_STA_CEATAEND (1 << 23) /* Bit 23: CMD6 CE-ATA command completion */ - -#define SDIO_ICR_CCRCFAILC (1 << 0) /* Bit 0: CCRCFAIL flag clear bit */ -#define SDIO_ICR_DCRCFAILC (1 << 1) /* Bit 1: DCRCFAIL flag clear bit */ -#define SDIO_ICR_CTIMEOUTC (1 << 2) /* Bit 2: CTIMEOUT flag clear bit */ -#define SDIO_ICR_DTIMEOUTC (1 << 3) /* Bit 3: DTIMEOUT flag clear bit */ -#define SDIO_ICR_TXUNDERRC (1 << 4) /* Bit 4: TXUNDERR flag clear bit */ -#define SDIO_ICR_RXOVERRC (1 << 5) /* Bit 5: RXOVERR flag clear bit */ -#define SDIO_ICR_CMDRENDC (1 << 6) /* Bit 6: CMDREND flag clear bit */ -#define SDIO_ICR_CMDSENTC (1 << 7) /* Bit 7: CMDSENT flag clear bit */ -#define SDIO_ICR_DATAENDC (1 << 8) /* Bit 8: DATAEND flag clear bit */ -#define SDIO_ICR_STBITERRC (1 << 9) /* Bit 9: STBITERR flag clear bit */ -#define SDIO_ICR_DBCKENDC (1 << 10) /* Bit 10: DBCKEND flag clear bit */ -#define SDIO_ICR_SDIOITC (1 << 22) /* Bit 22: SDIOIT flag clear bit */ -#define SDIO_ICR_CEATAENDC (1 << 23) /* Bit 23: CEATAEND flag clear bit */ - -#define SDIO_ICR_RESET 0x00c007ff -#define SDIO_ICR_STATICFLAGS 0x000005ff - -#define SDIO_MASK_CCRCFAILIE (1 << 0) /* Bit 0: Command CRC fail interrupt enable */ -#define SDIO_MASK_DCRCFAILIE (1 << 1) /* Bit 1: Data CRC fail interrupt enable */ -#define SDIO_MASK_CTIMEOUTIE (1 << 2) /* Bit 2: Command timeout interrupt enable */ -#define SDIO_MASK_DTIMEOUTIE (1 << 3) /* Bit 3: Data timeout interrupt enable */ -#define SDIO_MASK_TXUNDERRIE (1 << 4) /* Bit 4: Tx FIFO underrun error interrupt enable */ -#define SDIO_MASK_RXOVERRIE (1 << 5) /* Bit 5: Rx FIFO overrun error interrupt enable */ -#define SDIO_MASK_CMDRENDIE (1 << 6) /* Bit 6: Command response received interrupt enable */ -#define SDIO_MASK_CMDSENTIE (1 << 7) /* Bit 7: Command sent interrupt enable */ -#define SDIO_MASK_DATAENDIE (1 << 8) /* Bit 8: Data end interrupt enable */ -#define SDIO_MASK_STBITERRIE (1 << 9) /* Bit 9: Start bit error interrupt enable */ -#define SDIO_MASK_DBCKENDIE (1 << 10) /* Bit 10: Data block end interrupt enable */ -#define SDIO_MASK_CMDACTIE (1 << 11) /* Bit 11: Command acting interrupt enable */ -#define SDIO_MASK_TXACTIE (1 << 12) /* Bit 12: Data transmit acting interrupt enable */ -#define SDIO_MASK_RXACTIE (1 << 13) /* Bit 13: Data receive acting interrupt enable */ -#define SDIO_MASK_TXFIFOHEIE (1 << 14) /* Bit 14: Tx FIFO half empty interrupt enable */ -#define SDIO_MASK_RXFIFOHFIE (1 << 15) /* Bit 15: Rx FIFO half full interrupt enable */ -#define SDIO_MASK_TXFIFOFIE (1 << 16) /* Bit 16: Tx FIFO full interrupt enable */ -#define SDIO_MASK_RXFIFOFIE (1 << 17) /* Bit 17: Rx FIFO full interrupt enable */ -#define SDIO_MASK_TXFIFOEIE (1 << 18) /* Bit 18: Tx FIFO empty interrupt enable */ -#define SDIO_MASK_RXFIFOEIE (1 << 19) /* Bit 19: Rx FIFO empty interrupt enable */ -#define SDIO_MASK_TXDAVLIE (1 << 20) /* Bit 20: Data available in Tx FIFO interrupt enable */ -#define SDIO_MASK_RXDAVLIE (1 << 21) /* Bit 21: Data available in Rx FIFO interrupt enable */ -#define SDIO_MASK_SDIOITIE (1 << 22) /* Bit 22: SDIO mode interrupt received interrupt enable */ -#define SDIO_MASK_CEATAENDIE (1 << 23) /* Bit 23: CE-ATA command completion interrupt enable */ - -#define SDIO_MASK_RESET (0) - -#define SDIO_FIFOCNT_SHIFT (0) -#define SDIO_FIFOCNT_MASK (0x01ffffff << SDIO_FIFOCNT_SHIFT) - -#endif /* __ARCH_ARM_SRC_STM32F7_CHIP_STM32F74XX75XX_SDMMC_H */ -