diff --git a/arch/risc-v/src/mpfs/Make.defs b/arch/risc-v/src/mpfs/Make.defs index b3f8b3f20f6..e8d4c61c72a 100755 --- a/arch/risc-v/src/mpfs/Make.defs +++ b/arch/risc-v/src/mpfs/Make.defs @@ -106,3 +106,7 @@ ifeq (${CONFIG_MPFS_OPENSBI},y) CHIP_ASRCS += mpfs_opensbi_utils.S CHIP_CSRCS += mpfs_opensbi.c endif + +ifeq ($(CONFIG_USBDEV),y) +CHIP_CSRCS += mpfs_usb.c +endif diff --git a/arch/risc-v/src/mpfs/hardware/mpfs_usb.h b/arch/risc-v/src/mpfs/hardware/mpfs_usb.h new file mode 100755 index 00000000000..f217de5be8e --- /dev/null +++ b/arch/risc-v/src/mpfs/hardware/mpfs_usb.h @@ -0,0 +1,472 @@ +/**************************************************************************** + * arch/risc-v/src/mpfs/hardware/mpfs_usb.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_RISCV_SRC_MPFS_HARDWARE_MPFS_USB_H +#define __ARCH_RISCV_SRC_MPFS_HARDWARE_MPFS_USB_H + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define SOFT_RESET_REG_MASK 0x03u + +#define MPFS_USB_NENDPOINTS 4 /* 4 IN and 4 OUT endpoints */ +#define MPFS_USB_MAXPACKETSIZE(ep) 64 +#define MPFS_USB_MAXPACKETSIZE_HS(ep) 512 +#define MPFS_EP0_MAXPACKET 64 +#define EP0 0 + +#define MPFS_USB_FADDR_OFFSET 0x000 +#define MPFS_USB_POWER_OFFSET 0x001 +#define MPFS_USB_TX_IRQ_OFFSET 0x002 +#define MPFS_USB_RX_IRQ_OFFSET 0x004 +#define MPFS_USB_TX_IRQ_ENABLE_OFFSET 0x006 +#define MPFS_USB_RX_IRQ_ENABLE_OFFSET 0x008 +#define MPFS_USB_IRQ_OFFSET 0x00a +#define MPFS_USB_ENABLE_OFFSET 0x00b +#define MPFS_USB_FRAME_OFFSET 0x00c +#define MPFS_USB_INDEX_OFFSET 0x00e +#define MPFS_USB_TEST_MODE_OFFSET 0x00f + +#define MPFS_USB_INDEXED_CSR_OFFSET 0x010 +#define MPFS_USB_INDEXED_CSR_SIZE 0x10 + +#define MPFS_USB_INDEXED_CSR (MPFS_USB_BASE + MPFS_USB_INDEXED_CSR_OFFSET) + +#define MPFS_USB_INDEXED_CSR_EP0_TX_MAP_P (MPFS_USB_INDEXED_CSR + 0x00) +#define MPFS_USB_INDEXED_CSR_EP0_CSR0 (MPFS_USB_INDEXED_CSR + 0x02) +#define MPFS_USB_INDEXED_CSR_EP0_RX_MAP_P (MPFS_USB_INDEXED_CSR + 0x04) +#define MPFS_USB_INDEXED_CSR_EP0_RX_CSR (MPFS_USB_INDEXED_CSR + 0x06) +#define MPFS_USB_INDEXED_CSR_EP0_COUNT0 (MPFS_USB_INDEXED_CSR + 0x08) + +#define MPFS_USB_INDEXED_CSR_TX_MAP_P (MPFS_USB_INDEXED_CSR + 0x00) +#define MPFS_USB_INDEXED_CSR_TX_CSR (MPFS_USB_INDEXED_CSR + 0x02) +#define MPFS_USB_INDEXED_CSR_RX_MAP_P (MPFS_USB_INDEXED_CSR + 0x04) +#define MPFS_USB_INDEXED_CSR_RX_CSR (MPFS_USB_INDEXED_CSR + 0x06) +#define MPFS_USB_INDEXED_CSR_RX_COUNT (MPFS_USB_INDEXED_CSR + 0x08) + +#define MPFS_USB_FIFO_OFFSET 0x020 +#define MPFS_USB_FIFO_REG_SIZE 0x004 + +#define MPFS_USB_FIFO_MAX 16 +#define MPFS_USB_FIFO_SIZE 0x04 + +#define MPFS_USB_DEV_CTRL_OFFSET 0x060 +#define MPFS_USB_MISC_OFFSET 0x061 +#define MPFS_USB_TX_FIFO_SIZE_OFFSET 0x062 +#define MPFS_USB_RX_FIFO_SIZE_OFFSET 0x063 +#define MPFS_USB_TX_FIFO_ADDR_OFFSET 0x064 +#define MPFS_USB_RX_FIFO_ADDR_OFFSET 0x066 +#define MPFS_USB_VBUS_CSR_OFFSET 0x068 +#define MPFS_USB_HW_VERSION_OFFSET 0x06c +#define MPFS_USB_RESERVED_OFFSET 0x06e + +#define MPFS_USB_ULPI_VBUS_CTRL_OFFSET 0x070 +#define MPFS_USB_ULPI_CARKIT_CTRL_OFFSET 0x071 +#define MPFS_USB_ULPI_IRQ_MASK_OFFSET 0x072 +#define MPFS_USB_ULPI_IRQ_SRC_OFFSET 0x073 +#define MPFS_USB_ULPI_DATA_REG_OFFSET 0x074 +#define MPFS_USB_ULPI_ADDR_REG_OFFSET 0x075 +#define MPFS_USB_ULPI_CTRL_REG_OFFSET 0x076 +#define MPFS_USB_ULPI_RAW_DATA_OFFSET 0x077 +#define MPFS_USB_EP_INFO_OFFSET 0x078 +#define MPFS_USB_RAM_INFO_OFFSET 0x079 +#define MPFS_USB_LINK_INFO_OFFSET 0x07a +#define MPFS_USB_VP_LEN_OFFSET 0x07b +#define MPFS_USB_HS_EOF1_OFFSET 0x07c +#define MPFS_USB_FS_EOF1_OFFSET 0x07d +#define MPFS_USB_LS_EOF1_OFFSET 0x07e +#define MPFS_USB_SOFT_RST_OFFSET 0x07f + +#define MPFS_USB_TAR_OFFSET 0x080 +#define MFFS_USB_TAR_MAX 16 +#define MPFS_USB_TAR_SIZE 0x08 +#define MPFS_USB_TAR_TX_FUNC_ADDR_OFFSET 0x00 +#define MPFS_USB_TAR_UNUSED0_OFFSET 0x01 +#define MPFS_USB_TAR_TX_HUB_ADDR_OFFSET 0x02 +#define MPFS_USB_TAR_TX_HUB_PORT_OFFSET 0x03 +#define MPFS_USB_TAR_RX_FUNC_ADDR 0x04 +#define MPFS_USB_TAR_UNUSED1 0x05 +#define MPFS_USB_TAR_RX_HUB_ADDR 0x06 +#define MPFS_USB_TAR_RX_HUB_PORT 0x07 + +#define MPFS_USB_ENDPOINT_OFFSET 0x100 +#define MPFS_USB_ENDPOINT_MAX 16 +#define MPFS_USB_ENDPOINT_SIZE 0x10 + +#define MPFS_USB_ENDPOINT_TX_MAX_P_OFFSET 0x00 +#define MPFS_USB_ENDPOINT_TX_CSR_OFFSET 0x02 +#define MPFS_USB_ENDPOINT_RX_MAX_P_OFFSET 0x04 +#define MPFS_USB_ENDPOINT_RX_CSR_OFFSET 0x06 +#define MPFS_USB_ENDPOINT_RX_COUNT_OFFSET 0x08 +#define MPFS_USB_ENDPOINT_TX_TYPE_OFFSET 0x0a +#define MPFS_USB_ENDPOINT_TX_INTERVAL_OFFSET 0x0b +#define MPFS_USB_ENDPOINT_RX_TYPE_OFFSET 0x0c +#define MPFS_USB_ENDPOINT_RX_INTERVAL_OFFSET 0x0d +#define MPFS_USB_ENDPOINT_reserved_OFFSET 0x0e +#define MPFS_USB_ENDPOINT_FIFO_SIZE_OFFSET 0x0f + +#define MPFS_USB_DMA_CHANNEL_OFFSET 0x200 +#define MPFS_USB_DMA_CHANNEL_MAX 8 +#define MPFS_USB_DMA_CHANNEL_SIZE 0x10 +#define MPFS_USB_DMA_IRQ_OFFSET 0x00 +#define MPFS_USB_DMA_CNTL_OFFSET 0x04 +#define MPFS_USB_DMA_ADDR_OFFSET 0x08 +#define MPFS_USB_DMA_COUNT_OFFSET 0x0c + +#define MPFS_USB_reserved_EXT_OFFSET 0x280 +#define MPFS_USB_RQ_PKT_CNT_OFFSET 0x300 +#define MPFS_USB_RQ_PKT_CNT_MAX_OFFSET 16 +#define MPFS_USB_RQ_PKT_CNT_SIZE_OFFSET 0x04 + +#define MPFS_USB_RX_DPBUF_DIS_OFFSET 0x340 +#define MPFS_USB_TX_DPBUF_DIS_OFFSET 0x342 +#define MPFS_USB_C_T_UCH_OFFSET 0x344 +#define MPFS_USB_C_T_HHSRTN_OFFSET 0x346 +#define MPFS_USB_C_T_HSBT_OFFSET 0x348 + +#define MPFS_USB_POWER (MPFS_USB_BASE + MPFS_USB_POWER_OFFSET) +#define MPFS_USB_POWER_ENABLE_SUSPENDM (1 << 0) +#define MPFS_USB_POWER_SUSPEND_MODE (1 << 1) +#define MPFS_USB_POWER_RESUME_SIGNAL (1 << 2) +#define MPFS_USB_POWER_BUS_RESET_SIGNAL (1 << 3) +#define MPFS_USB_POWER_HS_MODE (1 << 4) +#define MPFS_USB_POWER_ENABLE_HS (1 << 5) +#define MPFS_USB_POWER_SOFT_CONN (1 << 6) +#define MPFS_USB_POWER_ISO_UPDATE (1 << 7) + +#define MPFS_USB_FADDR (MPFS_USB_BASE + MPFS_USB_FADDR_OFFSET) +#define MPFS_USB_TX_IRQ (MPFS_USB_BASE + MPFS_USB_TX_IRQ_OFFSET) +#define MPFS_USB_RX_IRQ (MPFS_USB_BASE + MPFS_USB_RX_IRQ_OFFSET) +#define MPFS_USB_TX_IRQ_ENABLE (MPFS_USB_BASE + MPFS_USB_TX_IRQ_ENABLE_OFFSET) +#define MPFS_USB_RX_IRQ_ENABLE (MPFS_USB_BASE + MPFS_USB_RX_IRQ_ENABLE_OFFSET) +#define MPFS_USB_IRQ (MPFS_USB_BASE + MPFS_USB_IRQ_OFFSET) +#define MPFS_USB_ENABLE (MPFS_USB_BASE + MPFS_USB_ENABLE_OFFSET) +#define MPFS_USB_FRAME (MPFS_USB_BASE + MPFS_USB_FRAME_OFFSET) +#define MPFS_USB_INDEX (MPFS_USB_BASE + MPFS_USB_INDEX_OFFSET) +#define MPFS_USB_TEST_MODE (MPFS_USB_BASE + MPFS_USB_TEST_MODE_OFFSET) +#define MPFS_USB_DEV_CTRL (MPFS_USB_BASE + MPFS_USB_DEV_CTRL_OFFSET) +#define MPFS_USB_TAR(n) (MPFS_USB_BASE + MPFS_USB_TAR_OFFSET + MPFS_USB_TAR_SIZE * n) +#define MPFS_USB_TX_FIFO_SIZE (MPFS_USB_BASE + MPFS_USB_TX_FIFO_SIZE_OFFSET) +#define MPFS_USB_RX_FIFO_SIZE (MPFS_USB_BASE + MPFS_USB_RX_FIFO_SIZE_OFFSET) +#define MPFS_USB_FIFO(n) (MPFS_USB_BASE + MPFS_USB_FIFO_OFFSET + MPFS_USB_FIFO_SIZE * n) +#define MPFS_USB_TX_FIFO_ADDR (MPFS_USB_BASE + MPFS_USB_TX_FIFO_ADDR_OFFSET) +#define MPFS_USB_RX_FIFO_ADDR (MPFS_USB_BASE + MPFS_USB_RX_FIFO_ADDR_OFFSET) +#define MPFS_USB_SOFT_RST (MPFS_USB_BASE + MPFS_USB_SOFT_RST_OFFSET) +#define MPFS_USB_C_T_HSBT (MPFS_USB_BASE + MPFS_USB_C_T_HSBT_OFFSET) +#define MPFS_USB_ENDPOINT(n) (MPFS_USB_BASE + MPFS_USB_ENDPOINT_OFFSET + MPFS_USB_ENDPOINT_SIZE * n) +#define MPFS_USB_RX_DPBUF_DIS (MPFS_USB_BASE + MPFS_USB_RX_DPBUF_DIS_OFFSET) +#define MPFS_USB_TX_DPBUF_DIS (MPFS_USB_BASE + MPFS_USB_TX_DPBUF_DIS_OFFSET) + +#define MPFS_USB_DMA_CHANNEL(n) (MPFS_USB_BASE + MPFS_USB_DMA_CHANNEL_OFFSET + MPFS_USB_DMA_CHANNEL_SIZE * n) + +#define TXCSRL_REG_EPN_TX_PKT_RDY_MASK 0x0001u +#define TXCSRL_REG_EPN_TX_FIFO_NE_MASK 0x0002u +#define TXCSRL_REG_EPN_UNDERRUN_MASK 0x0004u +#define TXCSRL_REG_EPN_FLUSH_FIFO_MASK 0x0008u +#define TXCSRL_REG_EPN_SEND_STALL_MASK 0x0010u +#define TXCSRL_REG_EPN_STALL_SENT_MASK 0x0020u +#define TXCSRL_REG_EPN_CLR_DATA_TOG_MASK 0x0040u +#define TXCSRL_REG_EPN_ISO_INCOMP_TX_MASK 0x0080u + +/**************************************************************************** + * Endpoint RXCSRL register bit masks + ****************************************************************************/ + +#define RXCSRL_REG_EPN_RX_PKT_RDY_MASK 0x0001u +#define RXCSRL_REG_EPN_RX_FIFO_FULL_MASK 0x0002u +#define RXCSRL_REG_EPN_OVERRUN_MASK 0x0004u +#define RXCSRL_REG_EPN_DATA_ERR_MASK 0x0008u +#define RXCSRL_REG_EPN_FLUSH_FIFO_MASK 0x0010u +#define RXCSRL_REG_EPN_SEND_STALL_MASK 0x0020u +#define RXCSRL_REG_EPN_STALL_SENT_MASK 0x0040u +#define RXCSRL_REG_EPN_CLR_DAT_TOG_MASK 0x0080u + +/**************************************************************************** + * CSR0H bit masks (peripheral mode) + ****************************************************************************/ + +#define CSR0H_DEV_FLUSH_FIFO_MASK 0x0100u + +/**************************************************************************** + * TX_IRQ_ENABLE register masks + ****************************************************************************/ + +#define TX_IRQ_ENABLE_REG_CEP_MASK 0x0001u + +/**************************************************************************** + * CSR0L bit masks (peripheral mode) + ****************************************************************************/ + +#define CSR0L_DEV_RX_PKT_RDY_MASK 0x0001u +#define CSR0L_DEV_TX_PKT_RDY_MASK 0x0002u +#define CSR0L_DEV_STALL_SENT_MASK 0x0004u +#define CSR0L_DEV_DATA_END_MASK 0x0008u +#define CSR0L_DEV_SETUP_END_MASK 0x0010u +#define CSR0L_DEV_SEND_STALL_MASK 0x0020u +#define CSR0L_DEV_SERVICED_RX_PKT_RDY_MASK 0x0040u +#define CSR0L_DEV_SERVICED_SETUP_END_MASK 0x0080u + +/**************************************************************************** + * Endpoint TXMAXP register bit masks + ****************************************************************************/ + +#define TX_MAX_P_REG_NUM_USB_PKT_SHIFT 11u + +/**************************************************************************** + * Endpoint TXCSRH register bit masks + ****************************************************************************/ + +#define TXCSRH_REG_EPN_DMA_MODE_MASK 0x0400u +#define TXCSRH_REG_EPN_FRC_DAT_TOG_MASK 0x0800u +#define TXCSRH_REG_EPN_ENABLE_DMA_MASK 0x1000u +#define TXCSRH_REG_EPN_TXRX_MODE_MASK 0x2000u +#define TXCSRH_REG_EPN_ENABLE_ISO_MASK 0x4000u +#define TXCSRH_REG_EPN_ENABLE_AUTOSET_MASK 0x8000u + +/**************************************************************************** + * Endpoint DMA_CNTL register bit masks + ****************************************************************************/ + +#define DMA_CNTL_REG_START_XFR_MASK 0x00000001u +#define DMA_CNTL_REG_DMA_DIR_MASK 0x00000002u +#define DMA_CNTL_REG_DMA_MODE_MASK 0x00000004u +#define DMA_CNTL_REG_ENABLE_DMA_IRQ_MASK 0x00000008u +#define DMA_CNTL_REG_DMA_EP_NUM_MASK 0x000000F0u +#define DMA_CNTL_REG_DMA_BUS_ERR_MASK 0x00000100u +#define DMA_CNTL_REG_DMA_BURST_MODE_MASK 0x00000600u + +#define DMA_CNTL_REG_DMA_BURST_MODE_SHIFT 9u +#define DMA_CNTL_REG_DMA_EP_NUM_SHIFT 4u +#define DMA_CNTL_REG_DMA_DIR_SHIFT 1u +#define DMA_CNTL_REG_DMA_MODE_SHIFT 2u + +/**************************************************************************** + * Endpoint RXCSRH register bit masks + ****************************************************************************/ + +#define RXCSRL_REG_EPN_RX_ISO_INCOMP 0x0100u +#define RXCSRL_REG_EPN_DMA_MODE_MASK 0x0800u +#define RXCSRL_REG_EPN_ISO_PID_ERR_MASK 0x1000u +#define RXCSRL_REG_EPN_BI_DIS_NYET_MASK 0x1000u +#define RXCSRL_REG_EPN_ENABLE_DMA_MASK 0x2000u +#define RXCSRL_REG_EPN_ENABLE_ISO_MASK 0x4000u +#define RXCSRL_REG_EPN_ENABLE_AUTOCLR_MASK 0x8000u + +/**************************************************************************** + * Power register + ****************************************************************************/ + +#define POWER_REG_ENABLE_SUSPENDM_MASK 0x01u +#define POWER_REG_SUSPEND_MODE_MASK 0x02u +#define POWER_REG_RESUME_SIGNAL_MASK 0x04u +#define POWER_REG_BUS_RESET_SIGNAL_MASK 0x08u +#define POWER_REG_HS_MODE_MASK 0x10u +#define POWER_REG_ENABLE_HS_MASK 0x20u +#define POWER_REG_SOFT_CONN_MASK 0x40u +#define POWER_REG_ISO_UPDATE_MASK 0x80u + +/**************************************************************************** + * TXType register bit masks + ****************************************************************************/ + +#define TXTYPE_HOST_TARGET_EP_NUM_MASK 0x0fu +#define TXTYPE_HOST_TARGET_EP_PROTOCOL_MASK 0x30u +#define TXTYPE_HOST_TARGET_EP_SPEED_MASK 0xc0u + +#define TXTYPE_HOST_TARGET_EP_NUM_SHIFT 0u +#define TXTYPE_HOST_TARGET_EP_PROTOCOL_SHIFT 4u +#define TXTYPE_HOST_TARGET_EP_SPEED_SHIFT 6u + +/**************************************************************************** + * TXINTERVAL register bit masks + ****************************************************************************/ + +#define TXINTERVAL_HOST_REG_MASK 0x00 + +/**************************************************************************** + * RXType register bit masks + ****************************************************************************/ + +#define RXTYPE_HOST_TARGET_EP_NUM_MASK 0x0fu +#define RXTYPE_HOST_TARGET_EP_PROTOCOL_MASK 0x30u +#define RXTYPE_HOST_TARGET_EP_SPEED_MASK 0xc0u + +#define RXTYPE_HOST_TARGET_EP_NUM_SHIFT 0u +#define RXTYPE_HOST_TARGET_EP_PROTOCOL_SHIFT 4u +#define RXTYPE_HOST_TARGET_EP_SPEED_SHIFT 6u + +/**************************************************************************** + * Endpoint RXCSRL register bit masks + ****************************************************************************/ + +#define RXCSRL_HOST_EPN_RX_PKT_RDY_MASK 0x0001u +#define RXCSRL_HOST_EPN_RX_FIFO_FULL_MASK 0x0002u +#define RXCSRL_HOST_EPN_RESPONSE_ERR_MASK 0x0004u +#define RXCSRL_HOST_EPN_NAK_TIMEOUT_ERR_MASK 0x0008u +#define RXCSRL_HOST_EPN_FLUSH_FIFO_MASK 0x0010u +#define RXCSRL_HOST_EPN_IN_PKT_REQ_MASK 0x0020u +#define RXCSRL_HOST_EPN_STALL_RCVD_MASK 0x0040u +#define RXCSRL_HOST_EPN_CLR_DATA_TOG_MASK 0x0080u + +/**************************************************************************** + * CSR0L bit masks + ****************************************************************************/ + +#define CSR0L_HOST_RX_PKT_RDY_MASK 0x0001u +#define CSR0L_HOST_TX_PKT_RDY_MASK 0x0002u +#define CSR0L_HOST_STALL_RCVD_MASK 0x0004u +#define CSR0L_HOST_SETUP_PKT_MASK 0x0008u +#define CSR0L_HOST_RETRY_ERR_MASK 0x0010u +#define CSR0L_HOST_IN_PKT_REQ_MASK 0x0020u +#define CSR0L_HOST_STATUS_PKT_MASK 0x0040u +#define CSR0L_HOST_NAK_TIMEOUT_MASK 0x0080u + +/**************************************************************************** + * CSR0H bit masks + ****************************************************************************/ + +#define CSR0H_HOST_FLUSH_FIFO_MASK 0x0100u /* Self Clearing */ +#define CSR0H_HOST_DATA_TOG_MASK 0x0200u +#define CSR0H_HOST_DATA_TOG_WE_MASK 0x0400u /* Self Clearing */ +#define CSR0H_HOST_DISABLE_PING_MASK 0x0800u + +/**************************************************************************** + * INTRUSBE register - USB interrupts masks + ****************************************************************************/ + +#define SUSPEND_IRQ_MASK 0x01u +#define RESUME_IRQ_MASK 0x02u +#define RESET_IRQ_MASK 0x04u /* Device mode */ +#define BABBLE_IRQ_MASK 0x04u /* Host mode */ +#define SOF_IRQ_MASK 0x08u +#define CONNECT_IRQ_MASK 0x10u +#define DISCONNECT_IRQ_MASK 0x20u +#define SESSION_REQUEST_IRQ_MASK 0x40u +#define VBUS_ERROR_IRQ_MASK 0x80u + +/**************************************************************************** + * DevCTL register bit masks + ****************************************************************************/ + +#define DEV_CTRL_SESSION_MASK 0x01u +#define DEV_CTRL_HOST_REQ_MASK 0x02u +#define DEV_CTRL_HOST_MODE_MASK 0x04u +#define DEV_CTRL_VBUS_MASK 0x18u +#define DEV_CTRL_LS_DEV_MASK 0x20u +#define DEV_CTRL_FS_DEV_MASK 0x40u +#define DEV_CTRL_B_DEVICE_MASK 0x80u + +#define VBUS_BELOW_SESSION_END 0x00u +#define VBUS_ABOVE_SESSION_END 0x08u +#define VBUS_ABOVE_AVALID 0x10u +#define VBUS_ABOVE_VBUS_VALID 0x18u + +/**************************************************************************** + * Endpoint TXMAXP register bit masks + ****************************************************************************/ + +#define RX_MAX_P_REG_NUM_USB_PKT_SHIFT 11u + +struct mpfs_req_s +{ + struct usbdev_req_s req; /* Standard USB request */ + struct mpfs_req_s *flink; /* Supports a singly linked list */ + uint16_t inflight; /* Number of TX bytes tansmitting or + * number of RX bytes we are waiting */ +}; + +union wb_u +{ + uint16_t w; + uint8_t b[2]; +}; + +/* The head of a queue of requests */ + +struct mpfs_rqhead_s +{ + struct mpfs_req_s *head; /* Requests are added to the head of the list */ + struct mpfs_req_s *tail; /* Requests are removed from the tail of the list */ +}; + +struct mpfs_ep_s +{ + struct usbdev_ep_s ep; /* Standard endpoint structure */ + + struct mpfs_usbdev_s *dev; /* Reference to private driver data */ + struct mpfs_rqhead_s reqq; /* Read/write request queue */ + struct mpfs_rqhead_s pendq; /* Write requests pending stall sent */ + struct usbdev_epdesc_s *descb[2]; /* Pointers to this endpoint descriptors */ + volatile uint8_t epstate; /* State of the endpoint (see enum mpfs_epstate_e) */ + uint8_t stalled:1; /* true: Endpoint is stalled */ + uint8_t pending:1; /* true: IN Endpoint stall is pending */ + uint8_t halted:1; /* true: Endpoint feature halted */ + uint8_t zlpsent:1; /* Zero length packet has been sent */ + uint8_t txbusy:1; /* Write request queue is busy */ + uint8_t rxactive:1; /* read request is active (for top of queue) */ +}; + +/* Device Endpoint Descriptor. See USBDEV_* bit definitions above. */ + +struct usbdev_epdesc_s +{ + uintptr_t addr; /* Address of Data buffer (Both banks) */ + uint32_t pktsize; /* Packet Size */ +}; + +struct mpfs_usbdev_s +{ + struct usbdev_s usbdev; + + /* The bound device class driver */ + + struct usbdevclass_driver_s *driver; + + /* USB-specific fields */ + + struct usb_ctrlreq_s ctrl; /* Last EP0 request */ + uint8_t devstate; /* State of the device (see enum mpfs_devstate_e) */ + uint8_t prevstate; /* Previous state of the device before SUSPEND */ + uint8_t devaddr; /* Assigned device address */ + uint8_t selfpowered:1; /* 1: Device is self powered */ + uint16_t epavail; /* Bitset of available endpoints */ + + /* The endpoint list */ + + aligned_data(4) struct mpfs_ep_s eplist[MPFS_USB_NENDPOINTS]; + + /* Endpoint descriptors 2 banks for each endpoint */ + + aligned_data(4) + struct usbdev_epdesc_s ep_descriptors[MPFS_USB_NENDPOINTS * 2]; + + /* EP0 data buffer. For data that is included in an EP0 SETUP OUT + * transaction. In this case, no request is in place from the class + * driver and the incoming data is caught in this buffer. The size + * of valid data in the buffer is given by ctrlreg.len[]. For the + * case of EP0 SETUP IN transaction, the normal request mechanism is + * used and the class driver provides the buffering. + */ + + aligned_data(4) uint8_t ep0out[MPFS_EP0_MAXPACKET]; +}; + +#endif /* __ARCH_RISCV_SRC_MPFS_HARDWARE_MPFS_USB_H */ diff --git a/arch/risc-v/src/mpfs/mpfs_usb.c b/arch/risc-v/src/mpfs/mpfs_usb.c new file mode 100755 index 00000000000..95d5e26471b --- /dev/null +++ b/arch/risc-v/src/mpfs/mpfs_usb.c @@ -0,0 +1,3770 @@ +/**************************************************************************** + * arch/risc-v/src/mpfs/mpfs_usb.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include "hardware/mpfs_usb.h" +#include "riscv_arch.h" +#include "chip.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* USB trace error codes */ + +#define MPFS_TRACEERR_ALLOCFAIL 0x0001 +#define MPFS_TRACEERR_BADCLEARFEATURE 0x0002 +#define MPFS_TRACEERR_BADDEVGETSTATUS 0x0003 +#define MPFS_TRACEERR_BADEPGETSTATUS 0x0004 +#define MPFS_TRACEERR_BADEPNO 0x0006 +#define MPFS_TRACEERR_BADEPTYPE 0x0007 +#define MPFS_TRACEERR_BADGETCONFIG 0x0008 +#define MPFS_TRACEERR_BADGETSTATUS 0x0009 +#define MPFS_TRACEERR_BADSETADDRESS 0x000a +#define MPFS_TRACEERR_BADSETCONFIG 0x000b +#define MPFS_TRACEERR_BADSETFEATURE 0x000c +#define MPFS_TRACEERR_BINDFAILED 0x000d +#define MPFS_TRACEERR_DISPATCHSTALL 0x000e +#define MPFS_TRACEERR_EP0SETUPSTALLED 0x000f +#define MPFS_TRACEERR_EPOUTNULLPACKET 0x0010 +#define MPFS_TRACEERR_INVALIDCTRLREQ 0x0011 +#define MPFS_TRACEERR_IRQREGISTRATION 0x0012 +#define MPFS_TRACEERR_TXCOMPERR 0x0013 +#define MPFS_TRACEERR_INVALID_EP0_STATE 0x0014 + +/* USB trace interrupt codes */ + +#define MPFS_TRACEINTID_INTERRUPT 0x0001 +#define MPFS_TRACEINTID_EP_TX_IRQ 0x0002 +#define MPFS_TRACEINTID_EP_RX_IRQ 0x0003 +#define MPFS_TRACEINTID_EP_RX_CSR 0x0004 +#define MPFS_TRACEINTID_EP_RX_COUNT 0x0005 +#define MPFS_TRACEINTID_EP_TX_CSR 0x0006 +#define MPFS_TRACEINTID_EP0_CSR0 0x0007 +#define MPFS_TRACEINTID_EP0_COUNT0 0x0008 +#define MPFS_TRACEINTID_EP0SETUPSETADDRESS 0x0009 +#define MPFS_TRACEINTID_GETSTATUS 0x000a +#define MPFS_TRACEINTID_DEVGETSTATUS 0x000b +#define MPFS_TRACEINTID_IFGETSTATUS 0x000c +#define MPFS_TRACEINTID_CLEARFEATURE 0x000d +#define MPFS_TRACEINTID_SETFEATURE 0x000e +#define MPFS_TRACEINTID_GETCONFIG 0x000f +#define MPFS_TRACEINTID_SETCONFIG 0x0010 +#define MPFS_TRACEINTID_GETSETIF 0x0011 +#define MPFS_TRACEINTID_SYNCHFRAME 0x0012 +#define MPFS_TRACEINTID_DISPATCH 0x0013 +#define MPFS_TRACEINTID_EP0_STALLSENT 0x0014 +#define MPFS_TRACEINTID_DATA_END 0x0015 + +/* USB PMP configuration registers */ + +#define MPFS_PMPCFG_USB_0 (MPFS_MPUCFG_BASE + 0x600) +#define MPFS_PMPCFG_USB_1 (MPFS_MPUCFG_BASE + 0x608) +#define MPFS_PMPCFG_USB_2 (MPFS_MPUCFG_BASE + 0x610) +#define MPFS_PMPCFG_USB_3 (MPFS_MPUCFG_BASE + 0x618) + +/* IOMUX registers */ + +#define MPFS_SYSREG_IOMUX3 (MPFS_SYSREG_BASE + \ + MPFS_SYSREG_IOMUX3_CR_OFFSET) +#define MPFS_SYSREG_IOMUX4 (MPFS_SYSREG_BASE + \ + MPFS_SYSREG_IOMUX4_CR_OFFSET) + +#define MPFS_SYSREG_B2_CFG (MPFS_SYSREG_BASE + \ + MPFS_SYSREG_MSSIO_BANK2_CFG_CR) + +#define MPFS_SYSREG_B2_0_1 (MPFS_SYSREG_BASE + \ + MPFS_SYSREG_MSSIO_BANK2_IO_CFG_0_1_CR_OFFSET) +#define MPFS_SYSREG_B2_2_3 (MPFS_SYSREG_BASE + \ + MPFS_SYSREG_MSSIO_BANK2_IO_CFG_2_3_CR_OFFSET) +#define MPFS_SYSREG_B2_4_5 (MPFS_SYSREG_BASE + \ + MPFS_SYSREG_MSSIO_BANK2_IO_CFG_4_5_CR_OFFSET) +#define MPFS_SYSREG_B2_6_7 (MPFS_SYSREG_BASE + \ + MPFS_SYSREG_MSSIO_BANK2_IO_CFG_6_7_CR_OFFSET) +#define MPFS_SYSREG_B2_8_9 (MPFS_SYSREG_BASE + \ + MPFS_SYSREG_MSSIO_BANK2_IO_CFG_8_9_CR_OFFSET) +#define MPFS_SYSREG_B2_10_11 (MPFS_SYSREG_BASE + \ + MPFS_SYSREG_MSSIO_BANK2_IO_CFG_10_11_CR_OFFSET) +#define MPFS_SYSREG_B2_12_13 (MPFS_SYSREG_BASE + \ + MPFS_SYSREG_MSSIO_BANK2_IO_CFG_12_13_CR_OFFSET) +#define MPFS_SYSREG_B2_14_15 (MPFS_SYSREG_BASE + \ + MPFS_SYSREG_MSSIO_BANK2_IO_CFG_14_15_CR_OFFSET) +#define MPFS_SYSREG_B2_16_17 (MPFS_SYSREG_BASE + \ + MPFS_SYSREG_MSSIO_BANK2_IO_CFG_16_17_CR_OFFSET) +#define MPFS_SYSREG_B2_18_19 (MPFS_SYSREG_BASE + \ + MPFS_SYSREG_MSSIO_BANK2_IO_CFG_18_19_CR_OFFSET) +#define MPFS_SYSREG_B2_20_21 (MPFS_SYSREG_BASE + \ + MPFS_SYSREG_MSSIO_BANK2_IO_CFG_20_21_CR_OFFSET) +#define MPFS_SYSREG_B2_22_23 (MPFS_SYSREG_BASE + \ + MPFS_SYSREG_MSSIO_BANK2_IO_CFG_22_23_CR_OFFSET) + +/* Reset and clock control registers */ + +#define MPFS_SYSREG_SOFT_RESET_CR (MPFS_SYSREG_BASE + \ + MPFS_SYSREG_SOFT_RESET_CR_OFFSET) +#define MPFS_SYSREG_SUBBLK_CLOCK_CR (MPFS_SYSREG_BASE + \ + MPFS_SYSREG_SUBBLK_CLOCK_CR_OFFSET) + +#ifdef CONFIG_ENDIAN_BIG +# define LSB 1 +# define MSB 0 +#else +# define LSB 0 +# define MSB 1 +#endif + +#define MPFS_NUM_USB_PKT 1 +#define MPFS_MIN_EP_FIFO_SIZE 8 +#define MPFS_USB_REG_MAX 0x2000 + +/* Request queue operations *************************************************/ + +#define mpfs_rqempty(q) ((q)->head == NULL) +#define mpfs_rqpeek(q) ((q)->head) + +#define MPFS_EPSET_ALL (0xff) /* All endpoints */ +#define MPFS_EPSET_NOTEP0 (0xfe) /* All endpoints except EP0 */ +#define MPFS_EP_BIT(ep) (1 << (ep)) +#define MPFS_MAX_MULTIPACKET_SIZE (0x3fff) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +enum mpfs_epstate_e +{ + USB_EPSTATE_DISABLED = 0, /* Endpoint is disabled */ + USB_EPSTATE_STALLED, /* Endpoint is stalled */ + USB_EPSTATE_IDLE, /* Endpoint is idle */ + USB_EPSTATE_SENDING, /* Endpoint is sending data */ + USB_EPSTATE_RXSTOPPED, /* EP is stopped waiting for a read request */ + USB_EPSTATE_EP0DATAOUT, /* Endpoint 0 is receiving SETUP OUT data */ + USB_EPSTATE_EP0STATUSIN, /* Endpoint 0 is sending SETUP status */ + USB_EPSTATE_EP0ADDRESS /* Address change is pending completion */ +}; + +/* Device states */ + +enum mpfs_devstate_e +{ + USB_DEVSTATE_SUSPENDED = 0, /* The device is currently suspended */ + USB_DEVSTATE_POWERED, /* Host is powered through the USB cable */ + USB_DEVSTATE_DEFAULT, /* Device has been reset */ + USB_DEVSTATE_ADDRESSED, /* The device has an address on the bus */ + USB_DEVSTATE_CONFIGURED /* A valid configuration has been selected. */ +}; + +/* The result of EP0 SETUP */ + +enum mpfs_ep0setup_e +{ + USB_EP0SETUP_SUCCESS = 0, /* The SETUP was handled without incident */ + USB_EP0SETUP_DISPATCHED, /* The SETUP was forwarded */ + USB_EP0SETUP_ADDRESS, /* A new device address is pending */ + USB_EP0SETUP_STALL /* An error occurred */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int mpfs_ep_configure(struct usbdev_ep_s *ep, + const struct usb_epdesc_s *desc, bool last); +static int mpfs_ep_disable(struct usbdev_ep_s *ep); +static struct usbdev_req_s * + mpfs_ep_allocreq(struct usbdev_ep_s *ep); +#ifdef CONFIG_USBDEV_DMA +static void *mpfs_ep_allocbuffer(struct usbdev_ep_s *ep, uint16_t nbytes); +static void mpfs_ep_freebuffer(struct usbdev_ep_s *ep, void *buf); +#endif +static void mpfs_ep_freereq(struct usbdev_ep_s *ep, + struct usbdev_req_s *); +static int mpfs_ep_submit(struct usbdev_ep_s *ep, + struct usbdev_req_s *req); +static int mpfs_ep_cancel(struct usbdev_ep_s *ep, + struct usbdev_req_s *req); +static int mpfs_ep_stallresume(struct usbdev_ep_s *ep, bool resume); + +/* USB device controller operations */ + +static struct usbdev_ep_s * + mpfs_allocep(struct usbdev_s *dev, uint8_t epno, bool in, + uint8_t eptype); +static void mpfs_freeep(struct usbdev_s *dev, struct usbdev_ep_s *ep); +static int mpfs_getframe(struct usbdev_s *dev); +static int mpfs_wakeup(struct usbdev_s *dev); +static int mpfs_selfpowered(struct usbdev_s *dev, bool selfpowered); +static int mpfs_pullup(struct usbdev_s *dev, bool enable); + +/* Interrupt level processing */ + +static void mpfs_ep0_ctrlread(struct mpfs_usbdev_s *priv); +static void mpfs_ep0_wrstatus(struct mpfs_usbdev_s *priv, + const uint8_t *buffer, size_t buflen); +static void mpfs_ep0_dispatch(struct mpfs_usbdev_s *priv); +static void mpfs_setdevaddr(struct mpfs_usbdev_s *priv, uint8_t value); +static void mpfs_ep0_setup(struct mpfs_usbdev_s *priv); +static int mpfs_usb_interrupt(int irq, void *context, void *arg); + +static void mpfs_epset_reset(struct mpfs_usbdev_s *priv, uint16_t epset); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct mpfs_usbdev_s g_usbd; + +static const struct usbdev_epops_s g_epops = +{ + .configure = mpfs_ep_configure, + .disable = mpfs_ep_disable, + .allocreq = mpfs_ep_allocreq, + .freereq = mpfs_ep_freereq, +#ifdef CONFIG_USBDEV_DMA + .allocbuffer = mpfs_ep_allocbuffer, + .freebuffer = mpfs_ep_freebuffer, +#endif + .submit = mpfs_ep_submit, + .cancel = mpfs_ep_cancel, + .stall = mpfs_ep_stallresume, +}; + +static const struct usbdev_ops_s g_devops = +{ + .allocep = mpfs_allocep, + .freeep = mpfs_freeep, + .getframe = mpfs_getframe, + .wakeup = mpfs_wakeup, + .selfpowered = mpfs_selfpowered, + .pullup = mpfs_pullup, +}; + +/* This describes the endpoint 0 */ + +static const struct usb_epdesc_s g_ep0desc = +{ + .len = USB_SIZEOF_EPDESC, + .type = USB_DESC_TYPE_ENDPOINT, + .addr = EP0, + .attr = USB_EP_ATTR_XFER_CONTROL, + .mxpacketsize = + { + 64, 0 + }, + .interval = 0 +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mpfs_modifyreg16 + * + * Description: + * Atomically modify the specified bits in the memory mapped register. + * This also checks the addr range is valid. + * + * Input Parameters: + * addr - Address to access + * clearbits - Bits to clear + * setbits - Bits to set + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_modifyreg16(uintptr_t addr, uint16_t clearbits, + uint16_t setbits) +{ + irqstate_t flags; + uint16_t regval; + + DEBUGASSERT((addr >= MPFS_USB_BASE) && addr < (MPFS_USB_BASE + + MPFS_USB_REG_MAX)); + + flags = spin_lock_irqsave(NULL); + regval = getreg16(addr); + regval &= ~clearbits; + regval |= setbits; + putreg16(regval, addr); + spin_unlock_irqrestore(NULL, flags); +} + +/**************************************************************************** + * Register Operations + ****************************************************************************/ + +/**************************************************************************** + * Name: mpfs_putreg32 + * + * Description: + * Set the contents of a 32-bit register. This helper wrapper checks + * the range before accessing the address. + * + * Input Parameters: + * regval - Value to store + * regaddr - Address where the regval is stored + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void mpfs_putreg32(uint32_t regval, uintptr_t regaddr) +{ + DEBUGASSERT((regaddr >= MPFS_USB_BASE) && regaddr < (MPFS_USB_BASE + + MPFS_USB_REG_MAX)); + + putreg32(regval, regaddr); +} + +/**************************************************************************** + * Name: mpfs_putreg16 + * + * Description: + * Set the contents of a 16-bit register. This helper wrapper checks + * the range before accessing the address. + * + * Input Parameters: + * regval - Value to store + * regaddr - Address where the regval is stored + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void mpfs_putreg16(uint16_t regval, uintptr_t regaddr) +{ + DEBUGASSERT((regaddr >= MPFS_USB_BASE) && regaddr < (MPFS_USB_BASE + + MPFS_USB_REG_MAX)); + + putreg16(regval, regaddr); +} + +/**************************************************************************** + * Name: mpfs_putreg8 + * + * Description: + * Set the contents of an 8-bit register. This helper wrapper checks + * the range before accessing the address. + * + * Input Parameters: + * regval - Value to store + * regaddr - Address where the regval is stored + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void mpfs_putreg8(uint8_t regval, uintptr_t regaddr) +{ + DEBUGASSERT((regaddr >= MPFS_USB_BASE) && regaddr < (MPFS_USB_BASE + + MPFS_USB_REG_MAX)); + + putreg8(regval, regaddr); +} + +/**************************************************************************** + * Name: mpfs_enableclk + * + * Description: + * Enables the USB clock. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_enableclk(void) +{ + modifyreg32(MPFS_SYSREG_SUBBLK_CLOCK_CR, 0, SYSREG_SUBBLK_CLOCK_CR_USB); +} + +/**************************************************************************** + * Name: mpfs_disableclk + * + * Description: + * Disables the USB clock. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_disableclk(void) +{ + modifyreg32(MPFS_SYSREG_SUBBLK_CLOCK_CR, SYSREG_SUBBLK_CLOCK_CR_USB, 0); +} + +/**************************************************************************** + * Request Helpers + ****************************************************************************/ + +/**************************************************************************** + * Name: mpfs_req_dequeue + * + * Description: + * Removes an entry from the linked list. + * + * Input Parameters: + * queue - Head of the linked list + * + * Returned Value: + * The entry or NULL is none in the list + * + ****************************************************************************/ + +static struct mpfs_req_s *mpfs_req_dequeue(struct mpfs_rqhead_s *queue) +{ + struct mpfs_req_s *ret = queue->head; + + if (ret) + { + queue->head = ret->flink; + if (!queue->head) + { + queue->tail = NULL; + } + + ret->flink = NULL; + } + + return ret; +} + +/**************************************************************************** + * Name: mpfs_req_enqueue + * + * Description: + * Add an entry to the linked list. + * + * Input Parameters: + * queue - Head of the linked list + * req - The entry to be added + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_req_enqueue(struct mpfs_rqhead_s *queue, + struct mpfs_req_s *req) +{ + req->flink = NULL; + + if (!queue->head) + { + queue->head = req; + queue->tail = req; + } + else + { + queue->tail->flink = req; + queue->tail = req; + } +} + +/**************************************************************************** + * Name: mpfs_req_complete + * + * Description: + * Add an entry to the linked list. + * + * Input Parameters: + * privep - Endpoint private data + * result - Status of the completion process + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_req_complete(struct mpfs_ep_s *privep, int16_t result) +{ + struct mpfs_req_s *privreq; + irqstate_t flags; + + /* Remove the completed request at the head of the endpoint request list */ + + flags = enter_critical_section(); + privreq = mpfs_req_dequeue(&privep->reqq); + leave_critical_section(flags); + + if (privreq) + { + /* Save the result in the request structure */ + + privreq->req.result = result; + + /* Callback to the request completion handler */ + + privreq->flink = NULL; + privreq->req.callback(&privep->ep, &privreq->req); + + /* Mark the endpoint ready for the next transmission */ + + privep->epstate = USB_EPSTATE_IDLE; + privep->zlpsent = false; + } +} + +/**************************************************************************** + * Name: mpfs_req_cancel + * + * Description: + * Cancel all entries of an endpoint queue + * + * Input Parameters: + * privep - Endpoint private data + * result - Status of the completion process + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_req_cancel(struct mpfs_ep_s *privep, int16_t result) +{ + /* Complete every queued request with the specified status */ + + while (!mpfs_rqempty(&privep->reqq)) + { + usbtrace(TRACE_COMPLETE(USB_EPNO(privep->ep.eplog)), + (mpfs_rqpeek(&privep->reqq))->req.xfrd); + mpfs_req_complete(privep, result); + } +} + +/**************************************************************************** + * Name: mpfs_write_tx_fifo + * + * Description: + * Write data into the TX fifo buffer. + * + * Input Parameters: + * in_data - Pointer to the data to be sent + * length - Amount of bytes to write + * epno - Endpoint number + * + * Returned Value: + * None + * + ****************************************************************************/ + +void mpfs_write_tx_fifo(const void *in_data, uint32_t length, uint8_t epno) +{ + uint32_t i; + uint32_t *temp; + uint8_t *temp_8bit; + uint16_t tx_csr; + uint16_t words = length / 4; + uint16_t bytes = length - words * 4; + uint16_t offset; + + temp = (uint32_t *)in_data; + temp_8bit = (uint8_t *)in_data; + + /* Poll mode: wait for fifo empty first */ + + do + { + tx_csr = getreg16(MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_TX_CSR_OFFSET); + } + while (tx_csr & TXCSRL_REG_EPN_TX_FIFO_NE_MASK); + + /* Send 32-bit words first */ + + for (i = 0; i < words; i++) + { + mpfs_putreg32((uint32_t)temp[i], MPFS_USB_FIFO(epno)); + } + + offset = words << 2; + + /* Send the remaining bytes */ + + for (i = offset; i < (offset + bytes); i++) + { + mpfs_putreg8((uint8_t)temp_8bit[i], MPFS_USB_FIFO(epno)); + } +} + +/**************************************************************************** + * Name: mpfs_req_wrsetup + * + * Description: + * Setup the next queued write request. + * + * Input Parameters: + * priv - Private USB device abstraction + * privep - Private endpoint abstraction + * privreq - The actual write request + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_req_wrsetup(struct mpfs_usbdev_s *priv, + struct mpfs_ep_s *privep, + struct mpfs_req_s *privreq) +{ + const uint8_t *buf; + uint32_t packetsize; + uint8_t epno; + int nbytes; + + epno = USB_EPNO(privep->ep.eplog); + + mpfs_putreg8(epno, MPFS_USB_INDEX); + + /* Get the number of bytes remaining to be sent. */ + + DEBUGASSERT(privreq->req.xfrd < privreq->req.len); + nbytes = privreq->req.len - privreq->req.xfrd; + + usbtrace(TRACE_WRITE(USB_EPNO(privep->ep.eplog)), nbytes); + + /* Either send the maxpacketsize(multi) or all of the remaining data in + * the request. + */ + + if (nbytes >= MPFS_MAX_MULTIPACKET_SIZE) + { + nbytes = MPFS_MAX_MULTIPACKET_SIZE; + } + + /* This is the new number of bytes "in-flight" */ + + privreq->inflight = nbytes; + + /* The new buffer pointer is the start of the buffer plus the number of + * bytes successfully transferred plus the number of bytes previously + * "in-flight". + */ + + buf = privreq->req.buf + privreq->req.xfrd; + + /* Setup TX transfer using ep configured maxpacket size */ + + priv->eplist[epno].descb[1]->addr = (uintptr_t)buf; + packetsize = priv->eplist[epno].descb[1]->pktsize; + + /* Set automatic ZLP sending if requested on req */ + + if (privreq->req.flags & USBDEV_REQFLAGS_NULLPKT) + { + /* Handle this properly when DMA supported */ + } + + priv->eplist[epno].descb[1]->pktsize = packetsize; + + /* Indicate that we are in the sending state */ + + privep->epstate = USB_EPSTATE_SENDING; + + mpfs_write_tx_fifo(buf, nbytes, epno); + privreq->req.xfrd = nbytes; + + /* With poll mode (no DMA), we're done sending */ + + privep->epstate = USB_EPSTATE_IDLE; + + if (epno == EP0) + { + mpfs_putreg16(CSR0L_DEV_TX_PKT_RDY_MASK | CSR0L_DEV_DATA_END_MASK, + MPFS_USB_INDEXED_CSR_EP0_CSR0); + } + else + { + mpfs_modifyreg16(MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_TX_CSR_OFFSET, + 0, + TXCSRL_REG_EPN_TX_PKT_RDY_MASK); + } +} + +/**************************************************************************** + * Name: mpfs_ep_stall + * + * Description: + * Stall the endpoint. + * + * Input Parameters: + * privep - Private endpoint abstraction + * + * Returned Value: + * OK always + * + ****************************************************************************/ + +static int mpfs_ep_stall(struct mpfs_ep_s *privep) +{ + irqstate_t flags; + uint8_t epno; + + DEBUGASSERT(privep->dev); + + epno = USB_EPNO(privep->ep.eplog); + + mpfs_putreg8(epno, MPFS_USB_INDEX); + + /* Check that endpoint is enabled and not already in Halt state */ + + flags = enter_critical_section(); + if ((privep->epstate != USB_EPSTATE_DISABLED) && + (privep->epstate != USB_EPSTATE_STALLED)) + { + epno = USB_EPNO(privep->ep.eplog); + + usbtrace(TRACE_EPSTALL, epno); + + /* If this is an IN endpoint (or endpoint 0), then cancel any + * write requests in progress. + */ + + if (epno == EP0 || USB_ISEPIN(privep->ep.eplog)) + { + mpfs_req_cancel(privep, -EPERM); + } + + /* Put endpoint into stalled state */ + + privep->epstate = USB_EPSTATE_STALLED; + privep->stalled = true; + privep->pending = false; + + if (USB_ISEPIN(privep->ep.eplog)) + { + mpfs_modifyreg16(MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_TX_CSR_OFFSET, + 0, + TXCSRL_REG_EPN_SEND_STALL_MASK); + } + else + { + mpfs_modifyreg16(MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_RX_CSR_OFFSET, + 0, + RXCSRL_REG_EPN_SEND_STALL_MASK | + RXCSRL_REG_EPN_RX_PKT_RDY_MASK); + } + } + + leave_critical_section(flags); + + return OK; +} + +/**************************************************************************** + * Name: mpfs_req_write + * + * Description: + * Write all available entries on the queue. + * + * Input Parameters: + * priv - Private USB device abstraction + * privep - Private endpoint abstraction + * + * Returned Value: + * OK on success, a negated error code in case of failure + * + ****************************************************************************/ + +static int mpfs_req_write(struct mpfs_usbdev_s *priv, + struct mpfs_ep_s *privep) +{ + struct mpfs_req_s *privreq; + uint8_t epno; + int bytesleft; + + epno = USB_EPNO(privep->ep.eplog); + + mpfs_putreg8(epno, MPFS_USB_INDEX); + + /* We get here when an IN endpoint interrupt occurs. So now we know that + * there is no TX transfer in progress (epstate should be IDLE). + */ + + DEBUGASSERT(privep->epstate == USB_EPSTATE_IDLE); + + while (privep->epstate == USB_EPSTATE_IDLE) + { + /* Check the request from the head of the endpoint request queue */ + + privreq = mpfs_rqpeek(&privep->reqq); + if (!privreq) + { + /* Was there a pending endpoint stall? */ + + if (privep->pending) + { + /* Yes... stall the endpoint now */ + + mpfs_ep_stall(privep); + } + + return -ENOENT; + } + + uinfo("epno=%d req=%p: len=%d xfrd=%d inflight=%d\n", + epno, privreq, privreq->req.len, privreq->req.xfrd, + privreq->inflight); + + /* Handle any bytes in flight. */ + + privreq->req.xfrd += privreq->inflight; + privreq->inflight = 0; + + /* Get the number of bytes left to be sent in the packet */ + + bytesleft = privreq->req.len - privreq->req.xfrd; + if (bytesleft > 0) + { + /* Perform the write operation. */ + + mpfs_req_wrsetup(priv, privep, privreq); + } + else if ((privreq->req.len == 0) && !privep->zlpsent) + { + /* If we get here, we requested to send the zero length packet now. + */ + + privep->zlpsent = true; + privreq->inflight = 0; + + /* Setup 0 length TX transfer */ + + priv->eplist[0].descb[1]->addr = (uintptr_t)&priv->ep0out[0]; + + mpfs_putreg16(CSR0L_DEV_TX_PKT_RDY_MASK | CSR0L_DEV_DATA_END_MASK, + MPFS_USB_INDEXED_CSR_EP0_CSR0); + } + + if (privep->epstate == USB_EPSTATE_IDLE) + { + /* Return the write request to the class driver. Set the txbusy + * bit to prevent being called recursively from any new submission + * generated by returning the write request. + */ + + DEBUGASSERT(privreq->req.len == privreq->req.xfrd); + + privep->txbusy = true; + usbtrace(TRACE_COMPLETE(epno), privreq->req.xfrd); + mpfs_req_complete(privep, OK); + privep->txbusy = false; + + return OK; + } + } + + return OK; +} + +/**************************************************************************** + * Name: mpfs_read_rx_fifo + * + * Description: + * Read data from the RX fifo. + * + * Input Parameters: + * out_data - Address to read the data + * length - Amount of data to read + * epno - The endpoint number + * + * Returned Value: + * Amount of data read + * + ****************************************************************************/ + +uint16_t mpfs_read_rx_fifo(uint8_t *out_data, uint32_t length, uint8_t epno) +{ + uint16_t count; + uint32_t i; + uint32_t *temp; + uint8_t *temp_8bit; + uint16_t words; + uint16_t bytes; + uint16_t offset; + + if (epno == EP0) + { + /* Already read the count0 register, this is correct */ + + count = length; + } + else + { + count = getreg16(MPFS_USB_INDEXED_CSR_RX_COUNT); + } + + words = count / 4; + bytes = count - words * 4; + + temp = (uint32_t *)out_data; + temp_8bit = out_data; + + if (count == 0) + { + /* Nothing to do */ + + return 0; + } + + /* 32-bit writes first */ + + for (i = 0; i < words; i++) + { + temp[i] = getreg32(MPFS_USB_FIFO(epno)); + } + + offset = words << 2; + + /* ...and the remaining bytes last */ + + for (i = offset; i < (offset + bytes); i++) + { + temp_8bit[i] = getreg8(MPFS_USB_FIFO(epno)); + } + + return count; +} + +/**************************************************************************** + * Name: mpfs_req_read + * + * Description: + * Issue a read according to the request. + * + * Input Parameters: + * priv - USB device abstraction + * privep - Endpoint abstraction + * recvsize - Size of the read + * + * Returned Value: + * OK on success, an error code in case of trouble + * + ****************************************************************************/ + +static int mpfs_req_read(struct mpfs_usbdev_s *priv, + struct mpfs_ep_s *privep, uint16_t recvsize) +{ + struct mpfs_req_s *privreq; + int epno; + + DEBUGASSERT(priv && privep && privep->epstate == USB_EPSTATE_IDLE); + + /* Check the request from the head of the endpoint request queue */ + + epno = USB_EPNO(privep->ep.eplog); + + mpfs_putreg8(epno, MPFS_USB_INDEX); + + do + { + /* Peek at the next read request in the request queue */ + + privreq = mpfs_rqpeek(&privep->reqq); + if (!privreq) + { + /* When no read requests are pending no EP descriptors are set to + * ready. HW sends NAK to host if it tries to send something. + */ + + privep->epstate = USB_EPSTATE_RXSTOPPED; + + return -ENOENT; + } + + uinfo("EP%d: req.len=%d xfrd=%d recvsize=%d\n", + epno, privreq->req.len, privreq->req.xfrd, recvsize); + + /* Ignore any attempt to receive a zero length packet */ + + if (privreq->req.len == 0) + { + usbtrace(TRACE_DEVERROR(MPFS_TRACEERR_EPOUTNULLPACKET), 0); + mpfs_req_complete(privep, OK); + privreq = NULL; + } + + /* complete read request with available data */ + + if ((privreq->inflight) && (recvsize != 0)) + { + /* Update the total number of bytes transferred */ + + privreq->req.xfrd += recvsize; + privreq->inflight = 0; + + usbtrace(TRACE_COMPLETE(epno), privreq->req.xfrd); + mpfs_req_complete(privep, OK); + + /* Need to set recvsize to zero. When calling mpfs_req_complete() + * class driver could call submit() again and we have new request + * ready on next while() loop. + */ + + privep->rxactive = false; + recvsize = 0; + privreq = NULL; + } + } + while (privreq == NULL); + + DEBUGASSERT(recvsize == 0); + + /* activate new read request from queue */ + + privep->rxactive = true; + privreq->req.xfrd = 0; + privreq->inflight = privreq->req.len; + priv->eplist[epno].descb[0]->addr = (uintptr_t)privreq->req.buf; + + privreq->req.xfrd += mpfs_read_rx_fifo(privreq->req.buf, privreq->req.len, + epno); + + if (epno == EP0) + { + mpfs_putreg16(CSR0L_DEV_SERVICED_RX_PKT_RDY_MASK | + CSR0L_DEV_DATA_END_MASK, + MPFS_USB_INDEXED_CSR_EP0_CSR0); + } + else + { + mpfs_modifyreg16(MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_RX_CSR_OFFSET, + RXCSRL_REG_EPN_RX_PKT_RDY_MASK, 0); + } + + return OK; +} + +static void mpfs_ep_set_fifo_size(uint8_t epno, uint8_t in, + uint16_t fifo_size) +{ + uint16_t temp; + uint8_t i = 0; + + DEBUGASSERT(epno > EP0); + DEBUGASSERT(fifo_size > 8); + + DEBUGASSERT(fifo_size == 8 || fifo_size == 16 || fifo_size == 32 || + fifo_size == 64 || fifo_size == 128 || fifo_size == 512 || + fifo_size == 1024 || fifo_size == 2048 || fifo_size == 4096); + + mpfs_putreg8(epno, MPFS_USB_INDEX); + + temp = (fifo_size / MPFS_MIN_EP_FIFO_SIZE); + + while (!(temp & 0x01)) + { + temp >>= 1; + i++; + } + + /* in: Device to host */ + + if (in) + { + mpfs_putreg8(i, MPFS_USB_TX_FIFO_SIZE); + } + else + { + mpfs_putreg8(i, MPFS_USB_RX_FIFO_SIZE); + } +} + +/**************************************************************************** + * Name: mpfs_ep_configure_internal + * + * Description: + * This is the internal implementation of the endpoint configuration logic + * and implements the endpoint configuration method of the usbdev_ep_s + * interface. As an internal interface, it will be used to configure + * endpoint 0 which is not available to the class implementation. + * + * Input Parameters: + * privep - Endpoint abstraction + * desc - USB descriptor + * + * Returned Value: + * OK always + * + ****************************************************************************/ + +static int mpfs_ep_configure_internal(struct mpfs_ep_s *privep, + const struct usb_epdesc_s *desc) +{ + uint16_t maxpacket; + uint8_t epno; + uint8_t eptype; + bool dirin; + + DEBUGASSERT(privep && privep->dev && desc); + + uinfo("len: 0x%02x type: 0x%02x addr: 0x%02x attr: 0x%02x " + "maxpacketsize: 0x%02x%02x interval: 0x%02x\n", + desc->len, desc->type, desc->addr, desc->attr, + desc->mxpacketsize[1], desc->mxpacketsize[0], + desc->interval); + + /* Decode the endpoint descriptor */ + + epno = USB_EPNO(desc->addr); + dirin = (desc->addr & USB_DIR_MASK) == USB_REQ_DIR_IN; + eptype = (desc->attr & USB_EP_ATTR_XFERTYPE_MASK); + maxpacket = GETUINT16(desc->mxpacketsize); + + /* update endpoint descriptors to correct size */ + + privep->descb[0]->pktsize = maxpacket; + privep->descb[1]->pktsize = maxpacket; + + /* Initialize the endpoint structure */ + + privep->ep.eplog = desc->addr; /* Includes direction */ + privep->ep.maxpacket = maxpacket; + privep->epstate = USB_EPSTATE_IDLE; + + /* get current config IN and OUT */ + + mpfs_putreg8(epno, MPFS_USB_INDEX); + + /* Re-configure and enable the endpoint */ + + if (dirin && epno) + { + /* Clear previous config first */ + + mpfs_putreg16(0, MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_TX_CSR_OFFSET); + + mpfs_modifyreg16(MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_TX_CSR_OFFSET, + TXCSRL_REG_EPN_UNDERRUN_MASK | + TXCSRL_REG_EPN_SEND_STALL_MASK | + TXCSRL_REG_EPN_STALL_SENT_MASK, + 0); + + mpfs_ep_set_fifo_size(epno, 0, maxpacket); + + mpfs_putreg16(desc->addr, MPFS_USB_TX_FIFO_ADDR); + + /* Disable double buffering for now */ + + mpfs_modifyreg16(MPFS_USB_TX_DPBUF_DIS, 0, (1 << epno)); + + /* Sequence for setting the max packet size */ + + mpfs_putreg16(0, MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_TX_MAX_P_OFFSET); + mpfs_putreg16(MPFS_NUM_USB_PKT - 1, MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_TX_MAX_P_OFFSET); + mpfs_putreg16((MPFS_NUM_USB_PKT - 1) << TX_MAX_P_REG_NUM_USB_PKT_SHIFT, + MPFS_USB_ENDPOINT(epno) + MPFS_USB_ENDPOINT_TX_MAX_P_OFFSET); + + mpfs_modifyreg16(MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_TX_MAX_P_OFFSET, 0, maxpacket); + + /* Clear data toggle (by setting the bit, not clearing */ + + mpfs_modifyreg16(MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_TX_CSR_OFFSET, + 0, + TXCSRL_REG_EPN_CLR_DATA_TOG_MASK); + } + else if (epno) + { + /* Clear previous config */ + + mpfs_putreg16(0, MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_RX_CSR_OFFSET); + + mpfs_modifyreg16(MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_RX_CSR_OFFSET, + RXCSRL_REG_EPN_OVERRUN_MASK | + RXCSRL_REG_EPN_STALL_SENT_MASK | + RXCSRL_REG_EPN_SEND_STALL_MASK, + RXCSRL_REG_EPN_RX_PKT_RDY_MASK); + + mpfs_ep_set_fifo_size(epno, dirin, maxpacket); + mpfs_putreg16(desc->addr, MPFS_USB_RX_FIFO_ADDR); + + mpfs_modifyreg16(MPFS_USB_RX_DPBUF_DIS, 0, (1 << epno)); + mpfs_putreg16(0, MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_RX_MAX_P_OFFSET); + mpfs_putreg16(MPFS_NUM_USB_PKT - 1, MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_RX_MAX_P_OFFSET); + mpfs_putreg16((MPFS_NUM_USB_PKT - 1) << RX_MAX_P_REG_NUM_USB_PKT_SHIFT, + MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_RX_MAX_P_OFFSET); + mpfs_modifyreg16(MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_RX_MAX_P_OFFSET, 0, maxpacket); + + mpfs_modifyreg16(MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_RX_CSR_OFFSET, + 0, + RXCSRL_REG_EPN_CLR_DAT_TOG_MASK | + RXCSRL_REG_EPN_RX_PKT_RDY_MASK); + + mpfs_modifyreg16(MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_RX_CSR_OFFSET, + RXCSRL_REG_EPN_RX_PKT_RDY_MASK, + 0); + } + + switch (eptype) + { + case USB_EP_ATTR_XFER_CONTROL: + mpfs_putreg16(0, MPFS_USB_INDEXED_CSR_EP0_CSR0); + break; + + case USB_EP_ATTR_XFER_BULK: + if (dirin) + { + mpfs_modifyreg16(MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_TX_CSR_OFFSET, + TXCSRH_REG_EPN_ENABLE_ISO_MASK, + 0); + } + else + { + /* RXCSRL_REG_EPN_BI_DIS_NYET_MASK is enabled when cleared */ + + mpfs_modifyreg16(MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_RX_CSR_OFFSET, + RXCSRL_REG_EPN_ENABLE_ISO_MASK, + RXCSRL_REG_EPN_ENABLE_AUTOCLR_MASK | + RXCSRL_REG_EPN_BI_DIS_NYET_MASK | + RXCSRL_REG_EPN_RX_PKT_RDY_MASK); + } + break; + + case USB_EP_ATTR_XFER_INT: + if (dirin) + { + mpfs_modifyreg16(MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_TX_CSR_OFFSET, + TXCSRH_REG_EPN_ENABLE_ISO_MASK | + TXCSRH_REG_EPN_ENABLE_AUTOSET_MASK, + 0); + } + else + { + /* RXCSRL_REG_EPN_BI_DIS_NYET_MASK disabled when set */ + + mpfs_modifyreg16(MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_RX_CSR_OFFSET, + RXCSRL_REG_EPN_ENABLE_ISO_MASK | + RXCSRL_REG_EPN_BI_DIS_NYET_MASK, + RXCSRL_REG_EPN_ENABLE_AUTOCLR_MASK | + RXCSRL_REG_EPN_RX_PKT_RDY_MASK); + } + break; + + default: + usbtrace(TRACE_DEVERROR(MPFS_TRACEERR_BADEPTYPE), eptype); + return -EINVAL; + } + + /* Enable endpoint interrupts */ + + if (dirin) + { + mpfs_modifyreg16(MPFS_USB_TX_IRQ_ENABLE, 0, (1 << epno)); + } + else + { + mpfs_modifyreg16(MPFS_USB_RX_IRQ_ENABLE, 0, (1 << epno)); + } + + return OK; +} + +/**************************************************************************** + * Name: mpfs_ep_configure + * + * Description: + * This is the endpoint configuration method of the usbdev_ep_s interface. + * + * Input Parameters: + * ep - USB device abstraction + * desc - USB descriptor + * last - Set if the endpoint is the last one + * + * Returned Value: + * OK on success, a negated error code otherwise + * + ****************************************************************************/ + +static int mpfs_ep_configure(struct usbdev_ep_s *ep, + const struct usb_epdesc_s *desc, + bool last) +{ + struct mpfs_ep_s *privep = (struct mpfs_ep_s *)ep; + int ret; + + ret = mpfs_ep_configure_internal(privep, desc); + + /* If this was the last endpoint, then the class driver is fully + * configured. + */ + + if (ret == OK && last) + { + struct mpfs_usbdev_s *priv = privep->dev; + + /* Go to the configured state (we should have been in the addressed + * state) + */ + + DEBUGASSERT(priv && priv->devstate == USB_DEVSTATE_ADDRESSED); + priv->devstate = USB_DEVSTATE_CONFIGURED; + } + + return ret; +} + +static void mpfs_ep_reset(struct mpfs_usbdev_s *priv, uint8_t epno) +{ + struct mpfs_ep_s *privep = &priv->eplist[epno]; + + /* Disable endpoint interrupts */ + + mpfs_modifyreg16(MPFS_USB_RX_IRQ_ENABLE, (1 << epno), 0); + mpfs_modifyreg16(MPFS_USB_TX_IRQ_ENABLE, (1 << epno), 0); + + /* Cancel any queued requests. Since they are cancelled with status + * -ESHUTDOWN, then will not be requeued until the configuration is reset. + * NOTE: This should not be necessary... the CLASS_DISCONNECT above + * should result in the class implementation calling mpfs_ep_disable + * for each of its configured endpoints. + */ + + mpfs_req_cancel(privep, -ESHUTDOWN); + + /* Reset endpoint status */ + + privep->epstate = USB_EPSTATE_DISABLED; + privep->stalled = false; + privep->pending = false; + privep->halted = false; + privep->zlpsent = false; + privep->txbusy = false; + privep->rxactive = false; +} + +/**************************************************************************** + * Name: mpfs_setdevaddr + * + * Description: + * This function is called after the completion of the STATUS phase to + * instantiate the device address that was received during the SETUP + * phase. This enters the ADDRESSED state from either the DEFAULT or the + * CONFIGURED states. + * + * If called with address == 0, then function will revert to the DEFAULT, + * un-configured and un-addressed state. + * + * Input Parameters: + * priv - USB device abstraction + * address - Address to be set (or cleared) + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_setdevaddr(struct mpfs_usbdev_s *priv, uint8_t address) +{ + uinfo("ENTRY address=0x%x\n", address); + + DEBUGASSERT(address <= 0x7f); + + /* Vendor specific delay before chaning the address */ + + for (int delay = 0; delay < 5000 ; delay++) + { + asm volatile(""); + } + + mpfs_putreg8(address, MPFS_USB_FADDR); + + if (address) + { + priv->devstate = USB_DEVSTATE_ADDRESSED; + } + else + { + /* Revert to the un-addressed, default state */ + + priv->devstate = USB_DEVSTATE_DEFAULT; + } +} + +/**************************************************************************** + * Name: mpfs_ep_disable + * + * Description: + * This is the disable() method of the USB device endpoint structure. + * + * Input Parameters: + * ep - USB device endpoint + * + * Returned Value: + * OK always + * + ****************************************************************************/ + +static int mpfs_ep_disable(struct usbdev_ep_s *ep) +{ + struct mpfs_ep_s *privep = (struct mpfs_ep_s *)ep; + struct mpfs_usbdev_s *priv; + irqstate_t flags; + uint8_t epno; + + epno = USB_EPNO(ep->eplog); + + /* Reset the endpoint and cancel any ongoing activity */ + + flags = enter_critical_section(); + priv = privep->dev; + mpfs_ep_reset(priv, epno); + + /* Revert to the addressed-but-not-configured state */ + + mpfs_setdevaddr(priv, priv->devaddr); + leave_critical_section(flags); + + return OK; +} + +/**************************************************************************** + * Name: mpfs_ep_allocreq + * + * Description: + * This is the allocreq() method of the USB device endpoint structure. + * + * Input Parameters: + * ep - USB device endpoint (unused) + * + * Returned Value: + * New allocated request struct or NULL if no mempry + * + ****************************************************************************/ + +static struct usbdev_req_s *mpfs_ep_allocreq(struct usbdev_ep_s *ep) +{ + struct mpfs_req_s *privreq; + + privreq = (struct mpfs_req_s *)kmm_malloc(sizeof(struct mpfs_req_s)); + if (!privreq) + { + usbtrace(TRACE_DEVERROR(MPFS_TRACEERR_ALLOCFAIL), 0); + return NULL; + } + + memset(privreq, 0, sizeof(struct mpfs_req_s)); + return &privreq->req; +} + +/**************************************************************************** + * Name: mpfs_ep_freereq + * + * Description: + * This is the freereq() method of the USB device endpoint structure. This + * frees the addressed request. + * + * Input Parameters: + * ep - USB device endpoint + * req - Request to free + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_ep_freereq(struct usbdev_ep_s *ep, struct usbdev_req_s *req) +{ + struct mpfs_req_s *privreq = (struct mpfs_req_s *)req; + + kmm_free(privreq); +} + +/**************************************************************************** + * Name: mpfs_ep_allocbuffer + * + * Description: + * This is the allocbuffer() method of the USB device endpoint structure. + * + * Input Parameters: + * ep - USB device endpoint + * nbytes - Number of bytes to allocate + * + * Returned Value: + * Pointer to the data or NULL if no memory + * + * Assumptions/Limitations: + * DMA is not supported yet + * + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_DMA +static void *mpfs_ep_allocbuffer(struct usbdev_ep_s *ep, uint16_t nbytes) +{ + /* There is not special buffer allocation requirement */ + + return kumm_malloc(nbytes); +} +#endif + +/**************************************************************************** + * Name: mpfs_ep_freebuffer + * + * Description: + * This is the freebuffer() method of the USB device endpoint structure. + * + * Input Parameters: + * ep - USB device endpoint + * buf - Pointer to the data + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * DMA is not supported yet + * + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_DMA +static void mpfs_ep_freebuffer(struct usbdev_ep_s *ep, void *buf) +{ + /* There is not special buffer allocation requirement */ + + kumm_free(buf); +} +#endif + +/**************************************************************************** + * Name: mpfs_ep_submit + * + * Description: + * This is the submit() method of the USB device endpoint structure. + * + * Input Parameters: + * ep - USB device endpoint + * req - The request to be submitted + * + * Returned Value: + * OK on success + * + ****************************************************************************/ + +static int mpfs_ep_submit(struct usbdev_ep_s *ep, struct usbdev_req_s *req) +{ + struct mpfs_req_s *privreq = (struct mpfs_req_s *)req; + struct mpfs_ep_s *privep = (struct mpfs_ep_s *)ep; + struct mpfs_usbdev_s *priv; + irqstate_t flags; + uint8_t epno; + int ret = OK; + + usbtrace(TRACE_EPSUBMIT, USB_EPNO(ep->eplog)); + + priv = privep->dev; + + /* Handle the request from the class driver */ + + epno = USB_EPNO(ep->eplog); + req->result = -EINPROGRESS; + req->xfrd = 0; + privreq->inflight = 0; + flags = enter_critical_section(); + + /* Handle IN (device-to-host) requests. NOTE: If the class device is + * using the bi-directional EP0, then we assume that they intend the EP0 + * IN functionality (EP0 SETUP OUT data receipt does not use requests). + */ + + if (USB_ISEPIN(ep->eplog) || epno == EP0) + { + /* Check if the endpoint is stalled (or there is a stall pending) */ + + if (privep->stalled || privep->pending) + { + mpfs_req_enqueue(&privep->pendq, privreq); + + ret = OK; + } + + else + { + /* Add the new request to the request queue for the IN endpoint */ + + mpfs_req_enqueue(&privep->reqq, privreq); + + /* If the IN endpoint is IDLE and there is not write queue + * processing in progress, then transfer the data now. + */ + + if (privep->epstate == USB_EPSTATE_IDLE && !privep->txbusy) + { + ret = mpfs_req_write(priv, privep); + } + } + } + + /* Handle OUT (host-to-device) requests */ + + else + { + /* Add the new request to the request queue for the OUT endpoint */ + + mpfs_req_enqueue(&privep->reqq, privreq); + + /* Check if we have stopped RX receipt due to lack of read + * requests. In that case we are not receiving anything from host. + * and HW sends NAK to host. see mpfs_req_read() + * so this "state" is actually not required (at least yet) + */ + + if (privep->epstate == USB_EPSTATE_RXSTOPPED) + { + privep->epstate = USB_EPSTATE_IDLE; + } + + /* start new read if no active yet */ + + if (!privep->rxactive) + { + ret = mpfs_req_read(priv, privep, 0); + } + } + + leave_critical_section(flags); + + return ret; +} + +/**************************************************************************** + * Name: mpfs_ep_cancel + * + * Description: + * This is the cancel() method of the USB device endpoint structure. + * + * Input Parameters: + * ep - USB device endpoint + * req - The request to be canceled + * + * Returned Value: + * OK unconditionally + * + ****************************************************************************/ + +static int mpfs_ep_cancel(struct usbdev_ep_s *ep, struct usbdev_req_s *req) +{ + struct mpfs_ep_s *privep = (struct mpfs_ep_s *)ep; + irqstate_t flags; + + usbtrace(TRACE_EPCANCEL, USB_EPNO(ep->eplog)); + + flags = enter_critical_section(); + mpfs_req_cancel(privep, -EAGAIN); + leave_critical_section(flags); + + return OK; +} + +/**************************************************************************** + * Name: mpfs_ep_resume + * + * Description: + * This is the resume() method. + * + * Input Parameters: + * privep - Endpoint abstraction + * + * Returned Value: + * OK unconditionally + * + ****************************************************************************/ + +static int mpfs_ep_resume(struct mpfs_ep_s *privep) +{ + struct mpfs_usbdev_s *priv; + struct mpfs_req_s *req; + irqstate_t flags; + uint8_t epno; + + /* Check that endpoint is in Idle state */ + + DEBUGASSERT(privep->dev); + + flags = enter_critical_section(); + + /* Check if the endpoint is stalled */ + + if (privep->epstate == USB_EPSTATE_STALLED) + { + epno = USB_EPNO(privep->ep.eplog); + + priv = (struct mpfs_usbdev_s *)privep->dev; + + /* Return endpoint to Idle state */ + + privep->stalled = false; + privep->pending = false; + privep->epstate = USB_EPSTATE_IDLE; + + /* Clear STALLRQx request and reset data toggle */ + + if (USB_ISEPIN(privep->ep.eplog)) + { + mpfs_modifyreg16(MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_TX_CSR_OFFSET, + TXCSRL_REG_EPN_SEND_STALL_MASK, + 0); + } + else + { + mpfs_modifyreg16(MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_RX_CSR_OFFSET, + RXCSRL_REG_EPN_SEND_STALL_MASK, + RXCSRL_REG_EPN_RX_PKT_RDY_MASK); + mpfs_modifyreg16(MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_RX_CSR_OFFSET, + 0, + RXCSRL_REG_EPN_CLR_DAT_TOG_MASK | + RXCSRL_REG_EPN_RX_PKT_RDY_MASK); + } + + /* Copy any requests in the pending request queue to the working + * request queue. + */ + + while ((req = mpfs_req_dequeue(&privep->pendq)) != NULL) + { + mpfs_req_enqueue(&privep->reqq, req); + } + + /* Resuming any blocked data transfers on the endpoint */ + + if (epno == 0 || USB_ISEPIN(privep->ep.eplog)) + { + /* IN endpoint (or EP0). Restart any queued write requests */ + + mpfs_req_write(priv, privep); + } + } + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: mpfs_ep_stallresume + * + * Description: + * This is the stallresume() method. + * + * Input Parameters: + * ep - Endpoint abstraction + * resume - Resume (or stall) + * + * Returned Value: + * OK on success + * + ****************************************************************************/ + +static int mpfs_ep_stallresume(struct usbdev_ep_s *ep, bool resume) +{ + struct mpfs_ep_s *privep; + uint8_t epno; + irqstate_t flags; + int ret; + + /* Handle the resume condition */ + + privep = (struct mpfs_ep_s *)ep; + if (resume) + { + ret = mpfs_ep_resume(privep); + } + + /* Handle the stall condition */ + + else + { + /* If this is an IN endpoint (and not EP0) and if there are queued + * write requests, then we cannot stall now. Perhaps this is a + * protocol stall. In that case, we will need to drain the write + * requests before sending the stall. + */ + + flags = enter_critical_section(); + epno = USB_EPNO(ep->eplog); + if (epno != 0 && USB_ISEPIN(ep->eplog)) + { + /* Are there any unfinished write requests in the request + * queue? + */ + + if (!mpfs_rqempty(&privep->reqq)) + { + /* Just set a flag to indicate that the endpoint must be + * stalled on the next TRCPTX interrupt when the request + * queue becomes empty. + */ + + privep->pending = true; + leave_critical_section(flags); + return OK; + } + } + + /* Not an IN endpoint, endpoint 0, or no pending write requests. + * Stall the endpoint now. + */ + + ret = mpfs_ep_stall(privep); + leave_critical_section(flags); + } + + return ret; +} + +/**************************************************************************** + * Device Controller Operations + ****************************************************************************/ + +/**************************************************************************** + * Name: mpfs_ep_reserve + * + * Description: + * Find and un-reserved endpoint number and reserve it for the caller. + * + * Input Parameters: + * priv - USB device abstraction + * epset - Set of bits, one bit reflects one endpoint + * + * Returned Value: + * Endpoint structure or NULL in case of error + * + ****************************************************************************/ + +static inline struct mpfs_ep_s * +mpfs_ep_reserve(struct mpfs_usbdev_s *priv, uint8_t epset) +{ + struct mpfs_ep_s *privep = NULL; + irqstate_t flags; + int epndx = 0; + + flags = enter_critical_section(); + epset &= priv->epavail; + if (epset) + { + /* Select the lowest bit in the set of matching, available endpoints + * (skipping EP0) + */ + + for (epndx = 1; epndx < MPFS_USB_NENDPOINTS; epndx++) + { + uint8_t bit = MPFS_EP_BIT(epndx); + if ((epset & bit) != 0) + { + /* Mark the endpoint no longer available */ + + priv->epavail &= ~bit; + + /* And return the pointer to the standard endpoint + * structure. + */ + + privep = &priv->eplist[epndx]; + break; + } + } + } + + leave_critical_section(flags); + return privep; +} + +/**************************************************************************** + * Name: mpfs_allocep + * + * Description: + * This is the allocep() method of the USB device driver interface + * + * Input Parameters: + * dev - USB device abstraction + * epno - Endpoint number + * in - Direction + * eptype - EP type, unused + * + * Returned Value: + * USB device endpoint structure or NULL in case of error + * + ****************************************************************************/ + +static struct usbdev_ep_s *mpfs_allocep(struct usbdev_s *dev, uint8_t epno, + bool in, uint8_t eptype) +{ + struct mpfs_usbdev_s *priv = (struct mpfs_usbdev_s *)dev; + struct mpfs_ep_s *privep = NULL; + uint16_t epset = MPFS_EPSET_NOTEP0; + + /* Ignore any direction bits in the logical address */ + + epno = USB_EPNO(epno); + + /* A logical address of 0 means that any endpoint will do */ + + if (epno > 0) + { + /* Otherwise, we will return the endpoint structure only for the + * requested 'logical' endpoint. All of the other checks will still + * be performed. + */ + + if (epno >= MPFS_USB_NENDPOINTS) + { + usbtrace(TRACE_DEVERROR(MPFS_TRACEERR_BADEPNO), (uint16_t)epno); + return NULL; + } + + /* Convert the logical address to a physical OUT endpoint address and + * remove all of the candidate endpoints from the bitset except for the + * the IN/OUT pair for this logical address. + */ + + epset = MPFS_EP_BIT(epno); + } + + /* Check if the selected endpoint number is available */ + + privep = mpfs_ep_reserve(priv, epset); + if (!privep) + { + return NULL; + } + + return &privep->ep; +} + +/**************************************************************************** + * Name: mpfs_ep_unreserve + * + * Description: + * The endpoint is no longer in use. It will be unreserved and can be + * re-used if needed. + * + * Input Parameters: + * priv - USB device abstraction + * privep - Endpoint private struct + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void +mpfs_ep_unreserve(struct mpfs_usbdev_s *priv, struct mpfs_ep_s *privep) +{ + irqstate_t flags = enter_critical_section(); + priv->epavail |= MPFS_EP_BIT(USB_EPNO(privep->ep.eplog)); + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: mpfs_freeep + * + * Description: + * This is the freeep() method of the USB device driver interface + * + * Input Parameters: + * dev - USB device abstraction + * ep - Endpoint private struct + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_freeep(struct usbdev_s *dev, struct usbdev_ep_s *ep) +{ + struct mpfs_usbdev_s *priv; + struct mpfs_ep_s *privep; + + priv = (struct mpfs_usbdev_s *)dev; + privep = (struct mpfs_ep_s *)ep; + + if (priv && privep) + { + /* Mark the endpoint as available */ + + mpfs_ep_unreserve(priv, privep); + } +} + +/**************************************************************************** + * Name: mpfs_getframe + * + * Description: + * This is the getframe() method of the USB device driver interface + * + * Input Parameters: + * dev - USB device abstraction + * + * Returned Value: + * Last known frame number + * + ****************************************************************************/ + +static int mpfs_getframe(struct usbdev_s *dev) +{ + uint16_t frameno; + + /* Return the last frame number detected by the hardware */ + + frameno = getreg16(MPFS_USB_FRAME); + + return frameno; +} + +void mpfs_usb_suspend(FAR struct usbdev_s *dev, bool resume) +{ +} + +/**************************************************************************** + * Name: mpfs_resume + * + * Description: + * Resumes action in contrast to suspend. + * + * Input Parameters: + * priv - USB device abstraction + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_resume(struct mpfs_usbdev_s *priv) +{ + /* This function is called when either (1) a WAKEUP interrupt is received + * from the host PC, or (2) the class device implementation calls the + * wakeup() method. + */ + + /* Don't do anything if the device was not suspended */ + + if (priv->devstate == USB_DEVSTATE_SUSPENDED) + { + /* Revert to the previous state */ + + priv->devstate = priv->prevstate; + + /* Restore clocking to the USB peripheral */ + + mpfs_enableclk(); + + /* Restore full power -- whatever that means for this + * particular board + */ + + mpfs_usb_suspend((struct usbdev_s *)priv, true); + + /* Notify the class driver of the resume event */ + + if (priv->driver) + { + CLASS_RESUME(priv->driver, &priv->usbdev); + } + } +} + +/**************************************************************************** + * Name: mpfs_wakeup + * + * Description: + * This is the wakeup() method of the USB device driver interface + * + * Input Parameters: + * dev - USB device abstraction + * + * Returned Value: + * OK unconditionally + * + ****************************************************************************/ + +static int mpfs_wakeup(struct usbdev_s *dev) +{ + struct mpfs_usbdev_s *priv = (struct mpfs_usbdev_s *)dev; + irqstate_t flags; + + /* Resume normal operation */ + + flags = enter_critical_section(); + + mpfs_resume(priv); + + /* Device is always self-powered. Remote wakeup is not supported */ + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: mpfs_selfpowered + * + * Description: + * This is the selfpowered() method of the USB device driver interface + * + * Input Parameters: + * dev - USB device abstraction + * selfpowered - Selfpowered if set true + * + * Returned Value: + * OK unconditionally + * + ****************************************************************************/ + +static int mpfs_selfpowered(struct usbdev_s *dev, bool selfpowered) +{ + struct mpfs_usbdev_s *priv = (struct mpfs_usbdev_s *)dev; + + priv->selfpowered = selfpowered; + + return OK; +} + +/**************************************************************************** + * Name: mpfs_reset + * + * Description: + * This resets the USB state but doesn't reset the hardware itself + * + * Input Parameters: + * priv - USB device abstraction + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_reset(struct mpfs_usbdev_s *priv) +{ + uint8_t epno; + + /* Tell the class driver that we are disconnected. The class driver + * should then accept any new configurations. + */ + + CLASS_DISCONNECT(priv->driver, &priv->usbdev); + + /* The device enters the default state (un-addressed and un-configured) */ + + priv->devaddr = 0; + mpfs_setdevaddr(priv, 0); + + priv->devstate = USB_DEVSTATE_DEFAULT; + + /* Reset and disable all endpoints. Then re-configure EP0 */ + + mpfs_putreg16(0, MPFS_USB_RX_IRQ_ENABLE); + mpfs_putreg16(0, MPFS_USB_TX_IRQ_ENABLE); + + mpfs_epset_reset(priv, MPFS_EPSET_ALL); + mpfs_ep_configure_internal(&priv->eplist[EP0], &g_ep0desc); + + /* Set EP0 waiting for SETUP */ + + mpfs_ep0_ctrlread(priv); + + /* Reset endpoint data structures */ + + for (epno = 0; epno < MPFS_USB_NENDPOINTS; epno++) + { + struct mpfs_ep_s *privep = &priv->eplist[epno]; + + /* Cancel any queued requests. Since they are cancelled + * with status -ESHUTDOWN, then will not be requeued + * until the configuration is reset. NOTE: This should + * not be necessary... the CLASS_DISCONNECT above should + * result in the class implementation calling mpfs_ep_disable + * for each of its configured endpoints. + */ + + mpfs_req_cancel(privep, -ESHUTDOWN); + + /* Reset endpoint status */ + + privep->stalled = false; + privep->pending = false; + privep->halted = false; + privep->zlpsent = false; + privep->txbusy = false; + privep->rxactive = false; + } + + /* Re-configure the USB controller in its initial, unconnected state */ + +#ifdef CONFIG_USBDEV_DUALSPEED + priv->usbdev.speed = USB_SPEED_HIGH; + priv->usbdev.dualspeed = 0; +#else + priv->usbdev.speed = USB_SPEED_FULL; + priv->usbdev.dualspeed = 1; +#endif + + /* Clear all pending interrupt statuses */ + + mpfs_putreg8(0, MPFS_USB_INDEX); + mpfs_putreg8(0, MPFS_USB_IRQ); + + mpfs_putreg16(0, MPFS_USB_INDEXED_CSR_EP0_CSR0); + mpfs_putreg16(1, MPFS_USB_TX_IRQ_ENABLE); +} + +/**************************************************************************** + * Name: mpfs_ep0_wrstatus + * + * Description: + * Writes the ep0 status reply back to the host. + * + * Input Parameters: + * priv - USB device abstraction + * buffer - Data buffer + * buflen - Size of the data buffer + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_ep0_wrstatus(struct mpfs_usbdev_s *priv, + const uint8_t *buffer, size_t buflen) +{ + struct mpfs_ep_s *privep; + privep = &priv->eplist[0]; + + uint32_t packetsize; + + /* We need to make copy of data as source is in stack + * reusing the static ep0 setup buffer + */ + + DEBUGASSERT(buflen < MPFS_EP0_MAXPACKET); + memcpy(&priv->ep0out[0], buffer, buflen); + + /* Setup TX transfer */ + + priv->eplist[0].descb[1]->addr = (uintptr_t) &priv->ep0out[0]; + packetsize = priv->eplist[0].descb[1]->pktsize; + priv->eplist[0].descb[1]->pktsize = packetsize; + + if (buflen) + { + mpfs_write_tx_fifo(buffer, buflen, 0); + + mpfs_putreg16(CSR0L_DEV_TX_PKT_RDY_MASK | CSR0L_DEV_DATA_END_MASK, + MPFS_USB_INDEXED_CSR_EP0_CSR0); + } + else if (privep->epstate == USB_EPSTATE_EP0ADDRESS) + { + mpfs_putreg16(CSR0L_DEV_SERVICED_RX_PKT_RDY_MASK | + CSR0L_DEV_DATA_END_MASK, + MPFS_USB_INDEXED_CSR_EP0_CSR0); + + privep->epstate = USB_EPSTATE_IDLE; + mpfs_setdevaddr(priv, priv->devaddr); + } + + /* set read for next setup OUT */ + + mpfs_ep0_ctrlread(priv); +} + +/**************************************************************************** + * Name: mpfs_ep0_dispatch + * + * Description: + * Writes the ep0 status reply back to the host. + * + * Input Parameters: + * priv - USB device abstraction + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_ep0_dispatch(struct mpfs_usbdev_s *priv) +{ + uint8_t *dataout; + size_t outlen; + int ret; + + usbtrace(TRACE_INTDECODE(MPFS_TRACEINTID_DISPATCH), 0); + + mpfs_putreg16(CSR0L_DEV_SERVICED_RX_PKT_RDY_MASK, + MPFS_USB_INDEXED_CSR_EP0_CSR0); + + if (priv && priv->driver) + { + /* Assume IN SETUP (or OUT SETUP with no data) */ + + dataout = NULL; + outlen = 0; + + /* Was this an OUT SETUP command? */ + + if (USB_REQ_ISOUT(priv->ctrl.type)) + { + uint16_t tmplen = GETUINT16(priv->ctrl.len); + if (tmplen > 0) + { + dataout = priv->ep0out; + outlen = tmplen; + } + } + + /* Forward to the control request to the class driver implementation */ + + ret = CLASS_SETUP(priv->driver, &priv->usbdev, &priv->ctrl, + dataout, outlen); + + if (ret < 0) + { + /* Stall on failure */ + + usbtrace(TRACE_DEVERROR(MPFS_TRACEERR_DISPATCHSTALL), 0); + mpfs_ep_stall(&priv->eplist[EP0]); + } + } +} + +/**************************************************************************** + * Name: mpfs_ep0_setup + * + * Description: + * This function is called after the receiving of the SETUP packet + * data is ready on usb_ctrlreq_s struct. + * + * Input Parameters: + * priv - USB device abstraction + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_ep0_setup(struct mpfs_usbdev_s *priv) +{ + struct mpfs_ep_s *ep0 = &priv->eplist[EP0]; + struct mpfs_ep_s *privep; + union wb_u value; + union wb_u index; + union wb_u len; + union wb_u response; + enum mpfs_ep0setup_e ep0result; + uint8_t epno; + int nbytes = 0; /* Assume zero-length packet */ + int ret; + + /* Terminate any pending requests */ + + mpfs_req_cancel(ep0, -EPROTO); + + /* Assume NOT stalled; no TX in progress */ + + ep0->stalled = false; + ep0->pending = false; + ep0->epstate = USB_EPSTATE_IDLE; + + /* And extract the little-endian 16-bit values to host order */ + + value.w = GETUINT16(priv->ctrl.value); + index.w = GETUINT16(priv->ctrl.index); + len.w = GETUINT16(priv->ctrl.len); + + uinfo("SETUP: type=%02x req=%02x value=%04x index=%04x len=%04x\n", + priv->ctrl.type, priv->ctrl.req, value.w, index.w, len.w); + + /* Dispatch any non-standard requests */ + + if ((priv->ctrl.type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD) + { + /* Let the class implementation handle all non-standard requests */ + + mpfs_ep0_dispatch(priv); + return; + } + + /* Handle standard request. Pick off the things of interest to the + * USB device controller driver; pass what is left to the class driver + */ + + ep0result = USB_EP0SETUP_SUCCESS; + + switch (priv->ctrl.req) + { + case USB_REQ_GETSTATUS: + { + /* type: device-to-host; recipient = device, interface, endpoint + * value: 0 + * index: zero interface endpoint + * len: 2; data = status + */ + + usbtrace(TRACE_INTDECODE(MPFS_TRACEINTID_GETSTATUS), + priv->ctrl.type); + if (len.w != 2 || (priv->ctrl.type & USB_REQ_DIR_IN) == 0 || + index.b[MSB] != 0 || value.w != 0) + { + usbtrace(TRACE_DEVERROR(MPFS_TRACEERR_BADEPGETSTATUS), 0); + ep0result = USB_EP0SETUP_STALL; + } + else + { + switch (priv->ctrl.type & USB_REQ_RECIPIENT_MASK) + { + case USB_REQ_RECIPIENT_ENDPOINT: + { + epno = USB_EPNO(index.b[LSB]); + usbtrace(TRACE_INTDECODE(MPFS_TRACEINTID_GETSTATUS), epno); + if (epno >= MPFS_USB_NENDPOINTS) + { + usbtrace(TRACE_DEVERROR(MPFS_TRACEERR_BADEPGETSTATUS), + epno); + ep0result = USB_EP0SETUP_STALL; + } + else + { + privep = &priv->eplist[epno]; + response.w = 0; /* Not stalled */ + nbytes = 2; /* Response size: 2 bytes */ + + if (privep->stalled) + { + /* Endpoint stalled */ + + response.b[LSB] = 1; /* Stalled */ + } + } + } + break; + + case USB_REQ_RECIPIENT_DEVICE: + { + if (index.w == 0) + { + usbtrace(TRACE_INTDECODE(MPFS_TRACEINTID_DEVGETSTATUS), + 0); + + /* Features: Remote Wakeup=YES; selfpowered=? */ + + response.w = 0; + response.b[LSB] = (priv->selfpowered << + USB_FEATURE_SELFPOWERED) | + (1 << USB_FEATURE_REMOTEWAKEUP); + nbytes = 2; /* Response size: 2 bytes */ + } + else + { + usbtrace(TRACE_DEVERROR(MPFS_TRACEERR_BADDEVGETSTATUS), + 0); + ep0result = USB_EP0SETUP_STALL; + } + } + break; + + case USB_REQ_RECIPIENT_INTERFACE: + { + usbtrace(TRACE_INTDECODE(MPFS_TRACEINTID_IFGETSTATUS), 0); + response.w = 0; + nbytes = 2; /* Response size: 2 bytes */ + } + break; + + default: + { + usbtrace(TRACE_DEVERROR(MPFS_TRACEERR_BADGETSTATUS), 0); + ep0result = USB_EP0SETUP_STALL; + } + break; + } + } + } + break; + + case USB_REQ_CLEARFEATURE: + { + /* type: host-to-device; recipient = device, interface or endpoint + * value: feature selector + * index: zero interface endpoint; + * len: zero, data = none + */ + + usbtrace(TRACE_INTDECODE(MPFS_TRACEINTID_CLEARFEATURE), + priv->ctrl.type); + if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) != + USB_REQ_RECIPIENT_ENDPOINT) + { + /* Let the class implementation handle all + * recipients (except for the + * endpoint recipient) + */ + + mpfs_ep0_dispatch(priv); + ep0result = USB_EP0SETUP_DISPATCHED; + } + else + { + /* Endpoint recipient */ + + epno = USB_EPNO(index.b[LSB]); + if (epno < MPFS_USB_NENDPOINTS && index.b[MSB] == 0 && + value.w == USB_FEATURE_ENDPOINTHALT && len.w == 0) + { + privep = &priv->eplist[epno]; + privep->halted = false; + + ret = mpfs_ep_resume(privep); + if (ret < 0) + { + ep0result = USB_EP0SETUP_STALL; + } + } + else + { + usbtrace(TRACE_DEVERROR(MPFS_TRACEERR_BADCLEARFEATURE), 0); + ep0result = USB_EP0SETUP_STALL; + } + } + } + break; + + case USB_REQ_SETFEATURE: + { + /* type: host-to-device; recipient = device, interface, endpoint + * value: feature selector + * index: zero interface endpoint; + * len: 0; data = none + */ + + usbtrace(TRACE_INTDECODE(MPFS_TRACEINTID_SETFEATURE), + priv->ctrl.type); + if (((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) == + USB_REQ_RECIPIENT_DEVICE) && + value.w == USB_FEATURE_TESTMODE) + { + /* Special case recipient=device test mode */ + + uinfo("test mode: %d\n", index.w); + } + else if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) != + USB_REQ_RECIPIENT_ENDPOINT) + { + /* The class driver handles all recipients + * except recipient=endpoint + */ + + mpfs_ep0_dispatch(priv); + ep0result = USB_EP0SETUP_DISPATCHED; + } + else + { + /* Handler recipient=endpoint */ + + epno = USB_EPNO(index.b[LSB]); + if (epno < MPFS_USB_NENDPOINTS && index.b[MSB] == 0 && + value.w == USB_FEATURE_ENDPOINTHALT && len.w == 0) + { + privep = &priv->eplist[epno]; + privep->halted = true; + + ret = mpfs_ep_stall(privep); + if (ret < 0) + { + ep0result = USB_EP0SETUP_STALL; + } + } + else + { + usbtrace(TRACE_DEVERROR(MPFS_TRACEERR_BADSETFEATURE), 0); + ep0result = USB_EP0SETUP_STALL; + } + } + } + break; + + case USB_REQ_SETADDRESS: + { + /* type: host-to-device; recipient = device + * value: device address + * index: 0 + * len: 0; data = none + */ + + if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) != + USB_REQ_RECIPIENT_DEVICE || + index.w != 0 || len.w != 0 || value.w > 127) + { + usbtrace(TRACE_DEVERROR(MPFS_TRACEERR_BADSETADDRESS), 0); + ep0result = USB_EP0SETUP_STALL; + } + else + { + /* Note that setting of the device address will be deferred. + * A zero-length packet will be sent and the device address will + * be set when the zero-length packet transfer completes. + */ + + usbtrace(TRACE_INTDECODE(MPFS_TRACEINTID_EP0SETUPSETADDRESS), + value.w); + + priv->devaddr = value.w; + ep0result = USB_EP0SETUP_ADDRESS; + } + } + break; + + case USB_REQ_GETDESCRIPTOR: + /* type: device-to-host; recipient = device + * value: descriptor type and index + * index: 0 or language ID; + * len: descriptor len; data = descriptor + */ + + case USB_REQ_SETDESCRIPTOR: + /* type: host-to-device; recipient = device + * value: descriptor type and index + * index: 0 or language ID; + * len: descriptor len; data = descriptor + */ + + { + if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) == + USB_REQ_RECIPIENT_DEVICE) + { + /* The request seems valid... let the class implementation + * handle it. + */ + + mpfs_ep0_dispatch(priv); + ep0result = USB_EP0SETUP_DISPATCHED; + } + else + { + ep0result = USB_EP0SETUP_STALL; + } + } + break; + + case USB_REQ_GETCONFIGURATION: + /* type: device-to-host; recipient = device + * value: 0; + * index: 0; + * len: 1; data = configuration value + */ + + { + usbtrace(TRACE_INTDECODE(MPFS_TRACEINTID_GETCONFIG), + priv->ctrl.type); + + if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) == + USB_REQ_RECIPIENT_DEVICE && + value.w == 0 && index.w == 0 && len.w == 1) + { + /* The request seems valid... let the class implementation + * handle it. + */ + + mpfs_ep0_dispatch(priv); + ep0result = USB_EP0SETUP_DISPATCHED; + } + else + { + usbtrace(TRACE_DEVERROR(MPFS_TRACEERR_BADGETCONFIG), 0); + ep0result = USB_EP0SETUP_STALL; + } + } + break; + + case USB_REQ_SETCONFIGURATION: + /* type: host-to-device; recipient = device + * value: configuration value + * index: 0; + * len: 0; data = none + */ + + { + usbtrace(TRACE_INTDECODE(MPFS_TRACEINTID_SETCONFIG), + priv->ctrl.type); + + if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) == + USB_REQ_RECIPIENT_DEVICE && + index.w == 0 && len.w == 0) + { + /* The request seems valid... let the class implementation + * handle it. If the class implementation accepts it new + * configuration, it will call mpfs_ep_configure() to configure + * the endpoints. + */ + + mpfs_ep0_dispatch(priv); + ep0result = USB_EP0SETUP_DISPATCHED; + } + else + { + usbtrace(TRACE_DEVERROR(MPFS_TRACEERR_BADSETCONFIG), 0); + ep0result = USB_EP0SETUP_STALL; + } + } + break; + + case USB_REQ_GETINTERFACE: + /* type: device-to-host; recipient = interface + * value: 0 + * index: interface; + * len: 1; data = alt interface + */ + + case USB_REQ_SETINTERFACE: + /* type: host-to-device; recipient = interface + * value: alternate setting + * index: interface; + * len: 0; data = none + */ + + { + /* Let the class implementation handle the request */ + + usbtrace(TRACE_INTDECODE(MPFS_TRACEINTID_GETSETIF), priv->ctrl.type); + mpfs_ep0_dispatch(priv); + ep0result = USB_EP0SETUP_DISPATCHED; + } + break; + + case USB_REQ_SYNCHFRAME: + /* type: device-to-host; recipient = endpoint + * value: 0 + * index: endpoint; + * len: 2; data = frame number + */ + + { + usbtrace(TRACE_INTDECODE(MPFS_TRACEINTID_SYNCHFRAME), 0); + } + break; + + default: + { + usbtrace(TRACE_DEVERROR(MPFS_TRACEERR_INVALIDCTRLREQ), + priv->ctrl.req); + ep0result = USB_EP0SETUP_STALL; + } + break; + } + + /* Restrict the data length to the length requested in the setup packet */ + + if (nbytes > len.w) + { + nbytes = len.w; + } + + switch (ep0result) + { + case USB_EP0SETUP_SUCCESS: + { + /* Send the response (might be a zero-length packet) */ + + ep0->epstate = USB_EPSTATE_EP0STATUSIN; + mpfs_ep0_wrstatus(priv, response.b, nbytes); + } + break; + + case USB_EP0SETUP_ADDRESS: + { + /* Send the response (might be a zero-length packet) */ + + ep0->epstate = USB_EPSTATE_EP0ADDRESS; + mpfs_ep0_wrstatus(priv, response.b, nbytes); + } + break; + + case USB_EP0SETUP_STALL: + { + /* Stall EP0 */ + + usbtrace(TRACE_DEVERROR(MPFS_TRACEERR_EP0SETUPSTALLED), + priv->ctrl.req); + + mpfs_ep_stall(&priv->eplist[EP0]); + } + break; + + case USB_EP0SETUP_DISPATCHED: + default: + break; + } +} + +/**************************************************************************** + * Name: mpfs_ep0_ctrlread + * + * Description: + * Prepare 8-byte read req for ep0-ctrl setup phase. + * data is received on priv->ep0out buffer + * + * Input Parameters: + * priv - USB device abstraction + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_ep0_ctrlread(struct mpfs_usbdev_s *priv) +{ + priv->eplist[0].descb[0]->addr = (uintptr_t)&priv->ep0out[0]; + priv->eplist[0].descb[0]->pktsize = 8; +} + +/**************************************************************************** + * Name: mpfs_ep_rx_interrupt + * + * Description: + * Handle the USB endpoint interrupt from the host (OUT). + * + * Input Parameters: + * priv - USB device abstraction + * epno - The endpoint number + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_ep_rx_interrupt(struct mpfs_usbdev_s *priv, int epno) +{ + struct mpfs_ep_s *privep; + uint16_t reg; + uint16_t count; + + privep = &priv->eplist[epno]; + + mpfs_putreg8(epno, MPFS_USB_INDEX); + + reg = getreg16(MPFS_USB_ENDPOINT(epno) + MPFS_USB_ENDPOINT_RX_CSR_OFFSET); + + count = getreg16(MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_RX_COUNT_OFFSET); + + usbtrace(TRACE_INTDECODE(MPFS_TRACEINTID_EP_RX_CSR), reg); + usbtrace(TRACE_INTDECODE(MPFS_TRACEINTID_EP_RX_COUNT), count); + + if (privep->epstate == USB_EPSTATE_IDLE) + { + /* Continue processing the read request. */ + + usbtrace(TRACE_READ(USB_EPNO(epno)), count); + + mpfs_req_read(priv, privep, count); + } +} + +/**************************************************************************** + * Name: mpfs_ep_tx_interrupt + * + * Description: + * Handle the TX endpoint interrupt (IN, to the host) + * + * Input Parameters: + * priv - USB device abstraction + * epno - The endpoint number + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_ep_tx_interrupt(struct mpfs_usbdev_s *priv, int epno) +{ + struct mpfs_ep_s *privep; + privep = &priv->eplist[epno]; + + uint16_t tx_csr = getreg16(MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_TX_CSR_OFFSET); + + usbtrace(TRACE_INTDECODE(MPFS_TRACEINTID_EP_TX_CSR), tx_csr); + + if (tx_csr & TXCSRL_REG_EPN_UNDERRUN_MASK) + { + /* Under-run errors should happen only for ISO endpoints. */ + + mpfs_modifyreg16(MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_TX_CSR_OFFSET, + TXCSRL_REG_EPN_UNDERRUN_MASK, + 0); + } + + if (tx_csr & TXCSRL_REG_EPN_STALL_SENT_MASK) + { + mpfs_modifyreg16(MPFS_USB_ENDPOINT(epno) + + MPFS_USB_ENDPOINT_TX_CSR_OFFSET, + TXCSRL_REG_EPN_STALL_SENT_MASK, + 0); + } + + if (privep->epstate == USB_EPSTATE_SENDING || + privep->epstate == USB_EPSTATE_EP0STATUSIN) + { + /* Continue/resume processing the write requests */ + + privep->epstate = USB_EPSTATE_IDLE; + mpfs_req_write(priv, privep); + } + else + { + usbtrace(TRACE_DEVERROR(MPFS_TRACEERR_INVALID_EP0_STATE), tx_csr); + } +} + +/**************************************************************************** + * Name: mpfs_ctrl_ep_interrupt + * + * Description: + * Handle the EP0 USB endpoint interrupt + * + * Input Parameters: + * priv - USB device abstraction + * epno - The endpoint number + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_ctrl_ep_interrupt(struct mpfs_usbdev_s *priv, int epno) +{ + struct mpfs_ep_s *privep; + uint16_t count0; + uint16_t csr0; + uint8_t mode; + + /* Get the endpoint structure */ + + privep = &priv->eplist[epno]; + + mpfs_putreg8(epno, MPFS_USB_INDEX); + + /* Make sure we're in device mode */ + + mode = getreg8(MPFS_USB_DEV_CTRL) & DEV_CTRL_HOST_MODE_MASK; + DEBUGASSERT(!mode); + + /* Endpoint stall */ + + count0 = getreg16(MPFS_USB_INDEXED_CSR_EP0_COUNT0); + csr0 = getreg16(MPFS_USB_INDEXED_CSR_EP0_CSR0); + + usbtrace(TRACE_INTDECODE(MPFS_TRACEINTID_EP0_CSR0), csr0); + usbtrace(TRACE_INTDECODE(MPFS_TRACEINTID_EP0_COUNT0), count0); + + if (csr0 & CSR0L_DEV_STALL_SENT_MASK) + { + if (privep->epstate != USB_EPSTATE_STALLED) + { + usbtrace(TRACE_INTDECODE(MPFS_TRACEINTID_EP0_STALLSENT), csr0); + mpfs_modifyreg16(MPFS_USB_INDEXED_CSR_EP0_CSR0, + CSR0L_DEV_STALL_SENT_MASK, 0); + } + } + + /* Clear setup end if set */ + + if (csr0 & CSR0L_DEV_SETUP_END_MASK) + { + /* Setting SERVICED_SETUP_END bit clears Setup End bit */ + + mpfs_putreg16(CSR0L_DEV_SERVICED_SETUP_END_MASK, + MPFS_USB_INDEXED_CSR_EP0_CSR0); + } + + if (!csr0) + { + if (privep->epstate == USB_EPSTATE_SENDING || + privep->epstate == USB_EPSTATE_EP0STATUSIN) + { + /* Continue/resume processing the write requests */ + + privep->epstate = USB_EPSTATE_IDLE; + mpfs_req_write(priv, privep); + } + else + { + /* Unexpected interrupt or tx completion */ + + usbtrace(TRACE_DEVERROR(MPFS_TRACEERR_TXCOMPERR), + privep->epstate); + } + } + + /* RX packet received */ + + if (csr0 & CSR0L_DEV_DATA_END_MASK) + { + usbtrace(TRACE_INTDECODE(MPFS_TRACEINTID_DATA_END), csr0); + mpfs_modifyreg16(MPFS_USB_INDEXED_CSR_EP0_CSR0, 0, + CSR0L_DEV_DATA_END_MASK | + CSR0L_DEV_SERVICED_RX_PKT_RDY_MASK); + } + + /* SETUP packet received */ + + if (csr0 & CSR0L_DEV_RX_PKT_RDY_MASK) + { + uint16_t len; + + /* If a write request transfer was pending, complete it. */ + + if (privep->epstate == USB_EPSTATE_SENDING) + { + mpfs_req_complete(privep, -EPROTO); + } + + memset((uint8_t *)&priv->ctrl, 0, sizeof(struct usb_ctrlreq_s)); + + usbtrace(TRACE_READ(USB_EPNO(EP0)), count0); + + if (count0) + { + mpfs_read_rx_fifo((uint8_t *)&priv->ctrl, count0, EP0); + } + + /* SETUP data is ready */ + + len = GETUINT16(priv->ctrl.len); + if (USB_REQ_ISOUT(priv->ctrl.type) && len > 0) + { + /* Yes.. then we have to wait for the OUT data phase to complete + * before processing the SETUP command. + */ + + privep->epstate = USB_EPSTATE_EP0DATAOUT; + } + else + { + /* This is an SETUP IN command (or a SETUP IN with no data). */ + + privep->epstate = USB_EPSTATE_IDLE; + + /* Handle the SETUP OUT command now */ + + mpfs_ep0_setup(priv); + } + + /* Ready for next setup data */ + + mpfs_ep0_ctrlread(priv); + } +} + +/**************************************************************************** + * Name: mpfs_usb_interrupt + * + * Description: + * Handle the USB interrupt, for the device mode only. + * + * Input Parameters: + * irq - IRQ number (unused) + * context - Context (unused) + * arg - Pointer to the private data + * + * Returned Value: + * OK unconditionally + * + ****************************************************************************/ + +static int mpfs_usb_interrupt(int irq, void *context, void *arg) +{ + struct mpfs_usbdev_s *priv = (struct mpfs_usbdev_s *)arg; + uint16_t isr; + uint16_t pending_rx_ep; + uint16_t pending_tx_ep; + int i; + + /* Get the device interrupts */ + + isr = getreg8(MPFS_USB_IRQ); + + usbtrace(TRACE_INTDECODE(MPFS_TRACEINTID_INTERRUPT), isr); + + /* Get the endpoint interrupts */ + + pending_tx_ep = getreg16(MPFS_USB_TX_IRQ); + pending_rx_ep = getreg16(MPFS_USB_RX_IRQ); + + usbtrace(TRACE_INTDECODE(MPFS_TRACEINTID_EP_TX_IRQ), pending_tx_ep); + usbtrace(TRACE_INTDECODE(MPFS_TRACEINTID_EP_RX_IRQ), pending_rx_ep); + + if (isr & RESET_IRQ_MASK) + { + /* Handle the reset */ + + mpfs_reset(priv); + +#ifdef CONFIG_USBDEV_DUALSPEED + priv->usbdev.speed = USB_SPEED_HIGH; + priv->usbdev.dualspeed = 0; +#else + priv->usbdev.speed = USB_SPEED_FULL; + priv->usbdev.dualspeed = 1; +#endif + + return OK; + } + + /* Serve Endpoint Interrupts first */ + + if (pending_tx_ep & 0x01) + { + mpfs_ctrl_ep_interrupt(priv, 0); + } + + if (pending_tx_ep) + { + for (i = 1; i < MPFS_USB_NENDPOINTS; i++) + { + if ((pending_tx_ep & (1 << i))) + { + mpfs_ep_tx_interrupt(priv, i); + } + } + } + + if (pending_rx_ep) + { + for (i = 0; i < MPFS_USB_NENDPOINTS; i++) + { + if ((pending_rx_ep & (1 << i))) + { + mpfs_ep_rx_interrupt(priv, i); + } + } + } + + if (isr & SUSPEND_IRQ_MASK) + { + /* Unhandled */ + + uinfo("SUSPEND IRQ received\n"); + } + + /* SOF interrupt */ + + if (isr & SOF_IRQ_MASK) + { + /* Unhandled */ + + uinfo("SOF IRQ received\n"); + } + + if (isr & DISCONNECT_IRQ_MASK) + { + /* Unhandled */ + + uinfo("Disconnect IRQ\n"); + } + + if (isr & RESUME_IRQ_MASK) + { + mpfs_resume(priv); + } + + return OK; +} + +/**************************************************************************** + * Name: mpfs_usb_dma_interrupt + * + * Description: + * Handle the USB DMA interrupts. + * + * Input Parameters: + * irq - IRQ number (unused) + * context - Context (unused) + * arg - Pointer to the private data + * + * Returned Value: + * OK unconditionally + * + * Assumptions/Limitations: + * DMA is not supported yet + * + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_DMA +static int mpfs_usb_dma_interrupt(int irq, void *context, void *arg) +{ + return OK; +} +#endif + +/**************************************************************************** + * Endpoint Helpers + ****************************************************************************/ + +/**************************************************************************** + * Name: mpfs_epset_reset + * + * Description: + * Reset and disable a set of endpoints. + * + * Input Parameters: + * priv - USB device private data + * epset - Set of EPs to reset + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_epset_reset(struct mpfs_usbdev_s *priv, uint16_t epset) +{ + uint32_t bit; + int epno; + + /* Reset each endpoint in the set */ + + for (epno = 0, bit = 1, epset &= MPFS_EPSET_ALL; + epno < MPFS_USB_NENDPOINTS && epset != 0; + epno++, bit <<= 1) + { + /* Is this endpoint in the set? */ + + if ((epset & bit) != 0) + { + /* Yes, reset and disable it */ + + mpfs_ep_reset(priv, epno); + epset &= ~bit; + } + } +} + +/**************************************************************************** + * Name: mpfs_pullup + * + * Description: + * This is the pullup() method of the USB device driver interface. This is + * a no-op. + * + * Input Parameters: + * dev - USB device + * enable - Enable or disable + * + * Returned Value: + * OK unconditionally + * + ****************************************************************************/ + +static int mpfs_pullup(struct usbdev_s *dev, bool enable) +{ + return OK; +} + +/**************************************************************************** + * Initialization / Reset + ****************************************************************************/ + +/**************************************************************************** + * Name: mpfs_usb_iomux + * + * Description: + * This initializes the necessary pin-muxes for the USB controller. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_usb_iomux(void) +{ + putreg32(LIBERO_SETTING_IOMUX3_CR, MPFS_SYSREG_IOMUX3); + putreg32(LIBERO_SETTING_IOMUX4_CR, MPFS_SYSREG_IOMUX4); + + putreg32(LIBERO_SETTING_MSSIO_BANK2_CFG_CR, MPFS_SYSREG_B2_CFG); + + putreg32(LIBERO_SETTING_MSSIO_BANK2_IO_CFG_0_1_CR, MPFS_SYSREG_B2_0_1); + putreg32(LIBERO_SETTING_MSSIO_BANK2_IO_CFG_2_3_CR, MPFS_SYSREG_B2_2_3); + putreg32(LIBERO_SETTING_MSSIO_BANK2_IO_CFG_4_5_CR, MPFS_SYSREG_B2_4_5); + putreg32(LIBERO_SETTING_MSSIO_BANK2_IO_CFG_6_7_CR, MPFS_SYSREG_B2_6_7); + putreg32(LIBERO_SETTING_MSSIO_BANK2_IO_CFG_8_9_CR, MPFS_SYSREG_B2_8_9); + putreg32(LIBERO_SETTING_MSSIO_BANK2_IO_CFG_10_11_CR, + MPFS_SYSREG_B2_10_11); + putreg32(LIBERO_SETTING_MSSIO_BANK2_IO_CFG_12_13_CR, + MPFS_SYSREG_B2_12_13); + putreg32(LIBERO_SETTING_MSSIO_BANK2_IO_CFG_14_15_CR, + MPFS_SYSREG_B2_14_15); + putreg32(LIBERO_SETTING_MSSIO_BANK2_IO_CFG_16_17_CR, + MPFS_SYSREG_B2_16_17); + putreg32(LIBERO_SETTING_MSSIO_BANK2_IO_CFG_18_19_CR, + MPFS_SYSREG_B2_18_19); + putreg32(LIBERO_SETTING_MSSIO_BANK2_IO_CFG_20_21_CR, + MPFS_SYSREG_B2_20_21); + putreg32(LIBERO_SETTING_MSSIO_BANK2_IO_CFG_22_23_CR, + MPFS_SYSREG_B2_22_23); + +#ifdef CONFIG_USBDEV_DMA + /* DMA operations need to open the USB PMP registers for proper + * operation. If not configured, apply default settings. + */ + + uint64_t pmpcfg_usb_x; + + pmpcfg_usb_x = getreg64(MPFS_PMPCFG_USB_0); + if ((pmpcfg_usb_x & 0x1ffffff000000000) != 0x1f00000000000000) + { + uerr("Please check the MPFS_PMPCFG_USB_0 register.\n"); + putreg64(0x1f00000fffffffff, MPFS_PMPCFG_USB_0); + } + + pmpcfg_usb_x = getreg64(MPFS_PMPCFG_USB_1); + if ((pmpcfg_usb_x & 0x1ffffff000000000) != 0x1f00000000000000) + { + uerr("Please check the MPFS_PMPCFG_USB_1 register.\n"); + putreg64(0x1f00000fffffffff, MPFS_PMPCFG_USB_1); + } + + pmpcfg_usb_x = getreg64(MPFS_PMPCFG_USB_2); + if ((pmpcfg_usb_x & 0x1ffffff000000000) != 0x1f00000000000000) + { + uerr("Please check the MPFS_PMPCFG_USB_2 register.\n"); + putreg64(0x1f00000fffffffff, MPFS_PMPCFG_USB_2); + } + + pmpcfg_usb_x = getreg64(MPFS_PMPCFG_USB_3); + if ((pmpcfg_usb_x & 0x1ffffff000000000) != 0x1f00000000000000) + { + uerr("Please check the MPFS_PMPCFG_USB_3 register.\n"); + putreg64(0x1f00000fffffffff, MPFS_PMPCFG_USB_3); + } +#endif +} + +/**************************************************************************** + * Name: mpfs_hw_setup + * + * Description: + * This sets up the hardware by setting the clocks and resetting the proper + * modules. When ready, it issues the soft-connect procedure. + * + * Input Parameters: + * priv - USB device abstraction + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_hw_setup(struct mpfs_usbdev_s *priv) +{ + volatile uint8_t soft_reset; + + /* Prepare the pads properly first */ + + mpfs_usb_iomux(); + + /* Enable the clock */ + + mpfs_enableclk(); + + /* Disable USB block reset, also from FPGA */ + + modifyreg32(MPFS_SYSREG_SOFT_RESET_CR, SYSREG_SOFT_RESET_CR_USB | + SYSREG_SOFT_RESET_CR_FPGA, 0); + + /* Reset the controller */ + + mpfs_putreg8(SOFT_RESET_REG_MASK, MPFS_USB_SOFT_RST); + do + { + soft_reset = getreg8(MPFS_USB_SOFT_RST); + } + while (soft_reset); + + /* Enable main PLIC IRQ */ + + up_enable_irq(MPFS_IRQ_USB_MC); + + /* Clear IRQ status */ + + mpfs_putreg8(0x0, MPFS_USB_IRQ); + + /* Init descriptor base address */ + + memset((uint8_t *)(&priv->ep_descriptors[0]), 0, + sizeof(priv->ep_descriptors)); + + /* Enable interrupts */ + + mpfs_putreg8(SUSPEND_IRQ_MASK | RESUME_IRQ_MASK | RESET_IRQ_MASK | + CONNECT_IRQ_MASK | DISCONNECT_IRQ_MASK, + MPFS_USB_ENABLE); + + mpfs_putreg16(0x01, MPFS_USB_C_T_HSBT); + + /* Disable EP interrupts */ + + mpfs_putreg16(0x0, MPFS_USB_RX_IRQ_ENABLE); + mpfs_putreg16(0x0, MPFS_USB_TX_IRQ_ENABLE); + + /* Perform soft-connect */ + +#ifdef CONFIG_USBDEV_DUALSPEED + mpfs_putreg8(POWER_REG_SOFT_CONN_MASK | + POWER_REG_ENABLE_HS_MASK, MPFS_USB_POWER); +#else + mpfs_putreg8(POWER_REG_SOFT_CONN_MASK, MPFS_USB_POWER); +#endif +} + +/**************************************************************************** + * Name: mpfs_hw_shutdown + * + * Description: + * This shuts down the USB device. + * + * Input Parameters: + * priv - USB device abstraction + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_hw_shutdown(struct mpfs_usbdev_s *priv) +{ + priv->usbdev.speed = USB_SPEED_UNKNOWN; + + /* Disable all interrupts */ + + mpfs_putreg8(0, MPFS_USB_ENABLE); + + /* Disable clocking to the peripheral */ + + mpfs_disableclk(); +} + +/**************************************************************************** + * Name: mpfs_sw_setup + * + * Description: + * This sets up and initializes the software. + * + * Input Parameters: + * priv - USB device abstraction + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_sw_setup(struct mpfs_usbdev_s *priv) +{ + int epno; + + memset(priv, 0, sizeof(struct mpfs_usbdev_s)); + priv->usbdev.ops = &g_devops; + priv->usbdev.ep0 = &priv->eplist[EP0].ep; + priv->epavail = MPFS_EPSET_ALL & ~MPFS_EP_BIT(EP0); + priv->devstate = USB_DEVSTATE_SUSPENDED; + priv->prevstate = USB_DEVSTATE_POWERED; + + /* Initialize the endpoint list */ + + for (epno = 0; epno < MPFS_USB_NENDPOINTS; epno++) + { + /* Set endpoint operations, reference to driver structure (not + * really necessary because there is only one controller), and + * the (physical) endpoint number which is just the index to the + * endpoint. + */ + + priv->eplist[epno].ep.ops = &g_epops; + priv->eplist[epno].dev = priv; + priv->eplist[epno].ep.eplog = epno; + + /* We will use a maxpacket size for supported for each endpoint */ + +#ifdef CONFIG_USBDEV_DUALSPEED + priv->eplist[epno].ep.maxpacket = MPFS_USB_MAXPACKETSIZE_HS(epno); +#else + priv->eplist[epno].ep.maxpacket = MPFS_USB_MAXPACKETSIZE(epno); +#endif + + /* set descriptor addresses */ + + priv->eplist[epno].descb[0] = &priv->ep_descriptors[(epno << 1)]; + priv->eplist[epno].descb[1] = &priv->ep_descriptors[(epno << 1) + 1]; + } + + /* Select a smaller endpoint size for EP0 */ + +#if MPFS_EP0_MAXPACKET < 64 + priv->eplist[EP0].ep.maxpacket = MPFS_EP0_MAXPACKET; +#endif +} + +/**************************************************************************** + * Name: mpfs_sw_shutdown + * + * Description: + * This shuts down the sw. Currently this is a no-operation. + * + * Input Parameters: + * priv - USB device abstraction + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mpfs_sw_shutdown(struct mpfs_usbdev_s *priv) +{ +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: usbdev_register + * + * Description: + * Register a USB device class driver. The class driver's bind() method + * will be called to bind it to a USB device driver. + * + * Input Parameters: + * driver - USB device driver abstraction + * + * Returned Value: + * OK on success, a negated error otherwise + * + ****************************************************************************/ + +int usbdev_register(struct usbdevclass_driver_s *driver) +{ + struct mpfs_usbdev_s *priv = &g_usbd; + int ret; + + /* First hook up the driver */ + + priv->driver = driver; + + /* Then bind the class driver */ + + ret = CLASS_BIND(driver, &priv->usbdev); + if (ret) + { + usbtrace(TRACE_DEVERROR(MPFS_TRACEERR_BINDFAILED), (uint16_t)-ret); + priv->driver = NULL; + } + else + { + mpfs_hw_setup(priv); + } + + return ret; +} + +/**************************************************************************** + * Name: usbdev_unregister + * + * Description: + * Unregister usbdev class driver. If the USB device is connected to a + * USB host, it will first disconnect(). The driver is also requested to + * unbind() and clean up any device state, before this procedure finally + * returns. + * + * Input Parameters: + * driver - USB device driver abstraction + * + * Returned Value: + * OK on success, a negated error otherwise + * + ****************************************************************************/ + +int usbdev_unregister(struct usbdevclass_driver_s *driver) +{ + /* For now there is only one USB controller, but we will always refer to + * it using a pointer to make any future ports to multiple USB controllers + * easier. + */ + + struct mpfs_usbdev_s *priv = &g_usbd; + irqstate_t flags; + + /* Reset the hardware and cancel all requests. All requests must be + * canceled while the class driver is still bound. + */ + + flags = enter_critical_section(); + + /* Unbind the class driver */ + + CLASS_UNBIND(driver, &priv->usbdev); + + mpfs_hw_shutdown(priv); + mpfs_sw_shutdown(priv); + + /* Unhook the driver */ + + priv->driver = NULL; + leave_critical_section(flags); + + return OK; +} + +/**************************************************************************** + * Name: mpfs_usbinitialize + * + * Description: + * This is called from mpfs_usbinitialize() if it fails. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +void mpfs_usbuninitialize(void) +{ + /* Nothing to do */ +} + +/**************************************************************************** + * Name: mpfs_usbinitialize + * + * Description: + * This is called in the board startup phase. This prepares the software + * ready for the later phase. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +void mpfs_usbinitialize(void) +{ + /* For now there is only one USB controller, but we will always refer to + * it using a pointer to make any future ports to multiple USB controllers + * easier. + */ + + struct mpfs_usbdev_s *priv = &g_usbd; + + usbtrace(TRACE_DEVINIT, 0); + + /* Software initialization */ + + mpfs_sw_setup(priv); + + /* Attach interrupts */ + + if (irq_attach(MPFS_IRQ_USB_MC, mpfs_usb_interrupt, priv) != 0) + { + usbtrace(TRACE_DEVERROR(MPFS_TRACEERR_IRQREGISTRATION), + (uint16_t)MPFS_IRQ_USB_MC); + goto errout; + } + +#ifdef CONFIG_USBDEV_DMA + if (irq_attach(MPFS_IRQ_USB_DMA, mpfs_usb_dma_interrupt, priv) != 0) + { + usbtrace(TRACE_DEVERROR(MPFS_TRACEERR_IRQREGISTRATION), + (uint16_t)MPFS_IRQ_USB_DMA); + goto errout; + } +#endif + + return; + +errout: + mpfs_usbuninitialize(); +} + diff --git a/arch/risc-v/src/mpfs/mpfs_usb.h b/arch/risc-v/src/mpfs/mpfs_usb.h new file mode 100755 index 00000000000..9a3774a0d89 --- /dev/null +++ b/arch/risc-v/src/mpfs/mpfs_usb.h @@ -0,0 +1,72 @@ +/**************************************************************************** + * arch/risc-v/src/mpfs/mpfs_usb.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_RISCV_SRC_MPFS_MPFS_USB_H +#define __ARCH_RISCV_SRC_MPFS_MPFS_USB_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "chip.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: mpfs_usbinitialize + * + * Description: + * This is called in the board startup phase. This prepares the software + * ready for the later phase. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +void mpfs_usbinitialize(void); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_RISCV_SRC_MPFS_MPFS_USB_H */ diff --git a/boards/risc-v/mpfs/common/src/Make.defs b/boards/risc-v/mpfs/common/src/Make.defs index 9a3455b887f..9ed96cfc652 100755 --- a/boards/risc-v/mpfs/common/src/Make.defs +++ b/boards/risc-v/mpfs/common/src/Make.defs @@ -40,6 +40,10 @@ ifeq ($(CONFIG_MPFS_EMMCSD),y) CSRCS += mpfs_emmcsd.c endif +ifeq ($(CONFIG_USBDEV),y) +CSRCS += mpfs_usb.c +endif + DEPPATH += --dep-path src VPATH += :src CFLAGS += $(shell $(INCDIR) "$(CC)" $(TOPDIR)$(DELIM)arch$(DELIM)$(CONFIG_ARCH)$(DELIM)src$(DELIM)board$(DELIM)src) diff --git a/boards/risc-v/mpfs/common/src/mpfs_usb.c b/boards/risc-v/mpfs/common/src/mpfs_usb.c new file mode 100644 index 00000000000..61879cddd45 --- /dev/null +++ b/boards/risc-v/mpfs/common/src/mpfs_usb.c @@ -0,0 +1,81 @@ +/**************************************************************************** + * boards/risc-v/mpfs/common/src/mpfs_usb.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include + +#include "mpfs_usb.h" +#include "board_config.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mpfs_board_usb_init + * + * Description: + * Configure the USB driver. + * + * Returned Value: + * Zero (OK) is returned on success; A negated errno value is returned + * to indicate the nature of any failure. + * + ****************************************************************************/ + +int mpfs_board_usb_init(void) +{ + int ret = OK; + + mpfs_usbinitialize(); + +#ifdef CONFIG_CDCACM + ret = cdcacm_initialize(0, NULL); + if (ret != OK) + { + syslog(LOG_ERR, + "ERROR: Failed to register the CDC/ACM serial class: %d\n", + ret); + } +#endif + +#ifdef CONFIG_USBMONITOR + /* Start the USB Monitor */ + + syslog(LOG_INFO, "Starting USB Monitor\n"); + ret = usbmonitor_start(); + if (ret != OK) + { + syslog(LOG_ERR, "ERROR: Failed to start USB monitor: %d\n", ret); + return ret; + } +#endif + + return ret; +} diff --git a/boards/risc-v/mpfs/icicle/src/board_config.h b/boards/risc-v/mpfs/icicle/src/board_config.h index 2e41e265d80..b38fd34975b 100755 --- a/boards/risc-v/mpfs/icicle/src/board_config.h +++ b/boards/risc-v/mpfs/icicle/src/board_config.h @@ -48,6 +48,7 @@ int mpfs_bringup(void); int mpfs_board_spi_init(void); int mpfs_board_i2c_init(void); int mpfs_board_emmcsd_init(void); +int mpfs_board_usb_init(void); int mpfs_pwm_setup(void); #endif /* __BOARDS_RISCV_MPFS_ICICLE_SRC_BOARD_CONFIG_H */ diff --git a/boards/risc-v/mpfs/icicle/src/mpfs_bringup.c b/boards/risc-v/mpfs/icicle/src/mpfs_bringup.c index 940fa72500f..4a6a1e42aa6 100755 --- a/boards/risc-v/mpfs/icicle/src/mpfs_bringup.c +++ b/boards/risc-v/mpfs/icicle/src/mpfs_bringup.c @@ -62,6 +62,17 @@ int mpfs_bringup(void) { int ret = OK; +#ifdef CONFIG_USBDEV + /* Configure USB device driver */ + + ret = mpfs_board_usb_init(); + + if (ret < 0) + { + syslog(LOG_ERR, "Failed to initialize USB driver: %d\n", ret); + } +#endif + #if defined(CONFIG_I2C_DRIVER) /* Configure I2C peripheral interfaces */ diff --git a/boards/risc-v/mpfs/m100pfsevp/src/board_config.h b/boards/risc-v/mpfs/m100pfsevp/src/board_config.h index 2dc26307f5f..0a2499ce5e1 100755 --- a/boards/risc-v/mpfs/m100pfsevp/src/board_config.h +++ b/boards/risc-v/mpfs/m100pfsevp/src/board_config.h @@ -35,5 +35,6 @@ int mpfs_board_spi_init(void); int mpfs_board_i2c_init(void); int mpfs_pwm_setup(void); int mpfs_board_emmcsd_init(void); +int mpfs_board_usb_init(void); #endif /* __BOARDS_RISCV_MPFS_M100PFSEVP_SRC_BOARD_CONFIG_H */ diff --git a/boards/risc-v/mpfs/m100pfsevp/src/mpfs_bringup.c b/boards/risc-v/mpfs/m100pfsevp/src/mpfs_bringup.c index 570b9b6cb52..0152d5fac12 100755 --- a/boards/risc-v/mpfs/m100pfsevp/src/mpfs_bringup.c +++ b/boards/risc-v/mpfs/m100pfsevp/src/mpfs_bringup.c @@ -49,6 +49,17 @@ int mpfs_bringup(void) { int ret = OK; +#ifdef CONFIG_USBDEV + /* Configure USB device driver */ + + ret = mpfs_board_usb_init(); + + if (ret < 0) + { + syslog(LOG_ERR, "Failed to initialize USB driver: %d\n", ret); + } +#endif + #if defined(CONFIG_I2C_DRIVER) /* Configure I2C peripheral interfaces */