From 170e5c613492a3346f1814f490a282a67624e7b3 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Tue, 7 Oct 2014 15:01:42 -0600 Subject: [PATCH] Add files that implement true high speed support for the STM32 OTGHS peripheral. From Brennan Ashton --- arch/arm/src/stm32/chip/stm32_otghs.h | 1052 ++++++ arch/arm/src/stm32/stm32_otghs.h | 127 + arch/arm/src/stm32/stm32_otghshost.c | 4427 +++++++++++++++++++++++++ 3 files changed, 5606 insertions(+) create mode 100644 arch/arm/src/stm32/chip/stm32_otghs.h create mode 100644 arch/arm/src/stm32/stm32_otghs.h create mode 100644 arch/arm/src/stm32/stm32_otghshost.c diff --git a/arch/arm/src/stm32/chip/stm32_otghs.h b/arch/arm/src/stm32/chip/stm32_otghs.h new file mode 100644 index 0000000000..42b4422262 --- /dev/null +++ b/arch/arm/src/stm32/chip/stm32_otghs.h @@ -0,0 +1,1052 @@ +/**************************************************************************************************** + * arch/arm/src/stm32/chip/stm32_otghs.h + * + * Copyright (C) 2012 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_STM32_CHIP_STM32_OTGHS_H +#define __ARCH_ARM_SRC_STM32_CHIP_STM32_OTGHS_H + +/**************************************************************************************************** + * Included Files + ****************************************************************************************************/ + +#include +#include "chip.h" + +/**************************************************************************************************** + * Pre-processor Definitions + ****************************************************************************************************/ +/* General definitions */ + +#define OTGHS_EPTYPE_CTRL (0) /* Control */ +#define OTGHS_EPTYPE_ISOC (1) /* Isochronous */ +#define OTGHS_EPTYPE_BULK (2) /* Bulk */ +#define OTGHS_EPTYPE_INTR (3) /* Interrupt */ + +#define OTGHS_PID_DATA0 (0) +#define OTGHS_PID_DATA2 (1) +#define OTGHS_PID_DATA1 (2) +#define OTGHS_PID_MDATA (3) /* Non-control */ +#define OTGHS_PID_SETUP (3) /* Control */ + +/* If OTGHS2 is defined (FS mode of the HS module), then remap the OTGHS base address */ + +/* Register Offsets *********************************************************************************/ +/* Core global control and status registers */ + +#define STM32_OTGHS_GOTGCTL_OFFSET 0x0000 /* Control and status register */ +#define STM32_OTGHS_GOTGINT_OFFSET 0x0004 /* Interrupt register */ +#define STM32_OTGHS_GAHBCFG_OFFSET 0x0008 /* AHB configuration register */ +#define STM32_OTGHS_GUSBCFG_OFFSET 0x000c /* USB configuration register */ +#define STM32_OTGHS_GRSTCTL_OFFSET 0x0010 /* Reset register */ +#define STM32_OTGHS_GINTSTS_OFFSET 0x0014 /* Core interrupt register */ +#define STM32_OTGHS_GINTMSK_OFFSET 0x0018 /* Interrupt mask register */ +#define STM32_OTGHS_GRXSTSR_OFFSET 0x001c /* Receive status debug read/OTG status read register */ +#define STM32_OTGHS_GRXSTSP_OFFSET 0x0020 /* Receive status debug read/OTG status pop register */ +#define STM32_OTGHS_GRXFSIZ_OFFSET 0x0024 /* Receive FIFO size register */ +#define STM32_OTGHS_HNPTXFSIZ_OFFSET 0x0028 /* Host non-periodic transmit FIFO size register */ +#define STM32_OTGHS_DIEPTXF0_OFFSET 0x0028 /* Endpoint 0 Transmit FIFO size */ +#define STM32_OTGHS_HNPTXSTS_OFFSET 0x002c /* Non-periodic transmit FIFO/queue status register */ +#define STM32_OTGHS_GCCFG_OFFSET 0x0038 /* general core configuration register */ +#define STM32_OTGHS_CID_OFFSET 0x003c /* Core ID register */ +#define STM32_OTGHS_HPTXFSIZ_OFFSET 0x0100 /* Host periodic transmit FIFO size register */ + +#define STM32_OTGHS_DIEPTXF_OFFSET(n) (104+(((n)-1) << 2)) +#define STM32_OTGHS_DIEPTXF1_OFFSET 0x0104 /* Device IN endpoint transmit FIFO1 size register */ +#define STM32_OTGHS_DIEPTXF2_OFFSET 0x0108 /* Device IN endpoint transmit FIFO2 size register */ +#define STM32_OTGHS_DIEPTXF3_OFFSET 0x010c /* Device IN endpoint transmit FIFO3 size register */ + +/* Host-mode control and status registers */ + +#define STM32_OTGHS_HCFG_OFFSET 0x0400 /* Host configuration register */ +#define STM32_OTGHS_HFIR_OFFSET 0x0404 /* Host frame interval register */ +#define STM32_OTGHS_HFNUM_OFFSET 0x0408 /* Host frame number/frame time remaining register */ +#define STM32_OTGHS_HPTXSTS_OFFSET 0x0410 /* Host periodic transmit FIFO/queue status register */ +#define STM32_OTGHS_HAINT_OFFSET 0x0414 /* Host all channels interrupt register */ +#define STM32_OTGHS_HAINTMSK_OFFSET 0x0418 /* Host all channels interrupt mask register */ +#define STM32_OTGHS_HPRT_OFFSET 0x0440 /* Host port control and status register */ + +#define STM32_OTGHS_CHAN_OFFSET(n) (0x500 + ((n) << 5) +#define STM32_OTGHS_HCCHAR_CHOFFSET 0x0000 /* Host channel characteristics register */ +#define STM32_OTGHS_HCINT_CHOFFSET 0x0008 /* Host channel interrupt register */ +#define STM32_OTGHS_HCINTMSK_CHOFFSET 0x000c /* Host channel interrupt mask register */ +#define STM32_OTGHS_HCTSIZ_CHOFFSET 0x0010 /* Host channel interrupt register */ + +#define STM32_OTGHS_HCCHAR_OFFSET(n) (0x500 + ((n) << 5)) +#define STM32_OTGHS_HCCHAR0_OFFSET 0x0500 /* Host channel-0 characteristics register */ +#define STM32_OTGHS_HCCHAR1_OFFSET 0x0520 /* Host channel-1 characteristics register */ +#define STM32_OTGHS_HCCHAR2_OFFSET 0x0540 /* Host channel-2 characteristics register */ +#define STM32_OTGHS_HCCHAR3_OFFSET 0x0560 /* Host channel-3 characteristics register */ +#define STM32_OTGHS_HCCHAR4_OFFSET 0x0580 /* Host channel-4 characteristics register */ +#define STM32_OTGHS_HCCHAR5_OFFSET 0x05a0 /* Host channel-5 characteristics register */ +#define STM32_OTGHS_HCCHAR6_OFFSET 0x05c0 /* Host channel-6 characteristics register */ +#define STM32_OTGHS_HCCHAR7_OFFSET 0x05e0 /* Host channel-7 characteristics register */ +#define STM32_OTGHS_HCCHAR8_OFFSET 0x0600 /* Host channel-8 characteristics register */ +#define STM32_OTGHS_HCCHAR9_OFFSET 0x0620 /* Host channel-9 characteristics register */ +#define STM32_OTGHS_HCCHAR10_OFFSET 0x0640 /* Host channel-10 caracteristics register */ +#define STM32_OTGHS_HCCHAR11_OFFSET 0x0660 /* Host channel-11 characteristics register */ + +#define STM32_OTGHS_HCINT_OFFSET(n) (0x508 + ((n) << 5)) +#define STM32_OTGHS_HCINT0_OFFSET 0x0508 /* Host channel-0 interrupt register */ +#define STM32_OTGHS_HCINT1_OFFSET 0x0528 /* Host channel-1 interrupt register */ +#define STM32_OTGHS_HCINT2_OFFSET 0x0548 /* Host channel-2 interrupt register */ +#define STM32_OTGHS_HCINT3_OFFSET 0x0568 /* Host channel-3 interrupt register */ +#define STM32_OTGHS_HCINT4_OFFSET 0x0588 /* Host channel-4 interrupt register */ +#define STM32_OTGHS_HCINT5_OFFSET 0x05a8 /* Host channel-5 interrupt register */ +#define STM32_OTGHS_HCINT6_OFFSET 0x05c8 /* Host channel-6 interrupt register */ +#define STM32_OTGHS_HCINT7_OFFSET 0x05e8 /* Host channel-7 interrupt register */ +#define STM32_OTGHS_HCINT8_OFFSET 0x0608 /* Host channel-8 interrupt register */ +#define STM32_OTGHS_HCINT9_OFFSET 0x0628 /* Host channel-9 interrupt register */ +#define STM32_OTGHS_HCINT10_OFFSET 0x0648 /* Host channel-10 interrupt register */ +#define STM32_OTGHS_HCINT11_OFFSET 0x0668 /* Host channel-11 interrupt register */ + +#define STM32_OTGHS_HCINTMSK_OFFSET(n) (0x50c + ((n) << 5)) +#define STM32_OTGHS_HCINTMSK0_OFFSET 0x050c /* Host channel-0 interrupt mask register */ +#define STM32_OTGHS_HCINTMSK1_OFFSET 0x052c /* Host channel-1 interrupt mask register */ +#define STM32_OTGHS_HCINTMSK2_OFFSET 0x054c /* Host channel-2 interrupt mask register */ +#define STM32_OTGHS_HCINTMSK3_OFFSET 0x056c /* Host channel-3 interrupt mask register */ +#define STM32_OTGHS_HCINTMSK4_OFFSET 0x058c /* Host channel-4 interrupt mask register */ +#define STM32_OTGHS_HCINTMSK5_OFFSET 0x05ac /* Host channel-5 interrupt mask register */ +#define STM32_OTGHS_HCINTMSK6_OFFSET 0x05cc /* Host channel-6 interrupt mask register */ +#define STM32_OTGHS_HCINTMSK7_OFFSET 0x05ec /* Host channel-7 interrupt mask register */ +#define STM32_OTGHS_HCINTMSK8_OFFSET 0x060c /* Host channel-8 interrupt mask register */ +#define STM32_OTGHS_HCINTMSK9_OFFSET 0x062c /* Host channel-9 interrupt mask register */ +#define STM32_OTGHS_HCINTMSK10_OFFSET 0x064c /* Host channel-10 interrupt mask register */ +#define STM32_OTGHS_HCINTMSK11_OFFSET 0x068c /* Host channel-11 interrupt mask register */ + +#define STM32_OTGHS_HCTSIZ_OFFSET(n) (0x510 + ((n) << 5)) +#define STM32_OTGHS_HCTSIZ0_OFFSET 0x0510 /* Host channel-0 interrupt register */ +#define STM32_OTGHS_HCTSIZ1_OFFSET 0x0530 /* Host channel-1 interrupt register */ +#define STM32_OTGHS_HCTSIZ2_OFFSET 0x0550 /* Host channel-2 interrupt register */ +#define STM32_OTGHS_HCTSIZ3_OFFSET 0x0570 /* Host channel-3 interrupt register */ +#define STM32_OTGHS_HCTSIZ4_OFFSET 0x0590 /* Host channel-4 interrupt register */ +#define STM32_OTGHS_HCTSIZ5_OFFSET 0x05b0 /* Host channel-5 interrupt register */ +#define STM32_OTGHS_HCTSIZ6_OFFSET 0x05d0 /* Host channel-6 interrupt register */ +#define STM32_OTGHS_HCTSIZ7_OFFSET 0x06f0 /* Host channel-7 interrupt register */ +#define STM32_OTGHS_HCTSIZ8_OFFSET 0x0610 /* Host channel-8 interrupt register */ +#define STM32_OTGHS_HCTSIZ9_OFFSET 0x0630 /* Host channel-9 interrupt register */ +#define STM32_OTGHS_HCTSIZ10_OFFSET 0x0650 /* Host channel-10 interrupt register */ +#define STM32_OTGHS_HCTSIZ11_OFFSET 0x05f0 /* Host channel-11 interrupt register */ + +/* Device-mode control and status registers */ + +#define STM32_OTGHS_DCFG_OFFSET 0x0800 /* Device configuration register */ +#define STM32_OTGHS_DCTL_OFFSET 0x0804 /* Device control register */ +#define STM32_OTGHS_DSTS_OFFSET 0x0808 /* Device status register */ +#define STM32_OTGHS_DIEPMSK_OFFSET 0x0810 /* Device IN endpoint common interrupt mask register */ +#define STM32_OTGHS_DOEPMSK_OFFSET 0x0814 /* Device OUT endpoint common interrupt mask register */ +#define STM32_OTGHS_DAINT_OFFSET 0x0818 /* Device all endpoints interrupt register */ +#define STM32_OTGHS_DAINTMSK_OFFSET 0x081c /* All endpoints interrupt mask register */ +#define STM32_OTGHS_DVBUSDIS_OFFSET 0x0828 /* Device VBUS discharge time register */ +#define STM32_OTGHS_DVBUSPULSE_OFFSET 0x082c /* Device VBUS pulsing time register */ +#define STM32_OTGHS_DIEPEMPMSK_OFFSET 0x0834 /* Device IN endpoint FIFO empty interrupt mask register */ + +#define STM32_OTGHS_DIEP_OFFSET(n) (0x0900 + ((n) << 5)) +#define STM32_OTGHS_DIEPCTL_EPOFFSET 0x0000 /* Device endpoint control register */ +#define STM32_OTGHS_DIEPINT_EPOFFSET 0x0008 /* Device endpoint interrupt register */ +#define STM32_OTGHS_DIEPTSIZ_EPOFFSET 0x0010 /* Device IN endpoint transfer size register */ +#define STM32_OTGHS_DTXFSTS_EPOFFSET 0x0018 /* Device IN endpoint transmit FIFO status register */ + +#define STM32_OTGHS_DIEPCTL_OFFSET(n) (0x0900 + ((n) << 5)) +#define STM32_OTGHS_DIEPCTL0_OFFSET 0x0900 /* Device control IN endpoint 0 control register */ +#define STM32_OTGHS_DIEPCTL1_OFFSET 0x0920 /* Device control IN endpoint 2 control register */ +#define STM32_OTGHS_DIEPCTL2_OFFSET 0x0940 /* Device control IN endpoint 3 control register */ +#define STM32_OTGHS_DIEPCTL3_OFFSET 0x0960 /* Device control IN endpoint 4 control register */ + +#define STM32_OTGHS_DIEPINT_OFFSET(n) (0x0908 + ((n) << 5)) +#define STM32_OTGHS_DIEPINT0_OFFSET 0x0908 /* Device endpoint-0 interrupt register */ +#define STM32_OTGHS_DIEPINT1_OFFSET 0x0928 /* Device endpoint-1 interrupt register */ +#define STM32_OTGHS_DIEPINT2_OFFSET 0x0948 /* Device endpoint-2 interrupt register */ +#define STM32_OTGHS_DIEPINT3_OFFSET 0x0968 /* Device endpoint-3 interrupt register */ + +#define STM32_OTGHS_DIEPTSIZ_OFFSET(n) (0x910 + ((n) << 5)) +#define STM32_OTGHS_DIEPTSIZ0_OFFSET 0x0910 /* Device IN endpoint 0 transfer size register */ +#define STM32_OTGHS_DIEPTSIZ1_OFFSET 0x0930 /* Device IN endpoint 1 transfer size register */ +#define STM32_OTGHS_DIEPTSIZ2_OFFSET 0x0950 /* Device IN endpoint 2 transfer size register */ +#define STM32_OTGHS_DIEPTSIZ3_OFFSET 0x0970 /* Device IN endpoint 3 transfer size register */ + +#define STM32_OTGHS_DTXFSTS_OFFSET(n) (0x0918 + ((n) << 5)) +#define STM32_OTGHS_DTXFSTS0_OFFSET 0x0918 /* Device OUT endpoint-0 TxFIFO status register */ +#define STM32_OTGHS_DTXFSTS1_OFFSET 0x0938 /* Device OUT endpoint-1 TxFIFO status register */ +#define STM32_OTGHS_DTXFSTS2_OFFSET 0x0958 /* Device OUT endpoint-2 TxFIFO status register */ +#define STM32_OTGHS_DTXFSTS3_OFFSET 0x0978 /* Device OUT endpoint-3 TxFIFO status register */ + +#define STM32_OTGHS_DOEP_OFFSET(n) (0x0b00 + ((n) << 5)) +#define STM32_OTGHS_DOEPCTL_EPOFFSET 0x0000 /* Device control OUT endpoint 0 control register */ +#define STM32_OTGHS_DOEPINT_EPOFFSET 0x0008 /* Device endpoint-x interrupt register */ + +#define STM32_OTGHS_DOEPCTL_OFFSET(n) (0x0b00 + ((n) << 5)) +#define STM32_OTGHS_DOEPCTL0_OFFSET 0x00b00 /* Device OUT endpoint 0 control register */ +#define STM32_OTGHS_DOEPCTL1_OFFSET 0x00b20 /* Device OUT endpoint 1 control register */ +#define STM32_OTGHS_DOEPCTL2_OFFSET 0x00b40 /* Device OUT endpoint 2 control register */ +#define STM32_OTGHS_DOEPCTL3_OFFSET 0x00b60 /* Device OUT endpoint 3 control register */ + +#define STM32_OTGHS_DOEPINT_OFFSET(n) (0x0b08 + ((n) << 5)) +#define STM32_OTGHS_DOEPINT0_OFFSET 0x00b08 /* Device endpoint-0 interrupt register */ +#define STM32_OTGHS_DOEPINT1_OFFSET 0x00b28 /* Device endpoint-1 interrupt register */ +#define STM32_OTGHS_DOEPINT2_OFFSET 0x00b48 /* Device endpoint-2 interrupt register */ +#define STM32_OTGHS_DOEPINT3_OFFSET 0x00b68 /* Device endpoint-3 interrupt register */ + +#define STM32_OTGHS_DOEPTSIZ_OFFSET(n) (0x0b10 + ((n) << 5)) +#define STM32_OTGHS_DOEPTSIZ0_OFFSET 0x00b10 /* Device OUT endpoint-0 transfer size register */ +#define STM32_OTGHS_DOEPTSIZ1_OFFSET 0x00b30 /* Device OUT endpoint-1 transfer size register */ +#define STM32_OTGHS_DOEPTSIZ2_OFFSET 0x00b50 /* Device OUT endpoint-2 transfer size register */ +#define STM32_OTGHS_DOEPTSIZ3_OFFSET 0x00b70 /* Device OUT endpoint-3 transfer size register */ + +/* Power and clock gating registers */ + +#define STM32_OTGHS_PCGCCTL_OFFSET 0x0e00 /* Power and clock gating control register */ + +/* Data FIFO (DFIFO) access registers */ + +#define STM32_OTGHS_DFIFO_DEP_OFFSET(n) (0x1000 + ((n) << 12)) +#define STM32_OTGHS_DFIFO_HCH_OFFSET(n) (0x1000 + ((n) << 12)) + +#define STM32_OTGHS_DFIFO_DEP0_OFFSET 0x1000 /* 0x1000-0x1ffc Device IN/OUT Endpoint 0 DFIFO Write/Read Access */ +#define STM32_OTGHS_DFIFO_HCH0_OFFSET 0x1000 /* 0x1000-0x1ffc Host OUT/IN Channel 0 DFIFO Read/Write Access */ + +#define STM32_OTGHS_DFIFO_DEP1_OFFSET 0x2000 /* 0x2000-0x2ffc Device IN/OUT Endpoint 1 DFIFO Write/Read Access */ +#define STM32_OTGHS_DFIFO_HCH1_OFFSET 0x2000 /* 0x2000-0x2ffc Host OUT/IN Channel 1 DFIFO Read/Write Access */ + +#define STM32_OTGHS_DFIFO_DEP2_OFFSET 0x3000 /* 0x3000-0x3ffc Device IN/OUT Endpoint 2 DFIFO Write/Read Access */ +#define STM32_OTGHS_DFIFO_HCH2_OFFSET 0x3000 /* 0x3000-0x3ffc Host OUT/IN Channel 2 DFIFO Read/Write Access */ + +#define STM32_OTGHS_DFIFO_DEP3_OFFSET 0x4000 /* 0x4000-0x4ffc Device IN/OUT Endpoint 3 DFIFO Write/Read Access */ +#define STM32_OTGHS_DFIFO_HCH3_OFFSET 0x4000 /* 0x4000-0x4ffc Host OUT/IN Channel 3 DFIFO Read/Write Access */ + +/* Register Addresses *******************************************************************************/ + +#define STM32_OTGHS_GOTGCTL (STM32_OTGHS_BASE+STM32_OTGHS_GOTGCTL_OFFSET) +#define STM32_OTGHS_GOTGINT (STM32_OTGHS_BASE+STM32_OTGHS_GOTGINT_OFFSET) +#define STM32_OTGHS_GAHBCFG (STM32_OTGHS_BASE+STM32_OTGHS_GAHBCFG_OFFSET) +#define STM32_OTGHS_GUSBCFG (STM32_OTGHS_BASE+STM32_OTGHS_GUSBCFG_OFFSET) +#define STM32_OTGHS_GRSTCTL (STM32_OTGHS_BASE+STM32_OTGHS_GRSTCTL_OFFSET) +#define STM32_OTGHS_GINTSTS (STM32_OTGHS_BASE+STM32_OTGHS_GINTSTS_OFFSET) +#define STM32_OTGHS_GINTMSK (STM32_OTGHS_BASE+STM32_OTGHS_GINTMSK_OFFSET) +#define STM32_OTGHS_GRXSTSR (STM32_OTGHS_BASE+STM32_OTGHS_GRXSTSR_OFFSET) +#define STM32_OTGHS_GRXSTSP (STM32_OTGHS_BASE+STM32_OTGHS_GRXSTSP_OFFSET) +#define STM32_OTGHS_GRXFSIZ (STM32_OTGHS_BASE+STM32_OTGHS_GRXFSIZ_OFFSET) +#define STM32_OTGHS_HNPTXFSIZ (STM32_OTGHS_BASE+STM32_OTGHS_HNPTXFSIZ_OFFSET) +#define STM32_OTGHS_DIEPTXF0 (STM32_OTGHS_BASE+STM32_OTGHS_DIEPTXF0_OFFSET) +#define STM32_OTGHS_HNPTXSTS (STM32_OTGHS_BASE+STM32_OTGHS_HNPTXSTS_OFFSET) +#define STM32_OTGHS_GCCFG (STM32_OTGHS_BASE+STM32_OTGHS_GCCFG_OFFSET) +#define STM32_OTGHS_CID (STM32_OTGHS_BASE+STM32_OTGHS_CID_OFFSET) +#define STM32_OTGHS_HPTXFSIZ (STM32_OTGHS_BASE+STM32_OTGHS_HPTXFSIZ_OFFSET) + +#define STM32_OTGHS_DIEPTXF(n) (STM32_OTGHS_BASE+STM32_OTGHS_DIEPTXF_OFFSET(n)) +#define STM32_OTGHS_DIEPTXF1 (STM32_OTGHS_BASE+STM32_OTGHS_DIEPTXF1_OFFSET) +#define STM32_OTGHS_DIEPTXF2 (STM32_OTGHS_BASE+STM32_OTGHS_DIEPTXF2_OFFSET) +#define STM32_OTGHS_DIEPTXF3 (STM32_OTGHS_BASE+STM32_OTGHS_DIEPTXF3_OFFSET) + +/* Host-mode control and status registers */ + +#define STM32_OTGHS_HCFG (STM32_OTGHS_BASE+STM32_OTGHS_HCFG_OFFSET) +#define STM32_OTGHS_HFIR (STM32_OTGHS_BASE+STM32_OTGHS_HFIR_OFFSET) +#define STM32_OTGHS_HFNUM (STM32_OTGHS_BASE+STM32_OTGHS_HFNUM_OFFSET) +#define STM32_OTGHS_HPTXSTS (STM32_OTGHS_BASE+STM32_OTGHS_HPTXSTS_OFFSET) +#define STM32_OTGHS_HAINT (STM32_OTGHS_BASE+STM32_OTGHS_HAINT_OFFSET) +#define STM32_OTGHS_HAINTMSK (STM32_OTGHS_BASE+STM32_OTGHS_HAINTMSK_OFFSET) +#define STM32_OTGHS_HPRT (STM32_OTGHS_BASE+STM32_OTGHS_HPRT_OFFSET) + +#define STM32_OTGHS_CHAN(n) (STM32_OTGHS_BASE+STM32_OTGHS_CHAN_OFFSET(n)) + +#define STM32_OTGHS_HCCHAR(n) (STM32_OTGHS_BASE+STM32_OTGHS_HCCHAR_OFFSET(n)) +#define STM32_OTGHS_HCCHAR0 (STM32_OTGHS_BASE+STM32_OTGHS_HCCHAR0_OFFSET) +#define STM32_OTGHS_HCCHAR1 (STM32_OTGHS_BASE+STM32_OTGHS_HCCHAR1_OFFSET) +#define STM32_OTGHS_HCCHAR2 (STM32_OTGHS_BASE+STM32_OTGHS_HCCHAR2_OFFSET) +#define STM32_OTGHS_HCCHAR3 (STM32_OTGHS_BASE+STM32_OTGHS_HCCHAR3_OFFSET) +#define STM32_OTGHS_HCCHAR4 (STM32_OTGHS_BASE+STM32_OTGHS_HCCHAR4_OFFSET) +#define STM32_OTGHS_HCCHAR5 (STM32_OTGHS_BASE+STM32_OTGHS_HCCHAR5_OFFSET) +#define STM32_OTGHS_HCCHAR6 (STM32_OTGHS_BASE+STM32_OTGHS_HCCHAR6_OFFSET) +#define STM32_OTGHS_HCCHAR7 (STM32_OTGHS_BASE+STM32_OTGHS_HCCHAR7_OFFSET) +#define STM32_OTGHS_HCCHAR8 (STM32_OTGHS_BASE+STM32_OTGHS_HCCHAR8_OFFSET) +#define STM32_OTGHS_HCCHAR9 (STM32_OTGHS_BASE+STM32_OTGHS_HCCHAR9_OFFSET) +#define STM32_OTGHS_HCCHAR10 (STM32_OTGHS_BASE+STM32_OTGHS_HCCHAR10_OFFSET) +#define STM32_OTGHS_HCCHAR11 (STM32_OTGHS_BASE+STM32_OTGHS_HCCHAR11_OFFSET) + +#define STM32_OTGHS_HCINT(n) (STM32_OTGHS_BASE+STM32_OTGHS_HCINT_OFFSET(n)) +#define STM32_OTGHS_HCINT0 (STM32_OTGHS_BASE+STM32_OTGHS_HCINT0_OFFSET) +#define STM32_OTGHS_HCINT1 (STM32_OTGHS_BASE+STM32_OTGHS_HCINT1_OFFSET) +#define STM32_OTGHS_HCINT2 (STM32_OTGHS_BASE+STM32_OTGHS_HCINT2_OFFSET) +#define STM32_OTGHS_HCINT3 (STM32_OTGHS_BASE+STM32_OTGHS_HCINT3_OFFSET) +#define STM32_OTGHS_HCINT4 (STM32_OTGHS_BASE+STM32_OTGHS_HCINT4_OFFSET) +#define STM32_OTGHS_HCINT5 (STM32_OTGHS_BASE+STM32_OTGHS_HCINT5_OFFSET) +#define STM32_OTGHS_HCINT6 (STM32_OTGHS_BASE+STM32_OTGHS_HCINT6_OFFSET) +#define STM32_OTGHS_HCINT7 (STM32_OTGHS_BASE+STM32_OTGHS_HCINT7_OFFSET) +#define STM32_OTGHS_HCINT8 (STM32_OTGHS_BASE+STM32_OTGHS_HCINT8_OFFSET) +#define STM32_OTGHS_HCINT9 (STM32_OTGHS_BASE+STM32_OTGHS_HCINT9_OFFSET) +#define STM32_OTGHS_HCINT10 (STM32_OTGHS_BASE+STM32_OTGHS_HCINT10_OFFSET) +#define STM32_OTGHS_HCINT11 (STM32_OTGHS_BASE+STM32_OTGHS_HCINT11_OFFSET) + +#define STM32_OTGHS_HCINTMSK(n) (STM32_OTGHS_BASE+STM32_OTGHS_HCINTMSK_OFFSET(n)) +#define STM32_OTGHS_HCINTMSK0 (STM32_OTGHS_BASE+STM32_OTGHS_HCINTMSK0_OFFSET) +#define STM32_OTGHS_HCINTMSK1 (STM32_OTGHS_BASE+STM32_OTGHS_HCINTMSK1_OFFSET) +#define STM32_OTGHS_HCINTMSK2 (STM32_OTGHS_BASE+STM32_OTGHS_HCINTMSK2_OFFSET) +#define STM32_OTGHS_HCINTMSK3 (STM32_OTGHS_BASE+STM32_OTGHS_HCINTMSK3_OFFSET) +#define STM32_OTGHS_HCINTMSK4 (STM32_OTGHS_BASE+STM32_OTGHS_HCINTMSK4_OFFSET) +#define STM32_OTGHS_HCINTMSK5 (STM32_OTGHS_BASE+STM32_OTGHS_HCINTMSK5_OFFSET) +#define STM32_OTGHS_HCINTMSK6 (STM32_OTGHS_BASE+STM32_OTGHS_HCINTMSK6_OFFSET) +#define STM32_OTGHS_HCINTMSK7 (STM32_OTGHS_BASE+STM32_OTGHS_HCINTMSK7_OFFSET) +#define STM32_OTGHS_HCINTMSK8 (STM32_OTGHS_BASE+STM32_OTGHS_HCINTMSK8_OFFSET) +#define STM32_OTGHS_HCINTMSK9 (STM32_OTGHS_BASE+STM32_OTGHS_HCINTMSK9_OFFSET) +#define STM32_OTGHS_HCINTMSK10 (STM32_OTGHS_BASE+STM32_OTGHS_HCINTMSK10_OFFSET) +#define STM32_OTGHS_HCINTMSK11 (STM32_OTGHS_BASE+STM32_OTGHS_HCINTMSK11_OFFSET) + +#define STM32_OTGHS_HCTSIZ(n) (STM32_OTGHS_BASE+STM32_OTGHS_HCTSIZ_OFFSET(n)) +#define STM32_OTGHS_HCTSIZ0 (STM32_OTGHS_BASE+STM32_OTGHS_HCTSIZ0_OFFSET) +#define STM32_OTGHS_HCTSIZ1 (STM32_OTGHS_BASE+STM32_OTGHS_HCTSIZ1_OFFSET) +#define STM32_OTGHS_HCTSIZ2 (STM32_OTGHS_BASE+STM32_OTGHS_HCTSIZ2_OFFSET) +#define STM32_OTGHS_HCTSIZ3 (STM32_OTGHS_BASE+STM32_OTGHS_HCTSIZ3_OFFSET) +#define STM32_OTGHS_HCTSIZ4 (STM32_OTGHS_BASE+STM32_OTGHS_HCTSIZ4_OFFSET) +#define STM32_OTGHS_HCTSIZ5 (STM32_OTGHS_BASE+STM32_OTGHS_HCTSIZ5_OFFSET) +#define STM32_OTGHS_HCTSIZ6 (STM32_OTGHS_BASE+STM32_OTGHS_HCTSIZ6_OFFSET) +#define STM32_OTGHS_HCTSIZ7 (STM32_OTGHS_BASE+STM32_OTGHS_HCTSIZ7_OFFSET) +#define STM32_OTGHS_HCTSIZ8 (STM32_OTGHS_BASE+STM32_OTGHS_HCTSIZ8_OFFSET) +#define STM32_OTGHS_HCTSIZ9 (STM32_OTGHS_BASE+STM32_OTGHS_HCTSIZ9_OFFSET) +#define STM32_OTGHS_HCTSIZ10 (STM32_OTGHS_BASE+STM32_OTGHS_HCTSIZ10_OFFSET) +#define STM32_OTGHS_HCTSIZ11 (STM32_OTGHS_BASE+STM32_OTGHS_HCTSIZ11_OFFSET) + +/* Device-mode control and status registers */ + +#define STM32_OTGHS_DCFG (STM32_OTGHS_BASE+STM32_OTGHS_DCFG_OFFSET) +#define STM32_OTGHS_DCTL (STM32_OTGHS_BASE+STM32_OTGHS_DCTL_OFFSET) +#define STM32_OTGHS_DSTS (STM32_OTGHS_BASE+STM32_OTGHS_DSTS_OFFSET) +#define STM32_OTGHS_DIEPMSK (STM32_OTGHS_BASE+STM32_OTGHS_DIEPMSK_OFFSET) +#define STM32_OTGHS_DOEPMSK (STM32_OTGHS_BASE+STM32_OTGHS_DOEPMSK_OFFSET) +#define STM32_OTGHS_DAINT (STM32_OTGHS_BASE+STM32_OTGHS_DAINT_OFFSET) +#define STM32_OTGHS_DAINTMSK (STM32_OTGHS_BASE+STM32_OTGHS_DAINTMSK_OFFSET) +#define STM32_OTGHS_DVBUSDIS (STM32_OTGHS_BASE+STM32_OTGHS_DVBUSDIS_OFFSET) +#define STM32_OTGHS_DVBUSPULSE (STM32_OTGHS_BASE+STM32_OTGHS_DVBUSPULSE_OFFSET) +#define STM32_OTGHS_DIEPEMPMSK (STM32_OTGHS_BASE+STM32_OTGHS_DIEPEMPMSK_OFFSET) + +#define STM32_OTGHS_DIEP(n) (STM32_OTGHS_BASE+STM32_OTGHS_DIEP_OFFSET(n)) + +#define STM32_OTGHS_DIEPCTL(n) (STM32_OTGHS_BASE+STM32_OTGHS_DIEPCTL_OFFSET(n)) +#define STM32_OTGHS_DIEPCTL0 (STM32_OTGHS_BASE+STM32_OTGHS_DIEPCTL0_OFFSET) +#define STM32_OTGHS_DIEPCTL1 (STM32_OTGHS_BASE+STM32_OTGHS_DIEPCTL1_OFFSET) +#define STM32_OTGHS_DIEPCTL2 (STM32_OTGHS_BASE+STM32_OTGHS_DIEPCTL2_OFFSET) +#define STM32_OTGHS_DIEPCTL3 (STM32_OTGHS_BASE+STM32_OTGHS_DIEPCTL3_OFFSET) + +#define STM32_OTGHS_DIEPINT(n) (STM32_OTGHS_BASE+STM32_OTGHS_DIEPINT_OFFSET(n)) +#define STM32_OTGHS_DIEPINT0 (STM32_OTGHS_BASE+STM32_OTGHS_DIEPINT0_OFFSET) +#define STM32_OTGHS_DIEPINT1 (STM32_OTGHS_BASE+STM32_OTGHS_DIEPINT1_OFFSET) +#define STM32_OTGHS_DIEPINT2 (STM32_OTGHS_BASE+STM32_OTGHS_DIEPINT2_OFFSET) +#define STM32_OTGHS_DIEPINT3 (STM32_OTGHS_BASE+STM32_OTGHS_DIEPINT3_OFFSET) + +#define STM32_OTGHS_DIEPTSIZ(n) (STM32_OTGHS_BASE+STM32_OTGHS_DIEPTSIZ_OFFSET(n)) +#define STM32_OTGHS_DIEPTSIZ0 (STM32_OTGHS_BASE+STM32_OTGHS_DIEPTSIZ0_OFFSET) +#define STM32_OTGHS_DIEPTSIZ1 (STM32_OTGHS_BASE+STM32_OTGHS_DIEPTSIZ1_OFFSET) +#define STM32_OTGHS_DIEPTSIZ2 (STM32_OTGHS_BASE+STM32_OTGHS_DIEPTSIZ2_OFFSET) +#define STM32_OTGHS_DIEPTSIZ3 (STM32_OTGHS_BASE+STM32_OTGHS_DIEPTSIZ3_OFFSET) + +#define STM32_OTGHS_DTXFSTS(n) (STM32_OTGHS_BASE+STM32_OTGHS_DTXFSTS_OFFSET(n)) +#define STM32_OTGHS_DTXFSTS0 (STM32_OTGHS_BASE+STM32_OTGHS_DTXFSTS0_OFFSET) +#define STM32_OTGHS_DTXFSTS1 (STM32_OTGHS_BASE+STM32_OTGHS_DTXFSTS1_OFFSET) +#define STM32_OTGHS_DTXFSTS2 (STM32_OTGHS_BASE+STM32_OTGHS_DTXFSTS2_OFFSET) +#define STM32_OTGHS_DTXFSTS3 (STM32_OTGHS_BASE+STM32_OTGHS_DTXFSTS3_OFFSET) + +#define STM32_OTGHS_DOEP(n) (STM32_OTGHS_BASE+STM32_OTGHS_DOEP_OFFSET(n)) + +#define STM32_OTGHS_DOEPCTL(n) (STM32_OTGHS_BASE+STM32_OTGHS_DOEPCTL_OFFSET(n)) +#define STM32_OTGHS_DOEPCTL0 (STM32_OTGHS_BASE+STM32_OTGHS_DOEPCTL0_OFFSET) +#define STM32_OTGHS_DOEPCTL1 (STM32_OTGHS_BASE+STM32_OTGHS_DOEPCTL1_OFFSET) +#define STM32_OTGHS_DOEPCTL2 (STM32_OTGHS_BASE+STM32_OTGHS_DOEPCTL2_OFFSET) +#define STM32_OTGHS_DOEPCTL3 (STM32_OTGHS_BASE+STM32_OTGHS_DOEPCTL3_OFFSET) + +#define STM32_OTGHS_DOEPINT(n) (STM32_OTGHS_BASE+STM32_OTGHS_DOEPINT_OFFSET(n)) +#define STM32_OTGHS_DOEPINT0 (STM32_OTGHS_BASE+STM32_OTGHS_DOEPINT0_OFFSET) +#define STM32_OTGHS_DOEPINT1 (STM32_OTGHS_BASE+STM32_OTGHS_DOEPINT1_OFFSET) +#define STM32_OTGHS_DOEPINT2 (STM32_OTGHS_BASE+STM32_OTGHS_DOEPINT2_OFFSET) +#define STM32_OTGHS_DOEPINT3 (STM32_OTGHS_BASE+STM32_OTGHS_DOEPINT3_OFFSET) + +#define STM32_OTGHS_DOEPTSIZ(n) (STM32_OTGHS_BASE+STM32_OTGHS_DOEPTSIZ_OFFSET(n)) +#define STM32_OTGHS_DOEPTSIZ0 (STM32_OTGHS_BASE+STM32_OTGHS_DOEPTSIZ0_OFFSET) +#define STM32_OTGHS_DOEPTSIZ1 (STM32_OTGHS_BASE+STM32_OTGHS_DOEPTSIZ1_OFFSET) +#define STM32_OTGHS_DOEPTSIZ2 (STM32_OTGHS_BASE+STM32_OTGHS_DOEPTSIZ2_OFFSET) +#define STM32_OTGHS_DOEPTSIZ3 (STM32_OTGHS_BASE+STM32_OTGHS_DOEPTSIZ3_OFFSET) + +/* Power and clock gating registers */ + +#define STM32_OTGHS_PCGCCTL (STM32_OTGHS_BASE+STM32_OTGHS_PCGCCTL_OFFSET) + +/* Data FIFO (DFIFO) access registers */ + +#define STM32_OTGHS_DFIFO_DEP(n) (STM32_OTGHS_BASE+STM32_OTGHS_DFIFO_DEP_OFFSET(n)) +#define STM32_OTGHS_DFIFO_HCH(n) (STM32_OTGHS_BASE+STM32_OTGHS_DFIFO_HCH_OFFSET(n)) + +#define STM32_OTGHS_DFIFO_DEP0 (STM32_OTGHS_BASE+STM32_OTGHS_DFIFO_DEP0_OFFSET) +#define STM32_OTGHS_DFIFO_HCH0 (STM32_OTGHS_BASE+STM32_OTGHS_DFIFO_HCH0_OFFSET) + +#define STM32_OTGHS_DFIFO_DEP1 (STM32_OTGHS_BASE+STM32_OTGHS_DFIFO_DEP1_OFFSET) +#define STM32_OTGHS_DFIFO_HCH1 (STM32_OTGHS_BASE+STM32_OTGHS_DFIFO_HCH1_OFFSET) + +#define STM32_OTGHS_DFIFO_DEP2 (STM32_OTGHS_BASE+STM32_OTGHS_DFIFO_DEP2_OFFSET) +#define STM32_OTGHS_DFIFO_HCH2 (STM32_OTGHS_BASE+STM32_OTGHS_DFIFO_HCH2_OFFSET) + +#define STM32_OTGHS_DFIFO_DEP3 (STM32_OTGHS_BASE+STM32_OTGHS_DFIFO_DEP3_OFFSET) +#define STM32_OTGHS_DFIFO_HCH3 (STM32_OTGHS_BASE+STM32_OTGHS_DFIFO_HCH3_OFFSET) + +/* Register Bitfield Definitions ********************************************************************/ +/* Core global control and status registers */ + +/* Control and status register */ + +#define OTGHS_GOTGCTL_SRQSCS (1 << 0) /* Bit 0: Session request success */ +#define OTGHS_GOTGCTL_SRQ (1 << 1) /* Bit 1: Session request */ + /* Bits 2-72 Reserved, must be kept at reset value */ +#define OTGHS_GOTGCTL_HNGSCS (1 << 8) /* Bit 8: Host negotiation success */ +#define OTGHS_GOTGCTL_HNPRQ (1 << 9) /* Bit 9: HNP request */ +#define OTGHS_GOTGCTL_HSHNPEN (1 << 10) /* Bit 10: host set HNP enable */ +#define OTGHS_GOTGCTL_DHNPEN (1 << 11) /* Bit 11: Device HNP enabled */ + /* Bits 12-15: Reserved, must be kept at reset value */ +#define OTGHS_GOTGCTL_CIDSTS (1 << 16) /* Bit 16: Connector ID status */ +#define OTGHS_GOTGCTL_DBCT (1 << 17) /* Bit 17: Long/short debounce time */ +#define OTGHS_GOTGCTL_ASVLD (1 << 18) /* Bit 18: A-session valid */ +#define OTGHS_GOTGCTL_BSVLD (1 << 19) /* Bit 19: B-session valid */ + /* Bits 20-31: Reserved, must be kept at reset value */ +/* Interrupt register */ + /* Bits 1:0 Reserved, must be kept at reset value */ +#define OTGHS_GOTGINT_SEDET (1 << 2) /* Bit 2: Session end detected */ + /* Bits 3-7: Reserved, must be kept at reset value */ +#define OTGHS_GOTGINT_SRSSCHG (1 << 8) /* Bit 8: Session request success status change */ +#define OTGHS_GOTGINT_HNSSCHG (1 << 9) /* Bit 9: Host negotiation success status change */ + /* Bits 16:10 Reserved, must be kept at reset value */ +#define OTGHS_GOTGINT_HNGDET (1 << 17) /* Bit 17: Host negotiation detected */ +#define OTGHS_GOTGINT_ADTOCHG (1 << 18) /* Bit 18: A-device timeout change */ +#define OTGHS_GOTGINT_DBCDNE (1 << 19) /* Bit 19: Debounce done */ + /* Bits 2-31: Reserved, must be kept at reset value */ + +/* AHB configuration register */ + +#define OTGHS_GAHBCFG_GINTMSK (1 << 0) /* Bit 0: Global interrupt mask */ + /* Bits 1-6: Reserved, must be kept at reset value */ +#define OTGHS_GAHBCFG_TXFELVL (1 << 7) /* Bit 7: TxFIFO empty level */ +#define OTGHS_GAHBCFG_PTXFELVL (1 << 8) /* Bit 8: Periodic TxFIFO empty level */ + /* Bits 20-31: Reserved, must be kept at reset value */ +/* USB configuration register */ + +#define OTGHS_GUSBCFG_TOCAL_SHIFT (0) /* Bits 0-2: FS timeout calibration */ +#define OTGHS_GUSBCFG_TOCAL_MASK (7 << OTGHS_GUSBCFG_TOCAL_SHIFT) + /* Bits 3-5: Reserved, must be kept at reset value */ +#define OTGHS_GUSBCFG_PHYSEL (1 << 6) /* Bit 6: Full Speed serial transceiver select */ + /* Bit 7: Reserved, must be kept at reset value */ +#define OTGHS_GUSBCFG_SRPCAP (1 << 8) /* Bit 8: SRP-capable */ +#define OTGHS_GUSBCFG_HNPCAP (1 << 9) /* Bit 9: HNP-capable */ +#define OTGHS_GUSBCFG_TRDT_SHIFT (10) /* Bits 10-13: USB turnaround time */ +#define OTGHS_GUSBCFG_TRDT_MASK (15 << OTGHS_GUSBCFG_TRDT_SHIFT) +# define OTGHS_GUSBCFG_TRDT(n) ((n) << OTGHS_GUSBCFG_TRDT_SHIFT) + /* Bits 14-28: Reserved, must be kept at reset value */ +#define OTGHS_GUSBCFG_FHMOD (1 << 29) /* Bit 29: Force host mode */ +#define OTGHS_GUSBCFG_FDMOD (1 << 30) /* Bit 30: Force device mode */ +#define OTGHS_GUSBCFG_CTXPKT (1 << 31) /* Bit 31: Corrupt Tx packet */ + /* Bits 20-31: Reserved, must be kept at reset value */ +/* Reset register */ + +#define OTGHS_GRSTCTL_CSRST (1 << 0) /* Bit 0: Core soft reset */ +#define OTGHS_GRSTCTL_HSRST (1 << 1) /* Bit 1: HCLK soft reset */ +#define OTGHS_GRSTCTL_FCRST (1 << 2) /* Bit 2: Host frame counter reset */ + /* Bit 3 Reserved, must be kept at reset value */ +#define OTGHS_GRSTCTL_RXFFLSH (1 << 4) /* Bit 4: RxFIFO flush */ +#define OTGHS_GRSTCTL_TXFFLSH (1 << 5) /* Bit 5: TxFIFO flush */ +#define OTGHS_GRSTCTL_TXFNUM_SHIFT (10) /* Bits 6-10: TxFIFO number */ +#define OTGHS_GRSTCTL_TXFNUM_MASK (31 << OTGHS_GRSTCTL_TXFNUM_SHIFT) +# define OTGHS_GRSTCTL_TXFNUM_HNONPER (0 << OTGHS_GRSTCTL_TXFNUM_SHIFT) /* Non-periodic TxFIFO flush in host mode */ +# define OTGHS_GRSTCTL_TXFNUM_HPER (1 << OTGHS_GRSTCTL_TXFNUM_SHIFT) /* Periodic TxFIFO flush in host mode */ +# define OTGHS_GRSTCTL_TXFNUM_HALL (16 << OTGHS_GRSTCTL_TXFNUM_SHIFT) /* Flush all the transmit FIFOs in host mode.*/ +# define OTGHS_GRSTCTL_TXFNUM_D(n) ((n) << OTGHS_GRSTCTL_TXFNUM_SHIFT) /* TXFIFO n flush in device mode, n=0-15 */ +# define OTGHS_GRSTCTL_TXFNUM_DALL (16 << OTGHS_GRSTCTL_TXFNUM_SHIFT) /* Flush all the transmit FIFOs in device mode.*/ + /* Bits 11-31: Reserved, must be kept at reset value */ +#define OTGHS_GRSTCTL_AHBIDL (1 << 31) /* Bit 31: AHB master idle */ + +/* Core interrupt and Interrupt mask registers */ + +#define OTGHS_GINTSTS_CMOD (1 << 0) /* Bit 0: Current mode of operation */ +# define OTGHS_GINTSTS_DEVMODE (0) +# define OTGHS_GINTSTS_HOSTMODE (OTGHS_GINTSTS_CMOD) +#define OTGHS_GINT_MMIS (1 << 1) /* Bit 1: Mode mismatch interrupt */ +#define OTGHS_GINT_OTG (1 << 2) /* Bit 2: OTG interrupt */ +#define OTGHS_GINT_SOF (1 << 3) /* Bit 3: Start of frame */ +#define OTGHS_GINT_RXFLVL (1 << 4) /* Bit 4: RxFIFO non-empty */ +#define OTGHS_GINT_NPTXFE (1 << 5) /* Bit 5: Non-periodic TxFIFO empty */ +#define OTGHS_GINT_GINAKEFF (1 << 6) /* Bit 6: Global IN non-periodic NAK effective */ +#define OTGHS_GINT_GONAKEFF (1 << 7) /* Bit 7: Global OUT NAK effective */ + /* Bits 8-9: Reserved, must be kept at reset value */ +#define OTGHS_GINT_ESUSP (1 << 10) /* Bit 10: Early suspend */ +#define OTGHS_GINT_USBSUSP (1 << 11) /* Bit 11: USB suspend */ +#define OTGHS_GINT_USBRST (1 << 12) /* Bit 12: USB reset */ +#define OTGHS_GINT_ENUMDNE (1 << 13) /* Bit 13: Enumeration done */ +#define OTGHS_GINT_ISOODRP (1 << 14) /* Bit 14: Isochronous OUT packet dropped interrupt */ +#define OTGHS_GINT_EOPF (1 << 15) /* Bit 15: End of periodic frame interrupt */ + /* Bits 16 Reserved, must be kept at reset value */ +#define OTGHS_GINTMSK_EPMISM (1 << 17) /* Bit 17: Endpoint mismatch interrupt mask */ +#define OTGHS_GINT_IEP (1 << 18) /* Bit 18: IN endpoint interrupt */ +#define OTGHS_GINT_OEP (1 << 19) /* Bit 19: OUT endpoint interrupt */ +#define OTGHS_GINT_IISOIXFR (1 << 20) /* Bit 20: Incomplete isochronous IN transfer */ +#define OTGHS_GINT_IISOOXFR (1 << 21) /* Bit 21: Incomplete isochronous OUT transfer */ +#define OTGHS_GINT_IPXFR (1 << 21) /* Bit 21: Incomplete periodic transfer (host) */ + /* Bits 22-23: Reserved, must be kept at reset value */ +#define OTGHS_GINT_HPRT (1 << 24) /* Bit 24: Host port interrupt */ +#define OTGHS_GINT_HC (1 << 25) /* Bit 25: Host channels interrupt */ +#define OTGHS_GINT_PTXFE (1 << 26) /* Bit 26: Periodic TxFIFO empty */ + /* Bit 27 Reserved, must be kept at reset value */ +#define OTGHS_GINT_CIDSCHG (1 << 28) /* Bit 28: Connector ID status change */ +#define OTGHS_GINT_DISC (1 << 29) /* Bit 29: Disconnect detected interrupt */ +#define OTGHS_GINT_SRQ (1 << 30) /* Bit 30: Session request/new session detected interrupt */ +#define OTGHS_GINT_WKUP (1 << 31) /* Bit 31: Resume/remote wakeup detected interrupt */ + +/* Receive status debug read/OTG status read and pop registers (host mode) */ + +#define OTGHS_GRXSTSH_CHNUM_SHIFT (0) /* Bits 0-3: Channel number */ +#define OTGHS_GRXSTSH_CHNUM_MASK (15 << OTGHS_GRXSTSH_CHNUM_SHIFT) +#define OTGHS_GRXSTSH_BCNT_SHIFT (4) /* Bits 4-14: Byte count */ +#define OTGHS_GRXSTSH_BCNT_MASK (0x7ff << OTGHS_GRXSTSH_BCNT_SHIFT) +#define OTGHS_GRXSTSH_DPID_SHIFT (15) /* Bits 15-16: Data PID */ +#define OTGHS_GRXSTSH_DPID_MASK (3 << OTGHS_GRXSTSH_DPID_SHIFT) +# define OTGHS_GRXSTSH_DPID_DATA0 (0 << OTGHS_GRXSTSH_DPID_SHIFT) +# define OTGHS_GRXSTSH_DPID_DATA2 (1 << OTGHS_GRXSTSH_DPID_SHIFT) +# define OTGHS_GRXSTSH_DPID_DATA1 (2 << OTGHS_GRXSTSH_DPID_SHIFT) +# define OTGHS_GRXSTSH_DPID_MDATA (3 << OTGHS_GRXSTSH_DPID_SHIFT) +#define OTGHS_GRXSTSH_PKTSTS_SHIFT (17) /* Bits 17-20: Packet status */ +#define OTGHS_GRXSTSH_PKTSTS_MASK (15 << OTGHS_GRXSTSH_PKTSTS_SHIFT) +# define OTGHS_GRXSTSH_PKTSTS_INRECVD (2 << OTGHS_GRXSTSH_PKTSTS_SHIFT) /* IN data packet received */ +# define OTGHS_GRXSTSH_PKTSTS_INDONE (3 << OTGHS_GRXSTSH_PKTSTS_SHIFT) /* IN transfer completed */ +# define OTGHS_GRXSTSH_PKTSTS_DTOGERR (5 << OTGHS_GRXSTSH_PKTSTS_SHIFT) /* Data toggle error */ +# define OTGHS_GRXSTSH_PKTSTS_HALTED (7 << OTGHS_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 OTGHS_GRXSTSD_EPNUM_SHIFT (0) /* Bits 0-3: Endpoint number */ +#define OTGHS_GRXSTSD_EPNUM_MASK (15 << OTGHS_GRXSTSD_EPNUM_SHIFT) +#define OTGHS_GRXSTSD_BCNT_SHIFT (4) /* Bits 4-14: Byte count */ +#define OTGHS_GRXSTSD_BCNT_MASK (0x7ff << OTGHS_GRXSTSD_BCNT_SHIFT) +#define OTGHS_GRXSTSD_DPID_SHIFT (15) /* Bits 15-16: Data PID */ +#define OTGHS_GRXSTSD_DPID_MASK (3 << OTGHS_GRXSTSD_DPID_SHIFT) +# define OTGHS_GRXSTSD_DPID_DATA0 (0 << OTGHS_GRXSTSD_DPID_SHIFT) +# define OTGHS_GRXSTSD_DPID_DATA2 (1 << OTGHS_GRXSTSD_DPID_SHIFT) +# define OTGHS_GRXSTSD_DPID_DATA1 (2 << OTGHS_GRXSTSD_DPID_SHIFT) +# define OTGHS_GRXSTSD_DPID_MDATA (3 << OTGHS_GRXSTSD_DPID_SHIFT) +#define OTGHS_GRXSTSD_PKTSTS_SHIFT (17) /* Bits 17-20: Packet status */ +#define OTGHS_GRXSTSD_PKTSTS_MASK (15 << OTGHS_GRXSTSD_PKTSTS_SHIFT) +# define OTGHS_GRXSTSD_PKTSTS_OUTNAK (1 << OTGHS_GRXSTSD_PKTSTS_SHIFT) /* Global OUT NAK */ +# define OTGHS_GRXSTSD_PKTSTS_OUTRECVD (2 << OTGHS_GRXSTSD_PKTSTS_SHIFT) /* OUT data packet received */ +# define OTGHS_GRXSTSD_PKTSTS_OUTDONE (3 << OTGHS_GRXSTSD_PKTSTS_SHIFT) /* OUT transfer completed */ +# define OTGHS_GRXSTSD_PKTSTS_SETUPDONE (4 << OTGHS_GRXSTSD_PKTSTS_SHIFT) /* SETUP transaction completed */ +# define OTGHS_GRXSTSD_PKTSTS_SETUPRECVD (6 << OTGHS_GRXSTSD_PKTSTS_SHIFT) /* SETUP data packet received */ +#define OTGHS_GRXSTSD_FRMNUM_SHIFT (21) /* Bits 21-24: Frame number */ +#define OTGHS_GRXSTSD_FRMNUM_MASK (15 << OTGHS_GRXSTSD_FRMNUM_SHIFT) + /* Bits 25-31: Reserved, must be kept at reset value */ +/* Receive FIFO size register */ + +#define OTGHS_GRXFSIZ_MASK (0xffff) + +/* Host non-periodic transmit FIFO size register */ + +#define OTGHS_HNPTXFSIZ_NPTXFSA_SHIFT (0) /* Bits 0-15: Non-periodic transmit RAM start address */ +#define OTGHS_HNPTXFSIZ_NPTXFSA_MASK (0xffff << OTGHS_HNPTXFSIZ_NPTXFSA_SHIFT) +#define OTGHS_HNPTXFSIZ_NPTXFD_SHIFT (16) /* Bits 16-31: Non-periodic TxFIFO depth */ +#define OTGHS_HNPTXFSIZ_NPTXFD_MASK (0xffff << OTGHS_HNPTXFSIZ_NPTXFD_SHIFT) +# define OTGHS_HNPTXFSIZ_NPTXFD_MIN (16 << OTGHS_HNPTXFSIZ_NPTXFD_SHIFT) +# define OTGHS_HNPTXFSIZ_NPTXFD_MAX (256 << OTGHS_HNPTXFSIZ_NPTXFD_SHIFT) + +/* Endpoint 0 Transmit FIFO size */ + +#define OTGHS_DIEPTXF0_TX0FD_SHIFT (0) /* Bits 0-15: Endpoint 0 transmit RAM start address */ +#define OTGHS_DIEPTXF0_TX0FD_MASK (0xffff << OTGHS_DIEPTXF0_TX0FD_SHIFT) +#define OTGHS_DIEPTXF0_TX0FSA_SHIFT (16) /* Bits 16-31: Endpoint 0 TxFIFO depth */ +#define OTGHS_DIEPTXF0_TX0FSA_MASK (0xffff << OTGHS_DIEPTXF0_TX0FSA_SHIFT) +# define OTGHS_DIEPTXF0_TX0FSA_MIN (16 << OTGHS_DIEPTXF0_TX0FSA_SHIFT) +# define OTGHS_DIEPTXF0_TX0FSA_MAX (256 << OTGHS_DIEPTXF0_TX0FSA_SHIFT) + +/* Non-periodic transmit FIFO/queue status register */ + +#define OTGHS_HNPTXSTS_NPTXFSAV_SHIFT (0) /* Bits 0-15: Non-periodic TxFIFO space available */ +#define OTGHS_HNPTXSTS_NPTXFSAV_MASK (0xffff << OTGHS_HNPTXSTS_NPTXFSAV_SHIFT) +# define OTGHS_HNPTXSTS_NPTXFSAV_FULL (0 << OTGHS_HNPTXSTS_NPTXFSAV_SHIFT) +#define OTGHS_HNPTXSTS_NPTQXSAV_SHIFT (16) /* Bits 16-23: Non-periodic transmit request queue space available */ +#define OTGHS_HNPTXSTS_NPTQXSAV_MASK (0xff << OTGHS_HNPTXSTS_NPTQXSAV_SHIFT) +# define OTGHS_HNPTXSTS_NPTQXSAV_FULL (0 << OTGHS_HNPTXSTS_NPTQXSAV_SHIFT) +#define OTGHS_HNPTXSTS_NPTXQTOP_SHIFT (24) /* Bits 24-30: Top of the non-periodic transmit request queue */ +#define OTGHS_HNPTXSTS_NPTXQTOP_MASK (0x7f << OTGHS_HNPTXSTS_NPTXQTOP_SHIFT) +# define OTGHS_HNPTXSTS_TERMINATE (1 << 24) /* Bit 24: Terminate (last entry for selected channel/endpoint) */ +# define OTGHS_HNPTXSTS_TYPE_SHIFT (25) /* Bits 25-26: Status */ +# define OTGHS_HNPTXSTS_TYPE_MASK (3 << OTGHS_HNPTXSTS_TYPE_SHIFT) +# define OTGHS_HNPTXSTS_TYPE_INOUT (0 << OTGHS_HNPTXSTS_TYPE_SHIFT) /* IN/OUT token */ +# define OTGHS_HNPTXSTS_TYPE_ZLP (1 << OTGHS_HNPTXSTS_TYPE_SHIFT) /* Zero-length transmit packet (device IN/host OUT) */ +# define OTGHS_HNPTXSTS_TYPE_HALT (3 << OTGHS_HNPTXSTS_TYPE_SHIFT) /* Channel halt command */ +# define OTGHS_HNPTXSTS_CHNUM_SHIFT (27) /* Bits 27-30: Channel number */ +# define OTGHS_HNPTXSTS_CHNUM_MASK (15 << OTGHS_HNPTXSTS_CHNUM_SHIFT) +# define OTGHS_HNPTXSTS_EPNUM_SHIFT (27) /* Bits 27-30: Endpoint number */ +# define OTGHS_HNPTXSTS_EPNUM_MASK (15 << OTGHS_HNPTXSTS_EPNUM_SHIFT) + /* Bit 31 Reserved, must be kept at reset value */ +/* General core configuration register */ + /* Bits 15:0 Reserved, must be kept at reset value */ +#define OTGHS_GCCFG_PWRDWN (1 << 16) /* Bit 16: Power down */ + /* Bit 17 Reserved, must be kept at reset value */ +#define OTGHS_GCCFG_VBUSASEN (1 << 18) /* Bit 18: Enable the VBUS sensing “A” device */ +#define OTGHS_GCCFG_VBUSBSEN (1 << 19) /* Bit 19: Enable the VBUS sensing “B” device */ +#define OTGHS_GCCFG_SOFOUTEN (1 << 20) /* Bit 20: SOF output enable */ +#define OTGHS_GCCFG_NOVBUSSENS (1 << 21) /* Bit 21: VBUS sensing disable option */ + /* Bits 31:22 Reserved, must be kept at reset value */ +/* Core ID register (32-bit product ID) */ + +/* Host periodic transmit FIFO size register */ + +#define OTGHS_HPTXFSIZ_PTXSA_SHIFT (0) /* Bits 0-15: Host periodic TxFIFO start address */ +#define OTGHS_HPTXFSIZ_PTXSA_MASK (0xffff << OTGHS_HPTXFSIZ_PTXSA_SHIFT) +#define OTGHS_HPTXFSIZ_PTXFD_SHIFT (16) /* Bits 16-31: Host periodic TxFIFO depth */ +#define OTGHS_HPTXFSIZ_PTXFD_MASK (0xffff << OTGHS_HPTXFSIZ_PTXFD_SHIFT) + +/* Device IN endpoint transmit FIFOn size register */ + +#define OTGHS_DIEPTXF_INEPTXSA_SHIFT (0) /* Bits 0-15: IN endpoint FIFOx transmit RAM start address */ +#define OTGHS_DIEPTXF_INEPTXSA_MASK (0xffff << OTGHS_DIEPTXF_INEPTXSA_SHIFT) +#define OTGHS_DIEPTXF_INEPTXFD_SHIFT (16) /* Bits 16-31: IN endpoint TxFIFO depth */ +#define OTGHS_DIEPTXF_INEPTXFD_MASK (0xffff << OTGHS_DIEPTXF_INEPTXFD_SHIFT) +# define OTGHS_DIEPTXF_INEPTXFD_MIN (16 << OTGHS_DIEPTXF_INEPTXFD_MASK) + +/* Host-mode control and status registers */ + +/* Host configuration register */ + +#define OTGHS_HCFG_FSLSPCS_SHIFT (0) /* Bits 0-1: FS/LS PHY clock select */ +#define OTGHS_HCFG_FSLSPCS_MASK (3 << OTGHS_HCFG_FSLSPCS_SHIFT) +# define OTGHS_HCFG_FSLSPCS_FS48MHz (1 << OTGHS_HCFG_FSLSPCS_SHIFT) /* FS host mode, PHY clock is running at 48 MHz */ +# define OTGHS_HCFG_FSLSPCS_LS48MHz (1 << OTGHS_HCFG_FSLSPCS_SHIFT) /* LS host mode, Select 48 MHz PHY clock frequency */ +# define OTGHS_HCFG_FSLSPCS_LS6MHz (2 << OTGHS_HCFG_FSLSPCS_SHIFT) /* LS host mode, Select 6 MHz PHY clock frequency */ +#define OTGHS_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 OTGHS_HFIR_MASK (0xffff) + +/* Host frame number/frame time remaining register */ + +#define OTGHS_HFNUM_FRNUM_SHIFT (0) /* Bits 0-15: Frame number */ +#define OTGHS_HFNUM_FRNUM_MASK (0xffff << OTGHS_HFNUM_FRNUM_SHIFT) +#define OTGHS_HFNUM_FTREM_SHIFT (16) /* Bits 16-31: Frame time remaining */ +#define OTGHS_HFNUM_FTREM_MASK (0xffff << OTGHS_HFNUM_FTREM_SHIFT) + +/* Host periodic transmit FIFO/queue status register */ + +#define OTGHS_HPTXSTS_PTXFSAVL_SHIFT (0) /* Bits 0-15: Periodic transmit data FIFO space available */ +#define OTGHS_HPTXSTS_PTXFSAVL_MASK (0xffff << OTGHS_HPTXSTS_PTXFSAVL_SHIFT) +# define OTGHS_HPTXSTS_PTXFSAVL_FULL (0 << OTGHS_HPTXSTS_PTXFSAVL_SHIFT) +#define OTGHS_HPTXSTS_PTXQSAV_SHIFT (16) /* Bits 16-23: Periodic transmit request queue space available */ +#define OTGHS_HPTXSTS_PTXQSAV_MASK (0xff << OTGHS_HPTXSTS_PTXQSAV_SHIFT) +# define OTGHS_HPTXSTS_PTXQSAV_FULL (0 << OTGHS_HPTXSTS_PTXQSAV_SHIFT) +#define OTGHS_HPTXSTS_PTXQTOP_SHIFT (24) /* Bits 24-31: Top of the periodic transmit request queue */ +#define OTGHS_HPTXSTS_PTXQTOP_MASK (0x7f << OTGHS_HPTXSTS_PTXQTOP_SHIFT) +# define OTGHS_HPTXSTS_TERMINATE (1 << 24) /* Bit 24: Terminate (last entry for selected channel/endpoint) */ +# define OTGHS_HPTXSTS_TYPE_SHIFT (25) /* Bits 25-26: Type */ +# define OTGHS_HPTXSTS_TYPE_MASK (3 << OTGHS_HPTXSTS_TYPE_SHIFT) +# define OTGHS_HPTXSTS_TYPE_INOUT (0 << OTGHS_HPTXSTS_TYPE_SHIFT) /* IN/OUT token */ +# define OTGHS_HPTXSTS_TYPE_ZLP (1 << OTGHS_HPTXSTS_TYPE_SHIFT) /* Zero-length transmit packet */ +# define OTGHS_HPTXSTS_TYPE_HALT (3 << OTGHS_HPTXSTS_TYPE_SHIFT) /* Disable channel command */ +# define OTGHS_HPTXSTS_EPNUM_SHIFT (27) /* Bits 27-30: Endpoint number */ +# define OTGHS_HPTXSTS_EPNUM_MASK (15 << OTGHS_HPTXSTS_EPNUM_SHIFT) +# define OTGHS_HPTXSTS_CHNUM_SHIFT (27) /* Bits 27-30: Channel number */ +# define OTGHS_HPTXSTS_CHNUM_MASK (15 << OTGHS_HPTXSTS_CHNUM_SHIFT) +# define OTGHS_HPTXSTS_ODD (1 << 24) /* Bit 31: Send in odd (vs even) frame */ + +/* Host all channels interrupt and all channels interrupt mask registers */ + +#define OTGHS_HAINT(n) (1 << (n)) /* Bits 15:0 HAINTM: Channel interrupt */ + +/* Host port control and status register */ + +#define OTGHS_HPRT_PCSTS (1 << 0) /* Bit 0: Port connect status */ +#define OTGHS_HPRT_PCDET (1 << 1) /* Bit 1: Port connect detected */ +#define OTGHS_HPRT_PENA (1 << 2) /* Bit 2: Port enable */ +#define OTGHS_HPRT_PENCHNG (1 << 3) /* Bit 3: Port enable/disable change */ +#define OTGHS_HPRT_POCA (1 << 4) /* Bit 4: Port overcurrent active */ +#define OTGHS_HPRT_POCCHNG (1 << 5) /* Bit 5: Port overcurrent change */ +#define OTGHS_HPRT_PRES (1 << 6) /* Bit 6: Port resume */ +#define OTGHS_HPRT_PSUSP (1 << 7) /* Bit 7: Port suspend */ +#define OTGHS_HPRT_PRST (1 << 8) /* Bit 8: Port reset */ + /* Bit 9: Reserved, must be kept at reset value */ +#define OTGHS_HPRT_PLSTS_SHIFT (10) /* Bits 10-11: Port line status */ +#define OTGHS_HPRT_PLSTS_MASK (3 << OTGHS_HPRT_PLSTS_SHIFT) +# define OTGHS_HPRT_PLSTS_DP (1 << 10) /* Bit 10: Logic level of OTG_FS_FS_DP */ +# define OTGHS_HPRT_PLSTS_DM (1 << 11) /* Bit 11: Logic level of OTG_FS_FS_DM */ +#define OTGHS_HPRT_PPWR (1 << 12) /* Bit 12: Port power */ +#define OTGHS_HPRT_PTCTL_SHIFT (13) /* Bits 13-16: Port test control */ +#define OTGHS_HPRT_PTCTL_MASK (15 << OTGHS_HPRT_PTCTL_SHIFT) +# define OTGHS_HPRT_PTCTL_DISABLED (0 << OTGHS_HPRT_PTCTL_SHIFT) /* Test mode disabled */ +# define OTGHS_HPRT_PTCTL_J (1 << OTGHS_HPRT_PTCTL_SHIFT) /* Test_J mode */ +# define OTGHS_HPRT_PTCTL_L (2 << OTGHS_HPRT_PTCTL_SHIFT) /* Test_K mode */ +# define OTGHS_HPRT_PTCTL_SE0_NAK (3 << OTGHS_HPRT_PTCTL_SHIFT) /* Test_SE0_NAK mode */ +# define OTGHS_HPRT_PTCTL_PACKET (4 << OTGHS_HPRT_PTCTL_SHIFT) /* Test_Packet mode */ +# define OTGHS_HPRT_PTCTL_FORCE (5 << OTGHS_HPRT_PTCTL_SHIFT) /* Test_Force_Enable */ +#define OTGHS_HPRT_PSPD_SHIFT (17) /* Bits 17-18: Port speed */ +#define OTGHS_HPRT_PSPD_MASK (3 << OTGHS_HPRT_PSPD_SHIFT) +# define OTGHS_HPRT_PSPD_FS (1 << OTGHS_HPRT_PSPD_SHIFT) /* Full speed */ +# define OTGHS_HPRT_PSPD_LS (2 << OTGHS_HPRT_PSPD_SHIFT) /* Low speed */ + /* Bits 19-31: Reserved, must be kept at reset value */ + +/* Host channel-n characteristics register */ + +#define OTGHS_HCCHAR_MPSIZ_SHIFT (0) /* Bits 0-10: Maximum packet size */ +#define OTGHS_HCCHAR_MPSIZ_MASK (0x7ff << OTGHS_HCCHAR_MPSIZ_SHIFT) +#define OTGHS_HCCHAR_EPNUM_SHIFT (11) /* Bits 11-14: Endpoint number */ +#define OTGHS_HCCHAR_EPNUM_MASK (15 << OTGHS_HCCHAR_EPNUM_SHIFT) +#define OTGHS_HCCHAR_EPDIR (1 << 15) /* Bit 15: Endpoint direction */ +# define OTGHS_HCCHAR_EPDIR_OUT (0) +# define OTGHS_HCCHAR_EPDIR_IN OTGHS_HCCHAR_EPDIR + /* Bit 16 Reserved, must be kept at reset value */ +#define OTGHS_HCCHAR_LSDEV (1 << 17) /* Bit 17: Low-speed device */ +#define OTGHS_HCCHAR_EPTYP_SHIFT (18) /* Bits 18-19: Endpoint type */ +#define OTGHS_HCCHAR_EPTYP_MASK (3 << OTGHS_HCCHAR_EPTYP_SHIFT) +# define OTGHS_HCCHAR_EPTYP_CTRL (0 << OTGHS_HCCHAR_EPTYP_SHIFT) /* Control */ +# define OTGHS_HCCHAR_EPTYP_ISOC (1 << OTGHS_HCCHAR_EPTYP_SHIFT) /* Isochronous */ +# define OTGHS_HCCHAR_EPTYP_BULK (2 << OTGHS_HCCHAR_EPTYP_SHIFT) /* Bulk */ +# define OTGHS_HCCHAR_EPTYP_INTR (3 << OTGHS_HCCHAR_EPTYP_SHIFT) /* Interrupt */ +#define OTGHS_HCCHAR_MCNT_SHIFT (20) /* Bits 20-21: Multicount */ +#define OTGHS_HCCHAR_MCNT_MASK (3 << OTGHS_HCCHAR_MCNT_SHIFT) +#define OTGHS_HCCHAR_DAD_SHIFT (22) /* Bits 22-28: Device address */ +#define OTGHS_HCCHAR_DAD_MASK (0x7f << OTGHS_HCCHAR_DAD_SHIFT) +#define OTGHS_HCCHAR_ODDFRM (1 << 29) /* Bit 29: Odd frame */ +#define OTGHS_HCCHAR_CHDIS (1 << 30) /* Bit 30: Channel disable */ +#define OTGHS_HCCHAR_CHENA (1 << 31) /* Bit 31: Channel enable */ + +/* Host channel-n interrupt and Host channel-0 interrupt mask registers */ + +#define OTGHS_HCINT_XFRC (1 << 0) /* Bit 0: Transfer completed */ +#define OTGHS_HCINT_CHH (1 << 1) /* Bit 1: Channel halted */ + /* Bit 2: Reserved, must be kept at reset value */ +#define OTGHS_HCINT_STALL (1 << 3) /* Bit 3: STALL response received interrupt */ +#define OTGHS_HCINT_NAK (1 << 4) /* Bit 4: NAK response received interrupt */ +#define OTGHS_HCINT_ACK (1 << 5) /* Bit 5: ACK response received/transmitted interrupt */ +#define OTGHS_HCINT_NYET (1 << 6) /* Bit 6: Response received interrupt */ +#define OTGHS_HCINT_TXERR (1 << 7) /* Bit 7: Transaction error */ +#define OTGHS_HCINT_BBERR (1 << 8) /* Bit 8: Babble error */ +#define OTGHS_HCINT_FRMOR (1 << 9) /* Bit 9: Frame overrun */ +#define OTGHS_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 OTGHS_HCTSIZ_XFRSIZ_SHIFT (0) /* Bits 0-18: Transfer size */ +#define OTGHS_HCTSIZ_XFRSIZ_MASK (0x7ffff << OTGHS_HCTSIZ_XFRSIZ_SHIFT) +#define OTGHS_HCTSIZ_PKTCNT_SHIFT (19) /* Bits 19-28: Packet count */ +#define OTGHS_HCTSIZ_PKTCNT_MASK (0x3ff << OTGHS_HCTSIZ_PKTCNT_SHIFT) +#define OTGHS_HCTSIZ_DPID_SHIFT (29) /* Bits 29-30: Data PID */ +#define OTGHS_HCTSIZ_DPID_MASK (3 << OTGHS_HCTSIZ_DPID_SHIFT) +# define OTGHS_HCTSIZ_DPID_DATA0 (0 << OTGHS_HCTSIZ_DPID_SHIFT) +# define OTGHS_HCTSIZ_DPID_DATA2 (1 << OTGHS_HCTSIZ_DPID_SHIFT) +# define OTGHS_HCTSIZ_DPID_DATA1 (2 << OTGHS_HCTSIZ_DPID_SHIFT) +# define OTGHS_HCTSIZ_DPID_MDATA (3 << OTGHS_HCTSIZ_DPID_SHIFT) /* Non-control */ +# define OTGHS_HCTSIZ_PID_SETUP (3 << OTGHS_HCTSIZ_DPID_SHIFT) /* Control */ + /* Bit 31 Reserved, must be kept at reset value */ +/* Device-mode control and status registers */ + +/* Device configuration register */ + +#define OTGHS_DCFG_DSPD_SHIFT (0) /* Bits 0-1: Device speed */ +#define OTGHS_DCFG_DSPD_MASK (3 << OTGHS_DCFG_DSPD_SHIFT) +# define OTGHS_DCFG_DSPD_FS (3 << OTGHS_DCFG_DSPD_SHIFT) /* Full speed */ +#define OTGHS_DCFG_NZLSOHSK (1 << 2) /* Bit 2: Non-zero-length status OUT handshake */ + /* Bit 3: Reserved, must be kept at reset value */ +#define OTGHS_DCFG_DAD_SHIFT (4) /* Bits 4-10: Device address */ +#define OTGHS_DCFG_DAD_MASK (0x7f << OTGHS_DCFG_DAD_SHIFT) +#define OTGHS_DCFG_PFIVL_SHIFT (11) /* Bits 11-12: Periodic frame interval */ +#define OTGHS_DCFG_PFIVL_MASK (3 << OTGHS_DCFG_PFIVL_SHIFT) +# define OTGHS_DCFG_PFIVL_80PCT (0 << OTGHS_DCFG_PFIVL_SHIFT) /* 80% of the frame interval */ +# define OTGHS_DCFG_PFIVL_85PCT (1 << OTGHS_DCFG_PFIVL_SHIFT) /* 85% of the frame interval */ +# define OTGHS_DCFG_PFIVL_90PCT (2 << OTGHS_DCFG_PFIVL_SHIFT) /* 90% of the frame interval */ +# define OTGHS_DCFG_PFIVL_95PCT (3 << OTGHS_DCFG_PFIVL_SHIFT) /* 95% of the frame interval */ + /* Bits 13-31 Reserved, must be kept at reset value */ +/* Device control register */ + +#define OTGHS_TESTMODE_DISABLED (0) /* Test mode disabled */ +#define OTGHS_TESTMODE_J (1) /* Test_J mode */ +#define OTGHS_TESTMODE_K (2) /* Test_K mode */ +#define OTGHS_TESTMODE_SE0_NAK (3) /* Test_SE0_NAK mode */ +#define OTGHS_TESTMODE_PACKET (4) /* Test_Packet mode */ +#define OTGHS_TESTMODE_FORCE (5) /* Test_Force_Enable */ + +#define OTGHS_DCTL_RWUSIG (1 << 0) /* Bit 0: Remote wakeup signaling */ +#define OTGHS_DCTL_SDIS (1 << 1) /* Bit 1: Soft disconnect */ +#define OTGHS_DCTL_GINSTS (1 << 2) /* Bit 2: Global IN NAK status */ +#define OTGHS_DCTL_GONSTS (1 << 3) /* Bit 3: Global OUT NAK status */ +#define OTGHS_DCTL_TCTL_SHIFT (4) /* Bits 4-6: Test control */ +#define OTGHS_DCTL_TCTL_MASK (7 << OTGHS_DCTL_TCTL_SHIFT) +# define OTGHS_DCTL_TCTL_DISABLED (0 << OTGHS_DCTL_TCTL_SHIFT) /* Test mode disabled */ +# define OTGHS_DCTL_TCTL_J (1 << OTGHS_DCTL_TCTL_SHIFT) /* Test_J mode */ +# define OTGHS_DCTL_TCTL_K (2 << OTGHS_DCTL_TCTL_SHIFT) /* Test_K mode */ +# define OTGHS_DCTL_TCTL_SE0_NAK (3 << OTGHS_DCTL_TCTL_SHIFT) /* Test_SE0_NAK mode */ +# define OTGHS_DCTL_TCTL_PACKET (4 << OTGHS_DCTL_TCTL_SHIFT) /* Test_Packet mode */ +# define OTGHS_DCTL_TCTL_FORCE (5 << OTGHS_DCTL_TCTL_SHIFT) /* Test_Force_Enable */ +#define OTGHS_DCTL_SGINAK (1 << 7) /* Bit 7: Set global IN NAK */ +#define OTGHS_DCTL_CGINAK (1 << 8) /* Bit 8: Clear global IN NAK */ +#define OTGHS_DCTL_SGONAK (1 << 9) /* Bit 9: Set global OUT NAK */ +#define OTGHS_DCTL_CGONAK (1 << 10) /* Bit 10: Clear global OUT NAK */ +#define OTGHS_DCTL_POPRGDNE (1 << 11) /* Bit 11: Power-on programming done */ + /* Bits 12-31: Reserved, must be kept at reset value */ +/* Device status register */ + +#define OTGHS_DSTS_SUSPSTS (1 << 0) /* Bit 0: Suspend status */ +#define OTGHS_DSTS_ENUMSPD_SHIFT (1) /* Bits 1-2: Enumerated speed */ +#define OTGHS_DSTS_ENUMSPD_MASK (3 << OTGHS_DSTS_ENUMSPD_SHIFT) +# define OTGHS_DSTS_ENUMSPD_FS (3 << OTGHS_DSTS_ENUMSPD_MASK) /* Full speed */ + /* Bits 4-7: Reserved, must be kept at reset value */ +#define OTGHS_DSTS_EERR (1 << 3) /* Bit 3: Erratic error */ +#define OTGHS_DSTS_SOFFN_SHIFT (8) /* Bits 8-21: Frame number of the received SOF */ +#define OTGHS_DSTS_SOFFN_MASK (0x3fff << OTGHS_DSTS_SOFFN_SHIFT) +#define OTGHS_DSTS_SOFFN0 (1 << 8) /* Bits 8: Frame number even/odd bit */ +#define OTGHS_DSTS_SOFFN_EVEN 0 +#define OTGHS_DSTS_SOFFN_ODD OTGHS_DSTS_SOFFN0 + /* Bits 22-31: Reserved, must be kept at reset value */ +/* Device IN endpoint common interrupt mask register */ + +#define OTGHS_DIEPMSK_XFRCM (1 << 0) /* Bit 0: Transfer completed interrupt mask */ +#define OTGHS_DIEPMSK_EPDM (1 << 1) /* Bit 1: Endpoint disabled interrupt mask */ + /* Bit 2: Reserved, must be kept at reset value */ +#define OTGHS_DIEPMSK_TOM (1 << 3) /* Bit 3: Timeout condition mask (Non-isochronous endpoints) */ +#define OTGHS_DIEPMSK_ITTXFEMSK (1 << 4) /* Bit 4: IN token received when TxFIFO empty mask */ +#define OTGHS_DIEPMSK_INEPNMM (1 << 5) /* Bit 5: IN token received with EP mismatch mask */ +#define OTGHS_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 OTGHS_DOEPMSK_XFRCM (1 << 0) /* Bit 0: Transfer completed interrupt mask */ +#define OTGHS_DOEPMSK_EPDM (1 << 1) /* Bit 1: Endpoint disabled interrupt mask */ + /* Bit 2: Reserved, must be kept at reset value */ +#define OTGHS_DOEPMSK_STUPM (1 << 3) /* Bit 3: SETUP phase done mask */ +#define OTGHS_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 OTGHS_DAINT_IEP_SHIFT (0) /* Bits 0-15: IN endpoint interrupt bits */ +#define OTGHS_DAINT_IEP_MASK (0xffff << OTGHS_DAINT_IEP_SHIFT) +# define OTGHS_DAINT_IEP(n) (1 << (n)) +#define OTGHS_DAINT_OEP_SHIFT (16) /* Bits 16-31: OUT endpoint interrupt bits */ +#define OTGHS_DAINT_OEP_MASK (0xffff << OTGHS_DAINT_OEP_SHIFT) +# define OTGHS_DAINT_OEP(n) (1 << ((n)+16)) + +/* Device VBUS discharge time register */ + +#define OTGHS_DVBUSDIS_MASK (0xffff) + +/* Device VBUS pulsing time register */ + +#define OTGHS_DVBUSPULSE_MASK (0xfff) + +/* Device IN endpoint FIFO empty interrupt mask register */ + +#define OTGHS_DIEPEMPMSK(n) (1 << (n)) + +/* Device control IN endpoint 0 control register */ + +#define OTGHS_DIEPCTL0_MPSIZ_SHIFT (0) /* Bits 0-1: Maximum packet size */ +#define OTGHS_DIEPCTL0_MPSIZ_MASK (3 << OTGHS_DIEPCTL0_MPSIZ_SHIFT) +# define OTGHS_DIEPCTL0_MPSIZ_64 (0 << OTGHS_DIEPCTL0_MPSIZ_SHIFT) /* 64 bytes */ +# define OTGHS_DIEPCTL0_MPSIZ_32 (1 << OTGHS_DIEPCTL0_MPSIZ_SHIFT) /* 32 bytes */ +# define OTGHS_DIEPCTL0_MPSIZ_16 (2 << OTGHS_DIEPCTL0_MPSIZ_SHIFT) /* 16 bytes */ +# define OTGHS_DIEPCTL0_MPSIZ_8 (3 << OTGHS_DIEPCTL0_MPSIZ_SHIFT) /* 8 bytes */ + /* Bits 2-14: Reserved, must be kept at reset value */ +#define OTGHS_DIEPCTL0_USBAEP (1 << 15) /* Bit 15: USB active endpoint */ + /* Bit 16: Reserved, must be kept at reset value */ +#define OTGHS_DIEPCTL0_NAKSTS (1 << 17) /* Bit 17: NAK status */ +#define OTGHS_DIEPCTL0_EPTYP_SHIFT (18) /* Bits 18-19: Endpoint type */ +#define OTGHS_DIEPCTL0_EPTYP_MASK (3 << OTGHS_DIEPCTL0_EPTYP_SHIFT) +# define OTGHS_DIEPCTL0_EPTYP_CTRL (0 << OTGHS_DIEPCTL0_EPTYP_SHIFT) /* Control (hard-coded) */ + /* Bit 20: Reserved, must be kept at reset value */ +#define OTGHS_DIEPCTL0_STALL (1 << 21) /* Bit 21: STALL handshake */ +#define OTGHS_DIEPCTL0_TXFNUM_SHIFT (22) /* Bits 22-25: TxFIFO number */ +#define OTGHS_DIEPCTL0_TXFNUM_MASK (15 << OTGHS_DIEPCTL0_TXFNUM_SHIFT) +#define OTGHS_DIEPCTL0_CNAK (1 << 26) /* Bit 26: Clear NAK */ +#define OTGHS_DIEPCTL0_SNAK (1 << 27) /* Bit 27: Set NAK */ + /* Bits 28-29: Reserved, must be kept at reset value */ +#define OTGHS_DIEPCTL0_EPDIS (1 << 30) /* Bit 30: Endpoint disable */ +#define OTGHS_DIEPCTL0_EPENA (1 << 31) /* Bit 31: Endpoint enable */ + +/* Device control IN endpoint n control register */ + +#define OTGHS_DIEPCTL_MPSIZ_SHIFT (0) /* Bits 0-10: Maximum packet size */ +#define OTGHS_DIEPCTL_MPSIZ_MASK (0x7ff << OTGHS_DIEPCTL_MPSIZ_SHIFT) + /* Bits 11-14: Reserved, must be kept at reset value */ +#define OTGHS_DIEPCTL_USBAEP (1 << 15) /* Bit 15: USB active endpoint */ +#define OTGHS_DIEPCTL_EONUM (1 << 16) /* Bit 16: Even/odd frame */ +# define OTGHS_DIEPCTL_EVEN (0) +# define OTGHS_DIEPCTL_ODD OTGHS_DIEPCTL_EONUM +# define OTGHS_DIEPCTL_DATA0 (0) +# define OTGHS_DIEPCTL_DATA1 OTGHS_DIEPCTL_EONUM +#define OTGHS_DIEPCTL_NAKSTS (1 << 17) /* Bit 17: NAK status */ +#define OTGHS_DIEPCTL_EPTYP_SHIFT (18) /* Bits 18-19: Endpoint type */ +#define OTGHS_DIEPCTL_EPTYP_MASK (3 << OTGHS_DIEPCTL_EPTYP_SHIFT) +# define OTGHS_DIEPCTL_EPTYP_CTRL (0 << OTGHS_DIEPCTL_EPTYP_SHIFT) /* Control */ +# define OTGHS_DIEPCTL_EPTYP_ISOC (1 << OTGHS_DIEPCTL_EPTYP_SHIFT) /* Isochronous */ +# define OTGHS_DIEPCTL_EPTYP_BULK (2 << OTGHS_DIEPCTL_EPTYP_SHIFT) /* Bulk */ +# define OTGHS_DIEPCTL_EPTYP_INTR (3 << OTGHS_DIEPCTL_EPTYP_SHIFT) /* Interrupt */ + /* Bit 20: Reserved, must be kept at reset value */ +#define OTGHS_DIEPCTL_STALL (1 << 21) /* Bit 21: STALL handshake */ +#define OTGHS_DIEPCTL_TXFNUM_SHIFT (22) /* Bits 22-25: TxFIFO number */ +#define OTGHS_DIEPCTL_TXFNUM_MASK (15 << OTGHS_DIEPCTL_TXFNUM_SHIFT) +#define OTGHS_DIEPCTL_CNAK (1 << 26) /* Bit 26: Clear NAK */ +#define OTGHS_DIEPCTL_SNAK (1 << 27) /* Bit 27: Set NAK */ +#define OTGHS_DIEPCTL_SD0PID (1 << 28) /* Bit 28: Set DATA0 PID (interrupt/bulk) */ +#define OTGHS_DIEPCTL_SEVNFRM (1 << 28) /* Bit 28: Set even frame (isochronous)) */ +#define OTGHS_DIEPCTL_SODDFRM (1 << 29) /* Bit 29: Set odd frame (isochronous) */ +#define OTGHS_DIEPCTL_EPDIS (1 << 30) /* Bit 30: Endpoint disable */ +#define OTGHS_DIEPCTL_EPENA (1 << 31) /* Bit 31: Endpoint enable */ + +/* Device endpoint-n interrupt register */ + +#define OTGHS_DIEPINT_XFRC (1 << 0) /* Bit 0: Transfer completed interrupt */ +#define OTGHS_DIEPINT_EPDISD (1 << 1) /* Bit 1: Endpoint disabled interrupt */ + /* Bit 2: Reserved, must be kept at reset value */ +#define OTGHS_DIEPINT_TOC (1 << 3) /* Bit 3: Timeout condition */ +#define OTGHS_DIEPINT_ITTXFE (1 << 4) /* Bit 4: IN token received when TxFIFO is empty */ + /* Bit 5: Reserved, must be kept at reset value */ +#define OTGHS_DIEPINT_INEPNE (1 << 6) /* Bit 6: IN endpoint NAK effective */ +#define OTGHS_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 OTGHS_DIEPTSIZ0_XFRSIZ_SHIFT (0) /* Bits 0-6: Transfer size */ +#define OTGHS_DIEPTSIZ0_XFRSIZ_MASK (0x7f << OTGHS_DIEPTSIZ0_XFRSIZ_SHIFT) + /* Bits 7-18: Reserved, must be kept at reset value */ +#define OTGHS_DIEPTSIZ0_PKTCNT_SHIFT (19) /* Bits 19-20: Packet count */ +#define OTGHS_DIEPTSIZ0_PKTCNT_MASK (3 << OTGHS_DIEPTSIZ0_PKTCNT_SHIFT) + /* Bits 21-31: Reserved, must be kept at reset value */ +/* Device IN endpoint n transfer size register */ + +#define OTGHS_DIEPTSIZ_XFRSIZ_SHIFT (0) /* Bits 0-18: Transfer size */ +#define OTGHS_DIEPTSIZ_XFRSIZ_MASK (0x7ffff << OTGHS_DIEPTSIZ_XFRSIZ_SHIFT) +#define OTGHS_DIEPTSIZ_PKTCNT_SHIFT (19) /* Bit 19-28: Packet count */ +#define OTGHS_DIEPTSIZ_PKTCNT_MASK (0x3ff << OTGHS_DIEPTSIZ_PKTCNT_SHIFT) +#define OTGHS_DIEPTSIZ_MCNT_SHIFT (29) /* Bits 29-30: Multi count */ +#define OTGHS_DIEPTSIZ_MCNT_MASK (3 << OTGHS_DIEPTSIZ_MCNT_SHIFT) + /* Bit 31: Reserved, must be kept at reset value */ +/* Device OUT endpoint TxFIFO status register */ + +#define OTGHS_DTXFSTS_MASK (0xffff) + +/* Device OUT endpoint 0 control register */ + +#define OTGHS_DOEPCTL0_MPSIZ_SHIFT (0) /* Bits 0-1: Maximum packet size */ +#define OTGHS_DOEPCTL0_MPSIZ_MASK (3 << OTGHS_DOEPCTL0_MPSIZ_SHIFT) +# define OTGHS_DOEPCTL0_MPSIZ_64 (0 << OTGHS_DOEPCTL0_MPSIZ_SHIFT) /* 64 bytes */ +# define OTGHS_DOEPCTL0_MPSIZ_32 (1 << OTGHS_DOEPCTL0_MPSIZ_SHIFT) /* 32 bytes */ +# define OTGHS_DOEPCTL0_MPSIZ_16 (2 << OTGHS_DOEPCTL0_MPSIZ_SHIFT) /* 16 bytes */ +# define OTGHS_DOEPCTL0_MPSIZ_8 (3 << OTGHS_DOEPCTL0_MPSIZ_SHIFT) /* 8 bytes */ + /* Bits 2-14: Reserved, must be kept at reset value */ +#define OTGHS_DOEPCTL0_USBAEP (1 << 15) /* Bit 15: USB active endpoint */ + /* Bit 16: Reserved, must be kept at reset value */ +#define OTGHS_DOEPCTL0_NAKSTS (1 << 17) /* Bit 17: NAK status */ +#define OTGHS_DOEPCTL0_EPTYP_SHIFT (18) /* Bits 18-19: Endpoint type */ +#define OTGHS_DOEPCTL0_EPTYP_MASK (3 << OTGHS_DOEPCTL0_EPTYP_SHIFT) +# define OTGHS_DOEPCTL0_EPTYP_CTRL (0 << OTGHS_DOEPCTL0_EPTYP_SHIFT) /* Control (hard-coded) */ +#define OTGHS_DOEPCTL0_SNPM (1 << 20) /* Bit 20: Snoop mode */ +#define OTGHS_DOEPCTL0_STALL (1 << 21) /* Bit 21: STALL handshake */ + /* Bits 22-25: Reserved, must be kept at reset value */ +#define OTGHS_DOEPCTL0_CNAK (1 << 26) /* Bit 26: Clear NAK */ +#define OTGHS_DOEPCTL0_SNAK (1 << 27) /* Bit 27: Set NAK */ + /* Bits 28-29: Reserved, must be kept at reset value */ +#define OTGHS_DOEPCTL0_EPDIS (1 << 30) /* Bit 30: Endpoint disable */ +#define OTGHS_DOEPCTL0_EPENA (1 << 31) /* Bit 31: Endpoint enable */ + +/* Device OUT endpoint n control register */ + +#define OTGHS_DOEPCTL_MPSIZ_SHIFT (0) /* Bits 0-10: Maximum packet size */ +#define OTGHS_DOEPCTL_MPSIZ_MASK (0x7ff << OTGHS_DOEPCTL_MPSIZ_SHIFT) + /* Bits 11-14: Reserved, must be kept at reset value */ +#define OTGHS_DOEPCTL_USBAEP (1 << 15) /* Bit 15: USB active endpoint */ +#define OTGHS_DOEPCTL_DPID (1 << 16) /* Bit 16: Endpoint data PID (interrupt/buld) */ +# define OTGHS_DOEPCTL_DATA0 (0) +# define OTGHS_DOEPCTL_DATA1 OTGHS_DOEPCTL_DPID +#define OTGHS_DOEPCTL_EONUM (1 << 16) /* Bit 16: Even/odd frame (isochronous) */ +# define OTGHS_DOEPCTL_EVEN (0) +# define OTGHS_DOEPCTL_ODD OTGHS_DOEPCTL_EONUM +#define OTGHS_DOEPCTL_NAKSTS (1 << 17) /* Bit 17: NAK status */ +#define OTGHS_DOEPCTL_EPTYP_SHIFT (18) /* Bits 18-19: Endpoint type */ +#define OTGHS_DOEPCTL_EPTYP_MASK (3 << OTGHS_DOEPCTL_EPTYP_SHIFT) +# define OTGHS_DOEPCTL_EPTYP_CTRL (0 << OTGHS_DOEPCTL_EPTYP_SHIFT) /* Control */ +# define OTGHS_DOEPCTL_EPTYP_ISOC (1 << OTGHS_DOEPCTL_EPTYP_SHIFT) /* Isochronous */ +# define OTGHS_DOEPCTL_EPTYP_BULK (2 << OTGHS_DOEPCTL_EPTYP_SHIFT) /* Bulk */ +# define OTGHS_DOEPCTL_EPTYP_INTR (3 << OTGHS_DOEPCTL_EPTYP_SHIFT) /* Interrupt */ +#define OTGHS_DOEPCTL_SNPM (1 << 20) /* Bit 20: Snoop mode */ +#define OTGHS_DOEPCTL_STALL (1 << 21) /* Bit 21: STALL handshake */ + /* Bits 22-25: Reserved, must be kept at reset value */ +#define OTGHS_DOEPCTL_CNAK (1 << 26) /* Bit 26: Clear NAK */ +#define OTGHS_DOEPCTL_SNAK (1 << 27) /* Bit 27: Set NAK */ +#define OTGHS_DOEPCTL_SD0PID (1 << 28) /* Bit 28: Set DATA0 PID (interrupt/bulk) */ +#define OTGHS_DOEPCTL_SEVNFRM (1 << 28) /* Bit 28: Set even frame (isochronous) */ +#define OTGHS_DOEPCTL_SD1PID (1 << 29) /* Bit 29: Set DATA1 PID (interrupt/bulk) */ +#define OTGHS_DOEPCTL_SODDFRM (1 << 29) /* Bit 29: Set odd frame (isochronous */ +#define OTGHS_DOEPCTL_EPDIS (1 << 30) /* Bit 30: Endpoint disable */ +#define OTGHS_DOEPCTL_EPENA (1 << 31) /* Bit 31: Endpoint enable */ + +/* Device endpoint-n interrupt register */ + +#define OTGHS_DOEPINT_XFRC (1 << 0) /* Bit 0: Transfer completed interrupt */ +#define OTGHS_DOEPINT_EPDISD (1 << 1) /* Bit 1: Endpoint disabled interrupt */ + /* Bit 2: Reserved, must be kept at reset value */ +#define OTGHS_DOEPINT_SETUP (1 << 3) /* Bit 3: SETUP phase done */ +#define OTGHS_DOEPINT_OTEPDIS (1 << 4) /* Bit 4: OUT token received when endpoint disabled */ + /* Bit 5: Reserved, must be kept at reset value */ +#define OTGHS_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 OTGHS_DOEPTSIZ0_XFRSIZ_SHIFT (0) /* Bits 0-6: Transfer size */ +#define OTGHS_DOEPTSIZ0_XFRSIZ_MASK (0x7f << OTGHS_DOEPTSIZ0_XFRSIZ_SHIFT) + /* Bits 7-18: Reserved, must be kept at reset value */ +#define OTGHS_DOEPTSIZ0_PKTCNT (1 << 19) /* Bit 19 PKTCNT: Packet count */ + /* Bits 20-28: Reserved, must be kept at reset value */ +#define OTGHS_DOEPTSIZ0_STUPCNT_SHIFT (29) /* Bits 29-30: SETUP packet count */ +#define OTGHS_DOEPTSIZ0_STUPCNT_MASK (3 << OTGHS_DOEPTSIZ0_STUPCNT_SHIFT) + /* Bit 31: Reserved, must be kept at reset value */ +/* Device OUT endpoint-n transfer size register */ + +#define OTGHS_DOEPTSIZ_XFRSIZ_SHIFT (0) /* Bits 0-18: Transfer size */ +#define OTGHS_DOEPTSIZ_XFRSIZ_MASK (0x7ffff << OTGHS_DOEPTSIZ_XFRSIZ_SHIFT) +#define OTGHS_DOEPTSIZ_PKTCNT_SHIFT (19) /* Bit 19-28: Packet count */ +#define OTGHS_DOEPTSIZ_PKTCNT_MASK (0x3ff << OTGHS_DOEPTSIZ_PKTCNT_SHIFT) +#define OTGHS_DOEPTSIZ_STUPCNT_SHIFT (29) /* Bits 29-30: SETUP packet count */ +#define OTGHS_DOEPTSIZ_STUPCNT_MASK (3 << OTGHS_DOEPTSIZ_STUPCNT_SHIFT) +#define OTGHS_DOEPTSIZ_RXDPID_SHIFT (29) /* Bits 29-30: Received data PID */ +#define OTGHS_DOEPTSIZ_RXDPID_MASK (3 << OTGHS_DOEPTSIZ_RXDPID_SHIFT) +# define OTGHS_DOEPTSIZ_RXDPID_DATA0 (0 << OTGHS_DOEPTSIZ_RXDPID_SHIFT) +# define OTGHS_DOEPTSIZ_RXDPID_DATA2 (1 << OTGHS_DOEPTSIZ_RXDPID_SHIFT) +# define OTGHS_DOEPTSIZ_RXDPID_DATA1 (2 << OTGHS_DOEPTSIZ_RXDPID_SHIFT) +# define OTGHS_DOEPTSIZ_RXDPID_MDATA (3 << OTGHS_DOEPTSIZ_RXDPID_SHIFT) + /* Bit 31: Reserved, must be kept at reset value */ +/* Power and clock gating control register */ + +#define OTGHS_PCGCCTL_STPPCLK (1 << 0) /* Bit 0: Stop PHY clock */ +#define OTGHS_PCGCCTL_GATEHCLK (1 << 1) /* Bit 1: Gate HCLK */ + /* Bits 2-3: Reserved, must be kept at reset value */ +#define OTGHS_PCGCCTL_PHYSUSP (1 << 4) /* Bit 4: PHY Suspended */ + /* Bits 5-31: Reserved, must be kept at reset value */ + +#endif /* __ARCH_ARM_SRC_STM32_CHIP_STM32_OTGHS_H */ diff --git a/arch/arm/src/stm32/stm32_otghs.h b/arch/arm/src/stm32/stm32_otghs.h new file mode 100644 index 0000000000..2380862c04 --- /dev/null +++ b/arch/arm/src/stm32/stm32_otghs.h @@ -0,0 +1,127 @@ +/************************************************************************************ + * arch/arm/src/stm32/stm32_otghs.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_STM32_STM32_OTGHS_H +#define __ARCH_ARM_SRC_STM32_STM32_OTGHS_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +#include + +#include "stm32.h" +#include "chip/stm32_otghs.h" + +#if defined(CONFIG_STM32_OTGHS) + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ +/* Configuration ********************************************************************/ + +#ifndef CONFIG_OTGHS_PRI +# define CONFIG_OTGHS_PRI NVIC_SYSH_PRIORITY_DEFAULT +#endif + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/******************************************************************************* + * Name: 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 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_otghshost_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_STM32_STM32_OTGHS_H */ + diff --git a/arch/arm/src/stm32/stm32_otghshost.c b/arch/arm/src/stm32/stm32_otghshost.c new file mode 100644 index 0000000000..97025c90fd --- /dev/null +++ b/arch/arm/src/stm32/stm32_otghshost.c @@ -0,0 +1,4427 @@ +/******************************************************************************* + * arch/arm/src/stm32/stm32_otghshost.c + * + * Copyright (C) 2012-2014 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 "chip.h" /* Includes default GPIO settings */ +#include /* May redefine GPIO settings */ + +#include "up_arch.h" +#include "up_internal.h" + +#include "stm32_usbhost.h" + +#if defined(CONFIG_USBHOST) && defined(CONFIG_STM32_OTGHS) + +/******************************************************************************* + * Pre-processor Definitions + *******************************************************************************/ +/* Configuration ***************************************************************/ +/* + * STM32 USB OTG HS Host Driver Support + * + * Pre-requisites + * + * CONFIG_USBHOST - Enable general USB host support + * CONFIG_STM32_OTGHS - Enable the STM32 USB OTG HS block + * CONFIG_STM32_SYSCFG - Needed + * + * Options: + * + * CONFIG_STM32_OTGHS_RXFIFO_SIZE - Size of the RX FIFO in 32-bit words. + * Default 128 (512 bytes) + * CONFIG_STM32_OTGHS_NPTXFIFO_SIZE - Size of the non-periodic Tx FIFO + * in 32-bit words. Default 96 (384 bytes) + * CONFIG_STM32_OTGHS_PTXFIFO_SIZE - Size of the periodic Tx FIFO in 32-bit + * words. Default 96 (384 bytes) + * CONFIG_STM32_OTGHS_DESCSIZE - Maximum size of a descriptor. Default: 128 + * CONFIG_STM32_OTGHS_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. + * CONFIG_STM32_USBHOST_PKTDUMP - Dump all incoming and outgoing USB + * packets. Depends on CONFIG_DEBUG. + */ + +/* Pre-requisites (partial) */ + +#ifndef CONFIG_STM32_SYSCFG +# error "CONFIG_STM32_SYSCFG is required" +#endif + +/* Default RxFIFO size */ + +#ifndef CONFIG_STM32_OTGHS_RXFIFO_SIZE +# define CONFIG_STM32_OTGHS_RXFIFO_SIZE 128 +#endif + +/* Default host non-periodic Tx FIFO size */ + +#ifndef CONFIG_STM32_OTGHS_NPTXFIFO_SIZE +# define CONFIG_STM32_OTGHS_NPTXFIFO_SIZE 96 +#endif + +/* Default host periodic Tx fifo size register */ + +#ifndef CONFIG_STM32_OTGHS_PTXFIFO_SIZE +# define CONFIG_STM32_OTGHS_PTXFIFO_SIZE 96 +#endif + +/* Maximum size of a descriptor */ + +#ifndef CONFIG_STM32_OTGHS_DESCSIZE +# define CONFIG_STM32_OTGHS_DESCSIZE 128 +#endif + +/* Register/packet debug depends on CONFIG_DEBUG */ + +#ifndef CONFIG_DEBUG +# undef CONFIG_STM32_USBHOST_REGDEBUG +# undef CONFIG_STM32_USBHOST_PKTDUMP +#endif + +/* HCD Setup *******************************************************************/ +/* Hardware capabilities */ + +#define STM32_NHOST_CHANNELS 12 /* 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 HS max packet size */ +#define STM32_MAX_TX_FIFOS 12 /* Max number of TX FIFOs */ +#define STM32_MAX_PKTCOUNT 256 /* Max packet count */ +#define STM32_RETRY_COUNT 3 /* Number of ctrl transfer retries */ +#define STM32_DEF_DEVADDR 0 /* Default device address */ + +/* 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 */ +}; + +/* 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 epno; /* Device endpoint number (0-127) */ + uint8_t eptype; /* See OTGHS_EPTYPE_* definitions */ + uint8_t pid; /* Data PID */ + uint8_t npackets; /* Number of packets (for data toggle) */ + bool inuse; /* True: This channel is "in use" */ + volatile bool indata1; /* IN data toggle. True: DATA01 (Bulk and INTR only) */ + volatile bool outdata1; /* OUT data toggle. True: DATA01 */ + bool in; /* True: IN endpoint */ + volatile bool waiter; /* True: Thread is waiting for a channel event */ + uint16_t maxpacket; /* Max packet size */ + volatile uint16_t buflen; /* Buffer length (remaining) */ + volatile uint16_t inflight; /* Number of Tx bytes "in-flight" */ + FAR uint8_t *buffer; /* Transfer buffer pointer */ +}; + +/* This structure retains the state of the USB host controller */ + +struct 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; + + /* The bound device class driver */ + + struct usbhost_class_s *class; + + /* Overall driver status */ + + volatile uint8_t smstate; /* The state of the USB host state machine */ + uint8_t devaddr; /* Device address */ + uint8_t ep0in; /* EP0 IN control channel index */ + uint8_t ep0out; /* EP0 OUT control channel index */ + uint8_t ep0size; /* EP0 max packet size */ + uint8_t chidx; /* ID of channel waiting for space in Tx FIFO */ + bool lowspeed; /* True: low speed device */ + volatile bool connected; /* Connected to device */ + volatile bool eventwait; /* True: Thread is waiting for a port event */ + sem_t exclsem; /* Support mutually exclusive access */ + sem_t eventsem; /* Semaphore to wait for a port event */ + + /* The state of each host channel */ + + struct 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); +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); + +/* 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 const struct usb_ctrlreq_s *req); +static int stm32_ctrl_senddata(FAR struct stm32_usbhost_s *priv, + FAR uint8_t *buffer, unsigned int buflen); +static int stm32_ctrl_recvdata(FAR struct stm32_usbhost_s *priv, + FAR uint8_t *buffer, unsigned int buflen); +static int stm32_in_transfer(FAR struct stm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen); +static int stm32_out_transfer(FAR struct stm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen); + +/* 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_OTGHS_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 const bool *connected); +static int stm32_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx); + +static int stm32_ep0configure(FAR struct usbhost_driver_s *drvr, uint8_t funcaddr, + uint16_t maxpacketsize); +static int stm32_getdevinfo(FAR struct usbhost_driver_s *drvr, + FAR struct usbhost_devinfo_s *devinfo); +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, + FAR const struct usb_ctrlreq_s *req, + FAR uint8_t *buffer); +static int stm32_ctrlout(FAR struct usbhost_driver_s *drvr, + FAR const struct usb_ctrlreq_s *req, + FAR const uint8_t *buffer); +static int stm32_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, + FAR uint8_t *buffer, size_t buflen); +static void stm32_disconnect(FAR struct usbhost_driver_s *drvr); + +/* 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 = +{ + .drvr = + { + .ep0configure = stm32_ep0configure, + .getdevinfo = stm32_getdevinfo, + .epalloc = stm32_epalloc, + .epfree = stm32_epfree, + .alloc = stm32_alloc, + .free = stm32_free, + .ioalloc = stm32_ioalloc, + .iofree = stm32_iofree, + .ctrlin = stm32_ctrlin, + .ctrlout = stm32_ctrlout, + .transfer = stm32_transfer, + .disconnect = stm32_disconnect, + }, + .class = NULL, +}; + +/* 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) +{ + lldbg("%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. */ + + lldbg("[repeats %d more times]\n", count); + } + } + + /* Save the new address, value, count, and operation for next time */ + + prevaddr = addr; + preval = val; + count = 0; + prevwrite = iswrite; + + /* Show the new regisgter access */ + + 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) +{ + uint32_t regval; + + /* Clear any old pending interrupts for this host channel. */ + + stm32_putreg(STM32_OTGHS_HCINT(chidx), 0xffffffff); + + /* Enable channel interrupts required for transfers on this channel. */ + + regval = 0; + + switch (priv->chan[chidx].eptype) + { + case OTGHS_EPTYPE_CTRL: + case OTGHS_EPTYPE_BULK: + { +#ifdef HAVE_USBHOST_TRACE_VERBOSE + uint16_t intrace; + uint16_t outtrace; + + /* Determine the definitive trace ID to use below */ + + if (priv->chan[chidx].eptype == OTGHS_EPTYPE_CTRL) + { + intrace = OTGHS_VTRACE2_CHANCONF_CTRL_IN; + outtrace = OTGHS_VTRACE2_CHANCONF_CTRL_OUT; + } + else + { + intrace = OTGHS_VTRACE2_CHANCONF_BULK_IN; + outtrace = OTGHS_VTRACE2_CHANCONF_BULK_OUT; + } +#endif + + /* Interrupts required for CTRL and BULK endpoints */ + + regval |= (OTGHS_HCINT_XFRC | OTGHS_HCINT_STALL | OTGHS_HCINT_NAK | + OTGHS_HCINT_TXERR | OTGHS_HCINT_DTERR); + + /* Additional setting for IN/OUT endpoints */ + + if (priv->chan[chidx].in) + { + usbhost_vtrace2(intrace, chidx, priv->chan[chidx].epno); + regval |= OTGHS_HCINT_BBERR; + } + else + { + usbhost_vtrace2(outtrace, chidx, priv->chan[chidx].epno); + regval |= OTGHS_HCINT_NYET; + } + } + break; + + case OTGHS_EPTYPE_INTR: + { + /* Interrupts required for INTR endpoints */ + + regval |= (OTGHS_HCINT_XFRC | OTGHS_HCINT_STALL | OTGHS_HCINT_NAK | + OTGHS_HCINT_TXERR | OTGHS_HCINT_FRMOR | OTGHS_HCINT_DTERR); + + /* Additional setting for IN endpoints */ + + if (priv->chan[chidx].in) + { + usbhost_vtrace2(OTGHS_VTRACE2_CHANCONF_INTR_IN, chidx, + priv->chan[chidx].epno); + regval |= OTGHS_HCINT_BBERR; + } +#ifdef HAVE_USBHOST_TRACE_VERBOSE + else + { + usbhost_vtrace2(OTGHS_VTRACE2_CHANCONF_INTR_OUT, chidx, + priv->chan[chidx].epno); + } +#endif + } + break; + + case OTGHS_EPTYPE_ISOC: + { + /* Interrupts required for ISOC endpoints */ + + regval |= (OTGHS_HCINT_XFRC | OTGHS_HCINT_ACK | OTGHS_HCINT_FRMOR); + + /* Additional setting for IN endpoints */ + + if (priv->chan[chidx].in) + { + usbhost_vtrace2(OTGHS_VTRACE2_CHANCONF_ISOC_IN, chidx, + priv->chan[chidx].epno); + regval |= (OTGHS_HCINT_TXERR | OTGHS_HCINT_BBERR); + } +#ifdef HAVE_USBHOST_TRACE_VERBOSE + else + { + usbhost_vtrace2(OTGHS_VTRACE2_CHANCONF_ISOC_OUT, chidx, + priv->chan[chidx].epno); + } +#endif + } + break; + } + + stm32_putreg(STM32_OTGHS_HCINTMSK(chidx), regval); + + /* Enable the top level host channel interrupt. */ + + stm32_modifyreg(STM32_OTGHS_HAINTMSK, 0, OTGHS_HAINT(chidx)); + + /* Make sure host channel interrupts are enabled. */ + + stm32_modifyreg(STM32_OTGHS_GINTMSK, 0, OTGHS_GINT_HC); + + /* Program the HCCHAR register */ + + regval = ((uint32_t)priv->chan[chidx].maxpacket << OTGHS_HCCHAR_MPSIZ_SHIFT) | + ((uint32_t)priv->chan[chidx].epno << OTGHS_HCCHAR_EPNUM_SHIFT) | + ((uint32_t)priv->chan[chidx].eptype << OTGHS_HCCHAR_EPTYP_SHIFT) | + ((uint32_t)priv->devaddr << OTGHS_HCCHAR_DAD_SHIFT); + + /* Special case settings for low speed devices */ + + if (priv->lowspeed) + { + regval |= OTGHS_HCCHAR_LSDEV; + } + + /* Special case settings for IN endpoints */ + + if (priv->chan[chidx].in) + { + regval |= OTGHS_HCCHAR_EPDIR_IN; + } + + /* Special case settings for INTR endpoints */ + + if (priv->chan[chidx].eptype == OTGHS_EPTYPE_INTR) + { + regval |= OTGHS_HCCHAR_ODDFRM; + } + + /* Write the channel configuration */ + + stm32_putreg(STM32_OTGHS_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 interrrupt + * handling logic to know what to do next. + */ + + usbhost_vtrace2(OTGHS_VTRACE2_CHANHALT, chidx, chreason); + + priv->chan[chidx].chreason = (uint8_t)chreason; + + /* "The application can disable any channel by programming the OTG_HS_HCCHARx + * register with the CHDIS and CHENA bits set to 1. This enables the OTG_HS + * host to flush the posted requests (if any) and generates a channel halted + * interrupt. The application must wait for the CHH interrupt in OTG_HS_HCINTx + * before reallocating the channel for other transactions. The OTG_HS host + * does not interrupt the transaction that has already been started on the + * USB." + */ + + hcchar = stm32_getreg(STM32_OTGHS_HCCHAR(chidx)); + hcchar |= (OTGHS_HCCHAR_CHDIS | OTGHS_HCCHAR_CHENA); + + /* Get the endpoint type from the HCCHAR register */ + + eptype = hcchar & OTGHS_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_HS_HCCHARx register with the CHDIS bit + * set to 1, and the CHENA bit cleared to 0. + */ + + if (eptype == OTGHS_HCCHAR_EPTYP_CTRL || eptype == OTGHS_HCCHAR_EPTYP_BULK) + { + /* Get the number of words available in the non-periodic Tx FIFO. */ + + avail = stm32_getreg(STM32_OTGHS_HNPTXSTS) & OTGHS_HNPTXSTS_NPTXFSAV_MASK; + } + else /* if (eptype == OTGHS_HCCHAR_EPTYP_ISOC || eptype == OTGHS_HCCHAR_EPTYP_INTR) */ + { + /* Get the number of words available in the non-periodic Tx FIFO. */ + + avail = stm32_getreg(STM32_OTGHS_HPTXSTS) & OTGHS_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 &= ~OTGHS_HCCHAR_CHENA; + } + + /* Unmask the CHannel Halted (CHH) interrupt */ + + intmsk = stm32_getreg(STM32_OTGHS_HCINTMSK(chidx)); + intmsk |= OTGHS_HCINT_CHH; + stm32_putreg(STM32_OTGHS_HCINTMSK(chidx), intmsk); + + /* Halt the channel by setting CHDIS (and maybe CHENA) in the HCCHAR */ + + stm32_putreg(STM32_OTGHS_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 = irqsave(); + int ret = -ENODEV; + + /* Is the device still connected? */ + + if (priv->connected) + { + /* Yes.. then set waiter to indicate that we expect to be informed when + * either (1) the device is disconnected, or (2) the transfer completed. + */ + + chan->waiter = true; + ret = OK; + } + + irqrestore(flags); + return ret; +} + +/******************************************************************************* + * Name: 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 HS global interrupt needs to be disabled. However, here we disable + * all interrupts to exploit that fact that interrupts will be re-enabled + * while we wait. + */ + + flags = irqsave(); + + /* Loop, testing for an end of transfer conditino. The channel 'result' + * was set to EBUSY and 'waiter' was set to true before the transfer; 'waiter' + * will be set to false and 'result' will be set appropriately when the + * tranfer is completed. + */ + + do + { + /* Wait for the transfer to complete. NOTE the transfer may already + * completed before we get here or the transfer may complete while we + * wait here. + */ + + ret = sem_wait(&chan->waitsem); + + /* sem_wait should succeeed. But it is possible that we could be + * awakened by a signal too. + */ + + DEBUGASSERT(ret == OK || get_errno() == EINTR); + } + while (chan->waiter); + + /* The transfer is complete re-enable interrupts and return the result */ + + ret = -(int)chan->result; + irqrestore(flags); + return ret; +} + +/******************************************************************************* + * Name: 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? Is there a thread waiting for this transfer + * to complete? + */ + + if (chan->result != EBUSY && chan->waiter) + { + usbhost_vtrace2(chan->in ? OTGHS_VTRACE2_CHANWAKEUP_IN : + OTGHS_VTRACE2_CHANWAKEUP_OUT, + chan->epno, chan->result); + + stm32_givesem(&chan->waitsem); + chan->waiter = false; + } +} + +/******************************************************************************* + * 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(OTGHS_VTRACE2_STARTTRANSFER, chidx, chan->buflen); + + chan->result = EBUSY; + chan->inflight = 0; + priv->chidx = chidx; + + /* Compute the expected number of packets associated to the transfer. + * If the transfer length is zero (or less than the size of one maximum + * size packet), then one packet is expected. + */ + + /* If the transfer size is greater than one packet, then calculate the + * number of packets that will be received/sent, including any partial + * final packet. + */ + + maxpacket = chan->maxpacket; + + if (chan->buflen > maxpacket) + { + npackets = (chan->buflen + maxpacket - 1) / maxpacket; + + /* Clip if the buffer length if it exceeds the maximum number of + * packets that can be transferred (this should not happen). + */ + + if (npackets > STM32_MAX_PKTCOUNT) + { + npackets = STM32_MAX_PKTCOUNT; + chan->buflen = STM32_MAX_PKTCOUNT * maxpacket; + usbhost_trace2(OTGHS_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 << OTGHS_HCTSIZ_XFRSIZ_SHIFT) | + ((uint32_t)npackets << OTGHS_HCTSIZ_PKTCNT_SHIFT) | + ((uint32_t)chan->pid << OTGHS_HCTSIZ_DPID_SHIFT); + stm32_putreg(STM32_OTGHS_HCTSIZ(chidx), regval); + + /* Setup the HCCHAR register: Frame oddness and host channel enable */ + + regval = stm32_getreg(STM32_OTGHS_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_OTGHS_HFNUM) & 1) == 0) + { + regval |= OTGHS_HCCHAR_ODDFRM; + } + else + { + regval &= ~OTGHS_HCCHAR_ODDFRM; + } + + regval &= ~OTGHS_HCCHAR_CHDIS; + regval |= OTGHS_HCCHAR_CHENA; + stm32_putreg(STM32_OTGHS_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 OTGHS_EPTYPE_CTRL: /* Non periodic transfer */ + case OTGHS_EPTYPE_BULK: + { + /* Read the Non-periodic Tx FIFO status register */ + + regval = stm32_getreg(STM32_OTGHS_HNPTXSTS); + avail = ((regval & OTGHS_HNPTXSTS_NPTXFSAV_MASK) >> OTGHS_HNPTXSTS_NPTXFSAV_SHIFT) << 2; + } + break; + + /* Periodic transfer */ + + case OTGHS_EPTYPE_INTR: + case OTGHS_EPTYPE_ISOC: + { + /* Read the Non-periodic Tx FIFO status register */ + + regval = stm32_getreg(STM32_OTGHS_HPTXSTS); + avail = ((regval & OTGHS_HPTXSTS_PTXFSAVL_MASK) >> OTGHS_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_OTGHS_HFNUM) & OTGHS_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 const struct usb_ctrlreq_s *req) +{ + FAR struct stm32_chan_s *chan; + uint32_t start; + uint32_t elapsed; + int ret; + + /* Loop while the device reports NAK (and a timeout is not exceeded */ + + chan = &priv->chan[priv->ep0out]; + start = clock_systimer(); + + do + { + /* Send the SETUP packet */ + + chan->pid = OTGHS_PID_SETUP; + chan->buffer = (FAR uint8_t *)req; + chan->buflen = USB_SIZEOF_CTRLREQ; + + /* Set up for the wait BEFORE starting the transfer */ + + ret = stm32_chan_waitsetup(priv, chan); + if (ret != OK) + { + usbhost_trace1(OTGHS_TRACE1_DEVDISCONN, 0); + return ret; + } + + /* Start the transfer */ + + stm32_transfer_start(priv, priv->ep0out); + + /* 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(OTGHS_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 uint8_t *buffer, unsigned int buflen) +{ + FAR struct stm32_chan_s *chan = &priv->chan[priv->ep0out]; + int ret; + + /* Save buffer information */ + + chan->buffer = buffer; + chan->buflen = buflen; + + /* Set the DATA PID */ + + if (buflen == 0) + { + /* For status OUT stage with buflen == 0, set PID DATA1 */ + + chan->outdata1 = true; + } + + /* Set the Data PID as per the outdata1 boolean */ + + chan->pid = chan->outdata1 ? OTGHS_PID_DATA1 : OTGHS_PID_DATA0; + + /* Set up for the wait BEFORE starting the transfer */ + + ret = stm32_chan_waitsetup(priv, chan); + if (ret != OK) + { + usbhost_trace1(OTGHS_TRACE1_DEVDISCONN, 0); + return ret; + } + + /* Start the transfer */ + + stm32_transfer_start(priv, priv->ep0out); + + /* 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 uint8_t *buffer, unsigned int buflen) +{ + FAR struct stm32_chan_s *chan = &priv->chan[priv->ep0in]; + int ret; + + /* Save buffer information */ + + chan->pid = OTGHS_PID_DATA1; + chan->buffer = buffer; + chan->buflen = buflen; + + /* Set up for the wait BEFORE starting the transfer */ + + ret = stm32_chan_waitsetup(priv, chan); + if (ret != OK) + { + usbhost_trace1(OTGHS_TRACE1_DEVDISCONN, 0); + return ret; + } + + /* Start the transfer */ + + stm32_transfer_start(priv, priv->ep0in); + + /* Wait for the transfer to complete and return the result */ + + return stm32_chan_wait(priv, chan); +} + +/******************************************************************************* + * Name: stm32_in_transfer + * + * Description: + * Transfer 'buflen' bytes into 'buffer' from an IN channel. + * + *******************************************************************************/ + +static int stm32_in_transfer(FAR struct stm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen) +{ + FAR struct stm32_chan_s *chan; + uint32_t start; + uint32_t elapsed; + int ret = OK; + + /* Loop until the transfer completes (i.e., buflen is decremented to zero) + * or a fatal error occurs (any error other than a simple NAK) + */ + + chan = &priv->chan[chidx]; + chan->buffer = buffer; + chan->buflen = buflen; + + start = clock_systimer(); + while (chan->buflen > 0) + { + /* Set up for the wait BEFORE starting the transfer */ + + ret = stm32_chan_waitsetup(priv, chan); + if (ret != OK) + { + usbhost_trace1(OTGHS_TRACE1_DEVDISCONN, 0); + return ret; + } + + /* Set up for the transfer based on the direction and the endpoint type */ + + switch (chan->eptype) + { + default: + case OTGHS_EPTYPE_CTRL: /* Control */ + { + /* This kind of transfer on control endpoints other than EP0 are not + * currently supported + */ + + return -ENOSYS; + } + + case OTGHS_EPTYPE_ISOC: /* Isochronous */ + { + /* Set up the IN data PID */ + + usbhost_vtrace2(OTGHS_VTRACE2_ISOCIN, chidx, buflen); + chan->pid = OTGHS_PID_DATA0; + } + break; + + case OTGHS_EPTYPE_BULK: /* Bulk */ + { + /* Setup the IN data PID */ + + usbhost_vtrace2(OTGHS_VTRACE2_BULKIN, chidx, buflen); + chan->pid = chan->indata1 ? OTGHS_PID_DATA1 : OTGHS_PID_DATA0; + } + break; + + case OTGHS_EPTYPE_INTR: /* Interrupt */ + { + /* Setup the IN data PID */ + + usbhost_vtrace2(OTGHS_VTRACE2_INTRIN, chidx, buflen); + chan->pid = chan->indata1 ? OTGHS_PID_DATA1 : OTGHS_PID_DATA0; + } + break; + } + + /* Start the transfer */ + + stm32_transfer_start(priv, chidx); + + /* 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 != OK) + { + usbhost_trace1(OTGHS_TRACE1_TRNSFRFAILED,ret); + + /* Check for a special case: If (1) the transfer was NAKed and (2) + * no Tx FIFO empty or Rx FIFO not-empty event occurred, then we + * should be able to just flush the Rx and Tx FIFOs and try again. + * We can detect this latter case becasue the then the transfer + * buffer pointer and buffer size will be unaltered. + */ + + elapsed = clock_systimer() - start; + if (ret != -EAGAIN || /* Not a NAK condition OR */ + elapsed >= STM32_DATANAK_DELAY || /* Timeout has elapsed OR */ + chan->buflen != buflen) /* Data has been partially transferred */ + { + /* Break out and return the error */ + + break; + } + } + } + + return ret; +} + +/******************************************************************************* + * Name: stm32_out_transfer + * + * Description: + * Transfer the 'buflen' bytes in 'buffer' through an OUT channel. + * + *******************************************************************************/ + +static int stm32_out_transfer(FAR struct stm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen) +{ + FAR struct stm32_chan_s *chan; + uint32_t start; + uint32_t elapsed; + size_t xfrlen; + int ret = OK; + + /* Loop until the transfer completes (i.e., buflen is decremented to zero) + * or a fatal error occurs (any error other than a simple NAK) + */ + + chan = &priv->chan[chidx]; + start = clock_systimer(); + + while (buflen > 0) + { + /* Transfer one packet at a time. The hardware is capable of queueing + * multiple OUT packets, but I just haven't figured out how to handle + * the case where a single OUT packet in the group is NAKed. + */ + + xfrlen = MIN(chan->maxpacket, buflen); + chan->buffer = buffer; + chan->buflen = xfrlen; + + /* Set up for the wait BEFORE starting the transfer */ + + ret = stm32_chan_waitsetup(priv, chan); + if (ret != OK) + { + usbhost_trace1(OTGHS_TRACE1_DEVDISCONN,0); + return ret; + } + + /* Set up for the transfer based on the direction and the endpoint type */ + + switch (chan->eptype) + { + default: + case OTGHS_EPTYPE_CTRL: /* Control */ + { + /* This kind of transfer on control endpoints other than EP0 are not + * currently supported + */ + + return -ENOSYS; + } + + case OTGHS_EPTYPE_ISOC: /* Isochronous */ + { + /* Set up the OUT data PID */ + + usbhost_vtrace2(OTGHS_VTRACE2_ISOCOUT, chidx, buflen); + chan->pid = OTGHS_PID_DATA0; + } + break; + + case OTGHS_EPTYPE_BULK: /* Bulk */ + { + /* Setup the OUT data PID */ + + usbhost_vtrace2(OTGHS_VTRACE2_BULKOUT, chidx, buflen); + chan->pid = chan->outdata1 ? OTGHS_PID_DATA1 : OTGHS_PID_DATA0; + } + break; + + case OTGHS_EPTYPE_INTR: /* Interrupt */ + { + /* Setup the OUT data PID */ + + usbhost_vtrace2(OTGHS_VTRACE2_INTROUT, chidx, buflen); + chan->pid = chan->outdata1 ? OTGHS_PID_DATA1 : OTGHS_PID_DATA0; + + /* Toggle the OUT data PID for the next transfer */ + + chan->outdata1 ^= true; + } + break; + } + + /* Start the transfer */ + + stm32_transfer_start(priv, chidx); + + /* Wait for the transfer to complete and get the result */ + + ret = stm32_chan_wait(priv, chan); + + /* Handle transfer failures */ + + if (ret != OK) + { + usbhost_trace1(OTGHS_TRACE1_TRNSFRFAILED,ret); + + /* Check for a special case: If (1) the transfer was NAKed and (2) + * no Tx FIFO empty or Rx FIFO not-empty event occurred, then we + * should be able to just flush the Rx and Tx FIFOs and try again. + * We can detect this latter case becasue the then the transfer + * buffer pointer and buffer size will be unaltered. + */ + + elapsed = clock_systimer() - start; + if (ret != -EAGAIN || /* Not a NAK condition OR */ + elapsed >= STM32_DATANAK_DELAY || /* Timeout has elapsed OR */ + chan->buflen != xfrlen) /* Data has been partially transferred */ + { + /* Break out and return the error */ + + break; + } + + /* Is this flush really necessary? What does the hardware do with the + * data in the FIFO when the NAK occurs? Does it discard it? + */ + + stm32_flush_txfifos(OTGHS_GRSTCTL_TXFNUM_HALL); + + /* Get the device a little time to catch up. Then retry the transfer + * using the same buffer pointer and length. + */ + + usleep(20*1000); + } + else + { + /* Successfully transferred. Update the buffer pointer and length */ + + buffer += xfrlen; + buflen -= xfrlen; + } + } + + return ret; +} + +/******************************************************************************* + * Name: 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_OTGHS_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 HS 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_OTGHS_HCINT(chidx)); + regval = stm32_getreg(STM32_OTGHS_HCINTMSK(chidx)); + + /* AND the two to get the set of enabled, pending HC interrupts */ + + pending &= regval; + ullvdbg("HCINTMSK%d: %08x pending: %08x\n", chidx, regval, pending); + + /* Check for a pending ACK response received/transmitted (ACK) interrupt */ + + if ((pending & OTGHS_HCINT_ACK) != 0) + { + /* Clear the pending the ACK response received/transmitted (ACK) interrupt */ + + stm32_putreg(STM32_OTGHS_HCINT(chidx), OTGHS_HCINT_ACK); + } + + /* Check for a pending STALL response receive (STALL) interrupt */ + + else if ((pending & OTGHS_HCINT_STALL) != 0) + { + /* Clear the NAK and STALL Conditions. */ + + stm32_putreg(STM32_OTGHS_HCINT(chidx), (OTGHS_HCINT_NAK | OTGHS_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 &= ~OTGHS_HCINT_NAK; + } + + /* Check for a pending Data Toggle ERRor (DTERR) interrupt */ + + else if ((pending & OTGHS_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_OTGHS_HCINT(chidx), (OTGHS_HCINT_NAK | OTGHS_HCINT_DTERR)); + } + + /* Check for a pending FRaMe OverRun (FRMOR) interrupt */ + + if ((pending & OTGHS_HCINT_FRMOR) != 0) + { + /* Halt the channel -- the CHH interrrupt is expected next */ + + stm32_chan_halt(priv, chidx, CHREASON_FRMOR); + + /* Clear the FRaMe OverRun (FRMOR) condition */ + + stm32_putreg(STM32_OTGHS_HCINT(chidx), OTGHS_HCINT_FRMOR); + } + + /* Check for a pending TransFeR Completed (XFRC) interrupt */ + + else if ((pending & OTGHS_HCINT_XFRC) != 0) + { + /* Clear the TransFeR Completed (XFRC) condition */ + + stm32_putreg(STM32_OTGHS_HCINT(chidx), OTGHS_HCINT_XFRC); + + /* Then handle the transfer completion event based on the endpoint type */ + + if (chan->eptype == OTGHS_EPTYPE_CTRL || chan->eptype == OTGHS_EPTYPE_BULK) + { + /* Halt the channel -- the CHH interrrupt 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_OTGHS_HCINT(chidx), OTGHS_HCINT_NAK); + } + else if (chan->eptype == OTGHS_EPTYPE_INTR) + { + /* Force the next transfer on an ODD frame */ + + regval = stm32_getreg(STM32_OTGHS_HCCHAR(chidx)); + regval |= OTGHS_HCCHAR_ODDFRM; + stm32_putreg(STM32_OTGHS_HCCHAR(chidx), regval); + + /* Set the request done state */ + + chan->result = OK; + } + } + + /* Check for a pending CHannel Halted (CHH) interrupt */ + + else if ((pending & OTGHS_HCINT_CHH) != 0) + { + /* Mask the CHannel Halted (CHH) interrupt */ + + regval = stm32_getreg(STM32_OTGHS_HCINTMSK(chidx)); + regval &= ~OTGHS_HCINT_CHH; + stm32_putreg(STM32_OTGHS_HCINTMSK(chidx), regval); + + /* Update the request state based on the host state machine state */ + + if (chan->chreason == CHREASON_XFRC) + { + /* Set the request done reult */ + + chan->result = OK; + } + else if (chan->chreason == CHREASON_STALL) + { + /* Set the request stall result */ + + chan->result = EPERM; + } + else if ((chan->chreason == CHREASON_TXERR) || + (chan->chreason == CHREASON_DTERR)) + { + /* Set the request I/O error result */ + + chan->result = EIO; + } + else if (chan->chreason == CHREASON_NAK) + { + /* Halt on NAK only happens on an INTR channel. Fetch the HCCHAR register + * and check for an interrupt endpoint. + */ + + regval = stm32_getreg(STM32_OTGHS_HCCHAR(chidx)); + if ((regval & OTGHS_HCCHAR_EPTYP_MASK) == OTGHS_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_OTGHS_HCINT(chidx), OTGHS_HCINT_CHH); + } + + /* Check for a pending Transaction ERror (TXERR) interrupt */ + + else if ((pending & OTGHS_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_OTGHS_HCINT(chidx), OTGHS_HCINT_TXERR); + } + + /* Check for a pending NAK response received (NAK) interrupt */ + + else if ((pending & OTGHS_HCINT_NAK) != 0) + { + /* For a BULK transfer, the hardware is capable of retrying + * automatically on a NAK. However, this is not always + * what we need to do. So we always halt the transfer and + * return control to high level logic in the even of a NAK. + */ + +#if 1 + /* Halt the interrupt channel */ + + if (chan->eptype == OTGHS_EPTYPE_INTR) + { + /* Halt the channel -- the CHH interrupt is expected next */ + + stm32_chan_halt(priv, chidx, CHREASON_NAK); + } + + /* Re-activate CTRL and BULK channels */ + + else if (chan->eptype == OTGHS_EPTYPE_CTRL || + chan->eptype == OTGHS_EPTYPE_BULK) + { + /* Re-activate the channel by clearing CHDIS and assuring that + * CHENA is set + */ + + regval = stm32_getreg(STM32_OTGHS_HCCHAR(chidx)); + regval |= OTGHS_HCCHAR_CHENA; + regval &= ~OTGHS_HCCHAR_CHDIS; + stm32_putreg(STM32_OTGHS_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_OTGHS_HCINT(chidx), OTGHS_HCINT_NAK); + } + + /* Check for a transfer complete event */ + + stm32_chan_wakeup(priv, chan); +} + +/******************************************************************************* + * Name: stm32_gint_hcoutisr + * + * Description: + * USB OTG HS 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_OTGHS_HCINT(chidx)); + regval = stm32_getreg(STM32_OTGHS_HCINTMSK(chidx)); + + /* AND the two to get the set of enabled, pending HC interrupts */ + + pending &= regval; + ullvdbg("HCINTMSK%d: %08x pending: %08x\n", chidx, regval, pending); + + /* Check for a pending ACK response received/transmitted (ACK) interrupt */ + + if ((pending & OTGHS_HCINT_ACK) != 0) + { + /* Clear the pending the ACK response received/transmitted (ACK) interrupt */ + + stm32_putreg(STM32_OTGHS_HCINT(chidx), OTGHS_HCINT_ACK); + } + + /* Check for a pending FRaMe OverRun (FRMOR) interrupt */ + + else if ((pending & OTGHS_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_OTGHS_HCINT(chidx), OTGHS_HCINT_FRMOR); + } + + /* Check for a pending TransFeR Completed (XFRC) interrupt */ + + else if ((pending & OTGHS_HCINT_XFRC) != 0) + { + /* Decrement the number of bytes remaining by the number of + * bytes that were "in-flight". + */ + + priv->chan[chidx].buffer += priv->chan[chidx].inflight; + priv->chan[chidx].buflen -= priv->chan[chidx].inflight; + priv->chan[chidx].inflight = 0; + + /* Halt the channel -- the CHH interrrupt is expected next */ + + stm32_chan_halt(priv, chidx, CHREASON_XFRC); + + /* Clear the pending the TransFeR Completed (XFRC) interrupt */ + + stm32_putreg(STM32_OTGHS_HCINT(chidx), OTGHS_HCINT_XFRC); + } + + /* Check for a pending STALL response receive (STALL) interrupt */ + + else if ((pending & OTGHS_HCINT_STALL) != 0) + { + /* Clear the pending the STALL response receiv (STALL) interrupt */ + + stm32_putreg(STM32_OTGHS_HCINT(chidx), OTGHS_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 & OTGHS_HCINT_NAK) != 0) + { + /* Halt the channel -- the CHH interrrupt is expected next */ + + stm32_chan_halt(priv, chidx, CHREASON_NAK); + + /* Clear the pending the NAK response received (NAK) interrupt */ + + stm32_putreg(STM32_OTGHS_HCINT(chidx), OTGHS_HCINT_NAK); + } + + /* Check for a pending Transaction ERror (TXERR) interrupt */ + + else if ((pending & OTGHS_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_OTGHS_HCINT(chidx), OTGHS_HCINT_TXERR); + } + + /* Check for a NYET interrupt */ + +#if 0 /* NYET is a reserved bit in the HCINT register */ + else if ((pending & OTGHS_HCINT_NYET) != 0) + { + /* Halt the channel */ + + stm32_chan_halt(priv, chidx, CHREASON_NYET); + + /* Clear the pending the NYET interrupt */ + + stm32_putreg(STM32_OTGHS_HCINT(chidx), OTGHS_HCINT_NYET); + } +#endif + + /* Check for a pending Data Toggle ERRor (DTERR) interrupt */ + + else if (pending & OTGHS_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_OTGHS_HCINT(chidx), (OTGHS_HCINT_DTERR | OTGHS_HCINT_NAK)); + } + + /* Check for a pending CHannel Halted (CHH) interrupt */ + + else if ((pending & OTGHS_HCINT_CHH) != 0) + { + /* Mask the CHannel Halted (CHH) interrupt */ + + regval = stm32_getreg(STM32_OTGHS_HCINTMSK(chidx)); + regval &= ~OTGHS_HCINT_CHH; + stm32_putreg(STM32_OTGHS_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_OTGHS_HCCHAR(chidx)); + + /* Is it a bulk endpoint? Were an odd number of packets + * transferred? + */ + + if ((regval & OTGHS_HCCHAR_EPTYP_MASK) == OTGHS_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_OTGHS_HCINT(chidx), OTGHS_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(OTGHS_VTRACE1_CONNECTED,0); + priv->connected = true; + DEBUGASSERT(priv->smstate == SMSTATE_DETACHED); + + /* Notify any waiters */ + + priv->smstate = SMSTATE_ATTACHED; + if (priv->eventwait) + { + stm32_givesem(&priv->eventsem); + priv->eventwait = 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(OTGHS_VTRACE1_DISCONNECTED,0); + + /* Are we bound to a class driver? */ + + if (priv->class) + { + /* Yes.. Disconnect the class driver */ + + CLASS_DISCONNECTED(priv->class); + priv->class = NULL; + } + + /* Re-Initilaize Host for new Enumeration */ + + priv->smstate = SMSTATE_DETACHED; + priv->ep0size = STM32_EP0_MAX_PACKET_SIZE; + priv->devaddr = STM32_DEF_DEVADDR; + priv->connected = false; + priv->lowspeed = false; + stm32_chan_freeall(priv); + + /* Notify any waiters that there is a change in the connection state */ + + if (priv->eventwait) + { + stm32_givesem(&priv->eventsem); + priv->eventwait = false; + } + } +} + +/******************************************************************************* + * Name: stm32_gint_sofisr + * + * Description: + * USB OTG HS start-of-frame interrupt handler + * + *******************************************************************************/ + +#ifdef CONFIG_STM32_OTGHS_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_OTGHS_GINTSTS, OTGHS_GINT_SOF); +} +#endif + +/******************************************************************************* + * Name: stm32_gint_rxflvlisr + * + * Description: + * USB OTG HS 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_OTGHS_GINTMSK); + intmsk &= ~OTGHS_GINT_RXFLVL; + stm32_putreg(STM32_OTGHS_GINTMSK, intmsk); + + /* Read and pop the next status from the Rx FIFO */ + + grxsts = stm32_getreg(STM32_OTGHS_GRXSTSP); + ullvdbg("GRXSTS: %08x\n", grxsts); + + /* Isolate the channel number/index in the status word */ + + chidx = (grxsts & OTGHS_GRXSTSH_CHNUM_MASK) >> OTGHS_GRXSTSH_CHNUM_SHIFT; + + /* Get the host channel characteristics register (HCCHAR) for this channel */ + + hcchar = stm32_getreg(STM32_OTGHS_HCCHAR(chidx)); + + /* Then process the interrupt according to the packet status */ + + switch (grxsts & OTGHS_GRXSTSH_PKTSTS_MASK) + { + case OTGHS_GRXSTSH_PKTSTS_INRECVD: /* IN data packet received */ + { + /* Read the data into the host buffer. */ + + bcnt = (grxsts & OTGHS_GRXSTSH_BCNT_MASK) >> OTGHS_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_OTGHS_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].buflen -= bcnt; + + /* Check if more packets are expected */ + + hctsiz = stm32_getreg(STM32_OTGHS_HCTSIZ(chidx)); + if ((hctsiz & OTGHS_HCTSIZ_PKTCNT_MASK) != 0) + { + /* Re-activate the channel when more packets are expected */ + + hcchar |= OTGHS_HCCHAR_CHENA; + hcchar &= ~OTGHS_HCCHAR_CHDIS; + stm32_putreg(STM32_OTGHS_HCCHAR(chidx), hcchar); + } + } + } + break; + + case OTGHS_GRXSTSH_PKTSTS_INDONE: /* IN transfer completed */ + case OTGHS_GRXSTSH_PKTSTS_DTOGERR: /* Data toggle error */ + case OTGHS_GRXSTSH_PKTSTS_HALTED: /* Channel halted */ + default: + break; + } + + /* Re-enable the RxFIFO non-empty interrupt */ + + intmsk |= OTGHS_GINT_RXFLVL; + stm32_putreg(STM32_OTGHS_GINTMSK, intmsk); +} + +/******************************************************************************* + * Name: stm32_gint_nptxfeisr + * + * Description: + * USB OTG HS 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->buflen -= chan->inflight; + chan->inflight = 0; + + /* If we have now transfered the entire buffer, then this transfer is + * complete (this case really should never happen because we disable + * the NPTXFE interrupt on the final packet). + */ + + if (chan->buflen <= 0) + { + /* Disable further Tx FIFO empty interrupts and bail. */ + + stm32_modifyreg(STM32_OTGHS_GINTMSK, OTGHS_GINT_NPTXFE, 0); + return; + } + + /* Read the status from the top of the non-periodic TxFIFO */ + + regval = stm32_getreg(STM32_OTGHS_HNPTXSTS); + + /* Extract the number of bytes available in the non-periodic Tx FIFO. */ + + avail = ((regval & OTGHS_HNPTXSTS_NPTXFSAV_MASK) >> OTGHS_HNPTXSTS_NPTXFSAV_SHIFT) << 2; + + /* Get minimal size packet that can be sent. Something is seriously + * configured wrong if one packet will not fit into the empty Tx FIFO. + */ + + DEBUGASSERT(chan->buflen > 0 && + avail >= MIN(chan->buflen, chan->maxpacket)); + + /* Get the size to put in the Tx FIFO now */ + + wrsize = chan->buflen; + if (wrsize > avail) + { + /* Clip the write size to the number of full, max sized packets + * that will fit in the Tx FIFO. + */ + + unsigned int wrpackets = avail / chan->maxpacket; + wrsize = wrpackets * chan->maxpacket; + } + + /* Otherwise, this will be the last packet to be sent in this transaction. + * We now need to disable further NPTXFE interrupts. + */ + + else + { + stm32_modifyreg(STM32_OTGHS_GINTMSK, OTGHS_GINT_NPTXFE, 0); + } + + /* Write the next group of packets into the Tx FIFO */ + + ullvdbg("HNPTXSTS: %08x chidx: %d avail: %d buflen: %d wrsize: %d\n", + regval, chidx, avail, chan->buflen, wrsize); + + stm32_gint_wrpacket(priv, chan->buffer, chidx, wrsize); +} + +/******************************************************************************* + * Name: stm32_gint_ptxfeisr + * + * Description: + * USB OTG HS 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->buflen -= chan->inflight; + chan->inflight = 0; + + /* If we have now transfered the entire buffer, then this transfer is + * complete (this case really should never happen because we disable + * the PTXFE interrupt on the final packet). + */ + + if (chan->buflen <= 0) + { + /* Disable further Tx FIFO empty interrupts and bail. */ + + stm32_modifyreg(STM32_OTGHS_GINTMSK, OTGHS_GINT_PTXFE, 0); + return; + } + + /* Read the status from the top of the periodic TxFIFO */ + + regval = stm32_getreg(STM32_OTGHS_HPTXSTS); + + /* Extract the number of bytes available in the periodic Tx FIFO. */ + + avail = ((regval & OTGHS_HPTXSTS_PTXFSAVL_MASK) >> OTGHS_HPTXSTS_PTXFSAVL_SHIFT) << 2; + + /* Get minimal size packet that can be sent. Something is seriously + * configured wrong if one packet will not fit into the empty Tx FIFO. + */ + + DEBUGASSERT(chan->buflen > 0 && + avail >= MIN(chan->buflen, chan->maxpacket)); + + /* Get the size to put in the Tx FIFO now */ + + wrsize = chan->buflen; + if (wrsize > avail) + { + /* Clip the write size to the number of full, max sized packets + * that will fit in the Tx FIFO. + */ + + unsigned int wrpackets = avail / chan->maxpacket; + wrsize = wrpackets * chan->maxpacket; + } + + /* Otherwise, this will be the last packet to be sent in this transaction. + * We now need to disable further PTXFE interrupts. + */ + + else + { + stm32_modifyreg(STM32_OTGHS_GINTMSK, OTGHS_GINT_PTXFE, 0); + } + + /* Write the next group of packets into the Tx FIFO */ + + ullvdbg("HPTXSTS: %08x chidx: %d avail: %d buflen: %d wrsize: %d\n", + regval, chidx, avail, chan->buflen, wrsize); + + stm32_gint_wrpacket(priv, chan->buffer, chidx, wrsize); +} + +/******************************************************************************* + * Name: stm32_gint_hcisr + * + * Description: + * USB OTG HS 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_OTGHS_HAINT); + for (i = 0; i < STM32_NHOST_CHANNELS; i++) + { + /* Is an interrupt pending on this channel? */ + + if ((haint & OTGHS_HAINT(i)) != 0) + { + /* Yes... read the HCCHAR register to get the direction bit */ + + hcchar = stm32_getreg(STM32_OTGHS_HCCHAR(i)); + + /* Was this an interrupt on an IN or an OUT channel? */ + + if ((hcchar & OTGHS_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 HS 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(OTGHS_VTRACE1_GINT_HPRT, 0); + /* Read the port status and control register (HPRT) */ + + hprt = stm32_getreg(STM32_OTGHS_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 & ~(OTGHS_HPRT_PENA | OTGHS_HPRT_PCDET | + OTGHS_HPRT_PENCHNG | OTGHS_HPRT_POCCHNG); + + /* Check for Port Overcurrent CHaNGe (POCCHNG) */ + + if ((hprt & OTGHS_HPRT_POCCHNG) != 0) + { + /* Set up to clear the POCCHNG status in the new HPRT contents. */ + + usbhost_vtrace1(OTGHS_VTRACE1_GINT_HPRT_POCCHNG, 0); + newhprt |= OTGHS_HPRT_POCCHNG; + } + + /* Check for Port Connect DETected (PCDET). The core sets this bit when a + * device connection is detected. + */ + + if ((hprt & OTGHS_HPRT_PCDET) != 0) + { + /* Set up to clear the PCDET status in the new HPRT contents. Then + * process the new connection event. + */ + + usbhost_vtrace1(OTGHS_VTRACE1_GINT_HPRT_PCDET, 0); + newhprt |= OTGHS_HPRT_PCDET; + stm32_portreset(priv); + stm32_gint_connected(priv); + } + + /* Check for Port Enable CHaNGed (PENCHNG) */ + + if ((hprt & OTGHS_HPRT_PENCHNG) != 0) + { + /* Set up to clear the PENCHNG status in the new HPRT contents. */ + + usbhost_vtrace1(OTGHS_VTRACE1_GINT_HPRT_PENCHNG, 0); + newhprt |= OTGHS_HPRT_PENCHNG; + + /* Was the port enabled? */ + + if ((hprt & OTGHS_HPRT_PENA) != 0) + { + /* Yes.. handle the new connection event */ + + stm32_gint_connected(priv); + + /* Check the Host ConFiGuration register (HCFG) */ + + hcfg = stm32_getreg(STM32_OTGHS_HCFG); + + /* Is this a low speed or full speed connection (OTG HS does not + * support high speed) + */ + + if ((hprt & OTGHS_HPRT_PSPD_MASK) == OTGHS_HPRT_PSPD_LS) + { + /* Set the Host Frame Interval Register for the 6KHz speed */ + + usbhost_vtrace1(OTGHS_VTRACE1_GINT_HPRT_LSDEV, 0); + stm32_putreg(STM32_OTGHS_HFIR, 6000); + + /* Are we switching from HS to LS? */ + + if ((hcfg & OTGHS_HCFG_FSLSPCS_MASK) != OTGHS_HCFG_FSLSPCS_LS6MHz) + { + + usbhost_vtrace1(OTGHS_VTRACE1_GINT_HPRT_FSLSSW, 0); + /* Yes... configure for LS */ + + hcfg &= ~OTGHS_HCFG_FSLSPCS_MASK; + hcfg |= OTGHS_HCFG_FSLSPCS_LS6MHz; + stm32_putreg(STM32_OTGHS_HCFG, hcfg); + + /* And reset the port */ + + stm32_portreset(priv); + } + } + else /* if ((hprt & OTGHS_HPRT_PSPD_MASK) == OTGHS_HPRT_PSPD_HS) */ + { + + usbhost_vtrace1(OTGHS_VTRACE1_GINT_HPRT_FSDEV, 0); + stm32_putreg(STM32_OTGHS_HFIR, 48000); + + /* Are we switching from LS to HS? */ + + if ((hcfg & OTGHS_HCFG_FSLSPCS_MASK) != OTGHS_HCFG_FSLSPCS_FS48MHz) + { + + usbhost_vtrace1(OTGHS_VTRACE1_GINT_HPRT_LSFSSW, 0); + /* Yes... configure for HS */ + + hcfg &= ~OTGHS_HCFG_FSLSPCS_MASK; + hcfg |= OTGHS_HCFG_FSLSPCS_FS48MHz; + stm32_putreg(STM32_OTGHS_HCFG, hcfg); + + /* And reset the port */ + + stm32_portreset(priv); + } + } + } + } + + /* Clear port interrupts by setting bits in the HPRT */ + + stm32_putreg(STM32_OTGHS_HPRT, newhprt); +} + +/******************************************************************************* + * Name: stm32_gint_discisr + * + * Description: + * USB OTG HS 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_OTGHS_GINTSTS, OTGHS_GINT_DISC); +} + +/******************************************************************************* + * Name: stm32_gint_ipxfrisr + * + * Description: + * USB OTG HS 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_OTGHS_HCCHAR(0)); + regval |= (OTGHS_HCCHAR_CHDIS | OTGHS_HCCHAR_CHENA); + stm32_putreg(STM32_OTGHS_HCCHAR(0), regval); + + /* Clear the incomplete isochronous OUT interrupt */ + + stm32_putreg(STM32_OTGHS_GINTSTS, OTGHS_GINT_IPXFR); +} + +/******************************************************************************* + * Name: stm32_gint_isr + * + * Description: + * USB OTG HS global interrupt handler + * + *******************************************************************************/ + +static int stm32_gint_isr(int irq, FAR void *context) +{ + /* At present, there is only support for a single OTG HS 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_OTGHS_GINTSTS); + pending &= stm32_getreg(STM32_OTGHS_GINTMSK); + + /* Return from the interrupt when there are no furhter pending + * interrupts. + */ + + if (pending == 0) + { + return OK; + } + + /* Otherwise, process each pending, unmasked GINT interrupts */ + + + /* Handle the start of frame interrupt */ + +#ifdef CONFIG_STM32_OTGHS_SOFINTR + if ((pending & OTGHS_GINT_SOF) != 0) + { + usbhost_vtrace1(OTGHS_VTRACE1_GINT_SOF, 0); + stm32_gint_sofisr(priv); + } +#endif + + /* Handle the RxFIFO non-empty interrupt */ + + if ((pending & OTGHS_GINT_RXFLVL) != 0) + { + usbhost_vtrace1(OTGHS_VTRACE1_GINT_RXFLVL, 0); + stm32_gint_rxflvlisr(priv); + } + + /* Handle the non-periodic TxFIFO empty interrupt */ + + if ((pending & OTGHS_GINT_NPTXFE) != 0) + { + usbhost_vtrace1(OTGHS_VTRACE1_GINT_NPTXFE, 0); + stm32_gint_nptxfeisr(priv); + } + + /* Handle the periodic TxFIFO empty interrupt */ + + if ((pending & OTGHS_GINT_PTXFE) != 0) + { + usbhost_vtrace1(OTGHS_VTRACE1_GINT_PTXFE, 0); + stm32_gint_ptxfeisr(priv); + } + + /* Handle the host channels interrupt */ + + if ((pending & OTGHS_GINT_HC) != 0) + { + usbhost_vtrace1(OTGHS_VTRACE1_GINT_HC, 0); + stm32_gint_hcisr(priv); + } + + /* Handle the host port interrupt */ + + if ((pending & OTGHS_GINT_HPRT) != 0) + { + stm32_gint_hprtisr(priv); + } + + /* Handle the disconnect detected interrupt */ + + if ((pending & OTGHS_GINT_DISC) != 0) + { + usbhost_vtrace1(OTGHS_VTRACE1_GINT_DISC, 0); + stm32_gint_discisr(priv); + } + + /* Handle the incomplete periodic transfer */ + + if ((pending & OTGHS_GINT_IPXFR) != 0) + { + usbhost_vtrace1(OTGHS_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 HS 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_OTGHS_GAHBCFG); + regval |= OTGHS_GAHBCFG_GINTMSK; + stm32_putreg(STM32_OTGHS_GAHBCFG, regval); +} + +static void stm32_gint_disable(void) +{ + uint32_t regval; + + /* Clear the GINTMSK bit to mask the interrupt */ + + regval = stm32_getreg(STM32_OTGHS_GAHBCFG); + regval &= ~OTGHS_GAHBCFG_GINTMSK; + stm32_putreg(STM32_OTGHS_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_OTGHS_GINTMSK, 0); + + /* Clear any pending interrupts. */ + + stm32_putreg(STM32_OTGHS_GINTSTS, 0xffffffff); + + /* Clear any pending USB OTG Interrupts (should be done elsewhere if OTG is supported) */ + + stm32_putreg(STM32_OTGHS_GOTGINT, 0xffffffff); + + /* Clear any pending USB OTG interrupts */ + + stm32_putreg(STM32_OTGHS_GINTSTS, 0xbfffffff); + + /* Enable the host interrupts */ + /* Common interrupts: + * + * OTGHS_GINT_WKUP : Resume/remote wakeup detected interrupt + * OTGHS_GINT_USBSUSP : USB suspend + */ + + regval = (OTGHS_GINT_WKUP | OTGHS_GINT_USBSUSP); + + /* If OTG were supported, we would need to enable the following as well: + * + * OTGHS_GINT_OTG : OTG interrupt + * OTGHS_GINT_SRQ : Session request/new session detected interrupt + * OTGHS_GINT_CIDSCHG : Connector ID status change + */ + + /* Host-specific interrupts + * + * OTGHS_GINT_SOF : Start of frame + * OTGHS_GINT_RXFLVL : RxFIFO non-empty + * OTGHS_GINT_IISOOXFR : Incomplete isochronous OUT transfer + * OTGHS_GINT_HPRT : Host port interrupt + * OTGHS_GINT_HC : Host channels interrupt + * OTGHS_GINT_DISC : Disconnect detected interrupt + */ + +#ifdef CONFIG_STM32_OTGHS_SOFINTR + regval |= (OTGHS_GINT_SOF | OTGHS_GINT_RXFLVL | OTGHS_GINT_IISOOXFR | + OTGHS_GINT_HPRT | OTGHS_GINT_HC | OTGHS_GINT_DISC); +#else + regval |= (OTGHS_GINT_RXFLVL | OTGHS_GINT_IPXFR | OTGHS_GINT_HPRT | + OTGHS_GINT_HC | OTGHS_GINT_DISC); +#endif + stm32_putreg(STM32_OTGHS_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 = irqsave(); + + /* Should we enable the periodic or non-peridic Tx FIFO empty interrupts */ + + regval = stm32_getreg(STM32_OTGHS_GINTMSK); + switch (chan->eptype) + { + default: + case OTGHS_EPTYPE_CTRL: /* Non periodic transfer */ + case OTGHS_EPTYPE_BULK: + regval |= OTGHS_GINT_NPTXFE; + break; + + case OTGHS_EPTYPE_INTR: /* Periodic transfer */ + case OTGHS_EPTYPE_ISOC: + regval |= OTGHS_GINT_PTXFE; + break; + } + + /* Enable interrupts */ + + stm32_putreg(STM32_OTGHS_GINTMSK, regval); + irqrestore(flags); +} + +/******************************************************************************* + * USB Host Controller Operations + *******************************************************************************/ + +/******************************************************************************* + * Name: stm32_wait + * + * Description: + * Wait for a device to be connected or disconneced. + * + * Input Parameters: + * conn - The USB host connection instance obtained as a parameter from the call to + * the USB driver initialization logic. + * connected - A pointer to a boolean value. TRUE: Wait for device to be + * connected; FALSE: wait for device to be disconnected + * + * Returned Values: + * Zero (OK) is returned when a device in connected. This function will not + * return until either (1) a device is connected or (2) some failure occurs. + * On a failure, a negated errno value is returned indicating the nature of + * the failure + * + * Assumptions: + * - Called from a single thread so no mutual exclusion is required. + * - Never called from an interrupt handler. + * + *******************************************************************************/ + +static int stm32_wait(FAR struct usbhost_connection_s *conn, + FAR const bool *connected) +{ + FAR struct stm32_usbhost_s *priv = &g_usbhost; + irqstate_t flags; + + /* Are we already connected? */ + + flags = irqsave(); + while (priv->connected == *connected) + { + /* No... wait for the connection/disconnection */ + + priv->eventwait = true; + stm32_takesem(&priv->eventsem); + } + + irqrestore(flags); + + udbg("Connected:%s\n", priv->connected ? "YES" : "NO"); + return OK; +} + +/******************************************************************************* + * 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 configdesc() method + * of the struct usbhost_class_s interface. After that, the class is in + * charge of the sequence of operations. + * + * Input Parameters: + * conn - The USB host connection instance obtained as a parameter from the call to + * the USB driver initialization logic. + * rphndx - Root hub port index. 0-(n-1) corresponds to root hub port 1-n. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * - Only a single class bound to a single device is supported. + * - Called from a single thread so no mutual exclusion is required. + * - Never called from an interrupt handler. + * + *******************************************************************************/ + +static int stm32_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx) +{ + FAR struct stm32_usbhost_s *priv = &g_usbhost; + uint32_t regval; + int chidx; + int ret; + + DEBUGASSERT(priv && rhpndx == 0); + + /* Are we connected to a device? The caller should have called the wait() + * method first to be assured that a device is connected. + */ + + while (!priv->connected) + { + /* No, return an error */ + + usbhost_trace1(OTGHS_TRACE1_DEVDISCONN,0); + return -ENODEV; + } + + DEBUGASSERT(priv->smstate == SMSTATE_ATTACHED); + + /* Allocate and initialize the control OUT channel */ + + chidx = stm32_chan_alloc(priv); + DEBUGASSERT(chidx >= 0); + + priv->ep0out = chidx; + priv->chan[chidx].epno = 0; + priv->chan[chidx].in = false; + priv->chan[chidx].eptype = OTGHS_EPTYPE_CTRL; + priv->chan[chidx].maxpacket = STM32_EP0_DEF_PACKET_SIZE; + priv->chan[chidx].indata1 = false; + priv->chan[chidx].outdata1 = false; + + /* Allocate and initialize the control IN channel */ + + chidx = stm32_chan_alloc(priv); + DEBUGASSERT(chidx >= 0); + + priv->ep0in = chidx; + priv->chan[chidx].epno = 0; + priv->chan[chidx].in = true; + priv->chan[chidx].eptype = OTGHS_EPTYPE_CTRL; + priv->chan[chidx].maxpacket = STM32_EP0_DEF_PACKET_SIZE; + priv->chan[chidx].indata1 = false; + priv->chan[chidx].outdata1 = false; + + /* USB 2.0 spec says at least 50ms delay before port reset. We wait 100ms. */ + + usleep(100*1000); + + /* Reset the host port */ + + stm32_portreset(priv); + + /* Get the current device speed */ + + regval = stm32_getreg(STM32_OTGHS_HPRT); + priv->lowspeed = ((regval & OTGHS_HPRT_PSPD_MASK) == OTGHS_HPRT_PSPD_LS); + + /* Configure control channels */ + + stm32_chan_configure(priv, priv->ep0out); + stm32_chan_configure(priv, priv->ep0in); + + /* Let the common usbhost_enumerate do all of the real work. Note that the + * FunctionAddress (USB address) is hardcoded to one. + */ + + uvdbg("Enumerate the device\n"); + priv->smstate = SMSTATE_ENUM; + ret = usbhost_enumerate(&g_usbhost.drvr, 1, &priv->class); + + /* The enumeration may fail either because of some HCD interfaces failure + * or because the device class is not supported. In either case, we just + * need to perform the disconnection operation and make ready for a new + * enumeration. + */ + + if (ret < 0) + { + /* Return to the disconnected state */ + + 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. + * funcaddr - The USB address of the function containing the endpoint that EP0 + * controls + * maxpacketsize - The maximum number of bytes that can be sent to or + * received from the endpoint in a single data packet + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ************************************************************************************/ + +static int stm32_ep0configure(FAR struct usbhost_driver_s *drvr, uint8_t funcaddr, + uint16_t maxpacketsize) +{ + FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; + + DEBUGASSERT(drvr && funcaddr < 128 && maxpacketsize < 2048); + + /* We must have exclusive access to the USB host hardware and state structures */ + + stm32_takesem(&priv->exclsem); + + /* Save the device address and EP0 max packet size */ + + priv->devaddr = funcaddr; + priv->ep0size = maxpacketsize; + + /* Configure the EP0 OUT channel */ + + priv->chan[priv->ep0out].maxpacket = maxpacketsize; + stm32_chan_configure(priv, priv->ep0out); + + /* Configure the EP0 IN channel */ + + priv->chan[priv->ep0in].maxpacket = maxpacketsize; + stm32_chan_configure(priv, priv->ep0in); + + stm32_givesem(&priv->exclsem); + return OK; +} + +/************************************************************************************ + * Name: stm32_getdevinfo + * + * Description: + * Get information about the connected device. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * devinfo - A pointer to memory provided by the caller in which to return the + * device information. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ************************************************************************************/ + +static int stm32_getdevinfo(FAR struct usbhost_driver_s *drvr, + FAR struct usbhost_devinfo_s *devinfo) +{ + FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; + + DEBUGASSERT(drvr && devinfo); + devinfo->speed = priv->lowspeed ? DEVINFO_SPEED_LOW : DEVINFO_SPEED_FULL; + 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; + FAR struct stm32_chan_s *chan; + int chidx; + int ret; + + /* Sanity check. NOTE that this method should only be called if a device is + * connected (because we need a valid low speed indication). + */ + + DEBUGASSERT(priv && epdesc && ep && priv->connected); + + /* We must have exclusive access to the USB host hardware and state structures */ + + stm32_takesem(&priv->exclsem); + + /* Allocate a host channel for the endpoint */ + + chidx = stm32_chan_alloc(priv); + if (chidx < 0) + { + udbg("Failed to allocate a host channel\n"); + ret = -ENOMEM; + goto errout; + } + + /* Decode the endpoint descriptor to initialize the channel data structures. + * Note: Here we depend on the fact that the endpoint point type is + * encoded in the same way in the endpoint descriptor as it is in the OTG + * HS hardware. + */ + + chan = &priv->chan[chidx]; + chan->epno = epdesc->addr & USB_EPNO_MASK; + chan->in = epdesc->in; + chan->eptype = epdesc->xfrtype; + chan->maxpacket = epdesc->mxpacketsize; + chan->indata1 = false; + chan->outdata1 = false; + + /* Then configure the endpoint */ + + stm32_chan_configure(priv, chidx); + + /* Return the index to the allocated channel as the endpoint "handle" */ + + *ep = (usbhost_ep_t)chidx; + ret = OK; + +errout: + 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) +{ + struct stm32_usbhost_s *priv = (struct stm32_usbhost_s *)drvr; + int chidx = (int)ep; + + DEBUGASSERT(priv && chidx < STM32_MAX_TX_FIFOS); + + /* We must have exclusive access to the USB host hardware and state structures */ + + stm32_takesem(&priv->exclsem); + + /* Halt the channel and mark the channel avaiable */ + + stm32_chan_free(priv, chidx); + + 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_OTGHS_DESCSIZE); + if (!alloc) + { + return -ENOMEM; + } + + /* Return the allocated address and size of the descriptor buffer */ + + *buffer = alloc; + *maxlen = CONFIG_STM32_OTGHS_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. + * req - Describes the request to be sent. This request must lie in memory + * created by DRVR_ALLOC. + * buffer - A buffer used for sending the request and for returning any + * responses. This buffer must be large enough to hold the length value + * in the request description. buffer must have been allocated using DRVR_ALLOC + * + * NOTE: On an IN transaction, req and buffer may refer to the same allocated + * memory. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * - Only a single class bound to a single device is supported. + * - Called from a single thread so no mutual exclusion is required. + * - Never called from an interrupt handler. + * + *******************************************************************************/ + +static int stm32_ctrlin(FAR struct usbhost_driver_s *drvr, + FAR const struct usb_ctrlreq_s *req, + FAR uint8_t *buffer) +{ + struct stm32_usbhost_s *priv = (struct stm32_usbhost_s *)drvr; + uint16_t buflen; + uint32_t start; + uint32_t elapsed; + int retries; + int ret; + + DEBUGASSERT(drvr && req); + usbhost_vtrace2(OTGHS_VTRACE2_CTRLIN, req->type, req->req); + uvdbg("type:%02x req:%02x value:%02x%02x index:%02x%02x len:%02x%02x\n", + req->type, req->req, req->value[1], req->value[0], + req->index[1], req->index[0], req->len[1], req->len[0]); + + /* Extract values from the request */ + + buflen = 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, req); + if (ret < 0) + { + usbhost_trace1(OTGHS_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, buffer, buflen); + if (ret < 0) + { + usbhost_trace1(OTGHS_TRACE1_RECVDATA, -ret); + } + } + + /* Handle the status OUT phase */ + + if (ret == OK) + { + priv->chan[priv->ep0out].outdata1 ^= true; + ret = stm32_ctrl_senddata(priv, NULL, 0); + if (ret == OK) + { + /* All success transactions exit here */ + + stm32_givesem(&priv->exclsem); + return OK; + } + + usbhost_trace1(OTGHS_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, + FAR const struct usb_ctrlreq_s *req, + FAR const uint8_t *buffer) +{ + struct stm32_usbhost_s *priv = (struct stm32_usbhost_s *)drvr; + uint16_t buflen; + uint32_t start; + uint32_t elapsed; + int retries; + int ret; + + DEBUGASSERT(drvr && req); + usbhost_vtrace2(OTGHS_VTRACE2_CTRLOUT, req->type, req->req); + uvdbg("type:%02x req:%02x value:%02x%02x index:%02x%02x len:%02x%02x\n", + req->type, req->req, req->value[1], req->value[0], + req->index[1], req->index[0], req->len[1], req->len[0]); + + /* Extract values from the request */ + + buflen = 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 */ + + /* Send the SETUP request */ + + ret = stm32_ctrl_sendsetup(priv, req); + if (ret < 0) + { + usbhost_trace1(OTGHS_TRACE1_SENDSETUP, -ret); + continue; + } + + /* Get the start time. Loop again until the timeout expires */ + + start = clock_systimer(); + do + { + /* Handle the data OUT phase (if any) */ + + if (buflen > 0) + { + /* Start DATA out transfer (only one DATA packet) */ + + priv->chan[priv->ep0out].outdata1 = true; + ret = stm32_ctrl_senddata(priv, NULL, 0); + if (ret < 0) + { + usbhost_trace1(OTGHS_TRACE1_SENDDATA, -ret); + } + } + + /* Handle the status IN phase */ + + if (ret == OK) + { + ret = stm32_ctrl_recvdata(priv, NULL, 0); + if (ret == OK) + { + /* All success transactins exit here */ + + stm32_givesem(&priv->exclsem); + return OK; + } + + usbhost_trace1(OTGHS_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 and return immediately. Only one transfer may be + * queued; Neither this method nor the ctrlin or ctrlout methods can be called + * again until this function returns. + * + * This is a blocking method; this functions will not return until the + * transfer has completed. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * ep - The IN or OUT endpoint descriptor for the device endpoint on which to + * perform the transfer. + * buffer - A buffer containing the data to be sent (OUT endpoint) or received + * (IN endpoint). buffer must have been allocated using DRVR_ALLOC + * buflen - The length of the data to be sent or received. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure: + * + * EAGAIN - If devices NAKs the transfer (or NYET or other error where + * it may be appropriate to restart the entire transaction). + * EPERM - If the endpoint stalls + * EIO - On a TX or data toggle error + * EPIPE - Overrun errors + * + * Assumptions: + * - Only a single class bound to a single device is supported. + * - Called from a single thread so no mutual exclusion is required. + * - Never called from an interrupt handler. + * + *******************************************************************************/ + +static int 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; + int ret; + + uvdbg("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_transfer(priv, chidx, buffer, buflen); + } + else + { + ret = stm32_out_transfer(priv, chidx, buffer, buflen); + } + + stm32_givesem(&priv->exclsem); + return ret; +} + +/******************************************************************************* + * 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. + * + * 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) +{ + struct stm32_usbhost_s *priv = (struct stm32_usbhost_s *)drvr; + DEBUGASSERT(priv); + + priv->class = 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_HS_GOTGINT), which indicates that the bus is stable again after the + * electrical debounce caused by the attachment of a pull-up resistor on DP + * (HS) 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_OTGHS_HPRT); + regval &= ~(OTGHS_HPRT_PENA|OTGHS_HPRT_PCDET|OTGHS_HPRT_PENCHNG|OTGHS_HPRT_POCCHNG); + regval |= OTGHS_HPRT_PRST; + stm32_putreg(STM32_OTGHS_HPRT, regval); + + up_mdelay(20); + + regval &= ~OTGHS_HPRT_PRST; + stm32_putreg(STM32_OTGHS_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 = OTGHS_GRSTCTL_TXFFLSH | txfnum; + stm32_putreg(regval, STM32_OTGHS_GRSTCTL); + + /* Wait for the FLUSH to complete */ + + for (timeout = 0; timeout < STM32_FLUSH_DELAY; timeout++) + { + regval = stm32_getreg(STM32_OTGHS_GRSTCTL); + if ((regval & OTGHS_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(OTGHS_GRSTCTL_RXFFLSH, STM32_OTGHS_GRSTCTL); + + /* Wait for the FLUSH to complete */ + + for (timeout = 0; timeout < STM32_FLUSH_DELAY; timeout++) + { + regval = stm32_getreg(STM32_OTGHS_GRSTCTL); + if ((regval & OTGHS_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_OTGHS_HPRT); + regval &= ~(OTGHS_HPRT_PENA|OTGHS_HPRT_PCDET|OTGHS_HPRT_PENCHNG|OTGHS_HPRT_POCCHNG); + + if (((regval & OTGHS_HPRT_PPWR) == 0) && state) + { + regval |= OTGHS_HPRT_PPWR; + stm32_putreg(STM32_OTGHS_HPRT, regval); + } + + if (((regval & OTGHS_HPRT_PPWR) != 0) && !state) + { + regval &= ~OTGHS_HPRT_PPWR; + stm32_putreg(STM32_OTGHS_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_OTGHS_PCGCCTL, 0); + + /* Initialize Host Configuration (HCFG) register */ + + regval = stm32_getreg(STM32_OTGHS_HCFG); + regval &= ~OTGHS_HCFG_FSLSPCS_MASK; + regval |= OTGHS_HCFG_FSLSPCS_FS48MHz; + stm32_putreg(STM32_OTGHS_HCFG, regval); + + /* Reset the host port */ + + stm32_portreset(priv); + + /* Clear the HS-/LS-only support bit in the HCFG register */ + + regval = stm32_getreg(STM32_OTGHS_HCFG); + regval &= ~OTGHS_HCFG_FSLSS; + stm32_putreg(STM32_OTGHS_HCFG, regval); + + /* Carve up FIFO memory for the Rx FIFO and the periodic and non-periodic Tx FIFOs */ + /* Configure Rx FIFO size (GRXHSIZ) */ + + stm32_putreg(STM32_OTGHS_GRXFSIZ, CONFIG_STM32_OTGHS_RXFIFO_SIZE); + offset = CONFIG_STM32_OTGHS_RXFIFO_SIZE; + + /* Setup the host non-periodic Tx FIFO size (HNPTXHSIZ) */ + + regval = (offset | (CONFIG_STM32_OTGHS_NPTXFIFO_SIZE << OTGHS_HNPTXFSIZ_NPTXFD_SHIFT)); + stm32_putreg(STM32_OTGHS_HNPTXFSIZ, regval); + offset += CONFIG_STM32_OTGHS_NPTXFIFO_SIZE; + + /* Set up the host periodic Tx fifo size register (HPTXHSIZ) */ + + regval = (offset | (CONFIG_STM32_OTGHS_PTXFIFO_SIZE << OTGHS_HPTXFSIZ_PTXFD_SHIFT)); + stm32_putreg(STM32_OTGHS_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(OTGHS_GRSTCTL_TXFNUM_HALL); + stm32_flush_rxfifo(); + + /* Clear all pending HC Interrupts */ + + for (i = 0; i < STM32_NHOST_CHANNELS; i++) + { + stm32_putreg(STM32_OTGHS_HCINT(i), 0xffffffff); + stm32_putreg(STM32_OTGHS_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) +{ + int i; + + /* Initialize the state data structure */ + + sem_init(&priv->eventsem, 0, 0); + sem_init(&priv->exclsem, 0, 1); + + priv->smstate = SMSTATE_DETACHED; + priv->ep0size = STM32_EP0_MAX_PACKET_SIZE; + priv->devaddr = STM32_DEF_DEVADDR; + priv->connected = false; + priv->lowspeed = 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]; + 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 HS serial + * transceiver: "This bit is always 1 with write-only access" + */ + + regval = stm32_getreg(STM32_OTGHS_GUSBCFG);; + regval |= OTGHS_GUSBCFG_PHYSEL; + stm32_putreg(STM32_OTGHS_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_OTGHS_GRSTCTL); + if ((regval & OTGHS_GRSTCTL_AHBIDL) != 0) + { + break; + } + } + + /* Then perform the core soft reset. */ + + stm32_putreg(STM32_OTGHS_GRSTCTL, OTGHS_GRSTCTL_CSRST); + for (timeout = 0; timeout < STM32_READY_DELAY; timeout++) + { + regval = stm32_getreg(STM32_OTGHS_GRSTCTL); + if ((regval & OTGHS_GRSTCTL_CSRST) == 0) + { + break; + } + } + + /* Wait for 3 PHY Clocks */ + + up_udelay(3); + + /* Deactivate the power down */ + + regval = (OTGHS_GCCFG_PWRDWN | OTGHS_GCCFG_VBUSASEN | OTGHS_GCCFG_VBUSBSEN); +#ifndef CONFIG_USBDEV_VBUSSENSING + regval |= OTGHS_GCCFG_NOVBUSSENS; +#endif +#ifdef CONFIG_STM32_OTGHS_SOFOUTPUT + regval |= OTGHS_GCCFG_SOFOUTEN; +#endif + stm32_putreg(STM32_OTGHS_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_OTGHS_GUSBCFG); + regval &= ~OTGHS_GUSBCFG_FDMOD; + regval |= OTGHS_GUSBCFG_FHMOD; + stm32_putreg(STM32_OTGHS_GUSBCFG, regval); + up_mdelay(50); + + /* Initialize host mode and return success */ + + stm32_host_initialize(priv); + return OK; +} + +/******************************************************************************* + * Public Functions + *******************************************************************************/ + +/******************************************************************************* + * Name: stm32_otghshost_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_otghshost_initialize(int controller) +{ + /* At present, there is only support for a single OTG HS 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 HS 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 HS 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 HS alternate function pins for DM, DP, ID, and SOF. + * + * PIN* SIGNAL DIRECTION + * ---- ----------- ---------- + * PA8 OTG_HS_SOF SOF clock output + * PA9 OTG_HS_VBUS VBUS input for device, Driven by external regulator by + * host (not an alternate function) + * PA10 OTG_HS_ID OTG ID pin (only needed in Dual mode) + * PA11 OTG_HS_DM D- I/O + * PA12 OTG_HS_DP D+ I/O + * + * *Pins may vary from device-to-device. + */ + + stm32_configgpio(GPIO_OTGHSFS_DM); + stm32_configgpio(GPIO_OTGHSFS_DP); +// stm32_configgpio(GPIO_OTGHSFS_ID); /* Only needed for OTG */ + + /* SOF output pin configuration is configurable */ + +#ifdef CONFIG_STM32_OTGHS_SOFOUTPUT + stm32_configgpio(GPIO_OTGHSFS_SOF); +#endif + + /* Initialize the USB OTG HS core */ + + stm32_hw_initialize(priv); + + /* Attach USB host controller interrupt handler */ + + if (irq_attach(STM32_IRQ_OTGHS, stm32_gint_isr) != 0) + { + usbhost_trace1(OTGHS_TRACE1_IRQATTACH, 0); + return NULL; + } + + /* Enable USB OTG HS global interrupts */ + + stm32_gint_enable(); + + /* Enable interrupts at the interrupt controller */ + + up_enable_irq(STM32_IRQ_OTGHS); + return &g_usbconn; +} + +#endif /* CONFIG_USBHOST && CONFIG_STM32_OTGHS */